diff --git a/.vscode/launch.json b/.vscode/launch.json index d83318d936..1b65dacf85 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -41,7 +41,9 @@ "request": "launch", "runtimeExecutable": "${execPath}", "args": [ - "--disable-extensions", + // Create a temp profile that has no extensions / user settings. + // This allows us to only have the C# extension + the dotnet runtime installer extension dependency. + "--profile-temp", "--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test/featureTests" ], @@ -63,7 +65,9 @@ "request": "launch", "runtimeExecutable": "${execPath}", "args": [ - "--disable-extensions", + // Create a temp profile that has no extensions / user settings. + // This allows us to only have the C# extension + the dotnet runtime installer extension dependency. + "--profile-temp", "${workspaceRoot}/test/integrationTests/testAssets/singleCsproj", "--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test/integrationTests" @@ -78,7 +82,8 @@ }, "sourceMaps": true, "outFiles": [ - "${workspaceRoot}/dist/*.js" + "${workspaceRoot}/dist/*.js", + "${workspaceRoot}/out/test/**/*.js" ], "preLaunchTask": "buildDev" }, @@ -88,7 +93,9 @@ "request": "launch", "runtimeExecutable": "${execPath}", "args": [ - "--disable-extensions", + // Create a temp profile that has no extensions / user settings. + // This allows us to only have the C# extension + the dotnet runtime installer extension dependency. + "--profile-temp", "${workspaceRoot}/test/integrationTests/testAssets/BasicRazorApp2_1", "--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test/integrationTests" @@ -108,7 +115,9 @@ "request": "launch", "runtimeExecutable": "${execPath}", "args": [ - "--disable-extensions", + // Create a temp profile that has no extensions / user settings. + // This allows us to only have the C# extension + the dotnet runtime installer extension dependency. + "--profile-temp", "${workspaceRoot}/test/integrationTests/testAssets/slnWithCsproj", "--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test/integrationTests" @@ -124,7 +133,8 @@ "stopOnEntry": false, "sourceMaps": true, "outFiles": [ - "${workspaceRoot}/dist/*.js" + "${workspaceRoot}/dist/*.js", + "${workspaceRoot}/out/test/**/*.js" ], "preLaunchTask": "buildDev" }, @@ -134,7 +144,9 @@ "request": "launch", "runtimeExecutable": "${execPath}", "args": [ - "--disable-extensions", + // Create a temp profile that has no extensions / user settings. + // This allows us to only have the C# extension + the dotnet runtime installer extension dependency. + "--profile-temp", "${workspaceRoot}/test/integrationTests/testAssets/singleCsproj", "--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test/integrationTests" @@ -150,7 +162,8 @@ "stopOnEntry": false, "sourceMaps": true, "outFiles": [ - "${workspaceRoot}/dist/*.js" + "${workspaceRoot}/dist/*.js", + "${workspaceRoot}/out/test/**/*.js" ], "preLaunchTask": "buildDev" }, @@ -160,7 +173,9 @@ "request": "launch", "runtimeExecutable": "${execPath}", "args": [ - "--disable-extensions", + // Create a temp profile that has no extensions / user settings. + // This allows us to only have the C# extension + the dotnet runtime installer extension dependency. + "--profile-temp", "${workspaceRoot}/test/integrationTests/testAssets/BasicRazorApp2_1", "--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test/integrationTests" @@ -180,7 +195,9 @@ "request": "launch", "runtimeExecutable": "${execPath}", "args": [ - "--disable-extensions", + // Create a temp profile that has no extensions / user settings. + // This allows us to only have the C# extension + the dotnet runtime installer extension dependency. + "--profile-temp", "${workspaceRoot}/test/integrationTests/testAssets/slnWithCsproj", "--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test/integrationTests" @@ -195,7 +212,8 @@ }, "sourceMaps": true, "outFiles": [ - "${workspaceRoot}/dist/*.js" + "${workspaceRoot}/dist/*.js", + "${workspaceRoot}/out/test/**/*.js" ], "preLaunchTask": "buildDev" }, @@ -205,7 +223,9 @@ "request": "launch", "runtimeExecutable": "${execPath}", "args": [ - "--disable-extensions", + // Create a temp profile that has no extensions / user settings. + // This allows us to only have the C# extension + the dotnet runtime installer extension dependency. + "--profile-temp", "${workspaceRoot}/test/integrationTests/testAssets/slnFilterWithCsproj", "--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test/integrationTests" @@ -219,7 +239,8 @@ }, "sourceMaps": true, "outFiles": [ - "${workspaceRoot}/dist/*.js" + "${workspaceRoot}/dist/*.js", + "${workspaceRoot}/out/test/**/*.js" ], "preLaunchTask": "buildDev" }, @@ -229,7 +250,9 @@ "request": "launch", "runtimeExecutable": "${execPath}", "args": [ - "--disable-extensions", + // Create a temp profile that has no extensions / user settings. + // This allows us to only have the C# extension + the dotnet runtime installer extension dependency. + "--profile-temp", "${workspaceRoot}/test/integrationTests/testAssets/slnWithGenerator", "--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test/integrationTests" @@ -243,7 +266,8 @@ }, "sourceMaps": true, "outFiles": [ - "${workspaceRoot}/dist/*.js" + "${workspaceRoot}/dist/*.js", + "${workspaceRoot}/out/test/**/*.js" ], "preLaunchTask": "buildDev" }, diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 742a01e475..af9fc956f6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,7 +23,7 @@ To **test** do the following: `npm run test` or F5 in VS Code with th ### Using a locally developed Roslyn server -https://github.com/dotnet/roslyn contains the server implementation. Follow the instructions there to build the repo as normal. Once built, the server executable will be located in the build output directory, typically +https://github.com/dotnet/roslyn contains the server implementation. Follow the instructions there to build the repo as normal. Once built, the server executable will be located in the build output directory, typically `$roslynRepoRoot/artifacts/bin/Microsoft.CodeAnalysis.LanguageServer/Debug/net7.0/Microsoft.CodeAnalysis.LanguageServer.exe` @@ -31,6 +31,16 @@ depending on which configuration is built. Then, launch the extension here and If you need to debug the server, you can set the VSCode setting `dotnet.server.waitForDebugger` to true. This will trigger a `Debugger.Launch()` on the server side as it starts. +### Using a locally developed Razor server + +https://github.com/dotnet/razor contains the server implementation. Follow the instructions there to build the repo as normal. Once built, the server will be located in the build output directory, typically + +`$razorRepoRoot/artifacts/bin/rzls/Debug/net7.0` + +depending on which configuration is built. Then, launch the extension here and change the VSCode setting `razor.languageServer.directory` to point to the Razor executable path you built above and reload the window. + +If you need to debug the server, you can set the VSCode setting `razor.languageServer.debug` to true. This will trigger a `Debugger.Launch()` on the server side as it starts. You can also set `razor.trace` to `Verbose` to get more log messages in the output window + ### Creating VSIXs VSIXs can be created using the gulp command `gulp vsix:release:package`. This will create all the platform specific VSIXs that you can then install manually in VSCode. @@ -42,4 +52,4 @@ To update the version of the roslyn server used by the extension do the followin 2. In the official build stage, look for the `Publish Assets` step. In there you will see it publishing the `Microsoft.CodeAnalysis.LanguageServer` package with some version, e.g. `4.6.0-3.23158.4`. Take note of that version number. 3. In the [package.json](package.json) inside the `defaults` section update the `roslyn` key to point to the version number you found above in step 2. 4. Build and test the change (make sure to run `gulp installDependencies` to get the new version!). If everything looks good, submit a PR. - * Adding new package versions might require authentication, run with the `--interactive` flag to login. You may need to install [azure artifacts nuget credential provider](https://github.com/microsoft/artifacts-credprovider#installation-on-windows) to run interactive authentication. \ No newline at end of file + * Adding new package versions might require authentication, run with the `--interactive` flag to login. You may need to install [azure artifacts nuget credential provider](https://github.com/microsoft/artifacts-credprovider#installation-on-windows) to run interactive authentication. diff --git a/package.json b/package.json index 95491ca75a..ade7df82c9 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "displayName": "C#", "author": "Microsoft Corporation", "license": "SEE LICENSE IN RuntimeLicenses/license.txt", + "qna": "https://github.com/dotnet/vscode-csharp/issues", "icon": "images/csharpIcon.png", "preview": false, "bugs": { @@ -35,7 +36,7 @@ } }, "defaults": { - "roslyn": "4.7.0-3.23316.4", + "roslyn": "4.7.0-3.23326.2", "omniSharp": "1.39.6", "razor": "7.0.0-preview.23275.2" }, diff --git a/src/lsptoolshost/roslynLanguageServer.ts b/src/lsptoolshost/roslynLanguageServer.ts index ba17ea9c11..edee850ef2 100644 --- a/src/lsptoolshost/roslynLanguageServer.ts +++ b/src/lsptoolshost/roslynLanguageServer.ts @@ -433,7 +433,7 @@ export class RoslynLanguageServer { if (serverPath.endsWith('.dll')) { // If we were given a path to a dll, launch that via dotnet. const argsWithPath = [ serverPath ].concat(args); - childProcess = cp.spawn('dotnet', argsWithPath, cpOptions); + childProcess = cp.spawn(dotnetExecutablePath, argsWithPath, cpOptions); } else { // Otherwise assume we were given a path to an executable. childProcess = cp.spawn(serverPath, args, cpOptions); diff --git a/src/razor/src/Completion/RazorCompletionItemProvider.ts b/src/razor/src/Completion/RazorCompletionItemProvider.ts index 029735744b..a4c177ac22 100644 --- a/src/razor/src/Completion/RazorCompletionItemProvider.ts +++ b/src/razor/src/Completion/RazorCompletionItemProvider.ts @@ -13,7 +13,7 @@ import { getUriPath } from '../UriPaths'; import { ProvisionalCompletionOrchestrator } from './ProvisionalCompletionOrchestrator'; import { LanguageKind } from '../RPC/LanguageKind'; import { RoslynLanguageServer } from '../../../lsptoolshost/roslynLanguageServer'; -import { CompletionItem, CompletionParams, CompletionTriggerKind } from 'vscode-languageclient'; +import { CompletionItem, CompletionList, CompletionParams, CompletionTriggerKind, MarkupContent } from 'vscode-languageclient'; import { UriConverter } from '../../../lsptoolshost/uriConverter'; import * as RazorConventions from '../RazorConventions'; import { MappingHelpers } from '../Mapping/MappingHelpers'; @@ -35,11 +35,6 @@ export class RazorCompletionItemProvider let completions: vscode.CompletionList | vscode.CompletionItem[]; - // For CSharp, completions need to keep the "data" field - // on the completion item for lazily resolving the edits in - // the resolveCompletionItem step. Using the vs code command - // drops that field because it doesn't exist in the declared vs code - // CompletionItem type. if (language === LanguageKind.CSharp) { const params: CompletionParams = { context: { @@ -52,6 +47,11 @@ export class RazorCompletionItemProvider position: projectedPosition }; + // For CSharp, completions need to keep the "data" field on the + // completion item for lazily resolving the edits in the + // resolveCompletionItem step. Using the vs code command drops + // that field because it doesn't exist in the declared vs code + // CompletionItem type. completions = await vscode .commands .executeCommand( @@ -71,6 +71,8 @@ export class RazorCompletionItemProvider completions instanceof Array ? completions // was vscode.CompletionItem[] : completions ? completions.items // was vscode.CompletionList : []; + + const data = (completions)?.itemDefaults?.data; // There are times when the generated code will not line up with the content of the .razor/.cshtml file. // Therefore, we need to offset all completion items' characters by a certain amount in order @@ -79,7 +81,7 @@ export class RazorCompletionItemProvider const completionCharacterOffset = projectedPosition.character - hostDocumentPosition.character; for (const completionItem of completionItems) { const doc = completionItem.documentation as vscode.MarkdownString; - if (doc) { + if (doc && doc.value) { // Without this, the documentation doesn't get rendered in the editor. const newDoc = new vscode.MarkdownString(doc.value); newDoc.isTrusted = doc.isTrusted; @@ -128,6 +130,10 @@ export class RazorCompletionItemProvider completionItem.insertText = intellicodeCompletion.textEditText; } } + + if (!(completionItem).data) { + (completionItem).data = data; + } } const isIncomplete = completions instanceof Array ? false @@ -204,6 +210,13 @@ export class RazorCompletionItemProvider item = newItem; + // The documentation object Roslyn returns is a MarkupContent, + // which we need to convert to a MarkdownString. + const markupContent = ((item.documentation)); + if (markupContent && markupContent.value) { + item.documentation = new vscode.MarkdownString(markupContent.value); + } + if (item.command && item.command.arguments?.length === 4) { let uri = vscode.Uri.parse(item.command.arguments[0]); @@ -238,4 +251,3 @@ function getTriggerKind(triggerKind: vscode.CompletionTriggerKind): CompletionTr } } - diff --git a/test/integrationTests/integrationHelpers.ts b/test/integrationTests/integrationHelpers.ts index 439d429357..f86464076c 100644 --- a/test/integrationTests/integrationHelpers.ts +++ b/test/integrationTests/integrationHelpers.ts @@ -16,6 +16,14 @@ export interface ActivationResult { } export async function activateCSharpExtension(): Promise { + // Ensure the dependent extension exists - when launching via F5 launch.json we can't install the extension prior to opening vscode. + const vscodeDotnetRuntimeExtensionId = "ms-dotnettools.vscode-dotnet-runtime"; + let dotnetRuntimeExtension = vscode.extensions.getExtension(vscodeDotnetRuntimeExtensionId); + if (!dotnetRuntimeExtension) { + await vscode.commands.executeCommand("workbench.extensions.installExtension", vscodeDotnetRuntimeExtensionId); + await vscode.commands.executeCommand("workbench.action.reloadWindow"); + } + const configuration = vscode.workspace.getConfiguration(); configuration.update('omnisharp.enableLspDriver', process.env.OMNISHARP_DRIVER === 'lsp' ? true : false); if (process.env.OMNISHARP_LOCATION) {