Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0cf237c839 | ||
|
|
e12bad5172 | ||
|
|
04bb260577 | ||
|
|
ddc1f901c2 | ||
|
|
9ca27af2df | ||
|
|
b964d4ebc4 | ||
|
|
082435ec8b | ||
|
|
324c5f59f4 | ||
|
|
72581c23b7 | ||
|
|
807ff80f56 | ||
|
|
b0e17a1fd9 | ||
|
|
bc64d25a1d | ||
|
|
ed5d8c8fac | ||
|
|
3a9f4c404d | ||
|
|
4330679e23 | ||
|
|
88e9724892 | ||
|
|
acf7b464eb | ||
|
|
78f5a38110 | ||
|
|
31eed4cee2 | ||
|
|
ea8ab1358f | ||
|
|
55558a53e3 | ||
|
|
536685c8a0 | ||
|
|
b6f184744c | ||
|
|
539ecc536f | ||
|
|
e819f36e91 | ||
|
|
140670c371 | ||
|
|
102ffc1616 | ||
|
|
28981f3ce6 | ||
|
|
775aba8650 | ||
|
|
b3fb2558fd | ||
|
|
80716ae228 | ||
|
|
6d71c6dd4f | ||
|
|
da68908e3e | ||
|
|
963ee02117 | ||
|
|
45a1804da4 | ||
|
|
f66eb138f4 | ||
|
|
da862646c3 | ||
|
|
1bcaa540b6 | ||
|
|
96e3386288 | ||
|
|
862a10f3f8 | ||
|
|
2e62382b24 | ||
|
|
5f9ef54c54 | ||
|
|
4575e2840e | ||
|
|
a9a09dcd11 | ||
|
|
e0e7944f3a | ||
|
|
63e18afcf1 | ||
|
|
fcdcd80157 | ||
|
|
126cf7cef6 | ||
|
|
ad451bb438 | ||
|
|
83be111769 | ||
|
|
2f931d930d | ||
|
|
bc6cb3e55f | ||
|
|
f44e9068ca | ||
|
|
c206a4b88c | ||
|
|
9e95e412a8 | ||
|
|
86ce6c6115 | ||
|
|
ecd3eb24f4 | ||
|
|
25e29a01c2 | ||
|
|
e9b8ad66a1 | ||
|
|
3e5a6aae80 | ||
|
|
49fea0249d | ||
|
|
7de9956a92 | ||
|
|
9abf44b7ac | ||
|
|
daaf169c8a | ||
|
|
23d94452bb | ||
|
|
705122a168 | ||
|
|
757f1f94be | ||
|
|
7fe2db1202 | ||
|
|
d8738c3e0c | ||
|
|
b1bd33b8f4 | ||
|
|
436c483d35 | ||
|
|
99bd382707 | ||
|
|
356bb903d0 | ||
|
|
b0fc9427cf | ||
|
|
60b20dce8d | ||
|
|
906fd2d8f0 | ||
|
|
4afadad93b | ||
|
|
3790fa95e7 | ||
|
|
a0882446d9 | ||
|
|
ce59810a6d | ||
|
|
770013d77e | ||
|
|
2388fa97f4 | ||
|
|
3381cde61c | ||
|
|
3a46f10d97 | ||
|
|
2b4b5d397d | ||
|
|
c36eb8e7b1 | ||
|
|
ad46d3887a | ||
|
|
8b704f5972 | ||
|
|
1009b76b22 | ||
|
|
84ff661e15 | ||
|
|
2292e7d762 | ||
|
|
c8e30c70b1 | ||
|
|
7947dd80d6 | ||
|
|
913f740cc2 | ||
|
|
2cbc90d98b | ||
|
|
25a29d0bc3 | ||
|
|
052f4e7b39 | ||
|
|
2b245202b5 | ||
|
|
15c6c0a50e | ||
|
|
a0e0254c73 | ||
|
|
528905326f | ||
|
|
458b2be489 | ||
|
|
95d4724dc8 | ||
|
|
a178faab9b | ||
|
|
25aa8206a0 | ||
|
|
879d880c82 | ||
|
|
16c4defcd0 | ||
|
|
725828c1c5 | ||
|
|
8b188541ba | ||
|
|
28f1bc991e | ||
|
|
aa424f1946 | ||
|
|
130711f4c5 | ||
|
|
863d26451c | ||
|
|
0a85b6c115 | ||
|
|
f2b3bd5a18 | ||
|
|
ab3e498c17 | ||
|
|
bfad3045bd | ||
|
|
842e685a6a | ||
|
|
3b36a23956 | ||
|
|
bf7287e70b | ||
|
|
63ce03573c | ||
|
|
9ddf1098f7 | ||
|
|
56f4be1f65 | ||
|
|
3b6dc61562 | ||
|
|
b8a15ddfe6 | ||
|
|
f3e188c0ce | ||
|
|
d403c8f984 | ||
|
|
35288aad17 | ||
|
|
4b90e4c151 | ||
|
|
cc85f3d7c3 | ||
|
|
fbbe28458a | ||
|
|
0d80edba3d | ||
|
|
76870c637c |
@@ -4,10 +4,12 @@ about: 'Issues pertaining to IntelliSense, autocomplete, code editing, etc. '
|
||||
|
||||
---
|
||||
|
||||
Type: LanguageService
|
||||
**----- Input information below -----**
|
||||
**Type: LanguageService**
|
||||
<!----- Input information below ----->
|
||||
|
||||
<!--
|
||||
**Please review existing issues and our documentation at https://github.com/Microsoft/vscode-cpptools/tree/master/Documentation prior to filing an issue.**
|
||||
-->
|
||||
|
||||
**Describe the bug**
|
||||
- OS and Version:
|
||||
@@ -17,19 +19,21 @@ Type: LanguageService
|
||||
- A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
*Provide a code sample including configuration files such as c_cpp_properties.json*
|
||||
<!-- Steps to reproduce the behavior: -->
|
||||
<!-- *The most actionable issue reports include a code sample including configuration files such as c_cpp_properties.json* -->
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
<!-- A clear and concise description of what you expected to happen. -->
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
<!-- If applicable, add screenshots to help explain your problem. -->
|
||||
|
||||
**Additional context**
|
||||
<!--
|
||||
*Call Stacks: For bugs like crashes, deadlocks, infinite loops, etc. that we are not able to repro and for which the call stack may be useful, please attach a debugger and/or create a dmp and provide the call stacks. Starting with 0.17.3, Windows binaries have symbols available in VS Code by setting your "symbolSearchPath" to "http://msdl.microsoft.com/download/symbols".*
|
||||
Add any other context about the problem here including log messages in your Output window ("C_Cpp.loggingLevel": "Debug" in settings.json).
|
||||
-->
|
||||
|
||||
+3
-1
@@ -35,7 +35,9 @@ script:
|
||||
- npm run tslint
|
||||
# pr-check needs to run before test. test modifies package.json.
|
||||
- npm run pr-check
|
||||
- npm run test
|
||||
- npm run unitTests
|
||||
# TODO: Merge tests into one group due to Gulp 4.0
|
||||
- npm run integrationTests
|
||||
# Dump integrationTest.log output
|
||||
- find ~ -name "integrationTests.log" -type f -exec cat {} \;
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
## How to build and debug the Microsoft CppTools Extension
|
||||
|
||||
These steps will allow you to debug the TypeScript code that is part of the Microsoft CppTools extension for Visual Studio Code.
|
||||
|
||||
Prerequisite steps:
|
||||
* Clone [this](https://github.com/Microsoft/vscode-cpptools) repository.
|
||||
* Install [npm](https://nodejs.org).
|
||||
* From a command line, run the following commands from the **Extension** folder in the root of the repository:
|
||||
* `npm install -g vsce` will install `vsce` globally to create the vsix package.
|
||||
* `npm install` will install the dependencies needed to build the extension.
|
||||
* Set an environment variable `CPPTOOLS_DEV=1`.
|
||||
* This enables the local developer workflow, copying dependencies from the **node_modules** folder.
|
||||
* Open the **Extension** folder in Visual Studio Code and F5.
|
||||
* Read the [contributing guidelines](https://github.com/Microsoft/vscode-cpptools/blob/master/CONTRIBUTING.md).
|
||||
## How to build and debug the Microsoft CppTools Extension
|
||||
|
||||
These steps will allow you to debug the TypeScript code that is part of the Microsoft CppTools extension for Visual Studio Code.
|
||||
|
||||
Prerequisite steps:
|
||||
* Clone [this](https://github.com/Microsoft/vscode-cpptools) repository.
|
||||
* Install [npm](https://nodejs.org).
|
||||
* From a command line, run the following commands from the **Extension** folder in the root of the repository:
|
||||
* `npm install` will install the dependencies needed to build the extension.
|
||||
* **(optional)** `npm install -g vsce` will install `vsce` globally to create a VSIX package that you can install.
|
||||
* **(optional)** Set an environment variable `CPPTOOLS_DEV=1`.
|
||||
* This enables the local developer workflow when testing the debugger, copying dependencies from the **node_modules** folder. Testing the language server does not require this step.
|
||||
* Open the **Extension** folder in Visual Studio Code and press F5. This will launch a VS Code Extension Host window and activate the TypeScript debugger. You can set breakpoints on the extension source code and debug your scenario.
|
||||
|
||||
+38
-1
@@ -3,7 +3,8 @@
|
||||
## Table of Contents
|
||||
* Setup: [Debugging Setup](#debugging-setup)
|
||||
* Debugger: [Why is debugging not working?](#why-is-debugging-not-working)
|
||||
* Build: [How to enable debug symbols?](#how-to-enable-debug-symbols)
|
||||
* Build: [How to enable debug symbols](#how-to-enable-debug-symbols)
|
||||
* Logging: [How to enable logging](#how-to-enable-logging)
|
||||
|
||||
## Debugging Setup
|
||||
The debugger needs to be configured to know which executable and debugger to use:
|
||||
@@ -43,3 +44,39 @@ When in doubt, please check your compiler's documentation for the options necess
|
||||
|
||||
* #### cl.exe
|
||||
Symbols are located in the `*.pdb` file.
|
||||
|
||||
## How to enable logging
|
||||
|
||||
Enabling logging will show communication information between VS Code and our extension and between our extension and the debugger.
|
||||
|
||||
### Logging for `MI` debuggers
|
||||
|
||||
The logging block with its defaults is as follows:
|
||||
|
||||
```
|
||||
"logging": {
|
||||
"trace": false,
|
||||
"traceResponse": false,
|
||||
"engineLogging": false
|
||||
}
|
||||
```
|
||||
|
||||
#### VS Code and the CppTools extension
|
||||
|
||||
The logging here is called `trace` logging and can be enabled by setting `trace` and `traceResponse` to `true` in the logging block inside `launch.json`. This will help diagnose issues related to VS Code's communication to our extension and our responses.
|
||||
|
||||
#### CppTools extension and the debugger
|
||||
|
||||
The logging between CppTools and the debugger is called `engineLogging`. When using an `MI` debugger such as `gdb` or `lldb`, this will show the request, response and events using the `mi` interpreter. This logging will help us determine whether the debugger is receiving the right commands and generating the correct responses.
|
||||
|
||||
### Logging for `Visual C++` debugger
|
||||
|
||||
The logging block with its defaults is as follows:
|
||||
|
||||
```
|
||||
"logging": {
|
||||
"engineLogging": false
|
||||
}
|
||||
```
|
||||
|
||||
The `Visual C++` debugger logging will show only the communication to and from VS Code as all communication to the debugger is done internally to the process and is not visible through logging.
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
If you are experiencing a problem that we are unable to diagnose based on information in your issue report, we might ask you to enable logging and send us your logs.
|
||||
|
||||
As of version 0.14.0 of the extension, logging information is now delivered directly to the Output window in VSCode. To turn on full logging for an issue report, add `"C_Cpp.loggingLevel": "Information"` to your **settings.json**.
|
||||
As of version 0.14.0 of the extension, logging information is now delivered directly to the Output window in VSCode. To turn on full logging for an issue report, add `"C_Cpp.loggingLevel": "Information"` or `"C_Cpp.loggingLevel": "Debug"` to your **settings.json**.
|
||||
|
||||

|
||||

|
||||
|
||||
VS Code organizes the logging from different extensions to improve readability so you must select the "C/C++" option in the log filter selector to see logging from the C/C++ extension:
|
||||
|
||||
|
||||
@@ -4,24 +4,17 @@ To use MinGW on Windows, we recommend you add the following configuration to you
|
||||
|
||||
## Extension version 0.17.0 and higher:
|
||||
|
||||
When 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`, `browse.path`, or `defines` to enable IntelliSense to work properly. For example:
|
||||
When you set the `compilerPath` property and change `intelliSenseMode` to `clang-x64` (or `gcc-x64` in version 0.18.0 and higher), you no longer need to copy the system include path or defines to `includePath`, `browse.path`, or `defines` to enable IntelliSense to work properly. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "MinGW",
|
||||
"intelliSenseMode": "clang-x64",
|
||||
"intelliSenseMode": "gcc-x64",
|
||||
"compilerPath": "C:/mingw64/bin/gcc.exe",
|
||||
"includePath": [
|
||||
"${workspaceFolder}"
|
||||
],
|
||||
"defines": [],
|
||||
"browse": {
|
||||
"path": [
|
||||
"${workspaceFolder}"
|
||||
],
|
||||
"limitSymbolsToIncludedHeaders": true,
|
||||
"databaseFilename": ""
|
||||
},
|
||||
"cStandard": "c11",
|
||||
"cppStandard": "c++17"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Windows Subsystem for Linux
|
||||
|
||||
> **Note:** If you are on **build 17110 of Windows or higher**, you must use version 0.17.0 or higher for IntelliSense to work. The Windows team turned on case-sensitive folders for the WSL environment and the C/C++ extension doesn't support case-sensitive folders until version 0.17.0.
|
||||
> **Note:** If you are on **build 17110 of Windows or higher**, you must use extension version 0.17.0 or higher for IntelliSense to work. The Windows team turned on case-sensitive folders for the WSL environment and the C/C++ extension doesn't support case-sensitive folders until version 0.17.0.
|
||||
|
||||
To use the Windows Subsystem for Linux with this extension you need to add a configuration to your **c_cpp_properties.json** file which adds the necessary header paths from within the WSL filesystem to the `includePath`.
|
||||
|
||||
@@ -13,22 +13,12 @@ In **c_cpp_properties.json** you can directly address your WSL compiler and incl
|
||||
```json
|
||||
{
|
||||
"name": "WSL",
|
||||
"intelliSenseMode": "clang-x64",
|
||||
"intelliSenseMode": "gcc-x64",
|
||||
"compilerPath": "/usr/bin/gcc",
|
||||
"includePath": [
|
||||
"${workspaceFolder}",
|
||||
"/mnt/c/libraries/lib1/include",
|
||||
"C:/libraries/lib2/include"
|
||||
"${workspaceFolder}/**"
|
||||
],
|
||||
"defines": [],
|
||||
"browse": {
|
||||
"path": [
|
||||
"${workspaceFolder}",
|
||||
"/mnt/c/libraries"
|
||||
],
|
||||
"limitSymbolsToIncludedHeaders": true,
|
||||
"databaseFilename": ""
|
||||
},
|
||||
"cStandard": "c11",
|
||||
"cppStandard": "c++17"
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
A friendly name for the configuration. "Linux", "Mac", and "Win32" are special names that instruct the extension to load that configuration by default on the associated operating system unless additional configurations have been created. The status bar in VS Code will show you which configuration is active. You can also click on the label in the status bar to change the active configuration.
|
||||
|
||||
* #### `intelliSenseMode`
|
||||
If `"C_Cpp.intelliSenseEngine"` is set to "Default" in your settings file, this property determines which mode the IntelliSense engine will run in. `"msvc-x64"` maps to Visual Studio mode with 64-bit pointer sizes. `"clang-x64"` maps to GCC/CLang mode with 64-bit pointer sizes. Windows uses `"msvc-x64"` by default and Linux/Mac use `"clang-x64"` by default.
|
||||
If `"C_Cpp.intelliSenseEngine"` is set to "Default" in your settings file, this property determines which mode the IntelliSense engine will run in. `"msvc-x64"` maps to Visual Studio mode with 64-bit pointer sizes. `"clang-x64"` maps to CLang mode with 64-bit pointer sizes. `"gcc-x64"` maps to GCC mode with 64-bit pointer sizes. Windows uses `"msvc-x64"` by default, macOS uses `"clang-x64"` by default, and Linux uses `"gcc-x64"` by default.
|
||||
|
||||
* #### `includePath`
|
||||
If `"C_Cpp.intelliSenseEngine"` is set to "Default" in your settings file, this list of paths will be used by IntelliSense to search for headers included by your source files. This is basically the same as the list of paths you pass to your compiler with the `-I` switch. If a path ends with `/**` the IntelliSense engine will do a recursive search for includes starting from that directory. If on Windows with Visual Studio installed, or if a compiler is specified in the `compilerPath` setting, it is not necessary to list the system include paths in this list.
|
||||
@@ -68,7 +68,11 @@
|
||||
A list of files that should be included before any other characters in the source file are processed. Files are included in the order listed.
|
||||
|
||||
* #### `compilerPath` (optional)
|
||||
The absolute path to the compiler you use to build your project. The extension will query the compiler to determine the system include paths and default defines to use for IntelliSense. Args can be added to modify the includes/defines used, e.g. `-nostdinc++`, `-m32`, etc., but paths with spaces must be surrounded by double quotes (`"`) if args are used.
|
||||
The absolute path to the compiler you use to build your project. The extension will query the compiler to determine the system include paths and default defines to use for IntelliSense.
|
||||
|
||||
Args can be added to modify the includes/defines used, e.g. `-nostdinc++`, `-m32`, `-fno-ms-extensions`, etc., but paths with spaces must be surrounded by double quotes (`"`) if args are used.
|
||||
|
||||
If you want to disable automatic querying of system include paths and defines, set this value to the emptry string `""`. This is generally not recommended, but there are some cases where automatic querying of system includes and defines is undesirable.
|
||||
|
||||
* #### `cStandard`
|
||||
The C standard revision to use for IntelliSense in your project.
|
||||
@@ -87,7 +91,7 @@
|
||||
### Browse properties
|
||||
|
||||
* #### `path`
|
||||
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`.
|
||||
This list of paths will be used by the Tag Parser to search for headers included by your source files. If omitted, `includePath` will be used as the `path`. 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 `${workspaceFolder}`. When false, the Tag Parser will parse all code files found in the paths specified in the **path** list.
|
||||
|
||||
@@ -17,4 +17,5 @@ install.lock
|
||||
*.vsix
|
||||
|
||||
# ignore vscode test
|
||||
.vscode-test/
|
||||
.vscode-test/
|
||||
browse*db*
|
||||
Vendored
+1
-1
@@ -24,7 +24,7 @@
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
||||
"--extensionTestsPath=${workspaceFolder}/out/test"
|
||||
"--extensionTestsPath=${workspaceFolder}/out/test/unitTests"
|
||||
],
|
||||
"stopOnEntry": false,
|
||||
"sourceMaps": true,
|
||||
|
||||
+129
-2
@@ -1,5 +1,132 @@
|
||||
# C/C++ for Visual Studio Code Change Log
|
||||
|
||||
## Version 0.20.0: October 29, 2018
|
||||
* Add IntegratedTerminal support for Linux and Windows. [#35](https://github.com/microsoft/vscode-cpptools/issues/35)
|
||||
* Unify Visual Studio Code debug protocol parsing by using a shared library with Visual Studio.
|
||||
* Fix IntelliSense-based `Go to Definition` on overloads (in the same TU). [#1071](https://github.com/Microsoft/vscode-cpptools/issues/1071)
|
||||
* Fix inactive regions not being disabled when falling back to the Tag Parser. [#2181](https://github.com/Microsoft/vscode-cpptools/issues/2181)
|
||||
* Fix `#include` completion not working with `compile_commands.json` or custom configuration providers. [#2242](https://github.com/Microsoft/vscode-cpptools/issues/2242)
|
||||
* Fix IntelliSense failing if recursive includes removes all paths. [#2442](https://github.com/Microsoft/vscode-cpptools/issues/2442)
|
||||
* Fix incorrect IntelliSense errors with MinGW (stop using `-fms-extensions` by default). [#2443](https://github.com/Microsoft/vscode-cpptools/issues/2443), [#2623](https://github.com/Microsoft/vscode-cpptools/issues/2623)
|
||||
* Fix error squiggles sometimes not updating after typing. [#2448](https://github.com/Microsoft/vscode-cpptools/issues/2448)
|
||||
* Add support for Mac framework paths in `compile_commands.json`. [#2508](https://github.com/Microsoft/vscode-cpptools/issues/2508)
|
||||
* Fix IntelliSense-based `Go to Definition` falling back to the Tag Parser for definitions not in the TU. [#2536](https://github.com/Microsoft/vscode-cpptools/issues/2536), [#2677](https://github.com/Microsoft/vscode-cpptools/issues/2677)
|
||||
* Fix IntelliSense-based `Go to Definition` on the identifier of a definition with no declaration. [#2573](https://github.com/Microsoft/vscode-cpptools/issues/2573)
|
||||
* Fix IntelliSense-based `Go to Definition` not falling back to the declaration (in certain cases). [#2574](https://github.com/Microsoft/vscode-cpptools/issues/2574)
|
||||
* Fix IntelliSense-based `Go to Definition` going to the wrong location after edits are made. [#2579](https://github.com/Microsoft/vscode-cpptools/issues/2579)
|
||||
* Fix `Go to Definition` when the `intelliSenseEngineFallback` is `Disabled` and `#include`s are missing. [#2583](https://github.com/Microsoft/vscode-cpptools/issues/2583)
|
||||
* Fix empty `C_Cpp.default.*` settings not being used. [#2584](https://github.com/Microsoft/vscode-cpptools/issues/2584)
|
||||
* Fix quoting around `ssh`'s command (for the debugger). [#2585](https://github.com/Microsoft/vscode-cpptools/issues/2585)
|
||||
* Fix crash on hover (and `Go to Definition`) when using the `Tag Parser`. [#2586](https://github.com/Microsoft/vscode-cpptools/issues/2586)
|
||||
* Fix errors when a workspace folder isn't open. [#2613](https://github.com/Microsoft/vscode-cpptools/issues/2613), [#2691](https://github.com/Microsoft/vscode-cpptools/issues/2691)
|
||||
* Fix `-isystem` without a space after getting ignored in `compile_comamands.json`. [#2629](https://github.com/Microsoft/vscode-cpptools/issues/2629)
|
||||
* Fix Insiders update channel installation bugs. [#2636](https://github.com/Microsoft/vscode-cpptools/issues/2636), [#2685](https://github.com/Microsoft/vscode-cpptools/issues/2685)
|
||||
* Fix IntelliSense-based `Go to Declaration` falling back to the Tag Parser if the definition is also in the TU. [#2642](https://github.com/Microsoft/vscode-cpptools/issues/2642)
|
||||
* Fix the `Disabled` `intelliSenseEngine` setting not working with custom configuration providers. [#2656](https://github.com/Microsoft/vscode-cpptools/issues/2656)
|
||||
|
||||
## Version 0.19.0: September 27, 2018
|
||||
* Change the symbol database to update without needing to save. [#202](https://github.com/Microsoft/vscode-cpptools/issues/202)
|
||||
* Enable IntelliSense-based `Go to Definition` for the current translation unit, including local variables and overloaded operators. [#255](https://github.com/Microsoft/vscode-cpptools/issues/255), [#979](https://github.com/Microsoft/vscode-cpptools/issues/979)
|
||||
* Improved the `Go to Definition` performance with large workspaces and files with lots of `#include`s. [#273](https://github.com/Microsoft/vscode-cpptools/issues/273)
|
||||
* Disable `Go to Definition` for invalid tokens, e.g. comments, strings, keywords, etc. [#559](https://github.com/Microsoft/vscode-cpptools/issues/559)
|
||||
* Add `C_Cpp.updateChannel` setting for easier access to Insider builds of the extension. [#1526](https://github.com/Microsoft/vscode-cpptools/issues/1526)
|
||||
* Add support for v2 of the configuration provider API. [#2237](https://github.com/Microsoft/vscode-cpptools/issues/2237)
|
||||
* Fix bug with parsing definitions in `compile_commands.json`. [#2305](https://github.com/Microsoft/vscode-cpptools/issues/2305)
|
||||
* Fix `sh` failure when attaching to a remote Linux process. [#2444](https://github.com/Microsoft/vscode-cpptools/issues/2444)
|
||||
* Fix incorrect default `cl.exe` macro. [PR #2468](https://github.com/Microsoft/vscode-cpptools/issues/2468)
|
||||
* Fix multiple bugs with the symbols in the Outline view not updating correctly. [#2477](https://github.com/Microsoft/vscode-cpptools/issues/2477), [#2500](https://github.com/Microsoft/vscode-cpptools/issues/2500), [#2504](https://github.com/Microsoft/vscode-cpptools/issues/2504)
|
||||
* Add support for `workspaceFolderBasename` expansion. [#2491](https://github.com/Microsoft/vscode-cpptools/issues/2491)
|
||||
* Gabriel Arjones (@g-arjones) [PR #2495](https://github.com/Microsoft/vscode-cpptools/pull/2495), [PR #2503](https://github.com/Microsoft/vscode-cpptools/pull/2503)
|
||||
* Fix bug with variable resolution. [#2532](https://github.com/Microsoft/vscode-cpptools/issues/2532)
|
||||
* Fix off-by-one bug with hover and `Go to Definition`. [#2535](https://github.com/Microsoft/vscode-cpptools/issues/2535)
|
||||
* Fix for [Microsoft/vscode#54213](https://github.com/Microsoft/vscode/issues/54213)
|
||||
|
||||
## Version 0.18.1: August 17, 2018
|
||||
* Fix 0.18.0 regression causing non-MinGW compilers to use `-fms-extensions` on Windows. [#2424](https://github.com/Microsoft/vscode-cpptools/issues/2424), [#2425](https://github.com/Microsoft/vscode-cpptools/issues/2425)
|
||||
|
||||
## Version 0.18.0: August 17, 2018
|
||||
### New Features
|
||||
* Add the `C_Cpp.intelliSenseEngine` setting value of `Disabled` (for users who only use the debugger). [#785](https://github.com/Microsoft/vscode-cpptools/issues/785)
|
||||
* Add `C_Cpp.workspaceSymbols` setting with default `Just My Code` to filter out system header symbols. [#1119](https://github.com/Microsoft/vscode-cpptools/issues/1119), [#2320](https://github.com/Microsoft/vscode-cpptools/issues/2320)
|
||||
* Add `C_Cpp.inactiveRegionForegroundColor` and `C_Cpp.inactiveRegionBackgroundColor` settings. [#1620](https://github.com/Microsoft/vscode-cpptools/issues/1620), [#2212](https://github.com/Microsoft/vscode-cpptools/issues/2212)
|
||||
* John Patterson (@john-patterson) [PR #2308](https://github.com/Microsoft/vscode-cpptools/pull/2308)
|
||||
* Add `gcc-x64` `intelliSenseMode` and send the correct clang or gcc version to our parser, fixing various IntelliSense errors. [#2112](https://github.com/Microsoft/vscode-cpptools/issues/2112), [#2175](https://github.com/Microsoft/vscode-cpptools/issues/2175), [#2260](https://github.com/Microsoft/vscode-cpptools/issues/2260), [#2299](https://github.com/Microsoft/vscode-cpptools/issues/2299), [#2317](https://github.com/Microsoft/vscode-cpptools/issues/2317)
|
||||
* Make `Go to Definition` on the definition go to the declaration instead. [#2298](https://github.com/Microsoft/vscode-cpptools/issues/2298)
|
||||
* Add multi-pass environment variable resolution allowing variables defined in terms of other variables. [#2057](https://github.com/Microsoft/vscode-cpptools/issues/2057)
|
||||
* John Patterson (@john-patterson) [PR #2322](https://github.com/Microsoft/vscode-cpptools/pull/2322)
|
||||
* Allow users to use `~` for `${userProfile}` on Windows. [PR #2333](https://github.com/Microsoft/vscode-cpptools/pull/2333)
|
||||
* Add support for compiler flags `-fms-extensions` and `-fno-ms-extensions` on Windows (the default for MinGW-based compilers). [#2363](https://github.com/Microsoft/vscode-cpptools/issues/2363)
|
||||
* Make completion "show more results" (i.e. inaccessible members) when invoked a 2nd time. [#2386](https://github.com/Microsoft/vscode-cpptools/issues/2386)
|
||||
|
||||
### Bug Fixes
|
||||
* Fix attach to process for systems without `bash` by using `sh` instead. [#569](https://github.com/Microsoft/vscode-cpptools/issues/569)
|
||||
* Andy Neff (@andyneff) [PR #2340](https://github.com/Microsoft/vscode-cpptools/pull/2340)
|
||||
* Fix IntelliSense crash after hover or completion with `_Complex` types. [#689](https://github.com/Microsoft/vscode-cpptools/issues/689), [#1112](https://github.com/Microsoft/vscode-cpptools/issues/1112)
|
||||
* Fix `files.exclude` not working to exclude non-workspace folders from symbol parsing. [#1066](https://github.com/Microsoft/vscode-cpptools/issues/1066)
|
||||
* Fix `Switch Header/Source` to give results that match the parent folder name before using just the file name. [#1085](https://github.com/Microsoft/vscode-cpptools/issues/1085)
|
||||
* Fix incorrect IntelliSense errors caused by namespace lookup failure when instantiation template arguments in clang mode. [#1395](https://github.com/Microsoft/vscode-cpptools/issues/1395), [#1559](https://github.com/Microsoft/vscode-cpptools/issues/1559), [#1753](https://github.com/Microsoft/vscode-cpptools/issues/1753), [#2272](https://github.com/Microsoft/vscode-cpptools/issues/2272)
|
||||
* Fix missing parameter help when using { for constructors. [#1667](https://github.com/Microsoft/vscode-cpptools/issues/1667)
|
||||
* Fix Mac framework dependencies not being discovered. [#1913](https://github.com/Microsoft/vscode-cpptools/issues/1913)
|
||||
* Fix `compilerPath` not working with `${workspaceFolder}`. [#1982](https://github.com/Microsoft/vscode-cpptools/issues/1982)
|
||||
* Fix red flame getting stuck after modifying `c_cpp_properties.json`. [#2077](https://github.com/Microsoft/vscode-cpptools/issues/2077)
|
||||
* Don't add empty `windowsSDKVersion` if none exists. [#2300](https://github.com/Microsoft/vscode-cpptools/issues/2300)
|
||||
* Fix IntelliSense crash when the gcc-8 type_traits header is used. [#2323](https://github.com/Microsoft/vscode-cpptools/issues/2323), [#2328](https://github.com/Microsoft/vscode-cpptools/issues/2328)
|
||||
* Limit configuration popups to one at a time. [#2324](https://github.com/Microsoft/vscode-cpptools/issues/2324)
|
||||
* Don't show `includePath` code actions if compile commands or custom configuration providers are used. [#2334](https://github.com/Microsoft/vscode-cpptools/issues/2334)
|
||||
* Fix `Cpp.clang_format_path` not accepting environment variables. [#2344](https://github.com/Microsoft/vscode-cpptools/issues/2344)
|
||||
* Fix IntelliSense not working with non-ASCII characters in the WSL install path. [#2351](https://github.com/Microsoft/vscode-cpptools/issues/2351)
|
||||
* Filter out buggy IntelliSense error `"= delete" can only appear on the first declaration of a function`. [#2352](https://github.com/Microsoft/vscode-cpptools/issues/2352)
|
||||
* Fix IntelliSense failing with WSL if gcc is installed bug g++ isn't. [#2360](https://github.com/Microsoft/vscode-cpptools/issues/2360)
|
||||
* Fix WSL paths starting with `/mnt/` failing to get symbols parsed. [#2361](https://github.com/Microsoft/vscode-cpptools/issues/2361)
|
||||
* Fix IntelliSense process crash when hovering over a designated initializer list with an anonymous struct. [#2370](https://github.com/Microsoft/vscode-cpptools/issues/2370)
|
||||
* Stop showing "File: " in completion details for internal compiler defines. [#2387](https://github.com/Microsoft/vscode-cpptools/issues/2387)
|
||||
* Invoke `Edit Configurations...` when the `Configuration Help` button is clicked. [#2408](https://github.com/Microsoft/vscode-cpptools/issues/2408)
|
||||
* Fix provider configuration prompt not showing for newly added workspace folders. [#2415](https://github.com/Microsoft/vscode-cpptools/issues/2415)
|
||||
* Fix to allow SIGINT to be sent using the kill -2 command when using pipeTransport.
|
||||
|
||||
## Version 0.17.7: July 22, 2018
|
||||
* Fix `Go to Definition` for code scoped with an aliased namespace. [#387](https://github.com/Microsoft/vscode-cpptools/issues/387)
|
||||
* Fix incorrect IntelliSense errors with template template-arguments. [#1014](https://github.com/Microsoft/vscode-cpptools/issues/1014)
|
||||
* Fix crash when using designated initializer lists. [#1440](https://github.com/Microsoft/vscode-cpptools/issues/1440)
|
||||
* Add `windowsSdkVersion` to `c_cpp_properties.json`. [#1585](https://github.com/Microsoft/vscode-cpptools/issues/1585)
|
||||
* Add `${vcpkgRoot}` variable. [#1817](https://github.com/Microsoft/vscode-cpptools/issues/1817)
|
||||
* Fix dangling IntelliSense processes. [#2075](https://github.com/Microsoft/vscode-cpptools/issues/2075), [#2169](https://github.com/Microsoft/vscode-cpptools/issues/2169)
|
||||
* Fix incorrect IntelliSense errors when class template argument deduction is used. [#2101](https://github.com/Microsoft/vscode-cpptools/issues/2101)
|
||||
* Skip automatic parsing of source files in Mac system framework paths. [#2156](https://github.com/Microsoft/vscode-cpptools/issues/2156)
|
||||
* Fix `Edit Configurations...` not working after `c_cpp_properties.json` is deleted. [#2214](https://github.com/Microsoft/vscode-cpptools/issues/2214)
|
||||
* Fix indexing of the entire root drive on Windows when no is folder open. [#2216](https://github.com/Microsoft/vscode-cpptools/issues/2216)
|
||||
* Disable the config provider message for headers outside the workspace and when debugging. [#2221](https://github.com/Microsoft/vscode-cpptools/issues/2221)
|
||||
* Add `Change Configuration Provider...` command. [#2224](https://github.com/Microsoft/vscode-cpptools/issues/2224)
|
||||
* Fix out-of-memory crash with `#include` code actions when no folder is open. [#2225](https://github.com/Microsoft/vscode-cpptools/issues/2225)
|
||||
* Fix `intelliSenseMode` with custom config providers on Windows. [#2228](https://github.com/Microsoft/vscode-cpptools/issues/2228)
|
||||
* Fix formatting not working on Windows if the VC++ 2015 redist isn't installed. [#2232](https://github.com/Microsoft/vscode-cpptools/issues/2232)
|
||||
* Fix variables not resolving in `macFrameworkPath`. [#2234](https://github.com/Microsoft/vscode-cpptools/issues/2234)
|
||||
* Fix `Go to Definition` not working for macros followed by `.` or `->`. [#2245](https://github.com/Microsoft/vscode-cpptools/issues/2245)
|
||||
* Fix `#include` autocomplete with Mac framework headers. [#2251](https://github.com/Microsoft/vscode-cpptools/issues/2251)
|
||||
* Fix for debugging to support empty arguments for debuggee. [#2258](https://github.com/Microsoft/vscode-cpptools/issues/2258)
|
||||
* Fix `Go to Definition` bug (missing symbols outside the workspace). [#2281](https://github.com/Microsoft/vscode-cpptools/issues/2281)
|
||||
* Add a setting to silence configuration provider warnings. [#2292](https://github.com/Microsoft/vscode-cpptools/issues/2292)
|
||||
* Fix for debugging async Visual C++ causing debugger to hang.
|
||||
* Fix `main` snippet.
|
||||
|
||||
## Version 0.17.6: July 2, 2018
|
||||
* Fix the database icon getting stuck with recursive includes. [#2104](https://github.com/Microsoft/vscode-cpptools/issues/2104)
|
||||
* Fix the red flame appearing late with recursive includes. [#2105](https://github.com/Microsoft/vscode-cpptools/issues/2105)
|
||||
* Fix source files being parsed in system directories. [#2156](https://github.com/Microsoft/vscode-cpptools/issues/2156)
|
||||
* Fix internal document corruption (visible after formatting) when edits are made too soon after activation. [#2162](https://github.com/Microsoft/vscode-cpptools/issues/2162)
|
||||
* Fix a crash when saving with recursive includes. [#2173](https://github.com/Microsoft/vscode-cpptools/issues/2173)
|
||||
* Fix a crash when the `includePath` or `browse.path` is `"**"`. [#2174](https://github.com/Microsoft/vscode-cpptools/issues/2174)
|
||||
* Fix IntelliSense for WSL without g++ installed. [#2178](https://github.com/Microsoft/vscode-cpptools/issues/2178)
|
||||
* Fix random IntelliSense (completion) failures due to edits being delayed. [#2184](https://github.com/Microsoft/vscode-cpptools/issues/2184)
|
||||
* Fix database deletion failure with non-ASCII file paths on Windows. [#2205](https://github.com/Microsoft/vscode-cpptools/issues/2205)
|
||||
* Fix `Go to Definition` results with `var::` and `var->`, and filter out invalid constructor results. [#2207](https://github.com/Microsoft/vscode-cpptools/issues/2207)
|
||||
* Fix a performance bug with recursive includes.
|
||||
* Fixed a CPU usage problem on Mac related to system frameworks parsing.
|
||||
* Keep the IntelliSense process around for 10 seconds after a file is closed in case it's needed again.
|
||||
* Added an API so build system extensions can provide IntelliSense configurations for source files. More details at [npmjs.com](https://www.npmjs.com/package/vscode-cpptools).
|
||||
* Fix automatic argument quoting when debugging with gdb/lldb to include when the argument has a '(' or ')' in it. Also escape existing '"' symbols.
|
||||
* Removed `-` in `ps` call for ProcessPicker and RemoteProcessPicker. [#2183](https://github.com/Microsoft/vscode-cpptools/issues/2183)
|
||||
|
||||
## Version 0.17.5: June 21, 2018
|
||||
* Detect `compile_commands.json` and show prompt to use it. [#1297](https://github.com/Microsoft/vscode-cpptools/issues/1297)
|
||||
* Change inactive regions from gray to translucent. [#1907](https://github.com/Microsoft/vscode-cpptools/issues/1907)
|
||||
@@ -198,7 +325,7 @@
|
||||
## Version 0.13.0: September 25, 2017
|
||||
* Reference highlighting is now provided by the extension for both IntelliSense engines.
|
||||
* Parameter help is now provided by both IntelliSense engines.
|
||||
* Light bulbs (code actions) for #include errors now suggest potential paths to add to the `includePath` based on a recursive search of the `browse.path`. [#846](https://github.com/Microsoft/vscode-cpptools/issues/846)
|
||||
* Light bulbs (code actions) for `#include` errors now suggest potential paths to add to the `includePath` based on a recursive search of the `browse.path`. [#846](https://github.com/Microsoft/vscode-cpptools/issues/846)
|
||||
* Browse database now removes old symbols when `browse.path` changes. [#262](https://github.com/Microsoft/vscode-cpptools/issues/262)
|
||||
* Add `*` on new lines after a multiline comment with `/**` is started. [#579](https://github.com/Microsoft/vscode-cpptools/issues/579)
|
||||
* Fix `Go to Definition`, completion, and parameter hints for partially scoped members. [#635](https://github.com/Microsoft/vscode-cpptools/issues/635)
|
||||
@@ -394,7 +521,7 @@
|
||||
* Switch between header and source.
|
||||
* Control which files are processed under include path.
|
||||
|
||||
## Version 0.6.1: June 03, 2016
|
||||
## Version 0.6.1: June 3, 2016
|
||||
* Bug fixes.
|
||||
|
||||
## Version 0.6.0: May 24, 2016
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ This preview release of the extension adds language support for C/C++ to Visual
|
||||
* Quick Info (Hover)
|
||||
* Error Squiggles
|
||||
* Debugging
|
||||
* Support for debugging Windows (PDB, Mingw/Cygwin), Linux and OS X applications
|
||||
* Support for debugging Windows (PDB, MinGW/Cygwin), Linux and macOS applications
|
||||
* Line by line code stepping
|
||||
* Breakpoints (including conditional and function breakpoints)
|
||||
* Variable inspection
|
||||
|
||||
@@ -205,11 +205,8 @@
|
||||
<tr>
|
||||
<td>
|
||||
<div>
|
||||
<h2 class="caption">June 2018 Update</h2>
|
||||
<div>Thank you for installing the C/C++ extension. In the June update, we focused on workspace configuration improvements.<br/>
|
||||
<br/>
|
||||
We dramatically improved the performance of recursive includes (e.g. using <code>${workspaceFolder}/**</code> in your <code>includePath</code>), we will prompt you to
|
||||
use compile_commands.json if it is found in your workspace, and we made the <code>browse.path</code> setting optional in the case that it is identical to your <code>includePath</code>.<br/>
|
||||
<h2 class="caption">October 2018 Update</h2>
|
||||
<div>Thank you for installing the C/C++ extension. In the October update, we finished our semantic-aware implementation of Go To Definition. We also added IntegratedTerminal support for debugging.<br/>
|
||||
<br/>
|
||||
Additional features and bug fixes are detailed in the <a href="https://github.com/Microsoft/vscode-cpptools/releases">full release notes</a>.</div>
|
||||
</div>
|
||||
@@ -234,9 +231,9 @@
|
||||
<td>
|
||||
<div>
|
||||
<h3 class="caption">Blog Posts</h3>
|
||||
<div><a href="https://blogs.msdn.microsoft.com/vcblog/2018/05/08/visual-studio-code-cc-extension-may-2018-update-intellisense-configuration-just-got-so-much-easier/">May 2018 Update</a></div>
|
||||
<div><a href="https://blogs.msdn.microsoft.com/vcblog/2018/03/29/visual-studio-code-cc-extension-march-2018-update/">March 2018 Update</a></div>
|
||||
<div><a href="https://blogs.msdn.microsoft.com/vcblog/2018/02/20/visual-studio-code-cc-extension-feb-2018-update/">February 2018 Update</a></div>
|
||||
<div><a href="https://blogs.msdn.microsoft.com/vcblog/2018/08/23/visual-studio-code-c-c-extension-august-2018-update/">August 2018 Update</a></div>
|
||||
<div><a href="https://blogs.msdn.microsoft.com/vcblog/2018/07/25/visual-studio-code-cc-extension-july-2018-update-and-intellisense-auto-configuration-for-cmake/">July 2018 Update</a></div>
|
||||
<div><a href="https://blogs.msdn.microsoft.com/vcblog/2018/06/21/visual-studio-code-cc-extension-june-2018-update/">June 2018 Update</a></div>
|
||||
<div><a href="https://blogs.msdn.microsoft.com/vcblog/2016/03/31/cc-extension-for-visual-studio-code/">C/C++ Extension anouncement</a></div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
|
||||
THIRD-PARTY SOFTWARE NOTICES AND INFORMATION
|
||||
|
||||
THIRD PARTY SOFTWARE NOTICES AND INFORMATION
|
||||
Do Not Translate or Localize
|
||||
|
||||
Microsoft C/C++ Extension for Visual Studio Code incorporates components from the projects listed below. Microsoft licenses these components under the Microsoft C/C++ Extension for Visual Studio Code license terms, except as noted. The original copyright notices and the licenses under which Microsoft received such components are set forth below for informational purposes. Microsoft reserves all rights not expressly granted herein, whether by implication, estoppel or otherwise.
|
||||
This Microsoft product may utilize material made available by third parties under the following license terms. Any source code that Microsoft is required to make available can be found at http://3rdpartysource.microsoft.com. You may also obtain a copy of any such source code (during the period provided by the license) by sending a check or money order for US $5.00 to:
|
||||
|
||||
Source Code Compliance Team
|
||||
Microsoft Corporation
|
||||
One Microsoft Way
|
||||
Redmond, WA 98052 USA
|
||||
|
||||
Please write “Source for [OSS PROJECT NAME]” in the memo line of your payment.
|
||||
|
||||
|
||||
1. agent-base (https://github.com/TooTallNate/node-agent-base)
|
||||
2. ANTLR (http://www.antlr2.org/)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"defaults": [
|
||||
"--clang",
|
||||
"--pack_alignment",
|
||||
"8"
|
||||
],
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"-D_MSC_FULL_VER=191426428",
|
||||
"-D_MSC_BUILD=0",
|
||||
"-D_M_X64=100",
|
||||
"-D_AMD64=100"
|
||||
"-D_M_AMD64=100"
|
||||
],
|
||||
"defaults_op" : "merge"
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"defaults": [
|
||||
"--clang",
|
||||
"--pack_alignment",
|
||||
"8"
|
||||
],
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"-D_MSC_FULL_VER=191426428",
|
||||
"-D_MSC_BUILD=0",
|
||||
"-D_M_X64=100",
|
||||
"-D_AMD64=100"
|
||||
"-D_M_AMD64=100"
|
||||
],
|
||||
"defaults_op" : "merge"
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"defaults": [
|
||||
"--clang",
|
||||
"--pack_alignment",
|
||||
"8"
|
||||
],
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"-D_MSC_FULL_VER=191426428",
|
||||
"-D_MSC_BUILD=0",
|
||||
"-D_M_X64=100",
|
||||
"-D_AMD64=100"
|
||||
"-D_M_AMD64=100"
|
||||
],
|
||||
"defaults_op" : "merge"
|
||||
}
|
||||
@@ -58,6 +58,11 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"windowsSdkVersion": {
|
||||
"description": "Version of the Windows SDK include path to use on Windows, e.g. '10.0.17134.0'.",
|
||||
"type": "string",
|
||||
"pattern": "^\\d{2}\\.\\d{1}\\.\\d{5}\\.\\d{1}$|^8\\.1$"
|
||||
},
|
||||
"defines": {
|
||||
"description": "A list of preprocessor definitions for the IntelliSense engine to use while parsing files. Optionally, use = to set a value, e.g. VERSION=1.",
|
||||
"type": "array",
|
||||
@@ -66,10 +71,11 @@
|
||||
}
|
||||
},
|
||||
"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.",
|
||||
"description": "If set, it overrides the default mode used by the IntelliSense engine. Windows defaults to msvc-x64, Linux defaults to gcc-x64, and Mac default to clang-x64.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"msvc-x64",
|
||||
"gcc-x64",
|
||||
"clang-x64",
|
||||
"${default}"
|
||||
]
|
||||
@@ -81,6 +87,10 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"configurationProvider": {
|
||||
"description": "The id of a VS Code extension that can provide IntelliSense configuration information for source files.",
|
||||
"type": "string"
|
||||
},
|
||||
"browse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -111,7 +121,7 @@
|
||||
"type": "object",
|
||||
"description": "Custom variables that can be reused anywhere in this file using the ${variable} or ${env:variable} syntax.",
|
||||
"patternProperties": {
|
||||
"(?!^workspaceFolder$)(?!^workspaceRoot$)(?!^default$)(^.+$)": {
|
||||
"(?!^workspaceFolder$)(?!^workspaceRoot$)(?!^workspaceFolderBasename$)(?!^default$)(^.+$)": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string"
|
||||
|
||||
+2
-2574
File diff suppressed because it is too large
Load Diff
@@ -67,7 +67,7 @@
|
||||
},
|
||||
"main": {
|
||||
"prefix": "main",
|
||||
"body": "\nint main(int argc, char const *argv[])\n{\n\t${1:/* code */}\n\treturn 0;\n}\n",
|
||||
"body": "main(int argc, char const *argv[])\n{\n\t${1:/* code */}\n\treturn 0;\n}\n",
|
||||
"description": "Code snippet for main()",
|
||||
"scope": "source.c, source.objc, source.c++, source.objc++"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"intelliSenseEngine_default_percentage": 100,
|
||||
"defaultIntelliSenseEngine": 100,
|
||||
"recursiveIncludes": 100
|
||||
"recursiveIncludes": 100,
|
||||
"gotoDefIntelliSense": 100
|
||||
}
|
||||
+34
-31
@@ -12,41 +12,39 @@ const mocha = require('gulp-mocha');
|
||||
const fs = require('fs');
|
||||
const optionsSchemaGenerator = require('./out/tools/GenerateOptionsSchema');
|
||||
|
||||
gulp.task('allTests', () => {
|
||||
gulp.start('unitTests');
|
||||
gulp.start('integrationTests');
|
||||
});
|
||||
gulp.task('unitTests', (done) => {
|
||||
env.set({
|
||||
CODE_TESTS_PATH: "./out/test/unitTests",
|
||||
});
|
||||
|
||||
gulp.task('unitTests', () => {
|
||||
gulp.src('./out/test/unitTests', {read: false}).pipe(
|
||||
mocha({
|
||||
ui: "tdd"
|
||||
return gulp.src('./test/runVsCodeTestsWithAbsolutePaths.js', {read: false})
|
||||
.pipe(mocha({ ui: "tdd" }))
|
||||
.once('error', err => {
|
||||
done();
|
||||
process.exit(1);
|
||||
})
|
||||
).once('error', err => {
|
||||
process.exit(1);
|
||||
})
|
||||
.once('end', () => {
|
||||
process.exit();
|
||||
})
|
||||
.once('end', () => {
|
||||
done();
|
||||
process.exit();
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('integrationTests', () => {
|
||||
gulp.task('integrationTests', (done) => {
|
||||
env.set({
|
||||
CODE_TESTS_PATH: "./out/test/integrationTests",
|
||||
CODE_TESTS_WORKSPACE: "./test/integrationTests/testAssets/SimpleCppProject"
|
||||
}
|
||||
);
|
||||
gulp.src('./test/runVsCodeTestsWithAbsolutePaths.js', {read: false}).pipe(
|
||||
mocha({
|
||||
ui: "tdd",
|
||||
delay: true
|
||||
});
|
||||
|
||||
return gulp.src('./test/runVsCodeTestsWithAbsolutePaths.js', {read: false})
|
||||
.pipe(mocha({ ui: "tdd" }))
|
||||
.once('error', err => {
|
||||
done();
|
||||
process.exit(1);
|
||||
})
|
||||
).once('error', err => {
|
||||
process.exit(1);
|
||||
})
|
||||
.once('end', () => {
|
||||
process.exit();
|
||||
})
|
||||
.once('end', () => {
|
||||
done();
|
||||
process.exit();
|
||||
});
|
||||
});
|
||||
|
||||
/// Misc Tasks
|
||||
@@ -67,7 +65,7 @@ const lintReporter = (output, file, options) => {
|
||||
};
|
||||
|
||||
gulp.task('tslint', () => {
|
||||
gulp.src(allTypeScript)
|
||||
return gulp.src(allTypeScript)
|
||||
.pipe(tslint({
|
||||
program: require('tslint').Linter.createProgram("./tsconfig.json"),
|
||||
configuration: "./tslint.json"
|
||||
@@ -78,14 +76,19 @@ gulp.task('tslint', () => {
|
||||
}))
|
||||
});
|
||||
|
||||
gulp.task('pr-check', () => {
|
||||
gulp.task('pr-check', (done) => {
|
||||
const packageJson = JSON.parse(fs.readFileSync('./package.json').toString());
|
||||
if (packageJson.activationEvents.length !== 1 && packageJson.activationEvents[0] !== '*') {
|
||||
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`.');
|
||||
done();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
gulp.task('generateOptionsSchema', () => {
|
||||
gulp.task('generateOptionsSchema', (done) => {
|
||||
optionsSchemaGenerator.generateOptionsSchema();
|
||||
|
||||
done();
|
||||
});
|
||||
Generated
+2671
-357
File diff suppressed because it is too large
Load Diff
+91
-14
@@ -2,7 +2,7 @@
|
||||
"name": "cpptools",
|
||||
"displayName": "C/C++",
|
||||
"description": "C/C++ IntelliSense, debugging, and code browsing.",
|
||||
"version": "0.17.5",
|
||||
"version": "0.20.0",
|
||||
"publisher": "ms-vscode",
|
||||
"preview": true,
|
||||
"icon": "LanguageCCPP_color_128x.png",
|
||||
@@ -63,7 +63,7 @@
|
||||
],
|
||||
"configuration": {
|
||||
"type": "object",
|
||||
"title": "C/C++ Configuration",
|
||||
"title": "C/C++",
|
||||
"properties": {
|
||||
"C_Cpp.clang_format_path": {
|
||||
"type": [
|
||||
@@ -104,10 +104,11 @@
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Default",
|
||||
"Tag Parser"
|
||||
"Tag Parser",
|
||||
"Disabled"
|
||||
],
|
||||
"default": "Default",
|
||||
"description": "Controls the IntelliSense provider. \"Tag Parser\" provides \"fuzzy\" results that are not context-aware. \"Default\" provides context-aware results and is in preview mode - member list, hover tooltips, and error squiggles are currently implemented. Features not yet implemented in the new default engine will use the tag parser engine instead.",
|
||||
"description": "Controls the IntelliSense provider. \"Tag Parser\" provides \"fuzzy\" results that are not context-aware. \"Default\" provides context-aware results. \"Disabled\" turns off C/C++ language service features.",
|
||||
"scope": "resource"
|
||||
},
|
||||
"C_Cpp.intelliSenseEngineFallback": {
|
||||
@@ -154,6 +155,24 @@
|
||||
"minimum": 0.1,
|
||||
"maximum": 1
|
||||
},
|
||||
"C_Cpp.inactiveRegionForegroundColor": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"default": null,
|
||||
"description": "Controls the font coloring of inactive preprocessor blocks. Input is in the form a hexadecimal color code or a valid Theme Color. If not set, this defaults to the syntax coloring scheme of the editor. This setting only applies when inactive region dimming is enabled.",
|
||||
"scope": "resource"
|
||||
},
|
||||
"C_Cpp.inactiveRegionBackgroundColor": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"default": null,
|
||||
"description": "Controls the background coloring of inactive preprocessor blocks. Input is in the form a hexadecimal color code or a valid Theme Color. If not set, this defaults to transparent. This setting only applies when inactive region dimming is enabled.",
|
||||
"scope": "resource"
|
||||
},
|
||||
"C_Cpp.formatting": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
@@ -201,6 +220,16 @@
|
||||
"description": "Controls whether parsing of the non-active workspace files uses sleeps to avoid using 100% CPU. The values highest/high/medium/low correspond to approximately 100/75/50/25% CPU usage.",
|
||||
"scope": "resource"
|
||||
},
|
||||
"C_Cpp.workspaceSymbols": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"All",
|
||||
"Just My Code"
|
||||
],
|
||||
"default": "Just My Code",
|
||||
"description": "The symbols to include in the query results when 'Go to Symbol in Workspace' is invoked",
|
||||
"scope": "resource"
|
||||
},
|
||||
"C_Cpp.exclusionPolicy": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
@@ -250,6 +279,16 @@
|
||||
"description": "Defines the editor behavior for when the Enter key is pressed inside a multiline or single line comment block.",
|
||||
"scope": "resource"
|
||||
},
|
||||
"C_Cpp.configurationWarnings": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Enabled",
|
||||
"Disabled"
|
||||
],
|
||||
"default": "Enabled",
|
||||
"description": "Determines whether pop up notifications will be shown when a configuration provider extension is unable to provide a configuration for a source file.",
|
||||
"scope": "resource"
|
||||
},
|
||||
"C_Cpp.default.includePath": {
|
||||
"type": [
|
||||
"array",
|
||||
@@ -286,6 +325,16 @@
|
||||
"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.windowsSdkVersion": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"default": null,
|
||||
"description": "Version of the Windows SDK include path to use on Windows, e.g. '10.0.17134.0'.",
|
||||
"pattern": "^\\d{2}\\.\\d{1}\\.\\d{5}\\.\\d{1}$|^8\\.1$",
|
||||
"scope": "resource"
|
||||
},
|
||||
"C_Cpp.default.compileCommands": {
|
||||
"type": [
|
||||
"string",
|
||||
@@ -314,6 +363,7 @@
|
||||
],
|
||||
"enum": [
|
||||
"msvc-x64",
|
||||
"gcc-x64",
|
||||
"clang-x64"
|
||||
],
|
||||
"default": null,
|
||||
@@ -359,6 +409,15 @@
|
||||
"description": "The value to use in a configuration if \"cppStandard\" is either not specified or set to \"${default}\".",
|
||||
"scope": "resource"
|
||||
},
|
||||
"C_Cpp.default.configurationProvider": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"default": null,
|
||||
"description": "The value to use in a configuration if \"configurationProvider\" is either not specified or set to \"${default}\".",
|
||||
"scope": "resource"
|
||||
},
|
||||
"C_Cpp.default.browse.path": {
|
||||
"type": [
|
||||
"array",
|
||||
@@ -397,6 +456,16 @@
|
||||
"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"
|
||||
},
|
||||
"C_Cpp.updateChannel": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Default",
|
||||
"Insiders"
|
||||
],
|
||||
"default": "Default",
|
||||
"description": "Set to \"Insiders\" to automatically download and install the latest Insiders builds of the extension, which include upcoming features and bug fixes.",
|
||||
"scope": "resource"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -406,6 +475,11 @@
|
||||
"title": "Select a Configuration...",
|
||||
"category": "C/Cpp"
|
||||
},
|
||||
{
|
||||
"command": "C_Cpp.ConfigurationProviderSelect",
|
||||
"title": "Change Configuration Provider...",
|
||||
"category": "C/Cpp"
|
||||
},
|
||||
{
|
||||
"command": "C_Cpp.ConfigurationEdit",
|
||||
"title": "Edit Configurations...",
|
||||
@@ -1313,7 +1387,9 @@
|
||||
"devDependencies": {
|
||||
"@types/mocha": "^2.2.43",
|
||||
"@types/node": "^8.0.46",
|
||||
"gulp": "3.9.1",
|
||||
"async-child-process": "^1.1.1",
|
||||
"child-process": "^1.0.2",
|
||||
"gulp": "^4.0.0",
|
||||
"gulp-env": "0.4.0",
|
||||
"gulp-mocha": "5.0.0",
|
||||
"gulp-tslint": "8.1.2",
|
||||
@@ -1332,16 +1408,17 @@
|
||||
"minimatch": "~3.0.4",
|
||||
"mkdirp": "~0.5.1",
|
||||
"tmp": "~0.0.33",
|
||||
"vscode-cpptools": "2.1.1",
|
||||
"vscode-debugadapter": "~1.24.0",
|
||||
"vscode-debugprotocol": "~1.24.0",
|
||||
"vscode-extension-telemetry": "~0.0.11",
|
||||
"vscode-extension-telemetry": "~0.1.0",
|
||||
"vscode-languageclient": "3.5.1",
|
||||
"yauzl": "~2.8.0"
|
||||
},
|
||||
"runtimeDependencies": [
|
||||
{
|
||||
"description": "C/C++ language components (Linux / x86_64)",
|
||||
"url": "https://go.microsoft.com/fwlink/?linkid=875587",
|
||||
"url": "https://go.microsoft.com/fwlink/?linkid=2026727",
|
||||
"platforms": [
|
||||
"linux"
|
||||
],
|
||||
@@ -1355,7 +1432,7 @@
|
||||
},
|
||||
{
|
||||
"description": "C/C++ language components (Linux / x86)",
|
||||
"url": "https://go.microsoft.com/fwlink/?linkid=875588",
|
||||
"url": "https://go.microsoft.com/fwlink/?linkid=2026568",
|
||||
"platforms": [
|
||||
"linux"
|
||||
],
|
||||
@@ -1371,7 +1448,7 @@
|
||||
},
|
||||
{
|
||||
"description": "C/C++ language components (OS X)",
|
||||
"url": "https://go.microsoft.com/fwlink/?linkid=875589",
|
||||
"url": "https://go.microsoft.com/fwlink/?linkid=2026569",
|
||||
"platforms": [
|
||||
"darwin"
|
||||
],
|
||||
@@ -1382,7 +1459,7 @@
|
||||
},
|
||||
{
|
||||
"description": "C/C++ language components (Windows)",
|
||||
"url": "https://go.microsoft.com/fwlink/?linkid=875590",
|
||||
"url": "https://go.microsoft.com/fwlink/?linkid=2026728",
|
||||
"platforms": [
|
||||
"win32"
|
||||
],
|
||||
@@ -1436,7 +1513,7 @@
|
||||
},
|
||||
{
|
||||
"description": "Mono Framework Assemblies",
|
||||
"url": "https://go.microsoft.com/fwlink/?LinkId=816539",
|
||||
"url": "https://go.microsoft.com/fwlink/?LinkId=2027135",
|
||||
"platforms": [
|
||||
"linux",
|
||||
"darwin"
|
||||
@@ -1445,7 +1522,7 @@
|
||||
},
|
||||
{
|
||||
"description": "Mono Runtime (Linux / x86)",
|
||||
"url": "https://go.microsoft.com/fwlink/?LinkId=816540",
|
||||
"url": "https://go.microsoft.com/fwlink/?LinkId=2027410",
|
||||
"platforms": [
|
||||
"linux"
|
||||
],
|
||||
@@ -1460,7 +1537,7 @@
|
||||
},
|
||||
{
|
||||
"description": "Mono Runtime (Linux / x86_64)",
|
||||
"url": "https://go.microsoft.com/fwlink/?LinkId=816541",
|
||||
"url": "https://go.microsoft.com/fwlink/?LinkId=2027416",
|
||||
"platforms": [
|
||||
"linux"
|
||||
],
|
||||
@@ -1473,7 +1550,7 @@
|
||||
},
|
||||
{
|
||||
"description": "Mono Runtime (OS X)",
|
||||
"url": "https://go.microsoft.com/fwlink/?LinkId=816542",
|
||||
"url": "https://go.microsoft.com/fwlink/?LinkId=2027403",
|
||||
"platforms": [
|
||||
"darwin"
|
||||
],
|
||||
|
||||
@@ -120,10 +120,10 @@ export class RemoteAttachPicker {
|
||||
|
||||
private getRemoteOSAndProcesses(pipeCmd: string): Promise<AttachItem[]> {
|
||||
// Commands to get OS and processes
|
||||
const command: string = `bash -c 'uname && if [ $(uname) == "Linux" ] ; then ${PsProcessParser.psLinuxCommand} ; elif [ $(uname) == "Darwin" ] ; ` +
|
||||
const command: string = `sh -c 'uname && if [ $(uname) = "Linux" ] ; then ${PsProcessParser.psLinuxCommand} ; elif [ $(uname) = "Darwin" ] ; ` +
|
||||
`then ${PsProcessParser.psDarwinCommand}; fi'`;
|
||||
|
||||
return execChildProcess(`${pipeCmd} "${command}"`, null, this._channel).then(output => {
|
||||
return execChildProcess(`${pipeCmd} '${command}'`, null, this._channel).then(output => {
|
||||
// OS will be on first line
|
||||
// Processess will follow if listed
|
||||
let lines: string[] = output.split(/\r?\n/);
|
||||
|
||||
@@ -31,37 +31,39 @@ abstract class CppConfigurationProvider implements vscode.DebugConfigurationProv
|
||||
* Try to add all missing attributes to the debug configuration being launched.
|
||||
*/
|
||||
resolveDebugConfiguration(folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration, token?: vscode.CancellationToken): vscode.ProviderResult<vscode.DebugConfiguration> {
|
||||
// Fail if cppvsdbg type is running on non-Windows
|
||||
if (config.type === 'cppvsdbg' && os.platform() !== 'win32') {
|
||||
vscode.window.showErrorMessage("Debugger of type: 'cppvsdbg' is only available on Windows. Use type: 'cppdbg' on the current OS platform.");
|
||||
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 (config) {
|
||||
// Fail if cppvsdbg type is running on non-Windows
|
||||
if (config.type === 'cppvsdbg' && os.platform() !== 'win32') {
|
||||
vscode.window.showErrorMessage("Debugger of type: 'cppvsdbg' is only available on Windows. Use type: 'cppdbg' on the current OS platform.");
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (replacedPipeProgram) {
|
||||
config.pipeTransport.pipeProgram = replacedPipeProgram;
|
||||
// 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;
|
||||
}
|
||||
// if config or type is not specified, return null to trigger VS Code to open a configuration file https://github.com/Microsoft/vscode/issues/54213
|
||||
return config && config.type ? config : null;
|
||||
}
|
||||
}
|
||||
|
||||
export class CppVsDbgConfigurationProvider extends CppConfigurationProvider {
|
||||
@@ -71,7 +73,7 @@ export class CppVsDbgConfigurationProvider extends CppConfigurationProvider {
|
||||
}
|
||||
|
||||
export class CppDbgConfigurationProvider extends CppConfigurationProvider {
|
||||
public constructor(provider: IConfigurationAssetProvider) {
|
||||
public constructor(provider: IConfigurationAssetProvider) {
|
||||
super(provider, DebuggerType.cppdbg);
|
||||
}
|
||||
}
|
||||
@@ -101,12 +103,12 @@ abstract class DefaultConfigurationProvider implements IConfigurationAssetProvid
|
||||
|
||||
public getInitialConfigurations(debuggerType: DebuggerType): any {
|
||||
let configurationSnippet: IConfigurationSnippet[] = [];
|
||||
|
||||
|
||||
// Only launch configurations are initial configurations
|
||||
this.configurations.forEach(configuration => {
|
||||
configurationSnippet.push(configuration.GetLaunchConfiguration());
|
||||
configurationSnippet.push(configuration.GetLaunchConfiguration());
|
||||
});
|
||||
|
||||
|
||||
let initialConfigurations: any = configurationSnippet.filter(snippet => snippet.debuggerType === debuggerType && snippet.isInitialConfiguration)
|
||||
.map(snippet => JSON.parse(snippet.bodyText));
|
||||
|
||||
@@ -141,7 +143,7 @@ class WindowsConfigurationProvider extends DefaultConfigurationProvider {
|
||||
constructor() {
|
||||
super();
|
||||
this.configurations = [
|
||||
new MIConfigurations(this.MIMode, this.executable, this.pipeProgram, this.setupCommandsBlock),
|
||||
new MIConfigurations(this.MIMode, this.executable, this.pipeProgram, this.setupCommandsBlock),
|
||||
new PipeTransportConfigurations(this.MIMode, this.executable, this.pipeProgram, this.setupCommandsBlock),
|
||||
new WindowsConfigurations(this.MIMode, this.executable, this.pipeProgram, this.setupCommandsBlock),
|
||||
new WSLConfigurations(this.MIMode, this.executable, this.pipeProgram, this.setupCommandsBlock),
|
||||
@@ -157,7 +159,7 @@ class OSXConfigurationProvider extends DefaultConfigurationProvider {
|
||||
constructor() {
|
||||
super();
|
||||
this.configurations = [
|
||||
new MIConfigurations(this.MIMode, this.executable, this.pipeProgram),
|
||||
new MIConfigurations(this.MIMode, this.executable, this.pipeProgram),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -177,7 +179,7 @@ class LinuxConfigurationProvider extends DefaultConfigurationProvider {
|
||||
constructor() {
|
||||
super();
|
||||
this.configurations = [
|
||||
new MIConfigurations(this.MIMode, this.executable, this.pipeProgram, this.setupCommandsBlock),
|
||||
new MIConfigurations(this.MIMode, this.executable, this.pipeProgram, this.setupCommandsBlock),
|
||||
new PipeTransportConfigurations(this.MIMode, this.executable, this.pipeProgram, this.setupCommandsBlock)
|
||||
];
|
||||
}
|
||||
@@ -220,7 +222,7 @@ export class ConfigurationSnippetProvider implements vscode.CompletionItemProvid
|
||||
|
||||
items.map((item) => {
|
||||
item.insertText = item.insertText + ','; // Add comma
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.resolve(new vscode.CompletionList(items, true));
|
||||
|
||||
@@ -111,8 +111,8 @@ export class PsProcessParser {
|
||||
// Note that comm on Linux systems is truncated to 16 characters:
|
||||
// https://bugzilla.redhat.com/show_bug.cgi?id=429565
|
||||
// Since 'args' contains the full path to the executable, even if truncated, searching will work as desired.
|
||||
public static get psLinuxCommand(): string { return `ps -axww -o pid=,comm=${PsProcessParser.commColumnTitle},args=`; }
|
||||
public static get psDarwinCommand(): string { return `ps -axww -o pid=,comm=${PsProcessParser.commColumnTitle},args= -c`; }
|
||||
public static get psLinuxCommand(): string { return `ps axww -o pid=,comm=${PsProcessParser.commColumnTitle},args=`; }
|
||||
public static get psDarwinCommand(): string { return `ps axww -o pid=,comm=${PsProcessParser.commColumnTitle},args= -c`; }
|
||||
|
||||
// Only public for tests.
|
||||
public static ParseProcessFromPs(processes: string): Process[] {
|
||||
|
||||
@@ -8,13 +8,15 @@ import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import {
|
||||
LanguageClient, LanguageClientOptions, ServerOptions, NotificationType, TextDocumentIdentifier,
|
||||
RequestType, ErrorAction, CloseAction, DidOpenTextDocumentParams
|
||||
RequestType, ErrorAction, CloseAction, DidOpenTextDocumentParams, Range
|
||||
} from 'vscode-languageclient';
|
||||
import { SourceFileConfigurationItem, WorkspaceBrowseConfiguration, SourceFileConfiguration, Version } from 'vscode-cpptools';
|
||||
import { Status } from 'vscode-cpptools/out/testApi';
|
||||
import * as util from '../common';
|
||||
import * as configs from './configurations';
|
||||
import { CppSettings, OtherSettings } from './settings';
|
||||
import * as telemetry from '../telemetry';
|
||||
import { PersistentState } from './persistentState';
|
||||
import { PersistentState, PersistentFolderState } from './persistentState';
|
||||
import { UI, getUI } from './ui';
|
||||
import { ClientCollection } from './clientCollection';
|
||||
import { createProtocolFilter } from './protocolFilter';
|
||||
@@ -22,11 +24,14 @@ import { DataBinding } from './dataBinding';
|
||||
import minimatch = require("minimatch");
|
||||
import * as logger from '../logger';
|
||||
import { updateLanguageConfigurations } from './extension';
|
||||
import { CustomConfigurationProvider, SourceFileConfigurationItem } from '../api';
|
||||
import { CancellationTokenSource } from 'vscode';
|
||||
import { SettingsTracker, getTracker } from './settingsTracker';
|
||||
import { getTestHook, TestHook } from '../testHook';
|
||||
import { getCustomConfigProviders, CustomConfigurationProviderCollection, CustomConfigurationProvider1 } from '../LanguageServer/customProviders';
|
||||
import { ABTestSettings, getABTestSettings } from '../abTesting';
|
||||
|
||||
let ui: UI;
|
||||
const configProviderTimeout: number = 2000;
|
||||
|
||||
interface NavigationPayload {
|
||||
navigation: string;
|
||||
@@ -88,8 +93,18 @@ interface DecorationRangesPair {
|
||||
ranges: vscode.Range[];
|
||||
}
|
||||
|
||||
// Need to convert vscode.Uri to a string before sending it to the language server.
|
||||
interface SourceFileConfigurationItemAdapter {
|
||||
uri: string;
|
||||
configuration: SourceFileConfiguration;
|
||||
}
|
||||
|
||||
interface CustomConfigurationParams {
|
||||
configurationItems: SourceFileConfigurationItem[];
|
||||
configurationItems: SourceFileConfigurationItemAdapter[];
|
||||
}
|
||||
|
||||
interface CustomBrowseConfigurationParams {
|
||||
browseConfiguration: WorkspaceBrowseConfiguration;
|
||||
}
|
||||
|
||||
interface CompileCommandsPaths {
|
||||
@@ -110,12 +125,14 @@ const ResetDatabaseNotification: NotificationType<void, void> = new Notification
|
||||
const PauseParsingNotification: NotificationType<void, void> = new NotificationType<void, void>('cpptools/pauseParsing');
|
||||
const ResumeParsingNotification: NotificationType<void, void> = new NotificationType<void, void>('cpptools/resumeParsing');
|
||||
const ActiveDocumentChangeNotification: NotificationType<TextDocumentIdentifier, void> = new NotificationType<TextDocumentIdentifier, void>('cpptools/activeDocumentChange');
|
||||
const TextEditorSelectionChangeNotification: NotificationType<vscode.Position, void> = new NotificationType<vscode.Position, void>('cpptools/textEditorSelectionChange');
|
||||
const TextEditorSelectionChangeNotification: NotificationType<Range, void> = new NotificationType<Range, void>('cpptools/textEditorSelectionChange');
|
||||
const ChangeFolderSettingsNotification: NotificationType<FolderSettingsParams, void> = new NotificationType<FolderSettingsParams, void>('cpptools/didChangeFolderSettings');
|
||||
const ChangeCompileCommandsNotification: NotificationType<FileChangedParams, void> = new NotificationType<FileChangedParams, void>('cpptools/didChangeCompileCommands');
|
||||
const ChangeSelectedSettingNotification: NotificationType<FolderSelectedSettingParams, void> = new NotificationType<FolderSelectedSettingParams, void>('cpptools/didChangeSelectedSetting');
|
||||
const IntervalTimerNotification: NotificationType<void, void> = new NotificationType<void, void>('cpptools/onIntervalTimer');
|
||||
const CustomConfigurationNotification: NotificationType<CustomConfigurationParams, void> = new NotificationType<CustomConfigurationParams, void>('cpptools/didChangeCustomConfiguration');
|
||||
const CustomBrowseConfigurationNotification: NotificationType<CustomBrowseConfigurationParams, void> = new NotificationType<CustomBrowseConfigurationParams, void>('cpptools/didChangeCustomBrowseConfiguration');
|
||||
const ClearCustomConfigurationsNotification: NotificationType<void, void> = new NotificationType<void, void>('cpptools/clearCustomConfigurations');
|
||||
|
||||
// Notifications from the server
|
||||
const ReloadWindowNotification: NotificationType<void, void> = new NotificationType<void, void>('cpptools/reloadWindow');
|
||||
@@ -127,6 +144,35 @@ const DebugProtocolNotification: NotificationType<OutputNotificationBody, void>
|
||||
const DebugLogNotification: NotificationType<OutputNotificationBody, void> = new NotificationType<OutputNotificationBody, void>('cpptools/debugLog');
|
||||
const InactiveRegionNotification: NotificationType<InactiveRegionParams, void> = new NotificationType<InactiveRegionParams, void>('cpptools/inactiveRegions');
|
||||
const CompileCommandsPathsNotification: NotificationType<CompileCommandsPaths, void> = new NotificationType<CompileCommandsPaths, void>('cpptools/compileCommandsPaths');
|
||||
const UpdateClangFormatPathNotification: NotificationType<string, void> = new NotificationType<string, void>('cpptools/updateClangFormatPath');
|
||||
|
||||
class BlockingTask<T> {
|
||||
private dependency: BlockingTask<any>;
|
||||
private done: boolean = false;
|
||||
private promise: Promise<T>;
|
||||
|
||||
constructor(task: () => T, dependency?: BlockingTask<any>) {
|
||||
this.promise = new Promise<T>(async (resolve, reject) => {
|
||||
try {
|
||||
let result: T = await task();
|
||||
resolve(result);
|
||||
this.done = true;
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
this.done = true;
|
||||
}
|
||||
});
|
||||
this.dependency = dependency;
|
||||
}
|
||||
|
||||
public get Done(): boolean {
|
||||
return this.done && (!this.dependency || this.dependency.Done);
|
||||
}
|
||||
|
||||
public then(onSucceeded: (value: T) => any, onRejected: (err) => any): Promise<any> {
|
||||
return this.promise.then(onSucceeded, onRejected);
|
||||
}
|
||||
}
|
||||
|
||||
let failureMessageShown: boolean = false;
|
||||
|
||||
@@ -148,12 +194,15 @@ export interface Client {
|
||||
RootUri: vscode.Uri;
|
||||
Name: string;
|
||||
TrackedDocuments: Set<vscode.TextDocument>;
|
||||
onDidChangeSettings(): void;
|
||||
onDidChangeSettings(): { [key: string] : string };
|
||||
onDidChangeVisibleTextEditors(editors: vscode.TextEditor[]): void;
|
||||
onDidChangeCustomConfigurations(provider: CustomConfigurationProvider): void;
|
||||
onRegisterCustomConfigurationProvider(provider: CustomConfigurationProvider1): Thenable<void>;
|
||||
updateCustomConfigurations(requestingProvider?: CustomConfigurationProvider1): Thenable<void>;
|
||||
updateCustomBrowseConfiguration(requestingProvider?: CustomConfigurationProvider1): Thenable<void>;
|
||||
provideCustomConfiguration(document: vscode.TextDocument): Promise<void>;
|
||||
getCurrentConfigName(): Thenable<string>;
|
||||
takeOwnership(document: vscode.TextDocument): void;
|
||||
runBlockingTask<T>(task: Thenable<T>): Thenable<T>;
|
||||
runBlockingThenableWithTimeout(thenable: () => Thenable<any>, ms: number, tokenSource?: CancellationTokenSource): Thenable<any>;
|
||||
queueTask<T>(task: () => Thenable<T>): Thenable<T>;
|
||||
requestWhenReady(request: () => Thenable<any>): Thenable<any>;
|
||||
notifyWhenReady(notify: () => void): void;
|
||||
requestGoToDeclaration(): Thenable<void>;
|
||||
@@ -161,13 +210,13 @@ export interface Client {
|
||||
requestNavigationList(document: vscode.TextDocument): Thenable<string>;
|
||||
activeDocumentChanged(document: vscode.TextDocument): void;
|
||||
activate(): void;
|
||||
selectionChanged(selection: vscode.Position): void;
|
||||
sendCustomConfigurations(configs: SourceFileConfigurationItem[]): void;
|
||||
selectionChanged(selection: Range): void;
|
||||
resetDatabase(): void;
|
||||
deactivate(): void;
|
||||
pauseParsing(): void;
|
||||
resumeParsing(): void;
|
||||
handleConfigurationSelectCommand(): void;
|
||||
handleConfigurationProviderSelectCommand(): void;
|
||||
handleShowParsingCommands(): void;
|
||||
handleConfigurationEditCommand(): void;
|
||||
handleAddToIncludePathCommand(path: string): void;
|
||||
@@ -196,6 +245,7 @@ class DefaultClient implements Client {
|
||||
private isSupported: boolean = true;
|
||||
private inactiveRegionsDecorations = new Map<string, DecorationRangesPair>();
|
||||
private settingsTracker: SettingsTracker;
|
||||
private configurationProvider: string;
|
||||
|
||||
// The "model" that is displayed via the UI (status bar).
|
||||
private model: ClientModel = {
|
||||
@@ -228,6 +278,10 @@ class DefaultClient implements Client {
|
||||
return this.trackedDocuments;
|
||||
}
|
||||
|
||||
private get AdditionalEnvironment(): { [key: string]: string | string[] } {
|
||||
return { workspaceFolderBasename: this.Name };
|
||||
}
|
||||
|
||||
private getName(workspaceFolder?: vscode.WorkspaceFolder): string {
|
||||
return workspaceFolder ? workspaceFolder.name : "untitled";
|
||||
}
|
||||
@@ -239,8 +293,7 @@ class DefaultClient implements Client {
|
||||
* @see notifyWhenReady(notify)
|
||||
*/
|
||||
|
||||
private pendingTask: Thenable<any>;
|
||||
private pendingRequests: number = 0;
|
||||
private pendingTask: BlockingTask<void>;
|
||||
|
||||
constructor(allClients: ClientCollection, workspaceFolder?: vscode.WorkspaceFolder) {
|
||||
try {
|
||||
@@ -253,34 +306,36 @@ class DefaultClient implements Client {
|
||||
ui.bind(this);
|
||||
|
||||
// requests/notifications are deferred until this.languageClient is set.
|
||||
this.runBlockingTask(languageClient.onReady().then(() => {
|
||||
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));
|
||||
this.disposables.push(this.configuration);
|
||||
this.queueBlockingTask(() => languageClient.onReady().then(
|
||||
() => {
|
||||
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));
|
||||
this.disposables.push(this.configuration);
|
||||
|
||||
// The configurations will not be sent to the language server until the default include paths and frameworks have been set.
|
||||
// The event handlers must be set before this happens.
|
||||
languageClient.sendRequest(QueryCompilerDefaultsRequest, {}).then((compilerDefaults: configs.CompilerDefaults) => {
|
||||
this.configuration.CompilerDefaults = compilerDefaults;
|
||||
});
|
||||
// The configurations will not be sent to the language server until the default include paths and frameworks have been set.
|
||||
// The event handlers must be set before this happens.
|
||||
languageClient.sendRequest(QueryCompilerDefaultsRequest, {}).then((compilerDefaults: configs.CompilerDefaults) => {
|
||||
this.configuration.CompilerDefaults = compilerDefaults;
|
||||
});
|
||||
|
||||
this.languageClient = languageClient;
|
||||
this.settingsTracker = getTracker(this.RootUri);
|
||||
telemetry.logLanguageServerEvent("NonDefaultInitialCppSettings", this.settingsTracker.getUserModifiedSettings());
|
||||
failureMessageShown = false;
|
||||
this.languageClient = languageClient;
|
||||
this.settingsTracker = getTracker(this.RootUri);
|
||||
telemetry.logLanguageServerEvent("NonDefaultInitialCppSettings", this.settingsTracker.getUserModifiedSettings());
|
||||
failureMessageShown = false;
|
||||
|
||||
// Listen for messages from the language server.
|
||||
this.registerNotifications();
|
||||
this.registerFileWatcher();
|
||||
}, (err) => {
|
||||
this.isSupported = false; // Running on an OS we don't support yet.
|
||||
if (!failureMessageShown) {
|
||||
failureMessageShown = true;
|
||||
vscode.window.showErrorMessage("Unable to start the C/C++ language server. IntelliSense features will be disabled. Error: " + String(err));
|
||||
}
|
||||
}));
|
||||
// Listen for messages from the language server.
|
||||
this.registerNotifications();
|
||||
this.registerFileWatcher();
|
||||
},
|
||||
(err) => {
|
||||
this.isSupported = false; // Running on an OS we don't support yet.
|
||||
if (!failureMessageShown) {
|
||||
failureMessageShown = true;
|
||||
vscode.window.showErrorMessage("Unable to start the C/C++ language server. IntelliSense features will be disabled. Error: " + String(err));
|
||||
}
|
||||
}));
|
||||
} catch (err) {
|
||||
this.isSupported = false; // Running on an OS we don't support yet.
|
||||
if (!failureMessageShown) {
|
||||
@@ -312,6 +367,7 @@ class DefaultClient implements Client {
|
||||
storagePath = path.join(storagePath, serverName);
|
||||
}
|
||||
|
||||
let abTestSettings: ABTestSettings = getABTestSettings();
|
||||
let clientOptions: LanguageClientOptions = {
|
||||
documentSelector: [
|
||||
{ scheme: 'file', language: 'cpp' },
|
||||
@@ -323,7 +379,7 @@ class DefaultClient implements Client {
|
||||
},
|
||||
workspaceFolder: workspaceFolder,
|
||||
initializationOptions: {
|
||||
clang_format_path: settings.clangFormatPath,
|
||||
clang_format_path: util.resolveVariables(settings.clangFormatPath, this.AdditionalEnvironment),
|
||||
clang_format_style: settings.clangFormatStyle,
|
||||
clang_format_fallbackStyle: settings.clangFormatFallbackStyle,
|
||||
clang_format_sortIncludes: settings.clangFormatSortIncludes,
|
||||
@@ -340,11 +396,14 @@ class DefaultClient implements Client {
|
||||
dimInactiveRegions: settings.dimInactiveRegions,
|
||||
loggingLevel: settings.loggingLevel,
|
||||
workspaceParsingPriority: settings.workspaceParsingPriority,
|
||||
workspaceSymbols: settings.workspaceSymbols,
|
||||
exclusionPolicy: settings.exclusionPolicy,
|
||||
preferredPathSeparator: settings.preferredPathSeparator,
|
||||
default: {
|
||||
systemIncludePath: settings.defaultSystemIncludePath
|
||||
}
|
||||
},
|
||||
vcpkg_root: util.getVcpkgRoot(),
|
||||
gotoDefIntelliSense: abTestSettings.UseGoToDefIntelliSense
|
||||
},
|
||||
middleware: createProtocolFilter(this, allClients), // Only send messages directed at this client.
|
||||
errorHandler: {
|
||||
@@ -380,16 +439,22 @@ class DefaultClient implements Client {
|
||||
return new LanguageClient(`cpptools: ${serverName}`, serverOptions, clientOptions);
|
||||
}
|
||||
|
||||
public onDidChangeSettings(): void {
|
||||
let changedSettings: { [key: string] : string} = this.settingsTracker.getChangedSettings();
|
||||
public onDidChangeSettings(): { [key: string] : string } {
|
||||
let changedSettings: { [key: string] : string } = this.settingsTracker.getChangedSettings();
|
||||
|
||||
if (Object.keys(changedSettings).length > 0) {
|
||||
if (changedSettings["commentContinuationPatterns"]) {
|
||||
updateLanguageConfigurations();
|
||||
}
|
||||
if (changedSettings["clang_format_path"]) {
|
||||
let settings: CppSettings = new CppSettings(this.RootUri);
|
||||
this.languageClient.sendNotification(UpdateClangFormatPathNotification, util.resolveVariables(settings.clangFormatPath, this.AdditionalEnvironment));
|
||||
}
|
||||
this.configuration.onDidChangeSettings();
|
||||
telemetry.logLanguageServerEvent("CppSettingsChange", changedSettings, null);
|
||||
}
|
||||
|
||||
return changedSettings;
|
||||
}
|
||||
|
||||
public onDidChangeVisibleTextEditors(editors: vscode.TextEditor[]): void {
|
||||
@@ -405,21 +470,190 @@ class DefaultClient implements Client {
|
||||
}
|
||||
}
|
||||
|
||||
public onDidChangeCustomConfigurations(provider: CustomConfigurationProvider): void {
|
||||
let documentUris: vscode.Uri[] = [];
|
||||
this.trackedDocuments.forEach(document => documentUris.push(document.uri));
|
||||
public onRegisterCustomConfigurationProvider(provider: CustomConfigurationProvider1): Thenable<void> {
|
||||
let onRegistered: () => void = () => {
|
||||
// version 2 providers control the browse.path. Avoid thrashing the tag parser database by pausing parsing until
|
||||
// the provider has sent the correct browse.path value.
|
||||
if (provider.version >= Version.v2) {
|
||||
this.pauseParsing();
|
||||
}
|
||||
};
|
||||
return this.notifyWhenReady(() => {
|
||||
if (!this.RootPath) {
|
||||
return; // There is no c_cpp_properties.json to edit because there is no folder open.
|
||||
}
|
||||
let selectedProvider: string = this.configuration.CurrentConfigurationProvider;
|
||||
if (!selectedProvider) {
|
||||
let ask: PersistentFolderState<boolean> = new PersistentFolderState<boolean>("Client.registerProvider", true, this.RootPath);
|
||||
if (ask.Value) {
|
||||
ui.showConfigureCustomProviderMessage(() => {
|
||||
let folderStr: string = (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 1) ? "the '" + this.Name + "'" : "this";
|
||||
const message: string = `${provider.name} would like to configure IntelliSense for ${folderStr} folder.`;
|
||||
const allow: string = "Allow";
|
||||
const dontAllow: string = "Don't Allow";
|
||||
const askLater: string = "Ask Me Later";
|
||||
|
||||
return vscode.window.showInformationMessage(message, allow, dontAllow, askLater).then(result => {
|
||||
switch (result) {
|
||||
case allow: {
|
||||
this.configuration.updateCustomConfigurationProvider(provider.extensionId).then(() => {
|
||||
onRegistered();
|
||||
telemetry.logLanguageServerEvent("customConfigurationProvider", { "providerId": provider.extensionId });
|
||||
});
|
||||
ask.Value = false;
|
||||
return true;
|
||||
}
|
||||
case dontAllow: {
|
||||
ask.Value = false;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
},
|
||||
() => ask.Value = false);
|
||||
}
|
||||
} else if (selectedProvider === provider.extensionId) {
|
||||
onRegistered();
|
||||
telemetry.logLanguageServerEvent("customConfigurationProvider", { "providerId": provider.extensionId });
|
||||
} else if (selectedProvider === provider.name) {
|
||||
onRegistered();
|
||||
this.configuration.updateCustomConfigurationProvider(provider.extensionId); // v0 -> v1 upgrade. Update the configurationProvider in c_cpp_properties.json
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public updateCustomConfigurations(requestingProvider?: CustomConfigurationProvider1): Thenable<void> {
|
||||
return this.notifyWhenReady(() => {
|
||||
if (!this.configurationProvider) {
|
||||
return;
|
||||
}
|
||||
let currentProvider: CustomConfigurationProvider1 = getCustomConfigProviders().get(this.configurationProvider);
|
||||
if (!currentProvider || (requestingProvider && requestingProvider.extensionId !== currentProvider.extensionId) || this.trackedDocuments.size === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let tokenSource: CancellationTokenSource = new CancellationTokenSource();
|
||||
let documentUris: vscode.Uri[] = [];
|
||||
this.trackedDocuments.forEach(document => documentUris.push(document.uri));
|
||||
|
||||
let task: () => Thenable<SourceFileConfigurationItem[]> = () => {
|
||||
return currentProvider.provideConfigurations(documentUris, tokenSource.token);
|
||||
};
|
||||
this.queueTaskWithTimeout(task, configProviderTimeout, tokenSource).then(configs => this.sendCustomConfigurations(configs), () => {});
|
||||
});
|
||||
}
|
||||
|
||||
public updateCustomBrowseConfiguration(requestingProvider?: CustomConfigurationProvider1): Thenable<void> {
|
||||
return this.notifyWhenReady(() => {
|
||||
if (!this.configurationProvider) {
|
||||
return;
|
||||
}
|
||||
console.log("updateCustomBrowseConfiguration");
|
||||
let currentProvider: CustomConfigurationProvider1 = getCustomConfigProviders().get(this.configurationProvider);
|
||||
if (!currentProvider || (requestingProvider && requestingProvider.extensionId !== currentProvider.extensionId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let tokenSource: CancellationTokenSource = new CancellationTokenSource();
|
||||
let task: () => Thenable<WorkspaceBrowseConfiguration> = async () => {
|
||||
if (await currentProvider.canProvideBrowseConfiguration(tokenSource.token)) {
|
||||
return currentProvider.provideBrowseConfiguration(tokenSource.token);
|
||||
}
|
||||
if (currentProvider.version >= Version.v2) {
|
||||
console.warn("failed to provide browse configuration");
|
||||
}
|
||||
return Promise.reject("");
|
||||
};
|
||||
this.queueTaskWithTimeout(task, configProviderTimeout, tokenSource).then(
|
||||
async config => {
|
||||
await this.sendCustomBrowseConfiguration(config);
|
||||
this.resumeParsing();
|
||||
},
|
||||
() => {});
|
||||
});
|
||||
}
|
||||
|
||||
public async provideCustomConfiguration(document: vscode.TextDocument): Promise<void> {
|
||||
let tokenSource: CancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
if (documentUris.length === 0) {
|
||||
return;
|
||||
let providers: CustomConfigurationProviderCollection = getCustomConfigProviders();
|
||||
if (providers.size === 0) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
console.log("provideCustomConfiguration");
|
||||
let providerId: string|undefined = await this.getCustomConfigurationProviderId();
|
||||
if (!providerId) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
this.runBlockingThenableWithTimeout(() => {
|
||||
return provider.provideConfigurations(documentUris, tokenSource.token);
|
||||
}, 1000, tokenSource).then((configs: SourceFileConfigurationItem[]) => {
|
||||
this.sendCustomConfigurations(configs);
|
||||
});
|
||||
let providerName: string = providerId;
|
||||
let configName: string = await this.getCurrentConfigName();
|
||||
const notReadyMessage: string = `${providerName} is not ready`;
|
||||
let provideConfigurationAsync: () => Thenable<SourceFileConfigurationItem[]> = async () => {
|
||||
// The config requests that we use a provider, try to get IntelliSense configuration info from that provider.
|
||||
try {
|
||||
let provider: CustomConfigurationProvider1|null = providers.get(providerId);
|
||||
if (provider) {
|
||||
if (!provider.isReady) {
|
||||
return Promise.reject(notReadyMessage);
|
||||
}
|
||||
|
||||
providerName = provider.name;
|
||||
if (await provider.canProvideConfiguration(document.uri, tokenSource.token)) {
|
||||
return provider.provideConfigurations([document.uri], tokenSource.token);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
}
|
||||
console.warn("failed to provide configuration");
|
||||
return Promise.reject("");
|
||||
};
|
||||
|
||||
return this.queueTaskWithTimeout(provideConfigurationAsync, configProviderTimeout, tokenSource).then(
|
||||
(configs: SourceFileConfigurationItem[]) => {
|
||||
if (configs && configs.length > 0) {
|
||||
this.sendCustomConfigurations(configs, true);
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
if (err === notReadyMessage) {
|
||||
return;
|
||||
}
|
||||
let settings: CppSettings = new CppSettings(this.RootUri);
|
||||
if (settings.configurationWarnings === "Enabled" && !this.isExternalHeader(document) && !vscode.debug.activeDebugSession) {
|
||||
const dismiss: string = "Dismiss";
|
||||
const disable: string = "Disable Warnings";
|
||||
let message: string = `'${providerName}' is unable to provide IntelliSense configuration information for '${document.uri.fsPath}'. ` +
|
||||
`Settings from the '${configName}' configuration will be used instead.`;
|
||||
if (err) {
|
||||
message += ` (${err})`;
|
||||
}
|
||||
|
||||
vscode.window.showInformationMessage(message, dismiss, disable).then(response => {
|
||||
switch (response) {
|
||||
case disable: {
|
||||
settings.toggleSetting("configurationWarnings", "Enabled", "Disabled");
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private isExternalHeader(document: vscode.TextDocument): boolean {
|
||||
return util.isHeader(document) && !document.uri.toString().startsWith(this.RootUri.toString());
|
||||
}
|
||||
|
||||
private getCustomConfigurationProviderId(): Thenable<string|undefined> {
|
||||
return this.queueTask(() => Promise.resolve(this.configuration.CurrentConfigurationProvider));
|
||||
}
|
||||
|
||||
public getCurrentConfigName(): Thenable<string> {
|
||||
return this.queueTask(() => Promise.resolve(this.configuration.CurrentConfiguration.name));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -443,74 +677,85 @@ class DefaultClient implements Client {
|
||||
|
||||
/*************************************************************************************
|
||||
* wait until the all pendingTasks are complete (e.g. language client is ready for use)
|
||||
* before attempting to send messages
|
||||
* before attempting to send messages or operate on the client.
|
||||
*************************************************************************************/
|
||||
public runBlockingTask(task: Thenable<any>): Thenable<any> {
|
||||
if (this.pendingTask) {
|
||||
return this.requestWhenReady(() => { return task; });
|
||||
} else {
|
||||
this.pendingTask = task;
|
||||
return task.then((result) => {
|
||||
this.pendingTask = undefined;
|
||||
return result;
|
||||
}, (error) => {
|
||||
this.pendingTask = undefined;
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public runBlockingThenableWithTimeout(thenable: () => Thenable<any>, ms: number, tokenSource?: CancellationTokenSource): Thenable<any> {
|
||||
let timer: NodeJS.Timer;
|
||||
// Create a promise that rejects in <ms> milliseconds
|
||||
let timeout: Promise<any> = new Promise((resolve, reject) => {
|
||||
timer = setTimeout(() => {
|
||||
clearTimeout(timer);
|
||||
if (tokenSource) {
|
||||
tokenSource.cancel();
|
||||
public queueTask(task: () => Thenable<any>): Thenable<any> {
|
||||
if (this.isSupported) {
|
||||
let nextTask: () => Thenable<any> = async () => {
|
||||
try {
|
||||
return await task();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
reject("Timed out in " + ms + "ms.");
|
||||
}, ms);
|
||||
});
|
||||
|
||||
// Returns a race between our timeout and the passed in promise
|
||||
return this.runBlockingTask(Promise.race([thenable(), timeout]).then((result: any) => {
|
||||
clearTimeout(timer);
|
||||
return result;
|
||||
}, (error: any) => {
|
||||
throw error;
|
||||
}));
|
||||
}
|
||||
|
||||
public requestWhenReady(request: () => Thenable<any>): Thenable<any> {
|
||||
if (this.pendingTask === undefined) {
|
||||
return request();
|
||||
} else if (this.isSupported && this.pendingTask) {
|
||||
this.pendingRequests++;
|
||||
return this.pendingTask.then(() => {
|
||||
this.pendingRequests--;
|
||||
if (this.pendingRequests === 0) {
|
||||
this.pendingTask = undefined;
|
||||
}
|
||||
return request();
|
||||
});
|
||||
};
|
||||
|
||||
if (this.pendingTask && !this.pendingTask.Done) {
|
||||
// We don't want the queue to stall because of a rejected promise.
|
||||
return this.pendingTask.then(nextTask, nextTask);
|
||||
} else {
|
||||
this.pendingTask = undefined;
|
||||
return nextTask();
|
||||
}
|
||||
} else {
|
||||
return Promise.reject("Unsupported client");
|
||||
}
|
||||
}
|
||||
|
||||
public notifyWhenReady(notify: () => void): void {
|
||||
if (this.pendingTask === undefined) {
|
||||
notify();
|
||||
} else if (this.isSupported && this.pendingTask) {
|
||||
this.pendingRequests++;
|
||||
this.pendingTask.then(() => {
|
||||
this.pendingRequests--;
|
||||
if (this.pendingRequests === 0) {
|
||||
this.pendingTask = undefined;
|
||||
/**
|
||||
* Queue a task that blocks all future tasks until it completes. This is currently only intended to be used
|
||||
* during language client startup and for custom configuration providers.
|
||||
* @param task The task that blocks all future tasks
|
||||
*/
|
||||
private queueBlockingTask(task: () => Thenable<void>): Thenable<void> {
|
||||
if (this.isSupported) {
|
||||
this.pendingTask = new BlockingTask<void>(task, this.pendingTask);
|
||||
} else {
|
||||
return Promise.reject("Unsupported client");
|
||||
}
|
||||
}
|
||||
|
||||
private queueTaskWithTimeout(task: () => Thenable<any>, ms: number, cancelToken?: CancellationTokenSource): Thenable<any> {
|
||||
let timer: NodeJS.Timer;
|
||||
// Create a promise that rejects in <ms> milliseconds
|
||||
let timeout: () => Promise<any> = () => new Promise((resolve, reject) => {
|
||||
timer = setTimeout(() => {
|
||||
clearTimeout(timer);
|
||||
if (cancelToken) {
|
||||
cancelToken.cancel();
|
||||
}
|
||||
notify();
|
||||
});
|
||||
reject("Timed out in " + ms + "ms.");
|
||||
}, ms);
|
||||
});
|
||||
|
||||
// Returns a race between our timeout and the passed in promise
|
||||
return this.queueTask(() => {
|
||||
return Promise.race([task(), timeout()]).then(
|
||||
(result: any) => {
|
||||
clearTimeout(timer);
|
||||
return result;
|
||||
},
|
||||
(error: any) => {
|
||||
clearTimeout(timer);
|
||||
throw error;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public requestWhenReady(request: () => Thenable<any>): Thenable<any> {
|
||||
return this.queueTask(request);
|
||||
}
|
||||
|
||||
public notifyWhenReady(notify: () => void, blockingTask?: boolean): Thenable<void> {
|
||||
let task: () => Thenable<void> = () => new Promise(resolve => {
|
||||
notify();
|
||||
resolve();
|
||||
});
|
||||
if (blockingTask) {
|
||||
return this.queueBlockingTask(task);
|
||||
} else {
|
||||
return this.queueTask(task);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -520,7 +765,7 @@ class DefaultClient implements Client {
|
||||
private registerNotifications(): void {
|
||||
console.assert(this.languageClient !== undefined, "This method must not be called until this.languageClient is set in \"onReady\"");
|
||||
|
||||
this.languageClient.onNotification(ReloadWindowNotification, () => this.reloadWindow());
|
||||
this.languageClient.onNotification(ReloadWindowNotification, () => util.promptForReloadWindowDueToSettingsChange());
|
||||
this.languageClient.onNotification(LogTelemetryNotification, (e) => this.logTelemetry(e));
|
||||
this.languageClient.onNotification(ReportNavigationNotification, (e) => this.navigate(e));
|
||||
this.languageClient.onNotification(ReportStatusNotification, (e) => this.updateStatus(e));
|
||||
@@ -591,15 +836,6 @@ class DefaultClient implements Client {
|
||||
* handle notifications coming from the language server
|
||||
*******************************************************/
|
||||
|
||||
private reloadWindow(): void {
|
||||
let reload: string = "Reload";
|
||||
vscode.window.showInformationMessage("Reload the workspace for the settings change to take effect.", reload).then((value: string) => {
|
||||
if (value === reload) {
|
||||
vscode.commands.executeCommand("workbench.action.reloadWindow");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private logTelemetry(notificationBody: TelemetryPayload): void {
|
||||
telemetry.logLanguageServerEvent(notificationBody.event, notificationBody.properties, notificationBody.metrics);
|
||||
}
|
||||
@@ -667,41 +903,51 @@ class DefaultClient implements Client {
|
||||
private updateStatus(notificationBody: ReportStatusNotificationBody): void {
|
||||
let message: string = notificationBody.status;
|
||||
util.setProgress(util.getProgressExecutableSuccess());
|
||||
let testHook: TestHook = getTestHook();
|
||||
if (message.endsWith("Indexing...")) {
|
||||
this.model.isTagParsing.Value = true;
|
||||
testHook.updateStatus(Status.TagParsingBegun);
|
||||
} else if (message.endsWith("Updating IntelliSense...")) {
|
||||
this.model.isUpdatingIntelliSense.Value = true;
|
||||
testHook.updateStatus(Status.IntelliSenseCompiling);
|
||||
} else if (message.endsWith("IntelliSense Ready")) {
|
||||
this.model.isUpdatingIntelliSense.Value = false;
|
||||
testHook.updateStatus(Status.IntelliSenseReady);
|
||||
} else if (message.endsWith("Ready")) { // Tag Parser Ready
|
||||
this.model.isTagParsing.Value = false;
|
||||
testHook.updateStatus(Status.TagParsingDone);
|
||||
util.setProgress(util.getProgressParseRootSuccess());
|
||||
} else if (message.endsWith("No Squiggles")) {
|
||||
util.setIntelliSenseProgress(util.getProgressIntelliSenseNoSquiggles());
|
||||
} else if (message.endsWith("IntelliSense Fallback")) {
|
||||
let showIntelliSenseFallbackMessage: PersistentState<boolean> = new PersistentState<boolean>("CPP.showIntelliSenseFallbackMessage", true);
|
||||
if (showIntelliSenseFallbackMessage.Value) {
|
||||
let learnMorePanel: string = "Learn More";
|
||||
let dontShowAgain: string = "Don't Show Again";
|
||||
let fallbackMsg: string = this.configuration.VcpkgInstalled ?
|
||||
"Update your IntelliSense settings or use Vcpkg to install libraries to help find missing headers." :
|
||||
"Configure your IntelliSense settings to help find missing headers.";
|
||||
vscode.window.showInformationMessage(fallbackMsg, learnMorePanel, dontShowAgain).then((value) => {
|
||||
switch (value) {
|
||||
case learnMorePanel:
|
||||
let uri: vscode.Uri = vscode.Uri.parse(`https://go.microsoft.com/fwlink/?linkid=864631`);
|
||||
vscode.commands.executeCommand('vscode.open', uri);
|
||||
vscode.commands.getCommands(true).then((commands: string[]) => {
|
||||
if (commands.indexOf("workbench.action.problems.focus") >= 0) {
|
||||
vscode.commands.executeCommand("workbench.action.problems.focus");
|
||||
}
|
||||
});
|
||||
break;
|
||||
case dontShowAgain:
|
||||
showIntelliSenseFallbackMessage.Value = false;
|
||||
break;
|
||||
}
|
||||
});
|
||||
ui.showConfigureIncludePathMessage(() => {
|
||||
let learnMorePanel: string = "Configuration Help";
|
||||
let dontShowAgain: string = "Don't Show Again";
|
||||
let fallbackMsg: string = this.configuration.VcpkgInstalled ?
|
||||
"Update your IntelliSense settings or use Vcpkg to install libraries to help find missing headers." :
|
||||
"Configure your IntelliSense settings to help find missing headers.";
|
||||
return vscode.window.showInformationMessage(fallbackMsg, learnMorePanel, dontShowAgain).then((value) => {
|
||||
switch (value) {
|
||||
case learnMorePanel:
|
||||
let uri: vscode.Uri = vscode.Uri.parse(`https://go.microsoft.com/fwlink/?linkid=864631`);
|
||||
vscode.commands.executeCommand('vscode.open', uri);
|
||||
vscode.commands.getCommands(true).then((commands: string[]) => {
|
||||
if (commands.indexOf("workbench.action.problems.focus") >= 0) {
|
||||
vscode.commands.executeCommand("workbench.action.problems.focus");
|
||||
}
|
||||
});
|
||||
this.handleConfigurationEditCommand();
|
||||
break;
|
||||
case dontShowAgain:
|
||||
showIntelliSenseFallbackMessage.Value = false;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
},
|
||||
() => showIntelliSenseFallbackMessage.Value = false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -712,9 +958,11 @@ class DefaultClient implements Client {
|
||||
|
||||
private updateInactiveRegions(params: InactiveRegionParams): void {
|
||||
let settings: CppSettings = new CppSettings(this.RootUri);
|
||||
|
||||
|
||||
let decoration: vscode.TextEditorDecorationType = vscode.window.createTextEditorDecorationType({
|
||||
opacity: settings.inactiveRegionOpacity.toString(),
|
||||
backgroundColor: settings.inactiveRegionBackgroundColor,
|
||||
color: settings.inactiveRegionForegroundColor,
|
||||
rangeBehavior: vscode.DecorationRangeBehavior.ClosedOpen
|
||||
});
|
||||
|
||||
@@ -752,43 +1000,46 @@ class DefaultClient implements Client {
|
||||
}
|
||||
|
||||
private promptCompileCommands(params: CompileCommandsPaths) : void {
|
||||
if (this.configuration.Configurations[this.configuration.CurrentConfiguration].compileCommands !== undefined) {
|
||||
if (this.configuration.CurrentConfiguration.compileCommands !== undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
let showCompileCommandsSelection: PersistentState<boolean> = new PersistentState<boolean>("CPP.showCompileCommandsSelection", true);
|
||||
if (!showCompileCommandsSelection.Value) {
|
||||
let ask: PersistentState<boolean> = new PersistentState<boolean>("CPP.showCompileCommandsSelection", true);
|
||||
if (!ask.Value) {
|
||||
return;
|
||||
}
|
||||
|
||||
let compileCommandStr: string = params.paths.length > 1 ? "a compile_commands.json file" : params.paths[0];
|
||||
let folderStr: string = (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 1) ? "the " + this.Name : "this";
|
||||
let folderStr: string = (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 1) ? "the '" + this.Name + "'" : "this";
|
||||
const message: string = `Would you like to use ${compileCommandStr} to auto-configure IntelliSense for ${folderStr} folder?`;
|
||||
|
||||
const yes: string = "Yes";
|
||||
const notNow: string = "Not Now";
|
||||
const dontAskAgain: string = "Don't Ask Again";
|
||||
vscode.window.showInformationMessage(message, yes, notNow, dontAskAgain).then((value) => {
|
||||
switch (value) {
|
||||
case yes:
|
||||
if (params.paths.length > 1) {
|
||||
ui.showCompileCommands(params.paths).then((index) => {
|
||||
ui.showConfigureCompileCommandsMessage(() => {
|
||||
const yes: string = "Yes";
|
||||
const no: string = "No";
|
||||
const askLater: string = "Ask Me Later";
|
||||
return vscode.window.showInformationMessage(message, yes, no, askLater).then(async (value) => {
|
||||
switch (value) {
|
||||
case yes:
|
||||
if (params.paths.length > 1) {
|
||||
let index: number = await ui.showCompileCommands(params.paths);
|
||||
if (index < 0) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
this.configuration.setCompileCommands(params.paths[index]);
|
||||
});
|
||||
} else {
|
||||
this.configuration.setCompileCommands(params.paths[0]);
|
||||
}
|
||||
break;
|
||||
case notNow:
|
||||
break;
|
||||
case dontAskAgain:
|
||||
showCompileCommandsSelection.Value = false;
|
||||
break;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.configuration.setCompileCommands(params.paths[0]);
|
||||
}
|
||||
return true;
|
||||
case askLater:
|
||||
break;
|
||||
case no:
|
||||
ask.Value = false;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
},
|
||||
() => ask.Value = false);
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
@@ -835,7 +1086,7 @@ class DefaultClient implements Client {
|
||||
this.resumeParsing();
|
||||
}
|
||||
|
||||
public selectionChanged(selection: vscode.Position): void {
|
||||
public selectionChanged(selection: Range): void {
|
||||
this.notifyWhenReady(() => this.languageClient.sendNotification(TextEditorSelectionChangeNotification, selection));
|
||||
}
|
||||
|
||||
@@ -866,11 +1117,18 @@ class DefaultClient implements Client {
|
||||
private onConfigurationsChanged(configurations: configs.Configuration[]): void {
|
||||
let params: FolderSettingsParams = {
|
||||
configurations: configurations,
|
||||
currentConfiguration: this.configuration.CurrentConfiguration
|
||||
currentConfiguration: this.configuration.CurrentConfigurationIndex
|
||||
};
|
||||
this.notifyWhenReady(() => {
|
||||
this.languageClient.sendNotification(ChangeFolderSettingsNotification, params);
|
||||
this.model.activeConfigName.Value = configurations[params.currentConfiguration].name;
|
||||
}).then(() => {
|
||||
let newProvider: string = this.configuration.CurrentConfigurationProvider;
|
||||
if (this.configurationProvider !== newProvider) {
|
||||
this.configurationProvider = newProvider;
|
||||
this.updateCustomConfigurations();
|
||||
this.updateCustomBrowseConfiguration();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -891,11 +1149,81 @@ class DefaultClient implements Client {
|
||||
this.notifyWhenReady(() => this.languageClient.sendNotification(ChangeCompileCommandsNotification, params));
|
||||
}
|
||||
|
||||
public sendCustomConfigurations(configs: SourceFileConfigurationItem[]): void {
|
||||
private isSourceFileConfigurationItem(input: any): input is SourceFileConfigurationItem {
|
||||
return (input && (util.isString(input.uri) || util.isUri(input.uri)) &&
|
||||
input.configuration && util.isArrayOfString(input.configuration.includePath) && util.isArrayOfString(input.configuration.defines) &&
|
||||
util.isString(input.configuration.intelliSenseMode) && util.isString(input.configuration.standard) && util.isOptionalString(input.configuration.compilerPath) &&
|
||||
util.isOptionalArrayOfString(input.configuration.forcedInclude));
|
||||
}
|
||||
|
||||
private sendCustomConfigurations(configs: any, blockingTask?: boolean): void {
|
||||
// configs is marked as 'any' because it is untrusted data coming from a 3rd-party. We need to sanitize it before sending it to the language server.
|
||||
if (!configs || !(configs instanceof Array)) {
|
||||
console.warn("discarding invalid SourceFileConfigurationItems[]: " + configs);
|
||||
return;
|
||||
}
|
||||
|
||||
let settings: CppSettings = new CppSettings(this.RootUri);
|
||||
let out: logger.Logger = logger.getOutputChannelLogger();
|
||||
if (settings.loggingLevel === "Debug") {
|
||||
out.appendLine("Custom configurations received:");
|
||||
}
|
||||
let sanitized: SourceFileConfigurationItemAdapter[] = [];
|
||||
configs.forEach(item => {
|
||||
if (this.isSourceFileConfigurationItem(item)) {
|
||||
sanitized.push({
|
||||
uri: item.uri.toString(),
|
||||
configuration: item.configuration
|
||||
});
|
||||
if (settings.loggingLevel === "Debug") {
|
||||
out.appendLine(` uri: ${item.uri.toString()}`);
|
||||
out.appendLine(` config: ${JSON.stringify(item.configuration, null, 2)}`);
|
||||
}
|
||||
if (item.configuration.includePath.some(path => path.endsWith('**'))) {
|
||||
console.warn("custom include paths should not use recursive includes ('**')");
|
||||
}
|
||||
} else {
|
||||
console.warn("discarding invalid SourceFileConfigurationItem: " + item);
|
||||
}
|
||||
});
|
||||
|
||||
if (sanitized.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let params: CustomConfigurationParams = {
|
||||
configurationItems: configs
|
||||
configurationItems: sanitized
|
||||
};
|
||||
this.notifyWhenReady(() => this.languageClient.sendNotification(CustomConfigurationNotification, params));
|
||||
this.notifyWhenReady(() => this.languageClient.sendNotification(CustomConfigurationNotification, params), blockingTask);
|
||||
}
|
||||
|
||||
private sendCustomBrowseConfiguration(config: any): Thenable<void> {
|
||||
// config is marked as 'any' because it is untrusted data coming from a 3rd-party. We need to sanitize it before sending it to the language server.
|
||||
if (!config || config instanceof Array) {
|
||||
console.warn("discarding invalid WorkspaceBrowseConfiguration: " + config);
|
||||
return Promise.resolve();
|
||||
}
|
||||
let sanitized: WorkspaceBrowseConfiguration = <WorkspaceBrowseConfiguration>config;
|
||||
if (!util.isArrayOfString(sanitized.browsePath) || !util.isOptionalString(sanitized.compilerPath) ||
|
||||
!util.isOptionalString(sanitized.standard) || !util.isOptionalString(sanitized.windowsSdkVersion)) {
|
||||
console.warn("discarding invalid WorkspaceBrowseConfiguration: " + config);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
let settings: CppSettings = new CppSettings(this.RootUri);
|
||||
let out: logger.Logger = logger.getOutputChannelLogger();
|
||||
if (settings.loggingLevel === "Debug") {
|
||||
out.appendLine(`Custom browse configuration received: ${JSON.stringify(sanitized, null, 2)}`);
|
||||
}
|
||||
|
||||
let params: CustomBrowseConfigurationParams = {
|
||||
browseConfiguration: sanitized
|
||||
};
|
||||
return this.notifyWhenReady(() => this.languageClient.sendNotification(CustomBrowseConfigurationNotification, params));
|
||||
}
|
||||
|
||||
private clearCustomConfigurations(): void {
|
||||
this.notifyWhenReady(() => this.languageClient.sendNotification(ClearCustomConfigurationsNotification));
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
@@ -913,6 +1241,29 @@ class DefaultClient implements Client {
|
||||
});
|
||||
}
|
||||
|
||||
public handleConfigurationProviderSelectCommand(): void {
|
||||
this.notifyWhenReady(() => {
|
||||
ui.showConfigurationProviders(this.configuration.CurrentConfigurationProvider)
|
||||
.then(extensionId => {
|
||||
if (extensionId === undefined) {
|
||||
// operation was cancelled.
|
||||
return;
|
||||
}
|
||||
this.configuration.updateCustomConfigurationProvider(extensionId)
|
||||
.then(() => {
|
||||
if (extensionId) {
|
||||
let provider: CustomConfigurationProvider1 = getCustomConfigProviders().get(extensionId);
|
||||
this.updateCustomConfigurations(provider);
|
||||
this.updateCustomBrowseConfiguration(provider);
|
||||
telemetry.logLanguageServerEvent("customConfigurationProvider", { "providerId": extensionId });
|
||||
} else {
|
||||
this.clearCustomConfigurations();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public handleShowParsingCommands(): void {
|
||||
this.notifyWhenReady(() => {
|
||||
ui.showParsingCommands()
|
||||
@@ -986,26 +1337,29 @@ class NullClient implements Client {
|
||||
RootUri: vscode.Uri = vscode.Uri.file("/");
|
||||
Name: string = "(empty)";
|
||||
TrackedDocuments = new Set<vscode.TextDocument>();
|
||||
onDidChangeSettings(): void {}
|
||||
onDidChangeSettings(): { [key: string] : string } { return {}; }
|
||||
onDidChangeVisibleTextEditors(editors: vscode.TextEditor[]): void {}
|
||||
onDidChangeCustomConfigurations(provider: CustomConfigurationProvider): void {}
|
||||
onRegisterCustomConfigurationProvider(provider: CustomConfigurationProvider1): Thenable<void> { return Promise.resolve(); }
|
||||
updateCustomConfigurations(requestingProvider?: CustomConfigurationProvider1): Thenable<void> { return Promise.resolve(); }
|
||||
updateCustomBrowseConfiguration(requestingProvider?: CustomConfigurationProvider1): Thenable<void> { return Promise.resolve(); }
|
||||
provideCustomConfiguration(document: vscode.TextDocument): Promise<void> { return Promise.resolve(); }
|
||||
getCurrentConfigName(): Thenable<string> { return Promise.resolve(""); }
|
||||
takeOwnership(document: vscode.TextDocument): void {}
|
||||
runBlockingTask<T>(task: Thenable<T>): Thenable<T> { return; }
|
||||
runBlockingThenableWithTimeout(thenable: () => Thenable<any>, ms: number, tokenSource?: CancellationTokenSource): Thenable<any> { return; }
|
||||
queueTask<T>(task: () => Thenable<T>): Thenable<T> { return task(); }
|
||||
requestWhenReady(request: () => Thenable<any>): Thenable<any> { return; }
|
||||
notifyWhenReady(notify: () => void): void {}
|
||||
sendCustomConfigurations(configs: SourceFileConfigurationItem[]): void {}
|
||||
requestGoToDeclaration(): Thenable<void> { return Promise.resolve(); }
|
||||
requestSwitchHeaderSource(rootPath: string, fileName: string): Thenable<string> { return Promise.resolve(""); }
|
||||
requestNavigationList(document: vscode.TextDocument): Thenable<string> { return Promise.resolve(""); }
|
||||
activeDocumentChanged(document: vscode.TextDocument): void {}
|
||||
activate(): void {}
|
||||
selectionChanged(selection: vscode.Position): void {}
|
||||
selectionChanged(selection: Range): void {}
|
||||
resetDatabase(): void {}
|
||||
deactivate(): void {}
|
||||
pauseParsing(): void {}
|
||||
resumeParsing(): void {}
|
||||
handleConfigurationSelectCommand(): void {}
|
||||
handleConfigurationProviderSelectCommand(): void {}
|
||||
handleShowParsingCommands(): void {}
|
||||
handleConfigurationEditCommand(): void {}
|
||||
handleAddToIncludePathCommand(path: string): void {}
|
||||
@@ -1015,4 +1369,4 @@ class NullClient implements Client {
|
||||
this.stringEvent.dispose();
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import * as util from '../common';
|
||||
import * as telemetry from '../telemetry';
|
||||
import * as cpptools from './client';
|
||||
import * as path from 'path';
|
||||
import { getCustomConfigProviders } from './customProviders';
|
||||
|
||||
const defaultClientKey: string = "@@default@@";
|
||||
export interface ClientKey {
|
||||
@@ -158,7 +159,7 @@ export class ClientCollection {
|
||||
this.activeClient = this.getClientFor(this.activeDocument.uri);
|
||||
this.activeClient.activeDocumentChanged(this.activeDocument);
|
||||
// may not need this, the navigation UI should not have changed.
|
||||
// this.activeClient.selectionChanged(vscode.window.activeTextEditor.selection.start);
|
||||
// this.activeClient.selectionChanged(Range.create(vscode.window.activeTextEditor.selection.start, vscode.window.activeTextEditor.selection.end);
|
||||
}
|
||||
|
||||
client.dispose();
|
||||
@@ -187,7 +188,9 @@ export class ClientCollection {
|
||||
} else {
|
||||
let key: string = util.asFolder(folder.uri);
|
||||
if (!this.languageClients.has(key)) {
|
||||
this.languageClients.set(key, cpptools.createClient(this, folder));
|
||||
let newClient: cpptools.Client = cpptools.createClient(this, folder);
|
||||
this.languageClients.set(key, newClient);
|
||||
getCustomConfigProviders().forEach(provider => newClient.onRegisterCustomConfigurationProvider(provider));
|
||||
}
|
||||
return this.languageClients.get(key);
|
||||
}
|
||||
|
||||
@@ -12,8 +12,11 @@ import * as telemetry from '../telemetry';
|
||||
import { PersistentFolderState } from './persistentState';
|
||||
import { CppSettings } from './settings';
|
||||
import { ABTestSettings, getABTestSettings } from '../abTesting';
|
||||
import { getCustomConfigProviders } from './customProviders';
|
||||
const configVersion: number = 4;
|
||||
|
||||
type Environment = { [key: string]: string | string[] };
|
||||
|
||||
// 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.
|
||||
@@ -34,7 +37,7 @@ function getDefaultCppProperties(): ConfigurationJson {
|
||||
};
|
||||
}
|
||||
|
||||
interface ConfigurationJson {
|
||||
export interface ConfigurationJson {
|
||||
configurations: Configuration[];
|
||||
env?: {[key: string]: string | string[]};
|
||||
version: number;
|
||||
@@ -47,10 +50,12 @@ export interface Configuration {
|
||||
cppStandard?: string;
|
||||
includePath?: string[];
|
||||
macFrameworkPath?: string[];
|
||||
windowsSdkVersion?: string;
|
||||
defines?: string[];
|
||||
intelliSenseMode?: string;
|
||||
compileCommands?: string;
|
||||
forcedInclude?: string[];
|
||||
configurationProvider?: string;
|
||||
browse?: Browse;
|
||||
}
|
||||
|
||||
@@ -66,6 +71,7 @@ export interface CompilerDefaults {
|
||||
cppStandard: string;
|
||||
includes: string[];
|
||||
frameworks: string[];
|
||||
windowsSdkVersion: string;
|
||||
intelliSenseMode: string;
|
||||
}
|
||||
|
||||
@@ -83,10 +89,11 @@ export class CppProperties {
|
||||
private defaultCppStandard: string = null;
|
||||
private defaultIncludes: string[] = null;
|
||||
private defaultFrameworks: string[] = null;
|
||||
private defaultWindowsSdkVersion: string = null;
|
||||
private vcpkgIncludes: string[] = [];
|
||||
private vcpkgPathReady: boolean = false;
|
||||
private defaultIntelliSenseMode: string = null;
|
||||
private readonly configurationGlobPattern: string = "**/c_cpp_properties.json"; // TODO: probably should be a single file, not all files...
|
||||
private readonly configurationGlobPattern: string = "c_cpp_properties.json";
|
||||
private disposables: vscode.Disposable[] = [];
|
||||
private configurationsChanged = new vscode.EventEmitter<Configuration[]>();
|
||||
private selectionChanged = new vscode.EventEmitter<number>();
|
||||
@@ -109,7 +116,7 @@ export class CppProperties {
|
||||
this.parsePropertiesFile();
|
||||
}
|
||||
if (!this.configurationJson) {
|
||||
this.resetToDefaultSettings(this.CurrentConfiguration === -1);
|
||||
this.resetToDefaultSettings(this.CurrentConfigurationIndex === -1);
|
||||
}
|
||||
|
||||
this.configFileWatcher = vscode.workspace.createFileSystemWatcher(path.join(this.configFolder, this.configurationGlobPattern));
|
||||
@@ -138,7 +145,15 @@ export class CppProperties {
|
||||
public get SelectionChanged(): vscode.Event<number> { return this.selectionChanged.event; }
|
||||
public get CompileCommandsChanged(): vscode.Event<string> { return this.compileCommandsChanged.event; }
|
||||
public get Configurations(): Configuration[] { return this.configurationJson.configurations; }
|
||||
public get CurrentConfiguration(): number { return this.currentConfigurationIndex.Value; }
|
||||
public get CurrentConfigurationIndex(): number { return this.currentConfigurationIndex.Value; }
|
||||
public get CurrentConfiguration(): Configuration { return this.Configurations[this.CurrentConfigurationIndex]; }
|
||||
|
||||
public get CurrentConfigurationProvider(): string|null {
|
||||
if (this.CurrentConfiguration.configurationProvider) {
|
||||
return this.CurrentConfiguration.configurationProvider;
|
||||
}
|
||||
return new CppSettings(this.rootUri).defaultConfigurationProvider;
|
||||
}
|
||||
|
||||
public get ConfigurationNames(): string[] {
|
||||
let result: string[] = [];
|
||||
@@ -152,6 +167,7 @@ export class CppProperties {
|
||||
this.defaultCppStandard = compilerDefaults.cppStandard;
|
||||
this.defaultIncludes = compilerDefaults.includes;
|
||||
this.defaultFrameworks = compilerDefaults.frameworks;
|
||||
this.defaultWindowsSdkVersion = compilerDefaults.windowsSdkVersion;
|
||||
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
|
||||
@@ -168,7 +184,7 @@ export class CppProperties {
|
||||
}
|
||||
|
||||
private onSelectionChanged(): void {
|
||||
this.selectionChanged.fire(this.CurrentConfiguration);
|
||||
this.selectionChanged.fire(this.CurrentConfigurationIndex);
|
||||
}
|
||||
|
||||
private onCompileCommandsChanged(path: string): void {
|
||||
@@ -188,8 +204,8 @@ export class CppProperties {
|
||||
|
||||
private resetToDefaultSettings(resetIndex: boolean): void {
|
||||
this.configurationJson = getDefaultCppProperties();
|
||||
if (resetIndex || this.CurrentConfiguration < 0 ||
|
||||
this.CurrentConfiguration >= this.configurationJson.configurations.length) {
|
||||
if (resetIndex || this.CurrentConfigurationIndex < 0 ||
|
||||
this.CurrentConfigurationIndex >= this.configurationJson.configurations.length) {
|
||||
this.currentConfigurationIndex.Value = this.getConfigIndexForPlatform(this.configurationJson);
|
||||
}
|
||||
this.configurationIncomplete = true;
|
||||
@@ -197,8 +213,12 @@ export class CppProperties {
|
||||
|
||||
private applyDefaultIncludePathsAndFrameworks(): void {
|
||||
if (this.configurationIncomplete && this.defaultIncludes && this.defaultFrameworks && this.vcpkgPathReady) {
|
||||
let configuration: Configuration = this.configurationJson.configurations[this.CurrentConfiguration];
|
||||
let configuration: Configuration = this.CurrentConfiguration;
|
||||
let settings: CppSettings = new CppSettings(this.rootUri);
|
||||
let isUnset: (input: any) => boolean = (input: any) => {
|
||||
// default values for "default" config settings is null.
|
||||
return input === null;
|
||||
};
|
||||
|
||||
// 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
|
||||
@@ -206,58 +226,69 @@ export class CppProperties {
|
||||
|
||||
// Only add settings from the default compiler if user hasn't explicitly set the corresponding VS Code setting.
|
||||
|
||||
if (!settings.defaultIncludePath) {
|
||||
if (isUnset(settings.defaultIncludePath)) {
|
||||
// We don't add system includes to the includePath anymore. The language server has this information.
|
||||
let abTestSettings: ABTestSettings = getABTestSettings();
|
||||
let rootFolder: string = abTestSettings.UseRecursiveIncludes ? "${workspaceFolder}/**" : "${workspaceFolder}";
|
||||
configuration.includePath = [rootFolder].concat(this.vcpkgIncludes);
|
||||
}
|
||||
// browse.path is not set by default anymore. When it is not set, the includePath will be used instead.
|
||||
if (!settings.defaultDefines) {
|
||||
if (isUnset(settings.defaultDefines)) {
|
||||
configuration.defines = (process.platform === 'win32') ? ["_DEBUG", "UNICODE", "_UNICODE"] : [];
|
||||
}
|
||||
if (!settings.defaultMacFrameworkPath && process.platform === 'darwin') {
|
||||
if (isUnset(settings.defaultMacFrameworkPath) && process.platform === 'darwin') {
|
||||
configuration.macFrameworkPath = this.defaultFrameworks;
|
||||
}
|
||||
if (!settings.defaultCompilerPath && this.defaultCompilerPath) {
|
||||
if (isUnset(settings.defaultWindowsSdkVersion) && this.defaultWindowsSdkVersion && process.platform === 'win32') {
|
||||
configuration.windowsSdkVersion = this.defaultWindowsSdkVersion;
|
||||
}
|
||||
if (isUnset(settings.defaultCompilerPath) && this.defaultCompilerPath) {
|
||||
configuration.compilerPath = this.defaultCompilerPath;
|
||||
}
|
||||
if (!settings.defaultCStandard && this.defaultCStandard) {
|
||||
if (isUnset(settings.defaultCStandard) && this.defaultCStandard) {
|
||||
configuration.cStandard = this.defaultCStandard;
|
||||
}
|
||||
if (!settings.defaultCppStandard && this.defaultCppStandard) {
|
||||
if (isUnset(settings.defaultCppStandard) && this.defaultCppStandard) {
|
||||
configuration.cppStandard = this.defaultCppStandard;
|
||||
}
|
||||
if (!settings.defaultIntelliSenseMode) {
|
||||
if (isUnset(settings.defaultIntelliSenseMode)) {
|
||||
configuration.intelliSenseMode = this.defaultIntelliSenseMode;
|
||||
}
|
||||
this.configurationIncomplete = false;
|
||||
}
|
||||
}
|
||||
|
||||
private get ExtendedEnvironment(): Environment {
|
||||
let result: Environment = {};
|
||||
if (this.configurationJson.env) {
|
||||
Object.assign(result, this.configurationJson.env);
|
||||
}
|
||||
|
||||
result["workspaceFolderBasename"] = this.rootUri ? path.basename(this.rootUri.fsPath) : "";
|
||||
return result;
|
||||
}
|
||||
|
||||
private async buildVcpkgIncludePath(): Promise<void> {
|
||||
try {
|
||||
// Check for vcpkg instance and include relevent paths if found.
|
||||
if (await util.checkFileExists(util.getVcpkgPathDescriptorFile())) {
|
||||
let vcpkgRoot: string = await util.readFileText(util.getVcpkgPathDescriptorFile());
|
||||
vcpkgRoot = vcpkgRoot.trim();
|
||||
if (await util.checkDirectoryExists(vcpkgRoot)) {
|
||||
let vcpkgInstalledPath: string = path.join(vcpkgRoot, "/installed");
|
||||
let list: string[] = await util.readDir(vcpkgInstalledPath);
|
||||
if (list !== undefined) {
|
||||
// For every *directory* in the list (non-recursive). Each directory is basically a platform.
|
||||
list.forEach((entry) => {
|
||||
if (entry !== "vcpkg") {
|
||||
let pathToCheck: string = path.join(vcpkgInstalledPath, entry);
|
||||
if (fs.existsSync(pathToCheck)) {
|
||||
let p: string = path.join(pathToCheck, "include");
|
||||
if (fs.existsSync(p)) {
|
||||
this.vcpkgIncludes.push(p);
|
||||
}
|
||||
// Check for vcpkgRoot and include relevent paths if found.
|
||||
let vcpkgRoot: string = util.getVcpkgRoot();
|
||||
if (vcpkgRoot) {
|
||||
let list: string[] = await util.readDir(vcpkgRoot);
|
||||
if (list !== undefined) {
|
||||
// For every *directory* in the list (non-recursive). Each directory is basically a platform.
|
||||
list.forEach((entry) => {
|
||||
if (entry !== "vcpkg") {
|
||||
let pathToCheck: string = path.join(vcpkgRoot, entry);
|
||||
if (fs.existsSync(pathToCheck)) {
|
||||
let p: string = path.join(pathToCheck, "include");
|
||||
if (fs.existsSync(p)) {
|
||||
p = p.replace(/\\/g, "/");
|
||||
p = p.replace(vcpkgRoot, "${vcpkgRoot}");
|
||||
this.vcpkgIncludes.push(p);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {} finally {
|
||||
@@ -285,15 +316,19 @@ export class CppProperties {
|
||||
|
||||
private getIntelliSenseModeForPlatform(name: string): string {
|
||||
// Do the built-in configs first.
|
||||
if (name === "Linux" || name === "Mac") {
|
||||
if (name === "Linux") {
|
||||
return "gcc-x64";
|
||||
} else if (name === "Mac") {
|
||||
return "clang-x64";
|
||||
} else if (name === "Win32") {
|
||||
return "msvc-x64";
|
||||
} else if (process.platform === 'win32') {
|
||||
// Custom configs default to the OS's preference.
|
||||
return "msvc-x64";
|
||||
} else {
|
||||
} else if (process.platform === 'darwin') {
|
||||
return "clang-x64";
|
||||
} else {
|
||||
return "gcc-x64";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,23 +336,51 @@ export class CppProperties {
|
||||
this.handleConfigurationEditCommand((document: vscode.TextDocument) => {
|
||||
telemetry.logLanguageServerEvent("addToIncludePath");
|
||||
this.parsePropertiesFile(); // Clear out any modifications we may have made internally.
|
||||
let config: Configuration = this.configurationJson.configurations[this.CurrentConfiguration];
|
||||
let config: Configuration = 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();
|
||||
this.handleConfigurationChange();
|
||||
});
|
||||
}
|
||||
|
||||
public updateCustomConfigurationProvider(providerId: string): Thenable<void> {
|
||||
return new Promise<void>((resolve) => {
|
||||
if (this.propertiesFile) {
|
||||
this.handleConfigurationEditCommand((document: vscode.TextDocument) => {
|
||||
this.parsePropertiesFile(); // Clear out any modifications we may have made internally.
|
||||
let config: Configuration = this.CurrentConfiguration;
|
||||
if (providerId) {
|
||||
config.configurationProvider = providerId;
|
||||
} else {
|
||||
delete config.configurationProvider;
|
||||
}
|
||||
fs.writeFileSync(this.propertiesFile.fsPath, JSON.stringify(this.configurationJson, null, 4));
|
||||
this.handleConfigurationChange();
|
||||
resolve();
|
||||
});
|
||||
} else {
|
||||
let settings: CppSettings = new CppSettings(this.rootUri);
|
||||
if (providerId) {
|
||||
settings.update("default.configurationProvider", providerId);
|
||||
} else {
|
||||
settings.update("default.configurationProvider", undefined); // delete the setting
|
||||
}
|
||||
this.CurrentConfiguration.configurationProvider = providerId;
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public setCompileCommands(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];
|
||||
let config: Configuration = this.CurrentConfiguration;
|
||||
config.compileCommands = path;
|
||||
fs.writeFileSync(this.propertiesFile.fsPath, JSON.stringify(this.configurationJson, null, 4));
|
||||
this.updateServerOnFolderSettingsChange();
|
||||
this.handleConfigurationChange();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -346,11 +409,11 @@ export class CppProperties {
|
||||
return result;
|
||||
}
|
||||
|
||||
private resolveAndSplit(paths: string[] | undefined, defaultValue: string[]): string[] {
|
||||
private resolveAndSplit(paths: string[] | undefined, defaultValue: string[], env: Environment): string[] {
|
||||
let result: string[] = [];
|
||||
if (paths) {
|
||||
paths.forEach(entry => {
|
||||
let entries: string[] = util.resolveVariables(entry, this.configurationJson.env).split(";").filter(e => e);
|
||||
let entries: string[] = util.resolveVariables(entry, env).split(";").filter(e => e);
|
||||
entries = this.resolveDefaults(entries, defaultValue);
|
||||
result = result.concat(entries);
|
||||
});
|
||||
@@ -358,29 +421,29 @@ export class CppProperties {
|
||||
return result;
|
||||
}
|
||||
|
||||
private resolveVariables(input: string | boolean, defaultValue: string | boolean): string | boolean {
|
||||
private resolveVariables(input: string | boolean, defaultValue: string | boolean, env: Environment): string | boolean {
|
||||
if (input === undefined || input === "${default}") {
|
||||
input = defaultValue;
|
||||
}
|
||||
if (typeof input === "boolean") {
|
||||
return input;
|
||||
}
|
||||
return util.resolveVariables(input, this.configurationJson.env);
|
||||
return util.resolveVariables(input, env);
|
||||
}
|
||||
|
||||
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) {
|
||||
private updateConfiguration(property: string[], defaultValue: string[], env: Environment): string[];
|
||||
private updateConfiguration(property: string, defaultValue: string, env: Environment): string;
|
||||
private updateConfiguration(property: string | boolean, defaultValue: boolean, env: Environment): boolean;
|
||||
private updateConfiguration(property, defaultValue, env): any {
|
||||
if (util.isString(property) || util.isString(defaultValue)) {
|
||||
return this.resolveVariables(property, defaultValue, env);
|
||||
} else if (util.isBoolean(property) || util.isBoolean(defaultValue)) {
|
||||
return this.resolveVariables(property, defaultValue, env);
|
||||
} else if (util.isArrayOfString(property) || util.isArrayOfString(defaultValue)) {
|
||||
if (property) {
|
||||
return this.resolveAndSplit(property, defaultValue);
|
||||
return this.resolveAndSplit(property, defaultValue, env);
|
||||
} else if (property === undefined && defaultValue) {
|
||||
return this.resolveAndSplit(defaultValue, []);
|
||||
return this.resolveAndSplit(defaultValue, [], env);
|
||||
}
|
||||
}
|
||||
return property;
|
||||
@@ -388,18 +451,21 @@ export class CppProperties {
|
||||
|
||||
private updateServerOnFolderSettingsChange(): void {
|
||||
let settings: CppSettings = new CppSettings(this.rootUri);
|
||||
let env: Environment = this.ExtendedEnvironment;
|
||||
for (let i: number = 0; i < this.configurationJson.configurations.length; i++) {
|
||||
let configuration: Configuration = this.configurationJson.configurations[i];
|
||||
|
||||
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);
|
||||
configuration.includePath = this.updateConfiguration(configuration.includePath, settings.defaultIncludePath, env);
|
||||
configuration.defines = this.updateConfiguration(configuration.defines, settings.defaultDefines, env);
|
||||
configuration.macFrameworkPath = this.updateConfiguration(configuration.macFrameworkPath, settings.defaultMacFrameworkPath, env);
|
||||
configuration.windowsSdkVersion = this.updateConfiguration(configuration.windowsSdkVersion, settings.defaultWindowsSdkVersion, env);
|
||||
configuration.forcedInclude = this.updateConfiguration(configuration.forcedInclude, settings.defaultForcedInclude, env);
|
||||
configuration.compileCommands = this.updateConfiguration(configuration.compileCommands, settings.defaultCompileCommands, env);
|
||||
configuration.compilerPath = this.updateConfiguration(configuration.compilerPath, settings.defaultCompilerPath, env);
|
||||
configuration.cStandard = this.updateConfiguration(configuration.cStandard, settings.defaultCStandard, env);
|
||||
configuration.cppStandard = this.updateConfiguration(configuration.cppStandard, settings.defaultCppStandard, env);
|
||||
configuration.intelliSenseMode = this.updateConfiguration(configuration.intelliSenseMode, settings.defaultIntelliSenseMode, env);
|
||||
configuration.configurationProvider = this.updateConfiguration(configuration.configurationProvider, settings.defaultConfigurationProvider, env);
|
||||
|
||||
if (!configuration.browse) {
|
||||
configuration.browse = {};
|
||||
@@ -418,11 +484,11 @@ export class CppProperties {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
configuration.browse.path = this.updateConfiguration(configuration.browse.path, settings.defaultBrowsePath);
|
||||
configuration.browse.path = this.updateConfiguration(configuration.browse.path, settings.defaultBrowsePath, env);
|
||||
}
|
||||
|
||||
configuration.browse.limitSymbolsToIncludedHeaders = this.updateConfiguration(configuration.browse.limitSymbolsToIncludedHeaders, settings.defaultLimitSymbolsToIncludedHeaders);
|
||||
configuration.browse.databaseFilename = this.updateConfiguration(configuration.browse.databaseFilename, settings.defaultDatabaseFilename);
|
||||
configuration.browse.limitSymbolsToIncludedHeaders = this.updateConfiguration(configuration.browse.limitSymbolsToIncludedHeaders, settings.defaultLimitSymbolsToIncludedHeaders, env);
|
||||
configuration.browse.databaseFilename = this.updateConfiguration(configuration.browse.databaseFilename, settings.defaultDatabaseFilename, env);
|
||||
}
|
||||
|
||||
this.updateCompileCommandsFileWatchers();
|
||||
@@ -464,15 +530,21 @@ export class CppProperties {
|
||||
} else {
|
||||
fs.mkdir(this.configFolder, (e: NodeJS.ErrnoException) => {
|
||||
if (!e || e.code === 'EEXIST') {
|
||||
let dirPathEscaped: string = this.configFolder.replace("#", "%23");
|
||||
let fullPathToFile: string = path.join(dirPathEscaped, "c_cpp_properties.json");
|
||||
let filePath: vscode.Uri = vscode.Uri.parse("untitled:" + fullPathToFile);
|
||||
let fullPathToFile: string = path.join(this.configFolder, "c_cpp_properties.json");
|
||||
let filePath: vscode.Uri = vscode.Uri.file(fullPathToFile).with({ scheme: "untitled" });
|
||||
vscode.workspace.openTextDocument(filePath).then((document: vscode.TextDocument) => {
|
||||
let edit: vscode.WorkspaceEdit = new vscode.WorkspaceEdit();
|
||||
if (this.configurationJson) {
|
||||
this.resetToDefaultSettings(true);
|
||||
}
|
||||
this.applyDefaultIncludePathsAndFrameworks();
|
||||
let settings: CppSettings = new CppSettings(this.rootUri);
|
||||
if (settings.defaultConfigurationProvider) {
|
||||
this.configurationJson.configurations.forEach(config => {
|
||||
config.configurationProvider = settings.defaultConfigurationProvider;
|
||||
});
|
||||
settings.update("default.configurationProvider", undefined); // delete the setting
|
||||
}
|
||||
edit.insert(document.uri, new vscode.Position(0, 0), JSON.stringify(this.configurationJson, null, 4));
|
||||
vscode.workspace.applyEdit(edit).then((status) => {
|
||||
// Fix for issue 163
|
||||
@@ -500,8 +572,8 @@ export class CppProperties {
|
||||
// parsePropertiesFile can fail, but it won't overwrite an existing configurationJson in the event of failure.
|
||||
// this.configurationJson should only be undefined here if we have never successfully parsed the propertiesFile.
|
||||
if (this.configurationJson !== undefined) {
|
||||
if (this.CurrentConfiguration < 0 ||
|
||||
this.CurrentConfiguration >= this.configurationJson.configurations.length) {
|
||||
if (this.CurrentConfigurationIndex < 0 ||
|
||||
this.CurrentConfigurationIndex >= this.configurationJson.configurations.length) {
|
||||
// If the index is out of bounds (during initialization or due to removal of configs), fix it.
|
||||
this.currentConfigurationIndex.Value = this.getConfigIndexForPlatform(this.configurationJson);
|
||||
}
|
||||
@@ -529,23 +601,33 @@ 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 >= 0 && this.CurrentConfiguration < this.configurationJson.configurations.length) {
|
||||
this.CurrentConfigurationIndex >= 0 && this.CurrentConfigurationIndex < 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) {
|
||||
if (newJson.configurations[i].name === this.configurationJson.configurations[this.CurrentConfigurationIndex].name) {
|
||||
this.currentConfigurationIndex.Value = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.configurationJson = newJson;
|
||||
if (this.CurrentConfiguration < 0 || this.CurrentConfiguration >= newJson.configurations.length) {
|
||||
if (this.CurrentConfigurationIndex < 0 || this.CurrentConfigurationIndex >= newJson.configurations.length) {
|
||||
this.currentConfigurationIndex.Value = this.getConfigIndexForPlatform(newJson);
|
||||
}
|
||||
|
||||
let dirty: boolean = false;
|
||||
for (let i: number = 0; i < this.configurationJson.configurations.length; i++) {
|
||||
let newId: string = getCustomConfigProviders().checkId(this.configurationJson.configurations[i].configurationProvider);
|
||||
if (newId !== this.configurationJson.configurations[i].configurationProvider) {
|
||||
dirty = true;
|
||||
this.configurationJson.configurations[i].configurationProvider = newId;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove disallowed variable overrides
|
||||
if (this.configurationJson.env) {
|
||||
delete this.configurationJson.env['workspaceRoot'];
|
||||
delete this.configurationJson.env['workspaceFolder'];
|
||||
delete this.configurationJson.env['workspaceFolderBasename'];
|
||||
delete this.configurationJson.env['default'];
|
||||
}
|
||||
|
||||
@@ -553,7 +635,6 @@ export class CppProperties {
|
||||
// the system includes were available.
|
||||
this.configurationIncomplete = false;
|
||||
|
||||
let dirty: boolean = false;
|
||||
if (this.configurationJson.version !== configVersion) {
|
||||
dirty = true;
|
||||
if (this.configurationJson.version === undefined) {
|
||||
@@ -563,7 +644,7 @@ export class CppProperties {
|
||||
if (this.configurationJson.version === 2) {
|
||||
this.updateToVersion3();
|
||||
}
|
||||
|
||||
|
||||
if (this.configurationJson.version === 3) {
|
||||
this.updateToVersion4();
|
||||
} else {
|
||||
@@ -575,7 +656,7 @@ export class CppProperties {
|
||||
if (dirty) {
|
||||
try {
|
||||
fs.writeFileSync(this.propertiesFile.fsPath, JSON.stringify(this.configurationJson, null, 4));
|
||||
} catch {
|
||||
} catch (err) {
|
||||
// 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?)');
|
||||
}
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All Rights Reserved.
|
||||
* See 'LICENSE' in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { CustomConfigurationProvider, Version, SourceFileConfigurationItem, WorkspaceBrowseConfiguration } from 'vscode-cpptools';
|
||||
import { CppSettings } from './settings';
|
||||
|
||||
/**
|
||||
* An interface that is guaranteed to be backward compatible with version 0
|
||||
*/
|
||||
export interface CustomConfigurationProvider1 extends CustomConfigurationProvider {
|
||||
isReady: boolean;
|
||||
readonly isValid: boolean;
|
||||
readonly version: Version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the incoming CustomConfigurationProvider so that we can treat all of them as if they were the same version (e.g. latest)
|
||||
*/
|
||||
class CustomProviderWrapper implements CustomConfigurationProvider1 {
|
||||
private provider: CustomConfigurationProvider;
|
||||
private _isReady: boolean;
|
||||
private _version: Version;
|
||||
|
||||
constructor(provider: CustomConfigurationProvider, version: Version) {
|
||||
this._isReady = version < Version.v2;
|
||||
this.provider = provider;
|
||||
if (provider.extensionId && version === Version.v0) {
|
||||
version = Version.v1; // provider implemented the new API but is interfacing with the extension using the old API version.
|
||||
}
|
||||
this._version = version;
|
||||
}
|
||||
|
||||
public get isReady(): boolean {
|
||||
return this._isReady;
|
||||
}
|
||||
|
||||
public set isReady(ready: boolean) {
|
||||
this._isReady = ready;
|
||||
}
|
||||
|
||||
public get isValid(): boolean {
|
||||
let valid: boolean = !!(this.provider.name && this.provider.canProvideConfiguration && this.provider.provideConfigurations);
|
||||
if (valid && this._version > Version.v0) {
|
||||
valid = !!(this.provider.extensionId && this.provider.dispose);
|
||||
}
|
||||
if (valid && this._version > Version.v1) {
|
||||
valid = !!(this.provider.canProvideBrowseConfiguration && this.provider.provideBrowseConfiguration);
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
public get version(): Version {
|
||||
return this._version;
|
||||
}
|
||||
|
||||
public get name(): string {
|
||||
return this.provider.name;
|
||||
}
|
||||
|
||||
public get extensionId(): string {
|
||||
return this._version === Version.v0 ? this.provider.name : this.provider.extensionId;
|
||||
}
|
||||
|
||||
public canProvideConfiguration(uri: vscode.Uri, token?: vscode.CancellationToken): Thenable<boolean> {
|
||||
return this.provider.canProvideConfiguration(uri, token);
|
||||
}
|
||||
|
||||
public provideConfigurations(uris: vscode.Uri[], token?: vscode.CancellationToken): Thenable<SourceFileConfigurationItem[]> {
|
||||
return this.provider.provideConfigurations(uris, token);
|
||||
}
|
||||
|
||||
public canProvideBrowseConfiguration(token?: vscode.CancellationToken): Thenable<boolean> {
|
||||
return this._version < Version.v2 ? Promise.resolve(false) : this.provider.canProvideBrowseConfiguration(token);
|
||||
}
|
||||
|
||||
public provideBrowseConfiguration(token?: vscode.CancellationToken): Thenable<WorkspaceBrowseConfiguration> {
|
||||
console.assert(this._version >= Version.v2);
|
||||
return this._version < Version.v2 ? Promise.resolve({browsePath: []}) : this.provider.provideBrowseConfiguration(token);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
if (this._version !== Version.v0) {
|
||||
this.provider.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class CustomConfigurationProviderCollection {
|
||||
private providers: Map<string, CustomProviderWrapper> = new Map<string, CustomProviderWrapper>();
|
||||
|
||||
private logProblems(provider: CustomConfigurationProvider, version: Version): void {
|
||||
let missing: string[] = [];
|
||||
if (!provider.name) {
|
||||
missing.push("'name'");
|
||||
}
|
||||
if (version !== Version.v0 && !provider.extensionId) {
|
||||
missing.push("'extensionId'");
|
||||
}
|
||||
if (!provider.canProvideConfiguration) {
|
||||
missing.push("'canProvideConfiguration'");
|
||||
}
|
||||
if (!provider.provideConfigurations) {
|
||||
missing.push("'canProvideConfiguration'");
|
||||
}
|
||||
if (version !== Version.v0 && !provider.dispose) {
|
||||
missing.push("'dispose'");
|
||||
}
|
||||
if (version >= Version.v2 && !provider.canProvideBrowseConfiguration) {
|
||||
missing.push("'canProvideBrowseConfiguration'");
|
||||
}
|
||||
if (version >= Version.v2 && !provider.provideBrowseConfiguration) {
|
||||
missing.push("'provideBrowseConfiguration'");
|
||||
}
|
||||
console.error(`CustomConfigurationProvider was not registered. The following properties are missing from the implementation: ${missing.join(", ")}.`);
|
||||
}
|
||||
|
||||
private getId(provider: string|CustomConfigurationProvider): string {
|
||||
if (typeof provider === "string") {
|
||||
return provider;
|
||||
} else if (provider.extensionId) {
|
||||
return provider.extensionId;
|
||||
} else if (provider.name) {
|
||||
return provider.name;
|
||||
} else {
|
||||
console.error(`invalid provider: ${provider}`);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public get size(): number {
|
||||
return this.providers.size;
|
||||
}
|
||||
|
||||
public add(provider: CustomConfigurationProvider, version: Version): boolean {
|
||||
if (new CppSettings().intelliSenseEngine === "Disabled") {
|
||||
console.warn("IntelliSense is disabled. Provider will not be registered.");
|
||||
return false;
|
||||
}
|
||||
|
||||
let wrapper: CustomProviderWrapper = new CustomProviderWrapper(provider, version);
|
||||
if (!wrapper.isValid) {
|
||||
this.logProblems(provider, version);
|
||||
return false;
|
||||
}
|
||||
|
||||
let exists: boolean = this.providers.has(wrapper.extensionId);
|
||||
if (exists) {
|
||||
let existing: CustomProviderWrapper = this.providers.get(wrapper.extensionId);
|
||||
exists = (existing.version === Version.v0 && wrapper.version === Version.v0);
|
||||
}
|
||||
|
||||
if (!exists) {
|
||||
this.providers.set(wrapper.extensionId, wrapper);
|
||||
} else {
|
||||
console.error(`CustomConfigurationProvider '${wrapper.extensionId}' has already been registered.`);
|
||||
}
|
||||
return !exists;
|
||||
}
|
||||
|
||||
public get(provider: string|CustomConfigurationProvider): CustomConfigurationProvider1|null {
|
||||
let id: string = this.getId(provider);
|
||||
|
||||
if (this.providers.has(id)) {
|
||||
return this.providers.get(id);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public forEach(func: (provider: CustomConfigurationProvider1) => void): void {
|
||||
this.providers.forEach(provider => func(provider));
|
||||
}
|
||||
|
||||
public remove(provider: CustomConfigurationProvider): void {
|
||||
let id: string = this.getId(provider);
|
||||
if (this.providers.has(id)) {
|
||||
this.providers.delete(id);
|
||||
} else {
|
||||
console.warn(`${id} is not registered`);
|
||||
}
|
||||
}
|
||||
|
||||
public checkId(providerId?: string): string {
|
||||
if (!providerId) {
|
||||
return providerId;
|
||||
}
|
||||
let found: CustomConfigurationProvider1[] = [];
|
||||
let noUpdate: boolean = false;
|
||||
this.forEach(provider => {
|
||||
if (provider.extensionId === providerId) {
|
||||
noUpdate = true;
|
||||
} else if (provider.name === providerId && provider.version !== Version.v0) {
|
||||
found.push(provider);
|
||||
}
|
||||
});
|
||||
if (noUpdate) {
|
||||
return providerId;
|
||||
}
|
||||
if (found.length === 1) {
|
||||
return found[0].extensionId;
|
||||
} else if (found.length > 1) {
|
||||
console.warn("duplicate provider name found. Not upgrading.");
|
||||
}
|
||||
return providerId;
|
||||
}
|
||||
}
|
||||
|
||||
let providerCollection: CustomConfigurationProviderCollection = new CustomConfigurationProviderCollection();
|
||||
|
||||
export function getCustomConfigProviders(): CustomConfigurationProviderCollection {
|
||||
return providerCollection;
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import { CancellationTokenSource } from "vscode-jsonrpc";
|
||||
import * as os from 'os';
|
||||
import * as fs from 'fs';
|
||||
import * as util from '../common';
|
||||
import * as telemetry from '../telemetry';
|
||||
@@ -16,8 +16,12 @@ import { ClientCollection } from './clientCollection';
|
||||
import { CppSettings } from './settings';
|
||||
import { PersistentWorkspaceState } from './persistentState';
|
||||
import { getLanguageConfig } from './languageConfig';
|
||||
import { CustomConfigurationProvider, SourceFileConfigurationItem } from '../api';
|
||||
import * as os from 'os';
|
||||
import { getCustomConfigProviders } from './customProviders';
|
||||
import { PlatformInformation } from '../platform';
|
||||
import { Range } from 'vscode-languageclient';
|
||||
import { ChildProcess, spawn, execSync } from 'child_process';
|
||||
import * as tmp from 'tmp';
|
||||
import { getTargetBuildInfo } from '../githubAPI';
|
||||
|
||||
let prevCrashFile: string;
|
||||
let clients: ClientCollection;
|
||||
@@ -26,10 +30,11 @@ let ui: UI;
|
||||
let disposables: vscode.Disposable[] = [];
|
||||
let languageConfigurations: vscode.Disposable[] = [];
|
||||
let intervalTimer: NodeJS.Timer;
|
||||
let insiderUpdateTimer: NodeJS.Timer;
|
||||
let realActivationOccurred: boolean = false;
|
||||
let tempCommands: vscode.Disposable[] = [];
|
||||
let activatedPreviously: PersistentWorkspaceState<boolean>;
|
||||
let customConfigurationProviders: CustomConfigurationProvider[] = [];
|
||||
const insiderUpdateTimerInterval: number = 1000 * 60 * 60;
|
||||
|
||||
/**
|
||||
* activate: set up the extension for language services
|
||||
@@ -77,40 +82,6 @@ export function activate(activationEventOccurred: boolean): void {
|
||||
}
|
||||
}
|
||||
|
||||
export function registerCustomConfigurationProvider(provider: CustomConfigurationProvider): void {
|
||||
customConfigurationProviders.push(provider);
|
||||
|
||||
// Request for configurations from the provider only if realActivationOccurred.
|
||||
// Otherwise, the request will be sent when realActivation is called.
|
||||
if (realActivationOccurred) {
|
||||
onDidChangeCustomConfiguration(provider);
|
||||
}
|
||||
}
|
||||
|
||||
export async function provideCustomConfiguration(document: vscode.TextDocument, client: Client): Promise<void> {
|
||||
let tokenSource: CancellationTokenSource = new CancellationTokenSource();
|
||||
if (customConfigurationProviders.length === 0) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return client.runBlockingThenableWithTimeout(async () => {
|
||||
// Loop through registered providers until one is able to service the current document
|
||||
for (let i: number = 0; i < customConfigurationProviders.length; i++) {
|
||||
if (await customConfigurationProviders[i].canProvideConfiguration(document.uri)) {
|
||||
return customConfigurationProviders[i].provideConfigurations([document.uri]);
|
||||
}
|
||||
}
|
||||
return Promise.reject("No providers found for " + document.uri);
|
||||
}, 1000, tokenSource).then((configs: SourceFileConfigurationItem[]) => {
|
||||
if (configs && configs.length > 0) {
|
||||
client.sendCustomConfigurations(configs);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function onDidChangeCustomConfiguration(customConfigurationProvider: CustomConfigurationProvider): void {
|
||||
clients.forEach((client: Client) => client.onDidChangeCustomConfigurations(customConfigurationProvider));
|
||||
}
|
||||
|
||||
function onDidOpenTextDocument(document: vscode.TextDocument): void {
|
||||
if (document.languageId === "c" || document.languageId === "cpp") {
|
||||
onActivationEvent();
|
||||
@@ -134,6 +105,10 @@ function onActivationEvent(): void {
|
||||
}
|
||||
|
||||
function realActivation(): void {
|
||||
if (new CppSettings().intelliSenseEngine === "Disabled") {
|
||||
throw new Error("Do not activate the extension when IntelliSense is disabled.");
|
||||
}
|
||||
|
||||
realActivationOccurred = true;
|
||||
console.log("starting language server");
|
||||
clients = new ClientCollection();
|
||||
@@ -147,7 +122,9 @@ function realActivation(): void {
|
||||
|
||||
// There may have already been registered CustomConfigurationProviders.
|
||||
// Request for configurations from those providers.
|
||||
customConfigurationProviders.forEach(provider => onDidChangeCustomConfiguration(provider));
|
||||
clients.forEach(client => {
|
||||
getCustomConfigProviders().forEach(provider => client.onRegisterCustomConfigurationProvider(provider));
|
||||
});
|
||||
|
||||
disposables.push(vscode.workspace.onDidChangeConfiguration(onDidChangeSettings));
|
||||
disposables.push(vscode.workspace.onDidSaveTextDocument(onDidSaveTextDocument));
|
||||
@@ -159,6 +136,12 @@ function realActivation(): void {
|
||||
|
||||
reportMacCrashes();
|
||||
|
||||
const settings: CppSettings = new CppSettings(clients.ActiveClient.RootUri);
|
||||
if (settings.updateChannel === 'Insiders') {
|
||||
insiderUpdateTimer = setInterval(checkAndApplyUpdate, insiderUpdateTimerInterval, settings.updateChannel);
|
||||
checkAndApplyUpdate(settings.updateChannel);
|
||||
}
|
||||
|
||||
intervalTimer = setInterval(onInterval, 2500);
|
||||
}
|
||||
|
||||
@@ -175,7 +158,19 @@ export function updateLanguageConfigurations(): void {
|
||||
*********************************************/
|
||||
|
||||
function onDidChangeSettings(): void {
|
||||
const changedActiveClientSettings: { [key: string] : string } = clients.ActiveClient.onDidChangeSettings();
|
||||
clients.forEach(client => client.onDidChangeSettings());
|
||||
|
||||
const newUpdateChannel: string = changedActiveClientSettings['updateChannel'];
|
||||
if (newUpdateChannel) {
|
||||
if (newUpdateChannel === 'Default') {
|
||||
clearInterval(insiderUpdateTimer);
|
||||
} else if (newUpdateChannel === 'Insiders') {
|
||||
insiderUpdateTimer = setInterval(checkAndApplyUpdate, insiderUpdateTimerInterval);
|
||||
}
|
||||
|
||||
checkAndApplyUpdate(newUpdateChannel);
|
||||
}
|
||||
}
|
||||
|
||||
let saveMessageShown: boolean = false;
|
||||
@@ -203,7 +198,7 @@ function onDidChangeActiveTextEditor(editor: vscode.TextEditor): void {
|
||||
} else {
|
||||
activeDocument = editor.document.uri.toString();
|
||||
clients.activeDocumentChanged(editor.document);
|
||||
clients.ActiveClient.selectionChanged(editor.selection.start);
|
||||
clients.ActiveClient.selectionChanged(Range.create(editor.selection.start, editor.selection.end));
|
||||
}
|
||||
ui.activeDocumentChanged();
|
||||
}
|
||||
@@ -221,7 +216,7 @@ function onDidChangeTextEditorSelection(event: vscode.TextEditorSelectionChangeE
|
||||
clients.activeDocumentChanged(event.textEditor.document);
|
||||
ui.activeDocumentChanged();
|
||||
}
|
||||
clients.ActiveClient.selectionChanged(event.selections[0].start);
|
||||
clients.ActiveClient.selectionChanged(Range.create(event.selections[0].start, event.selections[0].end));
|
||||
}
|
||||
|
||||
function onDidChangeVisibleTextEditors(editors: vscode.TextEditor[]): void {
|
||||
@@ -233,6 +228,134 @@ function onInterval(): void {
|
||||
clients.ActiveClient.onInterval();
|
||||
}
|
||||
|
||||
/**
|
||||
* Install a VSIX package. This helper function will exist until VSCode offers a command to do so.
|
||||
* @param updateChannel The user's updateChannel setting.
|
||||
*/
|
||||
async function installVsix(vsixLocation: string, updateChannel: string): Promise<void> {
|
||||
// Get the path to the VSCode command -- replace logic later when VSCode allows calling of
|
||||
// workbench.extensions.action.installVSIX from TypeScript w/o instead popping up a file dialog
|
||||
return PlatformInformation.GetPlatformInformation().then((platformInfo) => {
|
||||
const vsCodeScriptPath: string = function(platformInfo): string {
|
||||
if (platformInfo.platform === 'win32') {
|
||||
const vsCodeBinName: string = path.basename(process.execPath);
|
||||
let cmdFile: string; // Windows VS Code Insiders breaks VS Code naming conventions
|
||||
if (vsCodeBinName === 'Code - Insiders.exe') {
|
||||
cmdFile = 'code-insiders.cmd';
|
||||
} else {
|
||||
cmdFile = 'code.cmd';
|
||||
}
|
||||
const vsCodeExeDir: string = path.dirname(process.execPath);
|
||||
return path.join(vsCodeExeDir, 'bin', cmdFile);
|
||||
} else if (platformInfo.platform === 'darwin') {
|
||||
return path.join(process.execPath, '..', '..', '..', '..', '..',
|
||||
'Resources', 'app', 'bin', 'code');
|
||||
} else {
|
||||
const vsCodeBinName: string = path.basename(process.execPath);
|
||||
try {
|
||||
const stdout: Buffer = execSync('which ' + vsCodeBinName);
|
||||
return stdout.toString().trim();
|
||||
} catch (error) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}(platformInfo);
|
||||
if (!vsCodeScriptPath) {
|
||||
return Promise.reject(new Error('Failed to find VS Code script'));
|
||||
}
|
||||
|
||||
// Install the VSIX
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
let process: ChildProcess;
|
||||
try {
|
||||
process = spawn(vsCodeScriptPath, ['--install-extension', vsixLocation]);
|
||||
if (process.pid === undefined) {
|
||||
throw new Error();
|
||||
}
|
||||
} catch (error) {
|
||||
reject(new Error('Failed to launch VS Code script process for installation'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Timeout the process if no response is sent back. Ensures this Promise resolves/rejects
|
||||
const timer: NodeJS.Timer = setTimeout(() => {
|
||||
process.kill();
|
||||
reject(new Error('Failed to receive response from VS Code script process for installation within 10s.'));
|
||||
}, 10000);
|
||||
|
||||
// If downgrading, the VS Code CLI will prompt whether the user is sure they would like to downgrade.
|
||||
// Respond to this by writing 0 to stdin (the option to override and install the VSIX package)
|
||||
let sentOverride: boolean = false;
|
||||
process.stdout.on('data', () => {
|
||||
if (sentOverride) {
|
||||
return;
|
||||
}
|
||||
process.stdin.write('0\n');
|
||||
sentOverride = true;
|
||||
clearInterval(timer);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Query package.json and the GitHub API to determine whether the user should update, if so then install the update.
|
||||
* The update can be an upgrade or downgrade depending on the the updateChannel setting.
|
||||
* @param updateChannel The user's updateChannel setting.
|
||||
*/
|
||||
async function checkAndApplyUpdate(updateChannel: string): Promise<void> {
|
||||
// Wrap in new Promise to allow tmp.file callback to successfully resolve/reject
|
||||
// as tmp.file does not do anything with the callback functions return value
|
||||
const p: Promise<void> = new Promise<void>((resolve, reject) => {
|
||||
getTargetBuildInfo(updateChannel).then(buildInfo => {
|
||||
if (!buildInfo) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a temporary file, download the VSIX to it, then install the VSIX
|
||||
tmp.file({postfix: '.vsix'}, async (err, vsixPath, fd, cleanupCallback) => {
|
||||
if (err) {
|
||||
reject(new Error('Failed to create vsix file'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Place in try/catch as the .catch call catches a rejection in downloadFileToDestination
|
||||
// then the .catch call will return a resolved promise
|
||||
// Thusly, the .catch call must also throw, as a return would simply return an unused promise
|
||||
// instead of returning early from this function scope
|
||||
try {
|
||||
await util.downloadFileToDestination(buildInfo.downloadUrl, vsixPath)
|
||||
.catch(() => { throw new Error('Failed to download VSIX package'); });
|
||||
await installVsix(vsixPath, updateChannel)
|
||||
.catch((error: Error) => { throw error; });
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
clearInterval(insiderUpdateTimer);
|
||||
const message: string =
|
||||
`The C/C++ Extension has been updated to version ${buildInfo.name}. Please reload the window for the changes to take effect.`;
|
||||
util.promptReloadWindow(message);
|
||||
telemetry.logLanguageServerEvent('installVsix', { 'success': 'true' });
|
||||
|
||||
resolve();
|
||||
});
|
||||
}, (error: Error) => {
|
||||
// Handle getTargetBuildInfo rejection
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
await p.catch((error: Error) => {
|
||||
// Handle .then following getTargetBuildInfo rejection
|
||||
if (error.message.indexOf('/') !== -1 || error.message.indexOf('\\') !== -1) {
|
||||
error.message = "Potential PII hidden";
|
||||
}
|
||||
telemetry.logLanguageServerEvent('installVsix', { 'error': error.message, 'success': 'false' });
|
||||
});
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
* registered commands
|
||||
*********************************************/
|
||||
@@ -244,6 +367,7 @@ function registerCommands(): void {
|
||||
disposables.push(vscode.commands.registerCommand('C_Cpp.SwitchHeaderSource', onSwitchHeaderSource));
|
||||
disposables.push(vscode.commands.registerCommand('C_Cpp.ResetDatabase', onResetDatabase));
|
||||
disposables.push(vscode.commands.registerCommand('C_Cpp.ConfigurationSelect', onSelectConfiguration));
|
||||
disposables.push(vscode.commands.registerCommand('C_Cpp.ConfigurationProviderSelect', onSelectConfigurationProvider));
|
||||
disposables.push(vscode.commands.registerCommand('C_Cpp.ConfigurationEdit', onEditConfiguration));
|
||||
disposables.push(vscode.commands.registerCommand('C_Cpp.AddToIncludePath', onAddToIncludePath));
|
||||
disposables.push(vscode.commands.registerCommand('C_Cpp.ToggleErrorSquiggles', onToggleSquiggles));
|
||||
@@ -345,7 +469,7 @@ function selectClient(): Thenable<Client> {
|
||||
function onResetDatabase(): void {
|
||||
onActivationEvent();
|
||||
/* need to notify the affected client(s) */
|
||||
selectClient().then(client => client.resetDatabase(), rejected => { });
|
||||
selectClient().then(client => client.resetDatabase(), rejected => {});
|
||||
}
|
||||
|
||||
function onSelectConfiguration(): void {
|
||||
@@ -359,12 +483,21 @@ function onSelectConfiguration(): void {
|
||||
}
|
||||
}
|
||||
|
||||
function onSelectConfigurationProvider(): void {
|
||||
onActivationEvent();
|
||||
if (!isFolderOpen()) {
|
||||
vscode.window.showInformationMessage('Open a folder first to select a configuration provider');
|
||||
} else {
|
||||
selectClient().then(client => client.handleConfigurationProviderSelectCommand(), rejected => {});
|
||||
}
|
||||
}
|
||||
|
||||
function onEditConfiguration(): void {
|
||||
onActivationEvent();
|
||||
if (!isFolderOpen()) {
|
||||
vscode.window.showInformationMessage('Open a folder first to edit configurations');
|
||||
} else {
|
||||
selectClient().then(client => client.handleConfigurationEditCommand(), rejected => { });
|
||||
selectClient().then(client => client.handleConfigurationEditCommand(), rejected => {});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,17 +580,17 @@ function onShowReleaseNotes(): void {
|
||||
|
||||
function onPauseParsing(): void {
|
||||
onActivationEvent();
|
||||
selectClient().then(client => client.pauseParsing(), rejected => { });
|
||||
selectClient().then(client => client.pauseParsing(), rejected => {});
|
||||
}
|
||||
|
||||
function onResumeParsing(): void {
|
||||
onActivationEvent();
|
||||
selectClient().then(client => client.resumeParsing(), rejected => { });
|
||||
selectClient().then(client => client.resumeParsing(), rejected => {});
|
||||
}
|
||||
|
||||
function onShowParsingCommands(): void {
|
||||
onActivationEvent();
|
||||
selectClient().then(client => client.handleShowParsingCommands(), rejected => { });
|
||||
selectClient().then(client => client.handleShowParsingCommands(), rejected => {});
|
||||
}
|
||||
|
||||
function onTakeSurvey(): void {
|
||||
@@ -531,6 +664,11 @@ function handleCrashFileRead(err: NodeJS.ErrnoException, data: string): void {
|
||||
if (data.length > 16384) {
|
||||
data = data.substr(0, 16384) + "...";
|
||||
}
|
||||
if (data.length < 2) {
|
||||
return; // Don't send telemetry if there's no call stack.
|
||||
}
|
||||
// Get rid of the memory addresses (which breaks being able get a hit count for each crash call stack).
|
||||
data = data.replace(/0x................ /g, "");
|
||||
crashObject["CrashingThreadCallStack"] = data;
|
||||
telemetry.logLanguageServerEvent("MacCrash", crashObject, null);
|
||||
}
|
||||
@@ -539,6 +677,7 @@ export function deactivate(): Thenable<void> {
|
||||
console.log("deactivating extension");
|
||||
telemetry.logLanguageServerEvent("LanguageServerShutdown");
|
||||
clearInterval(intervalTimer);
|
||||
clearInterval(insiderUpdateTimer);
|
||||
disposables.forEach(d => d.dispose());
|
||||
languageConfigurations.forEach(d => d.dispose());
|
||||
ui.dispose();
|
||||
@@ -548,3 +687,17 @@ export function deactivate(): Thenable<void> {
|
||||
export function isFolderOpen(): boolean {
|
||||
return vscode.workspace.workspaceFolders !== undefined && vscode.workspace.workspaceFolders.length > 0;
|
||||
}
|
||||
|
||||
export function getClients(): ClientCollection {
|
||||
if (!realActivationOccurred) {
|
||||
realActivation();
|
||||
}
|
||||
return clients;
|
||||
}
|
||||
|
||||
export function getActiveClient(): Client {
|
||||
if (!realActivationOccurred) {
|
||||
realActivation();
|
||||
}
|
||||
return clients.ActiveClient;
|
||||
}
|
||||
@@ -20,12 +20,16 @@ class PersistentStateBase<T> {
|
||||
}
|
||||
|
||||
public get Value(): T {
|
||||
return this.state.get<T>(this.key, this.defaultvalue);
|
||||
return this.state.get<T>(this.key, this.defaultvalue);
|
||||
}
|
||||
|
||||
public set Value(newValue: T) {
|
||||
this.state.update(this.key, newValue);
|
||||
}
|
||||
|
||||
public get DefaultValue(): T {
|
||||
return this.defaultvalue;
|
||||
}
|
||||
}
|
||||
|
||||
// Abstraction for global state that persists across activations but is not present in a settings file
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
import { Middleware } from 'vscode-languageclient';
|
||||
import { ClientCollection } from './clientCollection';
|
||||
import { Client } from './client';
|
||||
import { provideCustomConfiguration } from './extension';
|
||||
|
||||
export function createProtocolFilter(me: Client, clients: ClientCollection): Middleware {
|
||||
// Disabling lint for invoke handlers
|
||||
@@ -24,7 +23,7 @@ export function createProtocolFilter(me: Client, clients: ClientCollection): Mid
|
||||
didOpen: (document, sendMessage) => {
|
||||
if (clients.checkOwnership(me, document)) {
|
||||
me.TrackedDocuments.add(document);
|
||||
provideCustomConfiguration(document, me).then(() => {
|
||||
me.provideCustomConfiguration(document).then(() => {
|
||||
sendMessage(document);
|
||||
}, () => {
|
||||
sendMessage(document);
|
||||
|
||||
@@ -41,23 +41,30 @@ export class CppSettings extends Settings {
|
||||
public get errorSquiggles(): string { return super.Section.get<string>("errorSquiggles"); }
|
||||
public get dimInactiveRegions(): boolean { return super.Section.get<boolean>("dimInactiveRegions"); }
|
||||
public get inactiveRegionOpacity(): number { return super.Section.get<number>("inactiveRegionOpacity"); }
|
||||
public get inactiveRegionForegroundColor(): string { return super.Section.get<string>("inactiveRegionForegroundColor"); }
|
||||
public get inactiveRegionBackgroundColor(): string { return super.Section.get<string>("inactiveRegionBackgroundColor"); }
|
||||
public get autoComplete(): string { return super.Section.get<string>("autocomplete"); }
|
||||
public get loggingLevel(): string { return super.Section.get<string>("loggingLevel"); }
|
||||
public get navigationLength(): number { return super.Section.get<number>("navigation.length", 60); }
|
||||
public get autoAddFileAssociations(): boolean { return super.Section.get<boolean>("autoAddFileAssociations"); }
|
||||
public get workspaceParsingPriority(): boolean { return super.Section.get<boolean>("workspaceParsingPriority"); }
|
||||
public get workspaceSymbols(): string { return super.Section.get<string>("workspaceSymbols"); }
|
||||
public get exclusionPolicy(): boolean { return super.Section.get<boolean>("exclusionPolicy"); }
|
||||
public get commentContinuationPatterns(): (string | CommentPattern)[] { return super.Section.get<(string | CommentPattern)[]>("commentContinuationPatterns"); }
|
||||
public get configurationWarnings(): string { return super.Section.get<string>("configurationWarnings"); }
|
||||
public get preferredPathSeparator(): string { return super.Section.get<string>("preferredPathSeparator"); }
|
||||
public get updateChannel(): string { return super.Section.get<string>("updateChannel"); }
|
||||
public get defaultIncludePath(): string[] { return super.Section.get<string[]>("default.includePath"); }
|
||||
public get defaultDefines(): string[] { return super.Section.get<string[]>("default.defines"); }
|
||||
public get defaultMacFrameworkPath(): string[] { return super.Section.get<string[]>("default.macFrameworkPath"); }
|
||||
public get defaultWindowsSdkVersion(): string { return super.Section.get<string>("default.windowsSdkVersion"); }
|
||||
public get defaultCompileCommands(): string { return super.Section.get<string>("default.compileCommands"); }
|
||||
public get defaultForcedInclude(): string[] { return super.Section.get<string[]>("default.forcedInclude"); }
|
||||
public get defaultIntelliSenseMode(): string { return super.Section.get<string>("default.intelliSenseMode"); }
|
||||
public get defaultCompilerPath(): string { return super.Section.get<string>("default.compilerPath"); }
|
||||
public get defaultCStandard(): string { return super.Section.get<string>("default.cStandard"); }
|
||||
public get defaultCppStandard(): string { return super.Section.get<string>("default.cppStandard"); }
|
||||
public get defaultConfigurationProvider(): string { return super.Section.get<string>("default.configurationProvider"); }
|
||||
public get defaultBrowsePath(): string[] { return super.Section.get<string[]>("default.browse.path"); }
|
||||
public get defaultDatabaseFilename(): string { return super.Section.get<string>("default.browse.databaseFilename"); }
|
||||
public get defaultLimitSymbolsToIncludedHeaders(): boolean { return super.Section.get<boolean>("default.browse.limitSymbolsToIncludedHeaders"); }
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { Client } from './client';
|
||||
import { getCustomConfigProviders, CustomConfigurationProviderCollection } from './customProviders';
|
||||
|
||||
let ui: UI;
|
||||
|
||||
@@ -16,11 +17,24 @@ interface KeyedQuickPickItem extends vscode.QuickPickItem {
|
||||
key: string;
|
||||
}
|
||||
|
||||
// Higher numbers mean greater priority.
|
||||
enum ConfigurationPriority {
|
||||
IncludePath = 1,
|
||||
CompileCommands = 2,
|
||||
CustomProvider = 3,
|
||||
}
|
||||
|
||||
interface ConfigurationResult {
|
||||
configured: boolean;
|
||||
priority: ConfigurationPriority;
|
||||
}
|
||||
|
||||
export class UI {
|
||||
private navigationStatusBarItem: vscode.StatusBarItem;
|
||||
private configStatusBarItem: vscode.StatusBarItem;
|
||||
private browseEngineStatusBarItem: vscode.StatusBarItem;
|
||||
private intelliSenseStatusBarItem: vscode.StatusBarItem;
|
||||
private configurationUIPromise: Thenable<ConfigurationResult>;
|
||||
|
||||
constructor() {
|
||||
// 1000 = priority, it needs to be high enough to be on the left of the Ln/Col.
|
||||
@@ -160,12 +174,26 @@ export class UI {
|
||||
items.push({ label: "Edit Configurations...", description: "", index: configurationNames.length });
|
||||
|
||||
return vscode.window.showQuickPick(items, options)
|
||||
.then(selection => {
|
||||
if (!selection) {
|
||||
return -1;
|
||||
}
|
||||
return selection.index;
|
||||
});
|
||||
.then(selection => (selection) ? selection.index : -1);
|
||||
}
|
||||
|
||||
public showConfigurationProviders(currentProvider: string|null): Thenable<string|undefined> {
|
||||
let options: vscode.QuickPickOptions = {};
|
||||
options.placeHolder = "Select a Configuration Provider...";
|
||||
let providers: CustomConfigurationProviderCollection = getCustomConfigProviders();
|
||||
|
||||
let items: KeyedQuickPickItem[] = [];
|
||||
providers.forEach(provider => {
|
||||
let label: string = provider.name;
|
||||
if (provider.extensionId === currentProvider) {
|
||||
label += " (active)";
|
||||
}
|
||||
items.push({ label: label, description: "", key: provider.extensionId });
|
||||
});
|
||||
items.push({ label: "(none)", description: "Disable the active configuration provider, if applicable.", key: "" });
|
||||
|
||||
return vscode.window.showQuickPick(items, options)
|
||||
.then(selection => (selection) ? selection.key : undefined);
|
||||
}
|
||||
|
||||
public showCompileCommands(paths: string[]): Thenable<number> {
|
||||
@@ -178,12 +206,7 @@ export class UI {
|
||||
}
|
||||
|
||||
return vscode.window.showQuickPick(items, options)
|
||||
.then(selection => {
|
||||
if (!selection) {
|
||||
return -1;
|
||||
}
|
||||
return selection.index;
|
||||
});
|
||||
.then(selection => (selection) ? selection.index : -1);
|
||||
}
|
||||
|
||||
public showWorkspaces(workspaceNames: { name: string; key: string }[]): Thenable<string> {
|
||||
@@ -194,12 +217,7 @@ export class UI {
|
||||
workspaceNames.forEach(name => items.push({ label: name.name, description: "", key: name.key }));
|
||||
|
||||
return vscode.window.showQuickPick(items, options)
|
||||
.then(selection => {
|
||||
if (!selection) {
|
||||
return "";
|
||||
}
|
||||
return selection.key;
|
||||
});
|
||||
.then(selection => (selection) ? selection.key : "");
|
||||
}
|
||||
|
||||
public showParsingCommands(): Thenable<number> {
|
||||
@@ -215,12 +233,50 @@ export class UI {
|
||||
}
|
||||
|
||||
return vscode.window.showQuickPick(items, options)
|
||||
.then(selection => {
|
||||
if (!selection) {
|
||||
return -1;
|
||||
}
|
||||
return selection.index;
|
||||
.then(selection => (selection) ? selection.index : -1);
|
||||
}
|
||||
|
||||
public showConfigureIncludePathMessage(prompt: () => Thenable<boolean>, onSkip: () => void): void {
|
||||
setTimeout(() => {
|
||||
this.showConfigurationPrompt(ConfigurationPriority.IncludePath, prompt, onSkip);
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
public showConfigureCompileCommandsMessage(prompt: () => Thenable<boolean>, onSkip: () => void): void {
|
||||
setTimeout(() => {
|
||||
this.showConfigurationPrompt(ConfigurationPriority.CompileCommands, prompt, onSkip);
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
public showConfigureCustomProviderMessage(prompt: () => Thenable<boolean>, onSkip: () => void): void {
|
||||
this.showConfigurationPrompt(ConfigurationPriority.CustomProvider, prompt, onSkip);
|
||||
}
|
||||
|
||||
private showConfigurationPrompt(priority: ConfigurationPriority, prompt: () => Thenable<boolean>, onSkip: () => void): void {
|
||||
let showPrompt: () => Thenable<ConfigurationResult> = async () => {
|
||||
let configured: boolean = await prompt();
|
||||
return Promise.resolve({
|
||||
priority: priority,
|
||||
configured: configured
|
||||
});
|
||||
};
|
||||
|
||||
if (this.configurationUIPromise) {
|
||||
this.configurationUIPromise = this.configurationUIPromise.then(result => {
|
||||
if (priority > result.priority) {
|
||||
return showPrompt();
|
||||
} else if (!result.configured) {
|
||||
return showPrompt();
|
||||
}
|
||||
onSkip();
|
||||
return Promise.resolve({
|
||||
priority: result.priority,
|
||||
configured: true
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.configurationUIPromise = showPrompt();
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
|
||||
@@ -118,6 +118,7 @@ function copyBinaryDependencies(): void {
|
||||
copy(miEngineRoot, DebugAdapterBinPath, "Microsoft.MIDebugEngine.dll");
|
||||
copy(miEngineRoot, DebugAdapterBinPath, "Microsoft.MIDebugEngine.dll.mdb");
|
||||
copy(miEngineRoot, DebugAdapterBinPath, "osxlaunchhelper.scpt");
|
||||
copy(miEngineRoot, DebugAdapterBinPath, "Microsoft.VisualStudio.Debugger.Interop.15.0.dll");
|
||||
|
||||
copy(openDebugRoot, DebugAdapterBinPath, "Microsoft.VisualStudio.Debugger.Interop.10.0.dll");
|
||||
copy(openDebugRoot, DebugAdapterBinPath, "Microsoft.VisualStudio.Debugger.Interop.11.0.dll");
|
||||
@@ -129,6 +130,8 @@ function copyBinaryDependencies(): void {
|
||||
copy(openDebugRoot, DebugAdapterBinPath, "OpenDebugAD7.exe.config");
|
||||
copy(openDebugRoot, DebugAdapterBinPath, "OpenDebugAD7.exe.mdb");
|
||||
copy(openDebugRoot, DebugAdapterBinPath, "Newtonsoft.Json.dll");
|
||||
copy(openDebugRoot, DebugAdapterBinPath, "WindowsDebugLauncher.exe");
|
||||
copy(openDebugRoot, DebugAdapterBinPath, "Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.dll");
|
||||
}
|
||||
|
||||
function copyMonoDependencies(): void {
|
||||
|
||||
+28
-61
@@ -4,15 +4,10 @@
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
'use strict';
|
||||
|
||||
import * as url from 'url';
|
||||
import * as https from 'https';
|
||||
import { ClientRequest } from 'http';
|
||||
import * as vscode from 'vscode';
|
||||
import * as fs from 'fs';
|
||||
|
||||
import * as util from './common';
|
||||
import * as Telemetry from './telemetry';
|
||||
import { PersistentState } from './LanguageServer/persistentState';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const userBucketMax: number = 100;
|
||||
const userBucketString: string = "CPP.UserBucket";
|
||||
@@ -21,61 +16,66 @@ const localConfigFile: string = "cpptools.json";
|
||||
interface Settings {
|
||||
defaultIntelliSenseEngine?: number;
|
||||
recursiveIncludes?: number;
|
||||
gotoDefIntelliSense?: number;
|
||||
}
|
||||
|
||||
export class ABTestSettings {
|
||||
private settings: Settings;
|
||||
private intelliSenseEngineDefault: PersistentState<number>;
|
||||
private recursiveIncludesDefault: PersistentState<number>;
|
||||
private gotoDefIntelliSenseDefault: PersistentState<number>;
|
||||
private bucket: PersistentState<number>;
|
||||
|
||||
constructor() {
|
||||
this.intelliSenseEngineDefault = new PersistentState<number>("ABTest.1", 100);
|
||||
this.recursiveIncludesDefault = new PersistentState<number>("ABTest.2", 100);
|
||||
this.gotoDefIntelliSenseDefault = new PersistentState<number>("ABTest.3", 100);
|
||||
this.settings = {
|
||||
defaultIntelliSenseEngine: this.intelliSenseEngineDefault.Value,
|
||||
recursiveIncludes: this.recursiveIncludesDefault.Value
|
||||
recursiveIncludes: this.recursiveIncludesDefault.Value,
|
||||
gotoDefIntelliSense: this.gotoDefIntelliSenseDefault.Value
|
||||
};
|
||||
this.bucket = new PersistentState<number>(userBucketString, -1);
|
||||
if (this.bucket.Value === -1) {
|
||||
this.bucket.Value = Math.floor(Math.random() * userBucketMax) + 1; // Range is [1, userBucketMax].
|
||||
}
|
||||
|
||||
this.updateSettingsAsync().then(() => {
|
||||
// Redownload cpptools.json after initialization so it's not blocked.
|
||||
// It'll be used the next time the extension reloads.
|
||||
this.downloadCpptoolsJsonPkgAsync();
|
||||
});
|
||||
this.updateSettings();
|
||||
// Redownload cpptools.json after initialization so it's not blocked.
|
||||
// It'll be used the next time the extension reloads.
|
||||
this.downloadCpptoolsJsonPkgAsync();
|
||||
|
||||
// Redownload occasionally to prevent an extra reload during long sessions.
|
||||
setInterval(() => { this.downloadCpptoolsJsonPkgAsync(); }, 30 * 60 * 1000); // 30 minutes.
|
||||
}
|
||||
|
||||
public get UseDefaultIntelliSenseEngine(): boolean {
|
||||
return this.settings.defaultIntelliSenseEngine ? this.settings.defaultIntelliSenseEngine >= this.bucket.Value : true;
|
||||
return util.isNumber(this.settings.defaultIntelliSenseEngine) ? this.settings.defaultIntelliSenseEngine >= this.bucket.Value : true;
|
||||
}
|
||||
|
||||
public get UseRecursiveIncludes(): boolean {
|
||||
return this.settings.recursiveIncludes ? this.settings.recursiveIncludes >= this.bucket.Value : true;
|
||||
return util.isNumber(this.settings.recursiveIncludes) ? this.settings.recursiveIncludes >= this.bucket.Value : true;
|
||||
}
|
||||
|
||||
private async updateSettingsAsync(): Promise<void> {
|
||||
public get UseGoToDefIntelliSense(): boolean {
|
||||
return util.isNumber(this.settings.gotoDefIntelliSense) ? this.settings.gotoDefIntelliSense >= this.bucket.Value : true;
|
||||
}
|
||||
|
||||
private updateSettings(): void {
|
||||
const cpptoolsJsonFile: string = util.getExtensionFilePath(localConfigFile);
|
||||
|
||||
|
||||
try {
|
||||
const exists: boolean = await util.checkFileExists(cpptoolsJsonFile);
|
||||
const exists: boolean = fs.existsSync(cpptoolsJsonFile);
|
||||
if (exists) {
|
||||
const fileContent: string = await util.readFileText(cpptoolsJsonFile);
|
||||
const fileContent: string = fs.readFileSync(cpptoolsJsonFile).toString();
|
||||
let newSettings: Settings = <Settings>JSON.parse(fileContent);
|
||||
if (newSettings.defaultIntelliSenseEngine) {
|
||||
this.intelliSenseEngineDefault.Value = newSettings.defaultIntelliSenseEngine;
|
||||
}
|
||||
if (newSettings.recursiveIncludes) {
|
||||
this.recursiveIncludesDefault.Value = newSettings.recursiveIncludes;
|
||||
}
|
||||
this.intelliSenseEngineDefault.Value = util.isNumber(newSettings.defaultIntelliSenseEngine) ? newSettings.defaultIntelliSenseEngine : this.intelliSenseEngineDefault.DefaultValue;
|
||||
this.recursiveIncludesDefault.Value = util.isNumber(newSettings.recursiveIncludes) ? newSettings.recursiveIncludes : this.recursiveIncludesDefault.DefaultValue;
|
||||
this.gotoDefIntelliSenseDefault.Value = util.isNumber(newSettings.gotoDefIntelliSense) ? newSettings.gotoDefIntelliSense : this.gotoDefIntelliSenseDefault.DefaultValue;
|
||||
this.settings = {
|
||||
defaultIntelliSenseEngine: this.intelliSenseEngineDefault.Value,
|
||||
recursiveIncludes: this.recursiveIncludesDefault.Value
|
||||
recursiveIncludes: this.recursiveIncludesDefault.Value,
|
||||
gotoDefIntelliSense: this.gotoDefIntelliSenseDefault.Value
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -83,50 +83,17 @@ export class ABTestSettings {
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Code is copied from DownloadPackage in packageManager.ts, but with ~75% fewer lines.
|
||||
private downloadCpptoolsJsonAsync(urlString): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
let parsedUrl: url.Url = url.parse(urlString);
|
||||
let request: ClientRequest = https.request({
|
||||
host: parsedUrl.host,
|
||||
path: parsedUrl.path,
|
||||
agent: util.getHttpsProxyAgent(),
|
||||
rejectUnauthorized: vscode.workspace.getConfiguration().get("http.proxyStrictSSL", true)
|
||||
}, (response) => {
|
||||
if (response.statusCode === 301 || response.statusCode === 302) {
|
||||
let redirectUrl: string | string[];
|
||||
if (typeof response.headers.location === "string") {
|
||||
redirectUrl = response.headers.location;
|
||||
} else {
|
||||
redirectUrl = response.headers.location[0];
|
||||
}
|
||||
return resolve(this.downloadCpptoolsJsonAsync(redirectUrl)); // Redirect - download from new location
|
||||
}
|
||||
if (response.statusCode !== 200) {
|
||||
return reject();
|
||||
}
|
||||
let downloadedBytes = 0; // tslint:disable-line
|
||||
let cppToolsJsonFile: fs.WriteStream = fs.createWriteStream(util.getExtensionFilePath(localConfigFile));
|
||||
response.on('data', (data) => { downloadedBytes += data.length; });
|
||||
response.on('end', () => { cppToolsJsonFile.close(); });
|
||||
cppToolsJsonFile.on('close', () => { resolve(); this.updateSettingsAsync(); });
|
||||
response.on('error', (error) => { reject(); });
|
||||
response.pipe(cppToolsJsonFile, { end: false });
|
||||
});
|
||||
request.on('error', (error) => { reject(); });
|
||||
request.end();
|
||||
});
|
||||
}
|
||||
|
||||
private downloadCpptoolsJsonPkgAsync(): Promise<void> {
|
||||
let hasError: boolean = false;
|
||||
let telemetryProperties: { [key: string]: string } = {};
|
||||
return this.downloadCpptoolsJsonAsync("https://go.microsoft.com/fwlink/?linkid=852750")
|
||||
const localConfigPath: string = util.getExtensionFilePath(localConfigFile);
|
||||
return util.downloadFileToDestination("https://go.microsoft.com/fwlink/?linkid=2026205", localConfigPath)
|
||||
.catch((error) => {
|
||||
// More specific error info is not likely to be helpful, and we get detailed download data from the initial install.
|
||||
hasError = true;
|
||||
})
|
||||
.then(() => {
|
||||
this.updateSettings();
|
||||
telemetryProperties['success'] = (!hasError).toString();
|
||||
Telemetry.logDebuggerEvent("cpptoolsJsonDownload", telemetryProperties);
|
||||
});
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All Rights Reserved.
|
||||
* See 'LICENSE' in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { CancellationToken } from 'vscode-jsonrpc';
|
||||
|
||||
/**
|
||||
* An interface to allow Custom Configuration Provider extensions to communicate with this extension.
|
||||
*/
|
||||
export interface CppToolsApi {
|
||||
/**
|
||||
* Register the Custom Configuration Provider.
|
||||
* This must be called as soon as the provider extension is ready. This is necessary for cpptools
|
||||
* to request configurations from the provider.
|
||||
* @param provider An instance of the [CustomConfigurationProvider](#CustomConfigurationProvider)
|
||||
* instance representing the provider extension.
|
||||
*/
|
||||
registerCustomConfigurationProvider(provider: CustomConfigurationProvider): void;
|
||||
|
||||
/**
|
||||
* Notifies cpptools that the current configuration has changed. Upon receiving this notification,
|
||||
* cpptools will request the new configurations.
|
||||
* @param provider An instance of the [CustomConfigurationProvider](#CustomConfigurationProvider)
|
||||
* instance representing the provider extension.
|
||||
*/
|
||||
didChangeCustomConfiguration(provider: CustomConfigurationProvider): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface to allow this extension to communicate with Custom Configuration Provider extensions.
|
||||
*/
|
||||
export interface CustomConfigurationProvider {
|
||||
/**
|
||||
* The name of the Custom Configuration Provider extension.
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* A request to determine whether this provider can provide IntelliSense configurations for the given document.
|
||||
* @param uri The URI of the document.
|
||||
* @param token (optional) The cancellation token.
|
||||
* @returns 'true' if this provider can provide IntelliSense configurations for the given document.
|
||||
*/
|
||||
canProvideConfiguration(uri: vscode.Uri, token?: CancellationToken): Thenable<boolean>;
|
||||
|
||||
/**
|
||||
* A request to get Intellisense configurations for the given files.
|
||||
* @param uris A list of one of more URIs for the files to provide configurations for.
|
||||
* @param token (optional) The cancellation token.
|
||||
* @returns A list of [SourceFileConfigurationItem](#SourceFileConfigurationItem) for the documents that this provider
|
||||
* is able to provide IntelliSense configurations for.
|
||||
* Note: If this provider cannot provide configurations for a file in `uris`, then the file will not be included
|
||||
* in the return value. An empty list will be returned if the provider cannot provide configurations for any of the files.
|
||||
*/
|
||||
provideConfigurations(uris: vscode.Uri[], token?: CancellationToken): Thenable<SourceFileConfigurationItem[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The model representing the custom IntelliSense configurations for a source file.
|
||||
*/
|
||||
export interface SourceFileConfiguration {
|
||||
/**
|
||||
* This must also include the system include path (compiler defaults) unless
|
||||
* [compilerPath](#SourceFileConfiguration.compilerPath) is specified.
|
||||
*/
|
||||
includePath: string[];
|
||||
|
||||
/**
|
||||
* This must also include the compiler default defines (__cplusplus, etc) unless
|
||||
* [compilerPath](#SourceFileConfiguration.compilerPath) is specified.
|
||||
*/
|
||||
defines: string[];
|
||||
|
||||
/**
|
||||
* Currently, "msvc-x64" or "clang-x64".
|
||||
*/
|
||||
intelliSenseMode: string;
|
||||
|
||||
/**
|
||||
* The C or C++ standard. See `package.json` for valid values.
|
||||
*/
|
||||
standard: string;
|
||||
|
||||
/**
|
||||
* Any files that need to be included before the source file is parsed.
|
||||
*/
|
||||
forcedInclude?: string[];
|
||||
|
||||
/**
|
||||
* The full path to the compiler. If specified, the extension will query it for default includes and defines and
|
||||
* add them to [includePath](#SourceFileConfiguration.includePath) and [defines](#SourceFileConfiguration.defines).
|
||||
*/
|
||||
compilerPath?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A model representing a source file and its corresponding configuration.
|
||||
*/
|
||||
export interface SourceFileConfigurationItem {
|
||||
/**
|
||||
* The URI of the source file.
|
||||
*/
|
||||
uri: string;
|
||||
|
||||
/**
|
||||
* The IntelliSense configuration for [uri](#SourceFileConfigurationItem.uri)
|
||||
*/
|
||||
configuration: SourceFileConfiguration;
|
||||
}
|
||||
@@ -11,10 +11,12 @@ class TemporaryCommandRegistrar {
|
||||
// Used to save/re-execute commands used before the extension has activated (e.g. delayed by dependency downloading).
|
||||
private delayedCommandsToExecute: Set<string>;
|
||||
private tempCommands: vscode.Disposable[]; // Need to save this to unregister/dispose the temporary commands.
|
||||
private isLanguageServerDisabled: boolean;
|
||||
|
||||
private commandsToRegister: string[] = [
|
||||
"C_Cpp.ConfigurationEdit",
|
||||
"C_Cpp.ConfigurationSelect",
|
||||
"C_Cpp.ConfigurationProviderSelect",
|
||||
"C_Cpp.SwitchHeaderSource",
|
||||
"C_Cpp.Navigate",
|
||||
"C_Cpp.GoToDeclaration",
|
||||
@@ -43,10 +45,18 @@ class TemporaryCommandRegistrar {
|
||||
|
||||
public registerTempCommand(command: string): void {
|
||||
this.tempCommands.push(vscode.commands.registerCommand(command, () => {
|
||||
if (this.isLanguageServerDisabled) {
|
||||
vscode.window.showInformationMessage("The command is disabled because \"C_Cpp.intelliSenseEngine\" is set to \"Disabled\".");
|
||||
return;
|
||||
}
|
||||
this.delayedCommandsToExecute.add(command);
|
||||
}));
|
||||
}
|
||||
|
||||
public disableLanguageServer(): void {
|
||||
this.isLanguageServerDisabled = true;
|
||||
}
|
||||
|
||||
public activateLanguageServer(): void {
|
||||
// Main activation code.
|
||||
this.tempCommands.forEach((command) => {
|
||||
|
||||
+208
-49
@@ -14,6 +14,8 @@ import * as url from 'url';
|
||||
import { PlatformInformation } from './platform';
|
||||
import { getOutputChannelLogger, showOutputChannel } from './logger';
|
||||
import * as assert from 'assert';
|
||||
import * as https from 'https';
|
||||
import { ClientRequest, OutgoingHttpHeaders } from 'http';
|
||||
|
||||
export let extensionContext: vscode.ExtensionContext;
|
||||
export function setExtensionContext(context: vscode.ExtensionContext): void {
|
||||
@@ -57,6 +59,32 @@ export function getVcpkgPathDescriptorFile(): string {
|
||||
}
|
||||
}
|
||||
|
||||
let vcpkgRoot: string;
|
||||
export function getVcpkgRoot(): string {
|
||||
if (!vcpkgRoot && vcpkgRoot !== "") {
|
||||
vcpkgRoot = "";
|
||||
// Check for vcpkg instance.
|
||||
if (fs.existsSync(getVcpkgPathDescriptorFile())) {
|
||||
let vcpkgRootTemp: string = fs.readFileSync(getVcpkgPathDescriptorFile()).toString();
|
||||
vcpkgRootTemp = vcpkgRootTemp.trim();
|
||||
if (fs.existsSync(vcpkgRootTemp)) {
|
||||
vcpkgRoot = path.join(vcpkgRootTemp, "/installed").replace(/\\/g, "/");
|
||||
}
|
||||
}
|
||||
}
|
||||
return vcpkgRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a fuzzy determination of whether a uri represents a header file.
|
||||
* For the purposes of this function, a header file has no extension, or an extension that begins with the letter 'h'.
|
||||
* @param document The document to check.
|
||||
*/
|
||||
export function isHeader(document: vscode.TextDocument): boolean {
|
||||
let ext: string = path.extname(document.uri.fsPath);
|
||||
return !ext || ext.startsWith(".h") || ext.startsWith(".H");
|
||||
}
|
||||
|
||||
// Extension is ready if install.lock exists and debugAdapters folder exist.
|
||||
export async function isExtensionReady(): Promise<boolean> {
|
||||
const doesInstallLockFileExist: boolean = await checkInstallLockFile();
|
||||
@@ -143,6 +171,38 @@ export function showReleaseNotes(): void {
|
||||
vscode.commands.executeCommand('vscode.previewHtml', vscode.Uri.file(getExtensionFilePath("ReleaseNotes.html")), vscode.ViewColumn.One, "C/C++ Extension Release Notes");
|
||||
}
|
||||
|
||||
export function isUri(input: any): input is vscode.Uri {
|
||||
return input && input instanceof vscode.Uri;
|
||||
}
|
||||
|
||||
export function isString(input: any): input is string {
|
||||
return typeof(input) === "string";
|
||||
}
|
||||
|
||||
export function isNumber(input: any): input is number {
|
||||
return typeof(input) === "number";
|
||||
}
|
||||
|
||||
export function isBoolean(input: any): input is boolean {
|
||||
return typeof(input) === "boolean";
|
||||
}
|
||||
|
||||
export function isArray(input: any): input is any[] {
|
||||
return input instanceof Array;
|
||||
}
|
||||
|
||||
export function isOptionalString(input: any): input is string|undefined {
|
||||
return input === undefined || isString(input);
|
||||
}
|
||||
|
||||
export function isArrayOfString(input: any): input is string[] {
|
||||
return isArray(input) && input.every(item => isString(item));
|
||||
}
|
||||
|
||||
export function isOptionalArrayOfString(input: any): input is string[]|undefined {
|
||||
return input === undefined || isArrayOfString(input);
|
||||
}
|
||||
|
||||
export function resolveVariables(input: string, additionalEnvironment: {[key: string]: string | string[]}): string {
|
||||
if (!input) {
|
||||
return "";
|
||||
@@ -152,55 +212,60 @@ export function resolveVariables(input: string, additionalEnvironment: {[key: st
|
||||
}
|
||||
|
||||
// Replace environment and configuration variables.
|
||||
let regexp: RegExp = /\$\{((env|config|workspaceFolder)(.|:))?(.*?)\}/g;
|
||||
let ret: string = input.replace(regexp, (match: string, ignored1: string, varType: string, ignored2: string, name: string) => {
|
||||
// Historically, if the variable didn't have anything before the "." or ":"
|
||||
// it was assumed to be an environment variable
|
||||
if (varType === undefined) {
|
||||
varType = "env";
|
||||
}
|
||||
let newValue: string = undefined;
|
||||
switch (varType) {
|
||||
case "env": {
|
||||
let v: string | string[] = additionalEnvironment[name];
|
||||
if (typeof v === "string") {
|
||||
newValue = v;
|
||||
} else if (input === match && v instanceof Array) {
|
||||
newValue = v.join(";");
|
||||
}
|
||||
if (!newValue) {
|
||||
newValue = process.env[name];
|
||||
}
|
||||
break;
|
||||
let regexp: () => RegExp = () => /\$\{((env|config|workspaceFolder)(\.|:))?(.*?)\}/g;
|
||||
let ret: string = input;
|
||||
let cycleCache: Set<string> = new Set();
|
||||
while (!cycleCache.has(ret)) {
|
||||
cycleCache.add(ret);
|
||||
ret = ret.replace(regexp(), (match: string, ignored1: string, varType: string, ignored2: string, name: string) => {
|
||||
// Historically, if the variable didn't have anything before the "." or ":"
|
||||
// it was assumed to be an environment variable
|
||||
if (varType === undefined) {
|
||||
varType = "env";
|
||||
}
|
||||
case "config": {
|
||||
let config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration();
|
||||
if (config) {
|
||||
newValue = config.get<string>(name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "workspaceFolder": {
|
||||
// Only replace ${workspaceFolder:name} variables for now.
|
||||
// We may consider doing replacement of ${workspaceFolder} here later, but we would have to update the language server and also
|
||||
// intercept messages with paths in them and add the ${workspaceFolder} variable back in (e.g. for light bulb suggestions)
|
||||
if (name && vscode.workspace && vscode.workspace.workspaceFolders) {
|
||||
let folder: vscode.WorkspaceFolder = vscode.workspace.workspaceFolders.find(folder => folder.name.toLocaleLowerCase() === name.toLocaleLowerCase());
|
||||
if (folder) {
|
||||
newValue = folder.uri.fsPath;
|
||||
let newValue: string;
|
||||
switch (varType) {
|
||||
case "env": {
|
||||
let v: string | string[] = additionalEnvironment[name];
|
||||
if (isString(v)) {
|
||||
newValue = v;
|
||||
} else if (input === match && isArrayOfString(v)) {
|
||||
newValue = v.join(";");
|
||||
}
|
||||
if (!isString(newValue)) {
|
||||
newValue = process.env[name];
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "config": {
|
||||
let config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration();
|
||||
if (config) {
|
||||
newValue = config.get<string>(name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "workspaceFolder": {
|
||||
// Only replace ${workspaceFolder:name} variables for now.
|
||||
// We may consider doing replacement of ${workspaceFolder} here later, but we would have to update the language server and also
|
||||
// intercept messages with paths in them and add the ${workspaceFolder} variable back in (e.g. for light bulb suggestions)
|
||||
if (name && vscode.workspace && vscode.workspace.workspaceFolders) {
|
||||
let folder: vscode.WorkspaceFolder = vscode.workspace.workspaceFolders.find(folder => folder.name.toLocaleLowerCase() === name.toLocaleLowerCase());
|
||||
if (folder) {
|
||||
newValue = folder.uri.fsPath;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: { assert.fail("unknown varType matched"); }
|
||||
}
|
||||
default: { assert.fail("unknown varType matched"); }
|
||||
}
|
||||
return (newValue) ? newValue : match;
|
||||
});
|
||||
return (isString(newValue)) ? newValue : match;
|
||||
});
|
||||
}
|
||||
|
||||
// Resolve '~' at the start of the path.
|
||||
regexp = /^\~/g;
|
||||
ret = ret.replace(regexp, (match: string, name: string) => {
|
||||
let newValue: string = process.env.HOME;
|
||||
regexp = () => /^\~/g;
|
||||
ret = ret.replace(regexp(), (match: string, name: string) => {
|
||||
let newValue: string = (process.platform === 'win32') ? process.env.USERPROFILE : process.env.HOME;
|
||||
return (newValue) ? newValue : match;
|
||||
});
|
||||
|
||||
@@ -333,10 +398,9 @@ export function readFileText(filePath: string, encoding: string = "utf8"): Promi
|
||||
fs.readFile(filePath, encoding, (err, data) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
} else {
|
||||
resolve(data);
|
||||
}
|
||||
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -347,14 +411,29 @@ export function writeFileText(filePath: string, content: string, encoding: strin
|
||||
fs.writeFile(filePath, content, { encoding }, (err) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteFile(filePath: string): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
if (fs.existsSync(filePath)) {
|
||||
fs.unlink(filePath, (err) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Get the path of the lock file. This is used to indicate that the platform-specific dependencies have been downloaded.
|
||||
export function getInstallLockPath(): string {
|
||||
return getExtensionFilePath("install.lock");
|
||||
@@ -503,3 +582,83 @@ export async function renamePromise(oldName: string, newName: string): Promise<v
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function promptForReloadWindowDueToSettingsChange(): void {
|
||||
promptReloadWindow("Reload the workspace for the settings change to take effect.");
|
||||
}
|
||||
|
||||
export function promptReloadWindow(message: string): void {
|
||||
let reload: string = "Reload";
|
||||
vscode.window.showInformationMessage(message, reload).then((value: string) => {
|
||||
if (value === reload) {
|
||||
vscode.commands.executeCommand("workbench.action.reloadWindow");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function downloadFileToDestination(urlStr: string, destinationPath: string, headers?: OutgoingHttpHeaders): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
let parsedUrl: url.Url = url.parse(urlStr);
|
||||
let request: ClientRequest = https.request({
|
||||
host: parsedUrl.host,
|
||||
path: parsedUrl.path,
|
||||
agent: getHttpsProxyAgent(),
|
||||
rejectUnauthorized: vscode.workspace.getConfiguration().get('http.proxyStrictSSL', true),
|
||||
headers: headers
|
||||
}, (response) => {
|
||||
if (response.statusCode === 301 || response.statusCode === 302) { // If redirected
|
||||
// Download from new location
|
||||
let redirectUrl: string;
|
||||
if (typeof response.headers.location === 'string') {
|
||||
redirectUrl = response.headers.location;
|
||||
} else {
|
||||
redirectUrl = response.headers.location[0];
|
||||
}
|
||||
return resolve(downloadFileToDestination(redirectUrl, destinationPath, headers));
|
||||
}
|
||||
if (response.statusCode !== 200) { // If request is not successful
|
||||
return reject();
|
||||
}
|
||||
// Write file using downloaded data
|
||||
let createdFile: fs.WriteStream = fs.createWriteStream(destinationPath);
|
||||
createdFile.on('finish', () => { resolve(); });
|
||||
response.on('error', (error) => { reject(); });
|
||||
response.pipe(createdFile);
|
||||
});
|
||||
request.on('error', (error) => { reject(); });
|
||||
request.end();
|
||||
});
|
||||
}
|
||||
|
||||
export function downloadFileToStr(urlStr: string, headers?: OutgoingHttpHeaders): Promise<any> {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
let parsedUrl: url.Url = url.parse(urlStr);
|
||||
let request: ClientRequest = https.request({
|
||||
host: parsedUrl.host,
|
||||
path: parsedUrl.path,
|
||||
agent: getHttpsProxyAgent(),
|
||||
rejectUnauthorized: vscode.workspace.getConfiguration().get('http.proxyStrictSSL', true),
|
||||
headers: headers
|
||||
}, (response) => {
|
||||
if (response.statusCode === 301 || response.statusCode === 302) { // If redirected
|
||||
// Download from new location
|
||||
let redirectUrl: string;
|
||||
if (typeof response.headers.location === 'string') {
|
||||
redirectUrl = response.headers.location;
|
||||
} else {
|
||||
redirectUrl = response.headers.location[0];
|
||||
}
|
||||
return resolve(downloadFileToStr(redirectUrl, headers));
|
||||
}
|
||||
if (response.statusCode !== 200) { // If request is not successful
|
||||
return reject();
|
||||
}
|
||||
let downloadedData: string = '';
|
||||
response.on('data', (data) => { downloadedData += data; });
|
||||
response.on('error', (error) => { reject(); });
|
||||
response.on('end', () => { resolve(downloadedData); });
|
||||
});
|
||||
request.on('error', (error) => { reject(); });
|
||||
request.end();
|
||||
});
|
||||
}
|
||||
+124
-18
@@ -1,18 +1,124 @@
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All Rights Reserved.
|
||||
* See 'LICENSE' in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
'use strict';
|
||||
|
||||
import { CppToolsApi, CustomConfigurationProvider } from './api';
|
||||
import * as LanguageServer from './LanguageServer/extension';
|
||||
|
||||
export class CppTools implements CppToolsApi {
|
||||
registerCustomConfigurationProvider(provider: CustomConfigurationProvider): void {
|
||||
LanguageServer.registerCustomConfigurationProvider(provider);
|
||||
}
|
||||
|
||||
didChangeCustomConfiguration(provider: CustomConfigurationProvider): void {
|
||||
LanguageServer.onDidChangeCustomConfiguration(provider);
|
||||
}
|
||||
}
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All Rights Reserved.
|
||||
* See 'LICENSE' in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
'use strict';
|
||||
|
||||
import { CustomConfigurationProvider, Version } from 'vscode-cpptools';
|
||||
import { CppToolsTestApi, CppToolsTestHook } from 'vscode-cpptools/out/testApi';
|
||||
import { CustomConfigurationProvider1, getCustomConfigProviders, CustomConfigurationProviderCollection } from './LanguageServer/customProviders';
|
||||
import { getOutputChannel } from './logger';
|
||||
import * as LanguageServer from './LanguageServer/extension';
|
||||
import * as test from './testHook';
|
||||
|
||||
export class CppTools implements CppToolsTestApi {
|
||||
private version: Version;
|
||||
private providers: CustomConfigurationProvider1[] = [];
|
||||
private failedRegistrations: CustomConfigurationProvider[] = [];
|
||||
private timers = new Map<string, NodeJS.Timer>();
|
||||
|
||||
constructor(version: Version) {
|
||||
if (version > Version.latest) {
|
||||
console.warn(`version ${version} is not supported by this version of cpptools`);
|
||||
console.warn(` using ${Version.latest} instead`);
|
||||
version = Version.latest;
|
||||
}
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
private addNotifyReadyTimer(provider: CustomConfigurationProvider1): void {
|
||||
if (this.version >= Version.v2) {
|
||||
const timeout: number = 30;
|
||||
let timer: NodeJS.Timer = setTimeout(() => {
|
||||
console.warn(`registered provider ${provider.extensionId} did not call 'notifyReady' within ${timeout} seconds`);
|
||||
}, timeout * 1000);
|
||||
this.timers.set(provider.extensionId, timer);
|
||||
}
|
||||
}
|
||||
|
||||
private removeNotifyReadyTimer(provider: CustomConfigurationProvider1): void {
|
||||
if (this.version >= Version.v2) {
|
||||
let timer: NodeJS.Timer = this.timers.get(provider.extensionId);
|
||||
if (timer) {
|
||||
this.timers.delete(provider.extensionId);
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public getVersion(): Version {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public registerCustomConfigurationProvider(provider: CustomConfigurationProvider): void {
|
||||
let providers: CustomConfigurationProviderCollection = getCustomConfigProviders();
|
||||
if (providers.add(provider, this.version)) {
|
||||
let added: CustomConfigurationProvider1 = providers.get(provider);
|
||||
getOutputChannel().appendLine(`Custom configuration provider '${added.name}' registered`);
|
||||
this.providers.push(added);
|
||||
LanguageServer.getClients().forEach(client => client.onRegisterCustomConfigurationProvider(added));
|
||||
this.addNotifyReadyTimer(added);
|
||||
} else {
|
||||
this.failedRegistrations.push(provider);
|
||||
}
|
||||
}
|
||||
|
||||
public notifyReady(provider: CustomConfigurationProvider): void {
|
||||
let providers: CustomConfigurationProviderCollection = getCustomConfigProviders();
|
||||
let p: CustomConfigurationProvider1 = providers.get(provider);
|
||||
|
||||
if (p) {
|
||||
this.removeNotifyReadyTimer(p);
|
||||
p.isReady = true;
|
||||
LanguageServer.getClients().forEach(client => {
|
||||
client.updateCustomConfigurations(p);
|
||||
client.updateCustomBrowseConfiguration(p);
|
||||
});
|
||||
} else if (this.failedRegistrations.find(p => p === provider)) {
|
||||
console.warn("provider not successfully registered, 'notifyReady' ignored");
|
||||
} else {
|
||||
console.warn("provider should be registered before signaling it's ready to provide configurations");
|
||||
}
|
||||
}
|
||||
|
||||
public didChangeCustomConfiguration(provider: CustomConfigurationProvider): void {
|
||||
let providers: CustomConfigurationProviderCollection = getCustomConfigProviders();
|
||||
let p: CustomConfigurationProvider1 = providers.get(provider);
|
||||
|
||||
if (p) {
|
||||
if (!p.isReady) {
|
||||
console.warn("didChangeCustomConfiguration was invoked before notifyReady");
|
||||
}
|
||||
LanguageServer.getClients().forEach(client => client.updateCustomConfigurations(p));
|
||||
} else if (this.failedRegistrations.find(p => p === provider)) {
|
||||
console.warn("provider not successfully registered, 'didChangeCustomConfiguration' ignored");
|
||||
} else {
|
||||
console.warn("provider should be registered before sending config change messages");
|
||||
}
|
||||
}
|
||||
|
||||
public didChangeCustomBrowseConfiguration(provider: CustomConfigurationProvider): void {
|
||||
let providers: CustomConfigurationProviderCollection = getCustomConfigProviders();
|
||||
let p: CustomConfigurationProvider1 = providers.get(provider);
|
||||
|
||||
if (p) {
|
||||
LanguageServer.getClients().forEach(client => client.updateCustomBrowseConfiguration(p));
|
||||
} else if (this.failedRegistrations.find(p => p === provider)) {
|
||||
console.warn("provider not successfully registered, 'didChangeCustomBrowseConfiguration' ignored");
|
||||
} else {
|
||||
console.warn("provider should be registered before sending config change messages");
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.providers.forEach(provider => {
|
||||
getCustomConfigProviders().remove(provider);
|
||||
provider.dispose();
|
||||
});
|
||||
this.providers = [];
|
||||
}
|
||||
|
||||
public getTestHook(): CppToolsTestHook {
|
||||
return test.getTestHook();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All Rights Reserved.
|
||||
* See 'LICENSE' in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
'use strict';
|
||||
|
||||
import { CustomConfigurationProvider, Version, CppToolsApi } from 'vscode-cpptools';
|
||||
import { CppToolsTestApi, CppToolsTestHook, CppToolsTestExtension } from 'vscode-cpptools/out/testApi';
|
||||
import { CppTools } from './cppTools';
|
||||
|
||||
/**
|
||||
* This class implements both interfaces since the extension returned CppToolsApi for v0,
|
||||
* but returns CppToolsTestExtension for v1 and later.
|
||||
*/
|
||||
export class CppTools1 implements CppToolsTestApi, CppToolsTestExtension {
|
||||
private backupApi: CppTools;
|
||||
|
||||
private get BackupApi(): CppToolsTestApi {
|
||||
if (!this.backupApi) {
|
||||
this.backupApi = new CppTools(Version.v0);
|
||||
}
|
||||
return this.backupApi;
|
||||
}
|
||||
|
||||
getApi(version: Version): CppToolsApi {
|
||||
switch (version) {
|
||||
case Version.v0:
|
||||
return this.BackupApi;
|
||||
|
||||
default:
|
||||
return new CppTools(version);
|
||||
}
|
||||
}
|
||||
|
||||
getTestApi(version: Version): CppToolsTestApi {
|
||||
return <CppToolsTestApi>this.getApi(version);
|
||||
}
|
||||
|
||||
getVersion(): Version {
|
||||
return this.BackupApi.getVersion();
|
||||
}
|
||||
|
||||
registerCustomConfigurationProvider(provider: CustomConfigurationProvider): void {
|
||||
this.BackupApi.registerCustomConfigurationProvider(provider);
|
||||
}
|
||||
|
||||
notifyReady(provider: CustomConfigurationProvider): void {
|
||||
this.BackupApi.notifyReady(provider);
|
||||
}
|
||||
|
||||
didChangeCustomConfiguration(provider: CustomConfigurationProvider): void {
|
||||
this.BackupApi.didChangeCustomConfiguration(provider);
|
||||
}
|
||||
|
||||
didChangeCustomBrowseConfiguration(provider: CustomConfigurationProvider): void {
|
||||
this.BackupApi.didChangeCustomBrowseConfiguration(provider);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
}
|
||||
|
||||
getTestHook(): CppToolsTestHook {
|
||||
return this.BackupApi.getTestHook();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All Rights Reserved.
|
||||
* See 'LICENSE' in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
'use strict';
|
||||
|
||||
import { PackageVersion } from './packageVersion';
|
||||
import * as util from './common';
|
||||
import { PlatformInformation } from './platform';
|
||||
import { OutgoingHttpHeaders } from 'http';
|
||||
|
||||
/**
|
||||
* The object representation of a Build Asset. Each Asset corresponds to information about a release file on GitHub.
|
||||
*/
|
||||
interface Asset {
|
||||
name: string;
|
||||
browser_download_url: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The object representation of a release in the GitHub API's release JSON.
|
||||
* Named Build so as to reduce confusion between a "Release" release and "Insiders" release.
|
||||
*/
|
||||
interface Build {
|
||||
name: string;
|
||||
assets: Asset[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Search each Asset by name to retrieve the download URL for a VSIX package
|
||||
* @param vsixName The name of the VSIX to search for
|
||||
* @return The download URL of the VSIX
|
||||
*/
|
||||
function getVsixDownloadUrl(build: Build, vsixName: string): string {
|
||||
const downloadUrl: string = build.assets.find(asset => {
|
||||
return asset.name === vsixName;
|
||||
}).browser_download_url;
|
||||
if (!downloadUrl) {
|
||||
throw new Error('Failed to find VSIX: ' + vsixName + ' in build: ' + build.name);
|
||||
}
|
||||
return downloadUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether an object is of type Asset.
|
||||
* @param input Incoming object.
|
||||
* @return Whether input is of type Asset.
|
||||
*/
|
||||
function isAsset(input: any): input is Asset {
|
||||
return input && input.name && typeof(input.name) === "string" &&
|
||||
input.browser_download_url && typeof(input.browser_download_url) === "string";
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether an object is of type Build. Note that earlier releases of the extension
|
||||
* do not have 4 or greater Assets (Mac, Win, Linux 32/64). Only call this on more recent Builds.
|
||||
* @param input Incoming object.
|
||||
* @return Whether input is of type Build.
|
||||
*/
|
||||
function isBuild(input: any): input is Build {
|
||||
return input && input.name && typeof(input.name) === "string" && isArrayOfAssets(input.assets) && input.assets.length >= 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether an object is of type Asset[].
|
||||
* @param input Incoming object.
|
||||
* @return Whether input is of type Asset[].
|
||||
*/
|
||||
function isArrayOfAssets(input: any): input is Asset[] {
|
||||
return input instanceof Array && input.every(item => isAsset(item));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether an object is of type Build[].
|
||||
* @param input Incoming object.
|
||||
* @return Whether input is of type Build[].
|
||||
*/
|
||||
function isArrayOfBuilds(input: any): input is Build[] {
|
||||
if (!input || !(input instanceof Array) || input.length === 0) {
|
||||
return false;
|
||||
}
|
||||
// Only check the five most recent builds for validity -- no need to check all of them
|
||||
for (let i: number = 0; i < 5 && i < input.length; i++) {
|
||||
if (!isBuild(input[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the user's platform information to the VSIX name relevant to them.
|
||||
* @param info Information about the user's operating system.
|
||||
* @return VSIX filename for the extension's releases matched to the user's platform.
|
||||
*/
|
||||
function vsixNameForPlatform(info: PlatformInformation): string {
|
||||
const vsixName: string = function(platformInfo): string {
|
||||
switch (platformInfo.platform) {
|
||||
case 'win32': return 'cpptools-win32.vsix';
|
||||
case 'darwin': return 'cpptools-osx.vsix';
|
||||
default: {
|
||||
switch (platformInfo.architecture) {
|
||||
case 'x86_64': return 'cpptools-linux.vsix';
|
||||
case 'x86':
|
||||
case 'i386':
|
||||
case 'i686': return 'cpptools-linux32.vsix';
|
||||
}
|
||||
}
|
||||
}
|
||||
}(info);
|
||||
if (!vsixName) {
|
||||
throw new Error('Failed to match VSIX name for: ' + info.platform + ':' + info.architecture);
|
||||
}
|
||||
return vsixName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for return value of getTargetBuildInfo containing the download URL and version of a Build.
|
||||
*/
|
||||
export interface BuildInfo {
|
||||
downloadUrl: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the GitHub API to retrieve the download URL of the extension version the user should update to, if any.
|
||||
* @param updateChannel The user's updateChannel setting.
|
||||
* @return Download URL for the extension VSIX package that the user should install. If the user
|
||||
* does not need to update, resolves to undefined.
|
||||
*/
|
||||
export async function getTargetBuildInfo(updateChannel: string): Promise<BuildInfo> {
|
||||
return getReleaseJson()
|
||||
.then(builds => {
|
||||
if (!builds || builds.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// If the user version is greater than or incomparable to the latest available verion then there is no need to update
|
||||
// Allows testing pre-releases without accidentally downgrading to the latest version
|
||||
const userVersion: PackageVersion = new PackageVersion(util.packageJson.version);
|
||||
const latestVersion: PackageVersion = new PackageVersion(builds[0].name);
|
||||
if (userVersion.isGreaterThan(latestVersion) || (userVersion.suffix && userVersion.suffix !== 'insiders')) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return getTargetBuild(builds, userVersion, updateChannel);
|
||||
})
|
||||
.then(async build => {
|
||||
if (!build) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
try {
|
||||
const platformInfo: PlatformInformation = await PlatformInformation.GetPlatformInformation();
|
||||
const vsixName: string = vsixNameForPlatform(platformInfo);
|
||||
const downloadUrl: string = getVsixDownloadUrl(build, vsixName);
|
||||
return { downloadUrl: downloadUrl, name: build.name };
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether there exists a Build in the given Build[] that should be installed.
|
||||
* @param builds The GitHub release list parsed as an array of Builds.
|
||||
* @param userVersion The verion of the extension that the user is running.
|
||||
* @param updateChannel The user's updateChannel setting.
|
||||
* @return The Build if the user should update to it, otherwise undefined.
|
||||
*/
|
||||
function getTargetBuild(builds: Build[], userVersion: PackageVersion, updateChannel: string): Build {
|
||||
// Get predicates to determine the build to install, if any
|
||||
let needsUpdate: (installed: PackageVersion, target: PackageVersion) => boolean;
|
||||
let useBuild: (build: Build) => boolean;
|
||||
if (updateChannel === 'Insiders') {
|
||||
needsUpdate = (installed: PackageVersion, target: PackageVersion) => { return target.isGreaterThan(installed); };
|
||||
useBuild = (build: Build): boolean => { return true; };
|
||||
} else if (updateChannel === 'Default') {
|
||||
needsUpdate = function(installed: PackageVersion, target: PackageVersion): boolean { return installed.isGreaterThan(target); };
|
||||
useBuild = (build: Build): boolean => { return build.name.indexOf('-') === -1; };
|
||||
} else {
|
||||
throw new Error('Incorrect updateChannel setting provided');
|
||||
}
|
||||
|
||||
// Get the build to install
|
||||
const targetBuild: Build = builds.find((build) => useBuild(build));
|
||||
if (!targetBuild) {
|
||||
throw new Error('Failed to determine installation candidate');
|
||||
}
|
||||
|
||||
// Check current version against target's version to determine if the installation should happen
|
||||
const targetVersion: PackageVersion = new PackageVersion(targetBuild.name);
|
||||
return needsUpdate(userVersion, targetVersion) ? targetBuild : undefined;
|
||||
}
|
||||
|
||||
interface Rate {
|
||||
remaining: number;
|
||||
}
|
||||
|
||||
interface RateLimit {
|
||||
rate: Rate;
|
||||
}
|
||||
|
||||
function isRate(input: any): input is Rate {
|
||||
return input && input.remaining && util.isNumber(input.remaining);
|
||||
}
|
||||
|
||||
function isRateLimit(input: any): input is RateLimit {
|
||||
return input && isRate(input.rate);
|
||||
}
|
||||
|
||||
async function getRateLimit(): Promise<RateLimit> {
|
||||
const header: OutgoingHttpHeaders = { 'User-Agent': 'vscode-cpptools' };
|
||||
const data: string = await util.downloadFileToStr('https://api.github.com/rate_limit', header)
|
||||
.catch(() => { throw new Error('Failed to download rate limit JSON'); });
|
||||
|
||||
let rateLimit: any;
|
||||
try {
|
||||
rateLimit = JSON.parse(data);
|
||||
} catch (error) {
|
||||
throw new Error('Failed to parse rate limit JSON');
|
||||
}
|
||||
|
||||
if (isRateLimit(rateLimit)) {
|
||||
return Promise.resolve(rateLimit);
|
||||
} else {
|
||||
throw new Error('Rate limit JSON is not of type RateLimit');
|
||||
}
|
||||
}
|
||||
|
||||
async function rateLimitExceeded(): Promise<boolean> {
|
||||
const rateLimit: RateLimit = await getRateLimit();
|
||||
return rateLimit.rate.remaining <= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Download and parse the release list JSON from the GitHub API into a Build[].
|
||||
* @return Information about the released builds of the C/C++ extension.
|
||||
*/
|
||||
async function getReleaseJson(): Promise<Build[]> {
|
||||
if (await rateLimitExceeded()) {
|
||||
throw new Error('Failed to stay within GitHub API rate limit');
|
||||
}
|
||||
|
||||
// Download release JSON
|
||||
const releaseUrl: string = 'https://api.github.com/repos/Microsoft/vscode-cpptools/releases';
|
||||
const header: OutgoingHttpHeaders = { 'User-Agent': 'vscode-cpptools' };
|
||||
|
||||
const data: string = await util.downloadFileToStr(releaseUrl, header)
|
||||
.catch(() => { throw new Error('Failed to download release JSON'); });
|
||||
|
||||
// Parse the file
|
||||
let releaseJson: any;
|
||||
try {
|
||||
releaseJson = JSON.parse(data);
|
||||
} catch (error) {
|
||||
throw new Error('Failed to parse release JSON');
|
||||
}
|
||||
|
||||
// Type check
|
||||
if (isArrayOfBuilds(releaseJson)) {
|
||||
return releaseJson;
|
||||
} else {
|
||||
throw new Error('Release JSON is not of type Build[]');
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
* See 'LICENSE' in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
export class InstallationInformation {
|
||||
export class InstallationInformation {
|
||||
stage: string;
|
||||
hasError: boolean;
|
||||
telemetryProperties: { [key: string]: string };
|
||||
@@ -14,19 +14,15 @@ export class InstallationInformation {
|
||||
}
|
||||
}
|
||||
|
||||
let installBlob: InstallationInformation ;
|
||||
let installBlob: InstallationInformation;
|
||||
|
||||
export function initializeInstallationInformation(): void {
|
||||
installBlob = new InstallationInformation ();
|
||||
}
|
||||
|
||||
export function getInstallationInformationInstance(): InstallationInformation {
|
||||
export function getInstallationInformation(): InstallationInformation {
|
||||
if (!installBlob) {
|
||||
installBlob = new InstallationInformation();
|
||||
}
|
||||
return installBlob;
|
||||
}
|
||||
|
||||
export function setInstallationStage(stage: string): void {
|
||||
if (!installBlob) {
|
||||
initializeInstallationInformation();
|
||||
}
|
||||
installBlob.stage = stage;
|
||||
getInstallationInformation().stage = stage;
|
||||
}
|
||||
+50
-23
@@ -13,24 +13,26 @@ import * as Telemetry from './telemetry';
|
||||
import * as util from './common';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { CppToolsApi, CppToolsExtension } from 'vscode-cpptools';
|
||||
import { getTemporaryCommandRegistrarInstance, initializeTemporaryCommandRegistrar } from './commands';
|
||||
import { PlatformInformation } from './platform';
|
||||
import { PackageManager, PackageManagerError, PackageManagerWebResponseError, IPackage } from './packageManager';
|
||||
import { PackageManager, PackageManagerError, IPackage } from './packageManager';
|
||||
import { PersistentState } from './LanguageServer/persistentState';
|
||||
import { initializeInstallationInformation, getInstallationInformationInstance, InstallationInformation, setInstallationStage } from './installationInformation';
|
||||
import { getInstallationInformation, InstallationInformation, setInstallationStage } from './installationInformation';
|
||||
import { Logger, getOutputChannelLogger, showOutputChannel } from './logger';
|
||||
import { CppTools } from './cppTools';
|
||||
import { CppToolsApi } from './api';
|
||||
import { CppTools1 } from './cppTools1';
|
||||
|
||||
const releaseNotesVersion: number = 3;
|
||||
const cppTools: CppTools = new CppTools();
|
||||
const cppTools: CppTools1 = new CppTools1();
|
||||
let languageServiceDisabled: boolean = false;
|
||||
let reloadMessageShown: boolean = false;
|
||||
let disposables: vscode.Disposable[] = [];
|
||||
|
||||
export async function activate(context: vscode.ExtensionContext): Promise<CppToolsApi> {
|
||||
export async function activate(context: vscode.ExtensionContext): Promise<CppToolsApi & CppToolsExtension> {
|
||||
initializeTemporaryCommandRegistrar();
|
||||
util.setExtensionContext(context);
|
||||
Telemetry.activate();
|
||||
util.setProgress(0);
|
||||
initializeInstallationInformation();
|
||||
|
||||
// Initialize the DebuggerExtension and register the related commands and providers.
|
||||
DebuggerExtension.initialize();
|
||||
@@ -43,6 +45,11 @@ export async function activate(context: vscode.ExtensionContext): Promise<CppToo
|
||||
export function deactivate(): Thenable<void> {
|
||||
DebuggerExtension.dispose();
|
||||
Telemetry.deactivate();
|
||||
disposables.forEach(d => d.dispose());
|
||||
|
||||
if (languageServiceDisabled) {
|
||||
return;
|
||||
}
|
||||
return LanguageServer.deactivate();
|
||||
}
|
||||
|
||||
@@ -57,6 +64,9 @@ async function processRuntimeDependencies(): Promise<void> {
|
||||
} catch (error) {
|
||||
getOutputChannelLogger().showErrorMessage('The installation of the C/C++ extension failed. Please see the output window for more information.');
|
||||
showOutputChannel();
|
||||
|
||||
// Send the failure telemetry since postInstall will not be called.
|
||||
sendTelemetry(await PlatformInformation.GetPlatformInformation());
|
||||
}
|
||||
// The extension have been installed and activated before.
|
||||
} else {
|
||||
@@ -68,6 +78,9 @@ async function processRuntimeDependencies(): Promise<void> {
|
||||
await onlineInstallation();
|
||||
} catch (error) {
|
||||
handleError(error);
|
||||
|
||||
// Send the failure telemetry since postInstall will not be called.
|
||||
sendTelemetry(await PlatformInformation.GetPlatformInformation());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,23 +186,12 @@ function touchInstallLockFile(): Promise<void> {
|
||||
}
|
||||
|
||||
function handleError(error: any): void {
|
||||
let installationInformation: InstallationInformation = getInstallationInformationInstance();
|
||||
let installationInformation: InstallationInformation = getInstallationInformation();
|
||||
installationInformation.hasError = true;
|
||||
installationInformation.telemetryProperties['stage'] = installationInformation.stage;
|
||||
let errorMessage: string;
|
||||
|
||||
if (error instanceof PackageManagerError) {
|
||||
// If this is a WebResponse error, log the IP that it resolved from the package URL
|
||||
if (error instanceof PackageManagerWebResponseError) {
|
||||
let webRequestPackageError: PackageManagerWebResponseError = error;
|
||||
if (webRequestPackageError.socket) {
|
||||
let address: any = webRequestPackageError.socket.address();
|
||||
if (address) {
|
||||
installationInformation.telemetryProperties['error.targetIP'] = address.address + ':' + address.port;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let packageError: PackageManagerError = error;
|
||||
|
||||
installationInformation.telemetryProperties['error.methodName'] = packageError.methodName;
|
||||
@@ -228,7 +230,7 @@ function handleError(error: any): void {
|
||||
}
|
||||
|
||||
function sendTelemetry(info: PlatformInformation): boolean {
|
||||
let installBlob: InstallationInformation = getInstallationInformationInstance();
|
||||
let installBlob: InstallationInformation = getInstallationInformation();
|
||||
const success: boolean = !installBlob.hasError;
|
||||
|
||||
installBlob.telemetryProperties['success'] = success.toString();
|
||||
@@ -274,14 +276,32 @@ async function postInstall(info: PlatformInformation): Promise<void> {
|
||||
}
|
||||
|
||||
async function finalizeExtensionActivation(): Promise<void> {
|
||||
if (vscode.workspace.getConfiguration("C_Cpp", null).get<string>("intelliSenseEngine") === "Disabled") {
|
||||
languageServiceDisabled = true;
|
||||
getTemporaryCommandRegistrarInstance().disableLanguageServer();
|
||||
disposables.push(vscode.workspace.onDidChangeConfiguration(() => {
|
||||
if (!reloadMessageShown && vscode.workspace.getConfiguration("C_Cpp", null).get<string>("intelliSenseEngine") !== "Disabled") {
|
||||
reloadMessageShown = true;
|
||||
util.promptForReloadWindowDueToSettingsChange();
|
||||
}
|
||||
}));
|
||||
return;
|
||||
}
|
||||
disposables.push(vscode.workspace.onDidChangeConfiguration(() => {
|
||||
if (!reloadMessageShown && vscode.workspace.getConfiguration("C_Cpp", null).get<string>("intelliSenseEngine") === "Disabled") {
|
||||
reloadMessageShown = true;
|
||||
util.promptForReloadWindowDueToSettingsChange();
|
||||
}
|
||||
}));
|
||||
getTemporaryCommandRegistrarInstance().activateLanguageServer();
|
||||
|
||||
// Update default for C_Cpp.intelliSenseEngine based on A/B testing settings.
|
||||
// (this may result in rewriting the package.json file)
|
||||
|
||||
let abTestSettings: cpptoolsJsonUtils.ABTestSettings = cpptoolsJsonUtils.getABTestSettings();
|
||||
let packageJson: any = util.getRawPackageJson();
|
||||
if (util.packageJson.extensionLocation && !util.packageJson.extensionLocation.path.includes(".vscode-insiders")) {
|
||||
let packageJsonPath: string = util.getExtensionFilePath("package.json");
|
||||
if (!packageJsonPath.includes(".vscode-insiders")) {
|
||||
let abTestSettings: cpptoolsJsonUtils.ABTestSettings = cpptoolsJsonUtils.getABTestSettings();
|
||||
let packageJson: any = util.getRawPackageJson();
|
||||
let prevIntelliSenseEngineDefault: any = packageJson.contributes.configuration.properties["C_Cpp.intelliSenseEngine"].default;
|
||||
if (abTestSettings.UseDefaultIntelliSenseEngine) {
|
||||
packageJson.contributes.configuration.properties["C_Cpp.intelliSenseEngine"].default = "Default";
|
||||
@@ -291,6 +311,12 @@ async function finalizeExtensionActivation(): Promise<void> {
|
||||
if (prevIntelliSenseEngineDefault !== packageJson.contributes.configuration.properties["C_Cpp.intelliSenseEngine"].default) {
|
||||
return util.writeFileText(util.getPackageJsonPath(), util.stringifyPackageJson(packageJson));
|
||||
}
|
||||
} else {
|
||||
let packageJson: any = util.getRawPackageJson();
|
||||
if (packageJson.contributes.configuration.properties['C_Cpp.updateChannel'].default === 'Default') {
|
||||
packageJson.contributes.configuration.properties['C_Cpp.updateChannel'].default = 'Insiders';
|
||||
return util.writeFileText(util.getPackageJsonPath(), util.stringifyPackageJson(packageJson));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,6 +331,7 @@ function rewriteManifest(): Promise<void> {
|
||||
"onCommand:extension.pickRemoteNativeProcess",
|
||||
"onCommand:C_Cpp.ConfigurationEdit",
|
||||
"onCommand:C_Cpp.ConfigurationSelect",
|
||||
"onCommand:C_Cpp.ConfigurationProviderSelect",
|
||||
"onCommand:C_Cpp.SwitchHeaderSource",
|
||||
"onCommand:C_Cpp.Navigate",
|
||||
"onCommand:C_Cpp.GoToDeclaration",
|
||||
|
||||
@@ -183,11 +183,10 @@ export class PackageManager {
|
||||
} catch (error) {
|
||||
retryCount += 1;
|
||||
lastError = error;
|
||||
if (retryCount >= MAX_RETRIES) {
|
||||
if (retryCount > MAX_RETRIES) {
|
||||
this.AppendChannel(` Failed to download ` + pkg.url);
|
||||
throw error;
|
||||
} else {
|
||||
// This will skip the success = true.
|
||||
this.AppendChannel(` Failed. Retrying...`);
|
||||
continue;
|
||||
}
|
||||
@@ -198,7 +197,7 @@ export class PackageManager {
|
||||
if (retryCount !== 0) {
|
||||
// Log telemetry to see if retrying helps.
|
||||
let telemetryProperties: { [key: string]: string } = {};
|
||||
telemetryProperties["success"] = `OnRetry${retryCount}`;
|
||||
telemetryProperties["success"] = success ? `OnRetry${retryCount}` : 'false';
|
||||
if (lastError instanceof PackageManagerError) {
|
||||
let packageError: PackageManagerError = lastError;
|
||||
telemetryProperties['error.methodName'] = packageError.methodName;
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All Rights Reserved.
|
||||
* See 'LICENSE' in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
'use strict';
|
||||
|
||||
export class PackageVersion {
|
||||
major: number;
|
||||
minor: number;
|
||||
patch: number;
|
||||
suffix?: string;
|
||||
suffixVersion: number;
|
||||
|
||||
constructor(version: string) {
|
||||
const tokens: string[] = version.split(new RegExp('[-\\.]', 'g')); // Match against dots and dashes
|
||||
if (tokens.length < 3) {
|
||||
throw new Error('Failed to parse version string: ' + version);
|
||||
}
|
||||
|
||||
this.major = parseInt(tokens[0]);
|
||||
this.minor = parseInt(tokens[1]);
|
||||
this.patch = parseInt(tokens[2]);
|
||||
|
||||
if (tokens.length > 3) {
|
||||
const firstDigitOffset: number = tokens[3].search(new RegExp(/(\d)/)); // Find first occurrence of 0-9
|
||||
if (firstDigitOffset !== -1) {
|
||||
this.suffix = tokens[3].substring(0, firstDigitOffset);
|
||||
this.suffixVersion = parseInt(tokens[3].substring(firstDigitOffset));
|
||||
} else {
|
||||
this.suffix = tokens[3];
|
||||
this.suffixVersion = 1;
|
||||
}
|
||||
} else {
|
||||
this.suffix = undefined;
|
||||
this.suffixVersion = 0;
|
||||
}
|
||||
|
||||
if (this.major === undefined || this.minor === undefined || this.patch === undefined) {
|
||||
throw new Error('Failed to parse version string: ' + version);
|
||||
}
|
||||
}
|
||||
|
||||
public isGreaterThan(other: PackageVersion): boolean {
|
||||
// PackageVersions cannot be compared if either have a suffix that is not 'insiders'
|
||||
if ((this.suffix && !this.suffix.startsWith('insiders')) || (other.suffix && !other.suffix.startsWith('insiders'))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let diff: number = this.major - other.major;
|
||||
if (diff) {
|
||||
return diff > 0;
|
||||
} else if (diff = this.minor - other.minor) {
|
||||
return diff > 0;
|
||||
} else if (diff = this.patch - other.patch) {
|
||||
return diff > 0;
|
||||
} else if (this.suffix) {
|
||||
return (other.suffix && this.suffixVersion > other.suffixVersion);
|
||||
} else {
|
||||
return other.suffix ? true : false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All Rights Reserved.
|
||||
* See 'LICENSE' in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
'use strict';
|
||||
|
||||
import { CppToolsTestHook, Status } from 'vscode-cpptools/out/testApi';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export class TestHook implements CppToolsTestHook {
|
||||
private statusChangedEvent: vscode.EventEmitter<Status> = new vscode.EventEmitter<Status>();
|
||||
|
||||
public get StatusChanged(): vscode.Event<Status> {
|
||||
return this.statusChangedEvent.event;
|
||||
}
|
||||
|
||||
public get valid(): boolean {
|
||||
return !!this.statusChangedEvent;
|
||||
}
|
||||
|
||||
public updateStatus(status: Status): void {
|
||||
this.statusChangedEvent.fire(status);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.statusChangedEvent.dispose();
|
||||
this.statusChangedEvent = null;
|
||||
}
|
||||
}
|
||||
|
||||
let testHook: TestHook;
|
||||
|
||||
export function getTestHook(): TestHook {
|
||||
if (!testHook || !testHook.valid) {
|
||||
testHook = new TestHook();
|
||||
}
|
||||
return testHook;
|
||||
}
|
||||
@@ -6,11 +6,15 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as assert from 'assert';
|
||||
import { getLanguageConfigFromPatterns } from '../../src/LanguageServer/languageConfig';
|
||||
import * as util from '../../src/common';
|
||||
import * as api from 'vscode-cpptools';
|
||||
import * as apit from 'vscode-cpptools/out/testApi';
|
||||
import * as config from '../../src/LanguageServer/configurations';
|
||||
import { CppSettings } from '../../src/LanguageServer/settings';
|
||||
import { getActiveClient } from '../../src/LanguageServer/extension';
|
||||
|
||||
suite("multiline comment setting tests", function() {
|
||||
suiteSetup(async function() {
|
||||
suite("multiline comment setting tests", function(): void {
|
||||
suiteSetup(async function(): Promise<void> {
|
||||
let extension: vscode.Extension<any> = vscode.extensions.getExtension("ms-vscode.cpptools");
|
||||
if (!extension.isActive) {
|
||||
await extension.activate();
|
||||
@@ -78,6 +82,267 @@ suite("multiline comment setting tests", function() {
|
||||
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
function cppPropertiesPath(): string {
|
||||
return vscode.workspace.workspaceFolders[0].uri.fsPath + "/.vscode/c_cpp_properties.json";
|
||||
}
|
||||
|
||||
async function changeCppProperties(cppProperties: config.ConfigurationJson, disposables: vscode.Disposable[]): Promise<void> {
|
||||
let promise: Promise<void> = new Promise<void>((resolve, reject) => {
|
||||
disposables.push(getActiveClient().ActiveConfigChanged(name => {
|
||||
if (name === cppProperties.configurations[0].name) {
|
||||
resolve();
|
||||
}
|
||||
}));
|
||||
|
||||
// Can't trust the file watcher, so we need to allocate additional time for the backup watcher to fire.
|
||||
setTimeout(() => { reject(new Error("timeout")); }, 4000);
|
||||
});
|
||||
await util.writeFileText(cppPropertiesPath(), JSON.stringify(cppProperties));
|
||||
let contents: string = await util.readFileText(cppPropertiesPath());
|
||||
console.log(" wrote c_cpp_properties.json: " + contents);
|
||||
return promise;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
suite("extensibility tests v2", function(): void {
|
||||
let cpptools: apit.CppToolsTestApi;
|
||||
let lastResult: api.SourceFileConfigurationItem[];
|
||||
let defaultConfig: api.SourceFileConfiguration = {
|
||||
includePath: [ "${workspaceFolder}", "/v2/folder" ],
|
||||
defines: [ "${workspaceFolder}" ],
|
||||
intelliSenseMode: "msvc-x64",
|
||||
standard: "c++17"
|
||||
};
|
||||
let lastBrowseResult: api.WorkspaceBrowseConfiguration;
|
||||
let defaultBrowseConfig: api.WorkspaceBrowseConfiguration = {
|
||||
browsePath: [ "/v2/folder" ],
|
||||
compilerPath: "",
|
||||
standard: "c++14",
|
||||
windowsSdkVersion: "8.1"
|
||||
};
|
||||
|
||||
// Has to be 'any' instead of api.CustomConfigurationProvider because of missing interface members.
|
||||
let provider: api.CustomConfigurationProvider = {
|
||||
name: "cpptoolsTest-v2",
|
||||
extensionId: "ms-vscode.cpptools-test2",
|
||||
canProvideConfiguration(document: vscode.Uri): Thenable<boolean> {
|
||||
return Promise.resolve(true);
|
||||
},
|
||||
provideConfigurations(uris: vscode.Uri[]): Thenable<api.SourceFileConfigurationItem[]> {
|
||||
let result: api.SourceFileConfigurationItem[] = [];
|
||||
uris.forEach(uri => {
|
||||
result.push({
|
||||
uri: uri.toString(),
|
||||
configuration: defaultConfig
|
||||
});
|
||||
});
|
||||
lastResult = result;
|
||||
return Promise.resolve(result);
|
||||
},
|
||||
canProvideBrowseConfiguration(): Thenable<boolean> {
|
||||
return Promise.resolve(true);
|
||||
},
|
||||
provideBrowseConfiguration(): Thenable<api.WorkspaceBrowseConfiguration> {
|
||||
lastBrowseResult = defaultBrowseConfig;
|
||||
return Promise.resolve(defaultBrowseConfig);
|
||||
},
|
||||
dispose(): void {
|
||||
console.log(" disposed");
|
||||
}
|
||||
};
|
||||
let disposables: vscode.Disposable[] = [];
|
||||
|
||||
suiteSetup(async function(): Promise<void> {
|
||||
cpptools = await apit.getCppToolsTestApi(api.Version.v2);
|
||||
cpptools.registerCustomConfigurationProvider(provider);
|
||||
cpptools.notifyReady(provider);
|
||||
disposables.push(cpptools);
|
||||
|
||||
await changeCppProperties({
|
||||
configurations: [ {name: "test2", configurationProvider: provider.extensionId} ],
|
||||
version: 4
|
||||
},
|
||||
disposables);
|
||||
});
|
||||
|
||||
suiteTeardown(function(): void {
|
||||
disposables.forEach(d => d.dispose());
|
||||
});
|
||||
|
||||
test("Check provider", async () => {
|
||||
// Open a c++ file to start the language server.
|
||||
let path: string = vscode.workspace.workspaceFolders[0].uri.fsPath + "/main2.cpp";
|
||||
let uri: vscode.Uri = vscode.Uri.file(path);
|
||||
|
||||
let testHook: apit.CppToolsTestHook = cpptools.getTestHook();
|
||||
let testResult: any = new Promise<void>((resolve, reject) => {
|
||||
disposables.push(testHook.StatusChanged(status => {
|
||||
if (status === apit.Status.IntelliSenseReady) {
|
||||
let expected: api.SourceFileConfigurationItem[] = [ {uri: uri.toString(), configuration: defaultConfig} ];
|
||||
assert.deepEqual(lastResult, expected);
|
||||
assert.deepEqual(lastBrowseResult, defaultBrowseConfig);
|
||||
resolve();
|
||||
}
|
||||
}));
|
||||
setTimeout(() => { reject(new Error("timeout")); }, 2500);
|
||||
});
|
||||
disposables.push(testHook);
|
||||
|
||||
await vscode.workspace.openTextDocument(path);
|
||||
await testResult;
|
||||
});
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
suite("extensibility tests v1", function(): void {
|
||||
let cpptools: apit.CppToolsTestApi;
|
||||
let lastResult: api.SourceFileConfigurationItem[];
|
||||
let defaultConfig: api.SourceFileConfiguration = {
|
||||
includePath: [ "${workspaceFolder}" ],
|
||||
defines: [ "${workspaceFolder}" ],
|
||||
intelliSenseMode: "msvc-x64",
|
||||
standard: "c++17"
|
||||
};
|
||||
|
||||
// Has to be 'any' instead of api.CustomConfigurationProvider because of missing interface members.
|
||||
let provider: any = {
|
||||
name: "cpptoolsTest-v1",
|
||||
extensionId: "ms-vscode.cpptools-test",
|
||||
canProvideConfiguration(document: vscode.Uri): Thenable<boolean> {
|
||||
return Promise.resolve(true);
|
||||
},
|
||||
provideConfigurations(uris: vscode.Uri[]): Thenable<api.SourceFileConfigurationItem[]> {
|
||||
let result: api.SourceFileConfigurationItem[] = [];
|
||||
uris.forEach(uri => {
|
||||
result.push({
|
||||
uri: uri.toString(),
|
||||
configuration: defaultConfig
|
||||
});
|
||||
});
|
||||
lastResult = result;
|
||||
return Promise.resolve(result);
|
||||
},
|
||||
dispose(): void {
|
||||
console.log(" disposed");
|
||||
}
|
||||
};
|
||||
let disposables: vscode.Disposable[] = [];
|
||||
|
||||
suiteSetup(async function(): Promise<void> {
|
||||
cpptools = await apit.getCppToolsTestApi(api.Version.v1);
|
||||
cpptools.registerCustomConfigurationProvider(provider);
|
||||
disposables.push(cpptools);
|
||||
|
||||
await changeCppProperties({
|
||||
configurations: [ {name: "test1", configurationProvider: provider.extensionId} ],
|
||||
version: 4
|
||||
},
|
||||
disposables);
|
||||
});
|
||||
|
||||
suiteTeardown(function(): void {
|
||||
disposables.forEach(d => d.dispose());
|
||||
});
|
||||
|
||||
test("Check provider", async () => {
|
||||
// Open a c++ file to start the language server.
|
||||
let path: string = vscode.workspace.workspaceFolders[0].uri.fsPath + "/main1.cpp";
|
||||
let uri: vscode.Uri = vscode.Uri.file(path);
|
||||
|
||||
let testHook: apit.CppToolsTestHook = cpptools.getTestHook();
|
||||
let testResult: any = new Promise<void>((resolve, reject) => {
|
||||
disposables.push(testHook.StatusChanged(status => {
|
||||
if (status === apit.Status.IntelliSenseReady) {
|
||||
let expected: api.SourceFileConfigurationItem[] = [ {uri: uri.toString(), configuration: defaultConfig} ];
|
||||
assert.deepEqual(lastResult, expected);
|
||||
resolve();
|
||||
}
|
||||
}));
|
||||
setTimeout(() => { reject(new Error("timeout")); }, 2500);
|
||||
});
|
||||
disposables.push(testHook);
|
||||
|
||||
await vscode.workspace.openTextDocument(path);
|
||||
await testResult;
|
||||
});
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
suite("extensibility tests v0", function(): void {
|
||||
let cpptools: apit.CppToolsTestApi;
|
||||
let lastResult: api.SourceFileConfigurationItem[];
|
||||
let defaultConfig: api.SourceFileConfiguration = {
|
||||
includePath: [ "${workspaceFolder}" ],
|
||||
defines: [ "${workspaceFolder}" ],
|
||||
intelliSenseMode: "msvc-x64",
|
||||
standard: "c++17"
|
||||
};
|
||||
|
||||
// Has to be 'any' instead of api.CustomConfigurationProvider because of missing interface members.
|
||||
let provider: any = {
|
||||
name: "cpptoolsTest-v0",
|
||||
canProvideConfiguration(document: vscode.Uri): Thenable<boolean> {
|
||||
return Promise.resolve(true);
|
||||
},
|
||||
provideConfigurations(uris: vscode.Uri[]): Thenable<api.SourceFileConfigurationItem[]> {
|
||||
let result: api.SourceFileConfigurationItem[] = [];
|
||||
uris.forEach(uri => {
|
||||
result.push({
|
||||
uri: uri.toString(),
|
||||
configuration: defaultConfig
|
||||
});
|
||||
});
|
||||
lastResult = result;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
};
|
||||
let disposables: vscode.Disposable[] = [];
|
||||
|
||||
suiteSetup(async function(): Promise<void> {
|
||||
cpptools = await apit.getCppToolsTestApi(api.Version.v0);
|
||||
cpptools.registerCustomConfigurationProvider(provider);
|
||||
disposables.push(cpptools); // This is a no-op for v0, but do it anyway to make sure nothing breaks.
|
||||
|
||||
await changeCppProperties({
|
||||
configurations: [ { name: "test0", configurationProvider: provider.name } ],
|
||||
version: 4
|
||||
},
|
||||
disposables);
|
||||
});
|
||||
|
||||
suiteTeardown(async function(): Promise<void> {
|
||||
disposables.forEach(d => d.dispose());
|
||||
await util.deleteFile(cppPropertiesPath());
|
||||
});
|
||||
|
||||
test("Check provider", async () => {
|
||||
// Open a C++ file to start the language server.
|
||||
let path: string = vscode.workspace.workspaceFolders[0].uri.fsPath + "/main.cpp";
|
||||
let uri: vscode.Uri = vscode.Uri.file(path);
|
||||
|
||||
let testHook: apit.CppToolsTestHook = cpptools.getTestHook();
|
||||
let testResult: any = new Promise<void>((resolve, reject) => {
|
||||
disposables.push(testHook.StatusChanged(status => {
|
||||
if (status === apit.Status.IntelliSenseReady) {
|
||||
let expected: api.SourceFileConfigurationItem[] = [ {uri: uri.toString(), configuration: defaultConfig} ];
|
||||
assert.deepEqual(lastResult, expected);
|
||||
resolve();
|
||||
}
|
||||
}));
|
||||
setTimeout(() => { reject(new Error("timeout")); }, 2500);
|
||||
});
|
||||
disposables.push(testHook);
|
||||
|
||||
await vscode.workspace.openTextDocument(path);
|
||||
await testResult;
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
suite("configuration tests", function() {
|
||||
suiteSetup(async function() {
|
||||
@@ -93,8 +358,6 @@ suite("configuration tests", function() {
|
||||
// Delete c_cpp_properties.json
|
||||
});
|
||||
|
||||
|
||||
|
||||
test("Check default configuration", () => {
|
||||
let rootUri: vscode.Uri;
|
||||
if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0) {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
int main(int argc, char* arv[]) {
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
int main(int argc, char* arv[]) {
|
||||
return 0;
|
||||
}
|
||||
@@ -5,11 +5,23 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
child_process = require('child_process');
|
||||
path = require('path');
|
||||
async_child_process = require('async-child-process');
|
||||
|
||||
if (process.env.CODE_TESTS_PATH
|
||||
&& process.env.CODE_TESTS_PATH.startsWith('.')){
|
||||
process.env.CODE_TESTS_PATH = path.join(process.cwd(), process.env.CODE_TESTS_PATH.substr(2));
|
||||
}
|
||||
|
||||
require(path.resolve(__dirname, '../node_modules/vscode/bin/test'));
|
||||
let optionsWithFullEnvironment = {
|
||||
cwd: path.resolve(__dirname, '..'),
|
||||
stdio: 'inherit',
|
||||
env: {
|
||||
...process.env,
|
||||
}
|
||||
};
|
||||
|
||||
spawn = child_process.spawn('node', [path.resolve(__dirname, '../node_modules/vscode/bin/test')], optionsWithFullEnvironment);
|
||||
|
||||
return async_child_process.join(spawn);
|
||||
@@ -0,0 +1,234 @@
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All Rights Reserved.
|
||||
* See 'LICENSE' in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as assert from "assert";
|
||||
import { resolveVariables } from "../../src/common";
|
||||
|
||||
suite("Common Utility validation", () => {
|
||||
suite("resolveVariables", () => {
|
||||
const success: string = "success";
|
||||
const home: string = process.env.HOME || process.env.USERPROFILE;
|
||||
|
||||
test("raw input", () => {
|
||||
const input: string = "test";
|
||||
inputAndEnvironment(input, {})
|
||||
.shouldResolveTo(input);
|
||||
});
|
||||
|
||||
test("raw input with tilde", () => {
|
||||
inputAndEnvironment("~/test", {})
|
||||
.shouldResolveTo(`${home}/test`);
|
||||
});
|
||||
|
||||
test("env input with tilde", () => {
|
||||
inputAndEnvironment("${path}/test", {
|
||||
path: home
|
||||
})
|
||||
.shouldResolveTo(`${home}/test`);
|
||||
});
|
||||
|
||||
test("solo env input resulting in array", () => {
|
||||
inputAndEnvironment("${test}", {
|
||||
test: ["foo", "bar"]
|
||||
})
|
||||
.shouldResolveTo("foo;bar");
|
||||
});
|
||||
|
||||
test("solo env input with empty array env value", () => {
|
||||
resolveVariablesWithInput("${empty}")
|
||||
.withEnvironment({
|
||||
"empty": []
|
||||
})
|
||||
.shouldResolveTo("");
|
||||
});
|
||||
|
||||
test("mixed raw and env input resulting in array", () => {
|
||||
const input: string = "baz${test}";
|
||||
resolveVariablesWithInput(input)
|
||||
.withEnvironment({
|
||||
test: ["foo", "bar"]
|
||||
})
|
||||
.shouldResolveTo(input);
|
||||
});
|
||||
|
||||
test("solo env input not in env config finds process env", () => {
|
||||
const processKey: string = `cpptoolstests_${Date.now()}`;
|
||||
const input: string = "foo${" + processKey + "}";
|
||||
let actual: string;
|
||||
try {
|
||||
process.env[processKey] = "bar";
|
||||
actual = resolveVariables(input, {});
|
||||
} finally {
|
||||
delete process.env[processKey];
|
||||
}
|
||||
assert.equal(actual, "foobar");
|
||||
});
|
||||
|
||||
test("env input", () => {
|
||||
resolveVariablesWithInput("${test}")
|
||||
.withEnvironment({
|
||||
"test": success
|
||||
})
|
||||
.shouldResolveTo(success);
|
||||
});
|
||||
|
||||
test("env input mixed with plain text", () => {
|
||||
resolveVariablesWithInput("${test}bar")
|
||||
.withEnvironment({
|
||||
"test": "foo"
|
||||
})
|
||||
.shouldResolveTo("foobar");
|
||||
});
|
||||
|
||||
test("env input with two variables", () => {
|
||||
resolveVariablesWithInput("f${a}${b}r")
|
||||
.withEnvironment({
|
||||
a: "oo",
|
||||
b: "ba"
|
||||
})
|
||||
.shouldResolveTo("foobar");
|
||||
});
|
||||
|
||||
test("env input not in env", () => {
|
||||
const input: string = "${test}";
|
||||
resolveVariablesWithInput(input)
|
||||
.withEnvironment({})
|
||||
.shouldResolveTo(input);
|
||||
});
|
||||
|
||||
test("env with macro inside environment definition", () => {
|
||||
resolveVariablesWithInput("${arm6.include}")
|
||||
.withEnvironment({
|
||||
"envRoot": "apps/tool/buildenv",
|
||||
"arm6.include": "${envRoot}/arm6/include"
|
||||
})
|
||||
.shouldResolveTo("apps/tool/buildenv/arm6/include");
|
||||
});
|
||||
|
||||
test("env nested with half open variable", () => {
|
||||
resolveVariablesWithInput("${arm6.include}")
|
||||
.withEnvironment({
|
||||
"envRoot": "apps/tool/buildenv",
|
||||
"arm6.include": "${envRoot/arm6/include"
|
||||
})
|
||||
.shouldResolveTo("${envRoot/arm6/include");
|
||||
});
|
||||
|
||||
test("env nested with half closed variable", () => {
|
||||
resolveVariablesWithInput("${arm6.include}")
|
||||
.withEnvironment({
|
||||
"envRoot": "apps/tool/buildenv",
|
||||
"arm6.include": "envRoot}/arm6/include"
|
||||
})
|
||||
.shouldResolveTo("envRoot}/arm6/include");
|
||||
});
|
||||
|
||||
test("env nested with a cycle", () => {
|
||||
resolveVariablesWithInput("${a}")
|
||||
.withEnvironment({
|
||||
"a": "${b}",
|
||||
"b": "${c}",
|
||||
"c": "${a}"
|
||||
})
|
||||
.shouldResolveTo("${a}");
|
||||
});
|
||||
|
||||
test("env input with 1 level of nested variables anchored at end", () => {
|
||||
resolveVariablesWithInput("${foo${test}}")
|
||||
.withEnvironment({
|
||||
"foobar": success,
|
||||
"test": "bar"
|
||||
})
|
||||
.shouldResolveTo("${foo${test}}");
|
||||
});
|
||||
|
||||
test("env input with 1 level of nested variables anchored in the middle", () => {
|
||||
resolveVariablesWithInput("${f${test}r}")
|
||||
.withEnvironment({
|
||||
"foobar": success,
|
||||
"test": "ooba"
|
||||
})
|
||||
.shouldResolveTo("${f${test}r}");
|
||||
});
|
||||
|
||||
test("env input with 1 level of nested variable anchored at front", () => {
|
||||
resolveVariablesWithInput("${${test}bar}")
|
||||
.withEnvironment({
|
||||
"foobar": success,
|
||||
"test": "foo"
|
||||
})
|
||||
.shouldResolveTo("${${test}bar}");
|
||||
});
|
||||
|
||||
test("env input with 3 levels of nested variables", () => {
|
||||
resolveVariablesWithInput("${foo${a${b${c}}}}")
|
||||
.withEnvironment({
|
||||
"foobar": success,
|
||||
"a1": "bar",
|
||||
"b2": "1",
|
||||
"c": "2"
|
||||
})
|
||||
.shouldResolveTo("${foo${a${b${c}}}}");
|
||||
});
|
||||
|
||||
test("env input contains env", () => {
|
||||
resolveVariablesWithInput("${envRoot}")
|
||||
.shouldLookupSymbol("envRoot");
|
||||
});
|
||||
|
||||
test("env input contains config", () => {
|
||||
resolveVariablesWithInput("${configRoot}")
|
||||
.shouldLookupSymbol("configRoot");
|
||||
});
|
||||
|
||||
test("env input contains workspaceFolder", () => {
|
||||
resolveVariablesWithInput("${workspaceFolderRoot}")
|
||||
.shouldLookupSymbol("workspaceFolderRoot");
|
||||
});
|
||||
|
||||
test("input contains env.", () => {
|
||||
resolveVariablesWithInput("${env.Root}")
|
||||
.shouldLookupSymbol("Root");
|
||||
});
|
||||
|
||||
test("input contains env:", () => {
|
||||
resolveVariablesWithInput("${env:Root}")
|
||||
.shouldLookupSymbol("Root");
|
||||
});
|
||||
|
||||
interface ResolveTestFlowEnvironment {
|
||||
withEnvironment(additionalEnvironment: {[key: string]: string | string[]}): ResolveTestFlowAssert;
|
||||
shouldLookupSymbol: (key: string) => void;
|
||||
}
|
||||
interface ResolveTestFlowAssert {
|
||||
shouldResolveTo: (x: string) => void;
|
||||
}
|
||||
|
||||
function resolveVariablesWithInput(input: string): ResolveTestFlowEnvironment {
|
||||
return {
|
||||
withEnvironment: (additionalEnvironment: {[key: string]: string | string[]}) => {
|
||||
return inputAndEnvironment(input, additionalEnvironment);
|
||||
},
|
||||
shouldLookupSymbol: (symbol: string) => {
|
||||
const environment: {[key: string]: string | string[]} = {};
|
||||
environment[symbol] = success;
|
||||
return inputAndEnvironment(input, environment)
|
||||
.shouldResolveTo(success);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function inputAndEnvironment(input: string, additionalEnvironment: {[key: string]: string | string[]}): ResolveTestFlowAssert {
|
||||
return {
|
||||
shouldResolveTo: (expected: string) => {
|
||||
const actual: string = resolveVariables(input, additionalEnvironment);
|
||||
const msg: string = `Expected ${expected}. Got ${actual} with input ${input} and environment ${JSON.stringify(additionalEnvironment)}.`;
|
||||
assert.equal(actual, expected, msg);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
@@ -101,6 +101,9 @@ You can change the behavior of GDB or LLDB by setting the following options.
|
||||
|
||||
* #### `miDebuggerPath`
|
||||
The path to the debugger (such as gdb). When only the executable is specified, it will search the operating system's PATH variable for a debugger (GDB on Linux and Windows, LLDB on OS X).
|
||||
|
||||
* #### `miDebuggerArgs`
|
||||
Additional arguments to pass to the debugger (such as gdb).
|
||||
|
||||
* #### `stopAtEntry`
|
||||
If set to true, the debugger should stop at the entry-point of the target (ignored on attach). Default value is `false`.
|
||||
|
||||
Reference in New Issue
Block a user