From c3862f47811d1164875f2c4750a7cf02a26bd68c Mon Sep 17 00:00:00 2001 From: Andrew Wang Date: Tue, 14 Mar 2017 14:41:30 -0700 Subject: [PATCH 01/40] Adding QuoteArgs Flag (#1322) * Adding quoteArgs flag * Adding QuoteArgs to PipeConfiguration * Updating Description for QuoteArgs * Fixing typo in description --- package.json | 40 ++++++++++++++++++++++++++++++++++++ src/tools/OptionsSchema.json | 10 +++++++++ 2 files changed, 50 insertions(+) diff --git a/package.json b/package.json index 70db2a4d3d..134f8ae1ea 100644 --- a/package.json +++ b/package.json @@ -727,6 +727,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 +759,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 +801,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 +843,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 +995,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 +1027,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 +1069,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 +1111,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": { 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", From 60799e81d1379018ca6a679663ecadc5e1d4971c Mon Sep 17 00:00:00 2001 From: John Leuenhagen Date: Wed, 15 Mar 2017 13:55:18 -0400 Subject: [PATCH 02/40] fix readme typo (#1326) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1d00cf2e4a..8a85507507 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,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 From 29db22be2b402e2c7b7984ad6101bd82b54f7a59 Mon Sep 17 00:00:00 2001 From: Peter Blazejewicz Date: Fri, 17 Mar 2017 15:21:26 +0100 Subject: [PATCH 03/40] Clarify and shorten watch NPM task description The `tsc` can be run from already defined NPM script and by this: - no local installation is needed for compiler - the long path for Windows OS command is no longer required Usage: npm run watch > csharp@1.8.0 watch /Users/piotrblazejewicz/git/omnisharp-vscode > tsc -watch -p ./ 3:19:52 PM - Compilation complete. Watching for file changes. ... Thanks! --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8a85507507..f653242872 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,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. From 6540859654c65381ccdf33657781e6ec7ecac445 Mon Sep 17 00:00:00 2001 From: Peter Blazejewicz Date: Sat, 18 Mar 2017 12:21:25 +0100 Subject: [PATCH 04/40] Switch from deprecated outDir to outFiles This commit updates task configuration to use current configuration keys and to limit number of warnings displayed in VSCode itself. Similar one: Microsoft/vscode-generator-code#59 Thanks! --- .vscode/launch.json | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) 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 From 7e9217691f4d9f6b7f100e68762c2f5665cf8158 Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Tue, 21 Mar 2017 07:46:30 -0700 Subject: [PATCH 05/40] Add '"' and ':' as trigger characters for project.json completion --- src/features/json/jsonContributions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/json/jsonContributions.ts b/src/features/json/jsonContributions.ts index 055549c761..798d74c380 100644 --- a/src/features/json/jsonContributions.ts +++ b/src/features/json/jsonContributions.ts @@ -45,7 +45,7 @@ export function addJSONProviders(): Disposable { let contributions = [new ProjectJSONContribution(xhr)]; contributions.forEach(contribution => { let selector = contribution.getDocumentSelector(); - subscriptions.push(languages.registerCompletionItemProvider(selector, new JSONCompletionItemProvider(contribution))); + subscriptions.push(languages.registerCompletionItemProvider(selector, new JSONCompletionItemProvider(contribution), '"', ':')); subscriptions.push(languages.registerHoverProvider(selector, new JSONHoverProvider(contribution))); }); From 5566d5f1f6ba4c2026b8e78eb73e3de367d71f25 Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Tue, 21 Mar 2017 09:56:52 -0700 Subject: [PATCH 06/40] Add additional trigger characters and filter text for project.json completion --- src/features/json/jsonContributions.ts | 3 ++- src/features/json/projectJSONContribution.ts | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/features/json/jsonContributions.ts b/src/features/json/jsonContributions.ts index 798d74c380..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) { From edece0d919ee2189e29d7bc8c91ea9d95746ee8c Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Tue, 21 Mar 2017 10:44:54 -0700 Subject: [PATCH 07/40] 1.8.0 -> 1.9.0-beta1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 134f8ae1ea..eb12d520b1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "csharp", "publisher": "ms-vscode", - "version": "1.8.0", + "version": "1.9.0-beta1", "description": "C# for Visual Studio Code (powered by OmniSharp).", "displayName": "C#", "author": "Microsoft Corporation", From 5261138b01fad4e88f2111fdeffe34c4240210fc Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Tue, 21 Mar 2017 10:45:01 -0700 Subject: [PATCH 08/40] Update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 629c07a86c..baa25cba8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ * 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 Release)_ + +#### Other Updates and Fixes + +* Improvements made to project.json package completion experience. ([#1338](https://github.com/OmniSharp/omnisharp-vscode/pull/1338)) + ## 1.8.0 (March 10, 2017) #### Go to Implementation From e1b0ab28d0e42da294098e5dbb25ca9f991ab9db Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Tue, 21 Mar 2017 10:49:37 -0700 Subject: [PATCH 09/40] Fix changelog typo --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index baa25cba8b..75ffef1d02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ * 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 Release)_ +## 1.9.0 _(Not Yet Released)_ #### Other Updates and Fixes From 054cdb16579de76505332a407a2ca5c323afa04e Mon Sep 17 00:00:00 2001 From: Rajkumar Janakiraman Date: Tue, 28 Mar 2017 10:45:33 -0700 Subject: [PATCH 10/40] Updating debugger to 1.9.0 which has the macos Sierra fix. (#1349) --- package.json | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index eb12d520b1..1ccafe406a 100644 --- a/package.json +++ b/package.json @@ -150,8 +150,8 @@ }, { "description": ".NET Core Debugger (Windows / x64)", - "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-8-6/coreclr-debug-win7-x64.zip", - "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-8-6/coreclr-debug-win7-x64.zip", + "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-0/coreclr-debug-win7-x64.zip", + "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-0/coreclr-debug-win7-x64.zip", "installPath": ".debugger", "runtimeIds": [ "win7-x64" @@ -159,8 +159,8 @@ }, { "description": ".NET Core Debugger (macOS / x64)", - "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-8-6/coreclr-debug-osx.10.11-x64.zip", - "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-8-6/coreclr-debug-osx.10.11-x64.zip", + "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-0/coreclr-debug-osx.10.11-x64.zip", + "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-0/coreclr-debug-osx.10.11-x64.zip", "installPath": ".debugger", "runtimeIds": [ "osx.10.11-x64" @@ -172,8 +172,8 @@ }, { "description": ".NET Core Debugger (CentOS / x64)", - "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-8-6/coreclr-debug-centos.7-x64.zip", - "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-8-6/coreclr-debug-centos.7-x64.zip", + "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-0/coreclr-debug-centos.7-x64.zip", + "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-0/coreclr-debug-centos.7-x64.zip", "installPath": ".debugger", "runtimeIds": [ "centos.7-x64" @@ -185,8 +185,8 @@ }, { "description": ".NET Core Debugger (Debian / x64)", - "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-8-6/coreclr-debug-debian.8-x64.zip", - "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-8-6/coreclr-debug-debian.8-x64.zip", + "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-0/coreclr-debug-debian.8-x64.zip", + "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-0/coreclr-debug-debian.8-x64.zip", "installPath": ".debugger", "runtimeIds": [ "debian.8-x64" @@ -198,8 +198,8 @@ }, { "description": ".NET Core Debugger (Fedora 23 / x64)", - "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-8-6/coreclr-debug-fedora.23-x64.zip", - "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-8-6/coreclr-debug-fedora.23-x64.zip", + "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-0/coreclr-debug-fedora.23-x64.zip", + "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-0/coreclr-debug-fedora.23-x64.zip", "installPath": ".debugger", "runtimeIds": [ "fedora.23-x64" @@ -211,8 +211,8 @@ }, { "description": ".NET Core Debugger (Fedora 24 / x64)", - "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-8-6/coreclr-debug-fedora.24-x64.zip", - "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-8-6/coreclr-debug-fedora.24-x64.zip", + "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-0/coreclr-debug-fedora.24-x64.zip", + "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-0/coreclr-debug-fedora.24-x64.zip", "installPath": ".debugger", "runtimeIds": [ "fedora.24-x64" @@ -224,8 +224,8 @@ }, { "description": ".NET Core Debugger (OpenSUSE 13 / x64)", - "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-8-6/coreclr-debug-opensuse.13.2-x64.zip", - "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-8-6/coreclr-debug-opensuse.13.2-x64.zip", + "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-0/coreclr-debug-opensuse.13.2-x64.zip", + "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-0/coreclr-debug-opensuse.13.2-x64.zip", "installPath": ".debugger", "runtimeIds": [ "opensuse.13.2-x64" @@ -237,8 +237,8 @@ }, { "description": ".NET Core Debugger (OpenSUSE 42 / x64)", - "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-8-6/coreclr-debug-opensuse.42.1-x64.zip", - "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-8-6/coreclr-debug-opensuse.42.1-x64.zip", + "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-0/coreclr-debug-opensuse.42.1-x64.zip", + "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-0/coreclr-debug-opensuse.42.1-x64.zip", "installPath": ".debugger", "runtimeIds": [ "opensuse.42.1-x64" @@ -250,8 +250,8 @@ }, { "description": ".NET Core Debugger (RHEL / x64)", - "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-8-6/coreclr-debug-rhel.7.2-x64.zip", - "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-8-6/coreclr-debug-rhel.7.2-x64.zip", + "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-0/coreclr-debug-rhel.7.2-x64.zip", + "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-0/coreclr-debug-rhel.7.2-x64.zip", "installPath": ".debugger", "runtimeIds": [ "rhel.7-x64" @@ -263,8 +263,8 @@ }, { "description": ".NET Core Debugger (Ubuntu 14.04 / x64)", - "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-8-6/coreclr-debug-ubuntu.14.04-x64.zip", - "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-8-6/coreclr-debug-ubuntu.14.04-x64.zip", + "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-0/coreclr-debug-ubuntu.14.04-x64.zip", + "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-0/coreclr-debug-ubuntu.14.04-x64.zip", "installPath": ".debugger", "runtimeIds": [ "ubuntu.14.04-x64" @@ -276,8 +276,8 @@ }, { "description": ".NET Core Debugger (Ubuntu 16.04 / x64)", - "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-8-6/coreclr-debug-ubuntu.16.04-x64.zip", - "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-8-6/coreclr-debug-ubuntu.16.04-x64.zip", + "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-0/coreclr-debug-ubuntu.16.04-x64.zip", + "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-0/coreclr-debug-ubuntu.16.04-x64.zip", "installPath": ".debugger", "runtimeIds": [ "ubuntu.16.04-x64" @@ -289,8 +289,8 @@ }, { "description": ".NET Core Debugger (Ubuntu 16.10 / x64)", - "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-8-6/coreclr-debug-ubuntu.16.10-x64.zip", - "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-8-6/coreclr-debug-ubuntu.16.10-x64.zip", + "url": "https://vsdebugger.azureedge.net/coreclr-debug-1-9-0/coreclr-debug-ubuntu.16.10-x64.zip", + "fallbackUrl": "https://vsdebugger.blob.core.windows.net/coreclr-debug-1-9-0/coreclr-debug-ubuntu.16.10-x64.zip", "installPath": ".debugger", "runtimeIds": [ "ubuntu.16.10-x64" From a10575255f9f41c75a5bb0109a3cf133b62d1a85 Mon Sep 17 00:00:00 2001 From: Gregg Miskelly Date: Tue, 28 Mar 2017 11:25:39 -0700 Subject: [PATCH 11/40] Update package.json/CHANGELOG.md for 1.9.0-beta2 (#1350) --- CHANGELOG.md | 6 ++++++ package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75ffef1d02..0982abbc99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ ## 1.9.0 _(Not Yet Released)_ +#### Debugger + +* **Hot issue:** Fixes debugging on OSX Sierra ([#1220](https://github.com/OmniSharp/omnisharp-vscode/issues/1220)) +* Several bug fixes that addressed problems with launch ([#1318](https://github.com/OmniSharp/omnisharp-vscode/issues/1318), [#1335](https://github.com/OmniSharp/omnisharp-vscode/issues/1335), [#1336](https://github.com/OmniSharp/omnisharp-vscode/issues/1336)) +* Fix issue where VS Code would incorrectly display threads as paused ([#1317](https://github.com/OmniSharp/omnisharp-vscode/issues/1317)) + #### Other Updates and Fixes * Improvements made to project.json package completion experience. ([#1338](https://github.com/OmniSharp/omnisharp-vscode/pull/1338)) diff --git a/package.json b/package.json index 1ccafe406a..9a54ea9d98 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "csharp", "publisher": "ms-vscode", - "version": "1.9.0-beta1", + "version": "1.9.0-beta2", "description": "C# for Visual Studio Code (powered by OmniSharp).", "displayName": "C#", "author": "Microsoft Corporation", From 9292f7c2314f49d3eb0035af18692365910890e5 Mon Sep 17 00:00:00 2001 From: Chuck Ries Date: Wed, 29 Mar 2017 10:42:39 -0700 Subject: [PATCH 12/40] Always use posix paths in asset generation We were using native 'path.join' when creating paths for VS Code assets (tasks.json, launch.json). On windows this would produce windows style paths. If the assets are checked into source control and used on posix systems, they will break. Posix paths will run perfectly fine on windows, so always use posix paths. --- src/assets.ts | 15 ++++++++++----- test/assets.test.ts | 24 ++++++++++++------------ 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/assets.ts b/src/assets.ts index e3beb5b88a..ec1f80f45d 100644 --- a/src/assets.ts +++ b/src/assets.ts @@ -194,15 +194,20 @@ export class AssetGenerator { return result; } + private convertNativePathToPosix(pathString: string) : string { + let parts = pathString.split(path.sep); + return parts.join(path.posix.sep); + } + private createLaunchConfiguration(): ConsoleLaunchConfiguration { return { name: '.NET Core Launch (console)', type: 'coreclr', request: 'launch', preLaunchTask: 'build', - program: this.computeProgramPath(), + program: this.convertNativePathToPosix(this.computeProgramPath()), args: [], - cwd: this.computeWorkingDirectory(), + cwd: this.convertNativePathToPosix(this.computeWorkingDirectory()), console: "internalConsole", stopAtEntry: false, internalConsoleOptions: "openOnSessionStart" @@ -215,9 +220,9 @@ export class AssetGenerator { type: 'coreclr', request: 'launch', preLaunchTask: 'build', - program: this.computeProgramPath(), + program: this.convertNativePathToPosix(this.computeProgramPath()), args: [], - cwd: this.computeWorkingDirectory(), + cwd: this.convertNativePathToPosix(this.computeWorkingDirectory()), stopAtEntry: false, internalConsoleOptions: "openOnSessionStart", launchBrowser: { @@ -282,7 +287,7 @@ export class AssetGenerator { return { taskName: 'build', - args: [buildPath], + args: [this.convertNativePathToPosix(buildPath)], isBuildCommand: true, problemMatcher: '$msCompile' }; diff --git a/test/assets.test.ts b/test/assets.test.ts index 17168ebd2c..8c62f2a78f 100644 --- a/test/assets.test.ts +++ b/test/assets.test.ts @@ -19,7 +19,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 +31,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']); }); @@ -43,7 +43,7 @@ suite("Asset generation: project.json", () => { let programPath = launchJson.configurations[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']); }); @@ -55,7 +55,7 @@ suite("Asset generation: project.json", () => { let programPath = launchJson.configurations[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']); }); @@ -67,7 +67,7 @@ suite("Asset generation: project.json", () => { let programPath = launchJson.configurations[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']); }); @@ -79,7 +79,7 @@ suite("Asset generation: project.json", () => { let programPath = launchJson.configurations[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 +127,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 +139,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']); }); @@ -151,7 +151,7 @@ suite("Asset generation: csproj", () => { let programPath = launchJson.configurations[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']); }); @@ -163,7 +163,7 @@ suite("Asset generation: csproj", () => { let programPath = launchJson.configurations[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']); }); @@ -175,7 +175,7 @@ suite("Asset generation: csproj", () => { let programPath = launchJson.configurations[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']); }); @@ -187,7 +187,7 @@ suite("Asset generation: csproj", () => { let programPath = launchJson.configurations[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']); }); }); From d3ba536346797a0c5f9400b4187d7ebdba6b917b Mon Sep 17 00:00:00 2001 From: Chuck Ries Date: Wed, 29 Mar 2017 12:50:18 -0700 Subject: [PATCH 13/40] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0982abbc99..fbbeb34a7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ #### Other Updates and Fixes * Improvements made to project.json package completion experience. ([#1338](https://github.com/OmniSharp/omnisharp-vscode/pull/1338)) +* Assets for building and debugging are now always generated with POSIX style paths. ([#1354](https://github.com/OmniSharp/omnisharp-vscode/pull/1354)) ## 1.8.0 (March 10, 2017) From f760886a4c14df88698d172285757e659b9fe709 Mon Sep 17 00:00:00 2001 From: Gregg Miskelly Date: Fri, 31 Mar 2017 09:26:26 -0700 Subject: [PATCH 14/40] Fix debugger.md markdown (#1360) Recently github has stopped liking the markdown in debugger.md. This fixes it. --- debugger.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/debugger.md b/debugger.md index 5841129fde..caaccef59b 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,7 +35,7 @@ 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 @@ -80,15 +80,15 @@ 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. * **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 +#### 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* @@ -98,26 +98,26 @@ Just My Code is a set of features that makes it easier to focus on debugging you * 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 +##### 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 +##### Symbol Path You can optionally provide paths to symbols following this schema: "symbolPath": [ "/Volumes/symbols" ] -#####Environment variables +##### Environment variables Environment variables may be passed to your program using this schema: "env": { "myVariableName":"theValueGoesHere" } -#####Console (terminal) window +##### 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" From c4f68af2dcf3691f1a2be9317f77e1b3e619d7f9 Mon Sep 17 00:00:00 2001 From: Andrew Wang Date: Mon, 3 Apr 2017 16:53:09 -0700 Subject: [PATCH 15/40] Adding help text to created launch.json (#1358) * Adding help text to created launch.json * Converting configuration to be strings + comments Also moving launch.json information from debugger.md to debugger-launchjson.md. This commit adds json-comment in order to parse json strings with comments for testing. * Remove json-comments and update md Removed json-comments to use jsonc-parser. Adding more information to launchjson.md and updating links. * Fixing CR Issues Removing package depedencies. * Fixing URLS --- debugger-launchjson.md | 110 ++++++++++++++++++++++ debugger.md | 51 +--------- package.json | 2 +- src/assets.ts | 206 +++++++++++++++++++---------------------- test/assets.test.ts | 33 +++---- 5 files changed, 225 insertions(+), 177 deletions(-) create mode 100644 debugger-launchjson.md diff --git a/debugger-launchjson.md b/debugger-launchjson.md new file mode 100644 index 0000000000..0abba3ae91 --- /dev/null +++ b/debugger-launchjson.md @@ -0,0 +1,110 @@ +# 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). + +## 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 caaccef59b..4ee4c52edb 100644 --- a/debugger.md +++ b/debugger.md @@ -1,4 +1,4 @@ -# 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​ @@ -37,7 +37,6 @@ The first time that C# code is opened in VS Code, the extension will download th ### 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: @@ -83,53 +82,11 @@ Your project is now all set. Set a breakpoint or two where you want to stop, cli ### 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 8c07528e1d..8d5275f419 100644 --- a/package.json +++ b/package.json @@ -1183,4 +1183,4 @@ } ] } -} \ No newline at end of file +} diff --git a/src/assets.ts b/src/assets.ts index ec1f80f45d..ef2d6e53e8 100644 --- a/src/assets.ts +++ b/src/assets.ts @@ -12,46 +12,6 @@ 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; -} - - export class AssetGenerator { public rootPath: string; public vscodeFolder: string; @@ -198,84 +158,90 @@ export class AssetGenerator { let parts = pathString.split(path.sep); return parts.join(path.posix.sep); } - - private createLaunchConfiguration(): ConsoleLaunchConfiguration { - return { - name: '.NET Core Launch (console)', - type: 'coreclr', - request: 'launch', - preLaunchTask: 'build', - program: this.convertNativePathToPosix(this.computeProgramPath()), - args: [], - cwd: this.convertNativePathToPosix(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": "${this.convertNativePathToPosix(this.computeProgramPath())}", + "args": [], + "cwd": "${this.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.convertNativePathToPosix(this.computeProgramPath()), - args: [], - cwd: this.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 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": "${this.convertNativePathToPosix(this.computeProgramPath())}", + "args": [], + "cwd": "${this.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} +]`; } } @@ -480,6 +446,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) { @@ -487,10 +457,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); } @@ -638,4 +618,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/test/assets.test.ts b/test/assets.test.ts index 8c62f2a78f..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()); @@ -39,8 +40,8 @@ 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.posix.sep); @@ -51,8 +52,8 @@ 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.posix.sep); @@ -63,8 +64,8 @@ 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.posix.sep); @@ -75,8 +76,8 @@ 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.posix.sep); @@ -147,8 +148,8 @@ 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.posix.sep); @@ -159,8 +160,8 @@ 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.posix.sep); @@ -171,8 +172,8 @@ 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.posix.sep); @@ -183,8 +184,8 @@ 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.posix.sep); From bf01486f322e12d2c90a7c259abbf42d4285fa0f Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Mon, 3 Apr 2017 23:04:25 -0400 Subject: [PATCH 16/40] Stops considering update operations when generating assets --- src/assets.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/assets.ts b/src/assets.ts index ef2d6e53e8..42c162dc5b 100644 --- a/src/assets.ts +++ b/src/assets.ts @@ -158,7 +158,7 @@ export class AssetGenerator { let parts = pathString.split(path.sep); return parts.join(path.posix.sep); } - + private createLaunchConfiguration(): string{ return ` { @@ -317,10 +317,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) { @@ -507,7 +505,7 @@ export function addAssetsIfNecessary(server: OmniSharpServer): Promise { - if (!hasOperations(operations)) { + if (!hasAddOperations(operations)) { return resolve(AddAssetResult.NotApplicable); } @@ -603,7 +601,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 => { From f9b8863c8e7da2fd2b6cd067b853da8b7e307c12 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Mon, 3 Apr 2017 23:04:53 -0400 Subject: [PATCH 17/40] Looks up build task by metadata rather than name --- src/assets.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assets.ts b/src/assets.ts index 42c162dc5b..32535188e8 100644 --- a/src/assets.ts +++ b/src/assets.ts @@ -331,7 +331,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); } From e84ada806597d4036bdd1ea8116e3279753b9ba9 Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Wed, 5 Apr 2017 12:29:14 -0700 Subject: [PATCH 18/40] Update OmniSharp prototcol for test and print test messages to output --- src/features/dotnetTest.ts | 11 ++++++++++- src/omnisharp/protocol.ts | 13 +++++++++++++ src/omnisharp/server.ts | 6 ++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/features/dotnetTest.ts b/src/features/dotnetTest.ts index 8a1db959b1..bf57ea8f78 100644 --- a/src/features/dotnetTest.ts +++ b/src/features/dotnetTest.ts @@ -36,7 +36,13 @@ 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 + '...'); + getTestOutputChannel().appendLine(`Running test ${testMethod}...`); + + let disposable = server.onTestMessage(e => + { + getTestOutputChannel().appendLine(e.Message); + }); + serverUtils .runDotNetTest(server, { FileName: fileName, MethodName: testMethod, TestFrameworkName: testFrameworkName }) .then( @@ -47,9 +53,12 @@ export function runDotnetTest(testMethod: string, fileName: string, testFramewor else { getTestOutputChannel().appendLine('Test failed \n'); } + + disposable.dispose(); }, reason => { vscode.window.showErrorMessage(`Failed to run test because ${reason}.`); + disposable.dispose(); }); } diff --git a/src/omnisharp/protocol.ts b/src/omnisharp/protocol.ts index 0e33cfe64c..c68502dbda 100644 --- a/src/omnisharp/protocol.ts +++ b/src/omnisharp/protocol.ts @@ -504,6 +504,7 @@ export namespace V2 { export interface GetTestStartInfoResponse { Executable: string; Argument: string; + WorkingDirectory: string; } export interface RunDotNetTestRequest { @@ -512,10 +513,22 @@ export namespace V2 { TestFrameworkName: string; } + export interface DotNetResult { + MethodName: string; + Outcome: string; + ErrorMessage: string; + ErrorStackTrace: string; + } + export interface RunDotNetTestResponse { 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..2ec5a4bdad 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); } From c89ce0d45aa8c964b92b8ef2b75234e95192bfaf Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Wed, 5 Apr 2017 13:08:01 -0700 Subject: [PATCH 19/40] Move convertNativePathToPosix to common --- src/assets.ts | 16 ++++++---------- src/common.ts | 5 +++++ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/assets.ts b/src/assets.ts index 32535188e8..96f38f89bf 100644 --- a/src/assets.ts +++ b/src/assets.ts @@ -11,6 +11,7 @@ import { OmniSharpServer } from './omnisharp/server'; import * as serverUtils from './omnisharp/utils'; import * as protocol from './omnisharp/protocol'; import { tolerantParse } from './json'; +import * as util from './common'; export class AssetGenerator { public rootPath: string; @@ -154,11 +155,6 @@ export class AssetGenerator { return result; } - private convertNativePathToPosix(pathString: string) : string { - let parts = pathString.split(path.sep); - return parts.join(path.posix.sep); - } - private createLaunchConfiguration(): string{ return ` { @@ -167,9 +163,9 @@ export class AssetGenerator { "request": "launch", "preLaunchTask": "build", // If you have changed target frameworks, make sure to update the program path. - "program": "${this.convertNativePathToPosix(this.computeProgramPath())}", + "program": "${util.convertNativePathToPosix(this.computeProgramPath())}", "args": [], - "cwd": "${this.convertNativePathToPosix(this.computeWorkingDirectory())}", + "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, @@ -185,9 +181,9 @@ export class AssetGenerator { "request": "launch", "preLaunchTask": "build", // If you have changed target frameworks, make sure to update the program path. - "program": "${this.convertNativePathToPosix(this.computeProgramPath())}", + "program": "${util.convertNativePathToPosix(this.computeProgramPath())}", "args": [], - "cwd": "${this.convertNativePathToPosix(this.computeWorkingDirectory())}", + "cwd": "${util.convertNativePathToPosix(this.computeWorkingDirectory())}", "stopAtEntry": false, "internalConsoleOptions": "openOnSessionStart", "launchBrowser": { @@ -253,7 +249,7 @@ export class AssetGenerator { return { taskName: 'build', - args: [this.convertNativePathToPosix(buildPath)], + args: [util.convertNativePathToPosix(buildPath)], isBuildCommand: true, problemMatcher: '$msCompile' }; diff --git a/src/common.ts b/src/common.ts index e120cc26d4..0c8be51bbd 100644 --- a/src/common.ts +++ b/src/common.ts @@ -112,3 +112,8 @@ export function deleteInstallFile(type: InstallFileType): Promise { }); }); } + +export function convertNativePathToPosix(pathString: string): string { + let parts = pathString.split(path.sep); + return parts.join(path.posix.sep); +} From d58a4c8264750cfa1b1863c4f9e96df7ec1529fd Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Thu, 6 Apr 2017 11:13:36 -0700 Subject: [PATCH 20/40] Add support for OmniSharp protocol changes to run/debug tests with 'dotnet vstest' --- src/features/dotnetTest.ts | 130 ++++++++++++++++++++++++++++--------- src/omnisharp/protocol.ts | 37 +++++++++-- src/omnisharp/utils.ts | 16 ++++- 3 files changed, 146 insertions(+), 37 deletions(-) diff --git a/src/features/dotnetTest.ts b/src/features/dotnetTest.ts index bf57ea8f78..ac6cdd72ec 100644 --- a/src/features/dotnetTest.ts +++ b/src/features/dotnetTest.ts @@ -3,10 +3,8 @@ * 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 * as vscode from 'vscode'; import * as serverUtils from "../omnisharp/utils"; import * as protocol from '../omnisharp/protocol'; @@ -35,23 +33,28 @@ 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}...`); + const output = getTestOutputChannel(); + + output.show(); + output.appendLine(`Running test ${testMethod}...`); - let disposable = server.onTestMessage(e => - { - getTestOutputChannel().appendLine(e.Message); + const disposable = server.onTestMessage(e => { + output.appendLine(e.Message); }); - serverUtils - .runDotNetTest(server, { FileName: fileName, MethodName: testMethod, TestFrameworkName: testFrameworkName }) - .then( - response => { + 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(); @@ -62,23 +65,92 @@ export function runDotnetTest(testMethod: string, fileName: string, testFramewor }); } +function getLaunchConfigurationForAttach(server: OmniSharpServer, fileName: string, testMethod: string, testFrameworkName: string): Promise { + const request: protocol.V2.GetTestStartInfoRequest = { + Filename: fileName, + MethodName: testMethod, + TestFrameworkName: testFrameworkName + }; + + return serverUtils.debugTestStart(server, request) + .then(response => { + return { + name: ".NET Test Attach", + type: "coreclr", + request: "attach", + processId: response.ProcessId + }; + }); +} + +function getLaunchConfigurationForLaunch(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 => { + let args = response.Argument.split(' '); + + // Ensure that quoted args are unquoted. + args = args.map(arg => { + if (arg.startsWith('"') && arg.endsWith('"')) { + return arg.substring(1, arg.length - 1); + } + else { + return arg; + } + }); + + return { + name: ".NET Test Launch", + type: "coreclr", + request: "launch", + program: response.Executable, + args: args, + cwd: response.WorkingDirectory, + stopAtEntry: false + }; + }); +} + + +function getLaunchConfiguration(server: OmniSharpServer, debugType: string, fileName: string, testMethod: string, testFrameworkName: string): Promise { + switch (debugType) { + case "attach": + return getLaunchConfigurationForAttach(server, fileName, testMethod, testFrameworkName); + case "launch": + return getLaunchConfigurationForLaunch(server, fileName, testMethod, testFrameworkName); + + default: + throw new Error(`Unexpected PreferredDebugType: ${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 + const request: protocol.V2.DebugTestCheckRequest = { + Filename: fileName + }; + + let debugType: string; + + return serverUtils.debugTestCheck(server, request) + .then(response => { + debugType = response.PreferredDebugType; + return getLaunchConfiguration(server, debugType, fileName, testMethod, testFrameworkName); + }) + .then(config => { + return vscode.commands.executeCommand('vscode.startDebug', config); + }) + .then(() => { + if (debugType === "attach") { + serverUtils.debugTestReady(server, { Filename: fileName }); } - ).then( - response => { }, - reason => { vscode.window.showErrorMessage(`Failed to start debugger on test because ${reason}.`); }); - }); + }) + .catch(reason => vscode.window.showErrorMessage(`Failed to start debugger: ${reason}`)); } export function updateCodeLensForTest(bucket: vscode.CodeLens[], fileName: string, node: protocol.Node, isDebugEnable: boolean) { diff --git a/src/omnisharp/protocol.ts b/src/omnisharp/protocol.ts index c68502dbda..7a50544256 100644 --- a/src/omnisharp/protocol.ts +++ b/src/omnisharp/protocol.ts @@ -419,7 +419,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 DebugTestCheck = '/v2/debugtest/check'; + export const DebugTestStart = '/v2/debugtest/start'; + export const DebugTestReady = '/v2/debugtest/ready'; } export interface Point { @@ -495,8 +498,31 @@ export namespace V2 { } // dotnet-test endpoints - export interface GetTestStartInfoRequest { - FileName: string; + export interface DebugTestCheckRequest extends Request { + } + + export interface DebugTestCheckResponse { + PreferredDebugType: string; + } + + export interface DebugTestStartRequest extends Request { + MethodName: string; + TestFrameworkName: string; + } + + export interface DebugTestStartResponse { + ProcessId: number; + HostProcessId: number; + } + + export interface DebugTestReadyRequest extends Request { + } + + export interface DebugTestReadyResponse { + IsReady: boolean; + } + + export interface GetTestStartInfoRequest extends Request { MethodName: string; TestFrameworkName: string; } @@ -507,8 +533,7 @@ export namespace V2 { WorkingDirectory: string; } - export interface RunDotNetTestRequest { - FileName: string; + export interface RunTestRequest extends Request { MethodName: string; TestFrameworkName: string; } @@ -520,7 +545,7 @@ export namespace V2 { ErrorStackTrace: string; } - export interface RunDotNetTestResponse { + export interface RunTestResponse { Failure: string; Pass: boolean; } diff --git a/src/omnisharp/utils.ts b/src/omnisharp/utils.ts index 3506ce7694..a07ae59b10 100644 --- a/src/omnisharp/utils.ts +++ b/src/omnisharp/utils.ts @@ -85,8 +85,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 debugTestCheck(server: OmniSharpServer, request: protocol.V2.DebugTestCheckRequest) { + return server.makeRequest(protocol.V2.Requests.DebugTestCheck, request); +} + +export function debugTestStart(server: OmniSharpServer, request: protocol.V2.DebugTestStartRequest) { + return server.makeRequest(protocol.V2.Requests.DebugTestStart, request); +} + +export function debugTestReady(server: OmniSharpServer, request: protocol.V2.DebugTestReadyRequest) { + return server.makeRequest(protocol.V2.Requests.DebugTestReady, request); } export function isNetCoreProject(project: protocol.MSBuildProject) { From da55ae42075b2d9976c3f324741dd4450ce4ad14 Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Thu, 6 Apr 2017 12:27:29 -0700 Subject: [PATCH 21/40] Tweak for OmniSharp protocol --- src/features/dotnetTest.ts | 2 +- src/omnisharp/protocol.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/dotnetTest.ts b/src/features/dotnetTest.ts index ac6cdd72ec..8974a4941f 100644 --- a/src/features/dotnetTest.ts +++ b/src/features/dotnetTest.ts @@ -139,7 +139,7 @@ export function debugDotnetTest(testMethod: string, fileName: string, testFramew return serverUtils.debugTestCheck(server, request) .then(response => { - debugType = response.PreferredDebugType; + debugType = response.DebugType; return getLaunchConfiguration(server, debugType, fileName, testMethod, testFrameworkName); }) .then(config => { diff --git a/src/omnisharp/protocol.ts b/src/omnisharp/protocol.ts index 7a50544256..2ef0a3185f 100644 --- a/src/omnisharp/protocol.ts +++ b/src/omnisharp/protocol.ts @@ -502,7 +502,7 @@ export namespace V2 { } export interface DebugTestCheckResponse { - PreferredDebugType: string; + DebugType: string; } export interface DebugTestStartRequest extends Request { From 3b8faea98ab8c8f940599e7ec2012d72e8d2dea1 Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Thu, 6 Apr 2017 12:39:17 -0700 Subject: [PATCH 22/40] Update OmniSharp to 1.14.0 --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 8d5275f419..61f6b082be 100644 --- a/package.json +++ b/package.json @@ -119,7 +119,7 @@ }, { "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.zip", "installPath": "./bin/omnisharp", "platforms": [ "win32" @@ -130,7 +130,7 @@ }, { "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.zip", "installPath": "./bin/omnisharp", "platforms": [ "win32" @@ -141,7 +141,7 @@ }, { "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.zip", "installPath": "./bin/omnisharp", "platforms": [ "darwin", From 82e2eb9b2e26a1cf1e11d48f2f0b25d82d22ff15 Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Thu, 6 Apr 2017 13:37:26 -0700 Subject: [PATCH 23/40] 1.9.0-beta2 -> 1.9.0-beta3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 61f6b082be..59cbe1b7ca 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "csharp", "publisher": "ms-vscode", - "version": "1.9.0-beta2", + "version": "1.9.0-beta3", "description": "C# for Visual Studio Code (powered by OmniSharp).", "displayName": "C#", "author": "Microsoft Corporation", From 3a3d0a280a68e8d2862dbe08bf6ea9055f1581cd Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Thu, 6 Apr 2017 14:40:15 -0700 Subject: [PATCH 24/40] Add TODO comment --- src/features/dotnetTest.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/features/dotnetTest.ts b/src/features/dotnetTest.ts index 8974a4941f..27375d4309 100644 --- a/src/features/dotnetTest.ts +++ b/src/features/dotnetTest.ts @@ -146,6 +146,7 @@ export function debugDotnetTest(testMethod: string, fileName: string, testFramew return vscode.commands.executeCommand('vscode.startDebug', config); }) .then(() => { + // TODO: Need to find out when the attach is really complete from the debugger. This is currently a race. if (debugType === "attach") { serverUtils.debugTestReady(server, { Filename: fileName }); } From 7530622b0c2e7b476198226644dd13bb72294bfb Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Fri, 7 Apr 2017 09:20:07 -0700 Subject: [PATCH 25/40] Add 'omnisharp.waitForDebugger' option to pass the --debug flag with launching OmniSharp --- package.json | 5 +++++ src/omnisharp/options.ts | 5 ++++- src/omnisharp/server.ts | 4 ++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 8d5275f419..d5a0ad1288 100644 --- a/package.json +++ b/package.json @@ -342,6 +342,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", 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/server.ts b/src/omnisharp/server.ts index e8c8f9f67f..1d7173a8fc 100644 --- a/src/omnisharp/server.ts +++ b/src/omnisharp/server.ts @@ -246,6 +246,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}`); From 6be1bc0a80bc2fd5124c7de4284c51ed2ad78ba7 Mon Sep 17 00:00:00 2001 From: Andrew Wang Date: Fri, 7 Apr 2017 10:29:34 -0700 Subject: [PATCH 26/40] Adding in configuration snippets (#1366) * Adding in configuration snippets * Adding user filled syntax --- package.json | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/package.json b/package.json index d5a0ad1288..0387954b80 100644 --- a/package.json +++ b/package.json @@ -1136,6 +1136,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)", From 610c50de0592c538b4e2bec94d571c4116c0cf98 Mon Sep 17 00:00:00 2001 From: Gregg Miskelly Date: Fri, 7 Apr 2017 13:39:16 -0700 Subject: [PATCH 27/40] Improve dependancy handling / Linux distro handling (#1371) * Improve dependancy handling / Linux distro handling https://github.com/OmniSharp/omnisharp-vscode/issues/1361 https://github.com/OmniSharp/omnisharp-vscode/issues/1323 Changes: 1. Add a setting to control what version of the debugger to use on Linux 2. We no longer automaticially select a debugger on Arch. Instead, we point folks to a web page telling them how to install it. 3. Added logic to the package manager so that it can detect if a package is already installed so it will not be redownloaded. This was needed since I wanted to trigger redownloads in the case that the user added the Linux distro setting. But it seemed like a useful feature anyway for folks on slow internet connections. 4. Moved the install code to its own .ts file * Updates to the changelog * Code review fixes * Restore original whitespace in package.json * Remove 'runtime id' from the install log --- CHANGELOG.md | 3 + package.json | 73 +++++++++++---- src/CSharpExtDownloader.ts | 142 +++++++++++++++++++++++++++++ src/coreclr-debug/activate.ts | 98 +++++++++++--------- src/main.ts | 133 +++------------------------- src/packages.ts | 55 +++++++++++- src/platform.ts | 162 ++++++++++++++++++++-------------- src/vscodePlatform.ts | 41 +++++++++ test/platform.test.ts | 79 ++++++++++++++++- 9 files changed, 536 insertions(+), 250 deletions(-) create mode 100644 src/CSharpExtDownloader.ts create mode 100644 src/vscodePlatform.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 95762e3516..db96814b8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,13 +8,16 @@ #### 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 ([#1318](https://github.com/OmniSharp/omnisharp-vscode/issues/1318), [#1335](https://github.com/OmniSharp/omnisharp-vscode/issues/1335), [#1336](https://github.com/OmniSharp/omnisharp-vscode/issues/1336)) * 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)). #### Other Updates and Fixes * Improvements made to project.json package completion experience. ([#1338](https://github.com/OmniSharp/omnisharp-vscode/pull/1338)) * Assets for building and debugging are now always generated with POSIX style paths. ([#1354](https://github.com/OmniSharp/omnisharp-vscode/pull/1354)) +* Improved the extension's runtime dependency download logic to skip re-downloading packages that were already successfully downloaded and installed. ## 1.8.1 (March 31, 2017) diff --git a/package.json b/package.json index 0387954b80..5cb5c72969 100644 --- a/package.json +++ b/package.json @@ -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,7 +118,8 @@ "platforms": [ "darwin", "linux" - ] + ], + "installTestPath": "./bin/framework/mscorlib.dll" }, { "description": "OmniSharp (.NET 4.6 / x86)", @@ -126,7 +130,8 @@ ], "architectures": [ "x86" - ] + ], + "installTestPath": "./bin/omnisharp/OmniSharp.exe" }, { "description": "OmniSharp (.NET 4.6 / x64)", @@ -137,7 +142,8 @@ ], "architectures": [ "x86_64" - ] + ], + "installTestPath": "./bin/omnisharp/OmniSharp.exe" }, { "description": "OmniSharp (Mono 4.6)", @@ -146,7 +152,8 @@ "platforms": [ "darwin", "linux" - ] + ], + "installTestPath": "./bin/omnisharp/OmniSharp.exe" }, { "description": ".NET Core Debugger (Windows / x64)", @@ -155,7 +162,8 @@ "installPath": ".debugger", "runtimeIds": [ "win7-x64" - ] + ], + "installTestPath": "./.debugger/vsdbg-ui.exe" }, { "description": ".NET Core Debugger (macOS / x64)", @@ -168,7 +176,8 @@ "binaries": [ "./vsdbg-ui", "./vsdbg" - ] + ], + "installTestPath": "./.debugger/vsdbg-ui" }, { "description": ".NET Core Debugger (CentOS / x64)", @@ -181,7 +190,8 @@ "binaries": [ "./vsdbg-ui", "./vsdbg" - ] + ], + "installTestPath": "./.debugger/vsdbg-ui" }, { "description": ".NET Core Debugger (Debian / x64)", @@ -194,7 +204,8 @@ "binaries": [ "./vsdbg-ui", "./vsdbg" - ] + ], + "installTestPath": "./.debugger/vsdbg-ui" }, { "description": ".NET Core Debugger (Fedora 23 / x64)", @@ -207,7 +218,8 @@ "binaries": [ "./vsdbg-ui", "./vsdbg" - ] + ], + "installTestPath": "./.debugger/vsdbg-ui" }, { "description": ".NET Core Debugger (Fedora 24 / x64)", @@ -220,7 +232,8 @@ "binaries": [ "./vsdbg-ui", "./vsdbg" - ] + ], + "installTestPath": "./.debugger/vsdbg-ui" }, { "description": ".NET Core Debugger (OpenSUSE 13 / x64)", @@ -233,7 +246,8 @@ "binaries": [ "./vsdbg-ui", "./vsdbg" - ] + ], + "installTestPath": "./.debugger/vsdbg-ui" }, { "description": ".NET Core Debugger (OpenSUSE 42 / x64)", @@ -246,7 +260,8 @@ "binaries": [ "./vsdbg-ui", "./vsdbg" - ] + ], + "installTestPath": "./.debugger/vsdbg-ui" }, { "description": ".NET Core Debugger (RHEL / x64)", @@ -259,7 +274,8 @@ "binaries": [ "./vsdbg-ui", "./vsdbg" - ] + ], + "installTestPath": "./.debugger/vsdbg-ui" }, { "description": ".NET Core Debugger (Ubuntu 14.04 / x64)", @@ -272,7 +288,8 @@ "binaries": [ "./vsdbg-ui", "./vsdbg" - ] + ], + "installTestPath": "./.debugger/vsdbg-ui" }, { "description": ".NET Core Debugger (Ubuntu 16.04 / x64)", @@ -285,7 +302,8 @@ "binaries": [ "./vsdbg-ui", "./vsdbg" - ] + ], + "installTestPath": "./.debugger/vsdbg-ui" }, { "description": ".NET Core Debugger (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, 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/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/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/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/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/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; + } +}; From 0cca9cff3c175c6428b789d5ee9ba4ba9498252e Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Mon, 10 Apr 2017 15:23:02 -0700 Subject: [PATCH 28/40] Update to new OmniSharp and change to launch test under debugger --- package.json | 6 +- src/features/changeForwarding.ts | 4 +- src/features/codeActionProvider.ts | 4 +- src/features/codeLensProvider.ts | 6 +- src/features/diagnosticsProvider.ts | 4 +- src/features/documentSymbolProvider.ts | 2 +- src/features/dotnetTest.ts | 113 ++++++++++++------------ src/features/formattingEditProvider.ts | 4 +- src/features/workspaceSymbolProvider.ts | 2 +- src/omnisharp/protocol.ts | 30 +++---- src/omnisharp/typeConvertion.ts | 2 +- src/omnisharp/utils.ts | 16 ++-- 12 files changed, 94 insertions(+), 99 deletions(-) diff --git a/package.json b/package.json index 59cbe1b7ca..ffcbcd514f 100644 --- a/package.json +++ b/package.json @@ -119,7 +119,7 @@ }, { "description": "OmniSharp (.NET 4.6 / x86)", - "url": "https://omnisharpdownload.blob.core.windows.net/ext/omnisharp-win-x86-1.14.0.zip", + "url": "https://omnisharpdownload.blob.core.windows.net/ext/omnisharp-win-x86-1.14.0.1.zip", "installPath": "./bin/omnisharp", "platforms": [ "win32" @@ -130,7 +130,7 @@ }, { "description": "OmniSharp (.NET 4.6 / x64)", - "url": "https://omnisharpdownload.blob.core.windows.net/ext/omnisharp-win-x64-1.14.0.zip", + "url": "https://omnisharpdownload.blob.core.windows.net/ext/omnisharp-win-x64-1.14.0.1.zip", "installPath": "./bin/omnisharp", "platforms": [ "win32" @@ -141,7 +141,7 @@ }, { "description": "OmniSharp (Mono 4.6)", - "url": "https://omnisharpdownload.blob.core.windows.net/ext/omnisharp-mono-1.14.0.zip", + "url": "https://omnisharpdownload.blob.core.windows.net/ext/omnisharp-mono-1.14.0.1.zip", "installPath": "./bin/omnisharp", "platforms": [ "darwin", 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..5c28558b9e 100644 --- a/src/features/codeActionProvider.ts +++ b/src/features/codeActionProvider.ts @@ -39,7 +39,7 @@ export default class OmnisharpCodeActionProvider extends AbstractProvider implem } let req: protocol.V2.GetCodeActionsRequest = { - Filename: document.fileName, + FileName: document.fileName, Selection: OmnisharpCodeActionProvider._asRange(range) }; @@ -49,7 +49,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..44e2a4a06a 100644 --- a/src/features/diagnosticsProvider.ts +++ b/src/features/diagnosticsProvider.ts @@ -193,7 +193,7 @@ 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 => { // Easy case: If there are no diagnostics in the file, we can clear it quickly. if (value.QuickFixes.length === 0) { @@ -228,7 +228,7 @@ 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.sort((a, b) => a.FileName.localeCompare(b.FileName)); let entries: [vscode.Uri, vscode.Diagnostic[]][] = []; 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 27375d4309..918cd6b79c 100644 --- a/src/features/dotnetTest.ts +++ b/src/features/dotnetTest.ts @@ -43,7 +43,7 @@ export function runDotnetTest(testMethod: string, fileName: string, testFramewor }); const request: protocol.V2.RunTestRequest = { - Filename: fileName, + FileName: fileName, MethodName: testMethod, TestFrameworkName: testFrameworkName }; @@ -65,90 +65,91 @@ export function runDotnetTest(testMethod: string, fileName: string, testFramewor }); } -function getLaunchConfigurationForAttach(server: OmniSharpServer, fileName: string, testMethod: string, testFrameworkName: string): Promise { - const request: protocol.V2.GetTestStartInfoRequest = { - Filename: fileName, +function createLaunchConfiguration(program: string, argsString: string, cwd: string) { + let args = argsString.split(' '); + + // Ensure that quoted args are unquoted. + args = args.map(arg => { + if (arg.startsWith('"') && arg.endsWith('"')) { + return arg.substring(1, arg.length - 1); + } + else { + return arg; + } + }); + + return { + name: ".NET Test Launch", + type: "coreclr", + request: "launch", + program, + args, + cwd, + stopAtEntry: true + }; +} + +function getLaunchConfigurationForVSTest(server: OmniSharpServer, fileName: string, testMethod: string, testFrameworkName: string): Promise { + const request: protocol.V2.DebugTestGetStartInfoRequest = { + FileName: fileName, MethodName: testMethod, TestFrameworkName: testFrameworkName }; - return serverUtils.debugTestStart(server, request) - .then(response => { - return { - name: ".NET Test Attach", - type: "coreclr", - request: "attach", - processId: response.ProcessId - }; - }); + return serverUtils.debugTestGetStartInfo(server, request) + .then(response => createLaunchConfiguration(response.FileName, response.Arguments, response.WorkingDirectory)); } -function getLaunchConfigurationForLaunch(server: OmniSharpServer, fileName: string, testMethod: string, testFrameworkName: string): Promise { +function getLaunchConfigurationForLegacy(server: OmniSharpServer, fileName: string, testMethod: string, testFrameworkName: string): Promise { const request: protocol.V2.GetTestStartInfoRequest = { - Filename: fileName, + FileName: fileName, MethodName: testMethod, TestFrameworkName: testFrameworkName }; return serverUtils.getTestStartInfo(server, request) - .then(response => { - let args = response.Argument.split(' '); - - // Ensure that quoted args are unquoted. - args = args.map(arg => { - if (arg.startsWith('"') && arg.endsWith('"')) { - return arg.substring(1, arg.length - 1); - } - else { - return arg; - } - }); - - return { - name: ".NET Test Launch", - type: "coreclr", - request: "launch", - program: response.Executable, - args: args, - cwd: response.WorkingDirectory, - stopAtEntry: false - }; - }); + .then(response => createLaunchConfiguration(response.Executable, response.Argument, response.WorkingDirectory)); } function getLaunchConfiguration(server: OmniSharpServer, debugType: string, fileName: string, testMethod: string, testFrameworkName: string): Promise { switch (debugType) { - case "attach": - return getLaunchConfigurationForAttach(server, fileName, testMethod, testFrameworkName); - case "launch": - return getLaunchConfigurationForLaunch(server, fileName, testMethod, testFrameworkName); + case "legacy": + return getLaunchConfigurationForLegacy(server, fileName, testMethod, testFrameworkName); + case "vstest": + return getLaunchConfigurationForVSTest(server, fileName, testMethod, testFrameworkName); default: - throw new Error(`Unexpected PreferredDebugType: ${debugType}`); + 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) { - const request: protocol.V2.DebugTestCheckRequest = { - Filename: fileName - }; - + // 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; - return serverUtils.debugTestCheck(server, request) - .then(response => { - debugType = response.DebugType; + return serverUtils.requestProjectInformation(server, { FileName: fileName} ) + .then(projectInfo => { + if (projectInfo.DotNetProject) { + debugType = "legacy"; + } + else if (projectInfo.MsBuildProject) { + debugType = "vstest"; + } + else { + throw new Error(); + } + return getLaunchConfiguration(server, debugType, fileName, testMethod, testFrameworkName); }) - .then(config => { - return vscode.commands.executeCommand('vscode.startDebug', config); - }) + .then(config => vscode.commands.executeCommand('vscode.startDebug', config)) .then(() => { - // TODO: Need to find out when the attach is really complete from the debugger. This is currently a race. - if (debugType === "attach") { - serverUtils.debugTestReady(server, { Filename: fileName }); + // For VS Test, we need to signal to start the test run after the debugger has launched. + // TODO: Need to find out when the debugger has actually launched. This is currently a race. + if (debugType === "vstest") { + serverUtils.debugTestRun(server, { FileName: fileName }); } }) .catch(reason => vscode.window.showErrorMessage(`Failed to start debugger: ${reason}`)); 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/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/omnisharp/protocol.ts b/src/omnisharp/protocol.ts index 2ef0a3185f..a35344b523 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,7 +60,7 @@ export namespace WireProtocol { } export interface Request { - Filename: string; + FileName: string; Line?: number; Column?: number; Buffer?: string; @@ -420,9 +421,8 @@ export namespace V2 { export const RunCodeAction = '/v2/runcodeaction'; export const GetTestStartInfo = '/v2/getteststartinfo'; export const RunTest = '/v2/runtest'; - export const DebugTestCheck = '/v2/debugtest/check'; - export const DebugTestStart = '/v2/debugtest/start'; - export const DebugTestReady = '/v2/debugtest/ready'; + export const DebugTestGetStartInfo = '/v2/debugtest/getstartinfo'; + export const DebugTestRun = '/v2/debugtest/run'; } export interface Point { @@ -498,28 +498,22 @@ export namespace V2 { } // dotnet-test endpoints - export interface DebugTestCheckRequest extends Request { - } - - export interface DebugTestCheckResponse { - DebugType: string; - } - - export interface DebugTestStartRequest extends Request { + export interface DebugTestGetStartInfoRequest extends Request { MethodName: string; TestFrameworkName: string; } - export interface DebugTestStartResponse { - ProcessId: number; - HostProcessId: number; + export interface DebugTestGetStartInfoResponse { + FileName: string; + Arguments: string; + WorkingDirectory: string; + EnvironmentVariables: Map; } - export interface DebugTestReadyRequest extends Request { + export interface DebugTestRunRequest extends Request { } - export interface DebugTestReadyResponse { - IsReady: boolean; + export interface DebugTestRunResponse { } export interface GetTestStartInfoRequest extends Request { 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 a07ae59b10..2758289bca 100644 --- a/src/omnisharp/utils.ts +++ b/src/omnisharp/utils.ts @@ -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); } @@ -89,16 +93,12 @@ export function runTest(server: OmniSharpServer, request: protocol.V2.RunTestReq return server.makeRequest(protocol.V2.Requests.RunTest, request); } -export function debugTestCheck(server: OmniSharpServer, request: protocol.V2.DebugTestCheckRequest) { - return server.makeRequest(protocol.V2.Requests.DebugTestCheck, request); -} - -export function debugTestStart(server: OmniSharpServer, request: protocol.V2.DebugTestStartRequest) { - return server.makeRequest(protocol.V2.Requests.DebugTestStart, request); +export function debugTestGetStartInfo(server: OmniSharpServer, request: protocol.V2.DebugTestGetStartInfoRequest) { + return server.makeRequest(protocol.V2.Requests.DebugTestGetStartInfo, request); } -export function debugTestReady(server: OmniSharpServer, request: protocol.V2.DebugTestReadyRequest) { - return server.makeRequest(protocol.V2.Requests.DebugTestReady, request); +export function debugTestRun(server: OmniSharpServer, request: protocol.V2.DebugTestRunRequest) { + return server.makeRequest(protocol.V2.Requests.DebugTestRun, request); } export function isNetCoreProject(project: protocol.MSBuildProject) { From 90c6d3bddcb3a93c2f3766590edb110c790551dc Mon Sep 17 00:00:00 2001 From: Andrew Wang Date: Tue, 11 Apr 2017 10:45:36 -0700 Subject: [PATCH 29/40] Fix RemoteProcessPicker (#1373) * Allowing quote args flag to handle pipeProgram Also moved list process command to be written to a temp file and redirected to the pipeProgram. * Fixing syntax * Change bash to sh Fixing syntax since = is for equality in sh. Also tested with Mac -> Linux w/ ZSH * Fixing CR issues * Handle debuggerCommand * Replacing all instances of debuggerCommand in str. * Revert package.json and fix check for command --- package.json | 190 +++++++++++++++--------------- scripts/remoteProcessPickerScript | 1 + src/features/processPicker.ts | 71 +++++++---- 3 files changed, 144 insertions(+), 118 deletions(-) create mode 100644 scripts/remoteProcessPickerScript diff --git a/package.json b/package.json index 297199fcdd..c2cb5b13ac 100644 --- a/package.json +++ b/package.json @@ -1172,102 +1172,102 @@ } }, "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}\"" - } + { + "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": [ { @@ -1321,4 +1321,4 @@ } ] } -} +} \ 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/features/processPicker.ts b/src/features/processPicker.ts index f04e39b450..49688fa187 100644 --- a/src/features/processPicker.ts +++ b/src/features/processPicker.ts @@ -5,6 +5,8 @@ import * as os from 'os'; import * as vscode from 'vscode'; +import { getExtensionPath } from '../common'; +import * as path from 'path'; import * as child_process from 'child_process'; import { PlatformInformation } from '../platform'; @@ -65,17 +67,39 @@ 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}`; + 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, @@ -93,10 +117,18 @@ export class RemoteAttachPicker { let ret = ""; for (let arg of args) { - if (ret) { + if (ret) + { ret += " "; } - ret += `"${arg}"`; + + if (arg.includes(' ')) { + ret += `"${arg}"`; + } + else + { + ret += `${arg}`; + } } return ret; @@ -117,16 +149,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,26 +423,26 @@ 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; + 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); } From d4403d77031fbfb7d4e1e4f9c74dde5a1ff44ad2 Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Tue, 11 Apr 2017 11:57:54 -0700 Subject: [PATCH 30/40] Properly handle quoted arguments when splitting command line arguments from VS Test --- src/common.ts | 54 ++++++++++++++++++++++++++++++++++++++ src/features/dotnetTest.ts | 13 ++------- test/common.test.ts | 46 +++++++++++++++++++++++++++++++- 3 files changed, 101 insertions(+), 12 deletions(-) diff --git a/src/common.ts b/src/common.ts index 0c8be51bbd..8b3e0d4a42 100644 --- a/src/common.ts +++ b/src/common.ts @@ -117,3 +117,57 @@ 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/features/dotnetTest.ts b/src/features/dotnetTest.ts index 918cd6b79c..fbe1fb201c 100644 --- a/src/features/dotnetTest.ts +++ b/src/features/dotnetTest.ts @@ -8,6 +8,7 @@ import { toRange } from '../omnisharp/typeConvertion'; import * as vscode from 'vscode'; import * as serverUtils from "../omnisharp/utils"; import * as protocol from '../omnisharp/protocol'; +import * as utils from '../common'; let _testOutputChannel: vscode.OutputChannel = undefined; @@ -66,17 +67,7 @@ export function runDotnetTest(testMethod: string, fileName: string, testFramewor } function createLaunchConfiguration(program: string, argsString: string, cwd: string) { - let args = argsString.split(' '); - - // Ensure that quoted args are unquoted. - args = args.map(arg => { - if (arg.startsWith('"') && arg.endsWith('"')) { - return arg.substring(1, arg.length - 1); - } - else { - return arg; - } - }); + let args = utils.splitCommandLineArgs(argsString); return { name: ".NET Test Launch", 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 = []; From 731d509910c05eb96f6dddd8007b4959db143561 Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Tue, 11 Apr 2017 13:59:25 -0700 Subject: [PATCH 31/40] Don't create hidden diagnostics --- src/features/codeActionProvider.ts | 1 - src/features/diagnosticsProvider.ts | 25 ++++++++++++++++--------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/features/codeActionProvider.ts b/src/features/codeActionProvider.ts index 5c28558b9e..81b1646115 100644 --- a/src/features/codeActionProvider.ts +++ b/src/features/codeActionProvider.ts @@ -33,7 +33,6 @@ export default class OmnisharpCodeActionProvider extends AbstractProvider implem } public provideCodeActions(document: TextDocument, range: Range, context: CodeActionContext, token: CancellationToken): Promise { - if (this._disabled) { return; } diff --git a/src/features/diagnosticsProvider.ts b/src/features/diagnosticsProvider.ts index 44e2a4a06a..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; } } @@ -195,8 +193,10 @@ class DiagnosticsProvider extends AbstractSupport { let handle = setTimeout(() => { 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); }); @@ -230,7 +230,10 @@ class DiagnosticsProvider extends AbstractSupport { serverUtils.codeCheck(this._server, { FileName: null }, this._projectValidation.token).then(value => { - let quickFixes = value.QuickFixes.sort((a, b) => a.FileName.localeCompare(b.FileName)); + let quickFixes = value.QuickFixes + .filter(DiagnosticsProvider._shouldInclude) + .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; } } From 68905f6f4021119a0d8c35c400a543e95bac0a7b Mon Sep 17 00:00:00 2001 From: Gregg Miskelly Date: Thu, 13 Apr 2017 13:38:15 -0700 Subject: [PATCH 32/40] Update the debugger to 1.9.2 (#1389) This updates the debugger to the 1.9.2 version. This includes the support needed for unit test support and limited desktop CLR debugging. --- package.json | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index c2cb5b13ac..32f4670cdc 100644 --- a/package.json +++ b/package.json @@ -157,8 +157,8 @@ }, { "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" @@ -167,8 +167,8 @@ }, { "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" @@ -181,8 +181,8 @@ }, { "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" @@ -195,8 +195,8 @@ }, { "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" @@ -209,8 +209,8 @@ }, { "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" @@ -223,8 +223,8 @@ }, { "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" @@ -237,8 +237,8 @@ }, { "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" @@ -251,8 +251,8 @@ }, { "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" @@ -265,8 +265,8 @@ }, { "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" @@ -279,8 +279,8 @@ }, { "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" @@ -293,8 +293,8 @@ }, { "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" @@ -307,8 +307,8 @@ }, { "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" From 944cd1a4728d8c9f2c47cb416719ad136eb3845d Mon Sep 17 00:00:00 2001 From: Andrew Wang Date: Thu, 13 Apr 2017 13:42:15 -0700 Subject: [PATCH 33/40] Adding clr debugger (#1390) --- package.json | 681 ++++++++++++++++++++++++++++- src/coreclr-debug/install.ts | 12 +- src/tools/GenerateOptionsSchema.ts | 5 + 3 files changed, 696 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 32f4670cdc..b903edae22 100644 --- a/package.json +++ b/package.json @@ -1318,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/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/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)); } From 059a845f3168eeb97c4cb79b782a48af9b948e08 Mon Sep 17 00:00:00 2001 From: Gregg Miskelly Date: Tue, 11 Apr 2017 18:27:09 -0700 Subject: [PATCH 34/40] Work In progress: support for sending events from the debugger This checkin isn't quite complete as it needs to be tested on Mac and Linux, and I need to update debugger packages, but this is the start of adding a new mechanism for sending events from the debugger to the extension for unit testing support. --- src/coreclr-debug/debuggerEventsProtocol.ts | 42 ++++++++ src/features/dotnetTest.ts | 113 ++++++++++++++++++-- 2 files changed, 146 insertions(+), 9 deletions(-) create mode 100644 src/coreclr-debug/debuggerEventsProtocol.ts diff --git a/src/coreclr-debug/debuggerEventsProtocol.ts b/src/coreclr-debug/debuggerEventsProtocol.ts new file mode 100644 index 0000000000..0eb30f9fd4 --- /dev/null +++ b/src/coreclr-debug/debuggerEventsProtocol.ts @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * 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 enum EventType { + // Indicates that the vsdbg-ui has received the attach or launch request and is starting up + starting, + // Indicates that vsdbg-ui has successfully launched the specified process. + // The ProcessLaunchedEvent interface details the event payload. + processLaunched, + // Debug session is ending + debuggingStopped + } + + export interface DebuggerEvent { + eventType: EventType; + } + + 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/features/dotnetTest.ts b/src/features/dotnetTest.ts index fbe1fb201c..52de4d6f78 100644 --- a/src/features/dotnetTest.ts +++ b/src/features/dotnetTest.ts @@ -5,10 +5,14 @@ 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; @@ -66,13 +70,16 @@ export function runDotnetTest(testMethod: string, fileName: string, testFramewor }); } -function createLaunchConfiguration(program: string, argsString: string, cwd: string) { +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, @@ -80,7 +87,8 @@ function createLaunchConfiguration(program: string, argsString: string, cwd: str }; } -function getLaunchConfigurationForVSTest(server: OmniSharpServer, fileName: string, testMethod: string, testFrameworkName: string): Promise { +function getLaunchConfigurationForVSTest(server: OmniSharpServer, fileName: string, testMethod: string, testFrameworkName: string, debugEventListener: DebugEventListener): Promise { + const request: protocol.V2.DebugTestGetStartInfoRequest = { FileName: fileName, MethodName: testMethod, @@ -88,7 +96,7 @@ function getLaunchConfigurationForVSTest(server: OmniSharpServer, fileName: stri }; return serverUtils.debugTestGetStartInfo(server, request) - .then(response => createLaunchConfiguration(response.FileName, response.Arguments, response.WorkingDirectory)); + .then(response => createLaunchConfiguration(response.FileName, response.Arguments, response.WorkingDirectory, debugEventListener.pipePath())); } function getLaunchConfigurationForLegacy(server: OmniSharpServer, fileName: string, testMethod: string, testFrameworkName: string): Promise { @@ -99,16 +107,16 @@ function getLaunchConfigurationForLegacy(server: OmniSharpServer, fileName: stri }; return serverUtils.getTestStartInfo(server, request) - .then(response => createLaunchConfiguration(response.Executable, response.Argument, response.WorkingDirectory)); + .then(response => createLaunchConfiguration(response.Executable, response.Argument, response.WorkingDirectory, null)); } -function getLaunchConfiguration(server: OmniSharpServer, debugType: string, fileName: string, testMethod: string, testFrameworkName: string): Promise { +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); + return getLaunchConfigurationForVSTest(server, fileName, testMethod, testFrameworkName, debugEventListener); default: throw new Error(`Unexpected debug type: ${debugType}`); @@ -120,20 +128,25 @@ export function debugDotnetTest(testMethod: string, fileName: string, testFramew // 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; return serverUtils.requestProjectInformation(server, { FileName: fileName} ) .then(projectInfo => { if (projectInfo.DotNetProject) { debugType = "legacy"; + return Promise.resolve(); } else if (projectInfo.MsBuildProject) { debugType = "vstest"; + debugEventListener = new DebugEventListener(); + return debugEventListener.start(); } else { throw new Error(); } - - return getLaunchConfiguration(server, debugType, fileName, testMethod, testFrameworkName); + }) + .then(() => { + return getLaunchConfiguration(server, debugType, fileName, testMethod, testFrameworkName, debugEventListener); }) .then(config => vscode.commands.executeCommand('vscode.startDebug', config)) .then(() => { @@ -143,7 +156,12 @@ export function debugDotnetTest(testMethod: string, fileName: string, testFramew serverUtils.debugTestRun(server, { FileName: fileName }); } }) - .catch(reason => vscode.window.showErrorMessage(`Failed to start debugger: ${reason}`)); + .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) { @@ -166,4 +184,81 @@ 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; + _serverSocket : net.Server; + _pipePath : string; + _outputChannel : vscode.OutputChannel; + + constructor() { + // 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); + } + + this._outputChannel = getTestOutputChannel(); + } + + 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; + + // TODO: On Unix do we need to unlink file if it exists already? + + 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; + } + + if (event.eventType === DebuggerEventsProtocol.EventType.debuggingStopped) { + this.fireDebuggingStopped(); + } + }); + + socket.on('end', () => { + this.fireDebuggingStopped(); + }); + }); + + return new Promise((resolve, reject) => { + this._serverSocket.listen(this._pipePath, () => { + resolve(); + }); + }); + } + + public pipePath() : string { + return this._pipePath; + } + + public close() { + if (this === DebugEventListener.s_activeInstance) { + DebugEventListener.s_activeInstance = null; + } + + if (this._serverSocket !== null) { + this._serverSocket.close(); + } + } + + private fireDebuggingStopped() : void { + // TODO: notify omniSharp + + this.close(); + } } \ No newline at end of file From c0acfe70a975d36ba08fb34c2afcc652874c25ed Mon Sep 17 00:00:00 2001 From: Gregg Miskelly Date: Wed, 12 Apr 2017 12:33:44 -0700 Subject: [PATCH 35/40] Fix Linux, 'EventType' declaration, and add '_isClosed' - Added code to unlink the unix domain socket (in case there is one around from a previous reboot). - I forgot that TypeScript enums don't behave quite like what I wanted. This switches from an enum to a module. - Fixes DebugEventListener to cleanup only once. - Handle errors in DebugEventListener --- src/assets.ts | 21 +---------- src/common.ts | 19 ++++++++++ src/coreclr-debug/debuggerEventsProtocol.ts | 13 ++++--- src/features/dotnetTest.ts | 41 ++++++++++++++++++--- 4 files changed, 63 insertions(+), 31 deletions(-) diff --git a/src/assets.ts b/src/assets.ts index 96f38f89bf..e62535eda8 100644 --- a/src/assets.ts +++ b/src/assets.ts @@ -541,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) ]); } diff --git a/src/common.ts b/src/common.ts index 8b3e0d4a42..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 diff --git a/src/coreclr-debug/debuggerEventsProtocol.ts b/src/coreclr-debug/debuggerEventsProtocol.ts index 0eb30f9fd4..94b140ce87 100644 --- a/src/coreclr-debug/debuggerEventsProtocol.ts +++ b/src/coreclr-debug/debuggerEventsProtocol.ts @@ -10,18 +10,19 @@ // // All messages are sent as UTF-8 JSON text with a tailing '\n' export namespace DebuggerEventsProtocol { - export enum EventType { + export module EventType { // Indicates that the vsdbg-ui has received the attach or launch request and is starting up - starting, + export const Starting = "starting"; // Indicates that vsdbg-ui has successfully launched the specified process. // The ProcessLaunchedEvent interface details the event payload. - processLaunched, + export const ProcessLaunched = "processLaunched"; // Debug session is ending - debuggingStopped - } + export const DebuggingStopped = "debuggingStopped"; + }; export interface DebuggerEvent { - eventType: EventType; + // Contains one of the 'DebuggerEventsProtocol.EventType' values + eventType: string; } export interface ProcessLaunchedEvent extends DebuggerEvent { diff --git a/src/features/dotnetTest.ts b/src/features/dotnetTest.ts index 52de4d6f78..d73f562e5c 100644 --- a/src/features/dotnetTest.ts +++ b/src/features/dotnetTest.ts @@ -191,6 +191,7 @@ class DebugEventListener { _serverSocket : net.Server; _pipePath : string; _outputChannel : vscode.OutputChannel; + _isClosed: boolean = false; constructor() { // NOTE: The max pipe name on OSX is fairly small, so this name shouldn't bee too long. @@ -211,8 +212,6 @@ class DebugEventListener { DebugEventListener.s_activeInstance.close(); } DebugEventListener.s_activeInstance = this; - - // TODO: On Unix do we need to unlink file if it exists already? this._serverSocket = net.createServer((socket: net.Socket) => { socket.on('data', (buffer: Buffer) => { @@ -225,7 +224,9 @@ class DebugEventListener { return; } - if (event.eventType === DebuggerEventsProtocol.EventType.debuggingStopped) { + if (event.eventType === DebuggerEventsProtocol.EventType.ProcessLaunched) { + // TODO: notify OmniSharp + } else if (event.eventType === DebuggerEventsProtocol.EventType.DebuggingStopped) { this.fireDebuggingStopped(); } }); @@ -235,9 +236,20 @@ class DebugEventListener { }); }); - return new Promise((resolve, reject) => { - this._serverSocket.listen(this._pipePath, () => { - resolve(); + 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(); + }); }); }); } @@ -250,6 +262,10 @@ class DebugEventListener { if (this === DebugEventListener.s_activeInstance) { DebugEventListener.s_activeInstance = null; } + if (this._isClosed) { + return; + } + this._isClosed = true; if (this._serverSocket !== null) { this._serverSocket.close(); @@ -257,8 +273,21 @@ class DebugEventListener { } private fireDebuggingStopped() : void { + if (this._isClosed) { + return; + } + // TODO: notify omniSharp 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 From 974fc894ac6bc46caea43217ba1e073ad89b2611 Mon Sep 17 00:00:00 2001 From: Gregg Miskelly Date: Thu, 13 Apr 2017 11:34:10 -0700 Subject: [PATCH 36/40] Complete work for PR 1379 - Add a bit more logging status - Move when OmniSharp is invoked --- src/features/dotnetTest.ts | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/features/dotnetTest.ts b/src/features/dotnetTest.ts index d73f562e5c..8b5b58ce7f 100644 --- a/src/features/dotnetTest.ts +++ b/src/features/dotnetTest.ts @@ -129,6 +129,8 @@ export function debugDotnetTest(testMethod: string, fileName: string, testFramew // 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 => { @@ -138,7 +140,7 @@ export function debugDotnetTest(testMethod: string, fileName: string, testFramew } else if (projectInfo.MsBuildProject) { debugType = "vstest"; - debugEventListener = new DebugEventListener(); + debugEventListener = new DebugEventListener(fileName, server, outputChannel); return debugEventListener.start(); } else { @@ -149,13 +151,6 @@ export function debugDotnetTest(testMethod: string, fileName: string, testFramew return getLaunchConfiguration(server, debugType, fileName, testMethod, testFrameworkName, debugEventListener); }) .then(config => vscode.commands.executeCommand('vscode.startDebug', config)) - .then(() => { - // For VS Test, we need to signal to start the test run after the debugger has launched. - // TODO: Need to find out when the debugger has actually launched. This is currently a race. - if (debugType === "vstest") { - serverUtils.debugTestRun(server, { FileName: fileName }); - } - }) .catch(reason => { vscode.window.showErrorMessage(`Failed to start debugger: ${reason}`); if (debugEventListener != null) { @@ -188,12 +183,18 @@ export function updateCodeLensForTest(bucket: vscode.CodeLens[], fileName: strin class DebugEventListener { static s_activeInstance : DebugEventListener = null; - _serverSocket : net.Server; - _pipePath : string; + _fileName: string; + _server: OmniSharpServer; _outputChannel : vscode.OutputChannel; + _pipePath : string; + + _serverSocket : net.Server; _isClosed: boolean = false; - constructor() { + 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') { @@ -201,8 +202,6 @@ class DebugEventListener { } else { this._pipePath = path.join(utils.getExtensionPath(), "." + pipeSuffix); } - - this._outputChannel = getTestOutputChannel(); } public start() : Promise { @@ -225,8 +224,12 @@ class DebugEventListener { } if (event.eventType === DebuggerEventsProtocol.EventType.ProcessLaunched) { - // TODO: notify OmniSharp + let processLaunchedEvent = (event); + this._outputChannel.appendLine(`Started debugging process #${processLaunchedEvent.targetProcessId}.`); + // TODO: provide the process id to OmniSharp + serverUtils.debugTestRun(this._server, { FileName: this._fileName }); } else if (event.eventType === DebuggerEventsProtocol.EventType.DebuggingStopped) { + this._outputChannel.appendLine("Debugging complete."); this.fireDebuggingStopped(); } }); @@ -278,7 +281,7 @@ class DebugEventListener { } // TODO: notify omniSharp - + this.close(); } From 61d761dd8647c3ac9e225c2e55096b53533a0415 Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Mon, 17 Apr 2017 13:32:13 -0700 Subject: [PATCH 37/40] Consume debugger events and notify OmniSharp --- package.json | 6 +-- src/features/dotnetTest.ts | 80 +++++++++++++++++++++++++------------- src/omnisharp/protocol.ts | 20 ++++++---- src/omnisharp/utils.ts | 10 +++-- 4 files changed, 75 insertions(+), 41 deletions(-) diff --git a/package.json b/package.json index b903edae22..54f5b6d03d 100644 --- a/package.json +++ b/package.json @@ -123,7 +123,7 @@ }, { "description": "OmniSharp (.NET 4.6 / x86)", - "url": "https://omnisharpdownload.blob.core.windows.net/ext/omnisharp-win-x86-1.14.0.1.zip", + "url": "https://omnisharpdownload.blob.core.windows.net/ext/omnisharp-win-x86-1.14.0.2.zip", "installPath": "./bin/omnisharp", "platforms": [ "win32" @@ -135,7 +135,7 @@ }, { "description": "OmniSharp (.NET 4.6 / x64)", - "url": "https://omnisharpdownload.blob.core.windows.net/ext/omnisharp-win-x64-1.14.0.1.zip", + "url": "https://omnisharpdownload.blob.core.windows.net/ext/omnisharp-win-x64-1.14.0.2.zip", "installPath": "./bin/omnisharp", "platforms": [ "win32" @@ -147,7 +147,7 @@ }, { "description": "OmniSharp (Mono 4.6)", - "url": "https://omnisharpdownload.blob.core.windows.net/ext/omnisharp-mono-1.14.0.1.zip", + "url": "https://omnisharpdownload.blob.core.windows.net/ext/omnisharp-mono-1.14.0.2.zip", "installPath": "./bin/omnisharp", "platforms": [ "darwin", diff --git a/src/features/dotnetTest.ts b/src/features/dotnetTest.ts index 8b5b58ce7f..aea373f4ea 100644 --- a/src/features/dotnetTest.ts +++ b/src/features/dotnetTest.ts @@ -132,7 +132,7 @@ export function debugDotnetTest(testMethod: string, fileName: string, testFramew let outputChannel = getTestOutputChannel(); outputChannel.appendLine(`Debugging method '${testMethod}'.`); - return serverUtils.requestProjectInformation(server, { FileName: fileName} ) + return serverUtils.requestProjectInformation(server, { FileName: fileName }) .then(projectInfo => { if (projectInfo.DotNetProject) { debugType = "legacy"; @@ -147,9 +147,7 @@ export function debugDotnetTest(testMethod: string, fileName: string, testFramew throw new Error(); } }) - .then(() => { - return getLaunchConfiguration(server, debugType, fileName, testMethod, testFrameworkName, debugEventListener); - }) + .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}`); @@ -182,13 +180,13 @@ export function updateCodeLensForTest(bucket: vscode.CodeLens[], fileName: strin } class DebugEventListener { - static s_activeInstance : DebugEventListener = null; + static s_activeInstance: DebugEventListener = null; _fileName: string; _server: OmniSharpServer; - _outputChannel : vscode.OutputChannel; - _pipePath : string; + _outputChannel: vscode.OutputChannel; + _pipePath: string; - _serverSocket : net.Server; + _serverSocket: net.Server; _isClosed: boolean = false; constructor(fileName: string, server: OmniSharpServer, outputChannel: vscode.OutputChannel) { @@ -204,38 +202,41 @@ class DebugEventListener { } } - public start() : Promise { + 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) { + } + catch (e) { this._outputChannel.appendLine("Warning: Invalid event received from debugger"); return; } - - if (event.eventType === DebuggerEventsProtocol.EventType.ProcessLaunched) { - let processLaunchedEvent = (event); - this._outputChannel.appendLine(`Started debugging process #${processLaunchedEvent.targetProcessId}.`); - // TODO: provide the process id to OmniSharp - serverUtils.debugTestRun(this._server, { FileName: this._fileName }); - } else if (event.eventType === DebuggerEventsProtocol.EventType.DebuggingStopped) { - this._outputChannel.appendLine("Debugging complete."); - this.fireDebuggingStopped(); + + 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.fireDebuggingStopped(); + this.onDebuggingStopped(); }); }); @@ -257,7 +258,7 @@ class DebugEventListener { }); } - public pipePath() : string { + public pipePath(): string { return this._pipePath; } @@ -265,9 +266,11 @@ class DebugEventListener { if (this === DebugEventListener.s_activeInstance) { DebugEventListener.s_activeInstance = null; } + if (this._isClosed) { return; } + this._isClosed = true; if (this._serverSocket !== null) { @@ -275,21 +278,42 @@ class DebugEventListener { } } - private fireDebuggingStopped() : void { + 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; } - - // TODO: notify omniSharp + + let request: protocol.V2.DebugTestStopRequest = { + FileName: this._fileName + }; + + serverUtils.debugTestStop(this._server, request); this.close(); } - private removeSocketFileIfExists() : Promise { + private removeSocketFileIfExists(): Promise { if (os.platform() === 'win32') { // Win32 doesn't use the file system for pipe names return Promise.resolve(); - } else { + } + else { return utils.deleteIfExists(this._pipePath); } } diff --git a/src/omnisharp/protocol.ts b/src/omnisharp/protocol.ts index a35344b523..2e03c51d34 100644 --- a/src/omnisharp/protocol.ts +++ b/src/omnisharp/protocol.ts @@ -67,13 +67,11 @@ export interface Request { 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 { @@ -422,7 +420,8 @@ export namespace V2 { export const GetTestStartInfo = '/v2/getteststartinfo'; export const RunTest = '/v2/runtest'; export const DebugTestGetStartInfo = '/v2/debugtest/getstartinfo'; - export const DebugTestRun = '/v2/debugtest/run'; + export const DebugTestLaunch = '/v2/debugtest/launch'; + export const DebugTestStop = '/v2/debugtest/stop'; } export interface Point { @@ -510,10 +509,17 @@ export namespace V2 { EnvironmentVariables: Map; } - export interface DebugTestRunRequest extends Request { + export interface DebugTestLaunchRequest extends Request { + TargetProcessId: number; } - export interface DebugTestRunResponse { + export interface DebugTestLaunchResponse { + } + + export interface DebugTestStopRequest extends Request { + } + + export interface DebugTestStopResponse { } export interface GetTestStartInfoRequest extends Request { diff --git a/src/omnisharp/utils.ts b/src/omnisharp/utils.ts index 2758289bca..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'; @@ -97,8 +97,12 @@ export function debugTestGetStartInfo(server: OmniSharpServer, request: protocol return server.makeRequest(protocol.V2.Requests.DebugTestGetStartInfo, request); } -export function debugTestRun(server: OmniSharpServer, request: protocol.V2.DebugTestRunRequest) { - return server.makeRequest(protocol.V2.Requests.DebugTestRun, 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) { From 2d6b3477d6f680a321b7acae369acd5598caa3d6 Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Wed, 19 Apr 2017 09:52:20 -0700 Subject: [PATCH 38/40] Add recent updates to changelog for 1.9 --- CHANGELOG.md | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db96814b8d..a67182f368 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## 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)) @@ -6,18 +6,40 @@ ## 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. +* **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 ([#1318](https://github.com/OmniSharp/omnisharp-vscode/issues/1318), [#1335](https://github.com/OmniSharp/omnisharp-vscode/issues/1335), [#1336](https://github.com/OmniSharp/omnisharp-vscode/issues/1336)) * 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)). +#### 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)) -* Assets for building and debugging are now always generated with POSIX style paths. ([#1354](https://github.com/OmniSharp/omnisharp-vscode/pull/1354)) +* 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) From 6f539044c56ba1121b364f85277cc45bd65cfbbe Mon Sep 17 00:00:00 2001 From: Andrew Wang Date: Wed, 19 Apr 2017 11:02:24 -0700 Subject: [PATCH 39/40] Fixing processPicker for WSL (#1405) * Returning error on processPick and validate prog child_process.exec will spawn a 32 bit process which C:\Windows\System32\bash.exe will not exist and fail for process picker. Will attempt to try to run C:\Windows\sysnative\bash.exe for process picker. This is not an issue if users just use bash.exe for pipeProgram. * Fixing style * Fixing PR Issues Validating sysnative program before replacing. * Reorganizing conditions --- src/features/processPicker.ts | 96 +++++++++++++++++++++++------------ 1 file changed, 63 insertions(+), 33 deletions(-) diff --git a/src/features/processPicker.ts b/src/features/processPicker.ts index 49688fa187..b7c4737ce0 100644 --- a/src/features/processPicker.ts +++ b/src/features/processPicker.ts @@ -6,6 +6,7 @@ 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'; @@ -45,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) { @@ -78,36 +108,38 @@ export class RemoteAttachPicker { quoteArgs = platformSpecificPipeTransportOptions.pipeTransport.quoteArgs != null ? platformSpecificPipeTransportOptions.pipeTransport.quoteArgs : quoteArgs; } - let pipeCmdList: string[] = []; - let scriptShellCmd: string = "sh -s"; - pipeCmdList.push(pipeProgram); + return RemoteAttachPicker.ValidateAndFixPipeProgram(pipeProgram).then(pipeProgram => { + let pipeCmdList: string[] = []; + let scriptShellCmd: string = "sh -s"; + pipeCmdList.push(pipeProgram); + + const debuggerCommandString: string = "${debuggerCommand}"; - 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); + } - 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); } - - pipeCmdList.push(arg); } - } - else { - pipeCmdList = pipeCmdList.concat(pipeArgs); - pipeCmdList.push(scriptShellCmd); - } + else { + pipeCmdList = pipeCmdList.concat(pipeArgs); + pipeCmdList.push(scriptShellCmd); + } - let pipeCmd: string = quoteArgs ? this.createArgumentList(pipeCmdList) : pipeCmdList.join(' '); + 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.")); + 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.")); + }); }); }); } @@ -117,16 +149,14 @@ export class RemoteAttachPicker { let ret = ""; for (let arg of args) { - if (ret) - { + if (ret) { ret += " "; } - + if (arg.includes(' ')) { ret += `"${arg}"`; } - else - { + else { ret += `${arg}`; } } @@ -426,7 +456,7 @@ function execChildProcess(process: string, workingDirectory: string): Promise { return PlatformInformation.GetCurrent().then(platformInfo => { let env = process.env; - if (platformInfo.isWindows && platformInfo.architecture === "x86_64") { + if (platformInfo.isWindows() && platformInfo.architecture === "x86_64") { let sysnative: String = process.env.WINDIR + "\\sysnative"; env.Path = process.env.PATH + ";" + sysnative; } @@ -444,15 +474,15 @@ function execChildProcessAndOutputErrorToChannel(process: string, workingDirecto 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); } From e9ab813a461f12e11516e88680013016ec5a6fd3 Mon Sep 17 00:00:00 2001 From: Gregg Miskelly Date: Thu, 20 Apr 2017 15:55:17 -0700 Subject: [PATCH 40/40] Debugger doc updates for 1.9 (#1408) - Update the changelog - Add link to WSL documentation --- CHANGELOG.md | 5 ++++- debugger-launchjson.md | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a67182f368..d2d641d83f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,9 +13,12 @@ #### 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 ([#1318](https://github.com/OmniSharp/omnisharp-vscode/issues/1318), [#1335](https://github.com/OmniSharp/omnisharp-vscode/issues/1335), [#1336](https://github.com/OmniSharp/omnisharp-vscode/issues/1336)) +* 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 diff --git a/debugger-launchjson.md b/debugger-launchjson.md index 0abba3ae91..5cf6658479 100644 --- a/debugger-launchjson.md +++ b/debugger-launchjson.md @@ -104,6 +104,8 @@ then add the pipeTransport field folloing this schema: 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.