/
dotNetIntellisenseProvider.ts
150 lines (132 loc) · 7.41 KB
/
dotNetIntellisenseProvider.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
'use strict';
import '../../../common/extensions';
import { inject, injectable } from 'inversify';
import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api';
import { CancellationToken, TextDocumentContentChangeEvent, Uri } from 'vscode';
import * as vscodeLanguageClient from 'vscode-languageclient';
import { ILanguageServer, ILanguageServerAnalysisOptions } from '../../../activation/types';
import { IWorkspaceService } from '../../../common/application/types';
import { IFileSystem } from '../../../common/platform/types';
import { IConfigurationService } from '../../../common/types';
import { createDeferred, Deferred } from '../../../common/utils/async';
import { Identifiers } from '../../constants';
import { IInteractiveWindowListener, IInteractiveWindowProvider, IJupyterExecution } from '../../types';
import { BaseIntellisenseProvider } from './baseIntellisenseProvider';
import { convertToMonacoCompletionList, convertToMonacoHover, convertToMonacoSignatureHelp } from './conversion';
import { IntellisenseDocument } from './intellisenseDocument';
// tslint:disable:no-any
@injectable()
export class DotNetIntellisenseProvider extends BaseIntellisenseProvider implements IInteractiveWindowListener {
private languageClientPromise: Deferred<vscodeLanguageClient.LanguageClient> | undefined;
private sentOpenDocument: boolean = false;
private active: boolean = false;
constructor(
@inject(ILanguageServer) private languageServer: ILanguageServer,
@inject(ILanguageServerAnalysisOptions) private readonly analysisOptions: ILanguageServerAnalysisOptions,
@inject(IWorkspaceService) workspaceService: IWorkspaceService,
@inject(IConfigurationService) private configService: IConfigurationService,
@inject(IFileSystem) fileSystem: IFileSystem,
@inject(IJupyterExecution) jupyterExecution: IJupyterExecution,
@inject(IInteractiveWindowProvider) interactiveWindowProvider: IInteractiveWindowProvider
) {
super(workspaceService, fileSystem, jupyterExecution, interactiveWindowProvider);
// Make sure we're active. We still listen to messages for adding and editing cells,
// but we don't actually return any data.
const isLsActive = () => {
const lsSetting = this.configService.getSettings().languageServer;
return lsSetting === 'microsoft';
};
this.active = isLsActive();
// Listen for updates to settings to change this flag. Don't bother disposing the config watcher. It lives
// till the extension dies anyway.
this.configService.getSettings().onDidChange(() => this.active = isLsActive());
}
protected get isActive(): boolean {
return this.active;
}
protected async provideCompletionItems(position: monacoEditor.Position, context: monacoEditor.languages.CompletionContext, cellId: string, token: CancellationToken): Promise<monacoEditor.languages.CompletionList> {
const languageClient = await this.getLanguageClient();
const document = await this.getDocument();
if (languageClient && document) {
const docPos = document.convertToDocumentPosition(cellId, position.lineNumber, position.column);
const result = await languageClient.sendRequest(
vscodeLanguageClient.CompletionRequest.type,
languageClient.code2ProtocolConverter.asCompletionParams(document, docPos, context),
token);
return convertToMonacoCompletionList(result, true);
}
return {
suggestions: [],
incomplete: false
};
}
protected async provideHover(position: monacoEditor.Position, cellId: string, token: CancellationToken): Promise<monacoEditor.languages.Hover> {
const languageClient = await this.getLanguageClient();
const document = await this.getDocument();
if (languageClient && document) {
const docPos = document.convertToDocumentPosition(cellId, position.lineNumber, position.column);
const result = await languageClient.sendRequest(
vscodeLanguageClient.HoverRequest.type,
languageClient.code2ProtocolConverter.asTextDocumentPositionParams(document, docPos),
token);
return convertToMonacoHover(result);
}
return {
contents: []
};
}
protected async provideSignatureHelp(position: monacoEditor.Position, _context: monacoEditor.languages.SignatureHelpContext, cellId: string, token: CancellationToken): Promise<monacoEditor.languages.SignatureHelp> {
const languageClient = await this.getLanguageClient();
const document = await this.getDocument();
if (languageClient && document) {
const docPos = document.convertToDocumentPosition(cellId, position.lineNumber, position.column);
const result = await languageClient.sendRequest(
vscodeLanguageClient.SignatureHelpRequest.type,
languageClient.code2ProtocolConverter.asTextDocumentPositionParams(document, docPos),
token);
return convertToMonacoSignatureHelp(result);
}
return {
signatures: [],
activeParameter: 0,
activeSignature: 0
};
}
protected async handleChanges(originalFile: string | undefined, document: IntellisenseDocument, changes: TextDocumentContentChangeEvent[]): Promise<void> {
// Then see if we can talk to our language client
if (this.active && document) {
// Cache our document state as it may change after we get our language client. Async call may allow a change to
// come in before we send the first doc open.
const docItem = document.textDocumentItem;
const docItemId = document.textDocumentId;
// Broadcast an update to the language server
const languageClient = await this.getLanguageClient(originalFile === Identifiers.EmptyFileName || originalFile === undefined ? undefined : Uri.file(originalFile));
if (!this.sentOpenDocument) {
this.sentOpenDocument = true;
return languageClient.sendNotification(vscodeLanguageClient.DidOpenTextDocumentNotification.type, { textDocument: docItem });
} else {
return languageClient.sendNotification(vscodeLanguageClient.DidChangeTextDocumentNotification.type, { textDocument: docItemId, contentChanges: changes });
}
}
}
private getLanguageClient(file?: Uri): Promise<vscodeLanguageClient.LanguageClient> {
if (!this.languageClientPromise) {
this.languageClientPromise = createDeferred<vscodeLanguageClient.LanguageClient>();
this.startup(file)
.then(() => {
this.languageClientPromise!.resolve(this.languageServer.languageClient);
})
.catch((e: any) => {
this.languageClientPromise!.reject(e);
});
}
return this.languageClientPromise.promise;
}
private async startup(resource?: Uri): Promise<void> {
// Start up the language server. We'll use this to talk to the language server
const options = await this.analysisOptions!.getAnalysisOptions();
await this.languageServer.start(resource, options);
}
}