typescript-language-server 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +128 -21
- package/lib/calls.d.ts +1 -1
- package/lib/calls.d.ts.map +1 -1
- package/lib/calls.js +140 -164
- package/lib/calls.js.map +1 -1
- package/lib/cli.js +1 -1
- package/lib/commands.d.ts +1 -0
- package/lib/commands.d.ts.map +1 -1
- package/lib/commands.js +3 -1
- package/lib/commands.js.map +1 -1
- package/lib/completion.d.ts +4 -7
- package/lib/completion.d.ts.map +1 -1
- package/lib/completion.js +64 -66
- package/lib/completion.js.map +1 -1
- package/lib/configuration-manager.d.ts +47 -0
- package/lib/configuration-manager.d.ts.map +1 -0
- package/lib/configuration-manager.js +105 -0
- package/lib/configuration-manager.js.map +1 -0
- package/lib/diagnostic-queue.d.ts +5 -4
- package/lib/diagnostic-queue.d.ts.map +1 -1
- package/lib/diagnostic-queue.js +7 -8
- package/lib/diagnostic-queue.js.map +1 -1
- package/lib/document-symbol.d.ts.map +1 -1
- package/lib/document-symbol.js +11 -10
- package/lib/document-symbol.js.map +1 -1
- package/lib/features/fix-all.d.ts.map +1 -1
- package/lib/features/fix-all.js +96 -112
- package/lib/features/fix-all.js.map +1 -1
- package/lib/features/inlay-hints.d.ts +11 -0
- package/lib/features/inlay-hints.d.ts.map +1 -0
- package/lib/features/inlay-hints.js +70 -0
- package/lib/features/inlay-hints.js.map +1 -0
- package/lib/features/source-definition.d.ts +11 -0
- package/lib/features/source-definition.d.ts.map +1 -0
- package/lib/features/source-definition.js +50 -0
- package/lib/features/source-definition.js.map +1 -0
- package/lib/file-lsp-server.spec.js +16 -25
- package/lib/file-lsp-server.spec.js.map +1 -1
- package/lib/hover.d.ts +2 -1
- package/lib/hover.d.ts.map +1 -1
- package/lib/hover.js +47 -7
- package/lib/hover.js.map +1 -1
- package/lib/logger.js +1 -1
- package/lib/logger.js.map +1 -1
- package/lib/lsp-client.d.ts +11 -15
- package/lib/lsp-client.d.ts.map +1 -1
- package/lib/lsp-client.js +29 -61
- package/lib/lsp-client.js.map +1 -1
- package/lib/lsp-connection.d.ts.map +1 -1
- package/lib/lsp-connection.js +3 -2
- package/lib/lsp-connection.js.map +1 -1
- package/lib/lsp-protocol.inlayHints.proposed.d.ts +0 -9
- package/lib/lsp-protocol.inlayHints.proposed.d.ts.map +1 -1
- package/lib/lsp-server.d.ts +15 -20
- package/lib/lsp-server.d.ts.map +1 -1
- package/lib/lsp-server.js +806 -901
- package/lib/lsp-server.js.map +1 -1
- package/lib/lsp-server.spec.js +772 -495
- package/lib/lsp-server.spec.js.map +1 -1
- package/lib/organize-imports.spec.js +8 -8
- package/lib/protocol-translation.d.ts +2 -7
- package/lib/protocol-translation.d.ts.map +1 -1
- package/lib/protocol-translation.js +19 -106
- package/lib/protocol-translation.js.map +1 -1
- package/lib/quickfix.js +1 -1
- package/lib/refactor.js +5 -1
- package/lib/refactor.js.map +1 -1
- package/lib/test-utils.d.ts +4 -3
- package/lib/test-utils.d.ts.map +1 -1
- package/lib/test-utils.js +103 -77
- package/lib/test-utils.js.map +1 -1
- package/lib/ts-protocol.d.ts +4 -19
- package/lib/ts-protocol.d.ts.map +1 -1
- package/lib/ts-protocol.js.map +1 -1
- package/lib/tsp-client.d.ts +36 -32
- package/lib/tsp-client.d.ts.map +1 -1
- package/lib/tsp-client.js +9 -7
- package/lib/tsp-client.js.map +1 -1
- package/lib/tsp-client.spec.js +26 -33
- package/lib/tsp-client.spec.js.map +1 -1
- package/lib/tsp-command-types.d.ts +1 -0
- package/lib/tsp-command-types.d.ts.map +1 -1
- package/lib/tsp-command-types.js +1 -1
- package/lib/tsp-command-types.js.map +1 -1
- package/lib/utils/typeConverters.d.ts +19 -1
- package/lib/utils/typeConverters.d.ts.map +1 -1
- package/lib/utils/typeConverters.js +103 -4
- package/lib/utils/typeConverters.js.map +1 -1
- package/lib/utils/versionProvider.js +17 -19
- package/lib/utils/versionProvider.js.map +1 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +2 -0
- package/lib/utils.js.map +1 -1
- package/package.json +10 -7
package/lib/lsp-server.js
CHANGED
|
@@ -4,15 +4,6 @@
|
|
|
4
4
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
|
|
5
5
|
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
6
|
*/
|
|
7
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
8
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
9
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
10
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
11
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
12
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
13
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
14
|
-
});
|
|
15
|
-
};
|
|
16
7
|
import * as path from 'node:path';
|
|
17
8
|
import fs from 'fs-extra';
|
|
18
9
|
import debounce from 'p-debounce';
|
|
@@ -24,10 +15,10 @@ import API from './utils/api.js';
|
|
|
24
15
|
import { PrefixingLogger } from './logger.js';
|
|
25
16
|
import { TspClient } from './tsp-client.js';
|
|
26
17
|
import { DiagnosticEventQueue } from './diagnostic-queue.js';
|
|
27
|
-
import { toDocumentHighlight,
|
|
18
|
+
import { toDocumentHighlight, asTagsDocumentation, uriToPath, toSymbolKind, toLocation, pathToUri, toTextEdit, asPlainText, normalizePath } from './protocol-translation.js';
|
|
28
19
|
import { LspDocuments } from './document.js';
|
|
29
20
|
import { asCompletionItem, asResolvedCompletionItem, getCompletionTriggerCharacter } from './completion.js';
|
|
30
|
-
import { asSignatureHelp } from './hover.js';
|
|
21
|
+
import { asSignatureHelp, toTsTriggerReason } from './hover.js';
|
|
31
22
|
import { Commands } from './commands.js';
|
|
32
23
|
import { provideQuickFix } from './quickfix.js';
|
|
33
24
|
import { provideRefactors } from './refactor.js';
|
|
@@ -36,38 +27,11 @@ import { collectDocumentSymbols, collectSymbolInformation } from './document-sym
|
|
|
36
27
|
import { computeCallers, computeCallees } from './calls.js';
|
|
37
28
|
import { TypeScriptVersionProvider } from './utils/versionProvider.js';
|
|
38
29
|
import { TypeScriptAutoFixProvider } from './features/fix-all.js';
|
|
30
|
+
import { TypeScriptInlayHintsProvider } from './features/inlay-hints.js';
|
|
31
|
+
import { SourceDefinitionCommand } from './features/source-definition.js';
|
|
32
|
+
import { Position, Range } from './utils/typeConverters.js';
|
|
39
33
|
import { CodeActionKind } from './utils/types.js';
|
|
40
|
-
|
|
41
|
-
allowIncompleteCompletions: true,
|
|
42
|
-
allowRenameOfImportPath: true,
|
|
43
|
-
allowTextChangesInNewFiles: true,
|
|
44
|
-
disableSuggestions: false,
|
|
45
|
-
displayPartsForJSDoc: true,
|
|
46
|
-
generateReturnInDocTemplate: true,
|
|
47
|
-
importModuleSpecifierEnding: 'auto',
|
|
48
|
-
importModuleSpecifierPreference: 'shortest',
|
|
49
|
-
includeAutomaticOptionalChainCompletions: true,
|
|
50
|
-
includeCompletionsForImportStatements: true,
|
|
51
|
-
includeCompletionsForModuleExports: true,
|
|
52
|
-
includeCompletionsWithClassMemberSnippets: true,
|
|
53
|
-
includeCompletionsWithInsertText: true,
|
|
54
|
-
includeCompletionsWithObjectLiteralMethodSnippets: true,
|
|
55
|
-
includeCompletionsWithSnippetText: true,
|
|
56
|
-
includeInlayEnumMemberValueHints: false,
|
|
57
|
-
includeInlayFunctionLikeReturnTypeHints: false,
|
|
58
|
-
includeInlayFunctionParameterTypeHints: false,
|
|
59
|
-
includeInlayParameterNameHints: 'none',
|
|
60
|
-
includeInlayParameterNameHintsWhenArgumentMatchesName: false,
|
|
61
|
-
includeInlayPropertyDeclarationTypeHints: false,
|
|
62
|
-
includeInlayVariableTypeHints: false,
|
|
63
|
-
includePackageJsonAutoImports: 'auto',
|
|
64
|
-
jsxAttributeCompletionStyle: 'auto',
|
|
65
|
-
lazyConfiguredProjectsFromExternalProject: false,
|
|
66
|
-
providePrefixAndSuffixTextForRename: true,
|
|
67
|
-
provideRefactorNotApplicableReason: false,
|
|
68
|
-
quotePreference: 'auto',
|
|
69
|
-
useLabelDetailsInCompletionEntries: true
|
|
70
|
-
};
|
|
34
|
+
import { ConfigurationManager } from './configuration-manager.js';
|
|
71
35
|
class ServerInitializingIndicator {
|
|
72
36
|
constructor(lspClient) {
|
|
73
37
|
this.lspClient = lspClient;
|
|
@@ -76,24 +40,24 @@ class ServerInitializingIndicator {
|
|
|
76
40
|
if (this._loadingProjectName) {
|
|
77
41
|
this._loadingProjectName = undefined;
|
|
78
42
|
if (this._progressReporter) {
|
|
79
|
-
this._progressReporter.
|
|
43
|
+
this._progressReporter.done();
|
|
80
44
|
this._progressReporter = undefined;
|
|
81
45
|
}
|
|
82
46
|
}
|
|
83
47
|
}
|
|
84
|
-
startedLoadingProject(projectName) {
|
|
48
|
+
async startedLoadingProject(projectName) {
|
|
85
49
|
// TS projects are loaded sequentially. Cancel existing task because it should always be resolved before
|
|
86
50
|
// the incoming project loading task is.
|
|
87
51
|
this.reset();
|
|
88
52
|
this._loadingProjectName = projectName;
|
|
89
|
-
this._progressReporter = this.lspClient.createProgressReporter();
|
|
53
|
+
this._progressReporter = await this.lspClient.createProgressReporter();
|
|
90
54
|
this._progressReporter.begin('Initializing JS/TS language features…');
|
|
91
55
|
}
|
|
92
56
|
finishedLoadingProject(projectName) {
|
|
93
57
|
if (this._loadingProjectName === projectName) {
|
|
94
58
|
this._loadingProjectName = undefined;
|
|
95
59
|
if (this._progressReporter) {
|
|
96
|
-
this._progressReporter.
|
|
60
|
+
this._progressReporter.done();
|
|
97
61
|
this._progressReporter = undefined;
|
|
98
62
|
}
|
|
99
63
|
}
|
|
@@ -102,14 +66,18 @@ class ServerInitializingIndicator {
|
|
|
102
66
|
export class LspServer {
|
|
103
67
|
constructor(options) {
|
|
104
68
|
this.options = options;
|
|
69
|
+
this._tspClient = null;
|
|
70
|
+
this._loadingIndicator = null;
|
|
71
|
+
this.initializeParams = null;
|
|
72
|
+
this.typeScriptAutoFixProvider = null;
|
|
105
73
|
this.features = {};
|
|
106
74
|
this.documents = new LspDocuments();
|
|
107
75
|
// True if diagnostic request is currently debouncing or the request is in progress. False only if there are
|
|
108
76
|
// no pending requests.
|
|
109
77
|
this.pendingDebouncedRequest = false;
|
|
110
78
|
this.doRequestDiagnosticsDebounced = debounce(() => this.doRequestDiagnostics(), 200);
|
|
79
|
+
this.configurationManager = new ConfigurationManager(this.documents);
|
|
111
80
|
this.logger = new PrefixingLogger(options.logger, '[lspserver]');
|
|
112
|
-
this.workspaceConfiguration = {};
|
|
113
81
|
}
|
|
114
82
|
closeAll() {
|
|
115
83
|
for (const file of [...this.documents.files]) {
|
|
@@ -162,169 +130,180 @@ export class LspServer {
|
|
|
162
130
|
}
|
|
163
131
|
return null;
|
|
164
132
|
}
|
|
165
|
-
initialize(params) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
&&
|
|
202
|
-
|
|
133
|
+
async initialize(params) {
|
|
134
|
+
this.logger.log('initialize', params);
|
|
135
|
+
if (this._tspClient) {
|
|
136
|
+
throw new Error('The "initialize" request has already called before.');
|
|
137
|
+
}
|
|
138
|
+
this.initializeParams = params;
|
|
139
|
+
const clientCapabilities = this.initializeParams.capabilities;
|
|
140
|
+
this._loadingIndicator = new ServerInitializingIndicator(this.options.lspClient);
|
|
141
|
+
this.workspaceRoot = this.initializeParams.rootUri ? uriToPath(this.initializeParams.rootUri) : this.initializeParams.rootPath || undefined;
|
|
142
|
+
const userInitializationOptions = this.initializeParams.initializationOptions || {};
|
|
143
|
+
const { disableAutomaticTypingAcquisition, hostInfo, maxTsServerMemory, npmLocation, locale } = userInitializationOptions;
|
|
144
|
+
const { logVerbosity, plugins } = {
|
|
145
|
+
logVerbosity: userInitializationOptions.logVerbosity || this.options.tsserverLogVerbosity,
|
|
146
|
+
plugins: userInitializationOptions.plugins || [],
|
|
147
|
+
};
|
|
148
|
+
const logFile = this.getLogFile(logVerbosity);
|
|
149
|
+
const globalPlugins = [];
|
|
150
|
+
const pluginProbeLocations = [];
|
|
151
|
+
for (const plugin of plugins) {
|
|
152
|
+
globalPlugins.push(plugin.name);
|
|
153
|
+
pluginProbeLocations.push(plugin.location);
|
|
154
|
+
}
|
|
155
|
+
const typescriptVersion = this.findTypescriptVersion();
|
|
156
|
+
if (typescriptVersion) {
|
|
157
|
+
this.logger.info(`Using Typescript version (${typescriptVersion.source}) ${typescriptVersion.versionString} from path "${typescriptVersion.tsServerPath}"`);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
throw Error('Could not find a valid TypeScript installation. Please ensure that the "typescript" dependency is installed in the workspace or that a valid --tsserver-path is specified. Exiting.');
|
|
161
|
+
}
|
|
162
|
+
this.configurationManager.mergeTsPreferences(userInitializationOptions.preferences || {});
|
|
163
|
+
// Setup supported features.
|
|
164
|
+
const { textDocument } = clientCapabilities;
|
|
165
|
+
this.features.definitionLinkSupport = textDocument?.definition?.linkSupport && typescriptVersion.version?.gte(API.v270);
|
|
166
|
+
const completionCapabilities = textDocument?.completion;
|
|
167
|
+
if (completionCapabilities?.completionItem) {
|
|
168
|
+
if (this.configurationManager.tsPreferences.useLabelDetailsInCompletionEntries
|
|
169
|
+
&& completionCapabilities.completionItem.labelDetailsSupport
|
|
170
|
+
&& typescriptVersion.version?.gte(API.v470)) {
|
|
171
|
+
this.features.completionLabelDetails = true;
|
|
172
|
+
}
|
|
173
|
+
if (completionCapabilities.completionItem.snippetSupport) {
|
|
174
|
+
this.features.completionSnippets = true;
|
|
175
|
+
}
|
|
176
|
+
if (textDocument?.publishDiagnostics?.tagSupport) {
|
|
177
|
+
this.features.diagnosticsTagSupport = true;
|
|
203
178
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
179
|
+
}
|
|
180
|
+
this.configurationManager.mergeTsPreferences({
|
|
181
|
+
useLabelDetailsInCompletionEntries: this.features.completionLabelDetails,
|
|
182
|
+
});
|
|
183
|
+
this.diagnosticQueue = new DiagnosticEventQueue(diagnostics => this.options.lspClient.publishDiagnostics(diagnostics), this.documents, this.features, this.logger);
|
|
184
|
+
this._tspClient = new TspClient({
|
|
185
|
+
apiVersion: typescriptVersion.version || API.defaultVersion,
|
|
186
|
+
tsserverPath: typescriptVersion.tsServerPath,
|
|
187
|
+
logFile,
|
|
188
|
+
logVerbosity,
|
|
189
|
+
disableAutomaticTypingAcquisition,
|
|
190
|
+
maxTsServerMemory,
|
|
191
|
+
npmLocation,
|
|
192
|
+
locale,
|
|
193
|
+
globalPlugins,
|
|
194
|
+
pluginProbeLocations,
|
|
195
|
+
logger: this.options.logger,
|
|
196
|
+
onEvent: this.onTsEvent.bind(this),
|
|
197
|
+
onExit: (exitCode, signal) => {
|
|
198
|
+
if (exitCode) {
|
|
199
|
+
this.logger.error(`tsserver process has exited (exit code: ${exitCode}, signal: ${signal}). Stopping the server.`);
|
|
222
200
|
}
|
|
223
|
-
});
|
|
224
|
-
const started = this.tspClient.start();
|
|
225
|
-
if (!started) {
|
|
226
|
-
throw new Error('tsserver process has failed to start.');
|
|
227
|
-
}
|
|
228
|
-
process.on('exit', () => {
|
|
229
201
|
this.shutdown();
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
const started = this.tspClient.start();
|
|
205
|
+
if (!started) {
|
|
206
|
+
throw new Error('tsserver process has failed to start.');
|
|
207
|
+
}
|
|
208
|
+
process.on('exit', () => {
|
|
209
|
+
this.shutdown();
|
|
210
|
+
});
|
|
211
|
+
process.on('SIGINT', () => {
|
|
212
|
+
process.exit();
|
|
213
|
+
});
|
|
214
|
+
this.typeScriptAutoFixProvider = new TypeScriptAutoFixProvider(this.tspClient);
|
|
215
|
+
await Promise.all([
|
|
216
|
+
this.configurationManager.setAndConfigureTspClient(this._tspClient, hostInfo),
|
|
217
|
+
this.tspClient.request("compilerOptionsForInferredProjects" /* CommandTypes.CompilerOptionsForInferredProjects */, {
|
|
218
|
+
options: {
|
|
219
|
+
module: "CommonJS" /* tsp.ModuleKind.CommonJS */,
|
|
220
|
+
target: "ES2016" /* tsp.ScriptTarget.ES2016 */,
|
|
221
|
+
jsx: "Preserve" /* tsp.JsxEmit.Preserve */,
|
|
222
|
+
allowJs: true,
|
|
223
|
+
allowSyntheticDefaultImports: true,
|
|
224
|
+
allowNonTsExtensions: true,
|
|
225
|
+
},
|
|
226
|
+
}),
|
|
227
|
+
]);
|
|
228
|
+
const logFileUri = logFile && pathToUri(logFile, undefined);
|
|
229
|
+
const initializeResult = {
|
|
230
|
+
capabilities: {
|
|
231
|
+
textDocumentSync: lsp.TextDocumentSyncKind.Incremental,
|
|
232
|
+
completionProvider: {
|
|
233
|
+
triggerCharacters: ['.', '"', '\'', '/', '@', '<'],
|
|
234
|
+
resolveProvider: true,
|
|
235
|
+
},
|
|
236
|
+
codeActionProvider: clientCapabilities.textDocument?.codeAction?.codeActionLiteralSupport
|
|
237
|
+
? { codeActionKinds: [
|
|
238
|
+
...TypeScriptAutoFixProvider.kinds.map(kind => kind.value),
|
|
239
|
+
CodeActionKind.SourceOrganizeImportsTs.value,
|
|
240
|
+
CodeActionKind.QuickFix.value,
|
|
241
|
+
CodeActionKind.Refactor.value,
|
|
242
|
+
] } : true,
|
|
243
|
+
definitionProvider: true,
|
|
244
|
+
documentFormattingProvider: true,
|
|
245
|
+
documentRangeFormattingProvider: true,
|
|
246
|
+
documentHighlightProvider: true,
|
|
247
|
+
documentSymbolProvider: true,
|
|
248
|
+
executeCommandProvider: {
|
|
249
|
+
commands: [
|
|
250
|
+
Commands.APPLY_WORKSPACE_EDIT,
|
|
251
|
+
Commands.APPLY_CODE_ACTION,
|
|
252
|
+
Commands.APPLY_REFACTORING,
|
|
253
|
+
Commands.ORGANIZE_IMPORTS,
|
|
254
|
+
Commands.APPLY_RENAME_FILE,
|
|
255
|
+
Commands.SOURCE_DEFINITION,
|
|
256
|
+
],
|
|
257
|
+
},
|
|
258
|
+
hoverProvider: true,
|
|
259
|
+
inlayHintProvider: true,
|
|
260
|
+
renameProvider: true,
|
|
261
|
+
referencesProvider: true,
|
|
262
|
+
signatureHelpProvider: {
|
|
263
|
+
triggerCharacters: ['(', ',', '<'],
|
|
264
|
+
retriggerCharacters: [')'],
|
|
265
|
+
},
|
|
266
|
+
workspaceSymbolProvider: true,
|
|
267
|
+
implementationProvider: true,
|
|
268
|
+
typeDefinitionProvider: true,
|
|
269
|
+
foldingRangeProvider: true,
|
|
270
|
+
semanticTokensProvider: {
|
|
271
|
+
documentSelector: null,
|
|
272
|
+
legend: {
|
|
273
|
+
// list taken from: https://github.com/microsoft/TypeScript/blob/main/src/services/classifier2020.ts#L10
|
|
274
|
+
tokenTypes: [
|
|
275
|
+
'class',
|
|
276
|
+
'enum',
|
|
277
|
+
'interface',
|
|
278
|
+
'namespace',
|
|
279
|
+
'typeParameter',
|
|
280
|
+
'type',
|
|
281
|
+
'parameter',
|
|
282
|
+
'variable',
|
|
283
|
+
'enumMember',
|
|
284
|
+
'property',
|
|
285
|
+
'function',
|
|
286
|
+
'member',
|
|
287
|
+
],
|
|
288
|
+
// token from: https://github.com/microsoft/TypeScript/blob/main/src/services/classifier2020.ts#L14
|
|
289
|
+
tokenModifiers: [
|
|
290
|
+
'declaration',
|
|
291
|
+
'static',
|
|
292
|
+
'async',
|
|
293
|
+
'readonly',
|
|
294
|
+
'defaultLibrary',
|
|
295
|
+
'local',
|
|
296
|
+
],
|
|
285
297
|
},
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
typeDefinitionProvider: true,
|
|
289
|
-
foldingRangeProvider: true,
|
|
290
|
-
semanticTokensProvider: {
|
|
291
|
-
documentSelector: null,
|
|
292
|
-
legend: {
|
|
293
|
-
// list taken from: https://github.com/microsoft/TypeScript/blob/main/src/services/classifier2020.ts#L10
|
|
294
|
-
tokenTypes: [
|
|
295
|
-
'class',
|
|
296
|
-
'enum',
|
|
297
|
-
'interface',
|
|
298
|
-
'namespace',
|
|
299
|
-
'typeParameter',
|
|
300
|
-
'type',
|
|
301
|
-
'parameter',
|
|
302
|
-
'variable',
|
|
303
|
-
'enumMember',
|
|
304
|
-
'property',
|
|
305
|
-
'function',
|
|
306
|
-
'member'
|
|
307
|
-
],
|
|
308
|
-
// token from: https://github.com/microsoft/TypeScript/blob/main/src/services/classifier2020.ts#L14
|
|
309
|
-
tokenModifiers: [
|
|
310
|
-
'declaration',
|
|
311
|
-
'static',
|
|
312
|
-
'async',
|
|
313
|
-
'readonly',
|
|
314
|
-
'defaultLibrary',
|
|
315
|
-
'local'
|
|
316
|
-
]
|
|
317
|
-
},
|
|
318
|
-
full: true,
|
|
319
|
-
range: true
|
|
320
|
-
}
|
|
298
|
+
full: true,
|
|
299
|
+
range: true,
|
|
321
300
|
},
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
301
|
+
},
|
|
302
|
+
logFileUri,
|
|
303
|
+
};
|
|
304
|
+
initializeResult.capabilities.callsProvider = true;
|
|
305
|
+
this.logger.log('onInitialize result', initializeResult);
|
|
306
|
+
return initializeResult;
|
|
328
307
|
}
|
|
329
308
|
getLogFile(logVerbosity) {
|
|
330
309
|
if (logVerbosity === undefined || logVerbosity === 'off') {
|
|
@@ -350,19 +329,9 @@ export class LspServer {
|
|
|
350
329
|
return undefined;
|
|
351
330
|
}
|
|
352
331
|
didChangeConfiguration(params) {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
(_b = this.diagnosticQueue) === null || _b === void 0 ? void 0 : _b.updateIgnoredDiagnosticCodes(ignoredDiagnosticCodes);
|
|
357
|
-
}
|
|
358
|
-
getWorkspacePreferencesForDocument(file) {
|
|
359
|
-
var _a;
|
|
360
|
-
const doc = this.documents.get(file);
|
|
361
|
-
if (!doc) {
|
|
362
|
-
return {};
|
|
363
|
-
}
|
|
364
|
-
const preferencesKey = doc.languageId.startsWith('typescript') ? 'typescript' : 'javascript';
|
|
365
|
-
return (_a = this.workspaceConfiguration[preferencesKey]) !== null && _a !== void 0 ? _a : {};
|
|
332
|
+
this.configurationManager.setWorkspaceConfiguration(params.settings || {});
|
|
333
|
+
const ignoredDiagnosticCodes = this.configurationManager.workspaceConfiguration.diagnostics?.ignoredCodes || [];
|
|
334
|
+
this.diagnosticQueue?.updateIgnoredDiagnosticCodes(ignoredDiagnosticCodes);
|
|
366
335
|
}
|
|
367
336
|
interuptDiagnostics(f) {
|
|
368
337
|
if (!this.diagnosticsTokenSource) {
|
|
@@ -373,28 +342,24 @@ export class LspServer {
|
|
|
373
342
|
this.requestDiagnostics();
|
|
374
343
|
return result;
|
|
375
344
|
}
|
|
376
|
-
requestDiagnostics() {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
yield this.doRequestDiagnosticsDebounced();
|
|
380
|
-
});
|
|
345
|
+
async requestDiagnostics() {
|
|
346
|
+
this.pendingDebouncedRequest = true;
|
|
347
|
+
await this.doRequestDiagnosticsDebounced();
|
|
381
348
|
}
|
|
382
|
-
doRequestDiagnostics() {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
this.pendingDebouncedRequest = false;
|
|
395
|
-
}
|
|
349
|
+
async doRequestDiagnostics() {
|
|
350
|
+
this.cancelDiagnostics();
|
|
351
|
+
const geterrTokenSource = new lsp.CancellationTokenSource();
|
|
352
|
+
this.diagnosticsTokenSource = geterrTokenSource;
|
|
353
|
+
const { files } = this.documents;
|
|
354
|
+
try {
|
|
355
|
+
return await this.tspClient.request("geterr" /* CommandTypes.Geterr */, { delay: 0, files }, this.diagnosticsTokenSource.token);
|
|
356
|
+
}
|
|
357
|
+
finally {
|
|
358
|
+
if (this.diagnosticsTokenSource === geterrTokenSource) {
|
|
359
|
+
this.diagnosticsTokenSource = undefined;
|
|
360
|
+
this.pendingDebouncedRequest = false;
|
|
396
361
|
}
|
|
397
|
-
}
|
|
362
|
+
}
|
|
398
363
|
}
|
|
399
364
|
cancelDiagnostics() {
|
|
400
365
|
if (this.diagnosticsTokenSource) {
|
|
@@ -412,7 +377,7 @@ export class LspServer {
|
|
|
412
377
|
file,
|
|
413
378
|
fileContent: params.textDocument.text,
|
|
414
379
|
scriptKindName: this.getScriptKindName(params.textDocument.languageId),
|
|
415
|
-
projectRootPath: this.workspaceRoot
|
|
380
|
+
projectRootPath: this.workspaceRoot,
|
|
416
381
|
});
|
|
417
382
|
this.requestDiagnostics();
|
|
418
383
|
}
|
|
@@ -422,9 +387,9 @@ export class LspServer {
|
|
|
422
387
|
textDocument: params.textDocument,
|
|
423
388
|
contentChanges: [
|
|
424
389
|
{
|
|
425
|
-
text: params.textDocument.text
|
|
426
|
-
}
|
|
427
|
-
]
|
|
390
|
+
text: params.textDocument.text,
|
|
391
|
+
},
|
|
392
|
+
],
|
|
428
393
|
});
|
|
429
394
|
}
|
|
430
395
|
}
|
|
@@ -455,7 +420,7 @@ export class LspServer {
|
|
|
455
420
|
// so we don't leave stale ones.
|
|
456
421
|
this.options.lspClient.publishDiagnostics({
|
|
457
422
|
uri: document.uri,
|
|
458
|
-
diagnostics: []
|
|
423
|
+
diagnostics: [],
|
|
459
424
|
});
|
|
460
425
|
}
|
|
461
426
|
didChangeTextDocument(params) {
|
|
@@ -497,7 +462,7 @@ export class LspServer {
|
|
|
497
462
|
offset,
|
|
498
463
|
endLine,
|
|
499
464
|
endOffset,
|
|
500
|
-
insertString: change.text
|
|
465
|
+
insertString: change.text,
|
|
501
466
|
});
|
|
502
467
|
document.applyEdit(textDocument.version, change);
|
|
503
468
|
}
|
|
@@ -506,76 +471,94 @@ export class LspServer {
|
|
|
506
471
|
didSaveTextDocument(_params) {
|
|
507
472
|
// do nothing
|
|
508
473
|
}
|
|
509
|
-
definition(params) {
|
|
510
|
-
return
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
type: 'definition',
|
|
514
|
-
params
|
|
515
|
-
});
|
|
474
|
+
async definition(params) {
|
|
475
|
+
return this.getDefinition({
|
|
476
|
+
type: this.features.definitionLinkSupport ? "definitionAndBoundSpan" /* CommandTypes.DefinitionAndBoundSpan */ : "definition" /* CommandTypes.Definition */,
|
|
477
|
+
params,
|
|
516
478
|
});
|
|
517
479
|
}
|
|
518
|
-
implementation(params) {
|
|
519
|
-
return
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
params
|
|
523
|
-
});
|
|
480
|
+
async implementation(params) {
|
|
481
|
+
return this.getSymbolLocations({
|
|
482
|
+
type: "implementation" /* CommandTypes.Implementation */,
|
|
483
|
+
params,
|
|
524
484
|
});
|
|
525
485
|
}
|
|
526
|
-
typeDefinition(params) {
|
|
527
|
-
return
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
params
|
|
531
|
-
});
|
|
486
|
+
async typeDefinition(params) {
|
|
487
|
+
return this.getSymbolLocations({
|
|
488
|
+
type: "typeDefinition" /* CommandTypes.TypeDefinition */,
|
|
489
|
+
params,
|
|
532
490
|
});
|
|
533
491
|
}
|
|
534
|
-
getDefinition({ type, params }) {
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
492
|
+
async getDefinition({ type, params }) {
|
|
493
|
+
const file = uriToPath(params.textDocument.uri);
|
|
494
|
+
this.logger.log(type, params, file);
|
|
495
|
+
if (!file) {
|
|
496
|
+
return undefined;
|
|
497
|
+
}
|
|
498
|
+
if (type === "definitionAndBoundSpan" /* CommandTypes.DefinitionAndBoundSpan */) {
|
|
499
|
+
const args = Position.toFileLocationRequestArgs(file, params.position);
|
|
500
|
+
const response = await this.tspClient.request(type, args);
|
|
501
|
+
if (response.type !== 'response' || !response.body) {
|
|
502
|
+
return undefined;
|
|
540
503
|
}
|
|
541
|
-
const
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
504
|
+
const span = Range.fromTextSpan(response.body.textSpan);
|
|
505
|
+
return response.body.definitions
|
|
506
|
+
.map((location) => {
|
|
507
|
+
const target = toLocation(location, this.documents);
|
|
508
|
+
const targetRange = location.contextStart && location.contextEnd
|
|
509
|
+
? Range.fromLocations(location.contextStart, location.contextEnd)
|
|
510
|
+
: target.range;
|
|
511
|
+
return {
|
|
512
|
+
originSelectionRange: span,
|
|
513
|
+
targetRange,
|
|
514
|
+
targetUri: target.uri,
|
|
515
|
+
targetSelectionRange: target.range,
|
|
516
|
+
};
|
|
545
517
|
});
|
|
546
|
-
|
|
547
|
-
});
|
|
518
|
+
}
|
|
519
|
+
return this.getSymbolLocations({ type: "definition" /* CommandTypes.Definition */, params });
|
|
548
520
|
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
521
|
+
async getSymbolLocations({ type, params }) {
|
|
522
|
+
const file = uriToPath(params.textDocument.uri);
|
|
523
|
+
this.logger.log(type, params, file);
|
|
524
|
+
if (!file) {
|
|
525
|
+
return [];
|
|
526
|
+
}
|
|
527
|
+
const args = Position.toFileLocationRequestArgs(file, params.position);
|
|
528
|
+
const response = await this.tspClient.request(type, args);
|
|
529
|
+
if (response.type !== 'response' || !response.body) {
|
|
530
|
+
return undefined;
|
|
531
|
+
}
|
|
532
|
+
return response.body.map(fileSpan => toLocation(fileSpan, this.documents));
|
|
533
|
+
}
|
|
534
|
+
async documentSymbol(params) {
|
|
535
|
+
const file = uriToPath(params.textDocument.uri);
|
|
536
|
+
this.logger.log('symbol', params, file);
|
|
537
|
+
if (!file) {
|
|
538
|
+
return [];
|
|
539
|
+
}
|
|
540
|
+
const response = await this.tspClient.request("navtree" /* CommandTypes.NavTree */, {
|
|
541
|
+
file,
|
|
542
|
+
});
|
|
543
|
+
const tree = response.body;
|
|
544
|
+
if (!tree || !tree.childItems) {
|
|
545
|
+
return [];
|
|
546
|
+
}
|
|
547
|
+
if (this.supportHierarchicalDocumentSymbol) {
|
|
570
548
|
const symbols = [];
|
|
571
549
|
for (const item of tree.childItems) {
|
|
572
|
-
|
|
550
|
+
collectDocumentSymbols(item, symbols);
|
|
573
551
|
}
|
|
574
552
|
return symbols;
|
|
575
|
-
}
|
|
553
|
+
}
|
|
554
|
+
const symbols = [];
|
|
555
|
+
for (const item of tree.childItems) {
|
|
556
|
+
collectSymbolInformation(params.textDocument.uri, item, symbols);
|
|
557
|
+
}
|
|
558
|
+
return symbols;
|
|
576
559
|
}
|
|
577
560
|
get supportHierarchicalDocumentSymbol() {
|
|
578
|
-
const textDocument = this.initializeParams
|
|
561
|
+
const textDocument = this.initializeParams?.capabilities.textDocument;
|
|
579
562
|
const documentSymbol = textDocument && textDocument.documentSymbol;
|
|
580
563
|
return !!documentSymbol && !!documentSymbol.hierarchicalDocumentSymbolSupport;
|
|
581
564
|
}
|
|
@@ -583,541 +566,477 @@ export class LspServer {
|
|
|
583
566
|
* implemented based on
|
|
584
567
|
* https://github.com/Microsoft/vscode/blob/master/extensions/typescript-language-features/src/features/completions.ts
|
|
585
568
|
*/
|
|
586
|
-
completion(params) {
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
const completions = (body ? body.entries : [])
|
|
610
|
-
.filter(entry => entry.kind !== 'warning')
|
|
611
|
-
.map(entry => asCompletionItem(entry, file, params.position, document, this.features));
|
|
612
|
-
return lsp.CompletionList.create(completions, body === null || body === void 0 ? void 0 : body.isIncomplete);
|
|
613
|
-
}
|
|
614
|
-
catch (error) {
|
|
615
|
-
if (error.message === 'No content available.') {
|
|
616
|
-
this.logger.info('No content was available for completion request');
|
|
617
|
-
return null;
|
|
569
|
+
async completion(params) {
|
|
570
|
+
const file = uriToPath(params.textDocument.uri);
|
|
571
|
+
this.logger.log('completion', params, file);
|
|
572
|
+
if (!file) {
|
|
573
|
+
return lsp.CompletionList.create([]);
|
|
574
|
+
}
|
|
575
|
+
const document = this.documents.get(file);
|
|
576
|
+
if (!document) {
|
|
577
|
+
throw new Error('The document should be opened for completion, file: ' + file);
|
|
578
|
+
}
|
|
579
|
+
try {
|
|
580
|
+
const result = await this.interuptDiagnostics(() => this.tspClient.request("completionInfo" /* CommandTypes.CompletionInfo */, {
|
|
581
|
+
file,
|
|
582
|
+
line: params.position.line + 1,
|
|
583
|
+
offset: params.position.character + 1,
|
|
584
|
+
triggerCharacter: getCompletionTriggerCharacter(params.context?.triggerCharacter),
|
|
585
|
+
triggerKind: params.context?.triggerKind,
|
|
586
|
+
}));
|
|
587
|
+
const { body } = result;
|
|
588
|
+
const completions = [];
|
|
589
|
+
for (const entry of body?.entries ?? []) {
|
|
590
|
+
if (entry.kind === 'warning') {
|
|
591
|
+
continue;
|
|
618
592
|
}
|
|
619
|
-
|
|
620
|
-
|
|
593
|
+
const completion = asCompletionItem(entry, file, params.position, document, this.features);
|
|
594
|
+
if (!completion) {
|
|
595
|
+
continue;
|
|
621
596
|
}
|
|
597
|
+
completions.push(completion);
|
|
622
598
|
}
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
formatOptions: this.getWorkspacePreferencesForDocument(item.data.file).format
|
|
630
|
-
});
|
|
631
|
-
const { body } = yield this.interuptDiagnostics(() => this.tspClient.request("completionEntryDetails" /* CommandTypes.CompletionDetails */, item.data));
|
|
632
|
-
const details = body && body.length && body[0];
|
|
633
|
-
if (!details) {
|
|
634
|
-
return item;
|
|
635
|
-
}
|
|
636
|
-
return asResolvedCompletionItem(item, details, this.tspClient, this.workspaceConfiguration.completions || {});
|
|
637
|
-
});
|
|
638
|
-
}
|
|
639
|
-
hover(params) {
|
|
640
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
641
|
-
const file = uriToPath(params.textDocument.uri);
|
|
642
|
-
this.logger.log('hover', params, file);
|
|
643
|
-
if (!file) {
|
|
644
|
-
return { contents: [] };
|
|
645
|
-
}
|
|
646
|
-
const result = yield this.interuptDiagnostics(() => this.getQuickInfo(file, params.position));
|
|
647
|
-
if (!result || !result.body) {
|
|
648
|
-
return { contents: [] };
|
|
599
|
+
return lsp.CompletionList.create(completions, body?.isIncomplete);
|
|
600
|
+
}
|
|
601
|
+
catch (error) {
|
|
602
|
+
if (error.message === 'No content available.') {
|
|
603
|
+
this.logger.info('No content was available for completion request');
|
|
604
|
+
return null;
|
|
649
605
|
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
if (result.body.displayString) {
|
|
653
|
-
contents.push({ language: 'typescript', value: result.body.displayString });
|
|
606
|
+
else {
|
|
607
|
+
throw error;
|
|
654
608
|
}
|
|
655
|
-
|
|
656
|
-
const documentation = asPlainText(result.body.documentation);
|
|
657
|
-
contents.push(documentation + (tags ? '\n\n' + tags : ''));
|
|
658
|
-
return {
|
|
659
|
-
contents,
|
|
660
|
-
range
|
|
661
|
-
};
|
|
662
|
-
});
|
|
609
|
+
}
|
|
663
610
|
}
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
catch (err) {
|
|
674
|
-
return undefined;
|
|
675
|
-
}
|
|
676
|
-
});
|
|
611
|
+
async completionResolve(item) {
|
|
612
|
+
this.logger.log('completion/resolve', item);
|
|
613
|
+
await this.configurationManager.configureGloballyFromDocument(item.data.file);
|
|
614
|
+
const { body } = await this.interuptDiagnostics(() => this.tspClient.request("completionEntryDetails" /* CommandTypes.CompletionDetails */, item.data));
|
|
615
|
+
const details = body && body.length && body[0];
|
|
616
|
+
if (!details) {
|
|
617
|
+
return item;
|
|
618
|
+
}
|
|
619
|
+
return asResolvedCompletionItem(item, details, this.tspClient, this.configurationManager.workspaceConfiguration.completions || {}, this.features);
|
|
677
620
|
}
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
621
|
+
async hover(params) {
|
|
622
|
+
const file = uriToPath(params.textDocument.uri);
|
|
623
|
+
this.logger.log('hover', params, file);
|
|
624
|
+
if (!file) {
|
|
625
|
+
return { contents: [] };
|
|
626
|
+
}
|
|
627
|
+
const result = await this.interuptDiagnostics(() => this.getQuickInfo(file, params.position));
|
|
628
|
+
if (!result || !result.body) {
|
|
629
|
+
return { contents: [] };
|
|
630
|
+
}
|
|
631
|
+
const range = Range.fromTextSpan(result.body);
|
|
632
|
+
const contents = [];
|
|
633
|
+
if (result.body.displayString) {
|
|
634
|
+
contents.push({ language: 'typescript', value: result.body.displayString });
|
|
635
|
+
}
|
|
636
|
+
const tags = asTagsDocumentation(result.body.tags);
|
|
637
|
+
const documentation = asPlainText(result.body.documentation);
|
|
638
|
+
contents.push(documentation + (tags ? '\n\n' + tags : ''));
|
|
639
|
+
return {
|
|
640
|
+
contents,
|
|
641
|
+
range,
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
async getQuickInfo(file, position) {
|
|
645
|
+
try {
|
|
646
|
+
return await this.tspClient.request("quickinfo" /* CommandTypes.Quickinfo */, {
|
|
686
647
|
file,
|
|
687
|
-
line:
|
|
688
|
-
offset:
|
|
648
|
+
line: position.line + 1,
|
|
649
|
+
offset: position.character + 1,
|
|
689
650
|
});
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
651
|
+
}
|
|
652
|
+
catch (err) {
|
|
653
|
+
return undefined;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
async rename(params) {
|
|
657
|
+
const file = uriToPath(params.textDocument.uri);
|
|
658
|
+
this.logger.log('onRename', params, file);
|
|
659
|
+
if (!file) {
|
|
660
|
+
return undefined;
|
|
661
|
+
}
|
|
662
|
+
const result = await this.tspClient.request("rename" /* CommandTypes.Rename */, {
|
|
663
|
+
file,
|
|
664
|
+
line: params.position.line + 1,
|
|
665
|
+
offset: params.position.character + 1,
|
|
666
|
+
});
|
|
667
|
+
if (!result.body || !result.body.info.canRename || result.body.locs.length === 0) {
|
|
668
|
+
return undefined;
|
|
669
|
+
}
|
|
670
|
+
const workspaceEdit = {};
|
|
671
|
+
result.body.locs
|
|
672
|
+
.forEach((spanGroup) => {
|
|
673
|
+
const uri = pathToUri(spanGroup.file, this.documents);
|
|
674
|
+
if (!workspaceEdit.changes) {
|
|
675
|
+
workspaceEdit.changes = {};
|
|
676
|
+
}
|
|
677
|
+
const textEdits = workspaceEdit.changes[uri] || (workspaceEdit.changes[uri] = []);
|
|
678
|
+
spanGroup.locs.forEach((textSpan) => {
|
|
679
|
+
textEdits.push({
|
|
680
|
+
newText: `${textSpan.prefixText || ''}${params.newName}${textSpan.suffixText || ''}`,
|
|
681
|
+
range: {
|
|
682
|
+
start: Position.fromLocation(textSpan.start),
|
|
683
|
+
end: Position.fromLocation(textSpan.end),
|
|
684
|
+
},
|
|
707
685
|
});
|
|
708
686
|
});
|
|
709
|
-
return workspaceEdit;
|
|
710
687
|
});
|
|
688
|
+
return workspaceEdit;
|
|
711
689
|
}
|
|
712
|
-
references(params) {
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
offset: params.position.character + 1
|
|
723
|
-
});
|
|
724
|
-
if (!result.body) {
|
|
725
|
-
return [];
|
|
726
|
-
}
|
|
727
|
-
return result.body.refs
|
|
728
|
-
.filter(fileSpan => params.context.includeDeclaration || !fileSpan.isDefinition)
|
|
729
|
-
.map(fileSpan => toLocation(fileSpan, this.documents));
|
|
690
|
+
async references(params) {
|
|
691
|
+
const file = uriToPath(params.textDocument.uri);
|
|
692
|
+
this.logger.log('onReferences', params, file);
|
|
693
|
+
if (!file) {
|
|
694
|
+
return [];
|
|
695
|
+
}
|
|
696
|
+
const result = await this.tspClient.request("references" /* CommandTypes.References */, {
|
|
697
|
+
file,
|
|
698
|
+
line: params.position.line + 1,
|
|
699
|
+
offset: params.position.character + 1,
|
|
730
700
|
});
|
|
701
|
+
if (!result.body) {
|
|
702
|
+
return [];
|
|
703
|
+
}
|
|
704
|
+
return result.body.refs
|
|
705
|
+
.filter(fileSpan => params.context.includeDeclaration || !fileSpan.isDefinition)
|
|
706
|
+
.map(fileSpan => toLocation(fileSpan, this.documents));
|
|
731
707
|
}
|
|
732
|
-
documentFormatting(params) {
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
if (!file) {
|
|
737
|
-
return [];
|
|
738
|
-
}
|
|
739
|
-
const formatOptions = this.getFormattingOptions(file, params.options);
|
|
740
|
-
// options are not yet supported in tsserver, but we can send a configure request first
|
|
741
|
-
yield this.tspClient.request("configure" /* CommandTypes.Configure */, {
|
|
742
|
-
formatOptions
|
|
743
|
-
});
|
|
744
|
-
const response = yield this.tspClient.request("format" /* CommandTypes.Format */, {
|
|
745
|
-
file,
|
|
746
|
-
line: 1,
|
|
747
|
-
offset: 1,
|
|
748
|
-
endLine: Number.MAX_SAFE_INTEGER,
|
|
749
|
-
endOffset: Number.MAX_SAFE_INTEGER,
|
|
750
|
-
options: formatOptions
|
|
751
|
-
});
|
|
752
|
-
if (response.body) {
|
|
753
|
-
return response.body.map(e => toTextEdit(e));
|
|
754
|
-
}
|
|
708
|
+
async documentFormatting(params) {
|
|
709
|
+
const file = uriToPath(params.textDocument.uri);
|
|
710
|
+
this.logger.log('documentFormatting', params, file);
|
|
711
|
+
if (!file) {
|
|
755
712
|
return [];
|
|
713
|
+
}
|
|
714
|
+
const formatOptions = params.options;
|
|
715
|
+
await this.configurationManager.configureGloballyFromDocument(file, formatOptions);
|
|
716
|
+
const response = await this.tspClient.request("format" /* CommandTypes.Format */, {
|
|
717
|
+
file,
|
|
718
|
+
line: 1,
|
|
719
|
+
offset: 1,
|
|
720
|
+
endLine: Number.MAX_SAFE_INTEGER,
|
|
721
|
+
endOffset: Number.MAX_SAFE_INTEGER,
|
|
722
|
+
options: formatOptions,
|
|
756
723
|
});
|
|
724
|
+
if (response.body) {
|
|
725
|
+
return response.body.map(e => toTextEdit(e));
|
|
726
|
+
}
|
|
727
|
+
return [];
|
|
757
728
|
}
|
|
758
|
-
documentRangeFormatting(params) {
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
if (!file) {
|
|
763
|
-
return [];
|
|
764
|
-
}
|
|
765
|
-
const formatOptions = this.getFormattingOptions(file, params.options);
|
|
766
|
-
// options are not yet supported in tsserver, but we can send a configure request first
|
|
767
|
-
yield this.tspClient.request("configure" /* CommandTypes.Configure */, {
|
|
768
|
-
formatOptions
|
|
769
|
-
});
|
|
770
|
-
const response = yield this.tspClient.request("format" /* CommandTypes.Format */, {
|
|
771
|
-
file,
|
|
772
|
-
line: params.range.start.line + 1,
|
|
773
|
-
offset: params.range.start.character + 1,
|
|
774
|
-
endLine: params.range.end.line + 1,
|
|
775
|
-
endOffset: params.range.end.character + 1,
|
|
776
|
-
options: formatOptions
|
|
777
|
-
});
|
|
778
|
-
if (response.body) {
|
|
779
|
-
return response.body.map(e => toTextEdit(e));
|
|
780
|
-
}
|
|
729
|
+
async documentRangeFormatting(params) {
|
|
730
|
+
const file = uriToPath(params.textDocument.uri);
|
|
731
|
+
this.logger.log('documentRangeFormatting', params, file);
|
|
732
|
+
if (!file) {
|
|
781
733
|
return [];
|
|
734
|
+
}
|
|
735
|
+
const formatOptions = params.options;
|
|
736
|
+
await this.configurationManager.configureGloballyFromDocument(file, formatOptions);
|
|
737
|
+
const response = await this.tspClient.request("format" /* CommandTypes.Format */, {
|
|
738
|
+
file,
|
|
739
|
+
line: params.range.start.line + 1,
|
|
740
|
+
offset: params.range.start.character + 1,
|
|
741
|
+
endLine: params.range.end.line + 1,
|
|
742
|
+
endOffset: params.range.end.character + 1,
|
|
743
|
+
options: formatOptions,
|
|
782
744
|
});
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
const workspacePreference = this.getWorkspacePreferencesForDocument(file);
|
|
786
|
-
let opts = Object.assign(Object.assign({}, (workspacePreference === null || workspacePreference === void 0 ? void 0 : workspacePreference.format) || {}), requestOptions);
|
|
787
|
-
// translate
|
|
788
|
-
if (opts.convertTabsToSpaces === undefined) {
|
|
789
|
-
opts.convertTabsToSpaces = requestOptions.insertSpaces;
|
|
745
|
+
if (response.body) {
|
|
746
|
+
return response.body.map(e => toTextEdit(e));
|
|
790
747
|
}
|
|
791
|
-
|
|
792
|
-
|
|
748
|
+
return [];
|
|
749
|
+
}
|
|
750
|
+
async signatureHelp(params) {
|
|
751
|
+
const file = uriToPath(params.textDocument.uri);
|
|
752
|
+
this.logger.log('signatureHelp', params, file);
|
|
753
|
+
if (!file) {
|
|
754
|
+
return undefined;
|
|
793
755
|
}
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
}
|
|
798
|
-
catch (err) {
|
|
799
|
-
this.logger.log(`No formatting options found ${err}`);
|
|
800
|
-
}
|
|
756
|
+
const response = await this.interuptDiagnostics(() => this.getSignatureHelp(file, params));
|
|
757
|
+
if (!response || !response.body) {
|
|
758
|
+
return undefined;
|
|
801
759
|
}
|
|
802
|
-
return
|
|
803
|
-
}
|
|
804
|
-
signatureHelp(params) {
|
|
805
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
806
|
-
const file = uriToPath(params.textDocument.uri);
|
|
807
|
-
this.logger.log('signatureHelp', params, file);
|
|
808
|
-
if (!file) {
|
|
809
|
-
return undefined;
|
|
810
|
-
}
|
|
811
|
-
const response = yield this.interuptDiagnostics(() => this.getSignatureHelp(file, params.position));
|
|
812
|
-
if (!response || !response.body) {
|
|
813
|
-
return undefined;
|
|
814
|
-
}
|
|
815
|
-
return asSignatureHelp(response.body);
|
|
816
|
-
});
|
|
760
|
+
return asSignatureHelp(response.body, params.context);
|
|
817
761
|
}
|
|
818
|
-
getSignatureHelp(file,
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
}
|
|
762
|
+
async getSignatureHelp(file, params) {
|
|
763
|
+
try {
|
|
764
|
+
const { position, context } = params;
|
|
765
|
+
return await this.tspClient.request("signatureHelp" /* CommandTypes.SignatureHelp */, {
|
|
766
|
+
file,
|
|
767
|
+
line: position.line + 1,
|
|
768
|
+
offset: position.character + 1,
|
|
769
|
+
triggerReason: context ? toTsTriggerReason(context) : undefined,
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
catch (err) {
|
|
773
|
+
return undefined;
|
|
774
|
+
}
|
|
831
775
|
}
|
|
832
|
-
codeAction(params) {
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
const
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
//
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
if (
|
|
869
|
-
|
|
870
|
-
if (diagnostics.length) {
|
|
871
|
-
actions.push(...yield this.typeScriptAutoFixProvider.provideCodeActions(kinds, file, diagnostics, this.documents));
|
|
872
|
-
}
|
|
776
|
+
async codeAction(params) {
|
|
777
|
+
const file = uriToPath(params.textDocument.uri);
|
|
778
|
+
this.logger.log('codeAction', params, file);
|
|
779
|
+
if (!file) {
|
|
780
|
+
return [];
|
|
781
|
+
}
|
|
782
|
+
const args = Range.toFileRangeRequestArgs(file, params.range);
|
|
783
|
+
const actions = [];
|
|
784
|
+
const kinds = params.context.only?.map(kind => new CodeActionKind(kind));
|
|
785
|
+
if (!kinds || kinds.some(kind => kind.contains(CodeActionKind.QuickFix))) {
|
|
786
|
+
const errorCodes = params.context.diagnostics.map(diagnostic => Number(diagnostic.code));
|
|
787
|
+
actions.push(...provideQuickFix(await this.getCodeFixes({ ...args, errorCodes }), this.documents));
|
|
788
|
+
}
|
|
789
|
+
if (!kinds || kinds.some(kind => kind.contains(CodeActionKind.Refactor))) {
|
|
790
|
+
actions.push(...provideRefactors(await this.getRefactors(args), args));
|
|
791
|
+
}
|
|
792
|
+
// organize import is provided by tsserver for any line, so we only get it if explicitly requested
|
|
793
|
+
if (kinds?.some(kind => kind.contains(CodeActionKind.SourceOrganizeImportsTs))) {
|
|
794
|
+
// see this issue for more context about how this argument is used
|
|
795
|
+
// https://github.com/microsoft/TypeScript/issues/43051
|
|
796
|
+
const skipDestructiveCodeActions = params.context.diagnostics.some(
|
|
797
|
+
// assume no severity is an error
|
|
798
|
+
d => (d.severity ?? 0) <= 2);
|
|
799
|
+
const response = await this.getOrganizeImports({
|
|
800
|
+
scope: { type: 'file', args },
|
|
801
|
+
skipDestructiveCodeActions,
|
|
802
|
+
});
|
|
803
|
+
actions.push(...provideOrganizeImports(response, this.documents));
|
|
804
|
+
}
|
|
805
|
+
// TODO: Since we rely on diagnostics pointing at errors in the correct places, we can't proceed if we are not
|
|
806
|
+
// sure that diagnostics are up-to-date. Thus we check `pendingDebouncedRequest` to see if there are *any*
|
|
807
|
+
// pending diagnostic requests (regardless of for which file).
|
|
808
|
+
// In general would be better to replace the whole diagnostics handling logic with the one from
|
|
809
|
+
// bufferSyncSupport.ts in VSCode's typescript language features.
|
|
810
|
+
if (kinds && !this.pendingDebouncedRequest) {
|
|
811
|
+
const diagnostics = this.diagnosticQueue?.getDiagnosticsForFile(file) || [];
|
|
812
|
+
if (diagnostics.length) {
|
|
813
|
+
actions.push(...await this.typeScriptAutoFixProvider.provideCodeActions(kinds, file, diagnostics, this.documents));
|
|
873
814
|
}
|
|
874
|
-
|
|
875
|
-
|
|
815
|
+
}
|
|
816
|
+
return actions;
|
|
876
817
|
}
|
|
877
|
-
getCodeFixes(args) {
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
}
|
|
885
|
-
});
|
|
818
|
+
async getCodeFixes(args) {
|
|
819
|
+
try {
|
|
820
|
+
return await this.tspClient.request("getCodeFixes" /* CommandTypes.GetCodeFixes */, args);
|
|
821
|
+
}
|
|
822
|
+
catch (err) {
|
|
823
|
+
return undefined;
|
|
824
|
+
}
|
|
886
825
|
}
|
|
887
|
-
getRefactors(args) {
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
}
|
|
895
|
-
});
|
|
826
|
+
async getRefactors(args) {
|
|
827
|
+
try {
|
|
828
|
+
return await this.tspClient.request("getApplicableRefactors" /* CommandTypes.GetApplicableRefactors */, args);
|
|
829
|
+
}
|
|
830
|
+
catch (err) {
|
|
831
|
+
return undefined;
|
|
832
|
+
}
|
|
896
833
|
}
|
|
897
|
-
getOrganizeImports(args) {
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
}
|
|
906
|
-
catch (err) {
|
|
907
|
-
return undefined;
|
|
908
|
-
}
|
|
909
|
-
});
|
|
834
|
+
async getOrganizeImports(args) {
|
|
835
|
+
try {
|
|
836
|
+
await this.configurationManager.configureGloballyFromDocument(args.scope.args.file);
|
|
837
|
+
return await this.tspClient.request("organizeImports" /* CommandTypes.OrganizeImports */, args);
|
|
838
|
+
}
|
|
839
|
+
catch (err) {
|
|
840
|
+
return undefined;
|
|
841
|
+
}
|
|
910
842
|
}
|
|
911
|
-
executeCommand(arg) {
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
if (codeAction.commands && codeAction.commands.length) {
|
|
926
|
-
for (const command of codeAction.commands) {
|
|
927
|
-
yield this.tspClient.request("applyCodeActionCommand" /* CommandTypes.ApplyCodeActionCommand */, { command });
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
else if (arg.command === Commands.APPLY_REFACTORING && arg.arguments) {
|
|
932
|
-
const args = arg.arguments[0];
|
|
933
|
-
const { body } = yield this.tspClient.request("getEditsForRefactor" /* CommandTypes.GetEditsForRefactor */, args);
|
|
934
|
-
if (!body || !body.edits.length) {
|
|
935
|
-
return;
|
|
936
|
-
}
|
|
937
|
-
for (const edit of body.edits) {
|
|
938
|
-
yield fs.ensureFile(edit.fileName);
|
|
939
|
-
}
|
|
940
|
-
if (!(yield this.applyFileCodeEdits(body.edits))) {
|
|
941
|
-
return;
|
|
942
|
-
}
|
|
943
|
-
const renameLocation = body.renameLocation;
|
|
944
|
-
if (renameLocation) {
|
|
945
|
-
yield this.options.lspClient.rename({
|
|
946
|
-
textDocument: {
|
|
947
|
-
uri: pathToUri(args.file, this.documents)
|
|
948
|
-
},
|
|
949
|
-
position: toPosition(renameLocation)
|
|
950
|
-
});
|
|
843
|
+
async executeCommand(arg, token, workDoneProgress) {
|
|
844
|
+
this.logger.log('executeCommand', arg);
|
|
845
|
+
if (arg.command === Commands.APPLY_WORKSPACE_EDIT && arg.arguments) {
|
|
846
|
+
const edit = arg.arguments[0];
|
|
847
|
+
await this.options.lspClient.applyWorkspaceEdit({ edit });
|
|
848
|
+
}
|
|
849
|
+
else if (arg.command === Commands.APPLY_CODE_ACTION && arg.arguments) {
|
|
850
|
+
const codeAction = arg.arguments[0];
|
|
851
|
+
if (!await this.applyFileCodeEdits(codeAction.changes)) {
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
if (codeAction.commands && codeAction.commands.length) {
|
|
855
|
+
for (const command of codeAction.commands) {
|
|
856
|
+
await this.tspClient.request("applyCodeActionCommand" /* CommandTypes.ApplyCodeActionCommand */, { command });
|
|
951
857
|
}
|
|
952
858
|
}
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
859
|
+
}
|
|
860
|
+
else if (arg.command === Commands.APPLY_REFACTORING && arg.arguments) {
|
|
861
|
+
const args = arg.arguments[0];
|
|
862
|
+
const { body } = await this.tspClient.request("getEditsForRefactor" /* CommandTypes.GetEditsForRefactor */, args);
|
|
863
|
+
if (!body || !body.edits.length) {
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
for (const edit of body.edits) {
|
|
867
|
+
await fs.ensureFile(edit.fileName);
|
|
868
|
+
}
|
|
869
|
+
if (!await this.applyFileCodeEdits(body.edits)) {
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
const renameLocation = body.renameLocation;
|
|
873
|
+
if (renameLocation) {
|
|
874
|
+
await this.options.lspClient.rename({
|
|
875
|
+
textDocument: {
|
|
876
|
+
uri: pathToUri(args.file, this.documents),
|
|
963
877
|
},
|
|
964
|
-
|
|
878
|
+
position: Position.fromLocation(renameLocation),
|
|
965
879
|
});
|
|
966
|
-
yield this.applyFileCodeEdits(body);
|
|
967
|
-
}
|
|
968
|
-
else if (arg.command === Commands.APPLY_RENAME_FILE && arg.arguments) {
|
|
969
|
-
const { sourceUri, targetUri } = arg.arguments[0];
|
|
970
|
-
this.applyRenameFile(sourceUri, targetUri);
|
|
971
880
|
}
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
881
|
+
}
|
|
882
|
+
else if (arg.command === Commands.ORGANIZE_IMPORTS && arg.arguments) {
|
|
883
|
+
const file = arg.arguments[0];
|
|
884
|
+
const additionalArguments = arg.arguments[1] || {};
|
|
885
|
+
await this.configurationManager.configureGloballyFromDocument(file);
|
|
886
|
+
const { body } = await this.tspClient.request("organizeImports" /* CommandTypes.OrganizeImports */, {
|
|
887
|
+
scope: {
|
|
888
|
+
type: 'file',
|
|
889
|
+
args: { file },
|
|
890
|
+
},
|
|
891
|
+
skipDestructiveCodeActions: additionalArguments.skipDestructiveCodeActions,
|
|
892
|
+
});
|
|
893
|
+
await this.applyFileCodeEdits(body);
|
|
894
|
+
}
|
|
895
|
+
else if (arg.command === Commands.APPLY_RENAME_FILE && arg.arguments) {
|
|
896
|
+
const { sourceUri, targetUri } = arg.arguments[0];
|
|
897
|
+
this.applyRenameFile(sourceUri, targetUri);
|
|
898
|
+
}
|
|
899
|
+
else if (arg.command === Commands.APPLY_COMPLETION_CODE_ACTION && arg.arguments) {
|
|
900
|
+
const [_, codeActions] = arg.arguments;
|
|
901
|
+
for (const codeAction of codeActions) {
|
|
902
|
+
await this.applyFileCodeEdits(codeAction.changes);
|
|
903
|
+
if (codeAction.commands && codeAction.commands.length) {
|
|
904
|
+
for (const command of codeAction.commands) {
|
|
905
|
+
await this.tspClient.request("applyCodeActionCommand" /* CommandTypes.ApplyCodeActionCommand */, { command });
|
|
980
906
|
}
|
|
981
|
-
// Execute only the first code action.
|
|
982
|
-
break;
|
|
983
907
|
}
|
|
908
|
+
// Execute only the first code action.
|
|
909
|
+
break;
|
|
984
910
|
}
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
911
|
+
}
|
|
912
|
+
else if (arg.command === Commands.SOURCE_DEFINITION) {
|
|
913
|
+
const [uri, position] = (arg.arguments || []);
|
|
914
|
+
const reporter = await this.options.lspClient.createProgressReporter(token, workDoneProgress);
|
|
915
|
+
return SourceDefinitionCommand.execute(uri, position, this.documents, this.tspClient, this.options.lspClient, reporter);
|
|
916
|
+
}
|
|
917
|
+
else {
|
|
918
|
+
this.logger.error(`Unknown command ${arg.command}.`);
|
|
919
|
+
}
|
|
989
920
|
}
|
|
990
|
-
applyFileCodeEdits(edits) {
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
edit: { changes }
|
|
1001
|
-
});
|
|
1002
|
-
return applied;
|
|
921
|
+
async applyFileCodeEdits(edits) {
|
|
922
|
+
if (!edits.length) {
|
|
923
|
+
return false;
|
|
924
|
+
}
|
|
925
|
+
const changes = {};
|
|
926
|
+
for (const edit of edits) {
|
|
927
|
+
changes[pathToUri(edit.fileName, this.documents)] = edit.textChanges.map(toTextEdit);
|
|
928
|
+
}
|
|
929
|
+
const { applied } = await this.options.lspClient.applyWorkspaceEdit({
|
|
930
|
+
edit: { changes },
|
|
1003
931
|
});
|
|
932
|
+
return applied;
|
|
1004
933
|
}
|
|
1005
|
-
applyRenameFile(sourceUri, targetUri) {
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
this.applyFileCodeEdits(edits);
|
|
1009
|
-
});
|
|
934
|
+
async applyRenameFile(sourceUri, targetUri) {
|
|
935
|
+
const edits = await this.getEditsForFileRename(sourceUri, targetUri);
|
|
936
|
+
this.applyFileCodeEdits(edits);
|
|
1010
937
|
}
|
|
1011
|
-
getEditsForFileRename(sourceUri, targetUri) {
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
}
|
|
1028
|
-
});
|
|
938
|
+
async getEditsForFileRename(sourceUri, targetUri) {
|
|
939
|
+
const newFilePath = uriToPath(targetUri);
|
|
940
|
+
const oldFilePath = uriToPath(sourceUri);
|
|
941
|
+
if (!newFilePath || !oldFilePath) {
|
|
942
|
+
return [];
|
|
943
|
+
}
|
|
944
|
+
try {
|
|
945
|
+
const { body } = await this.tspClient.request("getEditsForFileRename" /* CommandTypes.GetEditsForFileRename */, {
|
|
946
|
+
oldFilePath,
|
|
947
|
+
newFilePath,
|
|
948
|
+
});
|
|
949
|
+
return body;
|
|
950
|
+
}
|
|
951
|
+
catch (err) {
|
|
952
|
+
return [];
|
|
953
|
+
}
|
|
1029
954
|
}
|
|
1030
|
-
documentHighlight(arg) {
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
result.push(...highlights);
|
|
1059
|
-
}
|
|
955
|
+
async documentHighlight(arg) {
|
|
956
|
+
const file = uriToPath(arg.textDocument.uri);
|
|
957
|
+
this.logger.log('documentHighlight', arg, file);
|
|
958
|
+
if (!file) {
|
|
959
|
+
return [];
|
|
960
|
+
}
|
|
961
|
+
let response;
|
|
962
|
+
try {
|
|
963
|
+
response = await this.tspClient.request("documentHighlights" /* CommandTypes.DocumentHighlights */, {
|
|
964
|
+
file,
|
|
965
|
+
line: arg.position.line + 1,
|
|
966
|
+
offset: arg.position.character + 1,
|
|
967
|
+
filesToSearch: [file],
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
catch (err) {
|
|
971
|
+
return [];
|
|
972
|
+
}
|
|
973
|
+
if (!response.body) {
|
|
974
|
+
return [];
|
|
975
|
+
}
|
|
976
|
+
const result = [];
|
|
977
|
+
for (const item of response.body) {
|
|
978
|
+
// tsp returns item.file with POSIX path delimiters, whereas file is platform specific.
|
|
979
|
+
// Converting to a URI and back to a path ensures consistency.
|
|
980
|
+
if (normalizePath(item.file) === file) {
|
|
981
|
+
const highlights = toDocumentHighlight(item);
|
|
982
|
+
result.push(...highlights);
|
|
1060
983
|
}
|
|
1061
|
-
|
|
1062
|
-
|
|
984
|
+
}
|
|
985
|
+
return result;
|
|
1063
986
|
}
|
|
1064
987
|
lastFileOrDummy() {
|
|
1065
988
|
return this.documents.files[0] || this.workspaceRoot;
|
|
1066
989
|
}
|
|
1067
|
-
workspaceSymbol(params) {
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
return
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
end: toPosition(item.end)
|
|
1083
|
-
}
|
|
990
|
+
async workspaceSymbol(params) {
|
|
991
|
+
const result = await this.tspClient.request("navto" /* CommandTypes.Navto */, {
|
|
992
|
+
file: this.lastFileOrDummy(),
|
|
993
|
+
searchValue: params.query,
|
|
994
|
+
});
|
|
995
|
+
if (!result.body) {
|
|
996
|
+
return [];
|
|
997
|
+
}
|
|
998
|
+
return result.body.map(item => {
|
|
999
|
+
return {
|
|
1000
|
+
location: {
|
|
1001
|
+
uri: pathToUri(item.file, this.documents),
|
|
1002
|
+
range: {
|
|
1003
|
+
start: Position.fromLocation(item.start),
|
|
1004
|
+
end: Position.fromLocation(item.end),
|
|
1084
1005
|
},
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
}
|
|
1006
|
+
},
|
|
1007
|
+
kind: toSymbolKind(item.kind),
|
|
1008
|
+
name: item.name,
|
|
1009
|
+
};
|
|
1089
1010
|
});
|
|
1090
1011
|
}
|
|
1091
1012
|
/**
|
|
1092
1013
|
* implemented based on https://github.com/Microsoft/vscode/blob/master/extensions/typescript-language-features/src/features/folding.ts
|
|
1093
1014
|
*/
|
|
1094
|
-
foldingRanges(params) {
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
foldingRanges.push(foldingRange);
|
|
1114
|
-
}
|
|
1015
|
+
async foldingRanges(params) {
|
|
1016
|
+
const file = uriToPath(params.textDocument.uri);
|
|
1017
|
+
this.logger.log('foldingRanges', params, file);
|
|
1018
|
+
if (!file) {
|
|
1019
|
+
return undefined;
|
|
1020
|
+
}
|
|
1021
|
+
const document = this.documents.get(file);
|
|
1022
|
+
if (!document) {
|
|
1023
|
+
throw new Error("The document should be opened for foldingRanges', file: " + file);
|
|
1024
|
+
}
|
|
1025
|
+
const { body } = await this.tspClient.request("getOutliningSpans" /* CommandTypes.GetOutliningSpans */, { file });
|
|
1026
|
+
if (!body) {
|
|
1027
|
+
return undefined;
|
|
1028
|
+
}
|
|
1029
|
+
const foldingRanges = [];
|
|
1030
|
+
for (const span of body) {
|
|
1031
|
+
const foldingRange = this.asFoldingRange(span, document);
|
|
1032
|
+
if (foldingRange) {
|
|
1033
|
+
foldingRanges.push(foldingRange);
|
|
1115
1034
|
}
|
|
1116
|
-
|
|
1117
|
-
|
|
1035
|
+
}
|
|
1036
|
+
return foldingRanges;
|
|
1118
1037
|
}
|
|
1119
1038
|
asFoldingRange(span, document) {
|
|
1120
|
-
const range =
|
|
1039
|
+
const range = Range.fromTextSpan(span.textSpan);
|
|
1121
1040
|
const kind = this.asFoldingRangeKind(span);
|
|
1122
1041
|
// workaround for https://github.com/Microsoft/vscode/issues/49904
|
|
1123
1042
|
if (span.kind === 'comment') {
|
|
@@ -1132,7 +1051,7 @@ export class LspServer {
|
|
|
1132
1051
|
return {
|
|
1133
1052
|
startLine,
|
|
1134
1053
|
endLine,
|
|
1135
|
-
kind
|
|
1054
|
+
kind,
|
|
1136
1055
|
};
|
|
1137
1056
|
}
|
|
1138
1057
|
asFoldingRangeKind(span) {
|
|
@@ -1144,150 +1063,136 @@ export class LspServer {
|
|
|
1144
1063
|
default: return undefined;
|
|
1145
1064
|
}
|
|
1146
1065
|
}
|
|
1147
|
-
onTsEvent(event) {
|
|
1148
|
-
var _a;
|
|
1066
|
+
async onTsEvent(event) {
|
|
1149
1067
|
if (event.event === "semanticDiag" /* EventTypes.SementicDiag */ ||
|
|
1150
1068
|
event.event === "syntaxDiag" /* EventTypes.SyntaxDiag */ ||
|
|
1151
1069
|
event.event === "suggestionDiag" /* EventTypes.SuggestionDiag */) {
|
|
1152
|
-
|
|
1070
|
+
this.diagnosticQueue?.updateDiagnostics(event.event, event);
|
|
1153
1071
|
}
|
|
1154
1072
|
else if (event.event === "projectLoadingStart" /* EventTypes.ProjectLoadingStart */) {
|
|
1155
|
-
this.loadingIndicator.startedLoadingProject(event.body.projectName);
|
|
1073
|
+
await this.loadingIndicator.startedLoadingProject(event.body.projectName);
|
|
1156
1074
|
}
|
|
1157
1075
|
else if (event.event === "projectLoadingFinish" /* EventTypes.ProjectLoadingFinish */) {
|
|
1158
1076
|
this.loadingIndicator.finishedLoadingProject(event.body.projectName);
|
|
1159
1077
|
}
|
|
1160
1078
|
else {
|
|
1161
1079
|
this.logger.log('Ignored event', {
|
|
1162
|
-
event: event.event
|
|
1080
|
+
event: event.event,
|
|
1163
1081
|
});
|
|
1164
1082
|
}
|
|
1165
1083
|
}
|
|
1166
|
-
calls(params) {
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
if (!file) {
|
|
1172
|
-
return callsResult;
|
|
1173
|
-
}
|
|
1174
|
-
if (params.direction === lspcalls.CallDirection.Outgoing) {
|
|
1175
|
-
const documentProvider = (file) => this.documents.get(file);
|
|
1176
|
-
callsResult = yield computeCallees(this.tspClient, params, documentProvider);
|
|
1177
|
-
}
|
|
1178
|
-
else {
|
|
1179
|
-
callsResult = yield computeCallers(this.tspClient, params);
|
|
1180
|
-
}
|
|
1084
|
+
async calls(params) {
|
|
1085
|
+
let callsResult = { calls: [] };
|
|
1086
|
+
const file = uriToPath(params.textDocument.uri);
|
|
1087
|
+
this.logger.log('calls', params, file);
|
|
1088
|
+
if (!file) {
|
|
1181
1089
|
return callsResult;
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
this.
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
}
|
|
1192
|
-
yield this.tspClient.request("configure" /* CommandTypes.Configure */, {
|
|
1193
|
-
preferences: this.getInlayHintsOptions(file)
|
|
1194
|
-
});
|
|
1195
|
-
const doc = this.documents.get(file);
|
|
1196
|
-
if (!doc) {
|
|
1197
|
-
return { inlayHints: [] };
|
|
1198
|
-
}
|
|
1199
|
-
const start = doc.offsetAt((_b = (_a = params.range) === null || _a === void 0 ? void 0 : _a.start) !== null && _b !== void 0 ? _b : {
|
|
1200
|
-
line: 0,
|
|
1201
|
-
character: 0
|
|
1202
|
-
});
|
|
1203
|
-
const end = doc.offsetAt((_d = (_c = params.range) === null || _c === void 0 ? void 0 : _c.end) !== null && _d !== void 0 ? _d : {
|
|
1204
|
-
line: doc.lineCount + 1,
|
|
1205
|
-
character: 0
|
|
1206
|
-
});
|
|
1207
|
-
try {
|
|
1208
|
-
const result = yield this.tspClient.request("provideInlayHints" /* CommandTypes.ProvideInlayHints */, {
|
|
1209
|
-
file,
|
|
1210
|
-
start: start,
|
|
1211
|
-
length: end - start
|
|
1212
|
-
});
|
|
1213
|
-
return {
|
|
1214
|
-
inlayHints: (_f = (_e = result.body) === null || _e === void 0 ? void 0 : _e.map((item) => ({
|
|
1215
|
-
text: item.text,
|
|
1216
|
-
position: toPosition(item.position),
|
|
1217
|
-
whitespaceAfter: item.whitespaceAfter,
|
|
1218
|
-
whitespaceBefore: item.whitespaceBefore,
|
|
1219
|
-
kind: item.kind
|
|
1220
|
-
}))) !== null && _f !== void 0 ? _f : []
|
|
1221
|
-
};
|
|
1222
|
-
}
|
|
1223
|
-
catch (_g) {
|
|
1224
|
-
return {
|
|
1225
|
-
inlayHints: []
|
|
1226
|
-
};
|
|
1227
|
-
}
|
|
1228
|
-
});
|
|
1090
|
+
}
|
|
1091
|
+
if (params.direction === lspcalls.CallDirection.Outgoing) {
|
|
1092
|
+
const documentProvider = (file) => this.documents.get(file);
|
|
1093
|
+
callsResult = await computeCallees(this.tspClient, params, documentProvider);
|
|
1094
|
+
}
|
|
1095
|
+
else {
|
|
1096
|
+
callsResult = await computeCallers(this.tspClient, params);
|
|
1097
|
+
}
|
|
1098
|
+
return callsResult;
|
|
1229
1099
|
}
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
const workspacePreference = this.getWorkspacePreferencesForDocument(file);
|
|
1233
|
-
const userPreferences = ((_a = this.initializeParams.initializationOptions) === null || _a === void 0 ? void 0 : _a.preferences) || {};
|
|
1234
|
-
return Object.assign(Object.assign({}, userPreferences), (_b = workspacePreference.inlayHints) !== null && _b !== void 0 ? _b : {});
|
|
1100
|
+
async inlayHints(params) {
|
|
1101
|
+
return await TypeScriptInlayHintsProvider.provideInlayHints(params.textDocument.uri, params.range, this.documents, this.tspClient, this.options.lspClient, this.configurationManager);
|
|
1235
1102
|
}
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
if (!file) {
|
|
1241
|
-
return { data: [] };
|
|
1242
|
-
}
|
|
1243
|
-
const doc = this.documents.get(file);
|
|
1244
|
-
if (!doc) {
|
|
1245
|
-
return { data: [] };
|
|
1246
|
-
}
|
|
1247
|
-
const start = doc.offsetAt({
|
|
1248
|
-
line: 0,
|
|
1249
|
-
character: 0
|
|
1250
|
-
});
|
|
1251
|
-
const end = doc.offsetAt({
|
|
1252
|
-
line: doc.lineCount,
|
|
1253
|
-
character: 0
|
|
1254
|
-
});
|
|
1255
|
-
return this.getSemanticTokens(doc, file, start, end);
|
|
1103
|
+
async inlayHintsLegacy(params) {
|
|
1104
|
+
this.options.lspClient.logMessage({
|
|
1105
|
+
message: 'Support for experimental "typescript/inlayHints" request is deprecated. Use spec-compliant "textDocument/inlayHint" instead.',
|
|
1106
|
+
type: lsp.MessageType.Warning,
|
|
1256
1107
|
});
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1108
|
+
const file = uriToPath(params.textDocument.uri);
|
|
1109
|
+
this.logger.log('inlayHints', params, file);
|
|
1110
|
+
if (!file) {
|
|
1111
|
+
return { inlayHints: [] };
|
|
1112
|
+
}
|
|
1113
|
+
await this.configurationManager.configureGloballyFromDocument(file);
|
|
1114
|
+
const doc = this.documents.get(file);
|
|
1115
|
+
if (!doc) {
|
|
1116
|
+
return { inlayHints: [] };
|
|
1117
|
+
}
|
|
1118
|
+
const start = doc.offsetAt(params.range?.start ?? {
|
|
1119
|
+
line: 0,
|
|
1120
|
+
character: 0,
|
|
1121
|
+
});
|
|
1122
|
+
const end = doc.offsetAt(params.range?.end ?? {
|
|
1123
|
+
line: doc.lineCount + 1,
|
|
1124
|
+
character: 0,
|
|
1272
1125
|
});
|
|
1126
|
+
try {
|
|
1127
|
+
const result = await this.tspClient.request("provideInlayHints" /* CommandTypes.ProvideInlayHints */, {
|
|
1128
|
+
file,
|
|
1129
|
+
start: start,
|
|
1130
|
+
length: end - start,
|
|
1131
|
+
});
|
|
1132
|
+
return {
|
|
1133
|
+
inlayHints: result.body?.map((item) => ({
|
|
1134
|
+
text: item.text,
|
|
1135
|
+
position: Position.fromLocation(item.position),
|
|
1136
|
+
whitespaceAfter: item.whitespaceAfter,
|
|
1137
|
+
whitespaceBefore: item.whitespaceBefore,
|
|
1138
|
+
kind: item.kind,
|
|
1139
|
+
})) ?? [],
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
catch {
|
|
1143
|
+
return {
|
|
1144
|
+
inlayHints: [],
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
1273
1147
|
}
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1148
|
+
async semanticTokensFull(params) {
|
|
1149
|
+
const file = uriToPath(params.textDocument.uri);
|
|
1150
|
+
this.logger.log('semanticTokensFull', params, file);
|
|
1151
|
+
if (!file) {
|
|
1152
|
+
return { data: [] };
|
|
1153
|
+
}
|
|
1154
|
+
const doc = this.documents.get(file);
|
|
1155
|
+
if (!doc) {
|
|
1156
|
+
return { data: [] };
|
|
1157
|
+
}
|
|
1158
|
+
const start = doc.offsetAt({
|
|
1159
|
+
line: 0,
|
|
1160
|
+
character: 0,
|
|
1161
|
+
});
|
|
1162
|
+
const end = doc.offsetAt({
|
|
1163
|
+
line: doc.lineCount,
|
|
1164
|
+
character: 0,
|
|
1290
1165
|
});
|
|
1166
|
+
return this.getSemanticTokens(doc, file, start, end);
|
|
1167
|
+
}
|
|
1168
|
+
async semanticTokensRange(params) {
|
|
1169
|
+
const file = uriToPath(params.textDocument.uri);
|
|
1170
|
+
this.logger.log('semanticTokensRange', params, file);
|
|
1171
|
+
if (!file) {
|
|
1172
|
+
return { data: [] };
|
|
1173
|
+
}
|
|
1174
|
+
const doc = this.documents.get(file);
|
|
1175
|
+
if (!doc) {
|
|
1176
|
+
return { data: [] };
|
|
1177
|
+
}
|
|
1178
|
+
const start = doc.offsetAt(params.range.start);
|
|
1179
|
+
const end = doc.offsetAt(params.range.end);
|
|
1180
|
+
return this.getSemanticTokens(doc, file, start, end);
|
|
1181
|
+
}
|
|
1182
|
+
async getSemanticTokens(doc, file, startOffset, endOffset) {
|
|
1183
|
+
try {
|
|
1184
|
+
const result = await this.tspClient.request("encodedSemanticClassifications-full" /* CommandTypes.EncodedSemanticClassificationsFull */, {
|
|
1185
|
+
file,
|
|
1186
|
+
start: startOffset,
|
|
1187
|
+
length: endOffset - startOffset,
|
|
1188
|
+
format: '2020',
|
|
1189
|
+
});
|
|
1190
|
+
const spans = result.body?.spans ?? [];
|
|
1191
|
+
return { data: lspsemanticTokens.transformSpans(doc, spans) };
|
|
1192
|
+
}
|
|
1193
|
+
catch {
|
|
1194
|
+
return { data: [] };
|
|
1195
|
+
}
|
|
1291
1196
|
}
|
|
1292
1197
|
}
|
|
1293
1198
|
//# sourceMappingURL=lsp-server.js.map
|