From dd74f1e93a6ceae5840aea1a449892de5e5470f9 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Wed, 25 Oct 2023 17:44:25 -0700 Subject: [PATCH 1/4] Add support for nuget restore commands --- l10n/bundle.l10n.json | 3 + package.json | 4 +- src/lsptoolshost/restore.ts | 108 +++++++++++++++++++++++ src/lsptoolshost/roslynLanguageServer.ts | 4 + src/lsptoolshost/roslynProtocol.ts | 31 +++++++ src/main.ts | 3 +- 6 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 src/lsptoolshost/restore.ts diff --git a/l10n/bundle.l10n.json b/l10n/bundle.l10n.json index 859647485..91891bbcf 100644 --- a/l10n/bundle.l10n.json +++ b/l10n/bundle.l10n.json @@ -127,6 +127,9 @@ "Choose": "Choose", "Choose and set default": "Choose and set default", "Do not load any": "Do not load any", + "Restore already in progress": "Restore already in progress", + "Restore": "Restore", + "Sending request": "Sending request", "C# configuration has changed. Would you like to reload the window to apply your changes?": "C# configuration has changed. Would you like to reload the window to apply your changes?", "Pick a fix all scope": "Pick a fix all scope", "Fix All Code Action": "Fix All Code Action", diff --git a/package.json b/package.json index 303cb246d..d34188e12 100644 --- a/package.json +++ b/package.json @@ -2008,13 +2008,13 @@ "command": "dotnet.restore.project", "title": "%command.dotnet.restore.project%", "category": ".NET", - "enablement": "config.dotnet.server.useOmnisharp" + "enablement": "dotnet.server.activatedStandalone" }, { "command": "dotnet.restore.all", "title": "%command.dotnet.restore.all%", "category": ".NET", - "enablement": "config.dotnet.server.useOmnisharp" + "enablement": "dotnet.server.activatedStandalone" }, { "command": "csharp.downloadDebugger", diff --git a/src/lsptoolshost/restore.ts b/src/lsptoolshost/restore.ts new file mode 100644 index 000000000..79e96a23d --- /dev/null +++ b/src/lsptoolshost/restore.ts @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { RoslynLanguageServer } from './roslynLanguageServer'; +import { RestorableProjects, RestoreParams, RestorePartialResult, RestoreRequest } from './roslynProtocol'; +import path = require('path'); + +let _restoreInProgress = false; + +export function registerRestoreCommands( + context: vscode.ExtensionContext, + languageServer: RoslynLanguageServer, + restoreChannel: vscode.OutputChannel +) { + context.subscriptions.push( + vscode.commands.registerCommand('dotnet.restore.project', async (_request): Promise => { + return chooseProjectToRestore(languageServer, restoreChannel); + }) + ); + context.subscriptions.push( + vscode.commands.registerCommand('dotnet.restore.all', async (): Promise => { + return restore(languageServer, restoreChannel); + }) + ); +} +async function chooseProjectToRestore( + languageServer: RoslynLanguageServer, + restoreChannel: vscode.OutputChannel +): Promise { + const projects = await languageServer.sendRequest0( + RestorableProjects.type, + new vscode.CancellationTokenSource().token + ); + + const items = projects.map((p) => { + const projectName = path.basename(p); + const item: vscode.QuickPickItem = { + label: vscode.l10n.t(`Restore {0}`, projectName), + description: p, + }; + return item; + }); + + const pickedItem = await vscode.window.showQuickPick(items); + if (!pickedItem) { + return; + } + + await restore(languageServer, restoreChannel, pickedItem.description); +} + +async function restore( + languageServer: RoslynLanguageServer, + restoreChannel: vscode.OutputChannel, + projectFile?: string +): Promise { + if (_restoreInProgress) { + vscode.window.showErrorMessage(vscode.l10n.t('Restore already in progress')); + return; + } + _restoreInProgress = true; + restoreChannel.show(true); + + const request: RestoreParams = { projectFilePath: projectFile }; + await vscode.window + .withProgress( + { + location: vscode.ProgressLocation.Notification, + title: vscode.l10n.t('Restore'), + cancellable: true, + }, + async (progress, token) => { + const writeOutput = (output: RestorePartialResult) => { + if (output.message) { + restoreChannel.appendLine(output.message); + } + + progress.report({ message: output.stage }); + }; + + progress.report({ message: vscode.l10n.t('Sending request') }); + const responsePromise = languageServer.sendRequestWithProgress( + RestoreRequest.type, + request, + async (p) => writeOutput(p), + token + ); + + await responsePromise.then( + (result) => result.forEach((r) => writeOutput(r)), + (err) => restoreChannel.appendLine(err) + ); + } + ) + .then( + () => { + _restoreInProgress = false; + }, + () => { + _restoreInProgress = false; + } + ); + + return; +} diff --git a/src/lsptoolshost/roslynLanguageServer.ts b/src/lsptoolshost/roslynLanguageServer.ts index 946fc3609..a2a02c4cb 100644 --- a/src/lsptoolshost/roslynLanguageServer.ts +++ b/src/lsptoolshost/roslynLanguageServer.ts @@ -56,6 +56,7 @@ import { registerCodeActionFixAllCommands } from './fixAllCodeAction'; import { commonOptions, languageServerOptions, omnisharpOptions } from '../shared/options'; import { NamedPipeInformation } from './roslynProtocol'; import { IDisposable } from '../disposable'; +import { registerRestoreCommands } from './restore'; let _channel: vscode.OutputChannel; let _traceChannel: vscode.OutputChannel; @@ -835,6 +836,7 @@ export async function activateRoslynLanguageServer( optionObservable: Observable, outputChannel: vscode.OutputChannel, dotnetTestChannel: vscode.OutputChannel, + dotnetChannel: vscode.OutputChannel, reporter: TelemetryReporter, languageServerEvents: RoslynLanguageServerEvents ): Promise { @@ -872,6 +874,8 @@ export async function activateRoslynLanguageServer( // Register any needed debugger components that need to communicate with the language server. registerDebugger(context, languageServer, languageServerEvents, platformInfo, _channel); + registerRestoreCommands(context, languageServer, dotnetChannel); + registerOnAutoInsert(languageServer); context.subscriptions.push(registerLanguageServerOptionChanges(optionObservable)); diff --git a/src/lsptoolshost/roslynProtocol.ts b/src/lsptoolshost/roslynProtocol.ts index 9e6691468..9430dab66 100644 --- a/src/lsptoolshost/roslynProtocol.ts +++ b/src/lsptoolshost/roslynProtocol.ts @@ -150,6 +150,19 @@ export interface NamedPipeInformation { pipeName: string; } +export interface RestoreParams extends lsp.WorkDoneProgressParams, lsp.PartialResultParams { + /** + * An optional file path to restore. + * If none is specified, the solution (or all loaded projects) are restored. + */ + projectFilePath?: string; +} + +export interface RestorePartialResult { + stage: string; + message: string; +} + export namespace WorkspaceDebugConfigurationRequest { export const method = 'workspace/debugConfiguration'; export const messageDirection: lsp.MessageDirection = lsp.MessageDirection.clientToServer; @@ -229,3 +242,21 @@ export namespace CodeActionFixAllResolveRequest { export const messageDirection: lsp.MessageDirection = lsp.MessageDirection.clientToServer; export const type = new lsp.RequestType(method); } + +export namespace RestoreRequest { + export const method = 'workspace/restore'; + export const messageDirection: lsp.MessageDirection = lsp.MessageDirection.clientToServer; + export const type = new lsp.ProtocolRequestType< + RestoreParams, + RestorePartialResult[], + RestorePartialResult, + void, + void + >(method); +} + +export namespace RestorableProjects { + export const method = 'workspace/restorableProjects'; + export const messageDirection: lsp.MessageDirection = lsp.MessageDirection.clientToServer; + export const type = new lsp.RequestType0(method); +} diff --git a/src/main.ts b/src/main.ts index e5f046716..26bbd8169 100644 --- a/src/main.ts +++ b/src/main.ts @@ -82,6 +82,7 @@ export async function activate( const csharpChannel = vscode.window.createOutputChannel('C#'); const dotnetTestChannel = vscode.window.createOutputChannel('.NET Test Log'); + const dotnetChannel = vscode.window.createOutputChannel('.NET'); const csharpchannelObserver = new CsharpChannelObserver(csharpChannel); const csharpLogObserver = new CsharpLoggerObserver(csharpChannel); eventStream.subscribe(csharpchannelObserver.post); @@ -175,11 +176,11 @@ export async function activate( optionStream, csharpChannel, dotnetTestChannel, + dotnetChannel, reporter, roslynLanguageServerEvents ); } else { - const dotnetChannel = vscode.window.createOutputChannel('.NET'); const dotnetChannelObserver = new DotNetChannelObserver(dotnetChannel); const dotnetLoggerObserver = new DotnetLoggerObserver(dotnetChannel); eventStream.subscribe(dotnetChannelObserver.post); From c4d2c24c0b4a77a35c66c1ec5e3e44613c640698 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Fri, 27 Oct 2023 10:46:51 -0700 Subject: [PATCH 2/4] Adjust protocol names --- src/lsptoolshost/roslynProtocol.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lsptoolshost/roslynProtocol.ts b/src/lsptoolshost/roslynProtocol.ts index 9430dab66..2d58906c1 100644 --- a/src/lsptoolshost/roslynProtocol.ts +++ b/src/lsptoolshost/roslynProtocol.ts @@ -244,7 +244,7 @@ export namespace CodeActionFixAllResolveRequest { } export namespace RestoreRequest { - export const method = 'workspace/restore'; + export const method = 'workspace/_roslyn_restore'; export const messageDirection: lsp.MessageDirection = lsp.MessageDirection.clientToServer; export const type = new lsp.ProtocolRequestType< RestoreParams, @@ -256,7 +256,7 @@ export namespace RestoreRequest { } export namespace RestorableProjects { - export const method = 'workspace/restorableProjects'; + export const method = 'workspace/_roslyn_restorableProjects'; export const messageDirection: lsp.MessageDirection = lsp.MessageDirection.clientToServer; export const type = new lsp.RequestType0(method); } From 5c1ae36fcbac02216e1818d343ef5d5733c5af73 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Mon, 30 Oct 2023 16:25:57 -0700 Subject: [PATCH 3/4] Use roslyn version with restore support --- CHANGELOG.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37e075c84..fe2ba1f63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ - [O# Parity] Nuget restore [#5725](https://github.com/dotnet/vscode-csharp/issues/5725) - Debug from .csproj and .sln [#5876](https://github.com/dotnet/vscode-csharp/issues/5876) +## Next (Replace once current release version is generated) +* Update Roslyn to 4.9.0-1.23530.4 (PR: [#6603](https://github.com/dotnet/vscode-csharp/pull/6603)) + * Enable NuGet restore commands `dotnet.restore.all` and `dotnet.restore.project` (PR: [#70588](https://github.com/dotnet/roslyn/pull/70588)) + * Fix issue where server did not reload projects after NuGet restore (PR: [#70602](https://github.com/dotnet/roslyn/pull/70602)) + ## Latest * Update Roslyn to 4.9.0-1.23525.5 (PR: [#6595](https://github.com/dotnet/vscode-csharp/pull/6595)) * Fix some project loading issues caused by evaluation failures (PR: [#70496](https://github.com/dotnet/roslyn/pull/70496)) diff --git a/package.json b/package.json index d34188e12..fa557e48d 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ } }, "defaults": { - "roslyn": "4.9.0-1.23525.5", + "roslyn": "4.9.0-1.23530.4", "omniSharp": "1.39.10", "razor": "7.0.0-preview.23516.2", "razorOmnisharp": "7.0.0-preview.23363.1", From 77c4524f4f05fa4279a754e49aa845f7756f5662 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Thu, 2 Nov 2023 17:16:29 -0700 Subject: [PATCH 4/4] Feedback --- src/lsptoolshost/restore.ts | 4 ++-- src/main.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lsptoolshost/restore.ts b/src/lsptoolshost/restore.ts index 79e96a23d..6cf34697e 100644 --- a/src/lsptoolshost/restore.ts +++ b/src/lsptoolshost/restore.ts @@ -17,7 +17,7 @@ export function registerRestoreCommands( ) { context.subscriptions.push( vscode.commands.registerCommand('dotnet.restore.project', async (_request): Promise => { - return chooseProjectToRestore(languageServer, restoreChannel); + return chooseProjectAndRestore(languageServer, restoreChannel); }) ); context.subscriptions.push( @@ -26,7 +26,7 @@ export function registerRestoreCommands( }) ); } -async function chooseProjectToRestore( +async function chooseProjectAndRestore( languageServer: RoslynLanguageServer, restoreChannel: vscode.OutputChannel ): Promise { diff --git a/src/main.ts b/src/main.ts index 26bbd8169..e3b5e3ad1 100644 --- a/src/main.ts +++ b/src/main.ts @@ -82,7 +82,7 @@ export async function activate( const csharpChannel = vscode.window.createOutputChannel('C#'); const dotnetTestChannel = vscode.window.createOutputChannel('.NET Test Log'); - const dotnetChannel = vscode.window.createOutputChannel('.NET'); + const dotnetChannel = vscode.window.createOutputChannel('.NET NuGet Restore'); const csharpchannelObserver = new CsharpChannelObserver(csharpChannel); const csharpLogObserver = new CsharpLoggerObserver(csharpChannel); eventStream.subscribe(csharpchannelObserver.post);