From e0bb7964233d62520897e14541c44e8e462e1edb Mon Sep 17 00:00:00 2001 From: Andrew Wang Date: Fri, 23 Mar 2018 13:27:59 -0700 Subject: [PATCH 01/22] Boolean default types should be boolean not string (#1732) --- Extension/package.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Extension/package.json b/Extension/package.json index 52fa60c1d9..878ae2fc85 100644 --- a/Extension/package.json +++ b/Extension/package.json @@ -428,7 +428,7 @@ "ignoreFailures": { "type": "boolean", "description": "If true, failures from the command should be ignored. Default value is false.", - "default": "false" + "default": false } } }, @@ -453,7 +453,7 @@ "ignoreFailures": { "type": "boolean", "description": "If true, failures from the command should be ignored. Default value is false.", - "default": "" + "default": false } } }, @@ -476,7 +476,7 @@ "showDisplayString": { "type": "boolean", "description": "When a visualizerFile is specified, showDisplayString will enable the display string. Turning this option on can cause slower performance during debugging.", - "default": "true" + "default": true }, "environment": { "type": "array", @@ -533,12 +533,12 @@ "filterStdout": { "type": "boolean", "description": "Search stdout stream for server-started pattern and log stdout to debug output. Defaults to true.", - "default": "true" + "default": true }, "filterStderr": { "type": "boolean", "description": "Search stderr stream for server-started pattern and log stderr to debug output. Defaults to false.", - "default": "false" + "default": false }, "serverLaunchTimeout": { "type": "integer", @@ -553,7 +553,7 @@ "externalConsole": { "type": "boolean", "description": "If true, a console is launched for the debuggee. If false, no console is launched. Note this option is ignored in some cases for technical reasons.", - "default": "false" + "default": false }, "sourceFileMap": { "type": "object", @@ -673,7 +673,7 @@ "showDisplayString": { "type": "boolean", "description": "When a visualizerFile is specified, showDisplayString will enable the display string. Turning this option on can cause slower performance during debugging.", - "default": "true" + "default": true }, "additionalSOLibSearchPath": { "type": "string", @@ -712,12 +712,12 @@ "filterStdout": { "type": "boolean", "description": "Search stdout stream for server-started pattern and log stdout to debug output. Defaults to true.", - "default": "true" + "default": true }, "filterStderr": { "type": "boolean", "description": "Search stderr stream for server-started pattern and log stderr to debug output. Defaults to false.", - "default": "false" + "default": false }, "sourceFileMap": { "type": "object", @@ -825,7 +825,7 @@ "ignoreFailures": { "type": "boolean", "description": "If true, failures from the command should be ignored. Default value is false.", - "default": "false" + "default": false } } }, @@ -914,7 +914,7 @@ "externalConsole": { "type": "boolean", "description": "If true, a console is launched for the debuggee. If false, no console is launched.", - "default": "false" + "default": false }, "sourceFileMap": { "type": "object", From ff5d36392605343164fa5c81cc2bb6f6adc7cf07 Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Tue, 27 Mar 2018 18:43:07 -0700 Subject: [PATCH 02/22] Don't set the compilerPath if the default is empty. (#1741) * Don't set the compilerPath if the default is empty or compile commands is set. * Guard against empty std. --- Extension/src/LanguageServer/configurations.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Extension/src/LanguageServer/configurations.ts b/Extension/src/LanguageServer/configurations.ts index 397c16366d..271853a9e7 100644 --- a/Extension/src/LanguageServer/configurations.ts +++ b/Extension/src/LanguageServer/configurations.ts @@ -220,7 +220,9 @@ export class CppProperties { if (process.platform === 'darwin') { this.configurationJson.configurations[this.CurrentConfiguration].macFrameworkPath = this.defaultFrameworks; } - this.configurationJson.configurations[this.CurrentConfiguration].compilerPath = this.defaultCompilerPath; + if (this.defaultCompilerPath) { + this.configurationJson.configurations[this.CurrentConfiguration].compilerPath = this.defaultCompilerPath; + } if (this.defaultCStandard) { this.configurationJson.configurations[this.CurrentConfiguration].cStandard = this.defaultCStandard; } @@ -475,15 +477,16 @@ export class CppProperties { // Update the compilerPath, cStandard, and cppStandard with the default if they're missing. let config: Configuration = this.configurationJson.configurations[this.CurrentConfiguration]; - if (config.compilerPath === undefined) { + // Don't set the default if compileCommands exist, until it is fixed to have the correct value. + if (config.compilerPath === undefined && this.defaultCompilerPath && !config.compileCommands) { config.compilerPath = this.defaultCompilerPath; dirty = true; } - if (!config.cStandard) { + if (!config.cStandard && this.defaultCStandard) { config.cStandard = this.defaultCStandard; dirty = true; } - if (!config.cppStandard) { + if (!config.cppStandard && this.defaultCppStandard) { config.cppStandard = this.defaultCppStandard; dirty = true; } From 638fd678dc5ae7434cfacbb1f6d5786276425e7b Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Tue, 27 Mar 2018 20:34:35 -0700 Subject: [PATCH 03/22] Update changelog date. (#1745) --- Extension/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Extension/CHANGELOG.md b/Extension/CHANGELOG.md index c7da07d624..81e7c14170 100644 --- a/Extension/CHANGELOG.md +++ b/Extension/CHANGELOG.md @@ -1,6 +1,6 @@ # C/C++ for Visual Studio Code Change Log -## Version 0.16.0: March 22, 2018 +## Version 0.16.0: March 28, 2018 * Enable autocomplete for local and global scopes. [#13](https://github.com/Microsoft/vscode-cpptools/issues/13) * Add a setting to define multiline comment patterns: `C_Cpp.commentContinuationPatterns`. [#1100](https://github.com/Microsoft/vscode-cpptools/issues/1100), [#1539](https://github.com/Microsoft/vscode-cpptools/issues/1539) * Add a setting to disable inactive region highlighting: `C_Cpp.dimInactiveRegions`. [#1592](https://github.com/Microsoft/vscode-cpptools/issues/1592) From b65a89733c3a704e12266e0968c71fa27292b307 Mon Sep 17 00:00:00 2001 From: Andrew Wang Date: Wed, 28 Mar 2018 15:58:08 -0700 Subject: [PATCH 04/22] Adding file validation for download and unzip. (#1751) * Adding file validation for download and unzip. Refactored the download retry code. Added error handlers for read and write streams. WriteStream writes to a *.tmp file and then renames. This catches the case if VSCode dies in the middle of this process and the file is partially written. We will continue where we left off. * Adding return to resolve/reject calls. * Fix retry loop and check err in unlink and rename * Actually wait for those promises * Fix TSLint error --- Extension/package-lock.json | 2 +- Extension/src/common.ts | 22 ++++ Extension/src/packageManager.ts | 175 +++++++++++++++++++------------- 3 files changed, 126 insertions(+), 73 deletions(-) diff --git a/Extension/package-lock.json b/Extension/package-lock.json index c145d8bcad..87398cc69c 100644 --- a/Extension/package-lock.json +++ b/Extension/package-lock.json @@ -1,6 +1,6 @@ { "name": "cpptools", - "version": "0.15.0", + "version": "0.16.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/Extension/src/common.ts b/Extension/src/common.ts index 964b2f735d..6bf7a26d72 100644 --- a/Extension/src/common.ts +++ b/Extension/src/common.ts @@ -408,4 +408,26 @@ export function checkDistro(platformInfo: PlatformInformation): void { // or SunOS (the other platforms supported by node) getOutputChannelLogger().appendLine(`Warning: Debugging has not been tested for this platform. ${getReadmeMessage()}`); } +} + +export async function unlinkPromise(fileName: string): Promise { + return new Promise((resolve, reject) => { + fs.unlink(fileName, err => { + if (err) { + return reject(err); + } + return resolve(); + }); + }); +} + +export async function renamePromise(oldName: string, newName: string): Promise { + return new Promise((resolve, reject) => { + fs.rename(oldName, newName, err => { + if (err) { + return reject(err); + } + return resolve(); + }); + }); } \ No newline at end of file diff --git a/Extension/src/packageManager.ts b/Extension/src/packageManager.ts index a2ae74f0b5..985d3a120e 100644 --- a/Extension/src/packageManager.ts +++ b/Extension/src/packageManager.ts @@ -137,69 +137,74 @@ export class PackageManager { }); } - private DownloadPackage(pkg: IPackage): Promise { + private async DownloadPackage(pkg: IPackage): Promise { this.AppendChannel(`Downloading package '${pkg.description}' `); this.SetStatusText("$(cloud-download) Downloading packages..."); this.SetStatusTooltip(`Downloading package '${pkg.description}'...`); + const tmpResult: tmp.SyncResult = await this.CreateTempFile(pkg); + await this.DownloadPackageWithRetries(pkg, tmpResult); + } + + private async CreateTempFile(pkg: IPackage): Promise { return new Promise((resolve, reject) => { tmp.file({ prefix: "package-" }, (err, path, fd, cleanupCallback) => { if (err) { return reject(new PackageManagerError('Error from temp.file', 'DownloadPackage', pkg, err)); } - resolve({ name: path, fd: fd, removeCallback: cleanupCallback }); + return resolve({ name: path, fd: fd, removeCallback: cleanupCallback }); }); - }) - .then((tmpResult) => { - pkg.tmpFile = tmpResult; - - let lastError: any = null; - let retryCount: number = 0; - let handleDownloadFailure: (num: any, error: any) => void = (num, error) => { - retryCount = num; - lastError = error; + }); + } + + private async DownloadPackageWithRetries(pkg: IPackage, tmpResult: tmp.SyncResult): Promise { + pkg.tmpFile = tmpResult; + + let success: boolean = false; + let lastError: any = null; + let retryCount: number = 0; + const MAX_RETRIES: number = 5; + + // Retry the download at most MAX_RETRIES times with 2-32 seconds delay. + do { + try { + await this.DownloadFile(pkg.url, pkg, retryCount); + success = true; + } catch (error) { + retryCount += 1; + lastError = error; + if (retryCount >= MAX_RETRIES) { + this.AppendChannel(` Failed to download ` + pkg.url); + throw error; + } else { + // This will skip the success = true. this.AppendChannel(` Failed. Retrying...`); - }; - // Retry the download at most 5 times with 2-32 seconds delay. - return this.DownloadFile(pkg.url, pkg, 0).catch((error) => { - handleDownloadFailure(1, error); - return this.DownloadFile(pkg.url, pkg, 1).catch((error) => { - handleDownloadFailure(2, error); - return this.DownloadFile(pkg.url, pkg, 2).catch((error) => { - handleDownloadFailure(3, error); - return this.DownloadFile(pkg.url, pkg, 3).catch((error) => { - handleDownloadFailure(4, error); - return this.DownloadFile(pkg.url, pkg, 4).catch((error) => { - handleDownloadFailure(5, error); - return this.DownloadFile(pkg.url, pkg, 5); // Last try, don't catch the error. - }); - }); - }); - }); - }).then(() => { - this.AppendLineChannel(" Done!"); - if (retryCount !== 0) { - // Log telemetry to see if retrying helps. - let telemetryProperties: { [key: string]: string } = {}; - telemetryProperties["success"] = `OnRetry${retryCount}`; - if (lastError instanceof PackageManagerError) { - let packageError: PackageManagerError = lastError; - telemetryProperties['error.methodName'] = packageError.methodName; - telemetryProperties['error.message'] = packageError.message; - if (packageError.pkg) { - telemetryProperties['error.packageName'] = packageError.pkg.description; - telemetryProperties['error.packageUrl'] = packageError.pkg.url; - } - if (packageError.errorCode) { - telemetryProperties['error.errorCode'] = packageError.errorCode; - } - } - Telemetry.logDebuggerEvent("acquisition", telemetryProperties); - } - }); - }); + continue; + } + } + } while (!success && retryCount < MAX_RETRIES); + + this.AppendLineChannel(" Done!"); + if (retryCount !== 0) { + // Log telemetry to see if retrying helps. + let telemetryProperties: { [key: string]: string } = {}; + telemetryProperties["success"] = `OnRetry${retryCount}`; + if (lastError instanceof PackageManagerError) { + let packageError: PackageManagerError = lastError; + telemetryProperties['error.methodName'] = packageError.methodName; + telemetryProperties['error.message'] = packageError.message; + if (packageError.pkg) { + telemetryProperties['error.packageName'] = packageError.pkg.description; + telemetryProperties['error.packageUrl'] = packageError.pkg.url; + } + if (packageError.errorCode) { + telemetryProperties['error.errorCode'] = packageError.errorCode; + } + } + Telemetry.logDebuggerEvent("acquisition", telemetryProperties); + } } // reloadCpptoolsJson in main.ts uses ~25% of this function. @@ -276,11 +281,11 @@ export class PackageManager { }); response.on('end', () => { - resolve(); + return resolve(); }); response.on('error', (error) => { - reject(new PackageManagerWebResponseError(response.socket, 'HTTP/HTTPS Response error', 'DownloadFile', pkg, error.stack, error.name)); + return reject(new PackageManagerWebResponseError(response.socket, 'HTTP/HTTPS Response error', 'DownloadFile', pkg, error.stack, error.name)); }); // Begin piping data from the response to the package file @@ -291,7 +296,7 @@ export class PackageManager { let request: ClientRequest = https.request(options, handleHttpResponse); request.on('error', (error) => { - reject(new PackageManagerError('HTTP/HTTPS Request error' + (urlString.includes("fwlink") ? ": fwlink" : ""), 'DownloadFile', pkg, error.stack, error.message)); + return reject(new PackageManagerError('HTTP/HTTPS Request error' + (urlString.includes("fwlink") ? ": fwlink" : ""), 'DownloadFile', pkg, error.stack, error.message)); }); // Execute the request @@ -316,6 +321,15 @@ export class PackageManager { return reject(new PackageManagerError('Zip file error', 'InstallPackage', pkg, err)); } + // setup zip file events + zipfile.on('end', () => { + return resolve(); + }); + + zipfile.on('error', err => { + return reject(new PackageManagerError('Zip File Error', 'InstallPackage', pkg, err, err.code)); + }); + zipfile.readEntry(); zipfile.on('entry', (entry: yauzl.Entry) => { @@ -334,26 +348,52 @@ export class PackageManager { util.checkFileExists(absoluteEntryPath).then((exists: boolean) => { if (!exists) { // File - extract it - zipfile.openReadStream(entry, (err, readStream) => { + zipfile.openReadStream(entry, (err, readStream: fs.ReadStream) => { if (err) { return reject(new PackageManagerError('Error reading zip stream', 'InstallPackage', pkg, err)); } - mkdirp.mkdirp(path.dirname(absoluteEntryPath), { mode: 0o775 }, (err) => { + readStream.on('error', (err) => { + return reject(new PackageManagerError('Error in readStream', 'InstallPackage', pkg, err)); + }); + + mkdirp.mkdirp(path.dirname(absoluteEntryPath), { mode: 0o775 }, async (err) => { if (err) { return reject(new PackageManagerError('Error creating directory', 'InstallPackage', pkg, err, err.code)); } + // Create as a .tmp file to avoid partially unzipped files + // counting as completed files. + let absoluteEntryTempFile: string = absoluteEntryPath + ".tmp"; + if (fs.existsSync(absoluteEntryTempFile)) { + try { + await util.unlinkPromise(absoluteEntryTempFile); + } catch (err) { + return reject(new PackageManagerError(`Error unlinking file ${absoluteEntryTempFile}`, 'InstallPackage', pkg, err)); + } + } + // Make sure executable files have correct permissions when extracted let fileMode: number = (pkg.binaries && pkg.binaries.indexOf(absoluteEntryPath) !== -1) ? 0o755 : 0o664; - - let writeStream: fs.WriteStream = fs.createWriteStream(absoluteEntryPath, { mode: fileMode }); - readStream.pipe(writeStream); - writeStream.on('close', () => { + let writeStream: fs.WriteStream = fs.createWriteStream(absoluteEntryTempFile, { mode: fileMode }); + + writeStream.on('close', async () => { + try { + // Remove .tmp extension from the file. + await util.renamePromise(absoluteEntryTempFile, absoluteEntryPath); + } catch (err) { + return reject(new PackageManagerError(`Error renaming file ${absoluteEntryTempFile}`, 'InstallPackage', pkg, err)); + } // Wait till output is done writing before reading the next zip entry. // Otherwise, it's possible to try to launch the .exe before it is done being created. zipfile.readEntry(); }); + + writeStream.on('error', (err) => { + return reject(new PackageManagerError('Error in writeStream', 'InstallPackage', pkg, err)); + }); + + readStream.pipe(writeStream); }); }); } else { @@ -366,20 +406,11 @@ export class PackageManager { }); } }); - - zipfile.on('end', () => { - resolve(); - }); - - zipfile.on('error', err => { - reject(new PackageManagerError('Zip File Error', 'InstallPackage', pkg, err, err.code)); - }); - }); - }) - .then(() => { - // Clean up temp file - pkg.tmpFile.removeCallback(); }); + }).then(() => { + // Clean up temp file + pkg.tmpFile.removeCallback(); + }); } private AppendChannel(text: string): void { From 480af0c1554d4fc377e971e2a75f3bc33ac41c70 Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Fri, 30 Mar 2018 15:45:36 -0700 Subject: [PATCH 05/22] Dev/seanmcm/0 16 1 update (#1762) * Update for 0.16.1. --- Extension/CHANGELOG.md | 6 ++++++ Extension/package.json | 10 +++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Extension/CHANGELOG.md b/Extension/CHANGELOG.md index 81e7c14170..95df84f674 100644 --- a/Extension/CHANGELOG.md +++ b/Extension/CHANGELOG.md @@ -1,5 +1,11 @@ # C/C++ for Visual Studio Code Change Log +## Version 0.16.1: March 30, 2018 +* Fix random deadlock caused by logging code on Linux/Mac. [#1759](https://github.com/Microsoft/vscode-cpptools/issues/1759) +* Fix compiler from `compileCommands` not being queried for includes/defines if `compilerPath` isn't set on Windows. [#1754](https://github.com/Microsoft/vscode-cpptools/issues/1754) +* Fix OSX `UseShellExecute` I/O bug. [#1756](https://github.com/Microsoft/vscode-cpptools/issues/1756) +* Invalidate partially unzipped files from package manager. [#1757](https://github.com/Microsoft/vscode-cpptools/issues/1757) + ## Version 0.16.0: March 28, 2018 * Enable autocomplete for local and global scopes. [#13](https://github.com/Microsoft/vscode-cpptools/issues/13) * Add a setting to define multiline comment patterns: `C_Cpp.commentContinuationPatterns`. [#1100](https://github.com/Microsoft/vscode-cpptools/issues/1100), [#1539](https://github.com/Microsoft/vscode-cpptools/issues/1539) diff --git a/Extension/package.json b/Extension/package.json index 878ae2fc85..b2207eed99 100644 --- a/Extension/package.json +++ b/Extension/package.json @@ -2,7 +2,7 @@ "name": "cpptools", "displayName": "C/C++", "description": "C/C++ IntelliSense, debugging, and code browsing.", - "version": "0.16.0", + "version": "0.16.1", "publisher": "ms-vscode", "preview": true, "icon": "LanguageCCPP_color_128x.png", @@ -1145,7 +1145,7 @@ "runtimeDependencies": [ { "description": "C/C++ language components (Linux / x86_64)", - "url": "https://go.microsoft.com/fwlink/?linkid=867977", + "url": "https://go.microsoft.com/fwlink/?linkid=871264", "platforms": [ "linux" ], @@ -1159,7 +1159,7 @@ }, { "description": "C/C++ language components (Linux / x86)", - "url": "https://go.microsoft.com/fwlink/?linkid=867978", + "url": "https://go.microsoft.com/fwlink/?linkid=871265", "platforms": [ "linux" ], @@ -1175,7 +1175,7 @@ }, { "description": "C/C++ language components (OS X)", - "url": "https://go.microsoft.com/fwlink/?linkid=867979", + "url": "https://go.microsoft.com/fwlink/?linkid=871266", "platforms": [ "darwin" ], @@ -1186,7 +1186,7 @@ }, { "description": "C/C++ language components (Windows)", - "url": "https://go.microsoft.com/fwlink/?linkid=867980", + "url": "https://go.microsoft.com/fwlink/?linkid=871267", "platforms": [ "win32" ], From b32debdb7621634fbf82c7a44ee14cdfd6d271aa Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Fri, 30 Mar 2018 16:27:55 -0700 Subject: [PATCH 06/22] Update package-lock.json version. (#1765) --- Extension/package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Extension/package-lock.json b/Extension/package-lock.json index 87398cc69c..7a2f68f98b 100644 --- a/Extension/package-lock.json +++ b/Extension/package-lock.json @@ -1,6 +1,6 @@ { "name": "cpptools", - "version": "0.16.0", + "version": "0.16.1", "lockfileVersion": 1, "requires": true, "dependencies": { From 28102494b16264f3f3a4fcab73ea5ef77af21993 Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Tue, 3 Apr 2018 15:22:20 -0700 Subject: [PATCH 07/22] Update CONTRIBUTING.md (#1776) Fix broken link. --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3e0b3f45bb..0211aa40d3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ ## Contribution Steps -* [Build and debug the extension](Documentation/Getting%20started.md#build-and-debug-the-cpptools-extension). +* [Build and debug the extension](Documentation/Building%20the%20Extension.md). * File an [issue](https://github.com/Microsoft/vscode-cpptools/issues) and a [pull request](https://github.com/Microsoft/vscode-cpptools/pulls) with the change and we will review it. * If the change affects functionality, add a line describing the change to [**CHANGELOG.md**](Extension/CHANGELOG.md). * Try and add a test in [**test/extension.test.ts**](Extension/test/unitTests/extension.test.ts). From a44bab55897fc29fd17d6e433826cf427079e966 Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Wed, 4 Apr 2018 17:10:40 -0700 Subject: [PATCH 08/22] Fix https://github.com/Microsoft/vscode-cpptools/issues/1792 . (#1793) --- Extension/src/LanguageServer/clientCollection.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Extension/src/LanguageServer/clientCollection.ts b/Extension/src/LanguageServer/clientCollection.ts index eb63d8fba9..6cb850b8b8 100644 --- a/Extension/src/LanguageServer/clientCollection.ts +++ b/Extension/src/LanguageServer/clientCollection.ts @@ -8,6 +8,7 @@ import * as vscode from 'vscode'; import * as util from '../common'; import * as telemetry from '../telemetry'; import * as cpptools from './client'; +import * as path from 'path'; const defaultClientKey: string = "@@default@@"; export interface ClientKey { @@ -82,7 +83,7 @@ export class ClientCollection { public checkOwnership(client: cpptools.Client, document: vscode.TextDocument): boolean { let owners: cpptools.Client[] = []; this.languageClients.forEach(languageClient => { - if (document.uri.fsPath.startsWith(languageClient.RootPath)) { + if (document.uri.fsPath.startsWith(languageClient.RootPath + path.sep)) { owners.push(languageClient); } }); From 6517b8e395ab611b5a09acf27a7c172422e91ce3 Mon Sep 17 00:00:00 2001 From: Pierson Lee Date: Tue, 10 Apr 2018 11:05:01 -0700 Subject: [PATCH 09/22] Update launch.md for args to include comment about doublequoting (#1794) * Update launch.md * Update launch.md --- launch.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launch.md b/launch.md index 5c2bf5b7a0..d49a5a7c03 100644 --- a/launch.md +++ b/launch.md @@ -70,7 +70,7 @@ Set or change the following options to control VS Code's behavior during debuggi The following options enable you to modify the state of the target application when it is launched: * #### `args` - JSON array of command line arguments to pass to the program when it is launched. Example `["arg1", "arg2]`. + JSON array of command line arguments to pass to the program when it is launched. Example `["arg1", "arg2"]`. If you are escaping characters you will need to double escape them. For example `["{\\\"arg\\\": true}]` will send `{"arg1": true}` to your application. * #### `cwd` Sets the the working directory of the application launched by the debugger. From 7cff0f175d58c01fb22753e6fde4f1096b5c5399 Mon Sep 17 00:00:00 2001 From: Andrew Wang Date: Tue, 10 Apr 2018 12:04:29 -0700 Subject: [PATCH 10/22] Prevent package.json rewrite from being checked in with test (#1809) * Fix up package.json rewrite VSCode package.json is readonly. Rewriting package.json will use the disk package.json to prevent getting expanded fields from VSCode. It will only read from disk once. Added `gulp pr-check` to make sure that package.json activationEvents has not been rewritten for open source contributers. * run pr-check needs to be before run test * Update log message --- .travis.yml | 2 ++ Extension/gulpfile.js | 9 +++++++++ Extension/package.json | 1 + Extension/src/abTesting.ts | 13 +++++++------ Extension/src/common.ts | 26 +++++++++++++++++++++----- Extension/src/main.ts | 6 ++++-- 6 files changed, 44 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4ecc32d232..d5f452ae91 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,6 +33,8 @@ script: - npm install - npm run tslint - npm run compile + # pr-check needs to run before test. test modifies package.json. + - npm run pr-check - npm run test after_failure: diff --git a/Extension/gulpfile.js b/Extension/gulpfile.js index 9dc120c007..6bce747394 100644 --- a/Extension/gulpfile.js +++ b/Extension/gulpfile.js @@ -9,6 +9,7 @@ const gulp = require('gulp'); const env = require('gulp-env') const tslint = require('gulp-tslint'); const mocha = require('gulp-mocha'); +const fs = require('fs'); gulp.task('allTests', () => { gulp.start('unitTests'); @@ -73,4 +74,12 @@ gulp.task('tslint', () => { summarizeFailureOutput: false, emitError: false })) +}); + +gulp.task('pr-check', () => { + const packageJson = JSON.parse(fs.readFileSync('./package.json').toString()); + if (packageJson.activationEvents.length !== 1 && packageJson.activationEvents[0] !== '*') { + console.log('Please make sure to not check in package.json that has been rewritten by the extension activation. If you intended to have changes in package.json, please only check-in your changes. If you did not, please run `git checkout -- package.json`.'); + process.exit(1); + } }); \ No newline at end of file diff --git a/Extension/package.json b/Extension/package.json index b2207eed99..6f47cfa509 100644 --- a/Extension/package.json +++ b/Extension/package.json @@ -1109,6 +1109,7 @@ "integrationTests": "gulp integrationTests", "postinstall": "node ./node_modules/vscode/bin/install", "pretest": "tsc -p ./", + "pr-check": "gulp pr-check", "test": "gulp allTests", "tslint": "gulp tslint", "unitTests": "gulp unitTests", diff --git a/Extension/src/abTesting.ts b/Extension/src/abTesting.ts index c3fd053245..e688aa5804 100644 --- a/Extension/src/abTesting.ts +++ b/Extension/src/abTesting.ts @@ -80,16 +80,17 @@ export function downloadCpptoolsJsonPkg(): Promise { export function processCpptoolsJson(cpptoolsString: string): Promise { let cpptoolsObject: any = JSON.parse(cpptoolsString); let intelliSenseEnginePercentage: number = cpptoolsObject.intelliSenseEngine_default_percentage; + let packageJson: any = util.getRawPackageJson(); - if (!util.packageJson.extensionFolderPath.includes(".vscode-insiders")) { - let prevIntelliSenseEngineDefault: any = util.packageJson.contributes.configuration.properties["C_Cpp.intelliSenseEngine"].default; + if (!packageJson.extensionFolderPath.includes(".vscode-insiders")) { + let prevIntelliSenseEngineDefault: any = packageJson.contributes.configuration.properties["C_Cpp.intelliSenseEngine"].default; if (util.extensionContext.globalState.get(userBucketString, userBucketMax + 1) <= intelliSenseEnginePercentage) { - util.packageJson.contributes.configuration.properties["C_Cpp.intelliSenseEngine"].default = "Default"; + packageJson.contributes.configuration.properties["C_Cpp.intelliSenseEngine"].default = "Default"; } else { - util.packageJson.contributes.configuration.properties["C_Cpp.intelliSenseEngine"].default = "Tag Parser"; + packageJson.contributes.configuration.properties["C_Cpp.intelliSenseEngine"].default = "Tag Parser"; } - if (prevIntelliSenseEngineDefault !== util.packageJson.contributes.configuration.properties["C_Cpp.intelliSenseEngine"].default) { - return util.writeFileText(util.getPackageJsonPath(), util.getPackageJsonString()); + if (prevIntelliSenseEngineDefault !== packageJson.contributes.configuration.properties["C_Cpp.intelliSenseEngine"].default) { + return util.writeFileText(util.getPackageJsonPath(), util.stringifyPackageJson(packageJson)); } } } \ No newline at end of file diff --git a/Extension/src/common.ts b/Extension/src/common.ts index 6bf7a26d72..19cc5e3f4e 100644 --- a/Extension/src/common.ts +++ b/Extension/src/common.ts @@ -20,18 +20,34 @@ export function setExtensionContext(context: vscode.ExtensionContext): void { extensionContext = context; } -export let packageJson: any = vscode.extensions.getExtension("ms-vscode.cpptools").packageJSON; +// Use this package.json to read values +export const packageJson: any = vscode.extensions.getExtension("ms-vscode.cpptools").packageJSON; + +// Use getRawPackageJson to read and write back to package.json +// This prevents obtaining any of VSCode's expanded variables. +let rawPackageJson: any = null; +export function getRawPackageJson(): any { + if (rawPackageJson === null) { + const fileContents: Buffer = fs.readFileSync(getPackageJsonPath()); + rawPackageJson = JSON.parse(fileContents.toString()); + } + return rawPackageJson; +} + +// This function is used to stringify the rawPackageJson. +// Do not use with util.packageJson or else the expanded +// package.json will be written back. +export function stringifyPackageJson(packageJson: string): string { + return JSON.stringify(packageJson, null, 2); +} export function getExtensionFilePath(extensionfile: string): string { return path.resolve(extensionContext.extensionPath, extensionfile); } + export function getPackageJsonPath(): string { return getExtensionFilePath("package.json"); } -export function getPackageJsonString(): string { - packageJson.main = "./out/src/main"; // Needs to be reset, because the relative path is removed by VS Code. - return JSON.stringify(packageJson, null, 2); -} // Extension is ready if install.lock exists and debugAdapters folder exist. export async function isExtensionReady(): Promise { diff --git a/Extension/src/main.ts b/Extension/src/main.ts index 54d19f7f28..7d3d99c770 100644 --- a/Extension/src/main.ts +++ b/Extension/src/main.ts @@ -287,7 +287,9 @@ async function finalizeExtensionActivation(): Promise { function rewriteManifest(): Promise { // Replace activationEvents with the events that the extension should be activated for subsequent sessions. - util.packageJson.activationEvents = [ + let packageJson: any = util.getRawPackageJson(); + + packageJson.activationEvents = [ "onLanguage:cpp", "onLanguage:c", "onCommand:extension.pickNativeProcess", @@ -310,5 +312,5 @@ function rewriteManifest(): Promise { "onDebug" ]; - return util.writeFileText(util.getPackageJsonPath(), util.getPackageJsonString()); + return util.writeFileText(util.getPackageJsonPath(), util.stringifyPackageJson(packageJson)); } From e2b5bf9fbc3834cf556d11d12c4e959b64be62d9 Mon Sep 17 00:00:00 2001 From: Bob Brown Date: Tue, 10 Apr 2018 14:34:43 -0700 Subject: [PATCH 11/22] Address config issues: #1599, #1790 (#1807) * addresses #1599, #1790 * Update all configs when c_cpp_properties.json is parsed --- .../src/LanguageServer/configurations.ts | 44 +++++++++++-------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/Extension/src/LanguageServer/configurations.ts b/Extension/src/LanguageServer/configurations.ts index 271853a9e7..74f3066152 100644 --- a/Extension/src/LanguageServer/configurations.ts +++ b/Extension/src/LanguageServer/configurations.ts @@ -141,11 +141,14 @@ export class CppProperties { console.assert(rootPath !== undefined); this.currentConfigurationIndex = new PersistentFolderState("CppProperties.currentConfigurationIndex", -1, rootPath); this.configFolder = path.join(rootPath, ".vscode"); - this.resetToDefaultSettings(this.currentConfigurationIndex.Value === -1); - + let configFilePath: string = path.join(this.configFolder, "c_cpp_properties.json"); if (fs.existsSync(configFilePath)) { this.propertiesFile = vscode.Uri.file(configFilePath); + this.parsePropertiesFile(); + } + if (!this.configurationJson) { + this.resetToDefaultSettings(this.CurrentConfiguration === -1); } this.configFileWatcher = vscode.workspace.createFileSystemWatcher(path.join(this.configFolder, this.configurationGlobPattern)); @@ -452,6 +455,7 @@ export class CppProperties { // the system includes were available. this.configurationIncomplete = false; + // Update intelliSenseMode, compilerPath, cStandard, and cppStandard with the defaults if they're missing. let dirty: boolean = false; for (let i: number = 0; i < this.configurationJson.configurations.length; i++) { let config: Configuration = this.configurationJson.configurations[i]; @@ -459,6 +463,19 @@ export class CppProperties { dirty = true; config.intelliSenseMode = this.getIntelliSenseModeForPlatform(config.name); } + // Don't set the default if compileCommands exist, until it is fixed to have the correct value. + if (config.compilerPath === undefined && this.defaultCompilerPath && !config.compileCommands) { + config.compilerPath = this.defaultCompilerPath; + dirty = true; + } + if (!config.cStandard && this.defaultCStandard) { + config.cStandard = this.defaultCStandard; + dirty = true; + } + if (!config.cppStandard && this.defaultCppStandard) { + config.cppStandard = this.defaultCppStandard; + dirty = true; + } } if (this.configurationJson.version !== configVersion) { @@ -475,24 +492,13 @@ export class CppProperties { } } - // Update the compilerPath, cStandard, and cppStandard with the default if they're missing. - let config: Configuration = this.configurationJson.configurations[this.CurrentConfiguration]; - // Don't set the default if compileCommands exist, until it is fixed to have the correct value. - if (config.compilerPath === undefined && this.defaultCompilerPath && !config.compileCommands) { - config.compilerPath = this.defaultCompilerPath; - dirty = true; - } - if (!config.cStandard && this.defaultCStandard) { - config.cStandard = this.defaultCStandard; - dirty = true; - } - if (!config.cppStandard && this.defaultCppStandard) { - config.cppStandard = this.defaultCppStandard; - dirty = true; - } - if (dirty) { - fs.writeFileSync(this.propertiesFile.fsPath, JSON.stringify(this.configurationJson, null, 4)); + try { + fs.writeFileSync(this.propertiesFile.fsPath, JSON.stringify(this.configurationJson, null, 4)); + } catch { + // Ignore write errors, the file may be under source control. Updated settings will only be modified in memory. + vscode.window.showWarningMessage('Attempt to update "' + this.propertiesFile.fsPath + '" failed (do you have write access?)'); + } } } catch (err) { vscode.window.showErrorMessage('Failed to parse "' + this.propertiesFile.fsPath + '": ' + err.message); From 308e5a5c135260819a3b0d5aa31455622800c83c Mon Sep 17 00:00:00 2001 From: Andrew Wang Date: Tue, 10 Apr 2018 16:19:39 -0700 Subject: [PATCH 12/22] Download and Install Progress Window for Packages (#1806) * Adding progress bar * Updating vscode requirement and package version * Updating message --- Extension/package.json | 4 +- Extension/src/main.ts | 22 ++++++----- Extension/src/packageManager.ts | 67 ++++++++++++--------------------- 3 files changed, 40 insertions(+), 53 deletions(-) diff --git a/Extension/package.json b/Extension/package.json index 6f47cfa509..69ffcaea58 100644 --- a/Extension/package.json +++ b/Extension/package.json @@ -12,7 +12,7 @@ }, "license": "SEE LICENSE IN LICENSE.txt", "engines": { - "vscode": "^1.17.0" + "vscode": "^1.22.0" }, "bugs": { "url": "https://github.com/Microsoft/vscode-cpptools/issues", @@ -1128,7 +1128,7 @@ "tslint-no-unused-expression-chai": "0.0.3", "tslint": "5.8.0", "typescript": "^2.5.3", - "vscode": "^1.1.6" + "vscode": "^1.1.14" }, "dependencies": { "http-proxy-agent": "~2.0.0", diff --git a/Extension/src/main.ts b/Extension/src/main.ts index 7d3d99c770..3bb06dc9d6 100644 --- a/Extension/src/main.ts +++ b/Extension/src/main.ts @@ -114,18 +114,22 @@ async function downloadAndInstallPackages(info: PlatformInformation): Promise { - outputChannelLogger.appendLine(''); - setInstallationStage('installPackages'); - await packageManager.InstallPackages(); + outputChannelLogger.appendLine(''); + setInstallationStage('downloadPackages'); + await packageManager.DownloadPackages(progress); - statusItem.dispose(); + outputChannelLogger.appendLine(''); + setInstallationStage('installPackages'); + await packageManager.InstallPackages(progress); + }); } function makeBinariesExecutable(): Promise { diff --git a/Extension/src/packageManager.ts b/Extension/src/packageManager.ts index 985d3a120e..1ee9ae5647 100644 --- a/Extension/src/packageManager.ts +++ b/Extension/src/packageManager.ts @@ -67,24 +67,33 @@ export class PackageManager { public constructor( private platformInfo: PlatformInformation, - private outputChannel?: Logger, - private statusItem?: vscode.StatusBarItem) { + private outputChannel?: Logger) { // Ensure our temp files get cleaned up in case of error tmp.setGracefulCleanup(); } - public DownloadPackages(): Promise { + public DownloadPackages(progress: vscode.Progress<{message?: string; increment?: number}>): Promise { return this.GetPackages() .then((packages) => { - return this.BuildPromiseChain(packages, (pkg) => this.DownloadPackage(pkg)); + let count: number = 1; + return this.BuildPromiseChain(packages, (pkg): Promise => { + const p: Promise = this.DownloadPackage(pkg, `${count}/${packages.length}`, progress); + count += 1; + return p; + }); }); } - public InstallPackages(): Promise { + public InstallPackages(progress: vscode.Progress<{message?: string; increment?: number}>): Promise { return this.GetPackages() - .then((packages) => { - return this.BuildPromiseChain(packages, (pkg) => this.InstallPackage(pkg)); + .then((packages) => { + let count: number = 1; + return this.BuildPromiseChain(packages, (pkg): Promise => { + const p: Promise = this.InstallPackage(pkg, `${count}/${packages.length}`, progress); + count += 1; + return p; }); + }); } /** Builds a chain of promises by calling the promiseBuilder function once per item in the list. @@ -137,14 +146,13 @@ export class PackageManager { }); } - private async DownloadPackage(pkg: IPackage): Promise { + private async DownloadPackage(pkg: IPackage, progressCount: string, progress: vscode.Progress<{message?: string; increment?: number}>): Promise { this.AppendChannel(`Downloading package '${pkg.description}' `); - this.SetStatusText("$(cloud-download) Downloading packages..."); - this.SetStatusTooltip(`Downloading package '${pkg.description}'...`); + progress.report({message: `Downloading ${progressCount}: ${pkg.description}`}); const tmpResult: tmp.SyncResult = await this.CreateTempFile(pkg); - await this.DownloadPackageWithRetries(pkg, tmpResult); + await this.DownloadPackageWithRetries(pkg, tmpResult, progress); } private async CreateTempFile(pkg: IPackage): Promise { @@ -159,7 +167,7 @@ export class PackageManager { }); } - private async DownloadPackageWithRetries(pkg: IPackage, tmpResult: tmp.SyncResult): Promise { + private async DownloadPackageWithRetries(pkg: IPackage, tmpResult: tmp.SyncResult, progress: vscode.Progress<{message?: string; increment?: number}>): Promise { pkg.tmpFile = tmpResult; let success: boolean = false; @@ -170,7 +178,7 @@ export class PackageManager { // Retry the download at most MAX_RETRIES times with 2-32 seconds delay. do { try { - await this.DownloadFile(pkg.url, pkg, retryCount); + await this.DownloadFile(pkg.url, pkg, retryCount, progress); success = true; } catch (error) { retryCount += 1; @@ -208,7 +216,7 @@ export class PackageManager { } // reloadCpptoolsJson in main.ts uses ~25% of this function. - private DownloadFile(urlString: any, pkg: IPackage, delay: number): Promise { + private DownloadFile(urlString: any, pkg: IPackage, delay: number, progress: vscode.Progress<{message?: string; increment?: number}>): Promise { let parsedUrl: url.Url = url.parse(urlString); let proxyStrictSSL: any = vscode.workspace.getConfiguration().get("http.proxyStrictSSL", true); @@ -241,7 +249,7 @@ export class PackageManager { } else { redirectUrl = response.headers.location[0]; } - return resolve(this.DownloadFile(redirectUrl, pkg, 0)); + return resolve(this.DownloadFile(redirectUrl, pkg, 0, progress)); } else if (response.statusCode !== 200) { // Download failed - print error message let errorMessage: string = `failed (error code '${response.statusCode}')`; @@ -255,7 +263,6 @@ export class PackageManager { contentLength = response.headers['content-length'][0]; } let packageSize: number = parseInt(contentLength, 10); - let downloadedBytes: number = 0; let downloadPercentage: number = 0; let dots: number = 0; let tmpFile: fs.WriteStream = fs.createWriteStream(null, { fd: pkg.tmpFile.fd }); @@ -263,15 +270,6 @@ export class PackageManager { this.AppendChannel(`(${Math.ceil(packageSize / 1024)} KB) `); response.on('data', (data) => { - downloadedBytes += data.length; - - // Update status bar item with percentage - let newPercentage: number = Math.ceil(100 * (downloadedBytes / packageSize)); - if (newPercentage !== downloadPercentage) { - this.SetStatusTooltip(`Downloading package '${pkg.description}'... ${downloadPercentage}%`); - downloadPercentage = newPercentage; - } - // Update dots after package name in output console let newDots: number = Math.ceil(downloadPercentage / 5); if (newDots > dots) { @@ -305,11 +303,10 @@ export class PackageManager { }); } - private InstallPackage(pkg: IPackage): Promise { + private InstallPackage(pkg: IPackage, progressCount: string, progress: vscode.Progress<{message?: string; increment?: number}>): Promise { this.AppendLineChannel(`Installing package '${pkg.description}'`); - this.SetStatusText("$(desktop-download) Installing packages..."); - this.SetStatusTooltip(`Installing package '${pkg.description}'`); + progress.report({message: `Installing ${progressCount}: ${pkg.description}`}); return new Promise((resolve, reject) => { if (!pkg.tmpFile || pkg.tmpFile.fd === 0) { @@ -424,18 +421,4 @@ export class PackageManager { this.outputChannel.appendLine(text); } } - - private SetStatusText(text: string): void { - if (this.statusItem) { - this.statusItem.text = text; - this.statusItem.show(); - } - } - - private SetStatusTooltip(text: string): void { - if (this.statusItem) { - this.statusItem.tooltip = text; - this.statusItem.show(); - } - } } \ No newline at end of file From 1318cce09f238988981ab383c37c03f4bd8d8062 Mon Sep 17 00:00:00 2001 From: Andrew Wang Date: Fri, 13 Apr 2018 18:02:21 -0700 Subject: [PATCH 13/22] Adding snippet to fix pipeProgram in pipeTransport (#1829) * Adding snippet to fix pipeProgram in pipeTransport Now with VSCode 64 bit, WSL users need to use the correct pipeProgram in the correct folder. We will attempt to help users with this. * Fix tslint issue * Remove 64 bit check and check for relative path --- Extension/src/Debugger/attachToProcess.ts | 40 +++++++++++++-- .../src/Debugger/configurationProvider.ts | 26 ++++++++++ Extension/src/Debugger/configurations.ts | 3 +- Extension/src/Debugger/utils.ts | 49 +++++++++++++++++++ 4 files changed, 113 insertions(+), 5 deletions(-) create mode 100644 Extension/src/Debugger/utils.ts diff --git a/Extension/src/Debugger/attachToProcess.ts b/Extension/src/Debugger/attachToProcess.ts index c9e0f3f2f3..65b5230b65 100644 --- a/Extension/src/Debugger/attachToProcess.ts +++ b/Extension/src/Debugger/attachToProcess.ts @@ -3,10 +3,15 @@ * See 'LICENSE' in the project root for license information. * ------------------------------------------------------------------------------------------ */ -import * as vscode from 'vscode'; import { execChildProcess } from '../common'; import { PsProcessParser } from './nativeAttach'; + +import * as debugUtils from './utils'; +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; import * as util from '../common'; +import * as vscode from 'vscode'; export interface AttachItem extends vscode.QuickPickItem { id: string; @@ -49,20 +54,47 @@ export class RemoteAttachPicker { private _channel: vscode.OutputChannel = null; - public ShowAttachEntries(args: any): Promise { + public ShowAttachEntries(config: any): Promise { return util.isExtensionReady().then(ready => { if (!ready) { util.displayExtensionNotReadyPrompt(); } else { this._channel.clear(); - let pipeTransport: any = args ? args.pipeTransport : null; + let pipeTransport: any = config ? config.pipeTransport : null; if (pipeTransport === null) { return Promise.reject(new Error("Chosen debug configuration does not contain pipeTransport")); } - let pipeProgram: string = pipeTransport.pipeProgram; + let pipeProgram: string = null; + + if (os.platform() === 'win32' && + pipeTransport.pipeProgram && + !fs.existsSync(pipeTransport.pipeProgram)) { + const pipeProgramStr: string = pipeTransport.pipeProgram.toLowerCase().trim(); + const expectedArch: debugUtils.ArchType = debugUtils.ArchType[process.arch]; + + // Check for pipeProgram + if (!fs.existsSync(config.pipeTransport.pipeProgram)) { + pipeProgram = debugUtils.ArchitectureReplacer.checkAndReplaceWSLPipeProgram(pipeProgramStr, expectedArch); + } + + // If pipeProgram does not get replaced and there is a pipeCwd, concatenate with pipeProgramStr and attempt to replace. + if (!pipeProgram && config.pipeTransport.pipeCwd) { + const pipeCwdStr: string = config.pipeTransport.pipeCwd.toLowerCase().trim(); + const newPipeProgramStr: string = path.join(pipeCwdStr, pipeProgramStr); + + if (!fs.existsSync(newPipeProgramStr)) { + pipeProgram = debugUtils.ArchitectureReplacer.checkAndReplaceWSLPipeProgram(newPipeProgramStr, expectedArch); + } + } + } + + if (!pipeProgram) { + pipeProgram = pipeTransport.pipeProgram; + } + let pipeArgs: string[] = pipeTransport.pipeArgs; let argList: string = RemoteAttachPicker.createArgumentList(pipeArgs); diff --git a/Extension/src/Debugger/configurationProvider.ts b/Extension/src/Debugger/configurationProvider.ts index 622d5a160f..93b31f8e6c 100644 --- a/Extension/src/Debugger/configurationProvider.ts +++ b/Extension/src/Debugger/configurationProvider.ts @@ -3,8 +3,11 @@ * See 'LICENSE' in the project root for license information. * ------------------------------------------------------------------------------------------ */ +import * as debugUtils from './utils'; import * as os from 'os'; +import * as path from 'path'; import * as vscode from 'vscode'; + import { IConfiguration, IConfigurationSnippet, DebuggerType, MIConfigurations, WindowsConfigurations, WSLConfigurations, PipeTransportConfigurations } from './configurations'; import { parse } from 'jsonc-parser'; @@ -34,6 +37,29 @@ abstract class CppConfigurationProvider implements vscode.DebugConfigurationProv return undefined; } + // Modify WSL config for OpenDebugAD7 + if (os.platform() === 'win32' && + config.pipeTransport && + config.pipeTransport.pipeProgram) { + let replacedPipeProgram: string = null; + const pipeProgramStr: string = config.pipeTransport.pipeProgram.toLowerCase().trim(); + + // OpenDebugAD7 is a 32-bit process. Make sure the WSL pipe transport is using the correct program. + replacedPipeProgram = debugUtils.ArchitectureReplacer.checkAndReplaceWSLPipeProgram(pipeProgramStr, debugUtils.ArchType.ia32); + + // If pipeProgram does not get replaced and there is a pipeCwd, concatenate with pipeProgramStr and attempt to replace. + if (!replacedPipeProgram && !path.isAbsolute(pipeProgramStr) && config.pipeTransport.pipeCwd) { + const pipeCwdStr: string = config.pipeTransport.pipeCwd.toLowerCase().trim(); + const newPipeProgramStr: string = path.join(pipeCwdStr, pipeProgramStr); + + replacedPipeProgram = debugUtils.ArchitectureReplacer.checkAndReplaceWSLPipeProgram(newPipeProgramStr, debugUtils.ArchType.ia32); + } + + if (replacedPipeProgram) { + config.pipeTransport.pipeProgram = replacedPipeProgram; + } + } + return config; } } diff --git a/Extension/src/Debugger/configurations.ts b/Extension/src/Debugger/configurations.ts index aca5382647..6a5e5322aa 100644 --- a/Extension/src/Debugger/configurations.ts +++ b/Extension/src/Debugger/configurations.ts @@ -218,7 +218,8 @@ export class WindowsConfigurations extends Configuration { } export class WSLConfigurations extends Configuration { - public bashPipeProgram = "C:\\\\Windows\\\\sysnative\\\\bash.exe"; + // Detects if the current VSCode is 32-bit and uses the correct bash.exe + public bashPipeProgram = process.arch === 'ia32' ? "${env:windir}\\\\sysnative\\\\bash.exe" : "${env:windir}\\\\system32\\\\bash.exe"; public GetLaunchConfiguration(): IConfigurationSnippet { let name: string = `(${this.MIMode}) Bash on Windows Launch`; diff --git a/Extension/src/Debugger/utils.ts b/Extension/src/Debugger/utils.ts new file mode 100644 index 0000000000..ef8b29f8ea --- /dev/null +++ b/Extension/src/Debugger/utils.ts @@ -0,0 +1,49 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. + * See 'LICENSE' in the project root for license information. + * ------------------------------------------------------------------------------------------ */ + +export enum ArchType { + ia32, + x64 +} + +export class ArchitectureReplacer { + public static checkAndReplaceWSLPipeProgram(pipeProgramStr: string, expectedArch: ArchType): string { + let replacedPipeProgram: string = null; + const winDir: string = process.env.WINDIR ? process.env.WINDIR.toLowerCase() : null; + const winDirAltDirSep: string = process.env.WINDIR ? process.env.WINDIR.replace('\\', '/').toLowerCase() : null; + const winDirEnv: string = "${env:windir}"; + + if (winDir && winDirAltDirSep && (pipeProgramStr.indexOf(winDir) === 0 || pipeProgramStr.indexOf(winDirAltDirSep) === 0 || pipeProgramStr.indexOf(winDirEnv) === 0)) { + if (expectedArch === ArchType.x64) { + const pathSep: string = ArchitectureReplacer.checkForFolderInPath(pipeProgramStr, "sysnative"); + if (pathSep) { + // User has sysnative but we expect 64 bit. Should be using System32 since sysnative is a 32bit concept. + replacedPipeProgram = pipeProgramStr.replace(`${pathSep}sysnative${pathSep}`, `${pathSep}system32${pathSep}`); + } + } else if (expectedArch === ArchType.ia32) { + const pathSep: string = ArchitectureReplacer.checkForFolderInPath(pipeProgramStr, "system32"); + if (pathSep) { + // User has System32 but we expect 32 bit. Should be using sysnative + replacedPipeProgram = pipeProgramStr.replace(`${pathSep}system32${pathSep}`, `${pathSep}sysnative${pathSep}`); + } + } + } + + return replacedPipeProgram; + } + + // Checks to see if the folder name is in the path using both win and unix style path seperators. + // Returns the path seperator it detected if the folder is in the path. + // Or else it returns empty string to indicate it did not find it in the path. + public static checkForFolderInPath(path: string, folder: string): string { + if (path.indexOf(`/${folder}/`) >= 0) { + return '/'; + } else if (path.indexOf(`\\${folder}\\`) >= 0) { + return '\\'; + } + + return ""; + } +} \ No newline at end of file From 91e4f64f3de46259aacf43c114b9743d01631f18 Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Mon, 16 Apr 2018 08:44:25 -0700 Subject: [PATCH 14/22] Update MinGW.md (#1841) --- Documentation/LanguageServer/MinGW.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/LanguageServer/MinGW.md b/Documentation/LanguageServer/MinGW.md index 6e93cf3e5d..8c5ea278b8 100644 --- a/Documentation/LanguageServer/MinGW.md +++ b/Documentation/LanguageServer/MinGW.md @@ -74,3 +74,5 @@ For C projects, simply remove the C++ lines: ``` With these configurations, you should be all set up to use the new IntelliSense engine for linting, memberlist autocomplete, and quick info (tooltips). Add `"C_Cpp.intelliSenseEngine": "Default"` to your **settings.json** file to try out the new IntelliSense engine. + +UPDATE: Starting with 0.16.1, setting the `compilerPath` property to the full path to your MinGW compiler should set all the compiler includes and defines automatically, but the `browse.path` setting still needs to be set manually. From 136091e8ce09dfaa429c8bac72368f53e0561429 Mon Sep 17 00:00:00 2001 From: Bob Brown Date: Tue, 17 Apr 2018 13:32:24 -0700 Subject: [PATCH 15/22] Updates to MinGW documentation (#1844) * documentation update for mingw --- Documentation/LanguageServer/MinGW.md | 53 ++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/Documentation/LanguageServer/MinGW.md b/Documentation/LanguageServer/MinGW.md index 8c5ea278b8..3c5f8bb7a7 100644 --- a/Documentation/LanguageServer/MinGW.md +++ b/Documentation/LanguageServer/MinGW.md @@ -1,12 +1,49 @@ For developers using MinGW on Windows, we recommend you start with the following **c_cpp_properties.json** template. Select "C/Cpp: Edit Configurations" from the command palette to create this file if you haven't already. -Note that you may have to change the MinGW version number to match what you have installed. Eg. `C:/MinGW/lib/gcc/mingw32/5.3.0/` instead of `C:/MinGW/lib/gcc/mingw32/6.3.0/`. +## With extension version 0.16.1 and higher: + +Starting with version 0.16.1, if you set the `compilerPath` property and change `intelliSenseMode` to `clang-x64`, you no longer need to copy the system include path or defines to `includePath` and `defines` to enable IntelliSense to work properly. However, `browse.path` still needs to be updated manually to add the system include paths to enable code browsing for system headers. For example: ```json { "configurations": [ { - "name": "Win32", + "name": "MinGW", + "intelliSenseMode": "clang-x64", + "compilerPath": "C:/MinGW/bin/gcc.exe", + "includePath": [ + "${workspaceRoot}", + ], + "defines": [ + "_DEBUG" + ], + "browse": { + "path": [ + "C:/MinGW/lib/gcc/mingw32/6.3.0/include", + "C:/MinGW/lib/gcc/mingw32/6.3.0/include-fixed", + "C:/MinGW/include/*" + "${workspaceRoot}", + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + } + } + ], + "version": 3 +} +``` + +Note that the `browse.path` setting is not automatically updated at this time and you may have to change the MinGW version number to match what you have installed. If you are using a different MinGW distribution, the values for `compilerPath` and `browse.path` will likely be different than what is written here. + +## With extension version 0.16.0 and earlier: + +In earlier versions of the extension, the `includePath` and a some system defines need to be set in order for IntelliSense to work properly. Note that you may have to change the MinGW version number to match what you have installed. Eg. `C:/MinGW/lib/gcc/mingw32/5.3.0/` instead of `C:/MinGW/lib/gcc/mingw32/6.3.0/`. + +```json +{ + "configurations": [ + { + "name": "MinGW", "intelliSenseMode": "clang-x64", "includePath": [ "${workspaceRoot}", @@ -19,7 +56,6 @@ Note that you may have to change the MinGW version number to match what you have ], "defines": [ "_DEBUG", - "UNICODE", "__GNUC__=6", "__cdecl=__attribute__((__cdecl__))" ], @@ -28,6 +64,7 @@ Note that you may have to change the MinGW version number to match what you have "C:/MinGW/lib/gcc/mingw32/6.3.0/include", "C:/MinGW/lib/gcc/mingw32/6.3.0/include-fixed", "C:/MinGW/include/*" + "${workspaceRoot}", ], "limitSymbolsToIncludedHeaders": true, "databaseFilename": "" @@ -37,7 +74,7 @@ Note that you may have to change the MinGW version number to match what you have } ``` -The `includePath` above includes the system header paths that gcc uses in version 6.3.0 for C++ projects and matches the output of `gcc -v -E -x c++ -`. The `intelliSenseMode` should be set to **"clang-x64"** to get MinGW projects to work properly with IntelliSense. The `__GNUC__=#` define should match the major version of the toolchain in your installation (6 in this example). +The `includePath` above includes the system header paths that gcc uses in version 6.3.0 for C++ projects and matches the output of `"gcc -v -E -x c++ nul"`. The `intelliSenseMode` should be set to **"clang-x64"** to get MinGW projects to work properly with IntelliSense. The `__GNUC__=#` define should match the major version of the toolchain in your installation (6 in this example). For C projects, simply remove the C++ lines: @@ -45,7 +82,7 @@ For C projects, simply remove the C++ lines: { "configurations": [ { - "name": "Win32", + "name": "MinGW", "intelliSenseMode": "clang-x64", "includePath": [ "${workspaceRoot}", @@ -55,7 +92,6 @@ For C projects, simply remove the C++ lines: ], "defines": [ "_DEBUG", - "UNICODE", "__GNUC__=6", "__cdecl=__attribute__((__cdecl__))" ], @@ -64,6 +100,7 @@ For C projects, simply remove the C++ lines: "C:/MinGW/lib/gcc/mingw32/6.3.0/include", "C:/MinGW/lib/gcc/mingw32/6.3.0/include-fixed", "C:/MinGW/include/*" + "${workspaceRoot}", ], "limitSymbolsToIncludedHeaders": true, "databaseFilename": "" @@ -72,7 +109,3 @@ For C projects, simply remove the C++ lines: ] } ``` - -With these configurations, you should be all set up to use the new IntelliSense engine for linting, memberlist autocomplete, and quick info (tooltips). Add `"C_Cpp.intelliSenseEngine": "Default"` to your **settings.json** file to try out the new IntelliSense engine. - -UPDATE: Starting with 0.16.1, setting the `compilerPath` property to the full path to your MinGW compiler should set all the compiler includes and defines automatically, but the `browse.path` setting still needs to be set manually. From 999aad5633d8b4f46205b52dcf0f6163bf2c10c8 Mon Sep 17 00:00:00 2001 From: Bob Brown Date: Wed, 18 Apr 2018 14:40:01 -0700 Subject: [PATCH 16/22] fix typo --- Documentation/LanguageServer/c_cpp_properties.json.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/LanguageServer/c_cpp_properties.json.md b/Documentation/LanguageServer/c_cpp_properties.json.md index a391b01be9..a4742625dc 100644 --- a/Documentation/LanguageServer/c_cpp_properties.json.md +++ b/Documentation/LanguageServer/c_cpp_properties.json.md @@ -81,4 +81,4 @@ When true, the Tag Parser will only parse code files that have been directly or indirectly included by a source file in `${workspaceRoot}`. When false, the Tag Parser will parse all code files found in the paths specified in the **path** list. * #### `databaseFilename` - When set, this instructs the extension to save the Tag Parser's symbol database somewhere other than the workspace's default storage location. If a relative path is specified, it will be made relative to the workspace's default storage location, not the workspace folder itself. The `${workspaceRoot}` variable can be used to specify a path relative to the workspace folder (e.g. `$[workspaceRoot}/.vscode/browse.vc.db`) + When set, this instructs the extension to save the Tag Parser's symbol database somewhere other than the workspace's default storage location. If a relative path is specified, it will be made relative to the workspace's default storage location, not the workspace folder itself. The `${workspaceRoot}` variable can be used to specify a path relative to the workspace folder (e.g. `${workspaceRoot}/.vscode/browse.vc.db`) From 313ce1928fd3c7c0e96f07fd62cf04a885935dcf Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Thu, 19 Apr 2018 14:52:40 -0700 Subject: [PATCH 17/22] Update c_cpp_properties.json.md (#1851) --- Documentation/LanguageServer/c_cpp_properties.json.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/LanguageServer/c_cpp_properties.json.md b/Documentation/LanguageServer/c_cpp_properties.json.md index a4742625dc..23a2848a38 100644 --- a/Documentation/LanguageServer/c_cpp_properties.json.md +++ b/Documentation/LanguageServer/c_cpp_properties.json.md @@ -78,7 +78,7 @@ This list of paths will be used by the Tag Parser to search for headers included by your source files. The Tag Parser will automatically search all subfolders in these paths unless the path ends with a `/*` or `\*`. For example, `/usr/include` directs the Tag Parser to search the `include` folder and its subfolders for headers while `/usr/include/*` directs the Tag Parser not to look in any subfolders of `/usr/include`. * #### `limitSymbolsToIncludedHeaders` - When true, the Tag Parser will only parse code files that have been directly or indirectly included by a source file in `${workspaceRoot}`. When false, the Tag Parser will parse all code files found in the paths specified in the **path** list. + When true, the Tag Parser will only parse code files that have been directly or indirectly included by a source file in `${workspaceFolder}`. When false, the Tag Parser will parse all code files found in the paths specified in the **path** list. * #### `databaseFilename` - When set, this instructs the extension to save the Tag Parser's symbol database somewhere other than the workspace's default storage location. If a relative path is specified, it will be made relative to the workspace's default storage location, not the workspace folder itself. The `${workspaceRoot}` variable can be used to specify a path relative to the workspace folder (e.g. `${workspaceRoot}/.vscode/browse.vc.db`) + When set, this instructs the extension to save the Tag Parser's symbol database somewhere other than the workspace's default storage location. If a relative path is specified, it will be made relative to the workspace's default storage location, not the workspace folder itself. The `${workspaceFolder}` variable can be used to specify a path relative to the workspace folder (e.g. `${workspaceFolder}/.vscode/browse.vc.db`) From 8f21515eeb31b0ffb73ffd4ae62286243a82a9e3 Mon Sep 17 00:00:00 2001 From: Bob Brown Date: Thu, 19 Apr 2018 15:20:20 -0700 Subject: [PATCH 18/22] Add VS Code settings for configuration properties (#1848) * Add VS Code settings for configuration properties --- Extension/CHANGELOG.md | 6 + Extension/package.json | 148 ++++++++++ Extension/src/LanguageServer/client.ts | 11 +- .../src/LanguageServer/configurations.ts | 264 ++++++++++-------- Extension/src/LanguageServer/extension.ts | 20 +- Extension/src/LanguageServer/settings.ts | 13 + Extension/src/main.ts | 3 +- .../languageServer.integration.test.ts | 56 +++- 8 files changed, 399 insertions(+), 122 deletions(-) diff --git a/Extension/CHANGELOG.md b/Extension/CHANGELOG.md index 95df84f674..0621bdae28 100644 --- a/Extension/CHANGELOG.md +++ b/Extension/CHANGELOG.md @@ -1,5 +1,11 @@ # C/C++ for Visual Studio Code Change Log +## Version 0.17.0: May 7, 2018 +* Auto-complete for headers after typing `#include`. [#802](https://github.com/Microsoft/vscode-cpptools/issues/802) +* Configuration improvements. [#1338](https://github.com/Microsoft/vscode-cpptools/issues/1338) + * potentially addresses: [#368](https://github.com/Microsoft/vscode-cpptools/issues/368), [#410](https://github.com/Microsoft/vscode-cpptools/issues/410), [#1229](https://github.com/Microsoft/vscode-cpptools/issues/1229), [#1270](https://github.com/Microsoft/vscode-cpptools/issues/) +* Add support for querying system includes/defines from compilers in WSL environment. + ## Version 0.16.1: March 30, 2018 * Fix random deadlock caused by logging code on Linux/Mac. [#1759](https://github.com/Microsoft/vscode-cpptools/issues/1759) * Fix compiler from `compileCommands` not being queried for includes/defines if `compilerPath` isn't set on Windows. [#1754](https://github.com/Microsoft/vscode-cpptools/issues/1754) diff --git a/Extension/package.json b/Extension/package.json index 69ffcaea58..0829f547fc 100644 --- a/Extension/package.json +++ b/Extension/package.json @@ -229,6 +229,154 @@ }, "description": "Defines the editor behavior for when the Enter key is pressed inside a multiline or single line comment block.", "scope": "resource" + }, + "C_Cpp.default.includePath": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + }, + "default": null, + "description": "The value to use in a configuration if \"includePath\" is not specified, or the values to insert if \"${default}\" is present in \"includePath\".", + "scope": "resource" + }, + "C_Cpp.default.defines": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + }, + "default": null, + "description": "The value to use in a configuration if \"defines\" is not specified, or the values to insert if \"${default}\" is present in \"defines\".", + "scope": "resource" + }, + "C_Cpp.default.macFrameworkPath": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + }, + "default": null, + "description": "The value to use in a configuration if \"macFrameworkPath\" is not specified, or the values to insert if \"${default}\" is present in \"macFrameworkPath\".", + "scope": "resource" + }, + "C_Cpp.default.compileCommands": { + "type": [ + "string", + "null" + ], + "default": null, + "description": "The value to use in a configuration if \"compileCommands\" is not specified, or the values to insert if \"${default}\" is present in \"compileCommands\".", + "scope": "resource" + }, + "C_Cpp.default.forcedInclude": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + }, + "default": null, + "description": "The value to use in a configuration if \"forcedInclude\" is not specified, or the values to insert if \"${default}\" is present in \"forcedInclude\".", + "scope": "resource" + }, + "C_Cpp.default.intelliSenseMode": { + "type": [ + "string", + "null" + ], + "enum": [ + "msvc-x64", + "clang-x64" + ], + "default": null, + "description": "The value to use in a configuration if \"intelliSenseMode\" is not specified, or the values to insert if \"${default}\" is present in \"intelliSenseMode\".", + "scope": "resource" + }, + "C_Cpp.default.compilerPath": { + "type": [ + "string", + "null" + ], + "default": null, + "description": "The value to use in a configuration if \"compilerPath\" is not specified, or the values to insert if \"${default}\" is present in \"compilerPath\".", + "scope": "resource" + }, + "C_Cpp.default.cStandard": { + "type": [ + "string", + "null" + ], + "enum": [ + "c89", + "c99", + "c11" + ], + "default": null, + "description": "The value to use in a configuration if \"cStandard\" is not specified, or the values to insert if \"${default}\" is present in \"cStandard\".", + "scope": "resource" + }, + "C_Cpp.default.cppStandard": { + "type": [ + "string", + "null" + ], + "enum": [ + "c++98", + "c++03", + "c++11", + "c++14", + "c++17" + ], + "default": null, + "description": "The value to use in a configuration if \"cppStandard\" is not specified, or the values to insert if \"${default}\" is present in \"cppStandard\".", + "scope": "resource" + }, + "C_Cpp.default.browse.path": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + }, + "default": null, + "description": "The value to use in a configuration if \"browse.path\" is not specified, or the values to insert if \"${default}\" is present in \"browse.path\".", + "scope": "resource" + }, + "C_Cpp.default.browse.databaseFilename": { + "type": [ + "string", + "null" + ], + "default": null, + "description": "The value to use in a configuration if \"browse.databaseFilename\" is not specified, or the values to insert if \"${default}\" is present in \"browse.databaseFilename\".", + "scope": "resource" + }, + "C_Cpp.default.browse.limitSymbolsToIncludedHeaders": { + "type": "boolean", + "default": true, + "description": "The value to use in a configuration if \"browse.limitSymbolsToIncludedHeaders\" is not specified, or the values to insert if \"${default}\" is present in \"browse.limitSymbolsToIncludedHeaders\".", + "scope": "resource" + }, + "C_Cpp.default.systemIncludePath": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + }, + "default": null, + "description": "The value to use for the system include path. If set, it overrides the system include path acquired via \"compilerPath\" and \"compileCommands\" settings.", + "scope": "resource" } } }, diff --git a/Extension/src/LanguageServer/client.ts b/Extension/src/LanguageServer/client.ts index f3b67f54b0..0dfbfc2537 100644 --- a/Extension/src/LanguageServer/client.ts +++ b/Extension/src/LanguageServer/client.ts @@ -145,13 +145,13 @@ function collectSettingsForTelemetry(filter: (key: string, val: string, settings if (filter(key, val, settings)) { previousCppSettings[key] = val; - switch (String(key).toLowerCase()) { + switch (key.toLowerCase()) { case "clang_format_path": { continue; } case "clang_format_style": case "clang_format_fallbackstyle": { - let newKey: string = String(key) + "2"; + let newKey: string = key + "2"; if (val) { switch (String(val).toLowerCase()) { case "visual studio": @@ -177,6 +177,9 @@ function collectSettingsForTelemetry(filter: (key: string, val: string, settings break; } default: { + if (key.startsWith("default.")) { + continue; // Don't log c_cpp_properties.json defaults since they may contain PII. + } result[key] = String(previousCppSettings[key]); break; } @@ -317,7 +320,7 @@ class DefaultClient implements Client { ui.bind(this); this.onReadyPromise = languageClient.onReady().then(() => { - this.configuration = new configs.CppProperties(this.RootPath); + this.configuration = new configs.CppProperties(this.RootUri); this.configuration.ConfigurationsChanged((e) => this.onConfigurationsChanged(e)); this.configuration.SelectionChanged((e) => this.onSelectedConfigurationChanged(e)); this.configuration.CompileCommandsChanged((e) => this.onCompileCommandsChanged(e)); @@ -392,6 +395,7 @@ class DefaultClient implements Client { tab_size: other.editorTabSize, intelliSenseEngine: settings.intelliSenseEngine, intelliSenseEngineFallback: settings.intelliSenseEngineFallback, + defaultSystemIncludePath: settings.defaultSystemIncludePath, autocomplete: settings.autoComplete, errorSquiggles: settings.errorSquiggles, dimInactiveRegions: settings.dimInactiveRegions, @@ -446,6 +450,7 @@ class DefaultClient implements Client { if (changedSettings["commentContinuationPatterns"]) { updateLanguageConfigurations(); } + this.configuration.onDidChangeSettings(); telemetry.logLanguageServerEvent("CppSettingsChange", changedSettings, null); } } diff --git a/Extension/src/LanguageServer/configurations.ts b/Extension/src/LanguageServer/configurations.ts index 74f3066152..7641e57917 100644 --- a/Extension/src/LanguageServer/configurations.ts +++ b/Extension/src/LanguageServer/configurations.ts @@ -9,82 +9,32 @@ import * as fs from "fs"; import * as vscode from 'vscode'; import * as util from '../common'; import { PersistentFolderState } from './persistentState'; +import { CppSettings } from './settings'; const configVersion: number = 3; -let defaultSettings: string = `{ - "configurations": [ - { - "name": "Mac", - "includePath": [ - "/usr/include", - "/usr/local/include", - "$\{workspaceFolder\}" - ], - "defines": [], - "intelliSenseMode": "clang-x64", - "browse": { - "path": [ - "/usr/include", - "/usr/local/include", - "$\{workspaceFolder\}" - ], - "limitSymbolsToIncludedHeaders": true, - "databaseFilename": "" - }, - "macFrameworkPath": [ - "/System/Library/Frameworks", - "/Library/Frameworks" - ] - }, - { - "name": "Linux", - "includePath": [ - "/usr/include", - "/usr/local/include", - "$\{workspaceFolder\}" - ], - "defines": [], - "intelliSenseMode": "clang-x64", - "browse": { - "path": [ - "/usr/include", - "/usr/local/include", - "$\{workspaceFolder\}" - ], - "limitSymbolsToIncludedHeaders": true, - "databaseFilename": "" - } - }, - { - "name": "Win32", - "includePath": [ - "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include", - "$\{workspaceFolder\}" - ], - "defines": [ - "_DEBUG", - "UNICODE", - "_UNICODE" - ], - "intelliSenseMode": "msvc-x64", - "browse": { - "path": [ - "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include/*", - "$\{workspaceFolder\}" - ], - "limitSymbolsToIncludedHeaders": true, - "databaseFilename": "" - } - } - ], - "version": ${configVersion} +// No properties are set in the config since we want to apply vscode settings first (if applicable). +// That code won't trigger if another value is already set. +// The property defaults are moved down to applyDefaultIncludePathsAndFrameworks. +function getDefaultConfig(): Configuration { + if (process.platform === 'darwin') { + return { name: "Mac", browse: {} }; + } else if (process.platform === 'win32') { + return { name: "Win32", browse: {} }; + } else { + return { name: "Linux", browse: {} }; + } } -`; -export interface Browse { - path?: string[]; - limitSymbolsToIncludedHeaders?: boolean; - databaseFilename?: string; +function getDefaultCppProperties(): ConfigurationJson { + return { + configurations: [getDefaultConfig()], + version: configVersion + }; +} + +interface ConfigurationJson { + configurations: Configuration[]; + version: number; } export interface Configuration { @@ -101,20 +51,23 @@ export interface Configuration { browse?: Browse; } +export interface Browse { + path?: string[]; + limitSymbolsToIncludedHeaders?: boolean | string; + databaseFilename?: string; +} + export interface CompilerDefaults { compilerPath: string; cStandard: string; cppStandard: string; includes: string[]; frameworks: string[]; -} - -interface ConfigurationJson { - configurations: Configuration[]; - version: number; + intelliSenseMode: string; } export class CppProperties { + private rootUri: vscode.Uri; private propertiesFile: vscode.Uri = null; private readonly configFolder: string; private configurationJson: ConfigurationJson = null; @@ -127,21 +80,24 @@ export class CppProperties { private defaultCppStandard: string = null; private defaultIncludes: string[] = null; private defaultFrameworks: string[] = null; + private defaultIntelliSenseMode: string = null; private readonly configurationGlobPattern: string = "**/c_cpp_properties.json"; // TODO: probably should be a single file, not all files... private disposables: vscode.Disposable[] = []; private configurationsChanged = new vscode.EventEmitter(); private selectionChanged = new vscode.EventEmitter(); private compileCommandsChanged = new vscode.EventEmitter(); - // Any time the `defaultSettings` are parsed and assigned to `this.configurationJson`, + // Any time the default settings are parsed and assigned to `this.configurationJson`, // we want to track when the default includes have been added to it. private configurationIncomplete: boolean = true; - constructor(rootPath: string) { - console.assert(rootPath !== undefined); + constructor(rootUri: vscode.Uri) { + console.assert(rootUri !== undefined); + this.rootUri = rootUri; + let rootPath: string = rootUri ? rootUri.fsPath : ""; this.currentConfigurationIndex = new PersistentFolderState("CppProperties.currentConfigurationIndex", -1, rootPath); this.configFolder = path.join(rootPath, ".vscode"); - + let configFilePath: string = path.join(this.configFolder, "c_cpp_properties.json"); if (fs.existsSync(configFilePath)) { this.propertiesFile = vscode.Uri.file(configFilePath); @@ -189,6 +145,7 @@ export class CppProperties { this.defaultCppStandard = compilerDefaults.cppStandard; this.defaultIncludes = compilerDefaults.includes; this.defaultFrameworks = compilerDefaults.frameworks; + this.defaultIntelliSenseMode = compilerDefaults.intelliSenseMode; // defaultPaths is only used when there isn't a c_cpp_properties.json, but we don't send the configuration changed event // to the language server until the default include paths and frameworks have been sent. @@ -207,8 +164,19 @@ export class CppProperties { this.compileCommandsChanged.fire(path); } + public onDidChangeSettings(): void { + // Default settings may have changed in a way that affects the configuration. + // Just send another message since the language server will sort out whether anything important changed or not. + if (!this.propertiesFile) { + this.resetToDefaultSettings(true); + this.handleConfigurationChange(); + } else if (!this.configurationIncomplete) { + this.handleConfigurationChange(); + } + } + private resetToDefaultSettings(resetIndex: boolean): void { - this.configurationJson = JSON.parse(defaultSettings); + this.configurationJson = getDefaultCppProperties(); if (resetIndex || this.CurrentConfiguration < 0 || this.CurrentConfiguration >= this.configurationJson.configurations.length) { this.currentConfigurationIndex.Value = this.getConfigIndexForPlatform(this.configurationJson); @@ -218,19 +186,40 @@ export class CppProperties { private applyDefaultIncludePathsAndFrameworks(): void { if (this.configurationIncomplete && this.defaultIncludes && this.defaultFrameworks) { - this.configurationJson.configurations[this.CurrentConfiguration].includePath = this.defaultIncludes; - this.configurationJson.configurations[this.CurrentConfiguration].browse.path = this.defaultIncludes; - if (process.platform === 'darwin') { - this.configurationJson.configurations[this.CurrentConfiguration].macFrameworkPath = this.defaultFrameworks; + let configuration: Configuration = this.configurationJson.configurations[this.CurrentConfiguration]; + let settings: CppSettings = new CppSettings(this.rootUri); + + // Anything that has a vscode setting for it will be resolved in updateServerOnFolderSettingsChange. + // So if a property is currently unset, but has a vscode setting, don't set it yet, otherwise the linkage + // to the setting will be lost if this configuration is saved into a c_cpp_properties.json file. + + // Only add settings from the default compiler if user hasn't explicitly set the corresponding VS Code setting. + + if (!settings.defaultIncludePath) { + // We don't add system includes to the includePath anymore. The language server has this information. + configuration.includePath = ["${workspaceFolder}"]; + } + if (!settings.defaultDefines) { + configuration.defines = (process.platform === 'win32') ? ["_DEBUG", "UNICODE", "_UNICODE"] : []; + } + if (!settings.defaultBrowsePath) { + // We don't add system includes to the browse.path anymore. The language server has this information. + configuration.browse.path = ["${workspaceFolder}"]; + } + if (!settings.defaultMacFrameworkPath && process.platform === 'darwin') { + configuration.macFrameworkPath = this.defaultFrameworks; + } + if (!settings.defaultCompilerPath && this.defaultCompilerPath) { + configuration.compilerPath = this.defaultCompilerPath; } - if (this.defaultCompilerPath) { - this.configurationJson.configurations[this.CurrentConfiguration].compilerPath = this.defaultCompilerPath; + if (!settings.defaultCStandard && this.defaultCStandard) { + configuration.cStandard = this.defaultCStandard; } - if (this.defaultCStandard) { - this.configurationJson.configurations[this.CurrentConfiguration].cStandard = this.defaultCStandard; + if (!settings.defaultCppStandard && this.defaultCppStandard) { + configuration.cppStandard = this.defaultCppStandard; } - if (this.defaultCppStandard) { - this.configurationJson.configurations[this.CurrentConfiguration].cppStandard = this.defaultCppStandard; + if (!settings.defaultIntelliSenseMode) { + configuration.intelliSenseMode = this.defaultIntelliSenseMode; } this.configurationIncomplete = false; } @@ -284,7 +273,11 @@ export class CppProperties { public addToIncludePathCommand(path: string): void { this.handleConfigurationEditCommand((document: vscode.TextDocument) => { + this.parsePropertiesFile(); // Clear out any modifications we may have made internally. let config: Configuration = this.configurationJson.configurations[this.CurrentConfiguration]; + if (config.includePath === undefined) { + config.includePath = ["${default}"]; + } config.includePath.splice(config.includePath.length, 0, path); fs.writeFileSync(this.propertiesFile.fsPath, JSON.stringify(this.configurationJson, null, 4)); this.updateServerOnFolderSettingsChange(); @@ -300,38 +293,79 @@ export class CppProperties { this.onSelectionChanged(); } - private resolveAndSplit(paths: string[] | undefined): string[] { + private resolveDefaults(entries: string[], defaultValue: string[]): string[] { + let result: string[] = []; + entries.forEach(entry => { + if (entry === "${default}") { + result = result.concat(defaultValue); + } else { + result.push(entry); + } + }); + return result; + } + + private resolveAndSplit(paths: string[] | undefined, defaultValue: string[]): string[] { let result: string[] = []; if (paths) { paths.forEach(entry => { let entries: string[] = util.resolveVariables(entry).split(";").filter(e => e); + entries = this.resolveDefaults(entries, defaultValue); result = result.concat(entries); }); } return result; } + private resolveVariables(input: string | boolean, defaultValue: string | boolean): string | boolean { + if (input === undefined || input === "${default}") { + input = defaultValue; + } + if (typeof input === "boolean") { + return input; + } + return util.resolveVariables(input); + } + + private updateConfiguration(property: string[], defaultValue: string[]): string[]; + private updateConfiguration(property: string, defaultValue: string): string; + private updateConfiguration(property: string | boolean, defaultValue: boolean): boolean; + private updateConfiguration(property, defaultValue): any { + if (typeof property === "string" || typeof defaultValue === "string") { + return this.resolveVariables(property, defaultValue); + } else if (typeof property === "boolean" || typeof defaultValue === "boolean") { + return this.resolveVariables(property, defaultValue); + } else if (property instanceof Array || defaultValue instanceof Array) { + if (property) { + return this.resolveAndSplit(property, defaultValue); + } else if (property === undefined && defaultValue) { + return this.resolveAndSplit(defaultValue, []); + } + } + return property; + } + private updateServerOnFolderSettingsChange(): void { + let settings: CppSettings = new CppSettings(this.rootUri); for (let i: number = 0; i < this.configurationJson.configurations.length; i++) { let configuration: Configuration = this.configurationJson.configurations[i]; - if (configuration.includePath) { - configuration.includePath = this.resolveAndSplit(configuration.includePath); - } - if (configuration.browse && configuration.browse.path) { - configuration.browse.path = this.resolveAndSplit(configuration.browse.path); - } - if (configuration.macFrameworkPath) { - configuration.macFrameworkPath = this.resolveAndSplit(configuration.macFrameworkPath); - } - if (configuration.forcedInclude) { - configuration.forcedInclude = this.resolveAndSplit(configuration.forcedInclude); - } - if (configuration.compileCommands) { - configuration.compileCommands = util.resolveVariables(configuration.compileCommands); - } - if (configuration.compilerPath) { - configuration.compilerPath = util.resolveVariables(configuration.compilerPath); + + configuration.includePath = this.updateConfiguration(configuration.includePath, settings.defaultIncludePath); + configuration.defines = this.updateConfiguration(configuration.defines, settings.defaultDefines); + configuration.macFrameworkPath = this.updateConfiguration(configuration.macFrameworkPath, settings.defaultMacFrameworkPath); + configuration.forcedInclude = this.updateConfiguration(configuration.forcedInclude, settings.defaultForcedInclude); + configuration.compileCommands = this.updateConfiguration(configuration.compileCommands, settings.defaultCompileCommands); + configuration.compilerPath = this.updateConfiguration(configuration.compilerPath, settings.defaultCompilerPath); + configuration.cStandard = this.updateConfiguration(configuration.cStandard, settings.defaultCStandard); + configuration.cppStandard = this.updateConfiguration(configuration.cppStandard, settings.defaultCppStandard); + configuration.intelliSenseMode = this.updateConfiguration(configuration.intelliSenseMode, settings.defaultIntelliSenseMode); + + if (!configuration.browse) { + configuration.browse = {}; } + configuration.browse.path = this.updateConfiguration(configuration.browse.path, settings.defaultBrowsePath); + configuration.browse.limitSymbolsToIncludedHeaders = this.updateConfiguration(configuration.browse.limitSymbolsToIncludedHeaders, settings.defaultLimitSymbolsToIncludedHeaders); + configuration.browse.databaseFilename = this.updateConfiguration(configuration.browse.databaseFilename, settings.defaultDatabaseFilename); } this.updateCompileCommandsFileWatchers(); @@ -456,23 +490,25 @@ export class CppProperties { this.configurationIncomplete = false; // Update intelliSenseMode, compilerPath, cStandard, and cppStandard with the defaults if they're missing. + // If VS Code settings exist for these properties, don't add them to c_cpp_properties.json let dirty: boolean = false; + let settings: CppSettings = new CppSettings(this.rootUri); for (let i: number = 0; i < this.configurationJson.configurations.length; i++) { let config: Configuration = this.configurationJson.configurations[i]; - if (config.intelliSenseMode === undefined) { + if (config.intelliSenseMode === undefined && !settings.defaultIntelliSenseMode) { dirty = true; config.intelliSenseMode = this.getIntelliSenseModeForPlatform(config.name); } // Don't set the default if compileCommands exist, until it is fixed to have the correct value. - if (config.compilerPath === undefined && this.defaultCompilerPath && !config.compileCommands) { + if (config.compilerPath === undefined && this.defaultCompilerPath && !config.compileCommands && !settings.defaultCompilerPath) { config.compilerPath = this.defaultCompilerPath; dirty = true; } - if (!config.cStandard && this.defaultCStandard) { + if (!config.cStandard && this.defaultCStandard && !settings.defaultCStandard) { config.cStandard = this.defaultCStandard; dirty = true; } - if (!config.cppStandard && this.defaultCppStandard) { + if (!config.cppStandard && this.defaultCppStandard && !settings.defaultCppStandard) { config.cppStandard = this.defaultCppStandard; dirty = true; } diff --git a/Extension/src/LanguageServer/extension.ts b/Extension/src/LanguageServer/extension.ts index e034760cac..a5966cbdb0 100644 --- a/Extension/src/LanguageServer/extension.ts +++ b/Extension/src/LanguageServer/extension.ts @@ -47,14 +47,28 @@ export function activate(activationEventOccurred: boolean): void { // Check if an activation event has already occurred. if (activationEventOccurred) { - return onActivationEvent(); + onActivationEvent(); + return; } - + + // handle "workspaceContains:/.vscode/c_cpp_properties.json" activation event. + if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0) { + for (let i: number = 0; i < vscode.workspace.workspaceFolders.length; ++i) { + let config: string = path.join(vscode.workspace.workspaceFolders[i].uri.fsPath, ".vscode/c_cpp_properties.json"); + if (fs.existsSync(config)) { + onActivationEvent(); + return; + } + } + } + + // handle "onLanguage:cpp" and "onLanguage:c" activation events. if (vscode.workspace.textDocuments !== undefined && vscode.workspace.textDocuments.length > 0) { for (let i: number = 0; i < vscode.workspace.textDocuments.length; ++i) { let document: vscode.TextDocument = vscode.workspace.textDocuments[i]; if (document.languageId === "cpp" || document.languageId === "c") { - return onActivationEvent(); + onActivationEvent(); + return; } } } diff --git a/Extension/src/LanguageServer/settings.ts b/Extension/src/LanguageServer/settings.ts index e09f31ef77..e41d088a3d 100644 --- a/Extension/src/LanguageServer/settings.ts +++ b/Extension/src/LanguageServer/settings.ts @@ -47,6 +47,19 @@ export class CppSettings extends Settings { public get workspaceParsingPriority(): boolean { return super.Section.get("workspaceParsingPriority"); } public get exclusionPolicy(): boolean { return super.Section.get("exclusionPolicy"); } public get commentContinuationPatterns(): (string | CommentPattern)[] { return super.Section.get<(string | CommentPattern)[]>("commentContinuationPatterns"); } + public get defaultIncludePath(): string[] { return super.Section.get("default.includePath"); } + public get defaultDefines(): string[] { return super.Section.get("default.defines"); } + public get defaultMacFrameworkPath(): string[] { return super.Section.get("default.macFrameworkPath"); } + public get defaultCompileCommands(): string { return super.Section.get("default.compileCommands"); } + public get defaultForcedInclude(): string[] { return super.Section.get("default.forcedInclude"); } + public get defaultIntelliSenseMode(): string { return super.Section.get("default.intelliSenseMode"); } + public get defaultCompilerPath(): string { return super.Section.get("default.compilerPath"); } + public get defaultCStandard(): string { return super.Section.get("default.cStandard"); } + public get defaultCppStandard(): string { return super.Section.get("default.cppStandard"); } + public get defaultBrowsePath(): string[] { return super.Section.get("default.browse.path"); } + public get defaultDatabaseFilename(): string { return super.Section.get("default.browse.databaseFilename"); } + public get defaultLimitSymbolsToIncludedHeaders(): boolean { return super.Section.get("default.browse.limitSymbolsToIncludedHeaders"); } + public get defaultSystemIncludePath(): string[] { return super.Section.get("default.systemIncludePath"); } public toggleSetting(name: string, value1: string, value2: string): void { let value: string = super.Section.get(name); diff --git a/Extension/src/main.ts b/Extension/src/main.ts index 3bb06dc9d6..0a085d0417 100644 --- a/Extension/src/main.ts +++ b/Extension/src/main.ts @@ -313,7 +313,8 @@ function rewriteManifest(): Promise { "onCommand:C_Cpp.ResumeParsing", "onCommand:C_Cpp.ShowParsingCommands", "onCommand:C_Cpp.TakeSurvey", - "onDebug" + "onDebug", + "workspaceContains:/.vscode/c_cpp_properties.json" ]; return util.writeFileText(util.getPackageJsonPath(), util.stringifyPackageJson(packageJson)); diff --git a/Extension/test/integrationTests/languageServer.integration.test.ts b/Extension/test/integrationTests/languageServer.integration.test.ts index 267dafbf67..29af501c06 100644 --- a/Extension/test/integrationTests/languageServer.integration.test.ts +++ b/Extension/test/integrationTests/languageServer.integration.test.ts @@ -6,6 +6,8 @@ import * as vscode from 'vscode'; import * as assert from 'assert'; import { getLanguageConfigFromPatterns } from '../../src/LanguageServer/languageConfig'; +import * as config from '../../src/LanguageServer/configurations'; +import { CppSettings } from '../../src/LanguageServer/settings'; suite("multiline comment setting tests", function() { suiteSetup(async function() { @@ -74,4 +76,56 @@ suite("multiline comment setting tests", function() { assert.deepEqual(rules, defaultSLRules); }); -}); \ No newline at end of file +}); + +/* +suite("configuration tests", function() { + suiteSetup(async function() { + let extension: vscode.Extension = vscode.extensions.getExtension("ms-vscode.cpptools"); + if (!extension.isActive) { + await extension.activate(); + } + // Open a c++ file to start the language server. + await vscode.workspace.openTextDocument({ language: "cpp", content: "int main() { return 0; }"}); + }); + + suiteTeardown(async function() { + // Delete c_cpp_properties.json + }); + + + + test("Check default configuration", () => { + let rootUri: vscode.Uri; + if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0) { + rootUri = vscode.workspace.workspaceFolders[0].uri; + } + assert.notEqual(rootUri, undefined, "Root Uri is not defined"); + if (rootUri) { + let cppProperties: config.CppProperties = new config.CppProperties(rootUri); + let configurations: config.Configuration[] = cppProperties.Configurations; + let defaultConfig: config.Configuration = config.getDefaultConfig(); + assert.deepEqual(configurations[0], defaultConfig); + console.log(JSON.stringify(configurations, null, 2)); + + // Need to set the CompilerDefaults before the CppProperties can be successfully modified. + cppProperties.CompilerDefaults = { + compilerPath: "/path/to/compiler", + cStandard: "c99", + cppStandard: "c++14", + frameworks: ["/path/to/framework"], + includes: ["/path/to/includes"] + }; + + configurations[0].cppStandard = "${default}"; + + let s: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("C_Cpp.default", rootUri); + let d: any = s.inspect("cppStandard"); + s.update("cppStandard", "c++11", vscode.ConfigurationTarget.WorkspaceFolder); + d = s.inspect("cppStandard"); + + cppProperties.onDidChangeSettings(); + } + }); +}); +*/ \ No newline at end of file From 6648c400b703ffe68b43d50dab328e0ce936c88b Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Fri, 20 Apr 2018 14:15:56 -0700 Subject: [PATCH 19/22] Add preferredPathSeparator setting and update package.json version and fwlinks (#1854) * Add preferredPathSeparator setting * Update package.json version and fwlinks. --- Extension/package-lock.json | 8 +++---- Extension/package.json | 28 ++++++++++++++++-------- Extension/src/LanguageServer/client.ts | 3 ++- Extension/src/LanguageServer/settings.ts | 1 + 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/Extension/package-lock.json b/Extension/package-lock.json index 7a2f68f98b..944e1ba453 100644 --- a/Extension/package-lock.json +++ b/Extension/package-lock.json @@ -1,6 +1,6 @@ { "name": "cpptools", - "version": "0.16.1", + "version": "0.17.0-master", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -5100,9 +5100,9 @@ } }, "vscode": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/vscode/-/vscode-1.1.10.tgz", - "integrity": "sha512-MvFXXSGuhw0Q6GC6dQrnRc0ES+63wpttGIoYGBMQnoS9JFCCNC/rWfX0lBCHJyuKL2Q8CYg0ROsMEHbHVwEtVw==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/vscode/-/vscode-1.1.14.tgz", + "integrity": "sha512-acfn3fzGtTm7UjChAN7/YjsC0qIyJeuSrJwvm6qb7tLN6Geq1FmCz1JnBOc3kaY+HCLjQBAfwG/CsgnasOdXMw==", "dev": true, "requires": { "glob": "7.1.2", diff --git a/Extension/package.json b/Extension/package.json index 0829f547fc..1493dc1a72 100644 --- a/Extension/package.json +++ b/Extension/package.json @@ -2,7 +2,7 @@ "name": "cpptools", "displayName": "C/C++", "description": "C/C++ IntelliSense, debugging, and code browsing.", - "version": "0.16.1", + "version": "0.17.0-master", "publisher": "ms-vscode", "preview": true, "icon": "LanguageCCPP_color_128x.png", @@ -201,6 +201,16 @@ "description": "Instructs the extension when to use the \"files.exclude\" setting when determining which files should be added to the code navigation database while traversing through the paths in the \"browse.path\" array. \"checkFolders\" means that the exclusion filters will only be evaluated once per folder (individual files are not checked). \"checkFilesAndFolders\" means that the exclusion filters will be evaluated against every file and folder encountered. If your \"files.exclude\" setting only contains folders, then \"checkFolders\" is the best choice and will increase the speed at which the extension can initialize the code navigation database.", "scope": "resource" }, + "C_Cpp.preferredPathSeparator": { + "type": "string", + "enum": [ + "Forward Slash", + "Back Slash" + ], + "default": "Forward Slash", + "description": "The character used as a path separator for #include auto-completion results.", + "scope": "resource" + }, "C_Cpp.commentContinuationPatterns": { "type": "array", "default": [ @@ -1294,7 +1304,7 @@ "runtimeDependencies": [ { "description": "C/C++ language components (Linux / x86_64)", - "url": "https://go.microsoft.com/fwlink/?linkid=871264", + "url": "https://go.microsoft.com/fwlink/?linkid=872599", "platforms": [ "linux" ], @@ -1308,7 +1318,7 @@ }, { "description": "C/C++ language components (Linux / x86)", - "url": "https://go.microsoft.com/fwlink/?linkid=871265", + "url": "https://go.microsoft.com/fwlink/?linkid=872600", "platforms": [ "linux" ], @@ -1324,7 +1334,7 @@ }, { "description": "C/C++ language components (OS X)", - "url": "https://go.microsoft.com/fwlink/?linkid=871266", + "url": "https://go.microsoft.com/fwlink/?linkid=872601", "platforms": [ "darwin" ], @@ -1335,7 +1345,7 @@ }, { "description": "C/C++ language components (Windows)", - "url": "https://go.microsoft.com/fwlink/?linkid=871267", + "url": "https://go.microsoft.com/fwlink/?linkid=872602", "platforms": [ "win32" ], @@ -1343,7 +1353,7 @@ }, { "description": "ClangFormat (Linux / x86_64)", - "url": "https://go.microsoft.com/fwlink/?LinkID=848955", + "url": "https://go.microsoft.com/fwlink/?LinkID=872607", "platforms": [ "linux" ], @@ -1356,7 +1366,7 @@ }, { "description": "ClangFormat (Linux / x86)", - "url": "https://go.microsoft.com/fwlink/?LinkID=864640", + "url": "https://go.microsoft.com/fwlink/?LinkID=872608", "platforms": [ "linux" ], @@ -1371,7 +1381,7 @@ }, { "description": "ClangFormat (OS X)", - "url": "https://go.microsoft.com/fwlink/?LinkID=848956", + "url": "https://go.microsoft.com/fwlink/?LinkID=872609", "platforms": [ "darwin" ], @@ -1381,7 +1391,7 @@ }, { "description": "ClangFormat (Windows)", - "url": "https://go.microsoft.com/fwlink/?LinkID=848957", + "url": "https://go.microsoft.com/fwlink/?LinkID=872610", "platforms": [ "win32" ], diff --git a/Extension/src/LanguageServer/client.ts b/Extension/src/LanguageServer/client.ts index 0dfbfc2537..f621679399 100644 --- a/Extension/src/LanguageServer/client.ts +++ b/Extension/src/LanguageServer/client.ts @@ -401,7 +401,8 @@ class DefaultClient implements Client { dimInactiveRegions: settings.dimInactiveRegions, loggingLevel: settings.loggingLevel, workspaceParsingPriority: settings.workspaceParsingPriority, - exclusionPolicy: settings.exclusionPolicy + exclusionPolicy: settings.exclusionPolicy, + preferredPathSeparator: settings.preferredPathSeparator }, middleware: createProtocolFilter(this, allClients), // Only send messages directed at this client. errorHandler: { diff --git a/Extension/src/LanguageServer/settings.ts b/Extension/src/LanguageServer/settings.ts index e41d088a3d..40a3557f9d 100644 --- a/Extension/src/LanguageServer/settings.ts +++ b/Extension/src/LanguageServer/settings.ts @@ -47,6 +47,7 @@ export class CppSettings extends Settings { public get workspaceParsingPriority(): boolean { return super.Section.get("workspaceParsingPriority"); } public get exclusionPolicy(): boolean { return super.Section.get("exclusionPolicy"); } public get commentContinuationPatterns(): (string | CommentPattern)[] { return super.Section.get<(string | CommentPattern)[]>("commentContinuationPatterns"); } + public get preferredPathSeparator(): string { return super.Section.get("preferredPathSeparator"); } public get defaultIncludePath(): string[] { return super.Section.get("default.includePath"); } public get defaultDefines(): string[] { return super.Section.get("default.defines"); } public get defaultMacFrameworkPath(): string[] { return super.Section.get("default.macFrameworkPath"); } From 6faac1071fe7174095e6c0e780782479988f21e9 Mon Sep 17 00:00:00 2001 From: Bob Brown Date: Mon, 23 Apr 2018 14:13:34 -0700 Subject: [PATCH 20/22] Fixing issues found while testing settings changes (#1865) * Fixing issues found while testing settings changes * missed a property in the schema * additional safety around config updates --- Extension/c_cpp_properties.schema.json | 20 ++++++++++++------- Extension/package.json | 14 ++++++------- Extension/src/LanguageServer/client.ts | 6 ++++-- .../src/LanguageServer/configurations.ts | 4 ++-- 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/Extension/c_cpp_properties.schema.json b/Extension/c_cpp_properties.schema.json index c36547e399..d4aac1e681 100644 --- a/Extension/c_cpp_properties.schema.json +++ b/Extension/c_cpp_properties.schema.json @@ -11,7 +11,7 @@ ], "properties": { "name": { - "description": "Configuration identifier. Mac, Linux, or Win32 are special identifiers for configurations that will be auto-selected on those platforms, but the identifier can be anything.", + "description": "Configuration identifier. Mac, Linux, and Win32 are special identifiers for configurations that will be auto-selected on those platforms, but the identifier can be anything.", "type": "string" }, "compilerPath": { @@ -24,7 +24,8 @@ "enum": [ "c89", "c99", - "c11" + "c11", + "${default}" ] }, "cppStandard": { @@ -35,7 +36,8 @@ "c++03", "c++11", "c++14", - "c++17" + "c++17", + "${default}" ] }, "compileCommands": { @@ -64,12 +66,13 @@ } }, "intelliSenseMode": { + "description": "If set, it overrides the default mode used by the IntelliSense engine. Windows defaults to msvc-x64 and Linux/Mac default to clang-x64.", "type": "string", "enum": [ "msvc-x64", - "clang-x64" - ], - "description": "If set, it overrides the default mode used by the IntelliSense engine. Windows defaults to msvc-x64 and Linux/Mac default to clang-x64." + "clang-x64", + "${default}" + ] }, "forcedInclude": { "description": "A list of files that should be included before any include file in a translation unit.", @@ -83,7 +86,10 @@ "properties": { "limitSymbolsToIncludedHeaders": { "description": "true to process only those files directly or indirectly included as headers, false to process all files under the specified include paths.", - "type": "boolean" + "type": [ + "boolean", + "string" + ] }, "databaseFilename": { "description": "Path to the generated symbol database. If a relative path is specified, it will be made relative to the workspace's default storage location.", diff --git a/Extension/package.json b/Extension/package.json index 1493dc1a72..1155ba37e0 100644 --- a/Extension/package.json +++ b/Extension/package.json @@ -282,7 +282,7 @@ "null" ], "default": null, - "description": "The value to use in a configuration if \"compileCommands\" is not specified, or the values to insert if \"${default}\" is present in \"compileCommands\".", + "description": "The value to use in a configuration if \"compileCommands\" is either not specified, or set to \"${default}\".", "scope": "resource" }, "C_Cpp.default.forcedInclude": { @@ -307,7 +307,7 @@ "clang-x64" ], "default": null, - "description": "The value to use in a configuration if \"intelliSenseMode\" is not specified, or the values to insert if \"${default}\" is present in \"intelliSenseMode\".", + "description": "The value to use in a configuration if \"intelliSenseMode\" is either not specified or set to \"${default}\".", "scope": "resource" }, "C_Cpp.default.compilerPath": { @@ -316,7 +316,7 @@ "null" ], "default": null, - "description": "The value to use in a configuration if \"compilerPath\" is not specified, or the values to insert if \"${default}\" is present in \"compilerPath\".", + "description": "The value to use in a configuration if \"compilerPath\" is either not specified or set to \"${default}\".", "scope": "resource" }, "C_Cpp.default.cStandard": { @@ -330,7 +330,7 @@ "c11" ], "default": null, - "description": "The value to use in a configuration if \"cStandard\" is not specified, or the values to insert if \"${default}\" is present in \"cStandard\".", + "description": "The value to use in a configuration if \"cStandard\" is either not specified or set to \"${default}\".", "scope": "resource" }, "C_Cpp.default.cppStandard": { @@ -346,7 +346,7 @@ "c++17" ], "default": null, - "description": "The value to use in a configuration if \"cppStandard\" is not specified, or the values to insert if \"${default}\" is present in \"cppStandard\".", + "description": "The value to use in a configuration if \"cppStandard\" is either not specified or set to \"${default}\".", "scope": "resource" }, "C_Cpp.default.browse.path": { @@ -367,13 +367,13 @@ "null" ], "default": null, - "description": "The value to use in a configuration if \"browse.databaseFilename\" is not specified, or the values to insert if \"${default}\" is present in \"browse.databaseFilename\".", + "description": "The value to use in a configuration if \"browse.databaseFilename\" is either not specified or set to \"${default}\".", "scope": "resource" }, "C_Cpp.default.browse.limitSymbolsToIncludedHeaders": { "type": "boolean", "default": true, - "description": "The value to use in a configuration if \"browse.limitSymbolsToIncludedHeaders\" is not specified, or the values to insert if \"${default}\" is present in \"browse.limitSymbolsToIncludedHeaders\".", + "description": "The value to use in a configuration if \"browse.limitSymbolsToIncludedHeaders\" is either not specified or set to \"${default}\".", "scope": "resource" }, "C_Cpp.default.systemIncludePath": { diff --git a/Extension/src/LanguageServer/client.ts b/Extension/src/LanguageServer/client.ts index f621679399..6e133a4a75 100644 --- a/Extension/src/LanguageServer/client.ts +++ b/Extension/src/LanguageServer/client.ts @@ -395,14 +395,16 @@ class DefaultClient implements Client { tab_size: other.editorTabSize, intelliSenseEngine: settings.intelliSenseEngine, intelliSenseEngineFallback: settings.intelliSenseEngineFallback, - defaultSystemIncludePath: settings.defaultSystemIncludePath, autocomplete: settings.autoComplete, errorSquiggles: settings.errorSquiggles, dimInactiveRegions: settings.dimInactiveRegions, loggingLevel: settings.loggingLevel, workspaceParsingPriority: settings.workspaceParsingPriority, exclusionPolicy: settings.exclusionPolicy, - preferredPathSeparator: settings.preferredPathSeparator + preferredPathSeparator: settings.preferredPathSeparator, + default: { + systemIncludePath: settings.defaultSystemIncludePath + } }, middleware: createProtocolFilter(this, allClients), // Only send messages directed at this client. errorHandler: { diff --git a/Extension/src/LanguageServer/configurations.ts b/Extension/src/LanguageServer/configurations.ts index 7641e57917..5762f15aec 100644 --- a/Extension/src/LanguageServer/configurations.ts +++ b/Extension/src/LanguageServer/configurations.ts @@ -472,7 +472,7 @@ export class CppProperties { throw { message: "Invalid configuration file. There must be at least one configuration present in the array." }; } if (!this.configurationIncomplete && this.configurationJson && this.configurationJson.configurations && - this.CurrentConfiguration < this.configurationJson.configurations.length) { + this.CurrentConfiguration >= 0 && this.CurrentConfiguration < this.configurationJson.configurations.length) { for (let i: number = 0; i < newJson.configurations.length; i++) { if (newJson.configurations[i].name === this.configurationJson.configurations[this.CurrentConfiguration].name) { this.currentConfigurationIndex.Value = i; @@ -481,7 +481,7 @@ export class CppProperties { } } this.configurationJson = newJson; - if (this.CurrentConfiguration >= newJson.configurations.length) { + if (this.CurrentConfiguration < 0 || this.CurrentConfiguration >= newJson.configurations.length) { this.currentConfigurationIndex.Value = this.getConfigIndexForPlatform(newJson); } From b60b17b6a5dd88d5d957e9f277f6e7d4e64ec3fc Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Mon, 23 Apr 2018 15:02:39 -0700 Subject: [PATCH 21/22] Update changelog.md. (#1866) --- Extension/CHANGELOG.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Extension/CHANGELOG.md b/Extension/CHANGELOG.md index 0621bdae28..3b5b8bd3f2 100644 --- a/Extension/CHANGELOG.md +++ b/Extension/CHANGELOG.md @@ -3,8 +3,13 @@ ## Version 0.17.0: May 7, 2018 * Auto-complete for headers after typing `#include`. [#802](https://github.com/Microsoft/vscode-cpptools/issues/802) * Configuration improvements. [#1338](https://github.com/Microsoft/vscode-cpptools/issues/1338) - * potentially addresses: [#368](https://github.com/Microsoft/vscode-cpptools/issues/368), [#410](https://github.com/Microsoft/vscode-cpptools/issues/410), [#1229](https://github.com/Microsoft/vscode-cpptools/issues/1229), [#1270](https://github.com/Microsoft/vscode-cpptools/issues/) -* Add support for querying system includes/defines from compilers in WSL environment. + * Potentially addresses: [#368](https://github.com/Microsoft/vscode-cpptools/issues/368), [#410](https://github.com/Microsoft/vscode-cpptools/issues/410), [#1229](https://github.com/Microsoft/vscode-cpptools/issues/1229), [#1270](https://github.com/Microsoft/vscode-cpptools/issues/), [#1404](https://github.com/Microsoft/vscode-cpptools/issues/1404) +* Add support for querying system includes/defines from WSL and Cygwin compilers. [#1845](https://github.com/Microsoft/vscode-cpptools/issues/1845), [#1736](https://github.com/Microsoft/vscode-cpptools/issues/1736) +* Stop automatically adding `/usr/include` to the `includePath`. [#1819](https://github.com/Microsoft/vscode-cpptools/issues/1819) +* Fix wrong configuration being used if there are four or more. [#1599](https://github.com/Microsoft/vscode-cpptools/issues/1599) +* Fix `c_cpp_properties.json` requiring write access. [#1790](https://github.com/Microsoft/vscode-cpptools/issues/1790) +* Change file not found in `compile_commands.json` message from an error to a warning. [#1783](https://github.com/Microsoft/vscode-cpptools/issues/1783) +* Fix an IntelliSense crash during completion requests. [#1782](https://github.com/Microsoft/vscode-cpptools/issues/1782) ## Version 0.16.1: March 30, 2018 * Fix random deadlock caused by logging code on Linux/Mac. [#1759](https://github.com/Microsoft/vscode-cpptools/issues/1759) From 80da21f19931e8fc0b0e245e99014c890533bbc0 Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Mon, 23 Apr 2018 15:19:14 -0700 Subject: [PATCH 22/22] Seanmcm/update changelog0 17 insiders1 (#1867) * Update changelog.md. * Added the clang-format 6.0 update. --- Extension/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Extension/CHANGELOG.md b/Extension/CHANGELOG.md index 3b5b8bd3f2..b74af9b2d0 100644 --- a/Extension/CHANGELOG.md +++ b/Extension/CHANGELOG.md @@ -10,6 +10,7 @@ * Fix `c_cpp_properties.json` requiring write access. [#1790](https://github.com/Microsoft/vscode-cpptools/issues/1790) * Change file not found in `compile_commands.json` message from an error to a warning. [#1783](https://github.com/Microsoft/vscode-cpptools/issues/1783) * Fix an IntelliSense crash during completion requests. [#1782](https://github.com/Microsoft/vscode-cpptools/issues/1782) +* Update the installed clang-format to 6.0. ## Version 0.16.1: March 30, 2018 * Fix random deadlock caused by logging code on Linux/Mac. [#1759](https://github.com/Microsoft/vscode-cpptools/issues/1759)