diff --git a/.vscode/launch.json b/.vscode/launch.json
index 3bc58243b8..b89fcb9578 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -11,7 +11,9 @@
],
"stopOnEntry": false,
"sourceMaps": true,
- "outDir": "${workspaceRoot}/out/src"
+ "outFiles": [
+ "${workspaceRoot}/out/src/**/*.js"
+ ]
},
{
"name": "Launch Tests",
@@ -21,7 +23,9 @@
"args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test" ],
"stopOnEntry": false,
"sourceMaps": true,
- "outDir": "${workspaceRoot}/out/test"
+ "outFiles": [
+ "${workspaceRoot}/out/test/**/*.js"
+ ]
}
]
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 49d30e28db..d2d641d83f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,49 @@
-## Known Issues
+## Known Issues in 1.8.1
* Running and debugging of tests are not supported in .csproj-based .NET Core projects. However, there will still be clickable "run test" and "debug test" indicators above test methods. ([#1100](https://github.com/OmniSharp/omnisharp-vscode/issues/1100))
* When opening a .csproj-based .NET Core project in VS Code, the C# extension will not activate until a C# file is opened in the editor. ([#1150](https://github.com/OmniSharp/omnisharp-vscode/issues/1150))
* There currently is no completion support for package references in csproj files. ([#1156](https://github.com/OmniSharp/omnisharp-vscode/issues/1156))
+## 1.9.0 _(Not Yet Released)_
+
+#### Unit Testing
+
+* Support added for running and debugging unit tests in .csproj-based .NET Core projects. ([#1100](https://github.com/OmniSharp/omnisharp-vscode/issues/1100))
+
+#### Debugger
+
+* **Arch Linux change**: Before, the debugger would automatically use the Ubuntu 16 debugger on Arch. Now we require the debugger to be explicitly set. See https://aka.ms/vscode-csext-arch for more information.
+* Several bug fixes that addressed problems with launch ([#1335](https://github.com/OmniSharp/omnisharp-vscode/issues/1335), [#1336](https://github.com/OmniSharp/omnisharp-vscode/issues/1336))
+* Fixed several pipeTransport issues including introducing a new `quoteArgs` option ([#1318](https://github.com/OmniSharp/omnisharp-vscode/issues/1318)), and fixing attach with Docker ([#1369](https://github.com/OmniSharp/omnisharp-vscode/issues/1369))
+* Fix issue where VS Code would incorrectly display threads as paused ([#1317](https://github.com/OmniSharp/omnisharp-vscode/issues/1317))
+* Added new 'csharp.fallbackDebuggerLinuxRuntimeId' configuration setting to control the version of the debugger used on Linux ([#1361](https://github.com/OmniSharp/omnisharp-vscode/issues/1361)).
+* Added a new `clr` debugging `type` in launch.json to enable debugging for Azure scenarios. That type is limited to Windows Desktop CLR, 64-bit processes, and only supports the [Portable PDB debug format](https://github.com/OmniSharp/omnisharp-vscode/wiki/Portable-PDBs).
+* Added support for the launch.json editor's 'Add Configuration' button ([#1024](https://github.com/OmniSharp/omnisharp-vscode/issues/1024]))
+
+#### Project System
+
+* Properly handle package references with version ranges in .csproj (PR: [omnisharp-roslyn#814](https://github.com/OmniSharp/omnisharp-roslyn/pull/814))
+* Fix regression with MSBuild project system where a project reference and a binary reference could be added for the same assembly, causing ambiguity errors ([omnisharp-roslyn#795](https://github.com/OmniSharp/omnisharp-roslyn/issues/795), PR: [omnisharp-roslyn#815](https://github.com/OmniSharp/omnisharp-roslyn/pull/815))
+* If VS 2017 is on the current machine, use the MSBuild included with VS 2017 for processing projects. ([#1368](https://github.com/OmniSharp/omnisharp-vscode/issues/1368), PR: [omnisharp-roslyn#818]()https://github.com/OmniSharp/omnisharp-roslyn/issues/818)
+* Fixed null reference exception in DotNetProjectSystem when project reference is invalid (PR: [omnisharp-roslyn#797](https://github.com/OmniSharp/omnisharp-roslyn/pull/797))
+
+#### Improved OmniSharp Settings
+* Added support for global omnisharp.json file ([omnisharp-roslyn#717](https://github.com/OmniSharp/omnisharp-roslyn/issues/7170), PR: [omnisharp-roslyn#809](https://github.com/OmniSharp/omnisharp-roslyn/pull/809)) _(Contributed by [@filipw](https://github.com/filipw))_
+
+ This file can appear in one of the following locations:
+ * Windows: `%APPDATA%\OmniSharp\omnisharp.json`
+ * macOS/Linus: `~/.omnisharp/omnisharp.json`
+* Watch local and global omnisharp.json files for changes while OmniSharp is running. Currently, this only works for formatting options. (PR: [omnisharp-roslyn#804](https://github.com/OmniSharp/omnisharp-roslyn/pull/804))_(Contributed by [@filipw](https://github.com/filipw))_
+* New 'omnisharp.waitForDebugger' setting to make it easier to debug the OmniSharp server itself. (PR: [#1370](https://github.com/OmniSharp/omnisharp-roslyn/pull/1370))
+
+#### Other Updates and Fixes
+
+* Improvements made to project.json package completion experience. ([#1338](https://github.com/OmniSharp/omnisharp-vscode/pull/1338))
+* Diagnostics are no longer created for hidden diagnostics, addressing the problem of "Remove Unnecessary Usings" spam in the Problems pane. ([#1231](https://github.com/OmniSharp/omnisharp-vscode/issues/1231))
+* Improved the extension's runtime dependency download logic to skip re-downloading packages that were already successfully downloaded and installed.
+* Assets for building and debugging are now always generated with POSIX style paths. ([#1354](https://github.com/OmniSharp/omnisharp-vscode/pull/1354))
+* Don't offer to generate tasks.json if build task already exists. (PR #1363) _(Contributed by [@eamodio](https://github.com/eamodio))_
+
## 1.8.1 (March 31, 2017)
Fixes debugging on macOS Sierra 10.12.4.
diff --git a/README.md b/README.md
index 117d29a548..62d80c9b7d 100644
--- a/README.md
+++ b/README.md
@@ -34,7 +34,7 @@ See our [change log](https://github.com/OmniSharp/omnisharp-vscode/blob/v1.8.0/C
### Supported Operating Systems for Debugging
-* Currently, the C# debugger supports the following operatings systems:
+* Currently, the C# debugger supports the following operating systems:
* Windows (64-bit only)
* macOS
@@ -64,7 +64,7 @@ To **run and develop** do the following:
* Run `npm i`
* Run `npm run compile`
* Open in Visual Studio Code (`code .`)
-* *Optional:* run `tsc -w`, make code changes (on Windows, try `start node ".\node_modules\typescript\bin\tsc -w"`)
+* *Optional:* run `npm run watch`, make code changes
* Press F5 to debug
To **test** do the following: `npm run test` or F5 in VS Code with the "Launch Tests" debug configuration.
diff --git a/debugger-launchjson.md b/debugger-launchjson.md
new file mode 100644
index 0000000000..5cf6658479
--- /dev/null
+++ b/debugger-launchjson.md
@@ -0,0 +1,112 @@
+# Configurating launch.json for C# debugging
+The launch.json file is used to configure the debugger in Visual Studio Code.
+
+Visual Studio Code generates a launch.json with almost all of the required information.
+If your workspace has only one launchable project, the C# extension will offer to automatically generate this file.
+If you missed this prompt, you can force the generation by executing the command `.NET: Generate Assets for Build and Debug` from the VS Code command palette.
+The generated file contains two sections. One that configures debugging for launch and a second that configures debugging for attach.
+
+If you have more than one launchable project, then you will need to modify your launch.json file by hand.
+Visual Studio Code will still generate a basic template, but you will need to fill in the 'program' field to point at the executable dll that you would like to debug.
+
+
+# Configurating VS Code's debugging behavior
+
+## PreLaunchTask
+The `preLaunchTask` field runs the associated taskName in tasks.json before debugging your program. You can get the default build prelaunch task by executing the command `Tasks: Configure Tasks Runner` from the VS Code command palette.
+
+This will create a task that runs `dotnet build`. You can read more about tasks at [https://code.visualstudio.com/docs/editor/tasks](https://code.visualstudio.com/docs/editor/tasks).
+
+## Program
+The program field is set to the path to the application dll or .NET Core host executable to launch.
+
+Example: "${workspaceRoot}/bin/Debug/\/\" where:
+
+* \: (example: 'netstandard1.5') This is the framework that the app is being built for. It is set in the project.json file.
+* \: (example: 'MyApp') The name of the project being debugged.",
+
+## Cwd
+The working directory of the target process.
+
+## Args
+These are the arguments that will be passed to your program.
+
+## Stop at Entry
+If you need to stop at the entry point of the target, you can optionally set `stopAtEntry` to be "true".
+
+## Launch Browser
+The launch browser field can be optionally added if you need to launch with a web browser.
+If there are web server dependencies in your project.json, the auto generated launch file will add launch
+browser for you. It will open with the default program to handle URLs.
+
+## Environment variables
+Environment variables may be passed to your program using this schema:
+
+ "env": {
+ "myVariableName":"theValueGoesHere"
+ }
+
+## Console (terminal) window
+By default, processes are launched with their console output (stdout/stderr) going to the VS Code Debugger Console. This is useful for executables that take their input from the network, files, etc. But this does NOT work for applications that want to read from the console (ex: `Console.ReadLine`). For these applications, use a setting such as the following:
+
+ "console": "integratedTerminal"
+
+When this is set to `integratedTerminal` the target process will run inside [VS Code's integrated terminal](https://code.visualstudio.com/docs/editor/integrated-terminal). Click the 'Terminal' tab in the tab group beneath the editor to interact with your application.
+
+When this is set to `externalTerminal` the target process will run in a separate terminal.
+
+## Source File Map
+You can optionally configure a file by file mapping by providing map following this schema:
+
+ "sourceFileMap": {
+ "C:\foo":"/home/me/foo"
+ }
+
+## Symbol Path
+You can optionally provide paths to symbols following this schema:
+
+ "symbolPath": [ "/Volumes/symbols" ]
+
+## Just My Code
+You can optionally disable `justMyCode` by setting it to "false". You should disable Just My Code when you are trying to debug into a library that you pulled down which doesn't have symbols or is optimized.
+
+ "justMyCode":false*
+
+Just My Code is a set of features that makes it easier to focus on debugging your code by hiding some of the details of optimized libraries that you might be using, like the .NET Framework itself. The most important sub parts of this feature are --
+
+* User-unhandled exceptions: automatically stop the debugger just before exceptions are about to be caught by the framework
+* Just My Code stepping: when stepping, if framework code calls back to user code, automatically stop.
+
+## Require Exact Source
+The debugger requires the pdb and source code to be exactly the same. To change this and disable the sources to be the same add:
+
+ "requireExactSource": false
+
+## Stepping into properties and operators
+The debugger steps over properties and operators in managed code by default. In most cases, this provides a better debugging experience. To change this and enable stepping into properties or operators add:
+
+ "enableStepFiltering": false
+
+## Logging
+You can optionally enable or disable messages that should be logged to the output window. The flags in the logging field are: 'exceptions', 'moduleLoad', 'programOutput', 'engineLogging', and 'browserStdOut'.
+
+## PipeTransport
+If you need to have the debugger to connect to a remote computer using another executable to relay standard input and output bewteen VS Code and the .NET Core debugger backend (vsdbg),
+then add the pipeTransport field folloing this schema:
+
+ "pipeTransport": {
+ "pipeProgram": "ssh",
+ "pipeArgs": [ "-T", "ExampleAccount@ExampleTargetComputer" ],
+ "debuggerPath": "~/vsdbg/vsdbg",
+ "pipeCwd": "${workspaceRoot}",
+ "quoteArgs": true
+ }
+
+More information about pipe transport can be found [here](https://github.com/OmniSharp/omnisharp-vscode/wiki/Attaching-to-remote-processes).
+
+You can find information on configuring pipe transport for [Windows Subsystem for Linux](https://msdn.microsoft.com/en-us/commandline/wsl/about) (WSL) [here](https://github.com/OmniSharp/omnisharp-vscode/wiki/Windows-Subsystem-for-Linux).
+
+## Operating System Specific Configurations
+If there specific commands that need to be changed per operating system, you can use the fields: 'windows', 'osx', or 'linux'.
+You can replace any of the fields mentioned above for the specific operating system.
+
diff --git a/debugger.md b/debugger.md
index 5841129fde..4ee4c52edb 100644
--- a/debugger.md
+++ b/debugger.md
@@ -1,10 +1,10 @@
-#Instructions for setting up the .NET Core debugger
+# Instructions for setting up the .NET Core debugger
This page gives you detailed instructions on how to debug code running under .NET Core in VS Code.
-####Your Feedback
+#### Your Feedback
File bugs and feature requests [here](https://github.com/OmniSharp/omnisharp-vscode/issues) and [join our insiders group](http://landinghub.visualstudio.com/dotnetcoreinsiders) to help us build great tooling for .NET Core.
-####Requirements
+#### Requirements
* Requires .NET Core 1.0 (rc2 are earlier releases are not supported)
* X64 only
* Supported operating systems:
@@ -12,7 +12,7 @@ File bugs and feature requests [here](https://github.com/OmniSharp/omnisharp-vsc
* Linux: Red Hat Enterprise Linux 7.2+, Ubuntu 14.04 LTS, Ubuntu 16.04 LTS, Debian 8.2+, Linux Mint 17+, CentOS 7.1+, Oracle Linux 7.1+, Fedora 23, openSUSE 13.2
* Windows: 7+
-###First Time setup
+### First Time setup
##### 1: Get Visual Studio Code
Install Visual Studio Code (VSC). Pick the latest VSC version from here: https://code.visualstudio.com Make sure it is at least 1.5.
@@ -35,9 +35,8 @@ If you have previously installed the C# extension, make sure that you have a rec
The first time that C# code is opened in VS Code, the extension will download the platform-specific files needed for debugging and editing. Debugging and editor features will not work until these steps finish.
-###Once for each project
+### Once for each project
The following steps have to executed for every project.
-
##### 1: Get a project
You can start from scratch by creating an empty project with `dotnet new`. Begin by opening the terminal in Visual Studio Code (`View->Integrated Terminal`) and type these commands:
@@ -80,56 +79,14 @@ If your code has multiple projects or you would rather generate these files by h
##### 4: Start debugging
Your project is now all set. Set a breakpoint or two where you want to stop, click the debugger play button (or press F5) and you are off.
-###Debugging Code compiled on another computer
+### Debugging Code compiled on another computer
If your code was built on a different computer from where you would like to run in there are a few things to keep in mind --
-* **Source Maps**: Unless your local source code is at exactly the same path as where the code was originally built you will need to add a [sourceFileMap](#source-file-map) to launch.json.
+* **Source Maps**: Unless your local source code is at exactly the same path as where the code was originally built you will need to add a [sourceFileMap](https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#source-file-map) to launch.json.
* **Portable PDBs**: If the code was built on Windows, it might have been built using Windows PDBs instead of portable PDBs, but the C# extension only supports portable PDBs. See the [portable PDB documentation](https://github.com/OmniSharp/omnisharp-vscode/wiki/Portable-PDBs#how-to-generate-portable-pdbs) for more information.
-* **Debug vs. Release**: It is much easier to debug code which has been compiled in the `Debug` configuration. So unless the issue you are looking at only reproduces with optimizations, it is much better to use Debug bits. If you do need to debug optimized code, you will need to disable [justMyCode](#just-my-code) in launch.json.
-
-####More things to configure In launch.json
-#####Just My Code
-You can optionally disable justMyCode by setting it to "false". You should disable Just My Code when you are trying to debug into a library that you pulled down which doesn't have symbols or is optimized.
-
- "justMyCode":false*
-
-Just My Code is a set of features that makes it easier to focus on debugging your code by hiding some of the details of optimized libraries that you might be using, like the .NET Framework itself. The most important sub parts of this feature are --
-
-* User-unhandled exceptions: automatically stop the debugger just before exceptions are about to be caught by the framework
-* Just My Code stepping: when stepping, if framework code calls back to user code, automatically stop.
-
-#####Source File Map
-You can optionally configure a file by file mapping by providing map following this schema:
-
- "sourceFileMap": {
- "C:\foo":"/home/me/foo"
- }
-
-#####Symbol Path
-You can optionally provide paths to symbols following this schema:
-
- "symbolPath": [ "/Volumes/symbols" ]
-
-#####Environment variables
-Environment variables may be passed to your program using this schema:
-
- "env": {
- "myVariableName":"theValueGoesHere"
- }
-
-#####Console (terminal) window
-By default, processes are launched with their console output (stdout/stderr) going to the VS Code Debugger Console. This is useful for executables that take their input from the network, files, etc. But this does NOT work for applications that want to read from the console (ex: `Console.ReadLine`). For these applications, use a setting such as the following:
-
- "console": "integratedTerminal"
-
-When this is set to `integratedTerminal` the target process will run inside [VS Code's integrated terminal](https://code.visualstudio.com/docs/editor/integrated-terminal). Click the 'Terminal' tab in the tab group beneath the editor to interact with your application.
-
-When this is set to `externalTerminal` the target process will run in a separate terminal.
-
-##### Stepping into properties and operators
-The debugger steps over properties and operators in managed code by default. In most cases, this provides a better debugging experience. To change this and enable stepping into properties or operators add:
+* **Debug vs. Release**: It is much easier to debug code which has been compiled in the `Debug` configuration. So unless the issue you are looking at only reproduces with optimizations, it is much better to use Debug bits. If you do need to debug optimized code, you will need to disable [justMyCode](https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#just-my-code) in launch.json.
- "enableStepFiltering": false
+#### [Configurating launch.json for C# Debugging](debugger-launchjson.md)
#### Attach Support
The C# debugger supports attaching to processes. To do this, switch to the Debug tab, and open the configuration drop down.
diff --git a/package.json b/package.json
index 7a892545c7..54f5b6d03d 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "csharp",
"publisher": "ms-vscode",
- "version": "1.8.1",
+ "version": "1.9.0-beta3",
"description": "C# for Visual Studio Code (powered by OmniSharp).",
"displayName": "C#",
"author": "Microsoft Corporation",
@@ -79,7 +79,8 @@
"binaries": [
"./mono.linux-x86",
"./run"
- ]
+ ],
+ "installTestPath": "./bin/mono.linux-x86"
},
{
"description": "Mono Runtime (Linux / x64)",
@@ -94,7 +95,8 @@
"binaries": [
"./mono.linux-x86_64",
"./run"
- ]
+ ],
+ "installTestPath": "./bin/mono.linux-x86_64"
},
{
"description": "Mono Runtime (macOS)",
@@ -106,7 +108,8 @@
"binaries": [
"./mono.osx",
"./run"
- ]
+ ],
+ "installTestPath": "./bin/mono.osx"
},
{
"description": "Mono Framework Assemblies",
@@ -115,52 +118,57 @@
"platforms": [
"darwin",
"linux"
- ]
+ ],
+ "installTestPath": "./bin/framework/mscorlib.dll"
},
{
"description": "OmniSharp (.NET 4.6 / x86)",
- "url": "https://omnisharpdownload.blob.core.windows.net/ext/omnisharp-win-x86-1.11.0.zip",
+ "url": "https://omnisharpdownload.blob.core.windows.net/ext/omnisharp-win-x86-1.14.0.2.zip",
"installPath": "./bin/omnisharp",
"platforms": [
"win32"
],
"architectures": [
"x86"
- ]
+ ],
+ "installTestPath": "./bin/omnisharp/OmniSharp.exe"
},
{
"description": "OmniSharp (.NET 4.6 / x64)",
- "url": "https://omnisharpdownload.blob.core.windows.net/ext/omnisharp-win-x64-1.11.0.zip",
+ "url": "https://omnisharpdownload.blob.core.windows.net/ext/omnisharp-win-x64-1.14.0.2.zip",
"installPath": "./bin/omnisharp",
"platforms": [
"win32"
],
"architectures": [
"x86_64"
- ]
+ ],
+ "installTestPath": "./bin/omnisharp/OmniSharp.exe"
},
{
"description": "OmniSharp (Mono 4.6)",
- "url": "https://omnisharpdownload.blob.core.windows.net/ext/omnisharp-mono-1.11.0.zip",
+ "url": "https://omnisharpdownload.blob.core.windows.net/ext/omnisharp-mono-1.14.0.2.zip",
"installPath": "./bin/omnisharp",
"platforms": [
"darwin",
"linux"
- ]
+ ],
+ "installTestPath": "./bin/omnisharp/OmniSharp.exe"
},
{
"description": ".NET Core Debugger (Windows / x64)",
- "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-1/coreclr-debug-win7-x64.zip",
- "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-1/coreclr-debug-win7-x64.zip",
+ "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-2/coreclr-debug-win7-x64.zip",
+ "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-2/coreclr-debug-win7-x64.zip",
"installPath": ".debugger",
"runtimeIds": [
"win7-x64"
- ]
+ ],
+ "installTestPath": "./.debugger/vsdbg-ui.exe"
},
{
"description": ".NET Core Debugger (macOS / x64)",
- "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-1/coreclr-debug-osx.10.11-x64.zip",
- "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-1/coreclr-debug-osx.10.11-x64.zip",
+ "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-2/coreclr-debug-osx.10.11-x64.zip",
+ "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-2/coreclr-debug-osx.10.11-x64.zip",
"installPath": ".debugger",
"runtimeIds": [
"osx.10.11-x64"
@@ -168,12 +176,13 @@
"binaries": [
"./vsdbg-ui",
"./vsdbg"
- ]
+ ],
+ "installTestPath": "./.debugger/vsdbg-ui"
},
{
"description": ".NET Core Debugger (CentOS / x64)",
- "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-1/coreclr-debug-centos.7-x64.zip",
- "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-1/coreclr-debug-centos.7-x64.zip",
+ "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-2/coreclr-debug-centos.7-x64.zip",
+ "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-2/coreclr-debug-centos.7-x64.zip",
"installPath": ".debugger",
"runtimeIds": [
"centos.7-x64"
@@ -181,12 +190,13 @@
"binaries": [
"./vsdbg-ui",
"./vsdbg"
- ]
+ ],
+ "installTestPath": "./.debugger/vsdbg-ui"
},
{
"description": ".NET Core Debugger (Debian / x64)",
- "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-1/coreclr-debug-debian.8-x64.zip",
- "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-1/coreclr-debug-debian.8-x64.zip",
+ "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-2/coreclr-debug-debian.8-x64.zip",
+ "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-2/coreclr-debug-debian.8-x64.zip",
"installPath": ".debugger",
"runtimeIds": [
"debian.8-x64"
@@ -194,12 +204,13 @@
"binaries": [
"./vsdbg-ui",
"./vsdbg"
- ]
+ ],
+ "installTestPath": "./.debugger/vsdbg-ui"
},
{
"description": ".NET Core Debugger (Fedora 23 / x64)",
- "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-1/coreclr-debug-fedora.23-x64.zip",
- "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-1/coreclr-debug-fedora.23-x64.zip",
+ "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-2/coreclr-debug-fedora.23-x64.zip",
+ "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-2/coreclr-debug-fedora.23-x64.zip",
"installPath": ".debugger",
"runtimeIds": [
"fedora.23-x64"
@@ -207,12 +218,13 @@
"binaries": [
"./vsdbg-ui",
"./vsdbg"
- ]
+ ],
+ "installTestPath": "./.debugger/vsdbg-ui"
},
{
"description": ".NET Core Debugger (Fedora 24 / x64)",
- "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-1/coreclr-debug-fedora.24-x64.zip",
- "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-1/coreclr-debug-fedora.24-x64.zip",
+ "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-2/coreclr-debug-fedora.24-x64.zip",
+ "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-2/coreclr-debug-fedora.24-x64.zip",
"installPath": ".debugger",
"runtimeIds": [
"fedora.24-x64"
@@ -220,12 +232,13 @@
"binaries": [
"./vsdbg-ui",
"./vsdbg"
- ]
+ ],
+ "installTestPath": "./.debugger/vsdbg-ui"
},
{
"description": ".NET Core Debugger (OpenSUSE 13 / x64)",
- "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-1/coreclr-debug-opensuse.13.2-x64.zip",
- "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-1/coreclr-debug-opensuse.13.2-x64.zip",
+ "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-2/coreclr-debug-opensuse.13.2-x64.zip",
+ "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-2/coreclr-debug-opensuse.13.2-x64.zip",
"installPath": ".debugger",
"runtimeIds": [
"opensuse.13.2-x64"
@@ -233,12 +246,13 @@
"binaries": [
"./vsdbg-ui",
"./vsdbg"
- ]
+ ],
+ "installTestPath": "./.debugger/vsdbg-ui"
},
{
"description": ".NET Core Debugger (OpenSUSE 42 / x64)",
- "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-1/coreclr-debug-opensuse.42.1-x64.zip",
- "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-1/coreclr-debug-opensuse.42.1-x64.zip",
+ "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-2/coreclr-debug-opensuse.42.1-x64.zip",
+ "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-2/coreclr-debug-opensuse.42.1-x64.zip",
"installPath": ".debugger",
"runtimeIds": [
"opensuse.42.1-x64"
@@ -246,12 +260,13 @@
"binaries": [
"./vsdbg-ui",
"./vsdbg"
- ]
+ ],
+ "installTestPath": "./.debugger/vsdbg-ui"
},
{
"description": ".NET Core Debugger (RHEL / x64)",
- "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-1/coreclr-debug-rhel.7.2-x64.zip",
- "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-1/coreclr-debug-rhel.7.2-x64.zip",
+ "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-2/coreclr-debug-rhel.7.2-x64.zip",
+ "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-2/coreclr-debug-rhel.7.2-x64.zip",
"installPath": ".debugger",
"runtimeIds": [
"rhel.7-x64"
@@ -259,12 +274,13 @@
"binaries": [
"./vsdbg-ui",
"./vsdbg"
- ]
+ ],
+ "installTestPath": "./.debugger/vsdbg-ui"
},
{
"description": ".NET Core Debugger (Ubuntu 14.04 / x64)",
- "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-1/coreclr-debug-ubuntu.14.04-x64.zip",
- "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-1/coreclr-debug-ubuntu.14.04-x64.zip",
+ "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-2/coreclr-debug-ubuntu.14.04-x64.zip",
+ "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-2/coreclr-debug-ubuntu.14.04-x64.zip",
"installPath": ".debugger",
"runtimeIds": [
"ubuntu.14.04-x64"
@@ -272,12 +288,13 @@
"binaries": [
"./vsdbg-ui",
"./vsdbg"
- ]
+ ],
+ "installTestPath": "./.debugger/vsdbg-ui"
},
{
"description": ".NET Core Debugger (Ubuntu 16.04 / x64)",
- "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-1/coreclr-debug-ubuntu.16.04-x64.zip",
- "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-1/coreclr-debug-ubuntu.16.04-x64.zip",
+ "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-2/coreclr-debug-ubuntu.16.04-x64.zip",
+ "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-2/coreclr-debug-ubuntu.16.04-x64.zip",
"installPath": ".debugger",
"runtimeIds": [
"ubuntu.16.04-x64"
@@ -285,12 +302,13 @@
"binaries": [
"./vsdbg-ui",
"./vsdbg"
- ]
+ ],
+ "installTestPath": "./.debugger/vsdbg-ui"
},
{
"description": ".NET Core Debugger (Ubuntu 16.10 / x64)",
- "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-1/coreclr-debug-ubuntu.16.10-x64.zip",
- "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-1/coreclr-debug-ubuntu.16.10-x64.zip",
+ "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-2/coreclr-debug-ubuntu.16.10-x64.zip",
+ "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-2/coreclr-debug-ubuntu.16.10-x64.zip",
"installPath": ".debugger",
"runtimeIds": [
"ubuntu.16.10-x64"
@@ -298,7 +316,8 @@
"binaries": [
"./vsdbg-ui",
"./vsdbg"
- ]
+ ],
+ "installTestPath": "./.debugger/vsdbg-ui"
}
],
"engines": {
@@ -324,6 +343,22 @@
"default": false,
"description": "Suppress the warning that the .NET CLI is not on the path."
},
+ "csharp.fallbackDebuggerLinuxRuntimeId": {
+ "type": "string",
+ "enum": [
+ "centos.7-x64",
+ "debian.8-x64",
+ "fedora.23-x64",
+ "fedora.24-x64",
+ "opensuse.13.2-x64",
+ "opensuse.42.1-x64",
+ "rhel.7-x64",
+ "ubuntu.14.04-x64",
+ "ubuntu.16.04-x64",
+ "ubuntu.16.10-x64"
+ ],
+ "description": "If the current Linux distribution is not recognized, this option can be used to tell the debugger what version can be used. After changing this option, close VS Code, remove the debugger folder (~/.vscode/extensions/ms-vscode.csharp-/.debugger) if it has already downloaded, and restart VS Code."
+ },
"csharp.suppressDotnetRestoreNotification": {
"type": "boolean",
"default": false,
@@ -342,6 +377,11 @@
"default": false,
"description": "Launch OmniSharp with Mono."
},
+ "omnisharp.waitForDebugger": {
+ "type": "boolean",
+ "default": false,
+ "description": "Pass the --debug flag when launching the OmniSharp server to allow a debugger to be attached."
+ },
"omnisharp.loggingLevel": {
"type": "string",
"default": "information",
@@ -727,6 +767,11 @@
"description": "Environment variables passed to the pipe program.",
"default": {}
},
+ "quoteArgs": {
+ "type": "boolean",
+ "description": "Should arguments that contain characters that need to be quoted (example: spaces) be quoted? Defaults to 'true'. If set to false, the debugger command will no longer be automatically quoted.",
+ "default": true
+ },
"windows": {
"description": "Windows-specific pipe launch configuration options",
"default": {
@@ -754,6 +799,11 @@
},
"default": []
},
+ "quoteArgs": {
+ "type": "boolean",
+ "description": "Should arguments that contain characters that need to be quoted (example: spaces) be quoted? Defaults to 'true'. If set to false, the debugger command will no longer be automatically quoted.",
+ "default": true
+ },
"pipeEnv": {
"type": "object",
"additionalProperties": {
@@ -791,6 +841,11 @@
},
"default": []
},
+ "quoteArgs": {
+ "type": "boolean",
+ "description": "Should arguments that contain characters that need to be quoted (example: spaces) be quoted? Defaults to 'true'. If set to false, the debugger command will no longer be automatically quoted.",
+ "default": true
+ },
"pipeEnv": {
"type": "object",
"additionalProperties": {
@@ -828,6 +883,11 @@
},
"default": []
},
+ "quoteArgs": {
+ "type": "boolean",
+ "description": "Should arguments that contain characters that need to be quoted (example: spaces) be quoted? Defaults to 'true'. If set to false, the debugger command will no longer be automatically quoted.",
+ "default": true
+ },
"pipeEnv": {
"type": "object",
"additionalProperties": {
@@ -975,6 +1035,11 @@
"description": "Environment variables passed to the pipe program.",
"default": {}
},
+ "quoteArgs": {
+ "type": "boolean",
+ "description": "Should arguments that contain characters that need to be quoted (example: spaces) be quoted? Defaults to 'true'. If set to false, the debugger command will no longer be automatically quoted.",
+ "default": true
+ },
"windows": {
"description": "Windows-specific pipe launch configuration options",
"default": {
@@ -1002,6 +1067,11 @@
},
"default": []
},
+ "quoteArgs": {
+ "type": "boolean",
+ "description": "Should arguments that contain characters that need to be quoted (example: spaces) be quoted? Defaults to 'true'. If set to false, the debugger command will no longer be automatically quoted.",
+ "default": true
+ },
"pipeEnv": {
"type": "object",
"additionalProperties": {
@@ -1039,6 +1109,11 @@
},
"default": []
},
+ "quoteArgs": {
+ "type": "boolean",
+ "description": "Should arguments that contain characters that need to be quoted (example: spaces) be quoted? Defaults to 'true'. If set to false, the debugger command will no longer be automatically quoted.",
+ "default": true
+ },
"pipeEnv": {
"type": "object",
"additionalProperties": {
@@ -1076,6 +1151,11 @@
},
"default": []
},
+ "quoteArgs": {
+ "type": "boolean",
+ "description": "Should arguments that contain characters that need to be quoted (example: spaces) be quoted? Defaults to 'true'. If set to false, the debugger command will no longer be automatically quoted.",
+ "default": true
+ },
"pipeEnv": {
"type": "object",
"additionalProperties": {
@@ -1091,6 +1171,104 @@
}
}
},
+ "configurationSnippets": [
+ {
+ "label": ".NET: Launch .NET Core Console App",
+ "description": "Launch a .NET Core Console App with a debugger.",
+ "body": {
+ "name": ".NET Core Launch (console)",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ "program": "^\"\\${workspaceRoot}/bin/Debug/${1:}/${2:}\"",
+ "args": [],
+ "cwd": "^\"\\${workspaceRoot}\"",
+ "stopAtEntry": false,
+ "console": "internalConsole"
+ }
+ },
+ {
+ "label": ".NET: Attach to local .NET Core Console App",
+ "description": "Attach a debugger to a .NET Core Console App.",
+ "body": {
+ "name": ".NET Core Attach",
+ "type": "coreclr",
+ "request": "attach",
+ "processId": "^\"\\${command:pickProcess}\""
+ }
+ },
+ {
+ "label": ".NET: Launch a local .NET Core Web App",
+ "description": "Launch a .NET Core Web App with both a browser and a debugger.",
+ "body": {
+ "name": ".NET Core Launch (web)",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ "program": "^\"\\${workspaceRoot}/bin/Debug/${1:}/${2:}\"",
+ "args": [],
+ "cwd": "^\"\\${workspaceRoot}\"",
+ "stopAtEntry": false,
+ "launchBrowser": {
+ "enabled": true,
+ "args": "^\"\\${auto-detect-url}\"",
+ "windows": {
+ "command": "cmd.exe",
+ "args": "^\"/C start \\${auto-detect-url}\""
+ },
+ "osx": {
+ "command": "open"
+ },
+ "linux": {
+ "command": "xdg-open"
+ }
+ },
+ "env": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "sourceFileMap": {
+ "/Views": "^\"\\${workspaceRoot}/Views\""
+ }
+ }
+ },
+ {
+ "label": ".NET: Launch a remote .NET Core Console App",
+ "description": "Launch a .NET Core Console App on a remote machine.",
+ "body": {
+ "name": ".NET Core Launch (console)",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ "program": "^\"\\${workspaceRoot}/bin/Debug/${1:}/${2:}\"",
+ "args": [],
+ "cwd": "^\"\\${workspaceRoot}\"",
+ "stopAtEntry": false,
+ "console": "internalConsole",
+ "pipeTransport": {
+ "pipeCwd": "^\"\\${workspaceRoot}\"",
+ "pipeProgram": "^\"${3:enter the fully qualified path for the pipe program name, for example '/usr/bin/ssh'}\"",
+ "pipeArgs": [],
+ "debuggerPath": "^\"${4:enter the path for the debugger on the target machine, for example ~/vsdbg/vsdbg}\""
+ }
+ }
+ },
+ {
+ "label": ".NET: Attach to remote .NET Core Console App",
+ "description": "Attach a debugger to a .NET Core Console App on a remote machine.",
+ "body": {
+ "name": ".NET Core Attach",
+ "type": "coreclr",
+ "request": "attach",
+ "processId": "^\"\\${command:pickRemoteProcess}\"",
+ "pipeTransport": {
+ "pipeCwd": "^\"\\${workspaceRoot}\"",
+ "pipeProgram": "^\"${1:enter the fully qualified path for the pipe program name, for example '/usr/bin/ssh'}\"",
+ "pipeArgs": [],
+ "debuggerPath": "^\"${2:enter the path for the debugger on the target machine, for example ~/vsdbg/vsdbg}\""
+ }
+ }
+ }
+ ],
"initialConfigurations": [
{
"name": ".NET Core Launch (console)",
@@ -1140,7 +1318,686 @@
"processId": "${command:pickProcess}"
}
]
+ },
+ {
+ "type": "clr",
+ "label": ".NET",
+ "enableBreakpointsFor": {
+ "languageIds": [
+ "csharp",
+ "razor"
+ ]
+ },
+ "runtime": "node",
+ "runtimeArgs": [],
+ "variables": {
+ "pickProcess": "csharp.listProcess",
+ "pickRemoteProcess": "csharp.listRemoteProcess"
+ },
+ "program": "./out/src/coreclr-debug/proxy.js",
+ "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217",
+ "configurationAttributes": {
+ "launch": {
+ "type": "object",
+ "required": [
+ "program",
+ "cwd"
+ ],
+ "properties": {
+ "program": {
+ "type": "string",
+ "description": "Path to the application dll or .NET Core host executable to launch. Example: '${workspaceRoot}/bin/Debug//' where:\n: (example: 'netstandard1.5') This is the name of the framework that the app is being built for. It is set in the project.json file.\n: (example: 'MyApp') The name of the project being debugged.",
+ "default": "${workspaceRoot}/bin/Debug//"
+ },
+ "cwd": {
+ "type": "string",
+ "description": "Path to the working directory of the program being debugged. Default is the current workspace.",
+ "default": "${workspaceRoot}"
+ },
+ "args": {
+ "type": "array",
+ "description": "Command line arguments passed to the program.",
+ "items": {
+ "type": "string"
+ },
+ "default": []
+ },
+ "stopAtEntry": {
+ "type": "boolean",
+ "description": "If true, the debugger should stop at the entry point of the target.",
+ "default": false
+ },
+ "launchBrowser": {
+ "description": "Describes options to launch a web browser as part of launch",
+ "default": {
+ "enabled": true,
+ "args": "${auto-detect-url}",
+ "windows": {
+ "command": "cmd.exe",
+ "args": "/C start ${auto-detect-url}"
+ },
+ "osx": {
+ "command": "open"
+ },
+ "linux": {
+ "command": "xdg-open"
+ }
+ },
+ "type": "object",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "description": "Whether web browser launch is enabled",
+ "default": true
+ },
+ "args": {
+ "type": "string",
+ "description": "The arguments to pass to the command to open the browser. Use ${auto-detect-url} to automatically use the address the server is listening to",
+ "default": "${auto-detect-url}"
+ },
+ "osx": {
+ "description": "OSX-specific web launch configuration options",
+ "default": {
+ "command": "open"
+ },
+ "type": "object",
+ "properties": {
+ "command": {
+ "type": "string",
+ "description": "The command to execute for launching the web browser",
+ "default": "open"
+ },
+ "args": {
+ "type": "string",
+ "description": "The arguments to pass to the command to open the browser. Use ${auto-detect-url} to automatically use the address the server is listening to",
+ "default": "${auto-detect-url}"
+ }
+ }
+ },
+ "linux": {
+ "description": "Linux-specific web launch configuration options",
+ "default": {
+ "command": "xdg-open"
+ },
+ "type": "object",
+ "properties": {
+ "command": {
+ "type": "string",
+ "description": "The command to execute for launching the web browser",
+ "default": "xdg-open"
+ },
+ "args": {
+ "type": "string",
+ "description": "The arguments to pass to the command to open the browser. Use ${auto-detect-url} to automatically use the address the server is listening to",
+ "default": "${auto-detect-url}"
+ }
+ }
+ },
+ "windows": {
+ "description": "Windows-specific web launch configuration options",
+ "default": {
+ "command": "cmd.exe",
+ "args": "/C start ${auto-detect-url}"
+ },
+ "type": "object",
+ "properties": {
+ "command": {
+ "type": "string",
+ "description": "The command to execute for launching the web browser",
+ "default": "cmd.exe"
+ },
+ "args": {
+ "type": "string",
+ "description": "The arguments to pass to the command to open the browser. Use ${auto-detect-url} to automatically use the address the server is listening to",
+ "default": "${auto-detect-url}"
+ }
+ }
+ }
+ }
+ },
+ "env": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ },
+ "description": "Environment variables passed to the program.",
+ "default": {}
+ },
+ "console": {
+ "type": "string",
+ "enum": [
+ "internalConsole",
+ "integratedTerminal",
+ "externalTerminal"
+ ],
+ "enumDescriptions": [
+ "Output to the VS Code Debug Console. This doesn't support reading console input (ex:Console.ReadLine)",
+ "VS Code's integrated terminal",
+ "External terminal that can be configured via user settings"
+ ],
+ "description": "Where to launch the debug target.",
+ "default": "internalConsole"
+ },
+ "externalConsole": {
+ "type": "boolean",
+ "description": "Attribute 'externalConsole' is deprecated, use 'console' instead.",
+ "default": false
+ },
+ "sourceFileMap": {
+ "type": "object",
+ "description": "Optional source file mappings passed to the debug engine. Example: '{ \"C:\\foo\":\"/home/user/foo\" }'",
+ "additionalProperties": {
+ "type": "string"
+ },
+ "default": {
+ "": ""
+ }
+ },
+ "justMyCode": {
+ "type": "boolean",
+ "description": "Optional flag to only show user code.",
+ "default": true
+ },
+ "symbolPath": {
+ "type": "array",
+ "description": "Array of directories to use to search for .pdb files. These directories will be searched in addition to the default locations -- next to the module and the path where the pdb was originally dropped to. Example: '[ \"/Volumes/symbols\" ]",
+ "items": {
+ "type": "string"
+ },
+ "default": []
+ },
+ "requireExactSource": {
+ "type": "boolean",
+ "description": "Optional flag to require current source code to match the pdb.",
+ "default": true
+ },
+ "enableStepFiltering": {
+ "type": "boolean",
+ "description": "Optional flag to enable stepping over Properties and Operators.",
+ "default": true
+ },
+ "logging": {
+ "description": "Optional flags to determine what types of messages should be logged to the output window.",
+ "type": "object",
+ "required": [],
+ "default": {},
+ "properties": {
+ "exceptions": {
+ "type": "boolean",
+ "description": "Optional flag to determine whether exception messages should be logged to the output window.",
+ "default": true
+ },
+ "moduleLoad": {
+ "type": "boolean",
+ "description": "Optional flag to determine whether module load events should be logged to the output window.",
+ "default": true
+ },
+ "programOutput": {
+ "type": "boolean",
+ "description": "Optional flag to determine whether program output should be logged to the output window when not using an external console.",
+ "default": true
+ },
+ "engineLogging": {
+ "type": "boolean",
+ "description": "Optional flag to determine whether diagnostic engine logs should be logged to the output window.",
+ "default": false
+ },
+ "browserStdOut": {
+ "type": "boolean",
+ "description": "Optional flag to determine if stdout text from the launching the web browser should be logged to the output window.",
+ "default": true
+ }
+ }
+ },
+ "pipeTransport": {
+ "description": "When present, this tells the debugger to connect to a remote computer using another executable as a pipe that will relay standard input/output between VS Code and the .NET Core debugger backend executable (vsdbg).",
+ "type": "object",
+ "required": [
+ "debuggerPath"
+ ],
+ "default": {
+ "pipeCwd": "${workspaceRoot}",
+ "pipeProgram": "enter the fully qualified path for the pipe program name, for example '/usr/bin/ssh'",
+ "pipeArgs": [],
+ "debuggerPath": "enter the path for the debugger on the target machine, for example ~/vsdbg/vsdbg"
+ },
+ "properties": {
+ "pipeCwd": {
+ "type": "string",
+ "description": "The fully qualified path to the working directory for the pipe program.",
+ "default": "${workspaceRoot}"
+ },
+ "pipeProgram": {
+ "type": "string",
+ "description": "The fully qualified pipe command to execute.",
+ "default": "enter the fully qualified path for the pipe program name, for example '/usr/bin/ssh'"
+ },
+ "pipeArgs": {
+ "type": "array",
+ "description": "Command line arguments passed to the pipe program. Token ${debuggerCommand} in pipeArgs will get replaced by the full debugger command, this token can be specified inline with other arguments. If ${debuggerCommand} isn’t used in any argument, the full debugger command will be instead be added to the end of the argument list.",
+ "items": {
+ "type": "string"
+ },
+ "default": []
+ },
+ "debuggerPath": {
+ "type": "string",
+ "description": "The full path to the debugger on the target machine.",
+ "default": "enter the path for the debugger on the target machine, for example ~/vsdbg/vsdbg"
+ },
+ "pipeEnv": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ },
+ "description": "Environment variables passed to the pipe program.",
+ "default": {}
+ },
+ "quoteArgs": {
+ "type": "boolean",
+ "description": "Should arguments that contain characters that need to be quoted (example: spaces) be quoted? Defaults to 'true'. If set to false, the debugger command will no longer be automatically quoted.",
+ "default": true
+ },
+ "windows": {
+ "description": "Windows-specific pipe launch configuration options",
+ "default": {
+ "pipeCwd": "${workspaceRoot}",
+ "pipeProgram": "enter the fully qualified path for the pipe program name, for example 'c:\\tools\\plink.exe'",
+ "pipeArgs": []
+ },
+ "type": "object",
+ "properties": {
+ "pipeCwd": {
+ "type": "string",
+ "description": "The fully qualified path to the working directory for the pipe program.",
+ "default": "${workspaceRoot}"
+ },
+ "pipeProgram": {
+ "type": "string",
+ "description": "The fully qualified pipe command to execute.",
+ "default": "enter the fully qualified path for the pipe program name, for example '/usr/bin/ssh'"
+ },
+ "pipeArgs": {
+ "type": "array",
+ "description": "Command line arguments passed to the pipe program. Token ${debuggerCommand} in pipeArgs will get replaced by the full debugger command, this token can be specified inline with other arguments. If ${debuggerCommand} isn’t used in any argument, the full debugger command will be instead be added to the end of the argument list.",
+ "items": {
+ "type": "string"
+ },
+ "default": []
+ },
+ "quoteArgs": {
+ "type": "boolean",
+ "description": "Should arguments that contain characters that need to be quoted (example: spaces) be quoted? Defaults to 'true'. If set to false, the debugger command will no longer be automatically quoted.",
+ "default": true
+ },
+ "pipeEnv": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ },
+ "description": "Environment variables passed to the pipe program.",
+ "default": {}
+ }
+ }
+ },
+ "osx": {
+ "description": "OSX-specific pipe launch configuration options",
+ "default": {
+ "pipeCwd": "${workspaceRoot}",
+ "pipeProgram": "enter the fully qualified path for the pipe program name, for example '/usr/bin/ssh'",
+ "pipeArgs": []
+ },
+ "type": "object",
+ "properties": {
+ "pipeCwd": {
+ "type": "string",
+ "description": "The fully qualified path to the working directory for the pipe program.",
+ "default": "${workspaceRoot}"
+ },
+ "pipeProgram": {
+ "type": "string",
+ "description": "The fully qualified pipe command to execute.",
+ "default": "enter the fully qualified path for the pipe program name, for example '/usr/bin/ssh'"
+ },
+ "pipeArgs": {
+ "type": "array",
+ "description": "Command line arguments passed to the pipe program. Token ${debuggerCommand} in pipeArgs will get replaced by the full debugger command, this token can be specified inline with other arguments. If ${debuggerCommand} isn’t used in any argument, the full debugger command will be instead be added to the end of the argument list.",
+ "items": {
+ "type": "string"
+ },
+ "default": []
+ },
+ "quoteArgs": {
+ "type": "boolean",
+ "description": "Should arguments that contain characters that need to be quoted (example: spaces) be quoted? Defaults to 'true'. If set to false, the debugger command will no longer be automatically quoted.",
+ "default": true
+ },
+ "pipeEnv": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ },
+ "description": "Environment variables passed to the pipe program.",
+ "default": {}
+ }
+ }
+ },
+ "linux": {
+ "description": "Linux-specific pipe launch configuration options",
+ "default": {
+ "pipeCwd": "${workspaceRoot}",
+ "pipeProgram": "enter the fully qualified path for the pipe program name, for example '/usr/bin/ssh'",
+ "pipeArgs": []
+ },
+ "type": "object",
+ "properties": {
+ "pipeCwd": {
+ "type": "string",
+ "description": "The fully qualified path to the working directory for the pipe program.",
+ "default": "${workspaceRoot}"
+ },
+ "pipeProgram": {
+ "type": "string",
+ "description": "The fully qualified pipe command to execute.",
+ "default": "enter the fully qualified path for the pipe program name, for example '/usr/bin/ssh'"
+ },
+ "pipeArgs": {
+ "type": "array",
+ "description": "Command line arguments passed to the pipe program. Token ${debuggerCommand} in pipeArgs will get replaced by the full debugger command, this token can be specified inline with other arguments. If ${debuggerCommand} isn’t used in any argument, the full debugger command will be instead be added to the end of the argument list.",
+ "items": {
+ "type": "string"
+ },
+ "default": []
+ },
+ "quoteArgs": {
+ "type": "boolean",
+ "description": "Should arguments that contain characters that need to be quoted (example: spaces) be quoted? Defaults to 'true'. If set to false, the debugger command will no longer be automatically quoted.",
+ "default": true
+ },
+ "pipeEnv": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ },
+ "description": "Environment variables passed to the pipe program.",
+ "default": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "attach": {
+ "type": "object",
+ "required": [],
+ "properties": {
+ "processName": {
+ "type": "string",
+ "description": "",
+ "default": "The process name to attach to. If this is used, 'processId' should not be used."
+ },
+ "processId": {
+ "anyOf": [
+ {
+ "type": "string",
+ "description": "The process id to attach to. Use \"${command:pickProcesss}\" to get a list of running processes to attach to. If 'processId' used, 'processName' should not be used.",
+ "default": "${command:pickProcess}"
+ },
+ {
+ "type": "integer",
+ "description": "The process id to attach to. Use \"${command:pickProcesss}\" to get a list of running processes to attach to. If 'processId' used, 'processName' should not be used.",
+ "default": 0
+ }
+ ]
+ },
+ "sourceFileMap": {
+ "type": "object",
+ "description": "Optional source file mappings passed to the debug engine. Example: '{ \"C:\\foo\":\"/home/user/foo\" }'",
+ "additionalProperties": {
+ "type": "string"
+ },
+ "default": {
+ "": ""
+ }
+ },
+ "justMyCode": {
+ "type": "boolean",
+ "description": "Optional flag to only show user code.",
+ "default": true
+ },
+ "symbolPath": {
+ "type": "array",
+ "description": "Array of directories to use to search for .pdb files. These directories will be searched in addition to the default locations -- next to the module and the path where the pdb was originally dropped to. Example: '[ \"/Volumes/symbols\" ]",
+ "items": {
+ "type": "string"
+ },
+ "default": []
+ },
+ "requireExactSource": {
+ "type": "boolean",
+ "description": "Optional flag to require current source code to match the pdb.",
+ "default": true
+ },
+ "enableStepFiltering": {
+ "type": "boolean",
+ "description": "Optional flag to enable stepping over Properties and Operators.",
+ "default": true
+ },
+ "logging": {
+ "description": "Optional flags to determine what types of messages should be logged to the output window.",
+ "type": "object",
+ "required": [],
+ "default": {},
+ "properties": {
+ "exceptions": {
+ "type": "boolean",
+ "description": "Optional flag to determine whether exception messages should be logged to the output window.",
+ "default": true
+ },
+ "moduleLoad": {
+ "type": "boolean",
+ "description": "Optional flag to determine whether module load events should be logged to the output window.",
+ "default": true
+ },
+ "programOutput": {
+ "type": "boolean",
+ "description": "Optional flag to determine whether program output should be logged to the output window when not using an external console.",
+ "default": true
+ },
+ "engineLogging": {
+ "type": "boolean",
+ "description": "Optional flag to determine whether diagnostic engine logs should be logged to the output window.",
+ "default": false
+ },
+ "browserStdOut": {
+ "type": "boolean",
+ "description": "Optional flag to determine if stdout text from the launching the web browser should be logged to the output window.",
+ "default": true
+ }
+ }
+ },
+ "pipeTransport": {
+ "description": "When present, this tells the debugger to connect to a remote computer using another executable as a pipe that will relay standard input/output between VS Code and the .NET Core debugger backend executable (vsdbg).",
+ "type": "object",
+ "required": [
+ "debuggerPath"
+ ],
+ "default": {
+ "pipeCwd": "${workspaceRoot}",
+ "pipeProgram": "enter the fully qualified path for the pipe program name, for example '/usr/bin/ssh'",
+ "pipeArgs": [],
+ "debuggerPath": "enter the path for the debugger on the target machine, for example ~/vsdbg/vsdbg"
+ },
+ "properties": {
+ "pipeCwd": {
+ "type": "string",
+ "description": "The fully qualified path to the working directory for the pipe program.",
+ "default": "${workspaceRoot}"
+ },
+ "pipeProgram": {
+ "type": "string",
+ "description": "The fully qualified pipe command to execute.",
+ "default": "enter the fully qualified path for the pipe program name, for example '/usr/bin/ssh'"
+ },
+ "pipeArgs": {
+ "type": "array",
+ "description": "Command line arguments passed to the pipe program. Token ${debuggerCommand} in pipeArgs will get replaced by the full debugger command, this token can be specified inline with other arguments. If ${debuggerCommand} isn’t used in any argument, the full debugger command will be instead be added to the end of the argument list.",
+ "items": {
+ "type": "string"
+ },
+ "default": []
+ },
+ "debuggerPath": {
+ "type": "string",
+ "description": "The full path to the debugger on the target machine.",
+ "default": "enter the path for the debugger on the target machine, for example ~/vsdbg/vsdbg"
+ },
+ "pipeEnv": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ },
+ "description": "Environment variables passed to the pipe program.",
+ "default": {}
+ },
+ "quoteArgs": {
+ "type": "boolean",
+ "description": "Should arguments that contain characters that need to be quoted (example: spaces) be quoted? Defaults to 'true'. If set to false, the debugger command will no longer be automatically quoted.",
+ "default": true
+ },
+ "windows": {
+ "description": "Windows-specific pipe launch configuration options",
+ "default": {
+ "pipeCwd": "${workspaceRoot}",
+ "pipeProgram": "enter the fully qualified path for the pipe program name, for example 'c:\\tools\\plink.exe'",
+ "pipeArgs": []
+ },
+ "type": "object",
+ "properties": {
+ "pipeCwd": {
+ "type": "string",
+ "description": "The fully qualified path to the working directory for the pipe program.",
+ "default": "${workspaceRoot}"
+ },
+ "pipeProgram": {
+ "type": "string",
+ "description": "The fully qualified pipe command to execute.",
+ "default": "enter the fully qualified path for the pipe program name, for example '/usr/bin/ssh'"
+ },
+ "pipeArgs": {
+ "type": "array",
+ "description": "Command line arguments passed to the pipe program. Token ${debuggerCommand} in pipeArgs will get replaced by the full debugger command, this token can be specified inline with other arguments. If ${debuggerCommand} isn’t used in any argument, the full debugger command will be instead be added to the end of the argument list.",
+ "items": {
+ "type": "string"
+ },
+ "default": []
+ },
+ "quoteArgs": {
+ "type": "boolean",
+ "description": "Should arguments that contain characters that need to be quoted (example: spaces) be quoted? Defaults to 'true'. If set to false, the debugger command will no longer be automatically quoted.",
+ "default": true
+ },
+ "pipeEnv": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ },
+ "description": "Environment variables passed to the pipe program.",
+ "default": {}
+ }
+ }
+ },
+ "osx": {
+ "description": "OSX-specific pipe launch configuration options",
+ "default": {
+ "pipeCwd": "${workspaceRoot}",
+ "pipeProgram": "enter the fully qualified path for the pipe program name, for example '/usr/bin/ssh'",
+ "pipeArgs": []
+ },
+ "type": "object",
+ "properties": {
+ "pipeCwd": {
+ "type": "string",
+ "description": "The fully qualified path to the working directory for the pipe program.",
+ "default": "${workspaceRoot}"
+ },
+ "pipeProgram": {
+ "type": "string",
+ "description": "The fully qualified pipe command to execute.",
+ "default": "enter the fully qualified path for the pipe program name, for example '/usr/bin/ssh'"
+ },
+ "pipeArgs": {
+ "type": "array",
+ "description": "Command line arguments passed to the pipe program. Token ${debuggerCommand} in pipeArgs will get replaced by the full debugger command, this token can be specified inline with other arguments. If ${debuggerCommand} isn’t used in any argument, the full debugger command will be instead be added to the end of the argument list.",
+ "items": {
+ "type": "string"
+ },
+ "default": []
+ },
+ "quoteArgs": {
+ "type": "boolean",
+ "description": "Should arguments that contain characters that need to be quoted (example: spaces) be quoted? Defaults to 'true'. If set to false, the debugger command will no longer be automatically quoted.",
+ "default": true
+ },
+ "pipeEnv": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ },
+ "description": "Environment variables passed to the pipe program.",
+ "default": {}
+ }
+ }
+ },
+ "linux": {
+ "description": "Linux-specific pipe launch configuration options",
+ "default": {
+ "pipeCwd": "${workspaceRoot}",
+ "pipeProgram": "enter the fully qualified path for the pipe program name, for example '/usr/bin/ssh'",
+ "pipeArgs": []
+ },
+ "type": "object",
+ "properties": {
+ "pipeCwd": {
+ "type": "string",
+ "description": "The fully qualified path to the working directory for the pipe program.",
+ "default": "${workspaceRoot}"
+ },
+ "pipeProgram": {
+ "type": "string",
+ "description": "The fully qualified pipe command to execute.",
+ "default": "enter the fully qualified path for the pipe program name, for example '/usr/bin/ssh'"
+ },
+ "pipeArgs": {
+ "type": "array",
+ "description": "Command line arguments passed to the pipe program. Token ${debuggerCommand} in pipeArgs will get replaced by the full debugger command, this token can be specified inline with other arguments. If ${debuggerCommand} isn’t used in any argument, the full debugger command will be instead be added to the end of the argument list.",
+ "items": {
+ "type": "string"
+ },
+ "default": []
+ },
+ "quoteArgs": {
+ "type": "boolean",
+ "description": "Should arguments that contain characters that need to be quoted (example: spaces) be quoted? Defaults to 'true'. If set to false, the debugger command will no longer be automatically quoted.",
+ "default": true
+ },
+ "pipeEnv": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ },
+ "description": "Environment variables passed to the pipe program.",
+ "default": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
}
]
}
-}
\ No newline at end of file
+}
diff --git a/scripts/remoteProcessPickerScript b/scripts/remoteProcessPickerScript
new file mode 100644
index 0000000000..77c1622893
--- /dev/null
+++ b/scripts/remoteProcessPickerScript
@@ -0,0 +1 @@
+uname && if [ "$(uname)" = "Linux" ] ; then ps -axww -o pid=,comm=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,args= ; elif [ "$(uname)" = "Darwin" ] ; then ps -axww -o pid=,comm=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,args= -c; fi
\ No newline at end of file
diff --git a/src/CSharpExtDownloader.ts b/src/CSharpExtDownloader.ts
new file mode 100644
index 0000000000..0fee8165f9
--- /dev/null
+++ b/src/CSharpExtDownloader.ts
@@ -0,0 +1,142 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import * as vscode from 'vscode';
+import TelemetryReporter from 'vscode-extension-telemetry';
+import * as util from './common';
+import { Logger } from './logger';
+import { PackageManager, Status, PackageError } from './packages';
+import { PlatformInformation } from './platform';
+import { VSCodePlatformInformation } from './vscodePlatform';
+
+/*
+ * Class used to download the runtime dependencies of the C# Extension
+ */
+export class CSharpExtDownloader
+{
+ public constructor(
+ private channel: vscode.OutputChannel,
+ private logger: Logger,
+ private reporter: TelemetryReporter /* optional */,
+ private packageJSON: any) {
+ }
+
+ public installRuntimeDependencies(): Promise {
+ this.logger.append('Updating C# dependencies...');
+ this.channel.show();
+
+ let statusItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right);
+ let status: Status = {
+ setMessage: text => {
+ statusItem.text = text;
+ statusItem.show();
+ },
+ setDetail: text => {
+ statusItem.tooltip = text;
+ statusItem.show();
+ }
+ };
+
+ // Sends "AcquisitionStart" telemetry to indicate an acquisition started.
+ if (this.reporter) {
+ this.reporter.sendTelemetryEvent("AcquisitionStart");
+ }
+
+ let platformInfo: PlatformInformation;
+ let packageManager: PackageManager;
+ let installationStage = 'touchBeginFile';
+ let errorMessage = '';
+ let success = false;
+
+ let telemetryProps: any = {};
+
+ return util.touchInstallFile(util.InstallFileType.Begin)
+ .then(() => {
+ installationStage = 'getPlatformInfo';
+ return VSCodePlatformInformation.GetCurrent();
+ })
+ .then(info => {
+ platformInfo = info;
+ packageManager = new PackageManager(info, this.packageJSON);
+ this.logger.appendLine();
+
+ // Display platform information and RID followed by a blank line
+ this.logger.appendLine(`Platform: ${info.toString()}`);
+ this.logger.appendLine();
+
+ installationStage = 'downloadPackages';
+
+ const config = vscode.workspace.getConfiguration();
+ const proxy = config.get('http.proxy');
+ const strictSSL = config.get('http.proxyStrictSSL', true);
+
+ return packageManager.DownloadPackages(this.logger, status, proxy, strictSSL);
+ })
+ .then(() => {
+ this.logger.appendLine();
+
+ installationStage = 'installPackages';
+ return packageManager.InstallPackages(this.logger, status);
+ })
+ .then(() => {
+ installationStage = 'touchLockFile';
+ return util.touchInstallFile(util.InstallFileType.Lock);
+ })
+ .then(() => {
+ installationStage = 'completeSuccess';
+ success = true;
+ })
+ .catch(error => {
+ if (error instanceof PackageError) {
+ // we can log the message in a PackageError to telemetry as we do not put PII in PackageError messages
+ telemetryProps['error.message'] = error.message;
+
+ if (error.innerError) {
+ errorMessage = error.innerError.toString();
+ } else {
+ errorMessage = error.message;
+ }
+
+ if (error.pkg) {
+ telemetryProps['error.packageUrl'] = error.pkg.url;
+ }
+
+ } else {
+ // do not log raw errorMessage in telemetry as it is likely to contain PII.
+ errorMessage = error.toString();
+ }
+
+ this.logger.appendLine(`Failed at stage: ${installationStage}`);
+ this.logger.appendLine(errorMessage);
+ })
+ .then(() => {
+ telemetryProps['installStage'] = installationStage;
+ telemetryProps['platform.architecture'] = platformInfo.architecture;
+ telemetryProps['platform.platform'] = platformInfo.platform;
+ telemetryProps['platform.runtimeId'] = platformInfo.runtimeId;
+ if (platformInfo.distribution) {
+ telemetryProps['platform.distribution'] = platformInfo.distribution.toString();
+ }
+
+ if (this.reporter) {
+ this.reporter.sendTelemetryEvent('Acquisition', telemetryProps);
+ }
+
+ this.logger.appendLine();
+ installationStage = '';
+ this.logger.appendLine('Finished');
+
+ statusItem.dispose();
+ })
+ .then(() => {
+ // We do this step at the end so that we clean up the begin file in the case that we hit above catch block
+ // Attach a an empty catch to this so that errors here do not propogate
+ return util.deleteInstallFile(util.InstallFileType.Begin).catch((error) => { });
+ }).then(() => {
+ return success;
+ });
+
+ }
+}
diff --git a/src/assets.ts b/src/assets.ts
index e3beb5b88a..e62535eda8 100644
--- a/src/assets.ts
+++ b/src/assets.ts
@@ -11,46 +11,7 @@ import { OmniSharpServer } from './omnisharp/server';
import * as serverUtils from './omnisharp/utils';
import * as protocol from './omnisharp/protocol';
import { tolerantParse } from './json';
-
-interface DebugConfiguration {
- name: string;
- type: string;
- request: string;
- internalConsoleOptions?: string;
- sourceFileMap?: any;
-}
-
-interface ConsoleLaunchConfiguration extends DebugConfiguration {
- preLaunchTask: string;
- program: string;
- args: string[];
- cwd: string;
- stopAtEntry: boolean;
- env?: any;
- console?: string;
-}
-
-interface CommandLine {
- command: string;
- args?: string;
-}
-
-interface LaunchBrowserConfiguration {
- enabled: boolean;
- args: string;
- windows?: CommandLine;
- osx: CommandLine;
- linux: CommandLine;
-}
-
-interface WebLaunchConfiguration extends ConsoleLaunchConfiguration {
- launchBrowser: LaunchBrowserConfiguration;
-}
-
-interface AttachConfiguration extends DebugConfiguration {
- processId: string;
-}
-
+import * as util from './common';
export class AssetGenerator {
public rootPath: string;
@@ -194,83 +155,89 @@ export class AssetGenerator {
return result;
}
- private createLaunchConfiguration(): ConsoleLaunchConfiguration {
- return {
- name: '.NET Core Launch (console)',
- type: 'coreclr',
- request: 'launch',
- preLaunchTask: 'build',
- program: this.computeProgramPath(),
- args: [],
- cwd: this.computeWorkingDirectory(),
- console: "internalConsole",
- stopAtEntry: false,
- internalConsoleOptions: "openOnSessionStart"
- };
+ private createLaunchConfiguration(): string{
+ return `
+{
+ "name": ".NET Core Launch (console)",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ // If you have changed target frameworks, make sure to update the program path.
+ "program": "${util.convertNativePathToPosix(this.computeProgramPath())}",
+ "args": [],
+ "cwd": "${util.convertNativePathToPosix(this.computeWorkingDirectory())}",
+ // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window
+ "console": "internalConsole",
+ "stopAtEntry": false,
+ "internalConsoleOptions": "openOnSessionStart"
+}`;
}
- private createWebLaunchConfiguration(): WebLaunchConfiguration {
- return {
- name: '.NET Core Launch (web)',
- type: 'coreclr',
- request: 'launch',
- preLaunchTask: 'build',
- program: this.computeProgramPath(),
- args: [],
- cwd: this.computeWorkingDirectory(),
- stopAtEntry: false,
- internalConsoleOptions: "openOnSessionStart",
- launchBrowser: {
- enabled: true,
- args: '${auto-detect-url}',
- windows: {
- command: 'cmd.exe',
- args: '/C start ${auto-detect-url}'
- },
- osx: {
- command: 'open'
- },
- linux: {
- command: 'xdg-open'
- }
- },
- env: {
- ASPNETCORE_ENVIRONMENT: "Development"
- },
- sourceFileMap: {
- "/Views": "${workspaceRoot}/Views"
- }
- };
+ private createWebLaunchConfiguration(): string {
+ return `
+{
+ "name": ".NET Core Launch (web)",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ // If you have changed target frameworks, make sure to update the program path.
+ "program": "${util.convertNativePathToPosix(this.computeProgramPath())}",
+ "args": [],
+ "cwd": "${util.convertNativePathToPosix(this.computeWorkingDirectory())}",
+ "stopAtEntry": false,
+ "internalConsoleOptions": "openOnSessionStart",
+ "launchBrowser": {
+ "enabled": true,
+ "args": "\${auto-detect-url}",
+ "windows": {
+ "command": "cmd.exe",
+ "args": "/C start \${auto-detect-url}"
+ },
+ "osx": {
+ "command": "open"
+ },
+ "linux": {
+ "command": "xdg-open"
+ }
+ },
+ "env": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "sourceFileMap": {
+ "/Views": "\${workspaceRoot}/Views"
+ }
+}`;
}
- private createAttachConfiguration(): AttachConfiguration {
- return {
- name: '.NET Core Attach',
- type: 'coreclr',
- request: 'attach',
- processId: "${command:pickProcess}"
- };
+ // AttachConfiguration
+ private createAttachConfiguration(): string {
+ return `
+{
+ "name": ".NET Core Attach",
+ "type": "coreclr",
+ "request": "attach",
+ "processId": "\${command:pickProcess}"
+}`;
}
- public createLaunchJson(isWebProject: boolean): any {
- let version = '0.2.0';
+ public createLaunchJson(isWebProject: boolean): string {
if (!isWebProject) {
- return {
- version: version,
- configurations: [
- this.createLaunchConfiguration(),
- this.createAttachConfiguration()
- ]
- };
+ const launchConfigurationsMassaged: string = indentJsonString(this.createLaunchConfiguration());
+ const attachConfigurationsMassaged: string = indentJsonString(this.createAttachConfiguration());
+ return `
+[
+ ${launchConfigurationsMassaged},
+ ${attachConfigurationsMassaged}
+]`;
}
else {
- return {
- version: version,
- configurations: [
- this.createWebLaunchConfiguration(),
- this.createAttachConfiguration()
- ]
- };
+ const webLaunchConfigurationsMassaged: string = indentJsonString(this.createWebLaunchConfiguration());
+ const attachConfigurationsMassaged: string = indentJsonString(this.createAttachConfiguration());
+ return `
+[
+ ${webLaunchConfigurationsMassaged},
+ ${attachConfigurationsMassaged}
+]`;
}
}
@@ -282,7 +249,7 @@ export class AssetGenerator {
return {
taskName: 'build',
- args: [buildPath],
+ args: [util.convertNativePathToPosix(buildPath)],
isBuildCommand: true,
problemMatcher: '$msCompile'
};
@@ -346,10 +313,8 @@ interface Operations {
addLaunchJson?: boolean;
}
-function hasOperations(operations: Operations) {
- return operations.addLaunchJson ||
- operations.updateTasksJson ||
- operations.addLaunchJson;
+function hasAddOperations(operations: Operations) {
+ return operations.addLaunchJson || operations.addLaunchJson;
}
function getOperations(generator: AssetGenerator) {
@@ -362,7 +327,7 @@ function getBuildTasks(tasksConfiguration: tasks.TaskConfiguration): tasks.TaskD
function findBuildTask(tasksDescriptions: tasks.TaskDescription[]) {
if (tasksDescriptions) {
- const buildTask = tasksDescriptions.find(td => td.taskName === 'build');
+ const buildTask = tasksDescriptions.find(td => td.isBuildCommand);
if (buildTask !== undefined) {
result.push(buildTask);
}
@@ -475,6 +440,10 @@ function addTasksJsonIfNecessary(generator: AssetGenerator, operations: Operatio
});
}
+function indentJsonString(json: string, numSpaces: number = 4): string {
+ return json.split('\n').map(line => ' '.repeat(numSpaces) + line).join('\n').trim();
+}
+
function addLaunchJsonIfNecessary(generator: AssetGenerator, operations: Operations) {
return new Promise((resolve, reject) => {
if (!operations.addLaunchJson) {
@@ -482,10 +451,20 @@ function addLaunchJsonIfNecessary(generator: AssetGenerator, operations: Operati
}
const isWebProject = generator.hasWebServerDependency();
- const launchJson = generator.createLaunchJson(isWebProject);
- const launchJsonText = JSON.stringify(launchJson, null, ' ');
+ const launchJson: string = generator.createLaunchJson(isWebProject);
- fs.writeFile(generator.launchJsonPath, launchJsonText, err => {
+ const configurationsMassaged: string = indentJsonString(launchJson);
+
+ const launchJsonText = `
+{
+ // Use IntelliSense to find out which attributes exist for C# debugging
+ // Use hover for the description of the existing attributes
+ // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
+ "version": "0.2.0",
+ "configurations": ${configurationsMassaged}
+}`;
+
+ fs.writeFile(generator.launchJsonPath, launchJsonText.trim(), err => {
if (err) {
return reject(err);
}
@@ -522,7 +501,7 @@ export function addAssetsIfNecessary(server: OmniSharpServer): Promise {
- if (!hasOperations(operations)) {
+ if (!hasAddOperations(operations)) {
return resolve(AddAssetResult.NotApplicable);
}
@@ -562,27 +541,10 @@ function doesAnyAssetExist(generator: AssetGenerator) {
});
}
-function deleteAsset(path: string) {
- return new Promise((resolve, reject) => {
- fs.exists(path, exists => {
- if (exists) {
- // TODO: Should we check after unlinking to see if the file still exists?
- fs.unlink(path, err => {
- if (err) {
- return reject(err);
- }
-
- resolve();
- });
- }
- });
- });
-}
-
function deleteAssets(generator: AssetGenerator) {
return Promise.all([
- deleteAsset(generator.launchJsonPath),
- deleteAsset(generator.tasksJsonPath)
+ util.deleteIfExists(generator.launchJsonPath),
+ util.deleteIfExists(generator.tasksJsonPath)
]);
}
@@ -618,7 +580,7 @@ export function generateAssets(server: OmniSharpServer) {
if (containsDotNetCoreProjects(info)) {
const generator = new AssetGenerator(info);
getOperations(generator).then(operations => {
- if (hasOperations(operations)) {
+ if (hasAddOperations(operations)) {
shouldGenerateAssets(generator).then(res => {
if (res) {
fs.ensureDir(generator.vscodeFolder, err => {
@@ -633,4 +595,4 @@ export function generateAssets(server: OmniSharpServer) {
vscode.window.showErrorMessage("Could not locate .NET Core project. Assets were not generated.");
}
});
-}
\ No newline at end of file
+}
diff --git a/src/common.ts b/src/common.ts
index e120cc26d4..ede27ead65 100644
--- a/src/common.ts
+++ b/src/common.ts
@@ -73,6 +73,25 @@ export function fileExists(filePath: string): Promise {
});
}
+export function deleteIfExists(filePath: string): Promise {
+ return fileExists(filePath)
+ .then((exists: boolean) => {
+ return new Promise((resolve, reject) => {
+ if (!exists) {
+ resolve();
+ }
+
+ fs.unlink(filePath, err => {
+ if (err) {
+ return reject(err);
+ }
+
+ resolve();
+ });
+ });
+ });
+}
+
export enum InstallFileType {
Begin,
Lock
@@ -112,3 +131,62 @@ export function deleteInstallFile(type: InstallFileType): Promise {
});
});
}
+
+export function convertNativePathToPosix(pathString: string): string {
+ let parts = pathString.split(path.sep);
+ return parts.join(path.posix.sep);
+}
+
+/**
+ * Splits a string of command line arguments into a string array. This function
+ * handles double-quoted arguments, but it is tailored toward the needs of the
+ * text returned by VSTest, and is not generally useful as a command line parser.
+ * @param commandLineString Text of command line arguments
+ */
+export function splitCommandLineArgs(commandLineString: string): string[] {
+ let result = [];
+ let start = -1;
+ let index = 0;
+ let inQuotes = false;
+
+ while (index < commandLineString.length) {
+ let ch = commandLineString[index];
+
+ // Are we starting a new word?
+ if (start === -1 && ch !== ' ' && ch !== '"') {
+ start = index;
+ }
+
+ // is next character quote?
+ if (ch === '"') {
+ // Are we already in a quoted argument? If so, push the argument to the result list.
+ // If not, start a new quoted argument.
+ if (inQuotes) {
+ let arg = start >= 0
+ ? commandLineString.substring(start, index)
+ : "";
+ result.push(arg);
+ start = -1;
+ inQuotes = false;
+ }
+ else {
+ inQuotes = true;
+ }
+ }
+
+ if (!inQuotes && start >= 0 && ch === ' ') {
+ let arg = commandLineString.substring(start, index);
+ result.push(arg);
+ start = -1;
+ }
+
+ index++;
+ }
+
+ if (start >= 0) {
+ let arg = commandLineString.substring(start, commandLineString.length);
+ result.push(arg);
+ }
+
+ return result;
+}
diff --git a/src/coreclr-debug/activate.ts b/src/coreclr-debug/activate.ts
index e897330ba2..00bf5441c1 100644
--- a/src/coreclr-debug/activate.ts
+++ b/src/coreclr-debug/activate.ts
@@ -9,29 +9,43 @@ import TelemetryReporter from 'vscode-extension-telemetry';
import { CoreClrDebugUtil, DotnetInfo, } from './util';
import * as debugInstall from './install';
import { Logger } from './../logger';
-import { PlatformInformation } from './../platform';
+import { VSCodePlatformInformation } from './../vscodePlatform';
+import { CSharpExtDownloader } from './../CSharpExtDownloader';
let _debugUtil: CoreClrDebugUtil = null;
let _reporter: TelemetryReporter = null;
let _logger: Logger = null;
-export function activate(context: vscode.ExtensionContext, reporter: TelemetryReporter, logger: Logger, channel: vscode.OutputChannel) {
+export function activate(thisExtension : vscode.Extension, context: vscode.ExtensionContext, reporter: TelemetryReporter, logger: Logger, channel: vscode.OutputChannel) {
_debugUtil = new CoreClrDebugUtil(context.extensionPath, logger);
_reporter = reporter;
_logger = logger;
if (!CoreClrDebugUtil.existsSync(_debugUtil.debugAdapterDir())) {
- PlatformInformation.GetCurrent().then((info) => {
+ VSCodePlatformInformation.GetCurrent().then((info) => {
if (info.runtimeId) {
if (info.runtimeId === 'win7-x86') {
logger.appendLine(`[WARNING]: x86 Windows is not currently supported by the .NET Core debugger. Debugging will not be available.`);
+ } else if (info.isLinux() && VSCodePlatformInformation.isFallbackDebuggerLinuxRuntimeIdSet()) {
+ // The user set the fallback runtime id after the initial extension install, retry again now
+ const downloader = new CSharpExtDownloader(channel, logger, null, thisExtension.packageJSON);
+ downloader.installRuntimeDependencies().then((success : boolean) => {
+ if (success) {
+ completeDebuggerInstall(logger, channel);
+ }
+ });
} else {
logger.appendLine("[ERROR]: C# Extension failed to install the debugger package");
showInstallErrorMessage(channel);
}
} else {
if (info.isLinux) {
- logger.appendLine(`[WARNING]: The current Linux distribution '${info.distribution.name}' version '${info.distribution.version}' is not currently supported by the .NET Core debugger. Debugging will not be available.`);
+ if (info.distribution.name === 'arch') {
+ logger.appendLine("[WARNING]: The .NET Core debugger could not be automatically installed. Follow instructions on https://aka.ms/vscode-csext-arch to enable debugging on Arch Linux.");
+ } else {
+ logger.appendLine(`[WARNING]: The current Linux distribution '${info.distribution.name}' version '${info.distribution.version}' is not currently supported by the .NET Core debugger. Debugging will not be available.`);
+ logger.appendLine(`If '${info.distribution.name}' is binary compatible with a Linux distribution officially supported by .NET Core, you may be able to resolve this by setting 'csharp.fallbackDebuggerLinuxRuntimeId' in 'File->Preferences->Settings' and restarting VS Code.`);
+ }
} else {
logger.appendLine(`[WARNING]: The current operating system is not currently supported by the .NET Core debugger. Debugging will not be available.`);
}
@@ -42,45 +56,49 @@ export function activate(context: vscode.ExtensionContext, reporter: TelemetryRe
showInstallErrorMessage(channel);
});
} else if (!CoreClrDebugUtil.existsSync(_debugUtil.installCompleteFilePath())) {
- _debugUtil.checkDotNetCli()
- .then((dotnetInfo: DotnetInfo) => {
- _debugUtil.checkOpenSSLInstalledIfRequired().then((isInstalled) => {
- if (isInstalled) {
- let installer = new debugInstall.DebugInstaller(_debugUtil);
- installer.finishInstall()
- .then(() => {
- vscode.window.setStatusBarMessage('Successfully installed .NET Core Debugger.');
- })
- .catch((err) => {
- logger.appendLine("[ERROR]: An error occured while installing the .NET Core Debugger:");
- logger.appendLine(err);
- showInstallErrorMessage(channel);
- // TODO: log telemetry?
- });
- } else {
- logger.appendLine("[ERROR] The debugger cannot be installed. A required component, OpenSSL, is not correctly configured.");
- logger.appendLine("In order to use the debugger, open a terminal window and execute the following instructions.");
- logger.appendLine("See https://www.microsoft.com/net/core#macos for more details.");
- logger.appendLine();
- logger.appendLine(" brew update");
- logger.appendLine(" brew install openssl");
- logger.appendLine(" mkdir -p /usr/local/lib");
- logger.appendLine(" ln -s /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/");
- logger.appendLine(" ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/");
- channel.show();
- vscode.window.showErrorMessage("The .NET Core debugger cannot be installed. OpenSSL is not correctly configured. See the C# output channel for details.");
- }
- });
- }, (err) => {
- // Check for dotnet tools failed. pop the UI
- // err is a DotNetCliError but use defaults in the unexpected case that it's not
- showDotnetToolsWarning(err.ErrorMessage || _debugUtil.defaultDotNetCliErrorMessage());
- _logger.appendLine(err.ErrorString || err);
- // TODO: log telemetry?
- });
+ completeDebuggerInstall(logger, channel);
}
}
+function completeDebuggerInstall(logger: Logger, channel: vscode.OutputChannel) : void {
+ _debugUtil.checkDotNetCli()
+ .then((dotnetInfo: DotnetInfo) => {
+ _debugUtil.checkOpenSSLInstalledIfRequired().then((isInstalled) => {
+ if (isInstalled) {
+ let installer = new debugInstall.DebugInstaller(_debugUtil);
+ installer.finishInstall()
+ .then(() => {
+ vscode.window.setStatusBarMessage('Successfully installed .NET Core Debugger.');
+ })
+ .catch((err) => {
+ logger.appendLine("[ERROR]: An error occured while installing the .NET Core Debugger:");
+ logger.appendLine(err);
+ showInstallErrorMessage(channel);
+ // TODO: log telemetry?
+ });
+ } else {
+ logger.appendLine("[ERROR] The debugger cannot be installed. A required component, OpenSSL, is not correctly configured.");
+ logger.appendLine("In order to use the debugger, open a terminal window and execute the following instructions.");
+ logger.appendLine("See https://www.microsoft.com/net/core#macos for more details.");
+ logger.appendLine();
+ logger.appendLine(" brew update");
+ logger.appendLine(" brew install openssl");
+ logger.appendLine(" mkdir -p /usr/local/lib");
+ logger.appendLine(" ln -s /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/");
+ logger.appendLine(" ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/");
+ channel.show();
+ vscode.window.showErrorMessage("The .NET Core debugger cannot be installed. OpenSSL is not correctly configured. See the C# output channel for details.");
+ }
+ });
+ }, (err) => {
+ // Check for dotnet tools failed. pop the UI
+ // err is a DotNetCliError but use defaults in the unexpected case that it's not
+ showDotnetToolsWarning(err.ErrorMessage || _debugUtil.defaultDotNetCliErrorMessage());
+ _logger.appendLine(err.ErrorString || err);
+ // TODO: log telemetry?
+ });
+}
+
function showInstallErrorMessage(channel: vscode.OutputChannel) {
channel.show();
vscode.window.showErrorMessage("An error occured during installation of the .NET Core Debugger. The C# extension may need to be reinstalled.");
diff --git a/src/coreclr-debug/debuggerEventsProtocol.ts b/src/coreclr-debug/debuggerEventsProtocol.ts
new file mode 100644
index 0000000000..94b140ce87
--- /dev/null
+++ b/src/coreclr-debug/debuggerEventsProtocol.ts
@@ -0,0 +1,43 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+'use strict';
+
+// This contains the definition of messages that VsDbg-UI can send back to a listener which registers itself via the 'debuggerEventsPipeName'
+// property on a launch or attach request.
+//
+// All messages are sent as UTF-8 JSON text with a tailing '\n'
+export namespace DebuggerEventsProtocol {
+ export module EventType {
+ // Indicates that the vsdbg-ui has received the attach or launch request and is starting up
+ export const Starting = "starting";
+ // Indicates that vsdbg-ui has successfully launched the specified process.
+ // The ProcessLaunchedEvent interface details the event payload.
+ export const ProcessLaunched = "processLaunched";
+ // Debug session is ending
+ export const DebuggingStopped = "debuggingStopped";
+ };
+
+ export interface DebuggerEvent {
+ // Contains one of the 'DebuggerEventsProtocol.EventType' values
+ eventType: string;
+ }
+
+ export interface ProcessLaunchedEvent extends DebuggerEvent {
+ // Process id of the newly-launched target process
+ targetProcessId: number;
+ }
+
+ // Decodes a packet received from the debugger into an event
+ export function decodePacket(packet: Buffer) : DebuggerEvent {
+ // Verify the message ends in a newline
+ if (packet[packet.length-1] != 10 /*\n*/) {
+ throw new Error("Unexpected message received from debugger.");
+ }
+
+ const message = packet.toString('utf-8', 0, packet.length-1);
+ return JSON.parse(message);
+ }
+}
\ No newline at end of file
diff --git a/src/coreclr-debug/install.ts b/src/coreclr-debug/install.ts
index 494f932283..32b8dcdcc2 100644
--- a/src/coreclr-debug/install.ts
+++ b/src/coreclr-debug/install.ts
@@ -70,6 +70,8 @@ export class DebugInstaller {
const manifestPath = path.join(this._util.extensionDir(), 'package.json');
let manifestString = fs.readFileSync(manifestPath, 'utf8');
let manifestObject = JSON.parse(manifestString);
+
+ // .NET Core
delete manifestObject.contributes.debuggers[0].runtime;
delete manifestObject.contributes.debuggers[0].program;
@@ -78,7 +80,15 @@ export class DebugInstaller {
manifestObject.contributes.debuggers[0].osx = { program: programString };
manifestObject.contributes.debuggers[0].linux = { program: programString };
+ // .NET Framework
+ delete manifestObject.contributes.debuggers[1].runtime;
+ delete manifestObject.contributes.debuggers[1].program;
+
+ manifestObject.contributes.debuggers[1].windows = { program: programString + '.exe' };
+ manifestObject.contributes.debuggers[1].osx = { program: programString };
+ manifestObject.contributes.debuggers[1].linux = { program: programString };
+
manifestString = JSON.stringify(manifestObject, null, 2);
fs.writeFileSync(manifestPath, manifestString);
}
-}
\ No newline at end of file
+}
diff --git a/src/features/changeForwarding.ts b/src/features/changeForwarding.ts
index ddf586d95d..45c0116755 100644
--- a/src/features/changeForwarding.ts
+++ b/src/features/changeForwarding.ts
@@ -22,7 +22,7 @@ function forwardDocumentChanges(server: OmniSharpServer): Disposable {
return;
}
- serverUtils.updateBuffer(server, {Buffer: document.getText(), Filename: document.fileName}).catch(err => {
+ serverUtils.updateBuffer(server, {Buffer: document.getText(), FileName: document.fileName}).catch(err => {
console.error(err);
return err;
});
@@ -36,7 +36,7 @@ function forwardFileChanges(server: OmniSharpServer): Disposable {
return;
}
- let req = { Filename: uri.fsPath };
+ let req = { FileName: uri.fsPath };
serverUtils.filesChanged(server, [req]).catch(err => {
console.warn(`[o] failed to forward file change event for ${uri.fsPath}`, err);
diff --git a/src/features/codeActionProvider.ts b/src/features/codeActionProvider.ts
index d6d97f4b93..81b1646115 100644
--- a/src/features/codeActionProvider.ts
+++ b/src/features/codeActionProvider.ts
@@ -33,13 +33,12 @@ export default class OmnisharpCodeActionProvider extends AbstractProvider implem
}
public provideCodeActions(document: TextDocument, range: Range, context: CodeActionContext, token: CancellationToken): Promise {
-
if (this._disabled) {
return;
}
let req: protocol.V2.GetCodeActionsRequest = {
- Filename: document.fileName,
+ FileName: document.fileName,
Selection: OmnisharpCodeActionProvider._asRange(range)
};
@@ -49,7 +48,7 @@ export default class OmnisharpCodeActionProvider extends AbstractProvider implem
title: codeAction.Name,
command: this._commandId,
arguments: [{
- Filename: document.fileName,
+ FileName: document.fileName,
Selection: OmnisharpCodeActionProvider._asRange(range),
Identifier: codeAction.Identifier,
WantsTextChanges: true
diff --git a/src/features/codeLensProvider.ts b/src/features/codeLensProvider.ts
index 99b1b58755..f8d002ca61 100644
--- a/src/features/codeLensProvider.ts
+++ b/src/features/codeLensProvider.ts
@@ -32,7 +32,7 @@ export default class OmniSharpCodeLensProvider extends AbstractSupport implement
};
provideCodeLenses(document: TextDocument, token: CancellationToken): CodeLens[] | Thenable {
- return serverUtils.currentFileMembersAsTree(this._server, { Filename: document.fileName }, token).then(tree => {
+ return serverUtils.currentFileMembersAsTree(this._server, { FileName: document.fileName }, token).then(tree => {
let ret: CodeLens[] = [];
tree.TopLevelTypeDefinitions.forEach(node => this._convertQuickFix(ret, document.fileName, node));
return ret;
@@ -59,7 +59,7 @@ export default class OmniSharpCodeLensProvider extends AbstractSupport implement
if (codeLens instanceof OmniSharpCodeLens) {
let req = {
- Filename: codeLens.fileName,
+ FileName: codeLens.fileName,
Line: codeLens.range.start.line + 1,
Column: codeLens.range.start.character + 1,
OnlyThisFile: false,
@@ -75,7 +75,7 @@ export default class OmniSharpCodeLensProvider extends AbstractSupport implement
codeLens.command = {
title: len === 1 ? '1 reference' : `${len} references`,
command: 'editor.action.showReferences',
- arguments: [Uri.file(req.Filename), codeLens.range.start, res.QuickFixes.map(toLocation)]
+ arguments: [Uri.file(req.FileName), codeLens.range.start, res.QuickFixes.map(toLocation)]
};
return codeLens;
diff --git a/src/features/diagnosticsProvider.ts b/src/features/diagnosticsProvider.ts
index 281e17c572..ad108b00fa 100644
--- a/src/features/diagnosticsProvider.ts
+++ b/src/features/diagnosticsProvider.ts
@@ -100,12 +100,10 @@ export class Advisor {
for (let key in this._projectSourceFileCounts) {
sourceFileCount += this._projectSourceFileCounts[key];
if (sourceFileCount > 1000) {
- console.log(`_isHugeProject = true (${sourceFileCount})`);
return true;
}
}
- console.log(`_isHugeProject = false (${sourceFileCount})`);
return false;
}
}
@@ -193,10 +191,12 @@ class DiagnosticsProvider extends AbstractSupport {
let source = new vscode.CancellationTokenSource();
let handle = setTimeout(() => {
- serverUtils.codeCheck(this._server, { Filename: document.fileName }, source.token).then(value => {
+ serverUtils.codeCheck(this._server, { FileName: document.fileName }, source.token).then(value => {
+
+ let quickFixes = value.QuickFixes.filter(DiagnosticsProvider._shouldInclude);
// Easy case: If there are no diagnostics in the file, we can clear it quickly.
- if (value.QuickFixes.length === 0) {
+ if (quickFixes.length === 0) {
if (this._diagnostics.has(document.uri)) {
this._diagnostics.delete(document.uri);
}
@@ -205,7 +205,7 @@ class DiagnosticsProvider extends AbstractSupport {
}
// (re)set new diagnostics for this document
- let diagnostics = value.QuickFixes.map(DiagnosticsProvider._asDiagnostic);
+ let diagnostics = quickFixes.map(DiagnosticsProvider._asDiagnostic);
this._diagnostics.set(document.uri, diagnostics);
});
@@ -228,9 +228,12 @@ class DiagnosticsProvider extends AbstractSupport {
this._projectValidation = new vscode.CancellationTokenSource();
let handle = setTimeout(() => {
- serverUtils.codeCheck(this._server, { Filename: null }, this._projectValidation.token).then(value => {
+ serverUtils.codeCheck(this._server, { FileName: null }, this._projectValidation.token).then(value => {
+
+ let quickFixes = value.QuickFixes
+ .filter(DiagnosticsProvider._shouldInclude)
+ .sort((a, b) => a.FileName.localeCompare(b.FileName));
- let quickFixes = value.QuickFixes.sort((a, b) => a.FileName.localeCompare(b.FileName));
let entries: [vscode.Uri, vscode.Diagnostic[]][] = [];
let lastEntry: [vscode.Uri, vscode.Diagnostic[]];
@@ -269,6 +272,10 @@ class DiagnosticsProvider extends AbstractSupport {
});
}
+ private static _shouldInclude(quickFix: protocol.QuickFix): boolean {
+ return quickFix.LogLevel.toLowerCase() !== 'hidden';
+ }
+
// --- data converter
private static _asDiagnostic(quickFix: protocol.QuickFix): vscode.Diagnostic {
@@ -279,13 +286,13 @@ class DiagnosticsProvider extends AbstractSupport {
private static _asDiagnosticSeverity(logLevel: string): vscode.DiagnosticSeverity {
switch (logLevel.toLowerCase()) {
+ case 'error':
+ return vscode.DiagnosticSeverity.Error;
case 'warning':
- case 'warn':
return vscode.DiagnosticSeverity.Warning;
- case 'hidden':
- return vscode.DiagnosticSeverity.Information;
+ // info and hidden
default:
- return vscode.DiagnosticSeverity.Error;
+ return vscode.DiagnosticSeverity.Information;
}
}
diff --git a/src/features/documentSymbolProvider.ts b/src/features/documentSymbolProvider.ts
index b5ecac1733..a112e8a9a9 100644
--- a/src/features/documentSymbolProvider.ts
+++ b/src/features/documentSymbolProvider.ts
@@ -14,7 +14,7 @@ export default class OmnisharpDocumentSymbolProvider extends AbstractSupport imp
public provideDocumentSymbols(document: TextDocument, token: CancellationToken): Promise {
- return serverUtils.currentFileMembersAsTree(this._server, { Filename: document.fileName }, token).then(tree => {
+ return serverUtils.currentFileMembersAsTree(this._server, { FileName: document.fileName }, token).then(tree => {
let ret: SymbolInformation[] = [];
for (let node of tree.TopLevelTypeDefinitions) {
toDocumentSymbol(ret, node);
diff --git a/src/features/dotnetTest.ts b/src/features/dotnetTest.ts
index 8a1db959b1..aea373f4ea 100644
--- a/src/features/dotnetTest.ts
+++ b/src/features/dotnetTest.ts
@@ -3,13 +3,16 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-'use strict';
-
-import {OmniSharpServer} from '../omnisharp/server';
-import {toRange} from '../omnisharp/typeConvertion';
+import { OmniSharpServer } from '../omnisharp/server';
+import { toRange } from '../omnisharp/typeConvertion';
+import { DebuggerEventsProtocol } from '../coreclr-debug/debuggerEventsProtocol';
import * as vscode from 'vscode';
import * as serverUtils from "../omnisharp/utils";
import * as protocol from '../omnisharp/protocol';
+import * as utils from '../common';
+import * as net from 'net';
+import * as os from 'os';
+import * as path from 'path';
let _testOutputChannel: vscode.OutputChannel = undefined;
@@ -35,41 +38,123 @@ export function registerDotNetTestDebugCommand(server: OmniSharpServer): vscode.
// Run test through dotnet-test command. This function can be moved to a separate structure
export function runDotnetTest(testMethod: string, fileName: string, testFrameworkName: string, server: OmniSharpServer) {
- getTestOutputChannel().show();
- getTestOutputChannel().appendLine('Running test ' + testMethod + '...');
- serverUtils
- .runDotNetTest(server, { FileName: fileName, MethodName: testMethod, TestFrameworkName: testFrameworkName })
- .then(
- response => {
+ const output = getTestOutputChannel();
+
+ output.show();
+ output.appendLine(`Running test ${testMethod}...`);
+
+ const disposable = server.onTestMessage(e => {
+ output.appendLine(e.Message);
+ });
+
+ const request: protocol.V2.RunTestRequest = {
+ FileName: fileName,
+ MethodName: testMethod,
+ TestFrameworkName: testFrameworkName
+ };
+
+ serverUtils.runTest(server, request)
+ .then(response => {
if (response.Pass) {
- getTestOutputChannel().appendLine('Test passed \n');
+ output.appendLine('Test passed \n');
}
else {
- getTestOutputChannel().appendLine('Test failed \n');
+ output.appendLine('Test failed \n');
}
+
+ disposable.dispose();
},
reason => {
vscode.window.showErrorMessage(`Failed to run test because ${reason}.`);
+ disposable.dispose();
});
}
+function createLaunchConfiguration(program: string, argsString: string, cwd: string, debuggerEventsPipeName: string) {
+ let args = utils.splitCommandLineArgs(argsString);
+
+ return {
+ // NOTE: uncomment this for vsdbg developement
+ // debugServer: 4711,
+ name: ".NET Test Launch",
+ type: "coreclr",
+ request: "launch",
+ debuggerEventsPipeName: debuggerEventsPipeName,
+ program,
+ args,
+ cwd,
+ stopAtEntry: true
+ };
+}
+
+function getLaunchConfigurationForVSTest(server: OmniSharpServer, fileName: string, testMethod: string, testFrameworkName: string, debugEventListener: DebugEventListener): Promise {
+
+ const request: protocol.V2.DebugTestGetStartInfoRequest = {
+ FileName: fileName,
+ MethodName: testMethod,
+ TestFrameworkName: testFrameworkName
+ };
+
+ return serverUtils.debugTestGetStartInfo(server, request)
+ .then(response => createLaunchConfiguration(response.FileName, response.Arguments, response.WorkingDirectory, debugEventListener.pipePath()));
+}
+
+function getLaunchConfigurationForLegacy(server: OmniSharpServer, fileName: string, testMethod: string, testFrameworkName: string): Promise {
+ const request: protocol.V2.GetTestStartInfoRequest = {
+ FileName: fileName,
+ MethodName: testMethod,
+ TestFrameworkName: testFrameworkName
+ };
+
+ return serverUtils.getTestStartInfo(server, request)
+ .then(response => createLaunchConfiguration(response.Executable, response.Argument, response.WorkingDirectory, null));
+}
+
+
+function getLaunchConfiguration(server: OmniSharpServer, debugType: string, fileName: string, testMethod: string, testFrameworkName: string, debugEventListener: DebugEventListener): Promise {
+ switch (debugType) {
+ case "legacy":
+ return getLaunchConfigurationForLegacy(server, fileName, testMethod, testFrameworkName);
+ case "vstest":
+ return getLaunchConfigurationForVSTest(server, fileName, testMethod, testFrameworkName, debugEventListener);
+
+ default:
+ throw new Error(`Unexpected debug type: ${debugType}`);
+ }
+}
+
// Run test through dotnet-test command with debugger attached
export function debugDotnetTest(testMethod: string, fileName: string, testFrameworkName: string, server: OmniSharpServer) {
- serverUtils.getTestStartInfo(server, { FileName: fileName, MethodName: testMethod, TestFrameworkName: testFrameworkName }).then(response => {
- vscode.commands.executeCommand(
- 'vscode.startDebug', {
- "name": ".NET test launch",
- "type": "coreclr",
- "request": "launch",
- "program": response.Executable,
- "args": response.Argument.split(' '),
- "cwd": "${workspaceRoot}",
- "stopAtEntry": false
+ // We support to styles of 'dotnet test' for debugging: The legacy 'project.json' testing, and the newer csproj support
+ // using VS Test. These require a different level of communication.
+ let debugType: string;
+ let debugEventListener: DebugEventListener = null;
+ let outputChannel = getTestOutputChannel();
+ outputChannel.appendLine(`Debugging method '${testMethod}'.`);
+
+ return serverUtils.requestProjectInformation(server, { FileName: fileName })
+ .then(projectInfo => {
+ if (projectInfo.DotNetProject) {
+ debugType = "legacy";
+ return Promise.resolve();
}
- ).then(
- response => { },
- reason => { vscode.window.showErrorMessage(`Failed to start debugger on test because ${reason}.`); });
- });
+ else if (projectInfo.MsBuildProject) {
+ debugType = "vstest";
+ debugEventListener = new DebugEventListener(fileName, server, outputChannel);
+ return debugEventListener.start();
+ }
+ else {
+ throw new Error();
+ }
+ })
+ .then(() => getLaunchConfiguration(server, debugType, fileName, testMethod, testFrameworkName, debugEventListener))
+ .then(config => vscode.commands.executeCommand('vscode.startDebug', config))
+ .catch(reason => {
+ vscode.window.showErrorMessage(`Failed to start debugger: ${reason}`);
+ if (debugEventListener != null) {
+ debugEventListener.close();
+ }
+ });
}
export function updateCodeLensForTest(bucket: vscode.CodeLens[], fileName: string, node: protocol.Node, isDebugEnable: boolean) {
@@ -92,4 +177,144 @@ export function updateCodeLensForTest(bucket: vscode.CodeLens[], fileName: strin
{ title: "debug test", command: 'dotnet.test.debug', arguments: [testFeature.Data, fileName, testFrameworkName] }));
}
}
+}
+
+class DebugEventListener {
+ static s_activeInstance: DebugEventListener = null;
+ _fileName: string;
+ _server: OmniSharpServer;
+ _outputChannel: vscode.OutputChannel;
+ _pipePath: string;
+
+ _serverSocket: net.Server;
+ _isClosed: boolean = false;
+
+ constructor(fileName: string, server: OmniSharpServer, outputChannel: vscode.OutputChannel) {
+ this._fileName = fileName;
+ this._server = server;
+ this._outputChannel = outputChannel;
+ // NOTE: The max pipe name on OSX is fairly small, so this name shouldn't bee too long.
+ const pipeSuffix = "TestDebugEvents-" + process.pid;
+ if (os.platform() === 'win32') {
+ this._pipePath = "\\\\.\\pipe\\Microsoft.VSCode.CSharpExt." + pipeSuffix;
+ } else {
+ this._pipePath = path.join(utils.getExtensionPath(), "." + pipeSuffix);
+ }
+ }
+
+ public start(): Promise {
+
+ // We use our process id as part of the pipe name, so if we still somehow have an old instance running, close it.
+ if (DebugEventListener.s_activeInstance !== null) {
+ DebugEventListener.s_activeInstance.close();
+ }
+ DebugEventListener.s_activeInstance = this;
+
+ this._serverSocket = net.createServer((socket: net.Socket) => {
+ socket.on('data', (buffer: Buffer) => {
+ let event: DebuggerEventsProtocol.DebuggerEvent;
+ try {
+ event = DebuggerEventsProtocol.decodePacket(buffer);
+ }
+ catch (e) {
+ this._outputChannel.appendLine("Warning: Invalid event received from debugger");
+ return;
+ }
+
+ switch (event.eventType) {
+ case DebuggerEventsProtocol.EventType.ProcessLaunched:
+ let processLaunchedEvent = (event);
+ this._outputChannel.appendLine(`Started debugging process #${processLaunchedEvent.targetProcessId}.`);
+ this.onProcessLaunched(processLaunchedEvent.targetProcessId);
+ break;
+
+ case DebuggerEventsProtocol.EventType.DebuggingStopped:
+ this._outputChannel.appendLine("Debugging complete.\n");
+ this.onDebuggingStopped();
+ break;
+ }
+ });
+
+ socket.on('end', () => {
+ this.onDebuggingStopped();
+ });
+ });
+
+ return this.removeSocketFileIfExists().then(() => {
+ return new Promise((resolve, reject) => {
+ let isStarted: boolean = false;
+ this._serverSocket.on('error', (err: Error) => {
+ if (!isStarted) {
+ reject(err.message);
+ } else {
+ this._outputChannel.appendLine("Warning: Communications error on debugger event channel. " + err.message);
+ }
+ });
+ this._serverSocket.listen(this._pipePath, () => {
+ isStarted = true;
+ resolve();
+ });
+ });
+ });
+ }
+
+ public pipePath(): string {
+ return this._pipePath;
+ }
+
+ public close() {
+ if (this === DebugEventListener.s_activeInstance) {
+ DebugEventListener.s_activeInstance = null;
+ }
+
+ if (this._isClosed) {
+ return;
+ }
+
+ this._isClosed = true;
+
+ if (this._serverSocket !== null) {
+ this._serverSocket.close();
+ }
+ }
+
+ private onProcessLaunched(targetProcessId: number): void {
+ let request: protocol.V2.DebugTestLaunchRequest = {
+ FileName: this._fileName,
+ TargetProcessId: targetProcessId
+ };
+
+ const disposable = this._server.onTestMessage(e => {
+ this._outputChannel.appendLine(e.Message);
+ });
+
+ serverUtils.debugTestLaunch(this._server, request)
+ .then(_ => {
+ disposable.dispose();
+ });
+ }
+
+ private onDebuggingStopped(): void {
+ if (this._isClosed) {
+ return;
+ }
+
+ let request: protocol.V2.DebugTestStopRequest = {
+ FileName: this._fileName
+ };
+
+ serverUtils.debugTestStop(this._server, request);
+
+ this.close();
+ }
+
+ private removeSocketFileIfExists(): Promise {
+ if (os.platform() === 'win32') {
+ // Win32 doesn't use the file system for pipe names
+ return Promise.resolve();
+ }
+ else {
+ return utils.deleteIfExists(this._pipePath);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/features/formattingEditProvider.ts b/src/features/formattingEditProvider.ts
index 4719c1ac19..e5eabd4870 100644
--- a/src/features/formattingEditProvider.ts
+++ b/src/features/formattingEditProvider.ts
@@ -15,7 +15,7 @@ export default class FormattingSupport extends AbstractSupport implements Docume
public provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): Promise {
let request = {
- Filename: document.fileName,
+ FileName: document.fileName,
Line: range.start.line + 1,
Column: range.start.character + 1,
EndLine: range.end.line + 1,
@@ -32,7 +32,7 @@ export default class FormattingSupport extends AbstractSupport implements Docume
public provideOnTypeFormattingEdits(document: TextDocument, position: Position, ch: string, options: FormattingOptions, token: CancellationToken): Promise {
let request = {
- Filename: document.fileName,
+ FileName: document.fileName,
Line: position.line + 1,
Column: position.character + 1,
Character: ch
diff --git a/src/features/json/jsonContributions.ts b/src/features/json/jsonContributions.ts
index 055549c761..2bb4dd3a52 100644
--- a/src/features/json/jsonContributions.ts
+++ b/src/features/json/jsonContributions.ts
@@ -45,7 +45,8 @@ export function addJSONProviders(): Disposable {
let contributions = [new ProjectJSONContribution(xhr)];
contributions.forEach(contribution => {
let selector = contribution.getDocumentSelector();
- subscriptions.push(languages.registerCompletionItemProvider(selector, new JSONCompletionItemProvider(contribution)));
+ let triggerCharacters = ['"', ':', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
+ subscriptions.push(languages.registerCompletionItemProvider(selector, new JSONCompletionItemProvider(contribution), ...triggerCharacters));
subscriptions.push(languages.registerHoverProvider(selector, new JSONHoverProvider(contribution)));
});
diff --git a/src/features/json/projectJSONContribution.ts b/src/features/json/projectJSONContribution.ts
index 5d69bbcb41..2d11457f46 100644
--- a/src/features/json/projectJSONContribution.ts
+++ b/src/features/json/projectJSONContribution.ts
@@ -105,6 +105,7 @@ export class ProjectJSONContribution implements IJSONContribution {
let proposal = new CompletionItem(name);
proposal.kind = CompletionItemKind.Property;
proposal.insertText = insertText;
+ proposal.filterText = JSON.stringify(name);
result.add(proposal);
}
if (results.length === LIMIT) {
diff --git a/src/features/processPicker.ts b/src/features/processPicker.ts
index f04e39b450..b7c4737ce0 100644
--- a/src/features/processPicker.ts
+++ b/src/features/processPicker.ts
@@ -5,6 +5,9 @@
import * as os from 'os';
import * as vscode from 'vscode';
+import { getExtensionPath } from '../common';
+import * as fs from 'fs-extra';
+import * as path from 'path';
import * as child_process from 'child_process';
import { PlatformInformation } from '../platform';
@@ -43,6 +46,35 @@ export class RemoteAttachPicker {
private static _channel: vscode.OutputChannel = null;
+ public static ValidateAndFixPipeProgram(program: string): Promise {
+ return PlatformInformation.GetCurrent().then(platformInfo => {
+ // Check if we are on a 64 bit Windows
+ if (platformInfo.isWindows() && platformInfo.architecture === "x86_64") {
+ let sysRoot: string = process.env.SystemRoot;
+ let oldPath = path.join(sysRoot, 'System32');
+ let newPath = path.join(sysRoot, 'sysnative');
+
+ // Escape backslashes, replace and ignore casing
+ let regex = RegExp(oldPath.replace(/\\/g, '\\\\'), "ig");
+
+ // Replace System32 with sysnative
+ let newProgram = program.replace(regex, newPath);
+
+ // Check if program strong contains System32 directory.
+ // And if the program does not exist in System32, but it does in sysnative.
+ // Return sysnative program
+ if (program.toLowerCase().startsWith(oldPath.toLowerCase()) &&
+ !fs.existsSync(program) && fs.existsSync(newProgram)) {
+
+ return newProgram;
+ }
+ }
+
+ // Return original program and let it fall through
+ return program;
+ });
+ }
+
public static ShowAttachEntries(args: any): Promise {
// Create remote attach output channel for errors.
if (!RemoteAttachPicker._channel) {
@@ -65,25 +97,49 @@ export class RemoteAttachPicker {
return Promise.reject(new Error("Configuration \"" + name + "\" in launch.json does not have a " +
"pipeTransport argument with debuggerPath for pickRemoteProcess. Use pickProcess for local attach."));
} else {
- let pipeProgram = args.pipeTransport.pipeProgram;
- let pipeArgs = args.pipeTransport.pipeArgs;
- let platformSpecificPipeTransportOptions = RemoteAttachPicker.getPlatformSpecificPipeTransportOptions(args);
+ let pipeProgram: string = args.pipeTransport.pipeProgram;
+ let pipeArgs: string[] = args.pipeTransport.pipeArgs;
+ let quoteArgs: boolean = args.pipeTransport.quoteArgs != null ? args.pipeTransport.quoteArgs : true; // default value is true
+ let platformSpecificPipeTransportOptions: any = RemoteAttachPicker.getPlatformSpecificPipeTransportOptions(args);
if (platformSpecificPipeTransportOptions) {
pipeProgram = platformSpecificPipeTransportOptions.pipeProgram || pipeProgram;
pipeArgs = platformSpecificPipeTransportOptions.pipeArgs || pipeArgs;
+ quoteArgs = platformSpecificPipeTransportOptions.pipeTransport.quoteArgs != null ? platformSpecificPipeTransportOptions.pipeTransport.quoteArgs : quoteArgs;
}
- let argList = RemoteAttachPicker.createArgumentList(pipeArgs);
- let pipeCmd: string = `"${pipeProgram}" ${argList}`;
- return RemoteAttachPicker.getRemoteOSAndProcesses(pipeCmd).then(processes => {
- let attachPickOptions: vscode.QuickPickOptions = {
- matchOnDescription: true,
- matchOnDetail: true,
- placeHolder: "Select the process to attach to"
- };
- return vscode.window.showQuickPick(processes, attachPickOptions).then(item => {
- return item ? item.id : Promise.reject(new Error("Could not find a process id to attach."));
+ return RemoteAttachPicker.ValidateAndFixPipeProgram(pipeProgram).then(pipeProgram => {
+ let pipeCmdList: string[] = [];
+ let scriptShellCmd: string = "sh -s";
+ pipeCmdList.push(pipeProgram);
+
+ const debuggerCommandString: string = "${debuggerCommand}";
+
+ if (pipeArgs.filter(arg => arg.indexOf(debuggerCommandString) >= 0).length > 0) {
+ for (let arg of pipeArgs) {
+ while (arg.indexOf("${debuggerCommand}") >= 0) {
+ arg = arg.replace("${debuggerCommand}", scriptShellCmd);
+ }
+
+ pipeCmdList.push(arg);
+ }
+ }
+ else {
+ pipeCmdList = pipeCmdList.concat(pipeArgs);
+ pipeCmdList.push(scriptShellCmd);
+ }
+
+ let pipeCmd: string = quoteArgs ? this.createArgumentList(pipeCmdList) : pipeCmdList.join(' ');
+
+ return RemoteAttachPicker.getRemoteOSAndProcesses(pipeCmd).then(processes => {
+ let attachPickOptions: vscode.QuickPickOptions = {
+ matchOnDescription: true,
+ matchOnDetail: true,
+ placeHolder: "Select the process to attach to"
+ };
+ return vscode.window.showQuickPick(processes, attachPickOptions).then(item => {
+ return item ? item.id : Promise.reject(new Error("Could not find a process id to attach."));
+ });
});
});
}
@@ -96,7 +152,13 @@ export class RemoteAttachPicker {
if (ret) {
ret += " ";
}
- ret += `"${arg}"`;
+
+ if (arg.includes(' ')) {
+ ret += `"${arg}"`;
+ }
+ else {
+ ret += `${arg}`;
+ }
}
return ret;
@@ -117,16 +179,9 @@ export class RemoteAttachPicker {
}
public static getRemoteOSAndProcesses(pipeCmd: string): Promise {
-
- // If the client OS is NOT Windows, we need to escape '$(uname)' as otherwise it will be evaluated on the client side
- // If the client OS is Windows, we don't want to do that as it will be sent as-is, and thus the if statement will be broken
- const remoteUnameCommand = os.platform() !== "win32" ? "$\\(uname\\)" : "$(uname)";
+ const scriptPath = path.join(getExtensionPath(), 'scripts', 'remoteProcessPickerScript');
- // Commands to get OS and processes
- const command = `bash -c 'uname && if [ "${remoteUnameCommand}" == "Linux" ] ; then ${RemoteAttachPicker.linuxPsCommand} ; elif [ "${remoteUnameCommand}" == "Darwin" ] ; ` +
- `then ${RemoteAttachPicker.osxPsCommand}; fi'`;
-
- return execChildProcessAndOutputErrorToChannel(`${pipeCmd} "${command}"`, null, RemoteAttachPicker._channel).then(output => {
+ return execChildProcessAndOutputErrorToChannel(`${pipeCmd} < ${scriptPath}`, null, RemoteAttachPicker._channel).then(output => {
// OS will be on first line
// Processess will follow if listed
let lines = output.split(/\r?\n/);
@@ -398,36 +453,36 @@ function execChildProcess(process: string, workingDirectory: string): Promise {
- return PlatformInformation.GetCurrent().then(platformInfo => {
+function GetSysNativePathIfNeeded(): Promise {
+ return PlatformInformation.GetCurrent().then(platformInfo => {
let env = process.env;
- if (platformInfo.isWindows && platformInfo.architecture === "x86_64") {
- let sysnative : String = process.env.WINDIR + "\\sysnative";
- env.Path = process.env.PATH + ";" + sysnative;
+ if (platformInfo.isWindows() && platformInfo.architecture === "x86_64") {
+ let sysnative: String = process.env.WINDIR + "\\sysnative";
+ env.Path = process.env.PATH + ";" + sysnative;
}
-
+
return env;
});
}
function execChildProcessAndOutputErrorToChannel(process: string, workingDirectory: string, channel: vscode.OutputChannel): Promise {
- channel.appendLine(`Executing: ${process}`);
+ channel.appendLine(`Executing: ${process}`);
return new Promise((resolve, reject) => {
return GetSysNativePathIfNeeded().then(newEnv => {
child_process.exec(process, { cwd: workingDirectory, env: newEnv, maxBuffer: 500 * 1024 }, (error: Error, stdout: string, stderr: string) => {
let channelOutput = "";
-
+
if (stdout && stdout.length > 0) {
- channelOutput.concat(stdout);
+ channelOutput = channelOutput.concat(stdout);
}
if (stderr && stderr.length > 0) {
- channelOutput.concat(stderr);
+ channelOutput = channelOutput.concat("stderr: " + stderr);
}
if (error) {
- channelOutput.concat(error.message);
+ channelOutput = channelOutput.concat("Error Message: " + error.message);
}
diff --git a/src/features/workspaceSymbolProvider.ts b/src/features/workspaceSymbolProvider.ts
index 749e17fdbf..2d0b33ab62 100644
--- a/src/features/workspaceSymbolProvider.ts
+++ b/src/features/workspaceSymbolProvider.ts
@@ -16,7 +16,7 @@ export default class OmnisharpWorkspaceSymbolProvider extends AbstractSupport im
public provideWorkspaceSymbols(search: string, token: CancellationToken): Promise {
- return serverUtils.findSymbols(this._server, { Filter: search, Filename: '' }, token).then(res => {
+ return serverUtils.findSymbols(this._server, { Filter: search, FileName: '' }, token).then(res => {
if (res && Array.isArray(res.QuickFixes)) {
return res.QuickFixes.map(OmnisharpWorkspaceSymbolProvider._asSymbolInformation);
}
diff --git a/src/main.ts b/src/main.ts
index 5d67777820..1e73f241e7 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -10,8 +10,7 @@ import * as coreclrdebug from './coreclr-debug/activate';
import * as OmniSharp from './omnisharp/extension';
import * as util from './common';
import { Logger } from './logger';
-import { PackageManager, Status, PackageError } from './packages';
-import { PlatformInformation } from './platform';
+import { CSharpExtDownloader } from './CSharpExtDownloader';
import { addJSONProviders } from './features/json/jsonContributions';
let _channel: vscode.OutputChannel = null;
@@ -31,139 +30,29 @@ export function activate(context: vscode.ExtensionContext): any {
let logger = new Logger(text => _channel.append(text));
ensureRuntimeDependencies(extension, logger, reporter)
- .then(() => {
+ .then((success : boolean) => {
// activate language services
OmniSharp.activate(context, reporter);
// register JSON completion & hover providers for project.json
context.subscriptions.push(addJSONProviders());
- // activate coreclr-debug
- coreclrdebug.activate(context, reporter, logger, _channel);
+ if (success) {
+ // activate coreclr-debug
+ coreclrdebug.activate(extension, context, reporter, logger, _channel);
+ }
});
}
-function ensureRuntimeDependencies(extension: vscode.Extension, logger: Logger, reporter: TelemetryReporter): Promise {
+function ensureRuntimeDependencies(extension: vscode.Extension, logger: Logger, reporter: TelemetryReporter): Promise {
return util.installFileExists(util.InstallFileType.Lock)
.then(exists => {
if (!exists) {
- return util.touchInstallFile(util.InstallFileType.Begin).then(() => {
- return installRuntimeDependencies(extension, logger, reporter);
- });
- }
- });
-}
-
-function installRuntimeDependencies(extension: vscode.Extension, logger: Logger, reporter: TelemetryReporter): Promise {
- logger.append('Updating C# dependencies...');
- _channel.show();
-
- let statusItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right);
- let status: Status = {
- setMessage: text => {
- statusItem.text = text;
- statusItem.show();
- },
- setDetail: text => {
- statusItem.tooltip = text;
- statusItem.show();
- }
- };
-
- // Sends "AcquisitionStart" telemetry to indicate an acquisition started.
- reporter.sendTelemetryEvent("AcquisitionStart");
-
- let platformInfo: PlatformInformation;
- let packageManager: PackageManager;
- let installationStage = 'touchBeginFile';
- let errorMessage = '';
-
- let telemetryProps: any = {};
-
- return util.touchInstallFile(util.InstallFileType.Begin)
- .then(() => {
- installationStage = 'getPlatformInfo';
- return PlatformInformation.GetCurrent();
- })
- .then(info => {
- platformInfo = info;
- packageManager = new PackageManager(info, extension.packageJSON);
- logger.appendLine();
-
- // Display platform information and RID followed by a blank line
- logger.append(`Platform: ${info.toString()}`);
- if (info.runtimeId) {
- logger.appendLine(` (${info.runtimeId})`);
- }
- else {
- logger.appendLine();
- }
- logger.appendLine();
-
- installationStage = 'downloadPackages';
-
- const config = vscode.workspace.getConfiguration();
- const proxy = config.get('http.proxy');
- const strictSSL = config.get('http.proxyStrictSSL', true);
-
- return packageManager.DownloadPackages(logger, status, proxy, strictSSL);
- })
- .then(() => {
- logger.appendLine();
-
- installationStage = 'installPackages';
- return packageManager.InstallPackages(logger, status);
- })
- .then(() => {
- installationStage = 'touchLockFile';
- return util.touchInstallFile(util.InstallFileType.Lock);
- })
- .then(() => {
- installationStage = 'completeSuccess';
- })
- .catch(error => {
- if (error instanceof PackageError) {
- // we can log the message in a PackageError to telemetry as we do not put PII in PackageError messages
- telemetryProps['error.message'] = error.message;
-
- if (error.innerError) {
- errorMessage = error.innerError.toString();
- } else {
- errorMessage = error.message;
- }
-
- if (error.pkg) {
- telemetryProps['error.packageUrl'] = error.pkg.url;
- }
-
+ const downloader = new CSharpExtDownloader(_channel, logger, reporter, extension.packageJSON);
+ return downloader.installRuntimeDependencies();
} else {
- // do not log raw errorMessage in telemetry as it is likely to contain PII.
- errorMessage = error.toString();
+ return true;
}
-
- logger.appendLine(`Failed at stage: ${installationStage}`);
- logger.appendLine(errorMessage);
- })
- .then(() => {
- telemetryProps['installStage'] = installationStage;
- telemetryProps['platform.architecture'] = platformInfo.architecture;
- telemetryProps['platform.platform'] = platformInfo.platform;
- telemetryProps['platform.runtimeId'] = platformInfo.runtimeId;
- if (platformInfo.distribution) {
- telemetryProps['platform.distribution'] = platformInfo.distribution.toString();
- }
-
- reporter.sendTelemetryEvent('Acquisition', telemetryProps);
-
- logger.appendLine();
- installationStage = '';
- logger.appendLine('Finished');
-
- statusItem.dispose();
- })
- .then(() => {
- // We do this step at the end so that we clean up the begin file in the case that we hit above catch block
- // Attach a an empty catch to this so that errors here do not propogate
- return util.deleteInstallFile(util.InstallFileType.Begin).catch((error) => { });
});
}
+
diff --git a/src/omnisharp/options.ts b/src/omnisharp/options.ts
index 2f62130c51..5be1291373 100644
--- a/src/omnisharp/options.ts
+++ b/src/omnisharp/options.ts
@@ -9,6 +9,7 @@ export class Options {
constructor(
public path?: string,
public useMono?: boolean,
+ public waitForDebugger?: boolean,
public loggingLevel?: string,
public autoStart?: boolean,
public projectLoadTimeout?: number,
@@ -33,6 +34,8 @@ export class Options {
? csharpConfig.get('omnisharpUsesMono')
: omnisharpConfig.get('useMono');
+ const waitForDebugger = omnisharpConfig.get('waitForDebugger', false);
+
// support the legacy "verbose" level as "debug"
let loggingLevel = omnisharpConfig.get('loggingLevel');
if (loggingLevel.toLowerCase() === 'verbose') {
@@ -45,6 +48,6 @@ export class Options {
const maxProjectResults = omnisharpConfig.get('maxProjectResults', 250);
const useEditorFormattingSettings = omnisharpConfig.get('useEditorFormattingSettings', true);
- return new Options(path, useMono, loggingLevel, autoStart, projectLoadTimeout, maxProjectResults, useEditorFormattingSettings);
+ return new Options(path, useMono, waitForDebugger, loggingLevel, autoStart, projectLoadTimeout, maxProjectResults, useEditorFormattingSettings);
}
}
\ No newline at end of file
diff --git a/src/omnisharp/protocol.ts b/src/omnisharp/protocol.ts
index 0e33cfe64c..2e03c51d34 100644
--- a/src/omnisharp/protocol.ts
+++ b/src/omnisharp/protocol.ts
@@ -22,6 +22,7 @@ export module Requests {
export const GetCodeActions = '/getcodeactions';
export const GoToDefinition = '/gotoDefinition';
export const FindImplementations = '/findimplementations';
+ export const Project = '/project';
export const Projects = '/projects';
export const RemoveFromProject = '/removefromproject';
export const Rename = '/rename';
@@ -59,20 +60,18 @@ export namespace WireProtocol {
}
export interface Request {
- Filename: string;
+ FileName: string;
Line?: number;
Column?: number;
Buffer?: string;
Changes?: LinePositionSpanTextChange[];
}
-export interface GoToDefinitionRequest extends Request
-{
+export interface GoToDefinitionRequest extends Request {
WantMetadata?: boolean;
}
-export interface FindImplementationsRequest extends Request
-{
+export interface FindImplementationsRequest extends Request {
}
export interface LinePositionSpanTextChange {
@@ -419,7 +418,10 @@ export namespace V2 {
export const GetCodeActions = '/v2/getcodeactions';
export const RunCodeAction = '/v2/runcodeaction';
export const GetTestStartInfo = '/v2/getteststartinfo';
- export const RunDotNetTest = '/v2/runtest';
+ export const RunTest = '/v2/runtest';
+ export const DebugTestGetStartInfo = '/v2/debugtest/getstartinfo';
+ export const DebugTestLaunch = '/v2/debugtest/launch';
+ export const DebugTestStop = '/v2/debugtest/stop';
}
export interface Point {
@@ -495,8 +497,32 @@ export namespace V2 {
}
// dotnet-test endpoints
- export interface GetTestStartInfoRequest {
+ export interface DebugTestGetStartInfoRequest extends Request {
+ MethodName: string;
+ TestFrameworkName: string;
+ }
+
+ export interface DebugTestGetStartInfoResponse {
FileName: string;
+ Arguments: string;
+ WorkingDirectory: string;
+ EnvironmentVariables: Map;
+ }
+
+ export interface DebugTestLaunchRequest extends Request {
+ TargetProcessId: number;
+ }
+
+ export interface DebugTestLaunchResponse {
+ }
+
+ export interface DebugTestStopRequest extends Request {
+ }
+
+ export interface DebugTestStopResponse {
+ }
+
+ export interface GetTestStartInfoRequest extends Request {
MethodName: string;
TestFrameworkName: string;
}
@@ -504,18 +530,30 @@ export namespace V2 {
export interface GetTestStartInfoResponse {
Executable: string;
Argument: string;
+ WorkingDirectory: string;
}
- export interface RunDotNetTestRequest {
- FileName: string;
+ export interface RunTestRequest extends Request {
MethodName: string;
TestFrameworkName: string;
}
- export interface RunDotNetTestResponse {
+ export interface DotNetResult {
+ MethodName: string;
+ Outcome: string;
+ ErrorMessage: string;
+ ErrorStackTrace: string;
+ }
+
+ export interface RunTestResponse {
Failure: string;
Pass: boolean;
}
+
+ export interface TestMessageEvent {
+ MessageLevel: string;
+ Message: string;
+ }
}
export function findNetCoreAppTargetFramework(project: MSBuildProject): TargetFramework {
diff --git a/src/omnisharp/server.ts b/src/omnisharp/server.ts
index e8c8f9f67f..a9c4fc0ab3 100644
--- a/src/omnisharp/server.ts
+++ b/src/omnisharp/server.ts
@@ -43,6 +43,8 @@ module Events {
export const MsBuildProjectDiagnostics = 'MsBuildProjectDiagnostics';
+ export const TestMessage = 'TestMessage';
+
export const BeforeServerInstall = 'BeforeServerInstall';
export const BeforeServerStart = 'BeforeServerStart';
export const ServerStart = 'ServerStart';
@@ -193,6 +195,10 @@ export class OmniSharpServer {
return this._addListener(Events.MsBuildProjectDiagnostics, listener, thisArg);
}
+ public onTestMessage(listener: (e: protocol.V2.TestMessageEvent) => any, thisArg?: any) {
+ return this._addListener(Events.TestMessage, listener, thisArg);
+ }
+
public onBeforeServerInstall(listener: () => any) {
return this._addListener(Events.BeforeServerInstall, listener);
}
@@ -246,6 +252,10 @@ export class OmniSharpServer {
'--loglevel', this._options.loggingLevel
];
+ if (this._options.waitForDebugger === true) {
+ args.push('--debug');
+ }
+
this._logger.appendLine(`Starting OmniSharp server at ${new Date().toLocaleString()}`);
this._logger.increaseIndent();
this._logger.appendLine(`Target: ${solutionPath}`);
diff --git a/src/omnisharp/typeConvertion.ts b/src/omnisharp/typeConvertion.ts
index 520afbd974..4be52fa76d 100644
--- a/src/omnisharp/typeConvertion.ts
+++ b/src/omnisharp/typeConvertion.ts
@@ -45,7 +45,7 @@ export function createRequest(document: vscode.TextD
}
let request: protocol.Request = {
- Filename: document.fileName,
+ FileName: document.fileName,
Buffer: includeBuffer ? document.getText() : undefined,
Line,
Column
diff --git a/src/omnisharp/utils.ts b/src/omnisharp/utils.ts
index 3506ce7694..6ae0391d0e 100644
--- a/src/omnisharp/utils.ts
+++ b/src/omnisharp/utils.ts
@@ -5,7 +5,7 @@
'use strict';
-import {OmniSharpServer} from './server';
+import { OmniSharpServer } from './server';
import * as protocol from './protocol';
import * as vscode from 'vscode';
@@ -57,6 +57,10 @@ export function rename(server: OmniSharpServer, request: protocol.RenameRequest,
return server.makeRequest(protocol.Requests.Rename, request, token);
}
+export function requestProjectInformation(server: OmniSharpServer, request: protocol.Request) {
+ return server.makeRequest(protocol.Requests.Project, request);
+}
+
export function requestWorkspaceInformation(server: OmniSharpServer) {
return server.makeRequest(protocol.Requests.Projects);
}
@@ -85,8 +89,20 @@ export function getTestStartInfo(server: OmniSharpServer, request: protocol.V2.G
return server.makeRequest(protocol.V2.Requests.GetTestStartInfo, request);
}
-export function runDotNetTest(server: OmniSharpServer, request: protocol.V2.RunDotNetTestRequest) {
- return server.makeRequest(protocol.V2.Requests.RunDotNetTest, request);
+export function runTest(server: OmniSharpServer, request: protocol.V2.RunTestRequest) {
+ return server.makeRequest(protocol.V2.Requests.RunTest, request);
+}
+
+export function debugTestGetStartInfo(server: OmniSharpServer, request: protocol.V2.DebugTestGetStartInfoRequest) {
+ return server.makeRequest(protocol.V2.Requests.DebugTestGetStartInfo, request);
+}
+
+export function debugTestLaunch(server: OmniSharpServer, request: protocol.V2.DebugTestLaunchRequest) {
+ return server.makeRequest(protocol.V2.Requests.DebugTestLaunch, request);
+}
+
+export function debugTestStop(server: OmniSharpServer, request: protocol.V2.DebugTestStopRequest) {
+ return server.makeRequest(protocol.V2.Requests.DebugTestStop, request);
}
export function isNetCoreProject(project: protocol.MSBuildProject) {
diff --git a/src/packages.ts b/src/packages.ts
index b899d3051d..38938c265b 100644
--- a/src/packages.ts
+++ b/src/packages.ts
@@ -25,6 +25,9 @@ export interface Package {
architectures: string[];
binaries: string[];
tmpFile: tmp.SynchrounousResult;
+
+ // Path to use to test if the package has already been installed
+ installTestPath?: string;
}
export interface Status {
@@ -55,7 +58,7 @@ export class PackageManager {
public DownloadPackages(logger: Logger, status: Status, proxy: string, strictSSL: boolean): Promise {
return this.GetPackages()
.then(packages => {
- return util.buildPromiseChain(packages, pkg => downloadPackage(pkg, logger, status, proxy, strictSSL));
+ return util.buildPromiseChain(packages, pkg => maybeDownloadPackage(pkg, logger, status, proxy, strictSSL));
});
}
@@ -129,6 +132,16 @@ function getNoopStatus(): Status {
};
}
+function maybeDownloadPackage(pkg: Package, logger: Logger, status: Status, proxy: string, strictSSL: boolean): Promise {
+ return doesPackageTestPathExist(pkg).then((exists: boolean) => {
+ if (!exists) {
+ return downloadPackage(pkg, logger, status, proxy, strictSSL);
+ } else {
+ logger.appendLine(`Skipping package '${pkg.description}' (already downloaded).`);
+ }
+ });
+}
+
function downloadPackage(pkg: Package, logger: Logger, status: Status, proxy: string, strictSSL: boolean): Promise {
status = status || getNoopStatus();
@@ -243,6 +256,12 @@ function downloadFile(urlString: string, pkg: Package, logger: Logger, status: S
}
function installPackage(pkg: Package, logger: Logger, status?: Status): Promise {
+
+ if (!pkg.tmpFile) {
+ // Download of this package was skipped, so there is nothing to install
+ return Promise.resolve();
+ }
+
status = status || getNoopStatus();
logger.appendLine(`Installing package '${pkg.description}'`);
@@ -250,8 +269,21 @@ function installPackage(pkg: Package, logger: Logger, status?: Status): Promise<
status.setMessage("$(desktop-download) Installing packages...");
status.setDetail(`Installing package '${pkg.description}'`);
- return new Promise((resolve, reject) => {
- if (!pkg.tmpFile || pkg.tmpFile.fd == 0) {
+ return new Promise((resolve, baseReject) => {
+ const reject = (err) => {
+ // If anything goes wrong with unzip, make sure we delete the test path (if there is one)
+ // so we will retry again later
+ const testPath = getPackageTestPath(pkg);
+ if (testPath) {
+ fs.unlink(testPath, unlinkErr => {
+ baseReject(err);
+ });
+ } else {
+ baseReject(err);
+ }
+ };
+
+ if (pkg.tmpFile.fd == 0) {
return reject(new PackageError('Downloaded file unavailable', pkg));
}
@@ -312,3 +344,20 @@ function installPackage(pkg: Package, logger: Logger, status?: Status): Promise<
pkg.tmpFile.removeCallback();
});
}
+
+function doesPackageTestPathExist(pkg: Package) : Promise {
+ const testPath = getPackageTestPath(pkg);
+ if (testPath) {
+ return util.fileExists(testPath);
+ } else {
+ return Promise.resolve(false);
+ }
+}
+
+function getPackageTestPath(pkg: Package) : string {
+ if (pkg.installTestPath) {
+ return path.join(util.getExtensionPath(), pkg.installTestPath);
+ } else {
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/src/platform.ts b/src/platform.ts
index d7a774bf48..0f1c5a7b22 100644
--- a/src/platform.ts
+++ b/src/platform.ts
@@ -91,10 +91,11 @@ export class PlatformInformation {
public constructor(
public platform: string,
public architecture: string,
- public distribution: LinuxDistribution = null)
+ public distribution: LinuxDistribution = null,
+ linuxFallbackRuntimeId: ILinuxRuntimeIdFallback = null)
{
try {
- this.runtimeId = PlatformInformation.getRuntimeId(platform, architecture, distribution);
+ this.runtimeId = PlatformInformation.getRuntimeId(platform, architecture, distribution, linuxFallbackRuntimeId);
}
catch (err) {
this.runtimeId = null;
@@ -135,7 +136,7 @@ export class PlatformInformation {
return result;
}
- public static GetCurrent(): Promise {
+ public static GetCurrent(linuxFallbackRuntimeId: ILinuxRuntimeIdFallback = null): Promise {
let platform = os.platform();
let architecturePromise: Promise;
let distributionPromise: Promise;
@@ -162,7 +163,7 @@ export class PlatformInformation {
return Promise.all([architecturePromise, distributionPromise])
.then(([arch, distro]) => {
- return new PlatformInformation(platform, arch, distro);
+ return new PlatformInformation(platform, arch, distro, linuxFallbackRuntimeId);
});
}
@@ -192,7 +193,7 @@ export class PlatformInformation {
* Returns a supported .NET Core Runtime ID (RID) for the current platform. The list of Runtime IDs
* is available at https://github.com/dotnet/corefx/tree/master/pkg/Microsoft.NETCore.Platforms.
*/
- private static getRuntimeId(platform: string, architecture: string, distribution: LinuxDistribution): string {
+ private static getRuntimeId(platform: string, architecture: string, distribution: LinuxDistribution, linuxFallbackRuntimeId: ILinuxRuntimeIdFallback): string {
// Note: We could do much better here. Currently, we only return a limited number of RIDs that
// are officially supported.
@@ -216,26 +217,37 @@ export class PlatformInformation {
case 'linux':
if (architecture === 'x86_64') {
- const unknown_distribution = 'unknown_distribution';
- const unknown_version = 'unknown_version';
-
// First try the distribution name
- let runtimeId = PlatformInformation.getRuntimeIdHelper(distribution.name, distribution.version);
+ let runtimeId = PlatformInformation.getExactRuntimeId(distribution.name, distribution.version);
+
+ // If we didn't recognize the distribution or version, see if the caller has provided us a fall back value
+ if ((runtimeId === LinuxRuntimeId.unknown_distribution || runtimeId === LinuxRuntimeId.unknown_version) && linuxFallbackRuntimeId !== null)
+ {
+ const fallbackRuntimeValue = linuxFallbackRuntimeId.getFallbackLinuxRuntimeId();
+ if (fallbackRuntimeValue) {
+ runtimeId = fallbackRuntimeValue;
+ }
+ }
+
+ // If we don't have a fallback runtime id, try again with more fuzzy matching
+ if (runtimeId === LinuxRuntimeId.unknown_distribution) {
+ runtimeId = PlatformInformation.getRuntimeIdHelper(distribution.name, distribution.version);
+ }
// If the distribution isn't one that we understand, but the 'ID_LIKE' field has something that we understand, use that
//
// NOTE: 'ID_LIKE' doesn't specify the version of the 'like' OS. So we will use the 'VERSION_ID' value. This will restrict
// how useful ID_LIKE will be since it requires the version numbers to match up, but it is the best we can do.
- if (runtimeId === unknown_distribution && distribution.idLike && distribution.idLike.length > 0) {
+ if (runtimeId === LinuxRuntimeId.unknown_distribution && distribution.idLike && distribution.idLike.length > 0) {
for (let id of distribution.idLike) {
runtimeId = PlatformInformation.getRuntimeIdHelper(id, distribution.version);
- if (runtimeId !== unknown_distribution) {
+ if (runtimeId !== LinuxRuntimeId.unknown_distribution) {
break;
}
}
}
- if (runtimeId !== unknown_distribution && runtimeId !== unknown_version) {
+ if (runtimeId !== LinuxRuntimeId.unknown_distribution && runtimeId !== LinuxRuntimeId.unknown_version) {
return runtimeId;
}
}
@@ -248,98 +260,120 @@ export class PlatformInformation {
// Chances are, VS Code doesn't support these platforms either.
throw Error('Unsupported platform ' + platform);
}
-
- private static getRuntimeIdHelper(distributionName: string, distributionVersion: string): string {
- const unknown_distribution = 'unknown_distribution';
- const unknown_version = 'unknown_version';
-
- const centos_7 = 'centos.7-x64';
- const debian_8 = 'debian.8-x64';
- const fedora_23 = 'fedora.23-x64';
- const fedora_24 = 'fedora.24-x64';
- const opensuse_13_2 = 'opensuse.13.2-x64';
- const opensuse_42_1 = 'opensuse.42.1-x64';
- const rhel_7 = 'rhel.7-x64';
- const ubuntu_14_04 = 'ubuntu.14.04-x64';
- const ubuntu_16_04 = 'ubuntu.16.04-x64';
- const ubuntu_16_10 = 'ubuntu.16.10-x64';
+ private static getExactRuntimeId(distributionName: string, distributionVersion: string): string {
switch (distributionName) {
- case 'Zorin OS':
- case 'zorin': // ID changed in 12.1
- if (distributionVersion === "12") {
- return ubuntu_16_04;
- }
- break;
case 'ubuntu':
if (distributionVersion === "14.04") {
// This also works for Linux Mint
- return ubuntu_14_04;
+ return LinuxRuntimeId.ubuntu_14_04;
}
else if (distributionVersion === "16.04") {
- return ubuntu_16_04;
+ return LinuxRuntimeId.ubuntu_16_04;
}
else if (distributionVersion === "16.10") {
- return ubuntu_16_10;
+ return LinuxRuntimeId.ubuntu_16_10;
}
-
break;
- case 'elementary':
- case 'elementary OS':
- if (distributionVersion.startsWith("0.3")) {
- // Elementary OS 0.3 Freya is binary compatible with Ubuntu 14.04
- return ubuntu_14_04;
- }
- else if (distributionVersion.startsWith("0.4")) {
- // Elementary OS 0.4 Loki is binary compatible with Ubuntu 16.04
- return ubuntu_16_04;
- }
- break;
case 'linuxmint':
if (distributionVersion.startsWith("18")) {
// Linux Mint 18 is binary compatible with Ubuntu 16.04
- return ubuntu_16_04;
+ return LinuxRuntimeId.ubuntu_16_04;
}
break;
+
case 'centos':
case 'ol':
// Oracle Linux is binary compatible with CentOS
- return centos_7;
+ return LinuxRuntimeId.centos_7;
case 'fedora':
if (distributionVersion === "23") {
- return fedora_23;
+ return LinuxRuntimeId.fedora_23;
} else if (distributionVersion === "24") {
- return fedora_24;
+ return LinuxRuntimeId.fedora_24;
}
break;
case 'opensuse':
if (distributionVersion.startsWith("13.")) {
- return opensuse_13_2;
+ return LinuxRuntimeId.opensuse_13_2;
} else if (distributionVersion.startsWith("42.")) {
- return opensuse_42_1;
+ return LinuxRuntimeId.opensuse_42_1;
}
break;
case 'rhel':
- return rhel_7;
+ return LinuxRuntimeId.rhel_7;
case 'debian':
- return debian_8;
+ return LinuxRuntimeId.debian_8;
+ default:
+ return LinuxRuntimeId.unknown_distribution;
+ }
+
+ return LinuxRuntimeId.unknown_version;
+ }
+
+ private static getRuntimeIdHelper(distributionName: string, distributionVersion: string): string {
+
+ const runtimeId: string = PlatformInformation.getExactRuntimeId(distributionName, distributionVersion);
+ if (runtimeId !== LinuxRuntimeId.unknown_distribution) {
+ return runtimeId;
+ }
+
+ switch (distributionName) {
+ case 'Zorin OS':
+ case 'zorin': // ID changed in 12.1
+ if (distributionVersion === "12") {
+ return LinuxRuntimeId.ubuntu_16_04;
+ }
+ break;
+
+ case 'elementary':
+ case 'elementary OS':
+ if (distributionVersion.startsWith("0.3")) {
+ // Elementary OS 0.3 Freya is binary compatible with Ubuntu 14.04
+ return LinuxRuntimeId.ubuntu_14_04;
+ }
+ else if (distributionVersion.startsWith("0.4")) {
+ // Elementary OS 0.4 Loki is binary compatible with Ubuntu 16.04
+ return LinuxRuntimeId.ubuntu_16_04;
+ }
+ break;
+
case 'galliumos':
- if (distributionVersion.startsWith("2.0")) {
- return ubuntu_16_04;
+ if (distributionVersion.startsWith("2.0") || distributionVersion.startsWith("2.1")) {
+ return LinuxRuntimeId.ubuntu_16_04;
}
break;
- case 'arch':
- // NOTE: currently Arch Linux seems to be compatible enough with Ubuntu 16 that this works,
- // though in the future this may need to change as Arch follows a rolling release model.
- return ubuntu_16_04;
+
default:
- return unknown_distribution;
+ return LinuxRuntimeId.unknown_distribution;
}
- return unknown_version;
+ return LinuxRuntimeId.unknown_version;
}
}
+
+class LinuxRuntimeId
+{
+ public static readonly unknown_distribution = 'unknown_distribution';
+ public static readonly unknown_version = 'unknown_version';
+
+ public static readonly centos_7 = 'centos.7-x64';
+ public static readonly debian_8 = 'debian.8-x64';
+ public static readonly fedora_23 = 'fedora.23-x64';
+ public static readonly fedora_24 = 'fedora.24-x64';
+ public static readonly opensuse_13_2 = 'opensuse.13.2-x64';
+ public static readonly opensuse_42_1 = 'opensuse.42.1-x64';
+ public static readonly rhel_7 = 'rhel.7-x64';
+ public static readonly ubuntu_14_04 = 'ubuntu.14.04-x64';
+ public static readonly ubuntu_16_04 = 'ubuntu.16.04-x64';
+ public static readonly ubuntu_16_10 = 'ubuntu.16.10-x64';
+};
+
+export interface ILinuxRuntimeIdFallback
+{
+ getFallbackLinuxRuntimeId() : string;
+}
\ No newline at end of file
diff --git a/src/tools/GenerateOptionsSchema.ts b/src/tools/GenerateOptionsSchema.ts
index 14348c034e..c9aac7545d 100644
--- a/src/tools/GenerateOptionsSchema.ts
+++ b/src/tools/GenerateOptionsSchema.ts
@@ -86,8 +86,13 @@ export function GenerateOptionsSchema() {
schemaJSON.definitions = ReplaceReferences(schemaJSON.definitions, schemaJSON.definitions);
// Hard Code adding in configurationAttributes launch and attach.
+ // .NET Core
packageJSON.contributes.debuggers[0].configurationAttributes.launch = schemaJSON.definitions.LaunchOptions;
packageJSON.contributes.debuggers[0].configurationAttributes.attach = schemaJSON.definitions.AttachOptions;
+ // Full .NET Framework
+ packageJSON.contributes.debuggers[1].configurationAttributes.launch = schemaJSON.definitions.LaunchOptions;
+ packageJSON.contributes.debuggers[1].configurationAttributes.attach = schemaJSON.definitions.AttachOptions;
+
fs.writeFileSync('package.json', JSON.stringify(packageJSON, null, 2));
}
diff --git a/src/tools/OptionsSchema.json b/src/tools/OptionsSchema.json
index 68a8a3ed62..ea916a757a 100644
--- a/src/tools/OptionsSchema.json
+++ b/src/tools/OptionsSchema.json
@@ -32,6 +32,11 @@
},
"default": []
},
+ "quoteArgs": {
+ "type": "boolean",
+ "description": "Should arguments that contain characters that need to be quoted (example: spaces) be quoted? Defaults to 'true'. If set to false, the debugger command will no longer be automatically quoted.",
+ "default": true
+ },
"pipeEnv": {
"type": "object",
"additionalProperties": {
@@ -84,6 +89,11 @@
"description": "Environment variables passed to the pipe program.",
"default": {}
},
+ "quoteArgs": {
+ "type": "boolean",
+ "description": "Should arguments that contain characters that need to be quoted (example: spaces) be quoted? Defaults to 'true'. If set to false, the debugger command will no longer be automatically quoted.",
+ "default": true
+ },
"windows": {
"$ref": "#/definitions/PipeConfigurations",
"description": "Windows-specific pipe launch configuration options",
diff --git a/src/vscodePlatform.ts b/src/vscodePlatform.ts
new file mode 100644
index 0000000000..762eb02aa2
--- /dev/null
+++ b/src/vscodePlatform.ts
@@ -0,0 +1,41 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+'use strict';
+
+import * as vscode from 'vscode';
+import { PlatformInformation, ILinuxRuntimeIdFallback } from './platform';
+
+/*
+ * extension to the PlatformInformation that calls VS Code APIs in order to obtain the runtime id
+ * for distributions that the extension doesn't understand
+ */
+export class VSCodePlatformInformation
+{
+ public static GetCurrent(): Promise {
+
+ class VSCodeLinuxRuntimeIdFallback implements ILinuxRuntimeIdFallback {
+ getFallbackLinuxRuntimeId(): string {
+ return VSCodePlatformInformation.fallbackDebuggerLinuxRuntimeId();
+ }
+ };
+
+ return PlatformInformation.GetCurrent(new VSCodeLinuxRuntimeIdFallback());
+ }
+
+ public static isFallbackDebuggerLinuxRuntimeIdSet() : boolean {
+ if (VSCodePlatformInformation.fallbackDebuggerLinuxRuntimeId()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private static fallbackDebuggerLinuxRuntimeId() : string {
+ const config = vscode.workspace.getConfiguration('csharp');
+ return config.get('fallbackDebuggerLinuxRuntimeId', '');
+ }
+};
+
diff --git a/test/assets.test.ts b/test/assets.test.ts
index 17168ebd2c..c6998c0290 100644
--- a/test/assets.test.ts
+++ b/test/assets.test.ts
@@ -7,6 +7,7 @@ import { should } from 'chai';
import * as path from 'path';
import * as protocol from '../src/omnisharp/protocol';
import { AssetGenerator } from '../src/assets';
+import { parse } from 'jsonc-parser';
suite("Asset generation: project.json", () => {
suiteSetup(() => should());
@@ -19,7 +20,7 @@ suite("Asset generation: project.json", () => {
let buildPath = tasksJson.tasks[0].args[0];
// ${workspaceRoot}/project.json
- let segments = buildPath.split(path.sep);
+ let segments = buildPath.split(path.posix.sep);
segments.should.deep.equal(['${workspaceRoot}', 'project.json']);
});
@@ -31,7 +32,7 @@ suite("Asset generation: project.json", () => {
let buildPath = tasksJson.tasks[0].args[0];
// ${workspaceRoot}/nested/project.json
- let segments = buildPath.split(path.sep);
+ let segments = buildPath.split(path.posix.sep);
segments.should.deep.equal(['${workspaceRoot}', 'nested', 'project.json']);
});
@@ -39,11 +40,11 @@ suite("Asset generation: project.json", () => {
let rootPath = path.resolve('testRoot');
let info = createDotNetWorkspaceInformation(rootPath, 'testApp.dll', 'netcoreapp1.0');
let generator = new AssetGenerator(info, rootPath);
- let launchJson = generator.createLaunchJson(/*isWebProject*/ false);
- let programPath = launchJson.configurations[0].program;
+ let launchJson = parse(generator.createLaunchJson(/*isWebProject*/ false), null, true);
+ let programPath = launchJson[0].program;
// ${workspaceRoot}/bin/Debug/netcoreapp1.0/testApp.dll
- let segments = programPath.split(path.sep);
+ let segments = programPath.split(path.posix.sep);
segments.should.deep.equal(['${workspaceRoot}', 'bin', 'Debug', 'netcoreapp1.0', 'testApp.dll']);
});
@@ -51,11 +52,11 @@ suite("Asset generation: project.json", () => {
let rootPath = path.resolve('testRoot');
let info = createDotNetWorkspaceInformation(path.join(rootPath, 'nested'), 'testApp.dll', 'netcoreapp1.0');
let generator = new AssetGenerator(info, rootPath);
- let launchJson = generator.createLaunchJson(/*isWebProject*/ false);
- let programPath = launchJson.configurations[0].program;
+ let launchJson = parse(generator.createLaunchJson(/*isWebProject*/ false), null, true);
+ let programPath = launchJson[0].program;
// ${workspaceRoot}/nested/bin/Debug/netcoreapp1.0/testApp.dll
- let segments = programPath.split(path.sep);
+ let segments = programPath.split(path.posix.sep);
segments.should.deep.equal(['${workspaceRoot}', 'nested', 'bin', 'Debug', 'netcoreapp1.0', 'testApp.dll']);
});
@@ -63,11 +64,11 @@ suite("Asset generation: project.json", () => {
let rootPath = path.resolve('testRoot');
let info = createDotNetWorkspaceInformation(rootPath, 'testApp.dll', 'netcoreapp1.0');
let generator = new AssetGenerator(info, rootPath);
- let launchJson = generator.createLaunchJson(/*isWebProject*/ true);
- let programPath = launchJson.configurations[0].program;
+ let launchJson = parse(generator.createLaunchJson(/*isWebProject*/ true), null, true);
+ let programPath = launchJson[0].program;
// ${workspaceRoot}/bin/Debug/netcoreapp1.0/testApp.dll
- let segments = programPath.split(path.sep);
+ let segments = programPath.split(path.posix.sep);
segments.should.deep.equal(['${workspaceRoot}', 'bin', 'Debug', 'netcoreapp1.0', 'testApp.dll']);
});
@@ -75,11 +76,11 @@ suite("Asset generation: project.json", () => {
let rootPath = path.resolve('testRoot');
let info = createDotNetWorkspaceInformation(path.join(rootPath, 'nested'), 'testApp.dll', 'netcoreapp1.0');
let generator = new AssetGenerator(info, rootPath);
- let launchJson = generator.createLaunchJson(/*isWebProject*/ true);
- let programPath = launchJson.configurations[0].program;
+ let launchJson = parse(generator.createLaunchJson(/*isWebProject*/ true), null, true);
+ let programPath = launchJson[0].program;
// ${workspaceRoot}/nested/bin/Debug/netcoreapp1.0/testApp.dll
- let segments = programPath.split(path.sep);
+ let segments = programPath.split(path.posix.sep);
segments.should.deep.equal(['${workspaceRoot}', 'nested', 'bin', 'Debug', 'netcoreapp1.0', 'testApp.dll']);
});
});
@@ -127,7 +128,7 @@ suite("Asset generation: csproj", () => {
let buildPath = tasksJson.tasks[0].args[0];
// ${workspaceRoot}/project.json
- let segments = buildPath.split(path.sep);
+ let segments = buildPath.split(path.posix.sep);
segments.should.deep.equal(['${workspaceRoot}', 'testApp.csproj']);
});
@@ -139,7 +140,7 @@ suite("Asset generation: csproj", () => {
let buildPath = tasksJson.tasks[0].args[0];
// ${workspaceRoot}/nested/project.json
- let segments = buildPath.split(path.sep);
+ let segments = buildPath.split(path.posix.sep);
segments.should.deep.equal(['${workspaceRoot}', 'nested', 'testApp.csproj']);
});
@@ -147,11 +148,11 @@ suite("Asset generation: csproj", () => {
let rootPath = path.resolve('testRoot');
let info = createMSBuildWorkspaceInformation(path.join(rootPath, 'testApp.csproj'), 'testApp', 'netcoreapp1.0');
let generator = new AssetGenerator(info, rootPath);
- let launchJson = generator.createLaunchJson(/*isWebProject*/ false);
- let programPath = launchJson.configurations[0].program;
+ let launchJson = parse(generator.createLaunchJson(/*isWebProject*/ false), null, true);
+ let programPath = launchJson[0].program;
// ${workspaceRoot}/bin/Debug/netcoreapp1.0/testApp.dll
- let segments = programPath.split(path.sep);
+ let segments = programPath.split(path.posix.sep);
segments.should.deep.equal(['${workspaceRoot}', 'bin', 'Debug', 'netcoreapp1.0', 'testApp.dll']);
});
@@ -159,11 +160,11 @@ suite("Asset generation: csproj", () => {
let rootPath = path.resolve('testRoot');
let info = createMSBuildWorkspaceInformation(path.join(rootPath, 'nested', 'testApp.csproj'), 'testApp', 'netcoreapp1.0');
let generator = new AssetGenerator(info, rootPath);
- let launchJson = generator.createLaunchJson(/*isWebProject*/ false);
- let programPath = launchJson.configurations[0].program;
+ let launchJson = parse(generator.createLaunchJson(/*isWebProject*/ false), null, true);
+ let programPath = launchJson[0].program;
// ${workspaceRoot}/nested/bin/Debug/netcoreapp1.0/testApp.dll
- let segments = programPath.split(path.sep);
+ let segments = programPath.split(path.posix.sep);
segments.should.deep.equal(['${workspaceRoot}', 'nested', 'bin', 'Debug', 'netcoreapp1.0', 'testApp.dll']);
});
@@ -171,11 +172,11 @@ suite("Asset generation: csproj", () => {
let rootPath = path.resolve('testRoot');
let info = createMSBuildWorkspaceInformation(path.join(rootPath, 'testApp.csproj'), 'testApp', 'netcoreapp1.0');
let generator = new AssetGenerator(info, rootPath);
- let launchJson = generator.createLaunchJson(/*isWebProject*/ true);
- let programPath = launchJson.configurations[0].program;
+ let launchJson = parse(generator.createLaunchJson(/*isWebProject*/ true), null, true);
+ let programPath = launchJson[0].program;
// ${workspaceRoot}/bin/Debug/netcoreapp1.0/testApp.dll
- let segments = programPath.split(path.sep);
+ let segments = programPath.split(path.posix.sep);
segments.should.deep.equal(['${workspaceRoot}', 'bin', 'Debug', 'netcoreapp1.0', 'testApp.dll']);
});
@@ -183,11 +184,11 @@ suite("Asset generation: csproj", () => {
let rootPath = path.resolve('testRoot');
let info = createMSBuildWorkspaceInformation(path.join(rootPath, 'nested', 'testApp.csproj'), 'testApp', 'netcoreapp1.0');
let generator = new AssetGenerator(info, rootPath);
- let launchJson = generator.createLaunchJson(/*isWebProject*/ true);
- let programPath = launchJson.configurations[0].program;
+ let launchJson = parse(generator.createLaunchJson(/*isWebProject*/ true), null, true);
+ let programPath = launchJson[0].program;
// ${workspaceRoot}/nested/bin/Debug/netcoreapp1.0/testApp.dll
- let segments = programPath.split(path.sep);
+ let segments = programPath.split(path.posix.sep);
segments.should.deep.equal(['${workspaceRoot}', 'nested', 'bin', 'Debug', 'netcoreapp1.0', 'testApp.dll']);
});
});
diff --git a/test/common.test.ts b/test/common.test.ts
index cf90b15fe0..0624f7581e 100644
--- a/test/common.test.ts
+++ b/test/common.test.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { should } from 'chai';
-import { buildPromiseChain, safeLength, sum } from '../src/common';
+import { buildPromiseChain, splitCommandLineArgs, safeLength, sum } from '../src/common';
suite("Common", () => {
suiteSetup(() => should());
@@ -25,6 +25,50 @@ suite("Common", () => {
});
});
+ suite("splitCommandLineArgs", () => {
+ test("single argument", () => {
+ let result = splitCommandLineArgs("hello");
+
+ result.should.deep.equal(["hello"]);
+ });
+
+ test("two arguments", () => {
+ let result = splitCommandLineArgs("hello world");
+
+ result.should.deep.equal(["hello", "world"]);
+ });
+
+ test("single quoted argument", () => {
+ let result = splitCommandLineArgs("\"hello world\"");
+
+ result.should.deep.equal(["hello world"]);
+ });
+
+ test("two argument, one quoted", () => {
+ let result = splitCommandLineArgs("hello \"world\"");
+
+ result.should.deep.equal(["hello", "world"]);
+ });
+
+ test("many quoted arguments", () => {
+ let result = splitCommandLineArgs("\"a\" \"b\" \"c\" \"d\" \"e\"");
+
+ result.should.deep.equal(["a", "b", "c", "d", "e"]);
+ });
+
+ test("many arguments, some quoted, some not", () => {
+ let result = splitCommandLineArgs("\"a\" b \"c\" d \"e\"");
+
+ result.should.deep.equal(["a", "b", "c", "d", "e"]);
+ });
+
+ test("many arguments, some quoted, some not (inverted)", () => {
+ let result = splitCommandLineArgs("a \"b\" c \"d\" e");
+
+ result.should.deep.equal(["a", "b", "c", "d", "e"]);
+ });
+ });
+
suite("safeLength", () => {
test("return 0 for empty array", () => {
let array = [];
diff --git a/test/platform.test.ts b/test/platform.test.ts
index fe815c667c..738fd55737 100644
--- a/test/platform.test.ts
+++ b/test/platform.test.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { should } from 'chai';
-import { LinuxDistribution, PlatformInformation } from '../src/platform';
+import { LinuxDistribution, PlatformInformation, ILinuxRuntimeIdFallback } from '../src/platform';
suite("Platform", () => {
suiteSetup(() => should());
@@ -132,6 +132,45 @@ suite("Platform", () => {
should().equal(platformInfo.runtimeId, null);
})
+
+ test("Compute correct RID for GalliumOS 2.0", () => {
+ const platformInfo = new PlatformInformation('linux', 'x86_64', distro_gallium_2_0());
+
+ platformInfo.runtimeId.should.equal('ubuntu.16.04-x64');
+ })
+
+ test("Fallback runtime id not used for Ubuntu", () => {
+ const linuxFallbackRuntimeId = new TestLinuxRuntimeIdFallback('');
+ const platformInfo = new PlatformInformation('linux', 'x86_64', distro_ubuntu_16_04(), linuxFallbackRuntimeId);
+
+ platformInfo.runtimeId.should.equal('ubuntu.16.04-x64');
+ linuxFallbackRuntimeId.wasFallbackQueried().should.equal(false);
+ })
+
+ test("Fallback runtime id used for unknown distro", () => {
+ const linuxFallbackRuntimeId = new TestLinuxRuntimeIdFallback('ubuntu.16.04-x64');
+ const platformInfo = new PlatformInformation('linux', 'x86_64', distro_unknown_no_id_like(), linuxFallbackRuntimeId);
+
+ platformInfo.runtimeId.should.equal('ubuntu.16.04-x64');
+ linuxFallbackRuntimeId.wasFallbackQueried().should.equal(true);
+ })
+
+ test("Fallback runtime id ignored when not provided", () => {
+ const linuxFallbackRuntimeId = new TestLinuxRuntimeIdFallback('');
+ const platformInfo = new PlatformInformation('linux', 'x86_64', distro_unknown_no_id_like(), linuxFallbackRuntimeId);
+
+ should().equal(platformInfo.runtimeId, null);
+ linuxFallbackRuntimeId.wasFallbackQueried().should.equal(true);
+ })
+
+ test("Fallback runtime id precedence over heuristics", () => {
+ // NOTE: gallium normally uses ubuntu 16. In this test we force it to instead be something else.
+ const linuxFallbackRuntimeId = new TestLinuxRuntimeIdFallback('fedora.23-x64');
+ const platformInfo = new PlatformInformation('linux', 'x86_64', distro_gallium_2_0(), linuxFallbackRuntimeId);
+
+ platformInfo.runtimeId.should.equal('fedora.23-x64');
+ linuxFallbackRuntimeId.wasFallbackQueried().should.equal(true);
+ })
});
function distro_ubuntu_14_04(): LinuxDistribution {
@@ -306,4 +345,40 @@ VERSION="1.0 (rogers)"
ID=MakeBelieve`;
return LinuxDistribution.FromReleaseInfo(input, '\n');
-}
\ No newline at end of file
+}
+
+function distro_gallium_2_0(): LinuxDistribution {
+ const input = `
+NAME="GalliumOS"
+VERSION="2.0 (Xenon)"
+ID=galliumos
+ID_LIKE="ubuntu debian"
+PRETTY_NAME="GalliumOS 2.0"
+ANSI_COLOR="1;34"
+VERSION_ID="2.0"
+HOME_URL="https://galliumos.org/"
+SUPPORT_URL="https://galliumos.org/"
+BUG_REPORT_URL="https://github.com/GalliumOS/galliumos-distro/issues"
+UBUNTU_CODENAME=xenial`;
+
+ return LinuxDistribution.FromReleaseInfo(input, '\n');
+}
+
+class TestLinuxRuntimeIdFallback implements ILinuxRuntimeIdFallback {
+ private _fallbackRuntime: string;
+ private _wasFallbackQueried: boolean;
+
+ constructor(fallbackRuntime: string) {
+ this._fallbackRuntime = fallbackRuntime;
+ this._wasFallbackQueried = false;
+ }
+
+ getFallbackLinuxRuntimeId(): string {
+ this._wasFallbackQueried = true;
+ return this._fallbackRuntime;
+ }
+
+ wasFallbackQueried(): boolean {
+ return this._wasFallbackQueried;
+ }
+};