Skip to content

Commit 29b7519

Browse files
author
Mikhail Arkhipov
authored
Cleanup and unify LS download code (#9794)
* Common base for activator * Conditional service registration * Language server factory cleanup * Folder service * Test cleanup * Some restore * Server starts * Test fixes * Resolve circular injection * Working download * Fix bundle directory * Mock fixes * Test fixes * Test fixes * Avoid folder nesting * Test update * Settings * Hardcode LS type in tests * Update LS channel name * Remove import
1 parent 34d8097 commit 29b7519

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1128
-1098
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,5 @@ debug*.log
4343
ptvsd*.log
4444
pydevd*.log
4545
nodeLanguageServer/**
46+
nodeLanguageServer.*/**
4647

src/client/activation/activationService.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ export class LanguageServerExtensionActivationService implements IExtensionActiv
3939
private readonly workspaceService: IWorkspaceService;
4040
private readonly output: OutputChannel;
4141
private readonly appShell: IApplicationShell;
42-
private readonly lsNotSupportedDiagnosticService: IDiagnosticsService;
4342
private readonly interpreterService: IInterpreterService;
4443
private resource!: Resource;
4544

@@ -52,7 +51,6 @@ export class LanguageServerExtensionActivationService implements IExtensionActiv
5251
this.interpreterService = this.serviceContainer.get<IInterpreterService>(IInterpreterService);
5352
this.output = this.serviceContainer.get<OutputChannel>(IOutputChannel, STANDARD_OUTPUT_CHANNEL);
5453
this.appShell = this.serviceContainer.get<IApplicationShell>(IApplicationShell);
55-
this.lsNotSupportedDiagnosticService = this.serviceContainer.get<IDiagnosticsService>(IDiagnosticsService, LSNotSupportedDiagnosticServiceId);
5654
const commandManager = this.serviceContainer.get<ICommandManager>(ICommandManager);
5755
const disposables = serviceContainer.get<IDisposableRegistry>(IDisposableRegistry);
5856
disposables.push(this);
@@ -200,8 +198,9 @@ export class LanguageServerExtensionActivationService implements IExtensionActiv
200198
serverType = LanguageServerType.Jedi;
201199
break;
202200
}
203-
const diagnostic = await this.lsNotSupportedDiagnosticService.diagnose(undefined);
204-
this.lsNotSupportedDiagnosticService.handle(diagnostic).ignoreErrors();
201+
const lsNotSupportedDiagnosticService = this.serviceContainer.get<IDiagnosticsService>(IDiagnosticsService, LSNotSupportedDiagnosticServiceId);
202+
const diagnostic = await lsNotSupportedDiagnosticService.diagnose(undefined);
203+
lsNotSupportedDiagnosticService.handle(diagnostic).ignoreErrors();
205204
if (diagnostic.length) {
206205
sendTelemetryEvent(EventName.PYTHON_LANGUAGE_SERVER_PLATFORM_SUPPORTED, undefined, { supported: false });
207206
serverType = LanguageServerType.Jedi;
Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
import * as path from 'path';
5+
import {
6+
CancellationToken,
7+
CodeLens,
8+
CompletionContext,
9+
CompletionItem,
10+
CompletionList,
11+
DocumentSymbol,
12+
Hover,
13+
Location,
14+
LocationLink,
15+
Position,
16+
ProviderResult,
17+
ReferenceContext,
18+
SignatureHelp,
19+
SignatureHelpContext,
20+
SymbolInformation,
21+
TextDocument,
22+
TextDocumentContentChangeEvent,
23+
WorkspaceEdit
24+
} from 'vscode';
25+
import * as vscodeLanguageClient from 'vscode-languageclient';
26+
27+
import { injectable } from 'inversify';
28+
import { IWorkspaceService } from '../../common/application/types';
29+
import { traceDecorators } from '../../common/logger';
30+
import { IFileSystem } from '../../common/platform/types';
31+
import { IConfigurationService, Resource } from '../../common/types';
32+
import { EXTENSION_ROOT_DIR } from '../../constants';
33+
import { PythonInterpreter } from '../../interpreter/contracts';
34+
import { ILanguageServerActivator, ILanguageServerDownloader, ILanguageServerFolderService, ILanguageServerManager } from '../types';
35+
36+
/**
37+
* Starts the language server managers per workspaces (currently one for first workspace).
38+
*
39+
* @export
40+
* @class LanguageServerActivatorBase
41+
* @implements {ILanguageServerActivator}
42+
*/
43+
@injectable()
44+
export abstract class LanguageServerActivatorBase implements ILanguageServerActivator {
45+
protected resource?: Resource;
46+
constructor(
47+
protected readonly manager: ILanguageServerManager,
48+
private readonly workspace: IWorkspaceService,
49+
protected readonly fs: IFileSystem,
50+
protected readonly lsDownloader: ILanguageServerDownloader,
51+
protected readonly languageServerFolderService: ILanguageServerFolderService,
52+
protected readonly configurationService: IConfigurationService
53+
) {}
54+
55+
@traceDecorators.error('Failed to activate language server')
56+
public async start(resource: Resource, interpreter?: PythonInterpreter): Promise<void> {
57+
if (!resource) {
58+
resource = this.workspace.hasWorkspaceFolders ? this.workspace.workspaceFolders![0].uri : undefined;
59+
}
60+
this.resource = resource;
61+
await this.ensureLanguageServerIsAvailable(resource);
62+
await this.manager.start(resource, interpreter);
63+
}
64+
65+
public dispose(): void {
66+
this.manager.dispose();
67+
}
68+
69+
public abstract async ensureLanguageServerIsAvailable(resource: Resource): Promise<void>;
70+
71+
public activate(): void {
72+
this.manager.connect();
73+
}
74+
75+
public deactivate(): void {
76+
this.manager.disconnect();
77+
}
78+
79+
public handleOpen(document: TextDocument): void {
80+
const languageClient = this.getLanguageClient();
81+
if (languageClient) {
82+
languageClient.sendNotification(vscodeLanguageClient.DidOpenTextDocumentNotification.type, languageClient.code2ProtocolConverter.asOpenTextDocumentParams(document));
83+
}
84+
}
85+
86+
public handleChanges(document: TextDocument, changes: TextDocumentContentChangeEvent[]): void {
87+
const languageClient = this.getLanguageClient();
88+
if (languageClient) {
89+
languageClient.sendNotification(
90+
vscodeLanguageClient.DidChangeTextDocumentNotification.type,
91+
languageClient.code2ProtocolConverter.asChangeTextDocumentParams({ document, contentChanges: changes })
92+
);
93+
}
94+
}
95+
96+
public provideRenameEdits(document: TextDocument, position: Position, newName: string, token: CancellationToken): ProviderResult<WorkspaceEdit> {
97+
return this.handleProvideRenameEdits(document, position, newName, token);
98+
}
99+
100+
public provideDefinition(document: TextDocument, position: Position, token: CancellationToken): ProviderResult<Location | Location[] | LocationLink[]> {
101+
return this.handleProvideDefinition(document, position, token);
102+
}
103+
104+
public provideHover(document: TextDocument, position: Position, token: CancellationToken): ProviderResult<Hover> {
105+
return this.handleProvideHover(document, position, token);
106+
}
107+
108+
public provideReferences(document: TextDocument, position: Position, context: ReferenceContext, token: CancellationToken): ProviderResult<Location[]> {
109+
return this.handleProvideReferences(document, position, context, token);
110+
}
111+
112+
public provideCompletionItems(
113+
document: TextDocument,
114+
position: Position,
115+
token: CancellationToken,
116+
context: CompletionContext
117+
): ProviderResult<CompletionItem[] | CompletionList> {
118+
return this.handleProvideCompletionItems(document, position, token, context);
119+
}
120+
121+
public provideCodeLenses(document: TextDocument, token: CancellationToken): ProviderResult<CodeLens[]> {
122+
return this.handleProvideCodeLenses(document, token);
123+
}
124+
125+
public provideDocumentSymbols(document: TextDocument, token: CancellationToken): ProviderResult<SymbolInformation[] | DocumentSymbol[]> {
126+
return this.handleProvideDocumentSymbols(document, token);
127+
}
128+
129+
public provideSignatureHelp(document: TextDocument, position: Position, token: CancellationToken, context: SignatureHelpContext): ProviderResult<SignatureHelp> {
130+
return this.handleProvideSignatureHelp(document, position, token, context);
131+
}
132+
133+
protected async ensureLanguageServerFileIsAvailable(resource: Resource, fileName: string): Promise<string | undefined> {
134+
const settings = this.configurationService.getSettings(resource);
135+
if (!settings.downloadLanguageServer) {
136+
return;
137+
}
138+
const languageServerFolder = await this.languageServerFolderService.getLanguageServerFolderName(resource);
139+
const languageServerFolderPath = path.join(EXTENSION_ROOT_DIR, languageServerFolder);
140+
const mscorlib = path.join(languageServerFolderPath, fileName);
141+
if (!(await this.fs.fileExists(mscorlib))) {
142+
await this.lsDownloader.downloadLanguageServer(languageServerFolderPath, resource);
143+
}
144+
return languageServerFolderPath;
145+
}
146+
147+
private getLanguageClient(): vscodeLanguageClient.LanguageClient | undefined {
148+
const proxy = this.manager.languageProxy;
149+
if (proxy) {
150+
return proxy.languageClient;
151+
}
152+
}
153+
154+
private async handleProvideRenameEdits(document: TextDocument, position: Position, newName: string, token: CancellationToken): Promise<WorkspaceEdit | undefined> {
155+
const languageClient = this.getLanguageClient();
156+
if (languageClient) {
157+
const args: vscodeLanguageClient.RenameParams = {
158+
textDocument: languageClient.code2ProtocolConverter.asTextDocumentIdentifier(document),
159+
position: languageClient.code2ProtocolConverter.asPosition(position),
160+
newName
161+
};
162+
const result = await languageClient.sendRequest(vscodeLanguageClient.RenameRequest.type, args, token);
163+
if (result) {
164+
return languageClient.protocol2CodeConverter.asWorkspaceEdit(result);
165+
}
166+
}
167+
}
168+
169+
private async handleProvideDefinition(document: TextDocument, position: Position, token: CancellationToken): Promise<Location | Location[] | LocationLink[] | undefined> {
170+
const languageClient = this.getLanguageClient();
171+
if (languageClient) {
172+
const args: vscodeLanguageClient.TextDocumentPositionParams = {
173+
textDocument: languageClient.code2ProtocolConverter.asTextDocumentIdentifier(document),
174+
position: languageClient.code2ProtocolConverter.asPosition(position)
175+
};
176+
const result = await languageClient.sendRequest(vscodeLanguageClient.DefinitionRequest.type, args, token);
177+
if (result) {
178+
return languageClient.protocol2CodeConverter.asDefinitionResult(result);
179+
}
180+
}
181+
}
182+
183+
private async handleProvideHover(document: TextDocument, position: Position, token: CancellationToken): Promise<Hover | undefined> {
184+
const languageClient = this.getLanguageClient();
185+
if (languageClient) {
186+
const args: vscodeLanguageClient.TextDocumentPositionParams = {
187+
textDocument: languageClient.code2ProtocolConverter.asTextDocumentIdentifier(document),
188+
position: languageClient.code2ProtocolConverter.asPosition(position)
189+
};
190+
const result = await languageClient.sendRequest(vscodeLanguageClient.HoverRequest.type, args, token);
191+
if (result) {
192+
return languageClient.protocol2CodeConverter.asHover(result);
193+
}
194+
}
195+
}
196+
197+
private async handleProvideReferences(document: TextDocument, position: Position, context: ReferenceContext, token: CancellationToken): Promise<Location[] | undefined> {
198+
const languageClient = this.getLanguageClient();
199+
if (languageClient) {
200+
const args: vscodeLanguageClient.ReferenceParams = {
201+
textDocument: languageClient.code2ProtocolConverter.asTextDocumentIdentifier(document),
202+
position: languageClient.code2ProtocolConverter.asPosition(position),
203+
context
204+
};
205+
const result = await languageClient.sendRequest(vscodeLanguageClient.ReferencesRequest.type, args, token);
206+
if (result) {
207+
// Remove undefined part.
208+
return result.map(l => {
209+
const r = languageClient!.protocol2CodeConverter.asLocation(l);
210+
return r!;
211+
});
212+
}
213+
}
214+
}
215+
216+
private async handleProvideCodeLenses(document: TextDocument, token: CancellationToken): Promise<CodeLens[] | undefined> {
217+
const languageClient = this.getLanguageClient();
218+
if (languageClient) {
219+
const args: vscodeLanguageClient.CodeLensParams = {
220+
textDocument: languageClient.code2ProtocolConverter.asTextDocumentIdentifier(document)
221+
};
222+
const result = await languageClient.sendRequest(vscodeLanguageClient.CodeLensRequest.type, args, token);
223+
if (result) {
224+
return languageClient.protocol2CodeConverter.asCodeLenses(result);
225+
}
226+
}
227+
}
228+
229+
private async handleProvideCompletionItems(
230+
document: TextDocument,
231+
position: Position,
232+
token: CancellationToken,
233+
context: CompletionContext
234+
): Promise<CompletionItem[] | CompletionList | undefined> {
235+
const languageClient = this.getLanguageClient();
236+
if (languageClient) {
237+
const args = languageClient.code2ProtocolConverter.asCompletionParams(document, position, context);
238+
const result = await languageClient.sendRequest(vscodeLanguageClient.CompletionRequest.type, args, token);
239+
if (result) {
240+
return languageClient.protocol2CodeConverter.asCompletionResult(result);
241+
}
242+
}
243+
}
244+
245+
private async handleProvideDocumentSymbols(document: TextDocument, token: CancellationToken): Promise<SymbolInformation[] | DocumentSymbol[] | undefined> {
246+
const languageClient = this.getLanguageClient();
247+
if (languageClient) {
248+
const args: vscodeLanguageClient.DocumentSymbolParams = {
249+
textDocument: languageClient.code2ProtocolConverter.asTextDocumentIdentifier(document)
250+
};
251+
const result = await languageClient.sendRequest(vscodeLanguageClient.DocumentSymbolRequest.type, args, token);
252+
if (result && result.length) {
253+
// tslint:disable-next-line: no-any
254+
if ((result[0] as any).range) {
255+
// Document symbols
256+
const docSymbols = result as vscodeLanguageClient.DocumentSymbol[];
257+
return languageClient.protocol2CodeConverter.asDocumentSymbols(docSymbols);
258+
} else {
259+
// Document symbols
260+
const symbols = result as vscodeLanguageClient.SymbolInformation[];
261+
return languageClient.protocol2CodeConverter.asSymbolInformations(symbols);
262+
}
263+
}
264+
}
265+
}
266+
267+
private async handleProvideSignatureHelp(
268+
document: TextDocument,
269+
position: Position,
270+
token: CancellationToken,
271+
_context: SignatureHelpContext
272+
): Promise<SignatureHelp | undefined> {
273+
const languageClient = this.getLanguageClient();
274+
if (languageClient) {
275+
const args: vscodeLanguageClient.TextDocumentPositionParams = {
276+
textDocument: languageClient.code2ProtocolConverter.asTextDocumentIdentifier(document),
277+
position: languageClient.code2ProtocolConverter.asPosition(position)
278+
};
279+
const result = await languageClient.sendRequest(vscodeLanguageClient.SignatureHelpRequest.type, args, token);
280+
if (result) {
281+
return languageClient.protocol2CodeConverter.asSignatureHelp(result);
282+
}
283+
}
284+
}
285+
}

0 commit comments

Comments
 (0)