Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/api/common/extHostLanguageFeatures.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import type * as vscode from 'vscode';
7
import { asArray, coalesce, isFalsyOrEmpty, isNonEmptyArray } from '../../../base/common/arrays.js';
8
import { raceCancellationError } from '../../../base/common/async.js';
9
import { VSBuffer } from '../../../base/common/buffer.js';
10
import { CancellationToken } from '../../../base/common/cancellation.js';
11
import { NotImplementedError, isCancellationError } from '../../../base/common/errors.js';
12
import { IdGenerator } from '../../../base/common/idGenerator.js';
13
import { DisposableStore, Disposable as CoreDisposable } from '../../../base/common/lifecycle.js';
14
import { equals, mixin } from '../../../base/common/objects.js';
15
import { StopWatch } from '../../../base/common/stopwatch.js';
16
import { regExpLeadsToEndlessLoop } from '../../../base/common/strings.js';
17
import { assertType, isObject } from '../../../base/common/types.js';
18
import { URI, UriComponents } from '../../../base/common/uri.js';
19
import { IURITransformer } from '../../../base/common/uriIpc.js';
20
import { generateUuid } from '../../../base/common/uuid.js';
21
import { IPosition } from '../../../editor/common/core/position.js';
22
import { Range as EditorRange, IRange } from '../../../editor/common/core/range.js';
23
import { ISelection, Selection } from '../../../editor/common/core/selection.js';
24
import * as languages from '../../../editor/common/languages.js';
25
import { IAutoClosingPairConditional } from '../../../editor/common/languages/languageConfiguration.js';
26
import { encodeSemanticTokensDto } from '../../../editor/common/services/semanticTokensDto.js';
27
import { localize } from '../../../nls.js';
28
import { ExtensionIdentifier, IExtensionDescription } from '../../../platform/extensions/common/extensions.js';
29
import { ILogService } from '../../../platform/log/common/log.js';
30
import { isProposedApiEnabled } from '../../services/extensions/common/extensions.js';
31
import { Cache } from './cache.js';
32
import * as extHostProtocol from './extHost.protocol.js';
33
import { IExtHostApiDeprecationService } from './extHostApiDeprecationService.js';
34
import { CommandsConverter, ExtHostCommands } from './extHostCommands.js';
35
import { ExtHostDiagnostics } from './extHostDiagnostics.js';
36
import { ExtHostDocuments } from './extHostDocuments.js';
37
import { ExtHostTelemetry, IExtHostTelemetry } from './extHostTelemetry.js';
38
import * as typeConvert from './extHostTypeConverters.js';
39
import { CodeAction, CodeActionKind, CompletionList, DataTransfer, Disposable, DocumentDropOrPasteEditKind, DocumentSymbol, InlineCompletionsDisposeReasonKind, InlineCompletionDisplayLocationKind, InlineCompletionTriggerKind, InternalDataTransferItem, Location, NewSymbolNameTriggerKind, Range, SemanticTokens, SemanticTokensEdit, SemanticTokensEdits, SnippetString, SymbolInformation, SyntaxTokenType } from './extHostTypes.js';
40
import { Emitter } from '../../../base/common/event.js';
41
import { IInlineCompletionsUnificationState } from '../../services/inlineCompletions/common/inlineCompletionsUnification.js';
42
43
// --- adapter
44
45
class DocumentSymbolAdapter {
46
47
constructor(
48
private readonly _documents: ExtHostDocuments,
49
private readonly _provider: vscode.DocumentSymbolProvider
50
) { }
51
52
async provideDocumentSymbols(resource: URI, token: CancellationToken): Promise<languages.DocumentSymbol[] | undefined> {
53
const doc = this._documents.getDocument(resource);
54
const value = await this._provider.provideDocumentSymbols(doc, token);
55
if (isFalsyOrEmpty(value)) {
56
return undefined;
57
} else if (value![0] instanceof DocumentSymbol) {
58
return (<DocumentSymbol[]>value).map(typeConvert.DocumentSymbol.from);
59
} else {
60
return DocumentSymbolAdapter._asDocumentSymbolTree(<SymbolInformation[]>value);
61
}
62
}
63
64
private static _asDocumentSymbolTree(infos: SymbolInformation[]): languages.DocumentSymbol[] {
65
// first sort by start (and end) and then loop over all elements
66
// and build a tree based on containment.
67
infos = infos.slice(0).sort((a, b) => {
68
let res = a.location.range.start.compareTo(b.location.range.start);
69
if (res === 0) {
70
res = b.location.range.end.compareTo(a.location.range.end);
71
}
72
return res;
73
});
74
const res: languages.DocumentSymbol[] = [];
75
const parentStack: languages.DocumentSymbol[] = [];
76
for (const info of infos) {
77
const element: languages.DocumentSymbol = {
78
name: info.name || '!!MISSING: name!!',
79
kind: typeConvert.SymbolKind.from(info.kind),
80
tags: info.tags?.map(typeConvert.SymbolTag.from) || [],
81
detail: '',
82
containerName: info.containerName,
83
range: typeConvert.Range.from(info.location.range),
84
selectionRange: typeConvert.Range.from(info.location.range),
85
children: []
86
};
87
88
while (true) {
89
if (parentStack.length === 0) {
90
parentStack.push(element);
91
res.push(element);
92
break;
93
}
94
const parent = parentStack[parentStack.length - 1];
95
if (EditorRange.containsRange(parent.range, element.range) && !EditorRange.equalsRange(parent.range, element.range)) {
96
parent.children?.push(element);
97
parentStack.push(element);
98
break;
99
}
100
parentStack.pop();
101
}
102
}
103
return res;
104
}
105
}
106
107
class CodeLensAdapter {
108
109
private readonly _cache = new Cache<vscode.CodeLens>('CodeLens');
110
private readonly _disposables = new Map<number, DisposableStore>();
111
112
constructor(
113
private readonly _documents: ExtHostDocuments,
114
private readonly _commands: CommandsConverter,
115
private readonly _provider: vscode.CodeLensProvider,
116
private readonly _extension: IExtensionDescription,
117
private readonly _extTelemetry: ExtHostTelemetry,
118
private readonly _logService: ILogService,
119
) { }
120
121
async provideCodeLenses(resource: URI, token: CancellationToken): Promise<extHostProtocol.ICodeLensListDto | undefined> {
122
const doc = this._documents.getDocument(resource);
123
124
const lenses = await this._provider.provideCodeLenses(doc, token);
125
if (!lenses || token.isCancellationRequested) {
126
return undefined;
127
}
128
const cacheId = this._cache.add(lenses);
129
const disposables = new DisposableStore();
130
this._disposables.set(cacheId, disposables);
131
const result: extHostProtocol.ICodeLensListDto = {
132
cacheId,
133
lenses: [],
134
};
135
for (let i = 0; i < lenses.length; i++) {
136
137
if (!Range.isRange(lenses[i].range)) {
138
console.warn('INVALID code lens, range is not defined', this._extension.identifier.value);
139
continue;
140
}
141
142
result.lenses.push({
143
cacheId: [cacheId, i],
144
range: typeConvert.Range.from(lenses[i].range),
145
command: this._commands.toInternal(lenses[i].command, disposables)
146
});
147
}
148
return result;
149
}
150
151
async resolveCodeLens(symbol: extHostProtocol.ICodeLensDto, token: CancellationToken): Promise<extHostProtocol.ICodeLensDto | undefined> {
152
153
const lens = symbol.cacheId && this._cache.get(...symbol.cacheId);
154
if (!lens) {
155
return undefined;
156
}
157
158
let resolvedLens: vscode.CodeLens | undefined | null;
159
if (typeof this._provider.resolveCodeLens !== 'function' || lens.isResolved) {
160
resolvedLens = lens;
161
} else {
162
resolvedLens = await this._provider.resolveCodeLens(lens, token);
163
}
164
if (!resolvedLens) {
165
resolvedLens = lens;
166
}
167
168
if (token.isCancellationRequested) {
169
return undefined;
170
}
171
const disposables = symbol.cacheId && this._disposables.get(symbol.cacheId[0]);
172
if (!disposables) {
173
// disposed in the meantime
174
return undefined;
175
}
176
177
if (!resolvedLens.command) {
178
const error = new Error('INVALID code lens resolved, lacks command: ' + this._extension.identifier.value);
179
this._extTelemetry.onExtensionError(this._extension.identifier, error);
180
this._logService.error(error);
181
return undefined;
182
}
183
184
symbol.command = this._commands.toInternal(resolvedLens.command, disposables);
185
return symbol;
186
}
187
188
releaseCodeLenses(cachedId: number): void {
189
this._disposables.get(cachedId)?.dispose();
190
this._disposables.delete(cachedId);
191
this._cache.delete(cachedId);
192
}
193
}
194
195
function convertToLocationLinks(value: vscode.Location | vscode.Location[] | vscode.LocationLink[] | undefined | null): languages.LocationLink[] {
196
if (Array.isArray(value)) {
197
return (<any>value).map(typeConvert.DefinitionLink.from);
198
} else if (value) {
199
return [typeConvert.DefinitionLink.from(value)];
200
}
201
return [];
202
}
203
204
class DefinitionAdapter {
205
206
constructor(
207
private readonly _documents: ExtHostDocuments,
208
private readonly _provider: vscode.DefinitionProvider
209
) { }
210
211
async provideDefinition(resource: URI, position: IPosition, token: CancellationToken): Promise<languages.LocationLink[]> {
212
const doc = this._documents.getDocument(resource);
213
const pos = typeConvert.Position.to(position);
214
const value = await this._provider.provideDefinition(doc, pos, token);
215
return convertToLocationLinks(value);
216
}
217
}
218
219
class DeclarationAdapter {
220
221
constructor(
222
private readonly _documents: ExtHostDocuments,
223
private readonly _provider: vscode.DeclarationProvider
224
) { }
225
226
async provideDeclaration(resource: URI, position: IPosition, token: CancellationToken): Promise<languages.LocationLink[]> {
227
const doc = this._documents.getDocument(resource);
228
const pos = typeConvert.Position.to(position);
229
const value = await this._provider.provideDeclaration(doc, pos, token);
230
return convertToLocationLinks(value);
231
}
232
}
233
234
class ImplementationAdapter {
235
236
constructor(
237
private readonly _documents: ExtHostDocuments,
238
private readonly _provider: vscode.ImplementationProvider
239
) { }
240
241
async provideImplementation(resource: URI, position: IPosition, token: CancellationToken): Promise<languages.LocationLink[]> {
242
const doc = this._documents.getDocument(resource);
243
const pos = typeConvert.Position.to(position);
244
const value = await this._provider.provideImplementation(doc, pos, token);
245
return convertToLocationLinks(value);
246
}
247
}
248
249
class TypeDefinitionAdapter {
250
251
constructor(
252
private readonly _documents: ExtHostDocuments,
253
private readonly _provider: vscode.TypeDefinitionProvider
254
) { }
255
256
async provideTypeDefinition(resource: URI, position: IPosition, token: CancellationToken): Promise<languages.LocationLink[]> {
257
const doc = this._documents.getDocument(resource);
258
const pos = typeConvert.Position.to(position);
259
const value = await this._provider.provideTypeDefinition(doc, pos, token);
260
return convertToLocationLinks(value);
261
}
262
}
263
264
class HoverAdapter {
265
266
private _hoverCounter: number = 0;
267
private _hoverMap: Map<number, vscode.Hover> = new Map<number, vscode.Hover>();
268
269
private static HOVER_MAP_MAX_SIZE = 10;
270
271
constructor(
272
private readonly _documents: ExtHostDocuments,
273
private readonly _provider: vscode.HoverProvider,
274
) { }
275
276
async provideHover(resource: URI, position: IPosition, context: languages.HoverContext<{ id: number }> | undefined, token: CancellationToken): Promise<extHostProtocol.HoverWithId | undefined> {
277
278
const doc = this._documents.getDocument(resource);
279
const pos = typeConvert.Position.to(position);
280
281
let value: vscode.Hover | null | undefined;
282
if (context && context.verbosityRequest) {
283
const previousHoverId = context.verbosityRequest.previousHover.id;
284
const previousHover = this._hoverMap.get(previousHoverId);
285
if (!previousHover) {
286
throw new Error(`Hover with id ${previousHoverId} not found`);
287
}
288
const hoverContext: vscode.HoverContext = { verbosityDelta: context.verbosityRequest.verbosityDelta, previousHover };
289
value = await this._provider.provideHover(doc, pos, token, hoverContext);
290
} else {
291
value = await this._provider.provideHover(doc, pos, token);
292
}
293
if (!value || isFalsyOrEmpty(value.contents)) {
294
return undefined;
295
}
296
if (!value.range) {
297
value.range = doc.getWordRangeAtPosition(pos);
298
}
299
if (!value.range) {
300
value.range = new Range(pos, pos);
301
}
302
const convertedHover: languages.Hover = typeConvert.Hover.from(value);
303
const id = this._hoverCounter;
304
// Check if hover map has more than 10 elements and if yes, remove oldest from the map
305
if (this._hoverMap.size === HoverAdapter.HOVER_MAP_MAX_SIZE) {
306
const minimumId = Math.min(...this._hoverMap.keys());
307
this._hoverMap.delete(minimumId);
308
}
309
this._hoverMap.set(id, value);
310
this._hoverCounter += 1;
311
const hover: extHostProtocol.HoverWithId = {
312
...convertedHover,
313
id
314
};
315
return hover;
316
}
317
318
releaseHover(id: number): void {
319
this._hoverMap.delete(id);
320
}
321
}
322
323
class EvaluatableExpressionAdapter {
324
325
constructor(
326
private readonly _documents: ExtHostDocuments,
327
private readonly _provider: vscode.EvaluatableExpressionProvider,
328
) { }
329
330
async provideEvaluatableExpression(resource: URI, position: IPosition, token: CancellationToken): Promise<languages.EvaluatableExpression | undefined> {
331
332
const doc = this._documents.getDocument(resource);
333
const pos = typeConvert.Position.to(position);
334
335
const value = await this._provider.provideEvaluatableExpression(doc, pos, token);
336
if (value) {
337
return typeConvert.EvaluatableExpression.from(value);
338
}
339
return undefined;
340
}
341
}
342
343
class InlineValuesAdapter {
344
345
constructor(
346
private readonly _documents: ExtHostDocuments,
347
private readonly _provider: vscode.InlineValuesProvider,
348
) { }
349
350
async provideInlineValues(resource: URI, viewPort: IRange, context: extHostProtocol.IInlineValueContextDto, token: CancellationToken): Promise<languages.InlineValue[] | undefined> {
351
const doc = this._documents.getDocument(resource);
352
const value = await this._provider.provideInlineValues(doc, typeConvert.Range.to(viewPort), typeConvert.InlineValueContext.to(context), token);
353
if (Array.isArray(value)) {
354
return value.map(iv => typeConvert.InlineValue.from(iv));
355
}
356
return undefined;
357
}
358
}
359
360
class DocumentHighlightAdapter {
361
362
constructor(
363
private readonly _documents: ExtHostDocuments,
364
private readonly _provider: vscode.DocumentHighlightProvider
365
) { }
366
367
async provideDocumentHighlights(resource: URI, position: IPosition, token: CancellationToken): Promise<languages.DocumentHighlight[] | undefined> {
368
369
const doc = this._documents.getDocument(resource);
370
const pos = typeConvert.Position.to(position);
371
372
const value = await this._provider.provideDocumentHighlights(doc, pos, token);
373
if (Array.isArray(value)) {
374
return value.map(typeConvert.DocumentHighlight.from);
375
}
376
return undefined;
377
}
378
}
379
380
class MultiDocumentHighlightAdapter {
381
382
constructor(
383
private readonly _documents: ExtHostDocuments,
384
private readonly _provider: vscode.MultiDocumentHighlightProvider,
385
private readonly _logService: ILogService,
386
) { }
387
388
async provideMultiDocumentHighlights(resource: URI, position: IPosition, otherResources: URI[], token: CancellationToken): Promise<languages.MultiDocumentHighlight[] | undefined> {
389
const doc = this._documents.getDocument(resource);
390
const otherDocuments = otherResources.map(r => {
391
try {
392
return this._documents.getDocument(r);
393
} catch (err) {
394
this._logService.error('Error: Unable to retrieve document from URI: ' + r + '. Error message: ' + err);
395
return undefined;
396
}
397
}).filter(doc => doc !== undefined);
398
399
const pos = typeConvert.Position.to(position);
400
401
const value = await this._provider.provideMultiDocumentHighlights(doc, pos, otherDocuments, token);
402
if (Array.isArray(value)) {
403
return value.map(typeConvert.MultiDocumentHighlight.from);
404
}
405
return undefined;
406
}
407
}
408
409
class LinkedEditingRangeAdapter {
410
constructor(
411
private readonly _documents: ExtHostDocuments,
412
private readonly _provider: vscode.LinkedEditingRangeProvider
413
) { }
414
415
async provideLinkedEditingRanges(resource: URI, position: IPosition, token: CancellationToken): Promise<languages.LinkedEditingRanges | undefined> {
416
417
const doc = this._documents.getDocument(resource);
418
const pos = typeConvert.Position.to(position);
419
420
const value = await this._provider.provideLinkedEditingRanges(doc, pos, token);
421
if (value && Array.isArray(value.ranges)) {
422
return {
423
ranges: coalesce(value.ranges.map(typeConvert.Range.from)),
424
wordPattern: value.wordPattern
425
};
426
}
427
return undefined;
428
}
429
}
430
431
class ReferenceAdapter {
432
433
constructor(
434
private readonly _documents: ExtHostDocuments,
435
private readonly _provider: vscode.ReferenceProvider
436
) { }
437
438
async provideReferences(resource: URI, position: IPosition, context: languages.ReferenceContext, token: CancellationToken): Promise<languages.Location[] | undefined> {
439
const doc = this._documents.getDocument(resource);
440
const pos = typeConvert.Position.to(position);
441
442
const value = await this._provider.provideReferences(doc, pos, context, token);
443
if (Array.isArray(value)) {
444
return value.map(typeConvert.location.from);
445
}
446
return undefined;
447
}
448
}
449
450
export interface CustomCodeAction extends extHostProtocol.ICodeActionDto {
451
_isSynthetic?: boolean;
452
}
453
454
class CodeActionAdapter {
455
private static readonly _maxCodeActionsPerFile: number = 1000;
456
457
private readonly _cache = new Cache<vscode.CodeAction | vscode.Command>('CodeAction');
458
private readonly _disposables = new Map<number, DisposableStore>();
459
460
constructor(
461
private readonly _documents: ExtHostDocuments,
462
private readonly _commands: CommandsConverter,
463
private readonly _diagnostics: ExtHostDiagnostics,
464
private readonly _provider: vscode.CodeActionProvider,
465
private readonly _logService: ILogService,
466
private readonly _extension: IExtensionDescription,
467
private readonly _apiDeprecation: IExtHostApiDeprecationService,
468
) { }
469
470
async provideCodeActions(resource: URI, rangeOrSelection: IRange | ISelection, context: languages.CodeActionContext, token: CancellationToken): Promise<extHostProtocol.ICodeActionListDto | undefined> {
471
472
const doc = this._documents.getDocument(resource);
473
const ran = Selection.isISelection(rangeOrSelection)
474
? <vscode.Selection>typeConvert.Selection.to(rangeOrSelection)
475
: <vscode.Range>typeConvert.Range.to(rangeOrSelection);
476
const allDiagnostics: vscode.Diagnostic[] = [];
477
478
for (const diagnostic of this._diagnostics.getDiagnostics(resource)) {
479
if (ran.intersection(diagnostic.range)) {
480
const newLen = allDiagnostics.push(diagnostic);
481
if (newLen > CodeActionAdapter._maxCodeActionsPerFile) {
482
break;
483
}
484
}
485
}
486
487
const codeActionContext: vscode.CodeActionContext = {
488
diagnostics: allDiagnostics,
489
only: context.only ? new CodeActionKind(context.only) : undefined,
490
triggerKind: typeConvert.CodeActionTriggerKind.to(context.trigger),
491
};
492
493
const commandsOrActions = await this._provider.provideCodeActions(doc, ran, codeActionContext, token);
494
if (!isNonEmptyArray(commandsOrActions) || token.isCancellationRequested) {
495
return undefined;
496
}
497
498
const cacheId = this._cache.add(commandsOrActions);
499
const disposables = new DisposableStore();
500
this._disposables.set(cacheId, disposables);
501
const actions: CustomCodeAction[] = [];
502
for (let i = 0; i < commandsOrActions.length; i++) {
503
const candidate = commandsOrActions[i];
504
if (!candidate) {
505
continue;
506
}
507
508
if (CodeActionAdapter._isCommand(candidate) && !(candidate instanceof CodeAction)) {
509
// old school: synthetic code action
510
this._apiDeprecation.report('CodeActionProvider.provideCodeActions - return commands', this._extension,
511
`Return 'CodeAction' instances instead.`);
512
513
actions.push({
514
_isSynthetic: true,
515
title: candidate.title,
516
command: this._commands.toInternal(candidate, disposables),
517
});
518
} else {
519
const toConvert = candidate as vscode.CodeAction;
520
521
// new school: convert code action
522
if (codeActionContext.only) {
523
if (!toConvert.kind) {
524
this._logService.warn(`${this._extension.identifier.value} - Code actions of kind '${codeActionContext.only.value}' requested but returned code action does not have a 'kind'. Code action will be dropped. Please set 'CodeAction.kind'.`);
525
} else if (!codeActionContext.only.contains(toConvert.kind)) {
526
this._logService.warn(`${this._extension.identifier.value} - Code actions of kind '${codeActionContext.only.value}' requested but returned code action is of kind '${toConvert.kind.value}'. Code action will be dropped. Please check 'CodeActionContext.only' to only return requested code actions.`);
527
}
528
}
529
530
// Ensures that this is either a Range[] or an empty array so we don't get Array<Range | undefined>
531
const range = toConvert.ranges ?? [];
532
533
actions.push({
534
cacheId: [cacheId, i],
535
title: toConvert.title,
536
command: toConvert.command && this._commands.toInternal(toConvert.command, disposables),
537
diagnostics: toConvert.diagnostics && toConvert.diagnostics.map(typeConvert.Diagnostic.from),
538
edit: toConvert.edit && typeConvert.WorkspaceEdit.from(toConvert.edit, undefined),
539
kind: toConvert.kind && toConvert.kind.value,
540
isPreferred: toConvert.isPreferred,
541
isAI: isProposedApiEnabled(this._extension, 'codeActionAI') ? toConvert.isAI : false,
542
ranges: isProposedApiEnabled(this._extension, 'codeActionRanges') ? coalesce(range.map(typeConvert.Range.from)) : undefined,
543
disabled: toConvert.disabled?.reason
544
});
545
}
546
}
547
return { cacheId, actions };
548
}
549
550
async resolveCodeAction(id: extHostProtocol.ChainedCacheId, token: CancellationToken): Promise<{ edit?: extHostProtocol.IWorkspaceEditDto; command?: extHostProtocol.ICommandDto }> {
551
const [sessionId, itemId] = id;
552
const item = this._cache.get(sessionId, itemId);
553
if (!item || CodeActionAdapter._isCommand(item)) {
554
return {}; // code actions only!
555
}
556
if (!this._provider.resolveCodeAction) {
557
return {}; // this should not happen...
558
}
559
560
561
const resolvedItem = (await this._provider.resolveCodeAction(item, token)) ?? item;
562
563
let resolvedEdit: extHostProtocol.IWorkspaceEditDto | undefined;
564
if (resolvedItem.edit) {
565
resolvedEdit = typeConvert.WorkspaceEdit.from(resolvedItem.edit, undefined);
566
}
567
568
let resolvedCommand: extHostProtocol.ICommandDto | undefined;
569
if (resolvedItem.command) {
570
const disposables = this._disposables.get(sessionId);
571
if (disposables) {
572
resolvedCommand = this._commands.toInternal(resolvedItem.command, disposables);
573
}
574
}
575
576
return { edit: resolvedEdit, command: resolvedCommand };
577
}
578
579
releaseCodeActions(cachedId: number): void {
580
this._disposables.get(cachedId)?.dispose();
581
this._disposables.delete(cachedId);
582
this._cache.delete(cachedId);
583
}
584
585
private static _isCommand(thing: any): thing is vscode.Command {
586
return typeof (<vscode.Command>thing).command === 'string' && typeof (<vscode.Command>thing).title === 'string';
587
}
588
}
589
590
class DocumentPasteEditProvider {
591
592
private _cachedPrepare?: Map<string, vscode.DataTransferItem>;
593
594
private readonly _editsCache = new Cache<vscode.DocumentPasteEdit>('DocumentPasteEdit.edits');
595
596
constructor(
597
private readonly _proxy: extHostProtocol.MainThreadLanguageFeaturesShape,
598
private readonly _documents: ExtHostDocuments,
599
private readonly _provider: vscode.DocumentPasteEditProvider,
600
private readonly _handle: number,
601
private readonly _extension: IExtensionDescription,
602
) { }
603
604
async prepareDocumentPaste(resource: URI, ranges: IRange[], dataTransferDto: extHostProtocol.DataTransferDTO, token: CancellationToken): Promise<extHostProtocol.DataTransferDTO | undefined> {
605
if (!this._provider.prepareDocumentPaste) {
606
return;
607
}
608
609
this._cachedPrepare = undefined;
610
611
const doc = this._documents.getDocument(resource);
612
const vscodeRanges = ranges.map(range => typeConvert.Range.to(range));
613
614
const dataTransfer = typeConvert.DataTransfer.toDataTransfer(dataTransferDto, () => {
615
throw new NotImplementedError();
616
});
617
await this._provider.prepareDocumentPaste(doc, vscodeRanges, dataTransfer, token);
618
if (token.isCancellationRequested) {
619
return;
620
}
621
622
// Only send back values that have been added to the data transfer
623
const newEntries = Array.from(dataTransfer).filter(([, value]) => !(value instanceof InternalDataTransferItem));
624
625
// Store off original data transfer items so we can retrieve them on paste
626
const newCache = new Map<string, vscode.DataTransferItem>();
627
628
const items = await Promise.all(Array.from(newEntries, async ([mime, value]) => {
629
const id = generateUuid();
630
newCache.set(id, value);
631
return [mime, await typeConvert.DataTransferItem.from(mime, value, id)] as const;
632
}));
633
634
this._cachedPrepare = newCache;
635
636
return { items };
637
}
638
639
async providePasteEdits(requestId: number, resource: URI, ranges: IRange[], dataTransferDto: extHostProtocol.DataTransferDTO, context: extHostProtocol.IDocumentPasteContextDto, token: CancellationToken): Promise<extHostProtocol.IPasteEditDto[]> {
640
if (!this._provider.provideDocumentPasteEdits) {
641
return [];
642
}
643
644
const doc = this._documents.getDocument(resource);
645
const vscodeRanges = ranges.map(range => typeConvert.Range.to(range));
646
647
const items = dataTransferDto.items.map(([mime, value]): [string, vscode.DataTransferItem] => {
648
const cached = this._cachedPrepare?.get(value.id);
649
if (cached) {
650
return [mime, cached];
651
}
652
653
return [
654
mime,
655
typeConvert.DataTransferItem.to(mime, value, async id => {
656
return (await this._proxy.$resolvePasteFileData(this._handle, requestId, id)).buffer;
657
})
658
];
659
});
660
661
const dataTransfer = new DataTransfer(items);
662
663
const edits = await this._provider.provideDocumentPasteEdits(doc, vscodeRanges, dataTransfer, {
664
only: context.only ? new DocumentDropOrPasteEditKind(context.only) : undefined,
665
triggerKind: context.triggerKind,
666
}, token);
667
if (!edits || token.isCancellationRequested) {
668
return [];
669
}
670
671
const cacheId = this._editsCache.add(edits);
672
673
return edits.map((edit, i): extHostProtocol.IPasteEditDto => ({
674
_cacheId: [cacheId, i],
675
title: edit.title ?? localize('defaultPasteLabel', "Paste using '{0}' extension", this._extension.displayName || this._extension.name),
676
kind: edit.kind,
677
yieldTo: edit.yieldTo?.map(x => x.value),
678
insertText: typeof edit.insertText === 'string' ? edit.insertText : { snippet: edit.insertText.value },
679
additionalEdit: edit.additionalEdit ? typeConvert.WorkspaceEdit.from(edit.additionalEdit, undefined) : undefined,
680
}));
681
}
682
683
async resolvePasteEdit(id: extHostProtocol.ChainedCacheId, token: CancellationToken): Promise<{ insertText?: string | vscode.SnippetString; additionalEdit?: extHostProtocol.IWorkspaceEditDto }> {
684
const [sessionId, itemId] = id;
685
const item = this._editsCache.get(sessionId, itemId);
686
if (!item || !this._provider.resolveDocumentPasteEdit) {
687
return {}; // this should not happen...
688
}
689
690
const resolvedItem = (await this._provider.resolveDocumentPasteEdit(item, token)) ?? item;
691
return {
692
insertText: resolvedItem.insertText,
693
additionalEdit: resolvedItem.additionalEdit ? typeConvert.WorkspaceEdit.from(resolvedItem.additionalEdit, undefined) : undefined
694
};
695
}
696
697
releasePasteEdits(id: number): any {
698
this._editsCache.delete(id);
699
}
700
}
701
702
class DocumentFormattingAdapter {
703
704
constructor(
705
private readonly _documents: ExtHostDocuments,
706
private readonly _provider: vscode.DocumentFormattingEditProvider
707
) { }
708
709
async provideDocumentFormattingEdits(resource: URI, options: languages.FormattingOptions, token: CancellationToken): Promise<languages.TextEdit[] | undefined> {
710
711
const document = this._documents.getDocument(resource);
712
713
const value = await this._provider.provideDocumentFormattingEdits(document, <any>options, token);
714
if (Array.isArray(value)) {
715
return value.map(typeConvert.TextEdit.from);
716
}
717
return undefined;
718
}
719
}
720
721
class RangeFormattingAdapter {
722
723
constructor(
724
private readonly _documents: ExtHostDocuments,
725
private readonly _provider: vscode.DocumentRangeFormattingEditProvider
726
) { }
727
728
async provideDocumentRangeFormattingEdits(resource: URI, range: IRange, options: languages.FormattingOptions, token: CancellationToken): Promise<languages.TextEdit[] | undefined> {
729
730
const document = this._documents.getDocument(resource);
731
const ran = typeConvert.Range.to(range);
732
733
const value = await this._provider.provideDocumentRangeFormattingEdits(document, ran, <any>options, token);
734
if (Array.isArray(value)) {
735
return value.map(typeConvert.TextEdit.from);
736
}
737
return undefined;
738
}
739
740
async provideDocumentRangesFormattingEdits(resource: URI, ranges: IRange[], options: languages.FormattingOptions, token: CancellationToken): Promise<languages.TextEdit[] | undefined> {
741
assertType(typeof this._provider.provideDocumentRangesFormattingEdits === 'function', 'INVALID invocation of `provideDocumentRangesFormattingEdits`');
742
743
const document = this._documents.getDocument(resource);
744
const _ranges = <Range[]>ranges.map(typeConvert.Range.to);
745
const value = await this._provider.provideDocumentRangesFormattingEdits(document, _ranges, <any>options, token);
746
if (Array.isArray(value)) {
747
return value.map(typeConvert.TextEdit.from);
748
}
749
return undefined;
750
}
751
}
752
753
class OnTypeFormattingAdapter {
754
755
constructor(
756
private readonly _documents: ExtHostDocuments,
757
private readonly _provider: vscode.OnTypeFormattingEditProvider
758
) { }
759
760
autoFormatTriggerCharacters: string[] = []; // not here
761
762
async provideOnTypeFormattingEdits(resource: URI, position: IPosition, ch: string, options: languages.FormattingOptions, token: CancellationToken): Promise<languages.TextEdit[] | undefined> {
763
764
const document = this._documents.getDocument(resource);
765
const pos = typeConvert.Position.to(position);
766
767
const value = await this._provider.provideOnTypeFormattingEdits(document, pos, ch, <any>options, token);
768
if (Array.isArray(value)) {
769
return value.map(typeConvert.TextEdit.from);
770
}
771
return undefined;
772
}
773
}
774
775
class NavigateTypeAdapter {
776
777
private readonly _cache = new Cache<vscode.SymbolInformation>('WorkspaceSymbols');
778
779
constructor(
780
private readonly _provider: vscode.WorkspaceSymbolProvider,
781
private readonly _logService: ILogService
782
) { }
783
784
async provideWorkspaceSymbols(search: string, token: CancellationToken): Promise<extHostProtocol.IWorkspaceSymbolsDto> {
785
const value = await this._provider.provideWorkspaceSymbols(search, token);
786
787
if (!isNonEmptyArray(value)) {
788
return { symbols: [] };
789
}
790
791
const sid = this._cache.add(value);
792
const result: extHostProtocol.IWorkspaceSymbolsDto = {
793
cacheId: sid,
794
symbols: []
795
};
796
797
for (let i = 0; i < value.length; i++) {
798
const item = value[i];
799
if (!item || !item.name) {
800
this._logService.warn('INVALID SymbolInformation', item);
801
continue;
802
}
803
result.symbols.push({
804
...typeConvert.WorkspaceSymbol.from(item),
805
cacheId: [sid, i]
806
});
807
}
808
809
return result;
810
}
811
812
async resolveWorkspaceSymbol(symbol: extHostProtocol.IWorkspaceSymbolDto, token: CancellationToken): Promise<extHostProtocol.IWorkspaceSymbolDto | undefined> {
813
if (typeof this._provider.resolveWorkspaceSymbol !== 'function') {
814
return symbol;
815
}
816
if (!symbol.cacheId) {
817
return symbol;
818
}
819
const item = this._cache.get(...symbol.cacheId);
820
if (item) {
821
const value = await this._provider.resolveWorkspaceSymbol(item, token);
822
return value && mixin(symbol, typeConvert.WorkspaceSymbol.from(value), true);
823
}
824
return undefined;
825
}
826
827
releaseWorkspaceSymbols(id: number): any {
828
this._cache.delete(id);
829
}
830
}
831
832
class RenameAdapter {
833
834
static supportsResolving(provider: vscode.RenameProvider): boolean {
835
return typeof provider.prepareRename === 'function';
836
}
837
838
constructor(
839
private readonly _documents: ExtHostDocuments,
840
private readonly _provider: vscode.RenameProvider,
841
private readonly _logService: ILogService
842
) { }
843
844
async provideRenameEdits(resource: URI, position: IPosition, newName: string, token: CancellationToken): Promise<extHostProtocol.IWorkspaceEditDto & languages.Rejection | undefined> {
845
846
const doc = this._documents.getDocument(resource);
847
const pos = typeConvert.Position.to(position);
848
849
try {
850
const value = await this._provider.provideRenameEdits(doc, pos, newName, token);
851
if (!value) {
852
return undefined;
853
}
854
return typeConvert.WorkspaceEdit.from(value);
855
856
} catch (err) {
857
const rejectReason = RenameAdapter._asMessage(err);
858
if (rejectReason) {
859
return { rejectReason, edits: undefined! };
860
} else {
861
// generic error
862
return Promise.reject<extHostProtocol.IWorkspaceEditDto>(err);
863
}
864
}
865
}
866
867
async resolveRenameLocation(resource: URI, position: IPosition, token: CancellationToken): Promise<(languages.RenameLocation & languages.Rejection) | undefined> {
868
if (typeof this._provider.prepareRename !== 'function') {
869
return Promise.resolve(undefined);
870
}
871
872
const doc = this._documents.getDocument(resource);
873
const pos = typeConvert.Position.to(position);
874
875
try {
876
const rangeOrLocation = await this._provider.prepareRename(doc, pos, token);
877
878
let range: vscode.Range | undefined;
879
let text: string | undefined;
880
if (Range.isRange(rangeOrLocation)) {
881
range = rangeOrLocation;
882
text = doc.getText(rangeOrLocation);
883
884
} else if (isObject(rangeOrLocation)) {
885
range = rangeOrLocation.range;
886
text = rangeOrLocation.placeholder;
887
}
888
889
if (!range || !text) {
890
return undefined;
891
}
892
if (range.start.line > pos.line || range.end.line < pos.line) {
893
this._logService.warn('INVALID rename location: position line must be within range start/end lines');
894
return undefined;
895
}
896
return { range: typeConvert.Range.from(range), text };
897
898
} catch (err) {
899
const rejectReason = RenameAdapter._asMessage(err);
900
if (rejectReason) {
901
return { rejectReason, range: undefined!, text: undefined! };
902
} else {
903
return Promise.reject<any>(err);
904
}
905
}
906
}
907
908
private static _asMessage(err: any): string | undefined {
909
if (typeof err === 'string') {
910
return err;
911
} else if (err instanceof Error && typeof err.message === 'string') {
912
return err.message;
913
} else {
914
return undefined;
915
}
916
}
917
}
918
919
class NewSymbolNamesAdapter {
920
921
private static languageTriggerKindToVSCodeTriggerKind: Record<languages.NewSymbolNameTriggerKind, vscode.NewSymbolNameTriggerKind> = {
922
[languages.NewSymbolNameTriggerKind.Invoke]: NewSymbolNameTriggerKind.Invoke,
923
[languages.NewSymbolNameTriggerKind.Automatic]: NewSymbolNameTriggerKind.Automatic,
924
};
925
926
constructor(
927
private readonly _documents: ExtHostDocuments,
928
private readonly _provider: vscode.NewSymbolNamesProvider,
929
private readonly _logService: ILogService
930
) { }
931
932
async supportsAutomaticNewSymbolNamesTriggerKind() {
933
return this._provider.supportsAutomaticTriggerKind;
934
}
935
936
async provideNewSymbolNames(resource: URI, range: IRange, triggerKind: languages.NewSymbolNameTriggerKind, token: CancellationToken): Promise<languages.NewSymbolName[] | undefined> {
937
938
const doc = this._documents.getDocument(resource);
939
const pos = typeConvert.Range.to(range);
940
941
try {
942
const kind = NewSymbolNamesAdapter.languageTriggerKindToVSCodeTriggerKind[triggerKind];
943
const value = await this._provider.provideNewSymbolNames(doc, pos, kind, token);
944
if (!value) {
945
return undefined;
946
}
947
return value.map(v =>
948
typeof v === 'string' /* @ulugbekna: for backward compatibility because `value` used to be just `string[]` */
949
? { newSymbolName: v }
950
: { newSymbolName: v.newSymbolName, tags: v.tags }
951
);
952
} catch (err: unknown) {
953
this._logService.error(NewSymbolNamesAdapter._asMessage(err) ?? JSON.stringify(err, null, '\t') /* @ulugbekna: assuming `err` doesn't have circular references that could result in an exception when converting to JSON */);
954
return undefined;
955
}
956
}
957
958
// @ulugbekna: this method is also defined in RenameAdapter but seems OK to be duplicated
959
private static _asMessage(err: any): string | undefined {
960
if (typeof err === 'string') {
961
return err;
962
} else if (err instanceof Error && typeof err.message === 'string') {
963
return err.message;
964
} else {
965
return undefined;
966
}
967
}
968
}
969
970
class SemanticTokensPreviousResult {
971
constructor(
972
readonly resultId: string | undefined,
973
readonly tokens?: Uint32Array,
974
) { }
975
}
976
977
type RelaxedSemanticTokens = { readonly resultId?: string; readonly data: number[] };
978
type RelaxedSemanticTokensEdit = { readonly start: number; readonly deleteCount: number; readonly data?: number[] };
979
type RelaxedSemanticTokensEdits = { readonly resultId?: string; readonly edits: RelaxedSemanticTokensEdit[] };
980
981
type ProvidedSemanticTokens = vscode.SemanticTokens | RelaxedSemanticTokens;
982
type ProvidedSemanticTokensEdits = vscode.SemanticTokensEdits | RelaxedSemanticTokensEdits;
983
984
class DocumentSemanticTokensAdapter {
985
986
private readonly _previousResults: Map<number, SemanticTokensPreviousResult>;
987
private _nextResultId = 1;
988
989
constructor(
990
private readonly _documents: ExtHostDocuments,
991
private readonly _provider: vscode.DocumentSemanticTokensProvider,
992
) {
993
this._previousResults = new Map<number, SemanticTokensPreviousResult>();
994
}
995
996
async provideDocumentSemanticTokens(resource: URI, previousResultId: number, token: CancellationToken): Promise<VSBuffer | null> {
997
const doc = this._documents.getDocument(resource);
998
const previousResult = (previousResultId !== 0 ? this._previousResults.get(previousResultId) : null);
999
let value = typeof previousResult?.resultId === 'string' && typeof this._provider.provideDocumentSemanticTokensEdits === 'function'
1000
? await this._provider.provideDocumentSemanticTokensEdits(doc, previousResult.resultId, token)
1001
: await this._provider.provideDocumentSemanticTokens(doc, token);
1002
1003
if (previousResult) {
1004
this._previousResults.delete(previousResultId);
1005
}
1006
if (!value) {
1007
return null;
1008
}
1009
value = DocumentSemanticTokensAdapter._fixProvidedSemanticTokens(value);
1010
return this._send(DocumentSemanticTokensAdapter._convertToEdits(previousResult, value), value);
1011
}
1012
1013
async releaseDocumentSemanticColoring(semanticColoringResultId: number): Promise<void> {
1014
this._previousResults.delete(semanticColoringResultId);
1015
}
1016
1017
private static _fixProvidedSemanticTokens(v: ProvidedSemanticTokens | ProvidedSemanticTokensEdits): vscode.SemanticTokens | vscode.SemanticTokensEdits {
1018
if (DocumentSemanticTokensAdapter._isSemanticTokens(v)) {
1019
if (DocumentSemanticTokensAdapter._isCorrectSemanticTokens(v)) {
1020
return v;
1021
}
1022
return new SemanticTokens(new Uint32Array(v.data), v.resultId);
1023
} else if (DocumentSemanticTokensAdapter._isSemanticTokensEdits(v)) {
1024
if (DocumentSemanticTokensAdapter._isCorrectSemanticTokensEdits(v)) {
1025
return v;
1026
}
1027
return new SemanticTokensEdits(v.edits.map(edit => new SemanticTokensEdit(edit.start, edit.deleteCount, edit.data ? new Uint32Array(edit.data) : edit.data)), v.resultId);
1028
}
1029
return v;
1030
}
1031
1032
private static _isSemanticTokens(v: ProvidedSemanticTokens | ProvidedSemanticTokensEdits): v is ProvidedSemanticTokens {
1033
return v && !!((v as ProvidedSemanticTokens).data);
1034
}
1035
1036
private static _isCorrectSemanticTokens(v: ProvidedSemanticTokens): v is vscode.SemanticTokens {
1037
return (v.data instanceof Uint32Array);
1038
}
1039
1040
private static _isSemanticTokensEdits(v: ProvidedSemanticTokens | ProvidedSemanticTokensEdits): v is ProvidedSemanticTokensEdits {
1041
return v && Array.isArray((v as ProvidedSemanticTokensEdits).edits);
1042
}
1043
1044
private static _isCorrectSemanticTokensEdits(v: ProvidedSemanticTokensEdits): v is vscode.SemanticTokensEdits {
1045
for (const edit of v.edits) {
1046
if (!(edit.data instanceof Uint32Array)) {
1047
return false;
1048
}
1049
}
1050
return true;
1051
}
1052
1053
private static _convertToEdits(previousResult: SemanticTokensPreviousResult | null | undefined, newResult: vscode.SemanticTokens | vscode.SemanticTokensEdits): vscode.SemanticTokens | vscode.SemanticTokensEdits {
1054
if (!DocumentSemanticTokensAdapter._isSemanticTokens(newResult)) {
1055
return newResult;
1056
}
1057
if (!previousResult || !previousResult.tokens) {
1058
return newResult;
1059
}
1060
const oldData = previousResult.tokens;
1061
const oldLength = oldData.length;
1062
const newData = newResult.data;
1063
const newLength = newData.length;
1064
1065
let commonPrefixLength = 0;
1066
const maxCommonPrefixLength = Math.min(oldLength, newLength);
1067
while (commonPrefixLength < maxCommonPrefixLength && oldData[commonPrefixLength] === newData[commonPrefixLength]) {
1068
commonPrefixLength++;
1069
}
1070
1071
if (commonPrefixLength === oldLength && commonPrefixLength === newLength) {
1072
// complete overlap!
1073
return new SemanticTokensEdits([], newResult.resultId);
1074
}
1075
1076
let commonSuffixLength = 0;
1077
const maxCommonSuffixLength = maxCommonPrefixLength - commonPrefixLength;
1078
while (commonSuffixLength < maxCommonSuffixLength && oldData[oldLength - commonSuffixLength - 1] === newData[newLength - commonSuffixLength - 1]) {
1079
commonSuffixLength++;
1080
}
1081
1082
return new SemanticTokensEdits([{
1083
start: commonPrefixLength,
1084
deleteCount: (oldLength - commonPrefixLength - commonSuffixLength),
1085
data: newData.subarray(commonPrefixLength, newLength - commonSuffixLength)
1086
}], newResult.resultId);
1087
}
1088
1089
private _send(value: vscode.SemanticTokens | vscode.SemanticTokensEdits, original: vscode.SemanticTokens | vscode.SemanticTokensEdits): VSBuffer | null {
1090
if (DocumentSemanticTokensAdapter._isSemanticTokens(value)) {
1091
const myId = this._nextResultId++;
1092
this._previousResults.set(myId, new SemanticTokensPreviousResult(value.resultId, value.data));
1093
return encodeSemanticTokensDto({
1094
id: myId,
1095
type: 'full',
1096
data: value.data
1097
});
1098
}
1099
1100
if (DocumentSemanticTokensAdapter._isSemanticTokensEdits(value)) {
1101
const myId = this._nextResultId++;
1102
if (DocumentSemanticTokensAdapter._isSemanticTokens(original)) {
1103
// store the original
1104
this._previousResults.set(myId, new SemanticTokensPreviousResult(original.resultId, original.data));
1105
} else {
1106
this._previousResults.set(myId, new SemanticTokensPreviousResult(value.resultId));
1107
}
1108
return encodeSemanticTokensDto({
1109
id: myId,
1110
type: 'delta',
1111
deltas: (value.edits || []).map(edit => ({ start: edit.start, deleteCount: edit.deleteCount, data: edit.data }))
1112
});
1113
}
1114
1115
return null;
1116
}
1117
}
1118
1119
class DocumentRangeSemanticTokensAdapter {
1120
1121
constructor(
1122
private readonly _documents: ExtHostDocuments,
1123
private readonly _provider: vscode.DocumentRangeSemanticTokensProvider,
1124
) { }
1125
1126
async provideDocumentRangeSemanticTokens(resource: URI, range: IRange, token: CancellationToken): Promise<VSBuffer | null> {
1127
const doc = this._documents.getDocument(resource);
1128
const value = await this._provider.provideDocumentRangeSemanticTokens(doc, typeConvert.Range.to(range), token);
1129
if (!value) {
1130
return null;
1131
}
1132
return this._send(value);
1133
}
1134
1135
private _send(value: vscode.SemanticTokens): VSBuffer {
1136
return encodeSemanticTokensDto({
1137
id: 0,
1138
type: 'full',
1139
data: value.data
1140
});
1141
}
1142
}
1143
1144
class CompletionsAdapter {
1145
1146
static supportsResolving(provider: vscode.CompletionItemProvider): boolean {
1147
return typeof provider.resolveCompletionItem === 'function';
1148
}
1149
1150
private _cache = new Cache<vscode.CompletionItem>('CompletionItem');
1151
private _disposables = new Map<number, DisposableStore>();
1152
1153
constructor(
1154
private readonly _documents: ExtHostDocuments,
1155
private readonly _commands: CommandsConverter,
1156
private readonly _provider: vscode.CompletionItemProvider,
1157
private readonly _apiDeprecation: IExtHostApiDeprecationService,
1158
private readonly _extension: IExtensionDescription,
1159
) { }
1160
1161
async provideCompletionItems(resource: URI, position: IPosition, context: languages.CompletionContext, token: CancellationToken): Promise<extHostProtocol.ISuggestResultDto | undefined> {
1162
1163
const doc = this._documents.getDocument(resource);
1164
const pos = typeConvert.Position.to(position);
1165
1166
// The default insert/replace ranges. It's important to compute them
1167
// before asynchronously asking the provider for its results. See
1168
// https://github.com/microsoft/vscode/issues/83400#issuecomment-546851421
1169
const replaceRange = doc.getWordRangeAtPosition(pos) || new Range(pos, pos);
1170
const insertRange = replaceRange.with({ end: pos });
1171
1172
const sw = new StopWatch();
1173
const itemsOrList = await this._provider.provideCompletionItems(doc, pos, token, typeConvert.CompletionContext.to(context));
1174
1175
if (!itemsOrList) {
1176
// undefined and null are valid results
1177
return undefined;
1178
}
1179
1180
if (token.isCancellationRequested) {
1181
// cancelled -> return without further ado, esp no caching
1182
// of results as they will leak
1183
return undefined;
1184
}
1185
1186
const list = Array.isArray(itemsOrList) ? new CompletionList(itemsOrList) : itemsOrList;
1187
1188
// keep result for providers that support resolving
1189
const pid: number = CompletionsAdapter.supportsResolving(this._provider) ? this._cache.add(list.items) : this._cache.add([]);
1190
const disposables = new DisposableStore();
1191
this._disposables.set(pid, disposables);
1192
1193
const completions: extHostProtocol.ISuggestDataDto[] = [];
1194
const result: extHostProtocol.ISuggestResultDto = {
1195
x: pid,
1196
[extHostProtocol.ISuggestResultDtoField.completions]: completions,
1197
[extHostProtocol.ISuggestResultDtoField.defaultRanges]: { replace: typeConvert.Range.from(replaceRange), insert: typeConvert.Range.from(insertRange) },
1198
[extHostProtocol.ISuggestResultDtoField.isIncomplete]: list.isIncomplete || undefined,
1199
[extHostProtocol.ISuggestResultDtoField.duration]: sw.elapsed()
1200
};
1201
1202
for (let i = 0; i < list.items.length; i++) {
1203
const item = list.items[i];
1204
// check for bad completion item first
1205
const dto = this._convertCompletionItem(item, [pid, i], insertRange, replaceRange);
1206
completions.push(dto);
1207
}
1208
1209
return result;
1210
}
1211
1212
async resolveCompletionItem(id: extHostProtocol.ChainedCacheId, token: CancellationToken): Promise<extHostProtocol.ISuggestDataDto | undefined> {
1213
1214
if (typeof this._provider.resolveCompletionItem !== 'function') {
1215
return undefined;
1216
}
1217
1218
const item = this._cache.get(...id);
1219
if (!item) {
1220
return undefined;
1221
}
1222
1223
const dto1 = this._convertCompletionItem(item, id);
1224
1225
const resolvedItem = await this._provider.resolveCompletionItem(item, token);
1226
1227
if (!resolvedItem) {
1228
return undefined;
1229
}
1230
1231
const dto2 = this._convertCompletionItem(resolvedItem, id);
1232
1233
if (dto1[extHostProtocol.ISuggestDataDtoField.insertText] !== dto2[extHostProtocol.ISuggestDataDtoField.insertText]
1234
|| dto1[extHostProtocol.ISuggestDataDtoField.insertTextRules] !== dto2[extHostProtocol.ISuggestDataDtoField.insertTextRules]
1235
) {
1236
this._apiDeprecation.report('CompletionItem.insertText', this._extension, 'extension MAY NOT change \'insertText\' of a CompletionItem during resolve');
1237
}
1238
1239
if (dto1[extHostProtocol.ISuggestDataDtoField.commandIdent] !== dto2[extHostProtocol.ISuggestDataDtoField.commandIdent]
1240
|| dto1[extHostProtocol.ISuggestDataDtoField.commandId] !== dto2[extHostProtocol.ISuggestDataDtoField.commandId]
1241
|| !equals(dto1[extHostProtocol.ISuggestDataDtoField.commandArguments], dto2[extHostProtocol.ISuggestDataDtoField.commandArguments])
1242
) {
1243
this._apiDeprecation.report('CompletionItem.command', this._extension, 'extension MAY NOT change \'command\' of a CompletionItem during resolve');
1244
}
1245
1246
return {
1247
...dto1,
1248
[extHostProtocol.ISuggestDataDtoField.documentation]: dto2[extHostProtocol.ISuggestDataDtoField.documentation],
1249
[extHostProtocol.ISuggestDataDtoField.detail]: dto2[extHostProtocol.ISuggestDataDtoField.detail],
1250
[extHostProtocol.ISuggestDataDtoField.additionalTextEdits]: dto2[extHostProtocol.ISuggestDataDtoField.additionalTextEdits],
1251
1252
// (fishy) async insertText
1253
[extHostProtocol.ISuggestDataDtoField.insertText]: dto2[extHostProtocol.ISuggestDataDtoField.insertText],
1254
[extHostProtocol.ISuggestDataDtoField.insertTextRules]: dto2[extHostProtocol.ISuggestDataDtoField.insertTextRules],
1255
1256
// (fishy) async command
1257
[extHostProtocol.ISuggestDataDtoField.commandIdent]: dto2[extHostProtocol.ISuggestDataDtoField.commandIdent],
1258
[extHostProtocol.ISuggestDataDtoField.commandId]: dto2[extHostProtocol.ISuggestDataDtoField.commandId],
1259
[extHostProtocol.ISuggestDataDtoField.commandArguments]: dto2[extHostProtocol.ISuggestDataDtoField.commandArguments],
1260
};
1261
}
1262
1263
releaseCompletionItems(id: number): any {
1264
this._disposables.get(id)?.dispose();
1265
this._disposables.delete(id);
1266
this._cache.delete(id);
1267
}
1268
1269
private _convertCompletionItem(item: vscode.CompletionItem, id: extHostProtocol.ChainedCacheId, defaultInsertRange?: vscode.Range, defaultReplaceRange?: vscode.Range): extHostProtocol.ISuggestDataDto {
1270
1271
const disposables = this._disposables.get(id[0]);
1272
if (!disposables) {
1273
throw Error('DisposableStore is missing...');
1274
}
1275
1276
const command = this._commands.toInternal(item.command, disposables);
1277
const result: extHostProtocol.ISuggestDataDto = {
1278
//
1279
x: id,
1280
//
1281
[extHostProtocol.ISuggestDataDtoField.label]: item.label,
1282
[extHostProtocol.ISuggestDataDtoField.kind]: item.kind !== undefined ? typeConvert.CompletionItemKind.from(item.kind) : undefined,
1283
[extHostProtocol.ISuggestDataDtoField.kindModifier]: item.tags && item.tags.map(typeConvert.CompletionItemTag.from),
1284
[extHostProtocol.ISuggestDataDtoField.detail]: item.detail,
1285
[extHostProtocol.ISuggestDataDtoField.documentation]: typeof item.documentation === 'undefined' ? undefined : typeConvert.MarkdownString.fromStrict(item.documentation),
1286
[extHostProtocol.ISuggestDataDtoField.sortText]: item.sortText !== item.label ? item.sortText : undefined,
1287
[extHostProtocol.ISuggestDataDtoField.filterText]: item.filterText !== item.label ? item.filterText : undefined,
1288
[extHostProtocol.ISuggestDataDtoField.preselect]: item.preselect || undefined,
1289
[extHostProtocol.ISuggestDataDtoField.insertTextRules]: item.keepWhitespace ? languages.CompletionItemInsertTextRule.KeepWhitespace : languages.CompletionItemInsertTextRule.None,
1290
[extHostProtocol.ISuggestDataDtoField.commitCharacters]: item.commitCharacters?.join(''),
1291
[extHostProtocol.ISuggestDataDtoField.additionalTextEdits]: item.additionalTextEdits && item.additionalTextEdits.map(typeConvert.TextEdit.from),
1292
[extHostProtocol.ISuggestDataDtoField.commandIdent]: command?.$ident,
1293
[extHostProtocol.ISuggestDataDtoField.commandId]: command?.id,
1294
[extHostProtocol.ISuggestDataDtoField.commandArguments]: command?.$ident ? undefined : command?.arguments, // filled in on main side from $ident
1295
};
1296
1297
// 'insertText'-logic
1298
if (item.textEdit) {
1299
this._apiDeprecation.report('CompletionItem.textEdit', this._extension, `Use 'CompletionItem.insertText' and 'CompletionItem.range' instead.`);
1300
result[extHostProtocol.ISuggestDataDtoField.insertText] = item.textEdit.newText;
1301
1302
} else if (typeof item.insertText === 'string') {
1303
result[extHostProtocol.ISuggestDataDtoField.insertText] = item.insertText;
1304
1305
} else if (item.insertText instanceof SnippetString) {
1306
result[extHostProtocol.ISuggestDataDtoField.insertText] = item.insertText.value;
1307
result[extHostProtocol.ISuggestDataDtoField.insertTextRules]! |= languages.CompletionItemInsertTextRule.InsertAsSnippet;
1308
}
1309
1310
// 'overwrite[Before|After]'-logic
1311
let range: vscode.Range | { inserting: vscode.Range; replacing: vscode.Range } | undefined;
1312
if (item.textEdit) {
1313
range = item.textEdit.range;
1314
} else if (item.range) {
1315
range = item.range;
1316
}
1317
1318
if (Range.isRange(range)) {
1319
// "old" range
1320
result[extHostProtocol.ISuggestDataDtoField.range] = typeConvert.Range.from(range);
1321
1322
} else if (range && (!defaultInsertRange?.isEqual(range.inserting) || !defaultReplaceRange?.isEqual(range.replacing))) {
1323
// ONLY send range when it's different from the default ranges (safe bandwidth)
1324
result[extHostProtocol.ISuggestDataDtoField.range] = {
1325
insert: typeConvert.Range.from(range.inserting),
1326
replace: typeConvert.Range.from(range.replacing)
1327
};
1328
}
1329
1330
return result;
1331
}
1332
}
1333
1334
class InlineCompletionAdapter {
1335
private readonly _references = new ReferenceMap<{
1336
dispose(): void;
1337
items: readonly vscode.InlineCompletionItem[];
1338
list: vscode.InlineCompletionList | undefined;
1339
}>();
1340
1341
private readonly _isAdditionsProposedApiEnabled: boolean;
1342
1343
constructor(
1344
private readonly _extension: IExtensionDescription,
1345
private readonly _documents: ExtHostDocuments,
1346
private readonly _provider: vscode.InlineCompletionItemProvider,
1347
private readonly _commands: CommandsConverter,
1348
) {
1349
this._isAdditionsProposedApiEnabled = isProposedApiEnabled(this._extension, 'inlineCompletionsAdditions');
1350
}
1351
1352
public get supportsHandleEvents(): boolean {
1353
return isProposedApiEnabled(this._extension, 'inlineCompletionsAdditions')
1354
&& (typeof this._provider.handleDidShowCompletionItem === 'function'
1355
|| typeof this._provider.handleDidPartiallyAcceptCompletionItem === 'function'
1356
|| typeof this._provider.handleDidRejectCompletionItem === 'function'
1357
|| typeof this._provider.handleEndOfLifetime === 'function'
1358
);
1359
}
1360
1361
private readonly languageTriggerKindToVSCodeTriggerKind: Record<languages.InlineCompletionTriggerKind, InlineCompletionTriggerKind> = {
1362
[languages.InlineCompletionTriggerKind.Automatic]: InlineCompletionTriggerKind.Automatic,
1363
[languages.InlineCompletionTriggerKind.Explicit]: InlineCompletionTriggerKind.Invoke,
1364
};
1365
1366
async provideInlineCompletions(resource: URI, position: IPosition, context: languages.InlineCompletionContext, token: CancellationToken): Promise<extHostProtocol.IdentifiableInlineCompletions | undefined> {
1367
const doc = this._documents.getDocument(resource);
1368
const pos = typeConvert.Position.to(position);
1369
1370
const result = await this._provider.provideInlineCompletionItems(doc, pos, {
1371
selectedCompletionInfo:
1372
context.selectedSuggestionInfo
1373
? {
1374
range: typeConvert.Range.to(context.selectedSuggestionInfo.range),
1375
text: context.selectedSuggestionInfo.text
1376
}
1377
: undefined,
1378
triggerKind: this.languageTriggerKindToVSCodeTriggerKind[context.triggerKind],
1379
requestUuid: context.requestUuid,
1380
requestIssuedDateTime: context.requestIssuedDateTime,
1381
earliestShownDateTime: context.earliestShownDateTime,
1382
}, token);
1383
1384
if (!result) {
1385
// undefined and null are valid results
1386
return undefined;
1387
}
1388
1389
const { resultItems, list } = Array.isArray(result) ? { resultItems: result, list: undefined } : { resultItems: result.items, list: result };
1390
const commands = this._isAdditionsProposedApiEnabled ? Array.isArray(result) ? [] : result.commands || [] : [];
1391
const enableForwardStability = this._isAdditionsProposedApiEnabled && !Array.isArray(result) ? result.enableForwardStability : undefined;
1392
1393
let disposableStore: DisposableStore | undefined = undefined;
1394
const pid = this._references.createReferenceId({
1395
dispose() {
1396
disposableStore?.dispose();
1397
},
1398
items: resultItems,
1399
list,
1400
});
1401
1402
return {
1403
pid,
1404
languageId: doc.languageId,
1405
items: resultItems.map<extHostProtocol.IdentifiableInlineCompletion>((item, idx) => {
1406
let command: languages.Command | undefined = undefined;
1407
if (item.command) {
1408
if (!disposableStore) {
1409
disposableStore = new DisposableStore();
1410
}
1411
command = this._commands.toInternal(item.command, disposableStore);
1412
}
1413
1414
let action: languages.Command | undefined = undefined;
1415
if (item.action) {
1416
if (!disposableStore) {
1417
disposableStore = new DisposableStore();
1418
}
1419
action = this._commands.toInternal(item.action, disposableStore);
1420
}
1421
1422
const insertText = item.insertText;
1423
return ({
1424
insertText: typeof insertText === 'string' ? insertText : { snippet: insertText.value },
1425
filterText: item.filterText,
1426
range: item.range ? typeConvert.Range.from(item.range) : undefined,
1427
showRange: (this._isAdditionsProposedApiEnabled && item.showRange) ? typeConvert.Range.from(item.showRange) : undefined,
1428
command,
1429
action,
1430
idx: idx,
1431
completeBracketPairs: this._isAdditionsProposedApiEnabled ? item.completeBracketPairs : false,
1432
isInlineEdit: this._isAdditionsProposedApiEnabled ? item.isInlineEdit : false,
1433
showInlineEditMenu: this._isAdditionsProposedApiEnabled ? item.showInlineEditMenu : false,
1434
displayLocation: (item.displayLocation && this._isAdditionsProposedApiEnabled) ? {
1435
range: typeConvert.Range.from(item.displayLocation.range),
1436
label: item.displayLocation.label,
1437
kind: item.displayLocation.kind ? typeConvert.InlineCompletionDisplayLocationKind.from(item.displayLocation.kind) : InlineCompletionDisplayLocationKind.Code,
1438
} : undefined,
1439
warning: (item.warning && this._isAdditionsProposedApiEnabled) ? {
1440
message: typeConvert.MarkdownString.from(item.warning.message),
1441
icon: item.warning.icon ? typeConvert.IconPath.fromThemeIcon(item.warning.icon) : undefined,
1442
} : undefined,
1443
correlationId: this._isAdditionsProposedApiEnabled ? item.correlationId : undefined,
1444
suggestionId: undefined,
1445
});
1446
}),
1447
commands: commands.map(c => {
1448
if (!disposableStore) {
1449
disposableStore = new DisposableStore();
1450
}
1451
return typeConvert.CompletionCommand.from(c, this._commands, disposableStore);
1452
}),
1453
suppressSuggestions: false,
1454
enableForwardStability,
1455
};
1456
}
1457
1458
disposeCompletions(pid: number, reason: languages.InlineCompletionsDisposeReason) {
1459
const completionList = this._references.get(pid);
1460
if (this._provider.handleListEndOfLifetime && this._isAdditionsProposedApiEnabled && completionList?.list) {
1461
function translateReason(reason: languages.InlineCompletionsDisposeReason): vscode.InlineCompletionsDisposeReason {
1462
switch (reason.kind) {
1463
case 'lostRace':
1464
return { kind: InlineCompletionsDisposeReasonKind.LostRace };
1465
case 'tokenCancellation':
1466
return { kind: InlineCompletionsDisposeReasonKind.TokenCancellation };
1467
case 'other':
1468
return { kind: InlineCompletionsDisposeReasonKind.Other };
1469
case 'empty':
1470
return { kind: InlineCompletionsDisposeReasonKind.Empty };
1471
case 'notTaken':
1472
return { kind: InlineCompletionsDisposeReasonKind.NotTaken };
1473
default:
1474
return { kind: InlineCompletionsDisposeReasonKind.Other };
1475
}
1476
}
1477
1478
this._provider.handleListEndOfLifetime(completionList.list, translateReason(reason));
1479
}
1480
1481
const data = this._references.disposeReferenceId(pid);
1482
data?.dispose();
1483
}
1484
1485
handleDidShowCompletionItem(pid: number, idx: number, updatedInsertText: string): void {
1486
const completionItem = this._references.get(pid)?.items[idx];
1487
if (completionItem) {
1488
if (this._provider.handleDidShowCompletionItem && this._isAdditionsProposedApiEnabled) {
1489
this._provider.handleDidShowCompletionItem(completionItem, updatedInsertText);
1490
}
1491
}
1492
}
1493
1494
handlePartialAccept(pid: number, idx: number, acceptedCharacters: number, info: languages.PartialAcceptInfo): void {
1495
const completionItem = this._references.get(pid)?.items[idx];
1496
if (completionItem) {
1497
if (this._provider.handleDidPartiallyAcceptCompletionItem && this._isAdditionsProposedApiEnabled) {
1498
this._provider.handleDidPartiallyAcceptCompletionItem(completionItem, acceptedCharacters);
1499
this._provider.handleDidPartiallyAcceptCompletionItem(completionItem, typeConvert.PartialAcceptInfo.to(info));
1500
}
1501
}
1502
}
1503
1504
handleEndOfLifetime(pid: number, idx: number, reason: languages.InlineCompletionEndOfLifeReason<{ pid: number; idx: number }>): void {
1505
const completionItem = this._references.get(pid)?.items[idx];
1506
if (completionItem) {
1507
if (this._provider.handleEndOfLifetime && this._isAdditionsProposedApiEnabled) {
1508
const r = typeConvert.InlineCompletionEndOfLifeReason.to(reason, ref => this._references.get(ref.pid)?.items[ref.idx]);
1509
this._provider.handleEndOfLifetime(completionItem, r);
1510
}
1511
}
1512
}
1513
1514
handleRejection(pid: number, idx: number): void {
1515
const completionItem = this._references.get(pid)?.items[idx];
1516
if (completionItem) {
1517
if (this._provider.handleDidRejectCompletionItem && this._isAdditionsProposedApiEnabled) {
1518
this._provider.handleDidRejectCompletionItem(completionItem);
1519
}
1520
}
1521
}
1522
}
1523
1524
class ReferenceMap<T> {
1525
private readonly _references = new Map<number, T>();
1526
private _idPool = 1;
1527
1528
createReferenceId(value: T): number {
1529
const id = this._idPool++;
1530
this._references.set(id, value);
1531
return id;
1532
}
1533
1534
disposeReferenceId(referenceId: number): T | undefined {
1535
const value = this._references.get(referenceId);
1536
this._references.delete(referenceId);
1537
return value;
1538
}
1539
1540
get(referenceId: number): T | undefined {
1541
return this._references.get(referenceId);
1542
}
1543
}
1544
1545
class SignatureHelpAdapter {
1546
1547
private readonly _cache = new Cache<vscode.SignatureHelp>('SignatureHelp');
1548
1549
constructor(
1550
private readonly _documents: ExtHostDocuments,
1551
private readonly _provider: vscode.SignatureHelpProvider,
1552
) { }
1553
1554
async provideSignatureHelp(resource: URI, position: IPosition, context: extHostProtocol.ISignatureHelpContextDto, token: CancellationToken): Promise<extHostProtocol.ISignatureHelpDto | undefined> {
1555
const doc = this._documents.getDocument(resource);
1556
const pos = typeConvert.Position.to(position);
1557
const vscodeContext = this.reviveContext(context);
1558
1559
const value = await this._provider.provideSignatureHelp(doc, pos, token, vscodeContext);
1560
if (value) {
1561
const id = this._cache.add([value]);
1562
return { ...typeConvert.SignatureHelp.from(value), id };
1563
}
1564
return undefined;
1565
}
1566
1567
private reviveContext(context: extHostProtocol.ISignatureHelpContextDto): vscode.SignatureHelpContext {
1568
let activeSignatureHelp: vscode.SignatureHelp | undefined = undefined;
1569
if (context.activeSignatureHelp) {
1570
const revivedSignatureHelp = typeConvert.SignatureHelp.to(context.activeSignatureHelp);
1571
const saved = this._cache.get(context.activeSignatureHelp.id, 0);
1572
if (saved) {
1573
activeSignatureHelp = saved;
1574
activeSignatureHelp.activeSignature = revivedSignatureHelp.activeSignature;
1575
activeSignatureHelp.activeParameter = revivedSignatureHelp.activeParameter;
1576
} else {
1577
activeSignatureHelp = revivedSignatureHelp;
1578
}
1579
}
1580
return { ...context, activeSignatureHelp };
1581
}
1582
1583
releaseSignatureHelp(id: number): any {
1584
this._cache.delete(id);
1585
}
1586
}
1587
1588
class InlayHintsAdapter {
1589
1590
private _cache = new Cache<vscode.InlayHint>('InlayHints');
1591
private readonly _disposables = new Map<number, DisposableStore>();
1592
1593
constructor(
1594
private readonly _documents: ExtHostDocuments,
1595
private readonly _commands: CommandsConverter,
1596
private readonly _provider: vscode.InlayHintsProvider,
1597
private readonly _logService: ILogService,
1598
private readonly _extension: IExtensionDescription
1599
) { }
1600
1601
async provideInlayHints(resource: URI, ran: IRange, token: CancellationToken): Promise<extHostProtocol.IInlayHintsDto | undefined> {
1602
const doc = this._documents.getDocument(resource);
1603
const range = typeConvert.Range.to(ran);
1604
1605
const hints = await this._provider.provideInlayHints(doc, range, token);
1606
if (!Array.isArray(hints) || hints.length === 0) {
1607
// bad result
1608
this._logService.trace(`[InlayHints] NO inlay hints from '${this._extension.identifier.value}' for range ${JSON.stringify(ran)}`);
1609
return undefined;
1610
}
1611
if (token.isCancellationRequested) {
1612
// cancelled -> return without further ado, esp no caching
1613
// of results as they will leak
1614
return undefined;
1615
}
1616
const pid = this._cache.add(hints);
1617
this._disposables.set(pid, new DisposableStore());
1618
const result: extHostProtocol.IInlayHintsDto = { hints: [], cacheId: pid };
1619
for (let i = 0; i < hints.length; i++) {
1620
if (this._isValidInlayHint(hints[i], range)) {
1621
result.hints.push(this._convertInlayHint(hints[i], [pid, i]));
1622
}
1623
}
1624
this._logService.trace(`[InlayHints] ${result.hints.length} inlay hints from '${this._extension.identifier.value}' for range ${JSON.stringify(ran)}`);
1625
return result;
1626
}
1627
1628
async resolveInlayHint(id: extHostProtocol.ChainedCacheId, token: CancellationToken) {
1629
if (typeof this._provider.resolveInlayHint !== 'function') {
1630
return undefined;
1631
}
1632
const item = this._cache.get(...id);
1633
if (!item) {
1634
return undefined;
1635
}
1636
const hint = await this._provider.resolveInlayHint(item, token);
1637
if (!hint) {
1638
return undefined;
1639
}
1640
if (!this._isValidInlayHint(hint)) {
1641
return undefined;
1642
}
1643
return this._convertInlayHint(hint, id);
1644
}
1645
1646
releaseHints(id: number): any {
1647
this._disposables.get(id)?.dispose();
1648
this._disposables.delete(id);
1649
this._cache.delete(id);
1650
}
1651
1652
private _isValidInlayHint(hint: vscode.InlayHint, range?: vscode.Range): boolean {
1653
if (hint.label.length === 0 || Array.isArray(hint.label) && hint.label.every(part => part.value.length === 0)) {
1654
console.log('INVALID inlay hint, empty label', hint);
1655
return false;
1656
}
1657
if (range && !range.contains(hint.position)) {
1658
// console.log('INVALID inlay hint, position outside range', range, hint);
1659
return false;
1660
}
1661
return true;
1662
}
1663
1664
private _convertInlayHint(hint: vscode.InlayHint, id: extHostProtocol.ChainedCacheId): extHostProtocol.IInlayHintDto {
1665
1666
const disposables = this._disposables.get(id[0]);
1667
if (!disposables) {
1668
throw Error('DisposableStore is missing...');
1669
}
1670
1671
const result: extHostProtocol.IInlayHintDto = {
1672
label: '', // fill-in below
1673
cacheId: id,
1674
tooltip: typeConvert.MarkdownString.fromStrict(hint.tooltip),
1675
position: typeConvert.Position.from(hint.position),
1676
textEdits: hint.textEdits && hint.textEdits.map(typeConvert.TextEdit.from),
1677
kind: hint.kind && typeConvert.InlayHintKind.from(hint.kind),
1678
paddingLeft: hint.paddingLeft,
1679
paddingRight: hint.paddingRight,
1680
};
1681
1682
if (typeof hint.label === 'string') {
1683
result.label = hint.label;
1684
} else {
1685
const parts: languages.InlayHintLabelPart[] = [];
1686
result.label = parts;
1687
1688
for (const part of hint.label) {
1689
if (!part.value) {
1690
console.warn('INVALID inlay hint, empty label part', this._extension.identifier.value);
1691
continue;
1692
}
1693
const part2: languages.InlayHintLabelPart = {
1694
label: part.value,
1695
tooltip: typeConvert.MarkdownString.fromStrict(part.tooltip)
1696
};
1697
if (Location.isLocation(part.location)) {
1698
part2.location = typeConvert.location.from(part.location);
1699
}
1700
if (part.command) {
1701
part2.command = this._commands.toInternal(part.command, disposables);
1702
}
1703
parts.push(part2);
1704
}
1705
}
1706
return result;
1707
}
1708
}
1709
1710
class LinkProviderAdapter {
1711
1712
private _cache = new Cache<vscode.DocumentLink>('DocumentLink');
1713
1714
constructor(
1715
private readonly _documents: ExtHostDocuments,
1716
private readonly _provider: vscode.DocumentLinkProvider
1717
) { }
1718
1719
async provideLinks(resource: URI, token: CancellationToken): Promise<extHostProtocol.ILinksListDto | undefined> {
1720
const doc = this._documents.getDocument(resource);
1721
1722
const links = await this._provider.provideDocumentLinks(doc, token);
1723
if (!Array.isArray(links) || links.length === 0) {
1724
// bad result
1725
return undefined;
1726
}
1727
if (token.isCancellationRequested) {
1728
// cancelled -> return without further ado, esp no caching
1729
// of results as they will leak
1730
return undefined;
1731
}
1732
if (typeof this._provider.resolveDocumentLink !== 'function') {
1733
// no resolve -> no caching
1734
return { links: links.filter(LinkProviderAdapter._validateLink).map(typeConvert.DocumentLink.from) };
1735
1736
} else {
1737
// cache links for future resolving
1738
const pid = this._cache.add(links);
1739
const result: extHostProtocol.ILinksListDto = { links: [], cacheId: pid };
1740
for (let i = 0; i < links.length; i++) {
1741
1742
if (!LinkProviderAdapter._validateLink(links[i])) {
1743
continue;
1744
}
1745
1746
const dto: extHostProtocol.ILinkDto = typeConvert.DocumentLink.from(links[i]);
1747
dto.cacheId = [pid, i];
1748
result.links.push(dto);
1749
}
1750
return result;
1751
}
1752
}
1753
1754
private static _validateLink(link: vscode.DocumentLink): boolean {
1755
if (link.target && link.target.path.length > 50_000) {
1756
console.warn('DROPPING link because it is too long');
1757
return false;
1758
}
1759
return true;
1760
}
1761
1762
async resolveLink(id: extHostProtocol.ChainedCacheId, token: CancellationToken): Promise<extHostProtocol.ILinkDto | undefined> {
1763
if (typeof this._provider.resolveDocumentLink !== 'function') {
1764
return undefined;
1765
}
1766
const item = this._cache.get(...id);
1767
if (!item) {
1768
return undefined;
1769
}
1770
const link = await this._provider.resolveDocumentLink(item, token);
1771
if (!link || !LinkProviderAdapter._validateLink(link)) {
1772
return undefined;
1773
}
1774
return typeConvert.DocumentLink.from(link);
1775
}
1776
1777
releaseLinks(id: number): any {
1778
this._cache.delete(id);
1779
}
1780
}
1781
1782
class ColorProviderAdapter {
1783
1784
constructor(
1785
private _documents: ExtHostDocuments,
1786
private _provider: vscode.DocumentColorProvider
1787
) { }
1788
1789
async provideColors(resource: URI, token: CancellationToken): Promise<extHostProtocol.IRawColorInfo[]> {
1790
const doc = this._documents.getDocument(resource);
1791
const colors = await this._provider.provideDocumentColors(doc, token);
1792
if (!Array.isArray(colors)) {
1793
return [];
1794
}
1795
const colorInfos: extHostProtocol.IRawColorInfo[] = colors.map(ci => {
1796
return {
1797
color: typeConvert.Color.from(ci.color),
1798
range: typeConvert.Range.from(ci.range)
1799
};
1800
});
1801
return colorInfos;
1802
}
1803
1804
async provideColorPresentations(resource: URI, raw: extHostProtocol.IRawColorInfo, token: CancellationToken): Promise<languages.IColorPresentation[] | undefined> {
1805
const document = this._documents.getDocument(resource);
1806
const range = typeConvert.Range.to(raw.range);
1807
const color = typeConvert.Color.to(raw.color);
1808
const value = await this._provider.provideColorPresentations(color, { document, range }, token);
1809
if (!Array.isArray(value)) {
1810
return undefined;
1811
}
1812
return value.map(typeConvert.ColorPresentation.from);
1813
}
1814
}
1815
1816
class FoldingProviderAdapter {
1817
1818
constructor(
1819
private _documents: ExtHostDocuments,
1820
private _provider: vscode.FoldingRangeProvider
1821
) { }
1822
1823
async provideFoldingRanges(resource: URI, context: languages.FoldingContext, token: CancellationToken): Promise<languages.FoldingRange[] | undefined> {
1824
const doc = this._documents.getDocument(resource);
1825
const ranges = await this._provider.provideFoldingRanges(doc, context, token);
1826
if (!Array.isArray(ranges)) {
1827
return undefined;
1828
}
1829
return ranges.map(typeConvert.FoldingRange.from);
1830
}
1831
}
1832
1833
class SelectionRangeAdapter {
1834
1835
constructor(
1836
private readonly _documents: ExtHostDocuments,
1837
private readonly _provider: vscode.SelectionRangeProvider,
1838
private readonly _logService: ILogService
1839
) { }
1840
1841
async provideSelectionRanges(resource: URI, pos: IPosition[], token: CancellationToken): Promise<languages.SelectionRange[][]> {
1842
const document = this._documents.getDocument(resource);
1843
const positions = pos.map(typeConvert.Position.to);
1844
1845
const allProviderRanges = await this._provider.provideSelectionRanges(document, positions, token);
1846
if (!isNonEmptyArray(allProviderRanges)) {
1847
return [];
1848
}
1849
if (allProviderRanges.length !== positions.length) {
1850
this._logService.warn('BAD selection ranges, provider must return ranges for each position');
1851
return [];
1852
}
1853
const allResults: languages.SelectionRange[][] = [];
1854
for (let i = 0; i < positions.length; i++) {
1855
const oneResult: languages.SelectionRange[] = [];
1856
allResults.push(oneResult);
1857
1858
let last: vscode.Position | vscode.Range = positions[i];
1859
let selectionRange = allProviderRanges[i];
1860
1861
while (true) {
1862
if (!selectionRange.range.contains(last)) {
1863
throw new Error('INVALID selection range, must contain the previous range');
1864
}
1865
oneResult.push(typeConvert.SelectionRange.from(selectionRange));
1866
if (!selectionRange.parent) {
1867
break;
1868
}
1869
last = selectionRange.range;
1870
selectionRange = selectionRange.parent;
1871
}
1872
}
1873
return allResults;
1874
}
1875
}
1876
1877
class CallHierarchyAdapter {
1878
1879
private readonly _idPool = new IdGenerator('');
1880
private readonly _cache = new Map<string, Map<string, vscode.CallHierarchyItem>>();
1881
1882
constructor(
1883
private readonly _documents: ExtHostDocuments,
1884
private readonly _provider: vscode.CallHierarchyProvider
1885
) { }
1886
1887
async prepareSession(uri: URI, position: IPosition, token: CancellationToken): Promise<extHostProtocol.ICallHierarchyItemDto[] | undefined> {
1888
const doc = this._documents.getDocument(uri);
1889
const pos = typeConvert.Position.to(position);
1890
1891
const items = await this._provider.prepareCallHierarchy(doc, pos, token);
1892
if (!items) {
1893
return undefined;
1894
}
1895
1896
const sessionId = this._idPool.nextId();
1897
this._cache.set(sessionId, new Map());
1898
1899
if (Array.isArray(items)) {
1900
return items.map(item => this._cacheAndConvertItem(sessionId, item));
1901
} else {
1902
return [this._cacheAndConvertItem(sessionId, items)];
1903
}
1904
}
1905
1906
async provideCallsTo(sessionId: string, itemId: string, token: CancellationToken): Promise<extHostProtocol.IIncomingCallDto[] | undefined> {
1907
const item = this._itemFromCache(sessionId, itemId);
1908
if (!item) {
1909
throw new Error('missing call hierarchy item');
1910
}
1911
const calls = await this._provider.provideCallHierarchyIncomingCalls(item, token);
1912
if (!calls) {
1913
return undefined;
1914
}
1915
return calls.map(call => {
1916
return {
1917
from: this._cacheAndConvertItem(sessionId, call.from),
1918
fromRanges: call.fromRanges.map(r => typeConvert.Range.from(r))
1919
};
1920
});
1921
}
1922
1923
async provideCallsFrom(sessionId: string, itemId: string, token: CancellationToken): Promise<extHostProtocol.IOutgoingCallDto[] | undefined> {
1924
const item = this._itemFromCache(sessionId, itemId);
1925
if (!item) {
1926
throw new Error('missing call hierarchy item');
1927
}
1928
const calls = await this._provider.provideCallHierarchyOutgoingCalls(item, token);
1929
if (!calls) {
1930
return undefined;
1931
}
1932
return calls.map(call => {
1933
return {
1934
to: this._cacheAndConvertItem(sessionId, call.to),
1935
fromRanges: call.fromRanges.map(r => typeConvert.Range.from(r))
1936
};
1937
});
1938
}
1939
1940
releaseSession(sessionId: string): void {
1941
this._cache.delete(sessionId);
1942
}
1943
1944
private _cacheAndConvertItem(sessionId: string, item: vscode.CallHierarchyItem): extHostProtocol.ICallHierarchyItemDto {
1945
const map = this._cache.get(sessionId)!;
1946
const dto = typeConvert.CallHierarchyItem.from(item, sessionId, map.size.toString(36));
1947
map.set(dto._itemId, item);
1948
return dto;
1949
}
1950
1951
private _itemFromCache(sessionId: string, itemId: string): vscode.CallHierarchyItem | undefined {
1952
const map = this._cache.get(sessionId);
1953
return map?.get(itemId);
1954
}
1955
}
1956
1957
class TypeHierarchyAdapter {
1958
1959
private readonly _idPool = new IdGenerator('');
1960
private readonly _cache = new Map<string, Map<string, vscode.TypeHierarchyItem>>();
1961
1962
constructor(
1963
private readonly _documents: ExtHostDocuments,
1964
private readonly _provider: vscode.TypeHierarchyProvider
1965
) { }
1966
1967
async prepareSession(uri: URI, position: IPosition, token: CancellationToken): Promise<extHostProtocol.ITypeHierarchyItemDto[] | undefined> {
1968
const doc = this._documents.getDocument(uri);
1969
const pos = typeConvert.Position.to(position);
1970
1971
const items = await this._provider.prepareTypeHierarchy(doc, pos, token);
1972
if (!items) {
1973
return undefined;
1974
}
1975
1976
const sessionId = this._idPool.nextId();
1977
this._cache.set(sessionId, new Map());
1978
1979
if (Array.isArray(items)) {
1980
return items.map(item => this._cacheAndConvertItem(sessionId, item));
1981
} else {
1982
return [this._cacheAndConvertItem(sessionId, items)];
1983
}
1984
}
1985
1986
async provideSupertypes(sessionId: string, itemId: string, token: CancellationToken): Promise<extHostProtocol.ITypeHierarchyItemDto[] | undefined> {
1987
const item = this._itemFromCache(sessionId, itemId);
1988
if (!item) {
1989
throw new Error('missing type hierarchy item');
1990
}
1991
const supertypes = await this._provider.provideTypeHierarchySupertypes(item, token);
1992
if (!supertypes) {
1993
return undefined;
1994
}
1995
return supertypes.map(supertype => {
1996
return this._cacheAndConvertItem(sessionId, supertype);
1997
});
1998
}
1999
2000
async provideSubtypes(sessionId: string, itemId: string, token: CancellationToken): Promise<extHostProtocol.ITypeHierarchyItemDto[] | undefined> {
2001
const item = this._itemFromCache(sessionId, itemId);
2002
if (!item) {
2003
throw new Error('missing type hierarchy item');
2004
}
2005
const subtypes = await this._provider.provideTypeHierarchySubtypes(item, token);
2006
if (!subtypes) {
2007
return undefined;
2008
}
2009
return subtypes.map(subtype => {
2010
return this._cacheAndConvertItem(sessionId, subtype);
2011
});
2012
}
2013
2014
releaseSession(sessionId: string): void {
2015
this._cache.delete(sessionId);
2016
}
2017
2018
private _cacheAndConvertItem(sessionId: string, item: vscode.TypeHierarchyItem): extHostProtocol.ITypeHierarchyItemDto {
2019
const map = this._cache.get(sessionId)!;
2020
const dto = typeConvert.TypeHierarchyItem.from(item, sessionId, map.size.toString(36));
2021
map.set(dto._itemId, item);
2022
return dto;
2023
}
2024
2025
private _itemFromCache(sessionId: string, itemId: string): vscode.TypeHierarchyItem | undefined {
2026
const map = this._cache.get(sessionId);
2027
return map?.get(itemId);
2028
}
2029
}
2030
2031
class DocumentDropEditAdapter {
2032
2033
private readonly _cache = new Cache<vscode.DocumentDropEdit>('DocumentDropEdit');
2034
2035
constructor(
2036
private readonly _proxy: extHostProtocol.MainThreadLanguageFeaturesShape,
2037
private readonly _documents: ExtHostDocuments,
2038
private readonly _provider: vscode.DocumentDropEditProvider,
2039
private readonly _handle: number,
2040
private readonly _extension: IExtensionDescription,
2041
) { }
2042
2043
async provideDocumentOnDropEdits(requestId: number, uri: URI, position: IPosition, dataTransferDto: extHostProtocol.DataTransferDTO, token: CancellationToken): Promise<extHostProtocol.IDocumentDropEditDto[] | undefined> {
2044
const doc = this._documents.getDocument(uri);
2045
const pos = typeConvert.Position.to(position);
2046
const dataTransfer = typeConvert.DataTransfer.toDataTransfer(dataTransferDto, async (id) => {
2047
return (await this._proxy.$resolveDocumentOnDropFileData(this._handle, requestId, id)).buffer;
2048
});
2049
2050
const edits = await this._provider.provideDocumentDropEdits(doc, pos, dataTransfer, token);
2051
if (!edits) {
2052
return undefined;
2053
}
2054
2055
const editsArray = asArray(edits);
2056
const cacheId = this._cache.add(editsArray);
2057
2058
return editsArray.map((edit, i): extHostProtocol.IDocumentDropEditDto => ({
2059
_cacheId: [cacheId, i],
2060
title: edit.title ?? localize('defaultDropLabel', "Drop using '{0}' extension", this._extension.displayName || this._extension.name),
2061
kind: edit.kind?.value,
2062
yieldTo: edit.yieldTo?.map(x => x.value),
2063
insertText: typeof edit.insertText === 'string' ? edit.insertText : { snippet: edit.insertText.value },
2064
additionalEdit: edit.additionalEdit ? typeConvert.WorkspaceEdit.from(edit.additionalEdit, undefined) : undefined,
2065
}));
2066
}
2067
2068
async resolveDropEdit(id: extHostProtocol.ChainedCacheId, token: CancellationToken): Promise<{ additionalEdit?: extHostProtocol.IWorkspaceEditDto }> {
2069
const [sessionId, itemId] = id;
2070
const item = this._cache.get(sessionId, itemId);
2071
if (!item || !this._provider.resolveDocumentDropEdit) {
2072
return {}; // this should not happen...
2073
}
2074
2075
const resolvedItem = (await this._provider.resolveDocumentDropEdit(item, token)) ?? item;
2076
const additionalEdit = resolvedItem.additionalEdit ? typeConvert.WorkspaceEdit.from(resolvedItem.additionalEdit, undefined) : undefined;
2077
return { additionalEdit };
2078
}
2079
2080
releaseDropEdits(id: number): any {
2081
this._cache.delete(id);
2082
}
2083
}
2084
2085
type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | HoverAdapter
2086
| DocumentHighlightAdapter | MultiDocumentHighlightAdapter | ReferenceAdapter | CodeActionAdapter
2087
| DocumentPasteEditProvider | DocumentFormattingAdapter | RangeFormattingAdapter
2088
| OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter
2089
| CompletionsAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter
2090
| TypeDefinitionAdapter | ColorProviderAdapter | FoldingProviderAdapter | DeclarationAdapter
2091
| SelectionRangeAdapter | CallHierarchyAdapter | TypeHierarchyAdapter
2092
| DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter
2093
| EvaluatableExpressionAdapter | InlineValuesAdapter
2094
| LinkedEditingRangeAdapter | InlayHintsAdapter | InlineCompletionAdapter
2095
| DocumentDropEditAdapter | NewSymbolNamesAdapter;
2096
2097
class AdapterData {
2098
constructor(
2099
readonly adapter: Adapter,
2100
readonly extension: IExtensionDescription
2101
) { }
2102
}
2103
2104
export class ExtHostLanguageFeatures extends CoreDisposable implements extHostProtocol.ExtHostLanguageFeaturesShape {
2105
2106
private static _handlePool: number = 0;
2107
2108
private readonly _proxy: extHostProtocol.MainThreadLanguageFeaturesShape;
2109
private readonly _adapter = new Map<number, AdapterData>();
2110
2111
private _inlineCompletionsUnificationState: vscode.InlineCompletionsUnificationState;
2112
public get inlineCompletionsUnificationState(): vscode.InlineCompletionsUnificationState {
2113
return this._inlineCompletionsUnificationState;
2114
}
2115
2116
private readonly _onDidChangeInlineCompletionsUnificationState = this._register(new Emitter<void>());
2117
readonly onDidChangeInlineCompletionsUnificationState = this._onDidChangeInlineCompletionsUnificationState.event;
2118
2119
constructor(
2120
mainContext: extHostProtocol.IMainContext,
2121
private readonly _uriTransformer: IURITransformer,
2122
private readonly _documents: ExtHostDocuments,
2123
private readonly _commands: ExtHostCommands,
2124
private readonly _diagnostics: ExtHostDiagnostics,
2125
private readonly _logService: ILogService,
2126
private readonly _apiDeprecation: IExtHostApiDeprecationService,
2127
private readonly _extensionTelemetry: IExtHostTelemetry
2128
) {
2129
super();
2130
this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadLanguageFeatures);
2131
this._inlineCompletionsUnificationState = {
2132
codeUnification: false,
2133
modelUnification: false,
2134
expAssignments: []
2135
};
2136
}
2137
2138
private _transformDocumentSelector(selector: vscode.DocumentSelector, extension: IExtensionDescription): Array<extHostProtocol.IDocumentFilterDto> {
2139
return typeConvert.DocumentSelector.from(selector, this._uriTransformer, extension);
2140
}
2141
2142
private _createDisposable(handle: number): Disposable {
2143
return new Disposable(() => {
2144
this._adapter.delete(handle);
2145
this._proxy.$unregister(handle);
2146
});
2147
}
2148
2149
private _nextHandle(): number {
2150
return ExtHostLanguageFeatures._handlePool++;
2151
}
2152
2153
private async _withAdapter<A, R>(
2154
handle: number,
2155
ctor: { new(...args: any[]): A },
2156
callback: (adapter: A, extension: IExtensionDescription) => Promise<R>,
2157
fallbackValue: R,
2158
tokenToRaceAgainst: CancellationToken | undefined,
2159
doNotLog: boolean = false
2160
): Promise<R> {
2161
const data = this._adapter.get(handle);
2162
if (!data || !(data.adapter instanceof ctor)) {
2163
return fallbackValue;
2164
}
2165
2166
const t1: number = Date.now();
2167
if (!doNotLog) {
2168
this._logService.trace(`[${data.extension.identifier.value}] INVOKE provider '${callback.toString().replace(/[\r\n]/g, '')}'`);
2169
}
2170
2171
const result = callback(data.adapter, data.extension);
2172
2173
// logging,tracing
2174
Promise.resolve(result).catch(err => {
2175
if (!isCancellationError(err)) {
2176
this._logService.error(`[${data.extension.identifier.value}] provider FAILED`);
2177
this._logService.error(err);
2178
2179
this._extensionTelemetry.onExtensionError(data.extension.identifier, err);
2180
}
2181
}).finally(() => {
2182
if (!doNotLog) {
2183
this._logService.trace(`[${data.extension.identifier.value}] provider DONE after ${Date.now() - t1}ms`);
2184
}
2185
});
2186
2187
if (CancellationToken.isCancellationToken(tokenToRaceAgainst)) {
2188
return raceCancellationError(result, tokenToRaceAgainst);
2189
}
2190
return result;
2191
}
2192
2193
private _addNewAdapter(adapter: Adapter, extension: IExtensionDescription): number {
2194
const handle = this._nextHandle();
2195
this._adapter.set(handle, new AdapterData(adapter, extension));
2196
return handle;
2197
}
2198
2199
private static _extLabel(ext: IExtensionDescription): string {
2200
return ext.displayName || ext.name;
2201
}
2202
2203
private static _extId(ext: IExtensionDescription): string {
2204
return ext.identifier.value;
2205
}
2206
2207
// --- outline
2208
2209
registerDocumentSymbolProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentSymbolProvider, metadata?: vscode.DocumentSymbolProviderMetadata): vscode.Disposable {
2210
const handle = this._addNewAdapter(new DocumentSymbolAdapter(this._documents, provider), extension);
2211
const displayName = (metadata && metadata.label) || ExtHostLanguageFeatures._extLabel(extension);
2212
this._proxy.$registerDocumentSymbolProvider(handle, this._transformDocumentSelector(selector, extension), displayName);
2213
return this._createDisposable(handle);
2214
}
2215
2216
$provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise<languages.DocumentSymbol[] | undefined> {
2217
return this._withAdapter(handle, DocumentSymbolAdapter, adapter => adapter.provideDocumentSymbols(URI.revive(resource), token), undefined, token);
2218
}
2219
2220
// --- code lens
2221
2222
registerCodeLensProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable {
2223
const handle = this._nextHandle();
2224
const eventHandle = typeof provider.onDidChangeCodeLenses === 'function' ? this._nextHandle() : undefined;
2225
2226
this._adapter.set(handle, new AdapterData(new CodeLensAdapter(this._documents, this._commands.converter, provider, extension, this._extensionTelemetry, this._logService), extension));
2227
this._proxy.$registerCodeLensSupport(handle, this._transformDocumentSelector(selector, extension), eventHandle);
2228
let result = this._createDisposable(handle);
2229
2230
if (eventHandle !== undefined) {
2231
const subscription = provider.onDidChangeCodeLenses!(_ => this._proxy.$emitCodeLensEvent(eventHandle));
2232
result = Disposable.from(result, subscription);
2233
}
2234
2235
return result;
2236
}
2237
2238
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<extHostProtocol.ICodeLensListDto | undefined> {
2239
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.provideCodeLenses(URI.revive(resource), token), undefined, token, resource.scheme === 'output');
2240
}
2241
2242
$resolveCodeLens(handle: number, symbol: extHostProtocol.ICodeLensDto, token: CancellationToken): Promise<extHostProtocol.ICodeLensDto | undefined> {
2243
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.resolveCodeLens(symbol, token), undefined, undefined, true);
2244
}
2245
2246
$releaseCodeLenses(handle: number, cacheId: number): void {
2247
this._withAdapter(handle, CodeLensAdapter, adapter => Promise.resolve(adapter.releaseCodeLenses(cacheId)), undefined, undefined, true);
2248
}
2249
2250
// --- declaration
2251
2252
registerDefinitionProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable {
2253
const handle = this._addNewAdapter(new DefinitionAdapter(this._documents, provider), extension);
2254
this._proxy.$registerDefinitionSupport(handle, this._transformDocumentSelector(selector, extension));
2255
return this._createDisposable(handle);
2256
}
2257
2258
$provideDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<languages.LocationLink[]> {
2259
return this._withAdapter(handle, DefinitionAdapter, adapter => adapter.provideDefinition(URI.revive(resource), position, token), [], token);
2260
}
2261
2262
registerDeclarationProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DeclarationProvider): vscode.Disposable {
2263
const handle = this._addNewAdapter(new DeclarationAdapter(this._documents, provider), extension);
2264
this._proxy.$registerDeclarationSupport(handle, this._transformDocumentSelector(selector, extension));
2265
return this._createDisposable(handle);
2266
}
2267
2268
$provideDeclaration(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<languages.LocationLink[]> {
2269
return this._withAdapter(handle, DeclarationAdapter, adapter => adapter.provideDeclaration(URI.revive(resource), position, token), [], token);
2270
}
2271
2272
registerImplementationProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.ImplementationProvider): vscode.Disposable {
2273
const handle = this._addNewAdapter(new ImplementationAdapter(this._documents, provider), extension);
2274
this._proxy.$registerImplementationSupport(handle, this._transformDocumentSelector(selector, extension));
2275
return this._createDisposable(handle);
2276
}
2277
2278
$provideImplementation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<languages.LocationLink[]> {
2279
return this._withAdapter(handle, ImplementationAdapter, adapter => adapter.provideImplementation(URI.revive(resource), position, token), [], token);
2280
}
2281
2282
registerTypeDefinitionProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.TypeDefinitionProvider): vscode.Disposable {
2283
const handle = this._addNewAdapter(new TypeDefinitionAdapter(this._documents, provider), extension);
2284
this._proxy.$registerTypeDefinitionSupport(handle, this._transformDocumentSelector(selector, extension));
2285
return this._createDisposable(handle);
2286
}
2287
2288
$provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<languages.LocationLink[]> {
2289
return this._withAdapter(handle, TypeDefinitionAdapter, adapter => adapter.provideTypeDefinition(URI.revive(resource), position, token), [], token);
2290
}
2291
2292
// --- extra info
2293
2294
registerHoverProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.HoverProvider, extensionId?: ExtensionIdentifier): vscode.Disposable {
2295
const handle = this._addNewAdapter(new HoverAdapter(this._documents, provider), extension);
2296
this._proxy.$registerHoverProvider(handle, this._transformDocumentSelector(selector, extension));
2297
return this._createDisposable(handle);
2298
}
2299
2300
$provideHover(handle: number, resource: UriComponents, position: IPosition, context: languages.HoverContext<{ id: number }> | undefined, token: CancellationToken,): Promise<extHostProtocol.HoverWithId | undefined> {
2301
return this._withAdapter(handle, HoverAdapter, adapter => adapter.provideHover(URI.revive(resource), position, context, token), undefined, token);
2302
}
2303
2304
$releaseHover(handle: number, id: number): void {
2305
this._withAdapter(handle, HoverAdapter, adapter => Promise.resolve(adapter.releaseHover(id)), undefined, undefined);
2306
}
2307
2308
// --- debug hover
2309
2310
registerEvaluatableExpressionProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.EvaluatableExpressionProvider, extensionId?: ExtensionIdentifier): vscode.Disposable {
2311
const handle = this._addNewAdapter(new EvaluatableExpressionAdapter(this._documents, provider), extension);
2312
this._proxy.$registerEvaluatableExpressionProvider(handle, this._transformDocumentSelector(selector, extension));
2313
return this._createDisposable(handle);
2314
}
2315
2316
$provideEvaluatableExpression(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<languages.EvaluatableExpression | undefined> {
2317
return this._withAdapter(handle, EvaluatableExpressionAdapter, adapter => adapter.provideEvaluatableExpression(URI.revive(resource), position, token), undefined, token);
2318
}
2319
2320
// --- debug inline values
2321
2322
registerInlineValuesProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.InlineValuesProvider, extensionId?: ExtensionIdentifier): vscode.Disposable {
2323
2324
const eventHandle = typeof provider.onDidChangeInlineValues === 'function' ? this._nextHandle() : undefined;
2325
const handle = this._addNewAdapter(new InlineValuesAdapter(this._documents, provider), extension);
2326
2327
this._proxy.$registerInlineValuesProvider(handle, this._transformDocumentSelector(selector, extension), eventHandle);
2328
let result = this._createDisposable(handle);
2329
2330
if (eventHandle !== undefined) {
2331
const subscription = provider.onDidChangeInlineValues!(_ => this._proxy.$emitInlineValuesEvent(eventHandle));
2332
result = Disposable.from(result, subscription);
2333
}
2334
return result;
2335
}
2336
2337
$provideInlineValues(handle: number, resource: UriComponents, range: IRange, context: extHostProtocol.IInlineValueContextDto, token: CancellationToken): Promise<languages.InlineValue[] | undefined> {
2338
return this._withAdapter(handle, InlineValuesAdapter, adapter => adapter.provideInlineValues(URI.revive(resource), range, context, token), undefined, token);
2339
}
2340
2341
// --- occurrences
2342
2343
registerDocumentHighlightProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable {
2344
const handle = this._addNewAdapter(new DocumentHighlightAdapter(this._documents, provider), extension);
2345
this._proxy.$registerDocumentHighlightProvider(handle, this._transformDocumentSelector(selector, extension));
2346
return this._createDisposable(handle);
2347
}
2348
2349
registerMultiDocumentHighlightProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.MultiDocumentHighlightProvider): vscode.Disposable {
2350
const handle = this._addNewAdapter(new MultiDocumentHighlightAdapter(this._documents, provider, this._logService), extension);
2351
this._proxy.$registerMultiDocumentHighlightProvider(handle, this._transformDocumentSelector(selector, extension));
2352
return this._createDisposable(handle);
2353
}
2354
2355
$provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<languages.DocumentHighlight[] | undefined> {
2356
return this._withAdapter(handle, DocumentHighlightAdapter, adapter => adapter.provideDocumentHighlights(URI.revive(resource), position, token), undefined, token);
2357
}
2358
2359
$provideMultiDocumentHighlights(handle: number, resource: UriComponents, position: IPosition, otherModels: UriComponents[], token: CancellationToken): Promise<languages.MultiDocumentHighlight[] | undefined> {
2360
return this._withAdapter(handle, MultiDocumentHighlightAdapter, adapter => adapter.provideMultiDocumentHighlights(URI.revive(resource), position, otherModels.map(model => URI.revive(model)), token), undefined, token);
2361
}
2362
2363
// --- linked editing
2364
2365
registerLinkedEditingRangeProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.LinkedEditingRangeProvider): vscode.Disposable {
2366
const handle = this._addNewAdapter(new LinkedEditingRangeAdapter(this._documents, provider), extension);
2367
this._proxy.$registerLinkedEditingRangeProvider(handle, this._transformDocumentSelector(selector, extension));
2368
return this._createDisposable(handle);
2369
}
2370
2371
$provideLinkedEditingRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<extHostProtocol.ILinkedEditingRangesDto | undefined> {
2372
return this._withAdapter(handle, LinkedEditingRangeAdapter, async adapter => {
2373
const res = await adapter.provideLinkedEditingRanges(URI.revive(resource), position, token);
2374
if (res) {
2375
return {
2376
ranges: res.ranges,
2377
wordPattern: res.wordPattern ? ExtHostLanguageFeatures._serializeRegExp(res.wordPattern) : undefined
2378
};
2379
}
2380
return undefined;
2381
}, undefined, token);
2382
}
2383
2384
// --- references
2385
2386
registerReferenceProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.ReferenceProvider): vscode.Disposable {
2387
const handle = this._addNewAdapter(new ReferenceAdapter(this._documents, provider), extension);
2388
this._proxy.$registerReferenceSupport(handle, this._transformDocumentSelector(selector, extension));
2389
return this._createDisposable(handle);
2390
}
2391
2392
$provideReferences(handle: number, resource: UriComponents, position: IPosition, context: languages.ReferenceContext, token: CancellationToken): Promise<languages.Location[] | undefined> {
2393
return this._withAdapter(handle, ReferenceAdapter, adapter => adapter.provideReferences(URI.revive(resource), position, context, token), undefined, token);
2394
}
2395
2396
// --- code actions
2397
2398
registerCodeActionProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable {
2399
const store = new DisposableStore();
2400
const handle = this._addNewAdapter(new CodeActionAdapter(this._documents, this._commands.converter, this._diagnostics, provider, this._logService, extension, this._apiDeprecation), extension);
2401
this._proxy.$registerCodeActionSupport(handle, this._transformDocumentSelector(selector, extension), {
2402
providedKinds: metadata?.providedCodeActionKinds?.map(kind => kind.value),
2403
documentation: metadata?.documentation?.map(x => ({
2404
kind: x.kind.value,
2405
command: this._commands.converter.toInternal(x.command, store),
2406
}))
2407
}, ExtHostLanguageFeatures._extLabel(extension), ExtHostLanguageFeatures._extId(extension), Boolean(provider.resolveCodeAction));
2408
store.add(this._createDisposable(handle));
2409
return store;
2410
}
2411
2412
2413
$provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: languages.CodeActionContext, token: CancellationToken): Promise<extHostProtocol.ICodeActionListDto | undefined> {
2414
return this._withAdapter(handle, CodeActionAdapter, adapter => adapter.provideCodeActions(URI.revive(resource), rangeOrSelection, context, token), undefined, token);
2415
}
2416
2417
$resolveCodeAction(handle: number, id: extHostProtocol.ChainedCacheId, token: CancellationToken): Promise<{ edit?: extHostProtocol.IWorkspaceEditDto; command?: extHostProtocol.ICommandDto }> {
2418
return this._withAdapter(handle, CodeActionAdapter, adapter => adapter.resolveCodeAction(id, token), {}, undefined);
2419
}
2420
2421
$releaseCodeActions(handle: number, cacheId: number): void {
2422
this._withAdapter(handle, CodeActionAdapter, adapter => Promise.resolve(adapter.releaseCodeActions(cacheId)), undefined, undefined);
2423
}
2424
2425
// --- formatting
2426
2427
registerDocumentFormattingEditProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentFormattingEditProvider): vscode.Disposable {
2428
const handle = this._addNewAdapter(new DocumentFormattingAdapter(this._documents, provider), extension);
2429
this._proxy.$registerDocumentFormattingSupport(handle, this._transformDocumentSelector(selector, extension), extension.identifier, extension.displayName || extension.name);
2430
return this._createDisposable(handle);
2431
}
2432
2433
$provideDocumentFormattingEdits(handle: number, resource: UriComponents, options: languages.FormattingOptions, token: CancellationToken): Promise<languages.TextEdit[] | undefined> {
2434
return this._withAdapter(handle, DocumentFormattingAdapter, adapter => adapter.provideDocumentFormattingEdits(URI.revive(resource), options, token), undefined, token);
2435
}
2436
2437
registerDocumentRangeFormattingEditProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentRangeFormattingEditProvider): vscode.Disposable {
2438
const canFormatMultipleRanges = typeof provider.provideDocumentRangesFormattingEdits === 'function';
2439
const handle = this._addNewAdapter(new RangeFormattingAdapter(this._documents, provider), extension);
2440
this._proxy.$registerRangeFormattingSupport(handle, this._transformDocumentSelector(selector, extension), extension.identifier, extension.displayName || extension.name, canFormatMultipleRanges);
2441
return this._createDisposable(handle);
2442
}
2443
2444
$provideDocumentRangeFormattingEdits(handle: number, resource: UriComponents, range: IRange, options: languages.FormattingOptions, token: CancellationToken): Promise<languages.TextEdit[] | undefined> {
2445
return this._withAdapter(handle, RangeFormattingAdapter, adapter => adapter.provideDocumentRangeFormattingEdits(URI.revive(resource), range, options, token), undefined, token);
2446
}
2447
2448
$provideDocumentRangesFormattingEdits(handle: number, resource: UriComponents, ranges: IRange[], options: languages.FormattingOptions, token: CancellationToken): Promise<languages.TextEdit[] | undefined> {
2449
return this._withAdapter(handle, RangeFormattingAdapter, adapter => adapter.provideDocumentRangesFormattingEdits(URI.revive(resource), ranges, options, token), undefined, token);
2450
}
2451
2452
registerOnTypeFormattingEditProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.OnTypeFormattingEditProvider, triggerCharacters: string[]): vscode.Disposable {
2453
const handle = this._addNewAdapter(new OnTypeFormattingAdapter(this._documents, provider), extension);
2454
this._proxy.$registerOnTypeFormattingSupport(handle, this._transformDocumentSelector(selector, extension), triggerCharacters, extension.identifier);
2455
return this._createDisposable(handle);
2456
}
2457
2458
$provideOnTypeFormattingEdits(handle: number, resource: UriComponents, position: IPosition, ch: string, options: languages.FormattingOptions, token: CancellationToken): Promise<languages.TextEdit[] | undefined> {
2459
return this._withAdapter(handle, OnTypeFormattingAdapter, adapter => adapter.provideOnTypeFormattingEdits(URI.revive(resource), position, ch, options, token), undefined, token);
2460
}
2461
2462
// --- navigate types
2463
2464
registerWorkspaceSymbolProvider(extension: IExtensionDescription, provider: vscode.WorkspaceSymbolProvider): vscode.Disposable {
2465
const handle = this._addNewAdapter(new NavigateTypeAdapter(provider, this._logService), extension);
2466
this._proxy.$registerNavigateTypeSupport(handle, typeof provider.resolveWorkspaceSymbol === 'function');
2467
return this._createDisposable(handle);
2468
}
2469
2470
$provideWorkspaceSymbols(handle: number, search: string, token: CancellationToken): Promise<extHostProtocol.IWorkspaceSymbolsDto> {
2471
return this._withAdapter(handle, NavigateTypeAdapter, adapter => adapter.provideWorkspaceSymbols(search, token), { symbols: [] }, token);
2472
}
2473
2474
$resolveWorkspaceSymbol(handle: number, symbol: extHostProtocol.IWorkspaceSymbolDto, token: CancellationToken): Promise<extHostProtocol.IWorkspaceSymbolDto | undefined> {
2475
return this._withAdapter(handle, NavigateTypeAdapter, adapter => adapter.resolveWorkspaceSymbol(symbol, token), undefined, undefined);
2476
}
2477
2478
$releaseWorkspaceSymbols(handle: number, id: number): void {
2479
this._withAdapter(handle, NavigateTypeAdapter, adapter => adapter.releaseWorkspaceSymbols(id), undefined, undefined);
2480
}
2481
2482
// --- rename
2483
2484
registerRenameProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.RenameProvider): vscode.Disposable {
2485
const handle = this._addNewAdapter(new RenameAdapter(this._documents, provider, this._logService), extension);
2486
this._proxy.$registerRenameSupport(handle, this._transformDocumentSelector(selector, extension), RenameAdapter.supportsResolving(provider));
2487
return this._createDisposable(handle);
2488
}
2489
2490
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string, token: CancellationToken): Promise<extHostProtocol.IWorkspaceEditDto | undefined> {
2491
return this._withAdapter(handle, RenameAdapter, adapter => adapter.provideRenameEdits(URI.revive(resource), position, newName, token), undefined, token);
2492
}
2493
2494
$resolveRenameLocation(handle: number, resource: URI, position: IPosition, token: CancellationToken): Promise<languages.RenameLocation | undefined> {
2495
return this._withAdapter(handle, RenameAdapter, adapter => adapter.resolveRenameLocation(URI.revive(resource), position, token), undefined, token);
2496
}
2497
2498
registerNewSymbolNamesProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.NewSymbolNamesProvider): vscode.Disposable {
2499
const handle = this._addNewAdapter(new NewSymbolNamesAdapter(this._documents, provider, this._logService), extension);
2500
this._proxy.$registerNewSymbolNamesProvider(handle, this._transformDocumentSelector(selector, extension));
2501
return this._createDisposable(handle);
2502
}
2503
2504
$supportsAutomaticNewSymbolNamesTriggerKind(handle: number): Promise<boolean | undefined> {
2505
return this._withAdapter(
2506
handle,
2507
NewSymbolNamesAdapter,
2508
adapter => adapter.supportsAutomaticNewSymbolNamesTriggerKind(),
2509
false,
2510
undefined
2511
);
2512
}
2513
2514
$provideNewSymbolNames(handle: number, resource: UriComponents, range: IRange, triggerKind: languages.NewSymbolNameTriggerKind, token: CancellationToken): Promise<languages.NewSymbolName[] | undefined> {
2515
return this._withAdapter(handle, NewSymbolNamesAdapter, adapter => adapter.provideNewSymbolNames(URI.revive(resource), range, triggerKind, token), undefined, token);
2516
}
2517
2518
//#region semantic coloring
2519
2520
registerDocumentSemanticTokensProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentSemanticTokensProvider, legend: vscode.SemanticTokensLegend): vscode.Disposable {
2521
const handle = this._addNewAdapter(new DocumentSemanticTokensAdapter(this._documents, provider), extension);
2522
const eventHandle = (typeof provider.onDidChangeSemanticTokens === 'function' ? this._nextHandle() : undefined);
2523
this._proxy.$registerDocumentSemanticTokensProvider(handle, this._transformDocumentSelector(selector, extension), legend, eventHandle);
2524
let result = this._createDisposable(handle);
2525
2526
if (eventHandle) {
2527
const subscription = provider.onDidChangeSemanticTokens!(_ => this._proxy.$emitDocumentSemanticTokensEvent(eventHandle));
2528
result = Disposable.from(result, subscription);
2529
}
2530
2531
return result;
2532
}
2533
2534
$provideDocumentSemanticTokens(handle: number, resource: UriComponents, previousResultId: number, token: CancellationToken): Promise<VSBuffer | null> {
2535
return this._withAdapter(handle, DocumentSemanticTokensAdapter, adapter => adapter.provideDocumentSemanticTokens(URI.revive(resource), previousResultId, token), null, token);
2536
}
2537
2538
$releaseDocumentSemanticTokens(handle: number, semanticColoringResultId: number): void {
2539
this._withAdapter(handle, DocumentSemanticTokensAdapter, adapter => adapter.releaseDocumentSemanticColoring(semanticColoringResultId), undefined, undefined);
2540
}
2541
2542
registerDocumentRangeSemanticTokensProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentRangeSemanticTokensProvider, legend: vscode.SemanticTokensLegend): vscode.Disposable {
2543
const handle = this._addNewAdapter(new DocumentRangeSemanticTokensAdapter(this._documents, provider), extension);
2544
this._proxy.$registerDocumentRangeSemanticTokensProvider(handle, this._transformDocumentSelector(selector, extension), legend);
2545
return this._createDisposable(handle);
2546
}
2547
2548
$provideDocumentRangeSemanticTokens(handle: number, resource: UriComponents, range: IRange, token: CancellationToken): Promise<VSBuffer | null> {
2549
return this._withAdapter(handle, DocumentRangeSemanticTokensAdapter, adapter => adapter.provideDocumentRangeSemanticTokens(URI.revive(resource), range, token), null, token);
2550
}
2551
2552
//#endregion
2553
2554
// --- suggestion
2555
2556
registerCompletionItemProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, triggerCharacters: string[]): vscode.Disposable {
2557
const handle = this._addNewAdapter(new CompletionsAdapter(this._documents, this._commands.converter, provider, this._apiDeprecation, extension), extension);
2558
this._proxy.$registerCompletionsProvider(handle, this._transformDocumentSelector(selector, extension), triggerCharacters, CompletionsAdapter.supportsResolving(provider), extension.identifier);
2559
return this._createDisposable(handle);
2560
}
2561
2562
$provideCompletionItems(handle: number, resource: UriComponents, position: IPosition, context: languages.CompletionContext, token: CancellationToken): Promise<extHostProtocol.ISuggestResultDto | undefined> {
2563
return this._withAdapter(handle, CompletionsAdapter, adapter => adapter.provideCompletionItems(URI.revive(resource), position, context, token), undefined, token);
2564
}
2565
2566
$resolveCompletionItem(handle: number, id: extHostProtocol.ChainedCacheId, token: CancellationToken): Promise<extHostProtocol.ISuggestDataDto | undefined> {
2567
return this._withAdapter(handle, CompletionsAdapter, adapter => adapter.resolveCompletionItem(id, token), undefined, token);
2568
}
2569
2570
$releaseCompletionItems(handle: number, id: number): void {
2571
this._withAdapter(handle, CompletionsAdapter, adapter => adapter.releaseCompletionItems(id), undefined, undefined);
2572
}
2573
2574
// --- ghost text
2575
2576
registerInlineCompletionsProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.InlineCompletionItemProvider, metadata: vscode.InlineCompletionItemProviderMetadata | undefined): vscode.Disposable {
2577
const eventHandle = typeof provider.onDidChange === 'function' && isProposedApiEnabled(extension, 'inlineCompletionsAdditions') ? this._nextHandle() : undefined;
2578
const adapter = new InlineCompletionAdapter(extension, this._documents, provider, this._commands.converter);
2579
const handle = this._addNewAdapter(adapter, extension);
2580
let result = this._createDisposable(handle);
2581
2582
if (eventHandle !== undefined) {
2583
const subscription = provider.onDidChange!(_ => this._proxy.$emitInlineCompletionsChange(eventHandle));
2584
result = Disposable.from(result, subscription);
2585
}
2586
2587
this._proxy.$registerInlineCompletionsSupport(
2588
handle,
2589
this._transformDocumentSelector(selector, extension),
2590
adapter.supportsHandleEvents,
2591
ExtensionIdentifier.toKey(extension.identifier.value),
2592
extension.version,
2593
metadata?.groupId ? ExtensionIdentifier.toKey(metadata.groupId) : undefined,
2594
metadata?.yieldTo?.map(extId => ExtensionIdentifier.toKey(extId)) || [],
2595
metadata?.displayName,
2596
metadata?.debounceDelayMs,
2597
metadata?.excludes?.map(extId => ExtensionIdentifier.toKey(extId)) || [],
2598
eventHandle,
2599
);
2600
return result;
2601
}
2602
2603
$provideInlineCompletions(handle: number, resource: UriComponents, position: IPosition, context: languages.InlineCompletionContext, token: CancellationToken): Promise<extHostProtocol.IdentifiableInlineCompletions | undefined> {
2604
return this._withAdapter(handle, InlineCompletionAdapter, adapter => adapter.provideInlineCompletions(URI.revive(resource), position, context, token), undefined, undefined);
2605
}
2606
2607
$handleInlineCompletionDidShow(handle: number, pid: number, idx: number, updatedInsertText: string): void {
2608
this._withAdapter(handle, InlineCompletionAdapter, async adapter => {
2609
adapter.handleDidShowCompletionItem(pid, idx, updatedInsertText);
2610
}, undefined, undefined);
2611
}
2612
2613
$handleInlineCompletionPartialAccept(handle: number, pid: number, idx: number, acceptedCharacters: number, info: languages.PartialAcceptInfo): void {
2614
this._withAdapter(handle, InlineCompletionAdapter, async adapter => {
2615
adapter.handlePartialAccept(pid, idx, acceptedCharacters, info);
2616
}, undefined, undefined);
2617
}
2618
2619
$handleInlineCompletionEndOfLifetime(handle: number, pid: number, idx: number, reason: languages.InlineCompletionEndOfLifeReason<{ pid: number; idx: number }>): void {
2620
this._withAdapter(handle, InlineCompletionAdapter, async adapter => {
2621
adapter.handleEndOfLifetime(pid, idx, reason);
2622
}, undefined, undefined);
2623
}
2624
2625
$handleInlineCompletionRejection(handle: number, pid: number, idx: number): void {
2626
this._withAdapter(handle, InlineCompletionAdapter, async adapter => {
2627
adapter.handleRejection(pid, idx);
2628
}, undefined, undefined);
2629
}
2630
2631
$freeInlineCompletionsList(handle: number, pid: number, reason: languages.InlineCompletionsDisposeReason): void {
2632
this._withAdapter(handle, InlineCompletionAdapter, async adapter => { adapter.disposeCompletions(pid, reason); }, undefined, undefined);
2633
}
2634
2635
$acceptInlineCompletionsUnificationState(state: IInlineCompletionsUnificationState): void {
2636
this._inlineCompletionsUnificationState = state;
2637
this._onDidChangeInlineCompletionsUnificationState.fire();
2638
}
2639
2640
// --- parameter hints
2641
2642
registerSignatureHelpProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.SignatureHelpProvider, metadataOrTriggerChars: string[] | vscode.SignatureHelpProviderMetadata): vscode.Disposable {
2643
const metadata: extHostProtocol.ISignatureHelpProviderMetadataDto | undefined = Array.isArray(metadataOrTriggerChars)
2644
? { triggerCharacters: metadataOrTriggerChars, retriggerCharacters: [] }
2645
: metadataOrTriggerChars;
2646
2647
const handle = this._addNewAdapter(new SignatureHelpAdapter(this._documents, provider), extension);
2648
this._proxy.$registerSignatureHelpProvider(handle, this._transformDocumentSelector(selector, extension), metadata);
2649
return this._createDisposable(handle);
2650
}
2651
2652
$provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: extHostProtocol.ISignatureHelpContextDto, token: CancellationToken): Promise<extHostProtocol.ISignatureHelpDto | undefined> {
2653
return this._withAdapter(handle, SignatureHelpAdapter, adapter => adapter.provideSignatureHelp(URI.revive(resource), position, context, token), undefined, token);
2654
}
2655
2656
$releaseSignatureHelp(handle: number, id: number): void {
2657
this._withAdapter(handle, SignatureHelpAdapter, adapter => adapter.releaseSignatureHelp(id), undefined, undefined);
2658
}
2659
2660
// --- inline hints
2661
2662
registerInlayHintsProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.InlayHintsProvider): vscode.Disposable {
2663
2664
const eventHandle = typeof provider.onDidChangeInlayHints === 'function' ? this._nextHandle() : undefined;
2665
const handle = this._addNewAdapter(new InlayHintsAdapter(this._documents, this._commands.converter, provider, this._logService, extension), extension);
2666
2667
this._proxy.$registerInlayHintsProvider(handle, this._transformDocumentSelector(selector, extension), typeof provider.resolveInlayHint === 'function', eventHandle, ExtHostLanguageFeatures._extLabel(extension));
2668
let result = this._createDisposable(handle);
2669
2670
if (eventHandle !== undefined) {
2671
const subscription = provider.onDidChangeInlayHints!(uri => this._proxy.$emitInlayHintsEvent(eventHandle));
2672
result = Disposable.from(result, subscription);
2673
}
2674
return result;
2675
}
2676
2677
$provideInlayHints(handle: number, resource: UriComponents, range: IRange, token: CancellationToken): Promise<extHostProtocol.IInlayHintsDto | undefined> {
2678
return this._withAdapter(handle, InlayHintsAdapter, adapter => adapter.provideInlayHints(URI.revive(resource), range, token), undefined, token);
2679
}
2680
2681
$resolveInlayHint(handle: number, id: extHostProtocol.ChainedCacheId, token: CancellationToken): Promise<extHostProtocol.IInlayHintDto | undefined> {
2682
return this._withAdapter(handle, InlayHintsAdapter, adapter => adapter.resolveInlayHint(id, token), undefined, token);
2683
}
2684
2685
$releaseInlayHints(handle: number, id: number): void {
2686
this._withAdapter(handle, InlayHintsAdapter, adapter => adapter.releaseHints(id), undefined, undefined);
2687
}
2688
2689
// --- links
2690
2691
registerDocumentLinkProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable {
2692
const handle = this._addNewAdapter(new LinkProviderAdapter(this._documents, provider), extension);
2693
this._proxy.$registerDocumentLinkProvider(handle, this._transformDocumentSelector(selector, extension), typeof provider.resolveDocumentLink === 'function');
2694
return this._createDisposable(handle);
2695
}
2696
2697
$provideDocumentLinks(handle: number, resource: UriComponents, token: CancellationToken): Promise<extHostProtocol.ILinksListDto | undefined> {
2698
return this._withAdapter(handle, LinkProviderAdapter, adapter => adapter.provideLinks(URI.revive(resource), token), undefined, token, resource.scheme === 'output');
2699
}
2700
2701
$resolveDocumentLink(handle: number, id: extHostProtocol.ChainedCacheId, token: CancellationToken): Promise<extHostProtocol.ILinkDto | undefined> {
2702
return this._withAdapter(handle, LinkProviderAdapter, adapter => adapter.resolveLink(id, token), undefined, undefined, true);
2703
}
2704
2705
$releaseDocumentLinks(handle: number, id: number): void {
2706
this._withAdapter(handle, LinkProviderAdapter, adapter => adapter.releaseLinks(id), undefined, undefined, true);
2707
}
2708
2709
registerColorProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider): vscode.Disposable {
2710
const handle = this._addNewAdapter(new ColorProviderAdapter(this._documents, provider), extension);
2711
this._proxy.$registerDocumentColorProvider(handle, this._transformDocumentSelector(selector, extension));
2712
return this._createDisposable(handle);
2713
}
2714
2715
$provideDocumentColors(handle: number, resource: UriComponents, token: CancellationToken): Promise<extHostProtocol.IRawColorInfo[]> {
2716
return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColors(URI.revive(resource), token), [], token);
2717
}
2718
2719
$provideColorPresentations(handle: number, resource: UriComponents, colorInfo: extHostProtocol.IRawColorInfo, token: CancellationToken): Promise<languages.IColorPresentation[] | undefined> {
2720
return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColorPresentations(URI.revive(resource), colorInfo, token), undefined, token);
2721
}
2722
2723
registerFoldingRangeProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.FoldingRangeProvider): vscode.Disposable {
2724
const handle = this._nextHandle();
2725
const eventHandle = typeof provider.onDidChangeFoldingRanges === 'function' ? this._nextHandle() : undefined;
2726
2727
this._adapter.set(handle, new AdapterData(new FoldingProviderAdapter(this._documents, provider), extension));
2728
this._proxy.$registerFoldingRangeProvider(handle, this._transformDocumentSelector(selector, extension), extension.identifier, eventHandle);
2729
let result = this._createDisposable(handle);
2730
2731
if (eventHandle !== undefined) {
2732
const subscription = provider.onDidChangeFoldingRanges!(() => this._proxy.$emitFoldingRangeEvent(eventHandle));
2733
result = Disposable.from(result, subscription);
2734
}
2735
2736
return result;
2737
}
2738
2739
$provideFoldingRanges(handle: number, resource: UriComponents, context: vscode.FoldingContext, token: CancellationToken): Promise<languages.FoldingRange[] | undefined> {
2740
return this._withAdapter(
2741
handle,
2742
FoldingProviderAdapter,
2743
(adapter) =>
2744
adapter.provideFoldingRanges(URI.revive(resource), context, token),
2745
undefined,
2746
token
2747
);
2748
}
2749
2750
// --- smart select
2751
2752
registerSelectionRangeProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.SelectionRangeProvider): vscode.Disposable {
2753
const handle = this._addNewAdapter(new SelectionRangeAdapter(this._documents, provider, this._logService), extension);
2754
this._proxy.$registerSelectionRangeProvider(handle, this._transformDocumentSelector(selector, extension));
2755
return this._createDisposable(handle);
2756
}
2757
2758
$provideSelectionRanges(handle: number, resource: UriComponents, positions: IPosition[], token: CancellationToken): Promise<languages.SelectionRange[][]> {
2759
return this._withAdapter(handle, SelectionRangeAdapter, adapter => adapter.provideSelectionRanges(URI.revive(resource), positions, token), [], token);
2760
}
2761
2762
// --- call hierarchy
2763
2764
registerCallHierarchyProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CallHierarchyProvider): vscode.Disposable {
2765
const handle = this._addNewAdapter(new CallHierarchyAdapter(this._documents, provider), extension);
2766
this._proxy.$registerCallHierarchyProvider(handle, this._transformDocumentSelector(selector, extension));
2767
return this._createDisposable(handle);
2768
}
2769
2770
$prepareCallHierarchy(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<extHostProtocol.ICallHierarchyItemDto[] | undefined> {
2771
return this._withAdapter(handle, CallHierarchyAdapter, adapter => Promise.resolve(adapter.prepareSession(URI.revive(resource), position, token)), undefined, token);
2772
}
2773
2774
$provideCallHierarchyIncomingCalls(handle: number, sessionId: string, itemId: string, token: CancellationToken): Promise<extHostProtocol.IIncomingCallDto[] | undefined> {
2775
return this._withAdapter(handle, CallHierarchyAdapter, adapter => adapter.provideCallsTo(sessionId, itemId, token), undefined, token);
2776
}
2777
2778
$provideCallHierarchyOutgoingCalls(handle: number, sessionId: string, itemId: string, token: CancellationToken): Promise<extHostProtocol.IOutgoingCallDto[] | undefined> {
2779
return this._withAdapter(handle, CallHierarchyAdapter, adapter => adapter.provideCallsFrom(sessionId, itemId, token), undefined, token);
2780
}
2781
2782
$releaseCallHierarchy(handle: number, sessionId: string): void {
2783
this._withAdapter(handle, CallHierarchyAdapter, adapter => Promise.resolve(adapter.releaseSession(sessionId)), undefined, undefined);
2784
}
2785
2786
// --- type hierarchy
2787
registerTypeHierarchyProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.TypeHierarchyProvider): vscode.Disposable {
2788
const handle = this._addNewAdapter(new TypeHierarchyAdapter(this._documents, provider), extension);
2789
this._proxy.$registerTypeHierarchyProvider(handle, this._transformDocumentSelector(selector, extension));
2790
return this._createDisposable(handle);
2791
}
2792
2793
$prepareTypeHierarchy(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<extHostProtocol.ITypeHierarchyItemDto[] | undefined> {
2794
return this._withAdapter(handle, TypeHierarchyAdapter, adapter => Promise.resolve(adapter.prepareSession(URI.revive(resource), position, token)), undefined, token);
2795
}
2796
2797
$provideTypeHierarchySupertypes(handle: number, sessionId: string, itemId: string, token: CancellationToken): Promise<extHostProtocol.ITypeHierarchyItemDto[] | undefined> {
2798
return this._withAdapter(handle, TypeHierarchyAdapter, adapter => adapter.provideSupertypes(sessionId, itemId, token), undefined, token);
2799
}
2800
2801
$provideTypeHierarchySubtypes(handle: number, sessionId: string, itemId: string, token: CancellationToken): Promise<extHostProtocol.ITypeHierarchyItemDto[] | undefined> {
2802
return this._withAdapter(handle, TypeHierarchyAdapter, adapter => adapter.provideSubtypes(sessionId, itemId, token), undefined, token);
2803
}
2804
2805
$releaseTypeHierarchy(handle: number, sessionId: string): void {
2806
this._withAdapter(handle, TypeHierarchyAdapter, adapter => Promise.resolve(adapter.releaseSession(sessionId)), undefined, undefined);
2807
}
2808
2809
// --- Document on drop
2810
2811
registerDocumentOnDropEditProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentDropEditProvider, metadata?: vscode.DocumentDropEditProviderMetadata) {
2812
const handle = this._nextHandle();
2813
this._adapter.set(handle, new AdapterData(new DocumentDropEditAdapter(this._proxy, this._documents, provider, handle, extension), extension));
2814
2815
this._proxy.$registerDocumentOnDropEditProvider(handle, this._transformDocumentSelector(selector, extension), metadata ? {
2816
supportsResolve: !!provider.resolveDocumentDropEdit,
2817
dropMimeTypes: metadata.dropMimeTypes,
2818
providedDropKinds: metadata.providedDropEditKinds?.map(x => x.value),
2819
} : undefined);
2820
2821
return this._createDisposable(handle);
2822
}
2823
2824
$provideDocumentOnDropEdits(handle: number, requestId: number, resource: UriComponents, position: IPosition, dataTransferDto: extHostProtocol.DataTransferDTO, token: CancellationToken): Promise<extHostProtocol.IDocumentDropEditDto[] | undefined> {
2825
return this._withAdapter(handle, DocumentDropEditAdapter, adapter =>
2826
Promise.resolve(adapter.provideDocumentOnDropEdits(requestId, URI.revive(resource), position, dataTransferDto, token)), undefined, undefined);
2827
}
2828
2829
$resolveDropEdit(handle: number, id: extHostProtocol.ChainedCacheId, token: CancellationToken): Promise<{ additionalEdit?: extHostProtocol.IWorkspaceEditDto }> {
2830
return this._withAdapter(handle, DocumentDropEditAdapter, adapter => adapter.resolveDropEdit(id, token), {}, undefined);
2831
}
2832
2833
$releaseDocumentOnDropEdits(handle: number, cacheId: number): void {
2834
this._withAdapter(handle, DocumentDropEditAdapter, adapter => Promise.resolve(adapter.releaseDropEdits(cacheId)), undefined, undefined);
2835
}
2836
2837
// --- copy/paste actions
2838
2839
registerDocumentPasteEditProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentPasteEditProvider, metadata: vscode.DocumentPasteProviderMetadata): vscode.Disposable {
2840
const handle = this._nextHandle();
2841
this._adapter.set(handle, new AdapterData(new DocumentPasteEditProvider(this._proxy, this._documents, provider, handle, extension), extension));
2842
this._proxy.$registerPasteEditProvider(handle, this._transformDocumentSelector(selector, extension), {
2843
supportsCopy: !!provider.prepareDocumentPaste,
2844
supportsPaste: !!provider.provideDocumentPasteEdits,
2845
supportsResolve: !!provider.resolveDocumentPasteEdit,
2846
providedPasteEditKinds: metadata.providedPasteEditKinds?.map(x => x.value),
2847
copyMimeTypes: metadata.copyMimeTypes,
2848
pasteMimeTypes: metadata.pasteMimeTypes,
2849
});
2850
return this._createDisposable(handle);
2851
}
2852
2853
$prepareDocumentPaste(handle: number, resource: UriComponents, ranges: IRange[], dataTransfer: extHostProtocol.DataTransferDTO, token: CancellationToken): Promise<extHostProtocol.DataTransferDTO | undefined> {
2854
return this._withAdapter(handle, DocumentPasteEditProvider, adapter => adapter.prepareDocumentPaste(URI.revive(resource), ranges, dataTransfer, token), undefined, token);
2855
}
2856
2857
$providePasteEdits(handle: number, requestId: number, resource: UriComponents, ranges: IRange[], dataTransferDto: extHostProtocol.DataTransferDTO, context: extHostProtocol.IDocumentPasteContextDto, token: CancellationToken): Promise<extHostProtocol.IPasteEditDto[] | undefined> {
2858
return this._withAdapter(handle, DocumentPasteEditProvider, adapter => adapter.providePasteEdits(requestId, URI.revive(resource), ranges, dataTransferDto, context, token), undefined, token);
2859
}
2860
2861
$resolvePasteEdit(handle: number, id: extHostProtocol.ChainedCacheId, token: CancellationToken): Promise<{ additionalEdit?: extHostProtocol.IWorkspaceEditDto }> {
2862
return this._withAdapter(handle, DocumentPasteEditProvider, adapter => adapter.resolvePasteEdit(id, token), {}, undefined);
2863
}
2864
2865
$releasePasteEdits(handle: number, cacheId: number): void {
2866
this._withAdapter(handle, DocumentPasteEditProvider, adapter => Promise.resolve(adapter.releasePasteEdits(cacheId)), undefined, undefined);
2867
}
2868
2869
// --- configuration
2870
2871
private static _serializeRegExp(regExp: RegExp): extHostProtocol.IRegExpDto {
2872
return {
2873
pattern: regExp.source,
2874
flags: regExp.flags,
2875
};
2876
}
2877
2878
private static _serializeIndentationRule(indentationRule: vscode.IndentationRule): extHostProtocol.IIndentationRuleDto {
2879
return {
2880
decreaseIndentPattern: ExtHostLanguageFeatures._serializeRegExp(indentationRule.decreaseIndentPattern),
2881
increaseIndentPattern: ExtHostLanguageFeatures._serializeRegExp(indentationRule.increaseIndentPattern),
2882
indentNextLinePattern: indentationRule.indentNextLinePattern ? ExtHostLanguageFeatures._serializeRegExp(indentationRule.indentNextLinePattern) : undefined,
2883
unIndentedLinePattern: indentationRule.unIndentedLinePattern ? ExtHostLanguageFeatures._serializeRegExp(indentationRule.unIndentedLinePattern) : undefined,
2884
};
2885
}
2886
2887
private static _serializeOnEnterRule(onEnterRule: vscode.OnEnterRule): extHostProtocol.IOnEnterRuleDto {
2888
return {
2889
beforeText: ExtHostLanguageFeatures._serializeRegExp(onEnterRule.beforeText),
2890
afterText: onEnterRule.afterText ? ExtHostLanguageFeatures._serializeRegExp(onEnterRule.afterText) : undefined,
2891
previousLineText: onEnterRule.previousLineText ? ExtHostLanguageFeatures._serializeRegExp(onEnterRule.previousLineText) : undefined,
2892
action: onEnterRule.action
2893
};
2894
}
2895
2896
private static _serializeOnEnterRules(onEnterRules: vscode.OnEnterRule[]): extHostProtocol.IOnEnterRuleDto[] {
2897
return onEnterRules.map(ExtHostLanguageFeatures._serializeOnEnterRule);
2898
}
2899
2900
private static _serializeAutoClosingPair(autoClosingPair: vscode.AutoClosingPair): IAutoClosingPairConditional {
2901
return {
2902
open: autoClosingPair.open,
2903
close: autoClosingPair.close,
2904
notIn: autoClosingPair.notIn ? autoClosingPair.notIn.map(v => SyntaxTokenType.toString(v)) : undefined,
2905
};
2906
}
2907
2908
private static _serializeAutoClosingPairs(autoClosingPairs: vscode.AutoClosingPair[]): IAutoClosingPairConditional[] {
2909
return autoClosingPairs.map(ExtHostLanguageFeatures._serializeAutoClosingPair);
2910
}
2911
2912
setLanguageConfiguration(extension: IExtensionDescription, languageId: string, configuration: vscode.LanguageConfiguration): vscode.Disposable {
2913
const { wordPattern } = configuration;
2914
2915
// check for a valid word pattern
2916
if (wordPattern && regExpLeadsToEndlessLoop(wordPattern)) {
2917
throw new Error(`Invalid language configuration: wordPattern '${wordPattern}' is not allowed to match the empty string.`);
2918
}
2919
2920
// word definition
2921
if (wordPattern) {
2922
this._documents.setWordDefinitionFor(languageId, wordPattern);
2923
} else {
2924
this._documents.setWordDefinitionFor(languageId, undefined);
2925
}
2926
2927
if (configuration.__electricCharacterSupport) {
2928
this._apiDeprecation.report('LanguageConfiguration.__electricCharacterSupport', extension,
2929
`Do not use.`);
2930
}
2931
2932
if (configuration.__characterPairSupport) {
2933
this._apiDeprecation.report('LanguageConfiguration.__characterPairSupport', extension,
2934
`Do not use.`);
2935
}
2936
2937
const handle = this._nextHandle();
2938
const serializedConfiguration: extHostProtocol.ILanguageConfigurationDto = {
2939
comments: configuration.comments,
2940
brackets: configuration.brackets,
2941
wordPattern: configuration.wordPattern ? ExtHostLanguageFeatures._serializeRegExp(configuration.wordPattern) : undefined,
2942
indentationRules: configuration.indentationRules ? ExtHostLanguageFeatures._serializeIndentationRule(configuration.indentationRules) : undefined,
2943
onEnterRules: configuration.onEnterRules ? ExtHostLanguageFeatures._serializeOnEnterRules(configuration.onEnterRules) : undefined,
2944
__electricCharacterSupport: configuration.__electricCharacterSupport,
2945
__characterPairSupport: configuration.__characterPairSupport,
2946
autoClosingPairs: configuration.autoClosingPairs ? ExtHostLanguageFeatures._serializeAutoClosingPairs(configuration.autoClosingPairs) : undefined,
2947
};
2948
2949
this._proxy.$setLanguageConfiguration(handle, languageId, serializedConfiguration);
2950
return this._createDisposable(handle);
2951
}
2952
2953
$setWordDefinitions(wordDefinitions: extHostProtocol.ILanguageWordDefinitionDto[]): void {
2954
for (const wordDefinition of wordDefinitions) {
2955
this._documents.setWordDefinitionFor(wordDefinition.languageId, new RegExp(wordDefinition.regexSource, wordDefinition.regexFlags));
2956
}
2957
}
2958
}
2959
2960