diff --git a/news/1 Enhancements/3073.md b/news/1 Enhancements/3073.md new file mode 100644 index 000000000000..afd0809180a4 --- /dev/null +++ b/news/1 Enhancements/3073.md @@ -0,0 +1 @@ +Add "Restart Language Server" command diff --git a/package.json b/package.json index 04fff90aa5fb..13af6f5b9886 100644 --- a/package.json +++ b/package.json @@ -835,6 +835,11 @@ "command": "python.datascience.latestExtension", "title": "DataScience.latestExtension", "category": "Python" + }, + { + "command": "python.analysis.restartLanguageServer", + "title": "%python.command.python.analysis.restartLanguageServer.title%", + "category": "Python" } ], "menus": { diff --git a/package.nls.json b/package.nls.json index 5febf0116e27..22b06bdda4bb 100644 --- a/package.nls.json +++ b/package.nls.json @@ -114,6 +114,7 @@ "python.command.python.datascience.selectJupyterInterpreter.title": "Select Interpreter to start Jupyter server", "Datascience.currentlySelectedJupyterInterpreterForPlaceholder": "current: {0}", "python.command.python.analysis.clearCache.title": "Clear Module Analysis Cache", + "python.command.python.analysis.restartLanguageServer.title": "Restart Language Server", "python.snippet.launch.standard.label": "Python: Current File", "python.snippet.launch.module.label": "Python: Module", "python.snippet.launch.module.default": "enter-your-module-name", diff --git a/src/client/activation/activationService.ts b/src/client/activation/activationService.ts index 1633cf4b96e0..f72cca3bf4b6 100644 --- a/src/client/activation/activationService.ts +++ b/src/client/activation/activationService.ts @@ -32,8 +32,8 @@ import { IServiceContainer } from '../ioc/types'; import { PythonInterpreter } from '../pythonEnvironments/info'; import { sendTelemetryEvent } from '../telemetry'; import { EventName } from '../telemetry/constants'; +import { Commands } from './commands'; import { LanguageServerChangeHandler } from './common/languageServerChangeHandler'; -import { Commands } from './languageServer/constants'; import { RefCountedLanguageServer } from './refCountedLanguageServer'; import { IExtensionActivationService, diff --git a/src/client/activation/languageServer/constants.ts b/src/client/activation/commands.ts similarity index 74% rename from src/client/activation/languageServer/constants.ts rename to src/client/activation/commands.ts index 30094ca8507a..6a6628e6d4a3 100644 --- a/src/client/activation/languageServer/constants.ts +++ b/src/client/activation/commands.ts @@ -4,4 +4,5 @@ export namespace Commands { export const ClearAnalyisCache = 'python.analysis.clearCache'; + export const RestartLS = 'python.analysis.restartLanguageServer'; } diff --git a/src/client/activation/languageServer/manager.ts b/src/client/activation/languageServer/manager.ts index 6055a6ea35ad..0d0703541b9b 100644 --- a/src/client/activation/languageServer/manager.ts +++ b/src/client/activation/languageServer/manager.ts @@ -4,6 +4,7 @@ import '../../common/extensions'; import { inject, injectable, named } from 'inversify'; +import { ICommandManager } from '../../common/application/types'; import { traceDecorators } from '../../common/logger'; import { IConfigurationService, IDisposable, IExperimentsManager, Resource } from '../../common/types'; import { debounceSync } from '../../common/utils/decorators'; @@ -11,6 +12,7 @@ import { IServiceContainer } from '../../ioc/types'; import { PythonInterpreter } from '../../pythonEnvironments/info'; import { captureTelemetry } from '../../telemetry'; import { EventName } from '../../telemetry/constants'; +import { Commands } from '../commands'; import { LanguageClientMiddleware } from '../languageClientMiddleware'; import { ILanguageServerAnalysisOptions, @@ -39,8 +41,15 @@ export class DotNetLanguageServerManager implements ILanguageServerManager { @inject(ILanguageServerExtension) private readonly lsExtension: ILanguageServerExtension, @inject(ILanguageServerFolderService) private readonly folderService: ILanguageServerFolderService, @inject(IExperimentsManager) private readonly experimentsManager: IExperimentsManager, - @inject(IConfigurationService) private readonly configService: IConfigurationService - ) {} + @inject(IConfigurationService) private readonly configService: IConfigurationService, + @inject(ICommandManager) commandManager: ICommandManager + ) { + this.disposables.push( + commandManager.registerCommand(Commands.RestartLS, () => { + this.restartLanguageServer().ignoreErrors(); + }) + ); + } private static versionTelemetryProps(instance: DotNetLanguageServerManager) { return { diff --git a/src/client/activation/node/manager.ts b/src/client/activation/node/manager.ts index 14d64d9c1d44..8e21f079d29d 100644 --- a/src/client/activation/node/manager.ts +++ b/src/client/activation/node/manager.ts @@ -4,6 +4,7 @@ import '../../common/extensions'; import { inject, injectable, named } from 'inversify'; +import { ICommandManager } from '../../common/application/types'; import { traceDecorators } from '../../common/logger'; import { IConfigurationService, IDisposable, IExperimentsManager, Resource } from '../../common/types'; import { debounceSync } from '../../common/utils/decorators'; @@ -11,6 +12,7 @@ import { IServiceContainer } from '../../ioc/types'; import { PythonInterpreter } from '../../pythonEnvironments/info'; import { captureTelemetry } from '../../telemetry'; import { EventName } from '../../telemetry/constants'; +import { Commands } from '../commands'; import { LanguageClientMiddleware } from '../languageClientMiddleware'; import { ILanguageServerAnalysisOptions, @@ -38,8 +40,15 @@ export class NodeLanguageServerManager implements ILanguageServerManager { @inject(ILanguageServerFolderService) private readonly folderService: ILanguageServerFolderService, @inject(IExperimentsManager) private readonly experimentsManager: IExperimentsManager, - @inject(IConfigurationService) private readonly configService: IConfigurationService - ) {} + @inject(IConfigurationService) private readonly configService: IConfigurationService, + @inject(ICommandManager) commandManager: ICommandManager + ) { + this.disposables.push( + commandManager.registerCommand(Commands.RestartLS, () => { + this.restartLanguageServer().ignoreErrors(); + }) + ); + } private static versionTelemetryProps(instance: NodeLanguageServerManager) { return { diff --git a/src/client/common/application/commands.ts b/src/client/common/application/commands.ts index 967439d04047..3ef4263c8670 100644 --- a/src/client/common/application/commands.ts +++ b/src/client/common/application/commands.ts @@ -4,7 +4,7 @@ 'use strict'; import { CancellationToken, Position, TextDocument, Uri } from 'vscode'; -import { Commands as LSCommands } from '../../activation/languageServer/constants'; +import { Commands as LSCommands } from '../../activation/commands'; import { Commands as DSCommands } from '../../datascience/constants'; import { KernelSpecInterpreter } from '../../datascience/jupyter/kernels/kernelSelector'; import { INotebookModel, ISwitchKernelOptions } from '../../datascience/types'; @@ -77,6 +77,7 @@ interface ICommandNameWithoutArgumentTypeMapping { [DSCommands.CreateNewNotebook]: []; [Commands.OpenStartPage]: []; [LSCommands.ClearAnalyisCache]: []; + [LSCommands.RestartLS]: []; } /** diff --git a/src/test/activation/languageServer/manager.unit.test.ts b/src/test/activation/languageServer/manager.unit.test.ts index 5d8f3ac37ab8..09a24a0df6d0 100644 --- a/src/test/activation/languageServer/manager.unit.test.ts +++ b/src/test/activation/languageServer/manager.unit.test.ts @@ -2,9 +2,10 @@ // Licensed under the MIT License. import { expect, use } from 'chai'; import * as chaiAsPromised from 'chai-as-promised'; -import { instance, mock, verify, when } from 'ts-mockito'; -import { Uri } from 'vscode'; +import { anything, instance, mock, verify, when } from 'ts-mockito'; +import { Disposable, Uri } from 'vscode'; import { LanguageClientOptions } from 'vscode-languageclient/node'; +import { Commands } from '../../../client/activation/commands'; import { DotNetLanguageServerAnalysisOptions } from '../../../client/activation/languageServer/analysisOptions'; import { LanguageServerExtension } from '../../../client/activation/languageServer/languageServerExtension'; import { DotNetLanguageServerFolderService } from '../../../client/activation/languageServer/languageServerFolderService'; @@ -16,6 +17,8 @@ import { ILanguageServerFolderService, ILanguageServerProxy } from '../../../client/activation/types'; +import { CommandManager } from '../../../client/common/application/commandManager'; +import { ICommandManager } from '../../../client/common/application/types'; import { ConfigurationService } from '../../../client/common/configuration/service'; import { ExperimentsManager } from '../../../client/common/experiments/manager'; import { IConfigurationService, IExperimentsManager } from '../../../client/common/types'; @@ -37,6 +40,7 @@ suite('Language Server - Manager', () => { let folderService: ILanguageServerFolderService; let experimentsManager: IExperimentsManager; let configService: IConfigurationService; + let commandManager: ICommandManager; const languageClientOptions = ({ x: 1 } as any) as LanguageClientOptions; setup(() => { serviceContainer = mock(ServiceContainer); @@ -46,13 +50,19 @@ suite('Language Server - Manager', () => { folderService = mock(DotNetLanguageServerFolderService); experimentsManager = mock(ExperimentsManager); configService = mock(ConfigurationService); + + commandManager = mock(CommandManager); + const disposable = mock(Disposable); + when(commandManager.registerCommand(Commands.RestartLS, anything())).thenReturn(instance(disposable)); + manager = new DotNetLanguageServerManager( instance(serviceContainer), instance(analysisOptions), instance(lsExtension), instance(folderService), instance(experimentsManager), - instance(configService) + instance(configService), + instance(commandManager) ); });