diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a538dfc3..89bab30dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ - Debug from .csproj and .sln [#5876](https://github.com/dotnet/vscode-csharp/issues/5876) ## Latest +* 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)) * Update debugger to 2.9.0 (PR: [#6623](https://github.com/dotnet/vscode-csharp/pull/6623)) * Flush `Console.Write` buffer more often (Fixes: [#6598](https://github.com/dotnet/vscode-csharp/issues/6598)) * Fix logpoint freezing on start (Fixes: [#6585](https://github.com/dotnet/vscode-csharp/issues/6585)) diff --git a/l10n/bundle.l10n.json b/l10n/bundle.l10n.json index 456ab71a5..80ccf73e1 100644 --- a/l10n/bundle.l10n.json +++ b/l10n/bundle.l10n.json @@ -130,6 +130,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 e343ebd36..98b2a172c 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ } }, "defaults": { - "roslyn": "4.9.0-1.23526.14", + "roslyn": "4.9.0-1.23530.4", "omniSharp": "1.39.10", "razor": "7.0.0-preview.23528.1", "razorOmnisharp": "7.0.0-preview.23363.1", @@ -2038,13 +2038,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..6cf34697e --- /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 chooseProjectAndRestore(languageServer, restoreChannel); + }) + ); + context.subscriptions.push( + vscode.commands.registerCommand('dotnet.restore.all', async (): Promise => { + return restore(languageServer, restoreChannel); + }) + ); +} +async function chooseProjectAndRestore( + 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..2d58906c1 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/_roslyn_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/_roslyn_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..e3b5e3ad1 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 NuGet Restore'); 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);