Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.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 assert from 'assert';
7
import { TestInstantiationService } from '../../../../platform/instantiation/test/common/instantiationServiceMock.js';
8
import { setUnexpectedErrorHandler, errorHandler } from '../../../../base/common/errors.js';
9
import { URI } from '../../../../base/common/uri.js';
10
import * as types from '../../common/extHostTypes.js';
11
import { createTextModel } from '../../../../editor/test/common/testTextModel.js';
12
import { Position as EditorPosition, Position } from '../../../../editor/common/core/position.js';
13
import { Range as EditorRange } from '../../../../editor/common/core/range.js';
14
import { TestRPCProtocol } from '../common/testRPCProtocol.js';
15
import { IMarkerService } from '../../../../platform/markers/common/markers.js';
16
import { MarkerService } from '../../../../platform/markers/common/markerService.js';
17
import { ExtHostLanguageFeatures } from '../../common/extHostLanguageFeatures.js';
18
import { MainThreadLanguageFeatures } from '../../browser/mainThreadLanguageFeatures.js';
19
import { ExtHostCommands } from '../../common/extHostCommands.js';
20
import { MainThreadCommands } from '../../browser/mainThreadCommands.js';
21
import { ExtHostDocuments } from '../../common/extHostDocuments.js';
22
import { ExtHostDocumentsAndEditors } from '../../common/extHostDocumentsAndEditors.js';
23
import * as languages from '../../../../editor/common/languages.js';
24
import { getCodeLensModel } from '../../../../editor/contrib/codelens/browser/codelens.js';
25
import { getDefinitionsAtPosition, getImplementationsAtPosition, getTypeDefinitionsAtPosition, getDeclarationsAtPosition, getReferencesAtPosition } from '../../../../editor/contrib/gotoSymbol/browser/goToSymbol.js';
26
import { getHoversPromise } from '../../../../editor/contrib/hover/browser/getHover.js';
27
import { getOccurrencesAtPosition } from '../../../../editor/contrib/wordHighlighter/browser/wordHighlighter.js';
28
import { getCodeActions } from '../../../../editor/contrib/codeAction/browser/codeAction.js';
29
import { getWorkspaceSymbols } from '../../../contrib/search/common/search.js';
30
import { rename } from '../../../../editor/contrib/rename/browser/rename.js';
31
import { provideSignatureHelp } from '../../../../editor/contrib/parameterHints/browser/provideSignatureHelp.js';
32
import { provideSuggestionItems, CompletionOptions } from '../../../../editor/contrib/suggest/browser/suggest.js';
33
import { getDocumentFormattingEditsUntilResult, getDocumentRangeFormattingEditsUntilResult, getOnTypeFormattingEdits } from '../../../../editor/contrib/format/browser/format.js';
34
import { getLinks } from '../../../../editor/contrib/links/browser/getLinks.js';
35
import { MainContext, ExtHostContext } from '../../common/extHost.protocol.js';
36
import { ExtHostDiagnostics } from '../../common/extHostDiagnostics.js';
37
import type * as vscode from 'vscode';
38
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
39
import { NullLogService } from '../../../../platform/log/common/log.js';
40
import { ITextModel, EndOfLineSequence } from '../../../../editor/common/model.js';
41
import { getColors } from '../../../../editor/contrib/colorPicker/browser/color.js';
42
import { CancellationToken } from '../../../../base/common/cancellation.js';
43
import { nullExtensionDescription as defaultExtension } from '../../../services/extensions/common/extensions.js';
44
import { provideSelectionRanges } from '../../../../editor/contrib/smartSelect/browser/smartSelect.js';
45
import { mock } from '../../../../base/test/common/mock.js';
46
import { IEditorWorkerService } from '../../../../editor/common/services/editorWorker.js';
47
import { DisposableStore } from '../../../../base/common/lifecycle.js';
48
import { NullApiDeprecationService } from '../../common/extHostApiDeprecationService.js';
49
import { Progress } from '../../../../platform/progress/common/progress.js';
50
import { IExtHostFileSystemInfo } from '../../common/extHostFileSystemInfo.js';
51
import { URITransformerService } from '../../common/extHostUriTransformerService.js';
52
import { OutlineModel } from '../../../../editor/contrib/documentSymbols/browser/outlineModel.js';
53
import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js';
54
import { LanguageFeaturesService } from '../../../../editor/common/services/languageFeaturesService.js';
55
import { CodeActionTriggerSource } from '../../../../editor/contrib/codeAction/common/types.js';
56
import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js';
57
import { IExtHostTelemetry } from '../../common/extHostTelemetry.js';
58
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
59
import { runWithFakedTimers } from '../../../../base/test/common/timeTravelScheduler.js';
60
61
suite('ExtHostLanguageFeatures', function () {
62
63
const defaultSelector = { scheme: 'far' };
64
let model: ITextModel;
65
let extHost: ExtHostLanguageFeatures;
66
let mainThread: MainThreadLanguageFeatures;
67
const disposables = new DisposableStore();
68
let rpcProtocol: TestRPCProtocol;
69
let languageFeaturesService: ILanguageFeaturesService;
70
let originalErrorHandler: (e: any) => any;
71
let instantiationService: TestInstantiationService;
72
73
setup(() => {
74
75
model = createTextModel(
76
[
77
'This is the first line',
78
'This is the second line',
79
'This is the third line',
80
].join('\n'),
81
undefined,
82
undefined,
83
URI.parse('far://testing/file.a'));
84
85
rpcProtocol = new TestRPCProtocol();
86
87
languageFeaturesService = new LanguageFeaturesService();
88
89
// Use IInstantiationService to get typechecking when instantiating
90
let inst: IInstantiationService;
91
{
92
instantiationService = new TestInstantiationService();
93
instantiationService.stub(IMarkerService, MarkerService);
94
instantiationService.set(ILanguageFeaturesService, languageFeaturesService);
95
instantiationService.set(IUriIdentityService, new class extends mock<IUriIdentityService>() {
96
override asCanonicalUri(uri: URI): URI {
97
return uri;
98
}
99
});
100
inst = instantiationService;
101
}
102
103
originalErrorHandler = errorHandler.getUnexpectedErrorHandler();
104
setUnexpectedErrorHandler(() => { });
105
106
const extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(rpcProtocol, new NullLogService());
107
extHostDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({
108
addedDocuments: [{
109
isDirty: false,
110
versionId: model.getVersionId(),
111
languageId: model.getLanguageId(),
112
uri: model.uri,
113
lines: model.getValue().split(model.getEOL()),
114
EOL: model.getEOL(),
115
encoding: 'utf8'
116
}]
117
});
118
const extHostDocuments = new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors);
119
rpcProtocol.set(ExtHostContext.ExtHostDocuments, extHostDocuments);
120
121
const commands = new ExtHostCommands(rpcProtocol, new NullLogService(), new class extends mock<IExtHostTelemetry>() {
122
override onExtensionError(): boolean {
123
return true;
124
}
125
});
126
rpcProtocol.set(ExtHostContext.ExtHostCommands, commands);
127
rpcProtocol.set(MainContext.MainThreadCommands, disposables.add(inst.createInstance(MainThreadCommands, rpcProtocol)));
128
129
const diagnostics = new ExtHostDiagnostics(rpcProtocol, new NullLogService(), new class extends mock<IExtHostFileSystemInfo>() { }, extHostDocumentsAndEditors);
130
rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, diagnostics);
131
132
extHost = new ExtHostLanguageFeatures(rpcProtocol, new URITransformerService(null), extHostDocuments, commands, diagnostics, new NullLogService(), NullApiDeprecationService, new class extends mock<IExtHostTelemetry>() {
133
override onExtensionError(): boolean {
134
return true;
135
}
136
});
137
rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, extHost);
138
139
mainThread = rpcProtocol.set(MainContext.MainThreadLanguageFeatures, disposables.add(inst.createInstance(MainThreadLanguageFeatures, rpcProtocol)));
140
});
141
142
teardown(() => {
143
disposables.clear();
144
145
setUnexpectedErrorHandler(originalErrorHandler);
146
model.dispose();
147
mainThread.dispose();
148
instantiationService.dispose();
149
150
return rpcProtocol.sync();
151
});
152
153
ensureNoDisposablesAreLeakedInTestSuite();
154
155
// --- outline
156
157
test('DocumentSymbols, register/deregister', async () => {
158
assert.strictEqual(languageFeaturesService.documentSymbolProvider.all(model).length, 0);
159
const d1 = extHost.registerDocumentSymbolProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentSymbolProvider {
160
provideDocumentSymbols() {
161
return <vscode.SymbolInformation[]>[];
162
}
163
});
164
165
await rpcProtocol.sync();
166
assert.strictEqual(languageFeaturesService.documentSymbolProvider.all(model).length, 1);
167
d1.dispose();
168
return rpcProtocol.sync();
169
170
});
171
172
test('DocumentSymbols, evil provider', async () => {
173
disposables.add(extHost.registerDocumentSymbolProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentSymbolProvider {
174
provideDocumentSymbols(): any {
175
throw new Error('evil document symbol provider');
176
}
177
}));
178
disposables.add(extHost.registerDocumentSymbolProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentSymbolProvider {
179
provideDocumentSymbols(): any {
180
return [new types.SymbolInformation('test', types.SymbolKind.Field, new types.Range(0, 0, 0, 0))];
181
}
182
}));
183
184
await rpcProtocol.sync();
185
const value = (await OutlineModel.create(languageFeaturesService.documentSymbolProvider, model, CancellationToken.None)).asListOfDocumentSymbols();
186
assert.strictEqual(value.length, 1);
187
});
188
189
test('DocumentSymbols, data conversion', async () => {
190
disposables.add(extHost.registerDocumentSymbolProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentSymbolProvider {
191
provideDocumentSymbols(): any {
192
return [new types.SymbolInformation('test', types.SymbolKind.Field, new types.Range(0, 0, 0, 0))];
193
}
194
}));
195
196
await rpcProtocol.sync();
197
const value = (await OutlineModel.create(languageFeaturesService.documentSymbolProvider, model, CancellationToken.None)).asListOfDocumentSymbols();
198
assert.strictEqual(value.length, 1);
199
const entry = value[0];
200
assert.strictEqual(entry.name, 'test');
201
assert.deepStrictEqual(entry.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 });
202
});
203
204
test('Quick Outline uses a not ideal sorting, #138502', async function () {
205
const symbols = [
206
{ name: 'containers', range: { startLineNumber: 1, startColumn: 1, endLineNumber: 4, endColumn: 26 } },
207
{ name: 'container 0', range: { startLineNumber: 2, startColumn: 5, endLineNumber: 5, endColumn: 1 } },
208
{ name: 'name', range: { startLineNumber: 2, startColumn: 5, endLineNumber: 2, endColumn: 16 } },
209
{ name: 'ports', range: { startLineNumber: 3, startColumn: 5, endLineNumber: 5, endColumn: 1 } },
210
{ name: 'ports 0', range: { startLineNumber: 4, startColumn: 9, endLineNumber: 4, endColumn: 26 } },
211
{ name: 'containerPort', range: { startLineNumber: 4, startColumn: 9, endLineNumber: 4, endColumn: 26 } }
212
];
213
214
disposables.add(extHost.registerDocumentSymbolProvider(defaultExtension, defaultSelector, {
215
provideDocumentSymbols: (doc, token): any => {
216
return symbols.map(s => {
217
return new types.SymbolInformation(
218
s.name,
219
types.SymbolKind.Object,
220
new types.Range(s.range.startLineNumber - 1, s.range.startColumn - 1, s.range.endLineNumber - 1, s.range.endColumn - 1)
221
);
222
});
223
}
224
}));
225
226
await rpcProtocol.sync();
227
228
const value = (await OutlineModel.create(languageFeaturesService.documentSymbolProvider, model, CancellationToken.None)).asListOfDocumentSymbols();
229
230
assert.strictEqual(value.length, 6);
231
assert.deepStrictEqual(value.map(s => s.name), ['containers', 'container 0', 'name', 'ports', 'ports 0', 'containerPort']);
232
});
233
234
// --- code lens
235
236
test('CodeLens, evil provider', async () => {
237
return runWithFakedTimers({ useFakeTimers: true }, async () => {
238
disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider {
239
provideCodeLenses(): any {
240
throw new Error('evil');
241
}
242
}));
243
disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider {
244
provideCodeLenses() {
245
return [new types.CodeLens(new types.Range(0, 0, 0, 0))];
246
}
247
}));
248
249
await rpcProtocol.sync();
250
const value = await getCodeLensModel(languageFeaturesService.codeLensProvider, model, CancellationToken.None);
251
assert.strictEqual(value.lenses.length, 1);
252
value.dispose();
253
});
254
});
255
256
test('CodeLens, do not resolve a resolved lens', async () => {
257
return runWithFakedTimers({ useFakeTimers: true }, async () => {
258
disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider {
259
provideCodeLenses(): any {
260
return [new types.CodeLens(
261
new types.Range(0, 0, 0, 0),
262
{ command: 'id', title: 'Title' })];
263
}
264
resolveCodeLens(): any {
265
assert.ok(false, 'do not resolve');
266
}
267
}));
268
269
await rpcProtocol.sync();
270
const value = await getCodeLensModel(languageFeaturesService.codeLensProvider, model, CancellationToken.None);
271
assert.strictEqual(value.lenses.length, 1);
272
const [data] = value.lenses;
273
const symbol = await Promise.resolve(data.provider.resolveCodeLens!(model, data.symbol, CancellationToken.None));
274
assert.strictEqual(symbol!.command!.id, 'id');
275
assert.strictEqual(symbol!.command!.title, 'Title');
276
value.dispose();
277
});
278
});
279
280
test('CodeLens, missing command', async () => {
281
return runWithFakedTimers({ useFakeTimers: true }, async () => {
282
disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider {
283
provideCodeLenses() {
284
return [new types.CodeLens(new types.Range(0, 0, 0, 0))];
285
}
286
}));
287
288
await rpcProtocol.sync();
289
const value = await getCodeLensModel(languageFeaturesService.codeLensProvider, model, CancellationToken.None);
290
assert.strictEqual(value.lenses.length, 1);
291
const [data] = value.lenses;
292
const symbol = await Promise.resolve(data.provider.resolveCodeLens!(model, data.symbol, CancellationToken.None));
293
assert.strictEqual(symbol, undefined);
294
value.dispose();
295
});
296
});
297
298
// --- definition
299
300
test('Definition, data conversion', async () => {
301
302
disposables.add(extHost.registerDefinitionProvider(defaultExtension, defaultSelector, new class implements vscode.DefinitionProvider {
303
provideDefinition(): any {
304
return [new types.Location(model.uri, new types.Range(1, 2, 3, 4))];
305
}
306
}));
307
308
await rpcProtocol.sync();
309
const value = await getDefinitionsAtPosition(languageFeaturesService.definitionProvider, model, new EditorPosition(1, 1), false, CancellationToken.None);
310
assert.strictEqual(value.length, 1);
311
const [entry] = value;
312
assert.deepStrictEqual(entry.range, { startLineNumber: 2, startColumn: 3, endLineNumber: 4, endColumn: 5 });
313
assert.strictEqual(entry.uri.toString(), model.uri.toString());
314
});
315
316
test('Definition, one or many', async () => {
317
318
disposables.add(extHost.registerDefinitionProvider(defaultExtension, defaultSelector, new class implements vscode.DefinitionProvider {
319
provideDefinition(): any {
320
return [new types.Location(model.uri, new types.Range(1, 1, 1, 1))];
321
}
322
}));
323
disposables.add(extHost.registerDefinitionProvider(defaultExtension, defaultSelector, new class implements vscode.DefinitionProvider {
324
provideDefinition(): any {
325
return new types.Location(model.uri, new types.Range(2, 1, 1, 1));
326
}
327
}));
328
329
await rpcProtocol.sync();
330
const value = await getDefinitionsAtPosition(languageFeaturesService.definitionProvider, model, new EditorPosition(1, 1), false, CancellationToken.None);
331
assert.strictEqual(value.length, 2);
332
});
333
334
test('Definition, registration order', async () => {
335
336
disposables.add(extHost.registerDefinitionProvider(defaultExtension, defaultSelector, new class implements vscode.DefinitionProvider {
337
provideDefinition(): any {
338
return [new types.Location(URI.parse('far://first'), new types.Range(2, 3, 4, 5))];
339
}
340
}));
341
342
disposables.add(extHost.registerDefinitionProvider(defaultExtension, defaultSelector, new class implements vscode.DefinitionProvider {
343
provideDefinition(): any {
344
return new types.Location(URI.parse('far://second'), new types.Range(1, 2, 3, 4));
345
}
346
}));
347
348
await rpcProtocol.sync();
349
const value = await getDefinitionsAtPosition(languageFeaturesService.definitionProvider, model, new EditorPosition(1, 1), false, CancellationToken.None);
350
assert.strictEqual(value.length, 2);
351
// let [first, second] = value;
352
assert.strictEqual(value[0].uri.authority, 'second');
353
assert.strictEqual(value[1].uri.authority, 'first');
354
});
355
356
test('Definition, evil provider', async () => {
357
358
disposables.add(extHost.registerDefinitionProvider(defaultExtension, defaultSelector, new class implements vscode.DefinitionProvider {
359
provideDefinition(): any {
360
throw new Error('evil provider');
361
}
362
}));
363
disposables.add(extHost.registerDefinitionProvider(defaultExtension, defaultSelector, new class implements vscode.DefinitionProvider {
364
provideDefinition(): any {
365
return new types.Location(model.uri, new types.Range(1, 1, 1, 1));
366
}
367
}));
368
369
await rpcProtocol.sync();
370
const value = await getDefinitionsAtPosition(languageFeaturesService.definitionProvider, model, new EditorPosition(1, 1), false, CancellationToken.None);
371
assert.strictEqual(value.length, 1);
372
});
373
374
// -- declaration
375
376
test('Declaration, data conversion', async () => {
377
378
disposables.add(extHost.registerDeclarationProvider(defaultExtension, defaultSelector, new class implements vscode.DeclarationProvider {
379
provideDeclaration(): any {
380
return [new types.Location(model.uri, new types.Range(1, 2, 3, 4))];
381
}
382
}));
383
384
await rpcProtocol.sync();
385
const value = await getDeclarationsAtPosition(languageFeaturesService.declarationProvider, model, new EditorPosition(1, 1), false, CancellationToken.None);
386
assert.strictEqual(value.length, 1);
387
const [entry] = value;
388
assert.deepStrictEqual(entry.range, { startLineNumber: 2, startColumn: 3, endLineNumber: 4, endColumn: 5 });
389
assert.strictEqual(entry.uri.toString(), model.uri.toString());
390
});
391
392
// --- implementation
393
394
test('Implementation, data conversion', async () => {
395
396
disposables.add(extHost.registerImplementationProvider(defaultExtension, defaultSelector, new class implements vscode.ImplementationProvider {
397
provideImplementation(): any {
398
return [new types.Location(model.uri, new types.Range(1, 2, 3, 4))];
399
}
400
}));
401
402
await rpcProtocol.sync();
403
const value = await getImplementationsAtPosition(languageFeaturesService.implementationProvider, model, new EditorPosition(1, 1), false, CancellationToken.None);
404
assert.strictEqual(value.length, 1);
405
const [entry] = value;
406
assert.deepStrictEqual(entry.range, { startLineNumber: 2, startColumn: 3, endLineNumber: 4, endColumn: 5 });
407
assert.strictEqual(entry.uri.toString(), model.uri.toString());
408
});
409
410
// --- type definition
411
412
test('Type Definition, data conversion', async () => {
413
414
disposables.add(extHost.registerTypeDefinitionProvider(defaultExtension, defaultSelector, new class implements vscode.TypeDefinitionProvider {
415
provideTypeDefinition(): any {
416
return [new types.Location(model.uri, new types.Range(1, 2, 3, 4))];
417
}
418
}));
419
420
await rpcProtocol.sync();
421
const value = await getTypeDefinitionsAtPosition(languageFeaturesService.typeDefinitionProvider, model, new EditorPosition(1, 1), false, CancellationToken.None);
422
assert.strictEqual(value.length, 1);
423
const [entry] = value;
424
assert.deepStrictEqual(entry.range, { startLineNumber: 2, startColumn: 3, endLineNumber: 4, endColumn: 5 });
425
assert.strictEqual(entry.uri.toString(), model.uri.toString());
426
});
427
428
// --- extra info
429
430
test('HoverProvider, word range at pos', async () => {
431
432
disposables.add(extHost.registerHoverProvider(defaultExtension, defaultSelector, new class implements vscode.HoverProvider {
433
provideHover(): any {
434
return new types.Hover('Hello');
435
}
436
}));
437
438
await rpcProtocol.sync();
439
const hovers = await getHoversPromise(languageFeaturesService.hoverProvider, model, new EditorPosition(1, 1), CancellationToken.None);
440
assert.strictEqual(hovers.length, 1);
441
const [entry] = hovers;
442
assert.deepStrictEqual(entry.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 5 });
443
});
444
445
446
test('HoverProvider, given range', async () => {
447
448
disposables.add(extHost.registerHoverProvider(defaultExtension, defaultSelector, new class implements vscode.HoverProvider {
449
provideHover(): any {
450
return new types.Hover('Hello', new types.Range(3, 0, 8, 7));
451
}
452
}));
453
454
await rpcProtocol.sync();
455
const hovers = await getHoversPromise(languageFeaturesService.hoverProvider, model, new EditorPosition(1, 1), CancellationToken.None);
456
assert.strictEqual(hovers.length, 1);
457
const [entry] = hovers;
458
assert.deepStrictEqual(entry.range, { startLineNumber: 4, startColumn: 1, endLineNumber: 9, endColumn: 8 });
459
});
460
461
462
test('HoverProvider, registration order', async () => {
463
disposables.add(extHost.registerHoverProvider(defaultExtension, defaultSelector, new class implements vscode.HoverProvider {
464
provideHover(): any {
465
return new types.Hover('registered first');
466
}
467
}));
468
469
470
disposables.add(extHost.registerHoverProvider(defaultExtension, defaultSelector, new class implements vscode.HoverProvider {
471
provideHover(): any {
472
return new types.Hover('registered second');
473
}
474
}));
475
476
await rpcProtocol.sync();
477
const value = await getHoversPromise(languageFeaturesService.hoverProvider, model, new EditorPosition(1, 1), CancellationToken.None);
478
assert.strictEqual(value.length, 2);
479
const [first, second] = value;
480
assert.strictEqual(first.contents[0].value, 'registered second');
481
assert.strictEqual(second.contents[0].value, 'registered first');
482
});
483
484
485
test('HoverProvider, evil provider', async () => {
486
487
disposables.add(extHost.registerHoverProvider(defaultExtension, defaultSelector, new class implements vscode.HoverProvider {
488
provideHover(): any {
489
throw new Error('evil');
490
}
491
}));
492
disposables.add(extHost.registerHoverProvider(defaultExtension, defaultSelector, new class implements vscode.HoverProvider {
493
provideHover(): any {
494
return new types.Hover('Hello');
495
}
496
}));
497
498
await rpcProtocol.sync();
499
const hovers = await getHoversPromise(languageFeaturesService.hoverProvider, model, new EditorPosition(1, 1), CancellationToken.None);
500
assert.strictEqual(hovers.length, 1);
501
});
502
503
// --- occurrences
504
505
test('Occurrences, data conversion', async () => {
506
507
disposables.add(extHost.registerDocumentHighlightProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentHighlightProvider {
508
provideDocumentHighlights(): any {
509
return [new types.DocumentHighlight(new types.Range(0, 0, 0, 4))];
510
}
511
}));
512
513
await rpcProtocol.sync();
514
const value = (await getOccurrencesAtPosition(languageFeaturesService.documentHighlightProvider, model, new EditorPosition(1, 2), CancellationToken.None))!;
515
assert.strictEqual(value.size, 1);
516
const [entry] = Array.from(value.values())[0];
517
assert.deepStrictEqual(entry.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 5 });
518
assert.strictEqual(entry.kind, languages.DocumentHighlightKind.Text);
519
});
520
521
test('Occurrences, order 1/2', async () => {
522
523
disposables.add(extHost.registerDocumentHighlightProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentHighlightProvider {
524
provideDocumentHighlights(): any {
525
return undefined;
526
}
527
}));
528
disposables.add(extHost.registerDocumentHighlightProvider(defaultExtension, '*', new class implements vscode.DocumentHighlightProvider {
529
provideDocumentHighlights(): any {
530
return [new types.DocumentHighlight(new types.Range(0, 0, 0, 4))];
531
}
532
}));
533
534
await rpcProtocol.sync();
535
const value = (await getOccurrencesAtPosition(languageFeaturesService.documentHighlightProvider, model, new EditorPosition(1, 2), CancellationToken.None))!;
536
assert.strictEqual(value.size, 1);
537
const [entry] = Array.from(value.values())[0];
538
assert.deepStrictEqual(entry.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 5 });
539
assert.strictEqual(entry.kind, languages.DocumentHighlightKind.Text);
540
});
541
542
test('Occurrences, order 2/2', async () => {
543
544
disposables.add(extHost.registerDocumentHighlightProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentHighlightProvider {
545
provideDocumentHighlights(): any {
546
return [new types.DocumentHighlight(new types.Range(0, 0, 0, 2))];
547
}
548
}));
549
disposables.add(extHost.registerDocumentHighlightProvider(defaultExtension, '*', new class implements vscode.DocumentHighlightProvider {
550
provideDocumentHighlights(): any {
551
return [new types.DocumentHighlight(new types.Range(0, 0, 0, 4))];
552
}
553
}));
554
555
await rpcProtocol.sync();
556
const value = (await getOccurrencesAtPosition(languageFeaturesService.documentHighlightProvider, model, new EditorPosition(1, 2), CancellationToken.None))!;
557
assert.strictEqual(value.size, 1);
558
const [entry] = Array.from(value.values())[0];
559
assert.deepStrictEqual(entry.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 3 });
560
assert.strictEqual(entry.kind, languages.DocumentHighlightKind.Text);
561
});
562
563
test('Occurrences, evil provider', async () => {
564
565
disposables.add(extHost.registerDocumentHighlightProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentHighlightProvider {
566
provideDocumentHighlights(): any {
567
throw new Error('evil');
568
}
569
}));
570
571
disposables.add(extHost.registerDocumentHighlightProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentHighlightProvider {
572
provideDocumentHighlights(): any {
573
return [new types.DocumentHighlight(new types.Range(0, 0, 0, 4))];
574
}
575
}));
576
577
await rpcProtocol.sync();
578
const value = await getOccurrencesAtPosition(languageFeaturesService.documentHighlightProvider, model, new EditorPosition(1, 2), CancellationToken.None);
579
assert.strictEqual(value!.size, 1);
580
});
581
582
// --- references
583
584
test('References, registration order', async () => {
585
586
disposables.add(extHost.registerReferenceProvider(defaultExtension, defaultSelector, new class implements vscode.ReferenceProvider {
587
provideReferences(): any {
588
return [new types.Location(URI.parse('far://register/first'), new types.Range(0, 0, 0, 0))];
589
}
590
}));
591
592
disposables.add(extHost.registerReferenceProvider(defaultExtension, defaultSelector, new class implements vscode.ReferenceProvider {
593
provideReferences(): any {
594
return [new types.Location(URI.parse('far://register/second'), new types.Range(0, 0, 0, 0))];
595
}
596
}));
597
598
await rpcProtocol.sync();
599
const value = await getReferencesAtPosition(languageFeaturesService.referenceProvider, model, new EditorPosition(1, 2), false, false, CancellationToken.None);
600
assert.strictEqual(value.length, 2);
601
const [first, second] = value;
602
assert.strictEqual(first.uri.path, '/second');
603
assert.strictEqual(second.uri.path, '/first');
604
});
605
606
test('References, data conversion', async () => {
607
608
disposables.add(extHost.registerReferenceProvider(defaultExtension, defaultSelector, new class implements vscode.ReferenceProvider {
609
provideReferences(): any {
610
return [new types.Location(model.uri, new types.Position(0, 0))];
611
}
612
}));
613
614
await rpcProtocol.sync();
615
const value = await getReferencesAtPosition(languageFeaturesService.referenceProvider, model, new EditorPosition(1, 2), false, false, CancellationToken.None);
616
assert.strictEqual(value.length, 1);
617
const [item] = value;
618
assert.deepStrictEqual(item.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 });
619
assert.strictEqual(item.uri.toString(), model.uri.toString());
620
});
621
622
test('References, evil provider', async () => {
623
624
disposables.add(extHost.registerReferenceProvider(defaultExtension, defaultSelector, new class implements vscode.ReferenceProvider {
625
provideReferences(): any {
626
throw new Error('evil');
627
}
628
}));
629
disposables.add(extHost.registerReferenceProvider(defaultExtension, defaultSelector, new class implements vscode.ReferenceProvider {
630
provideReferences(): any {
631
return [new types.Location(model.uri, new types.Range(0, 0, 0, 0))];
632
}
633
}));
634
635
await rpcProtocol.sync();
636
const value = await getReferencesAtPosition(languageFeaturesService.referenceProvider, model, new EditorPosition(1, 2), false, false, CancellationToken.None);
637
assert.strictEqual(value.length, 1);
638
});
639
640
// --- quick fix
641
642
test('Quick Fix, command data conversion', async () => {
643
return runWithFakedTimers({ useFakeTimers: true }, async () => {
644
disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, {
645
provideCodeActions(): vscode.Command[] {
646
return [
647
{ command: 'test1', title: 'Testing1' },
648
{ command: 'test2', title: 'Testing2' }
649
];
650
}
651
}));
652
653
await rpcProtocol.sync();
654
const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.QuickFix }, Progress.None, CancellationToken.None);
655
const { validActions: actions } = value;
656
assert.strictEqual(actions.length, 2);
657
const [first, second] = actions;
658
assert.strictEqual(first.action.title, 'Testing1');
659
assert.strictEqual(first.action.command!.id, 'test1');
660
assert.strictEqual(second.action.title, 'Testing2');
661
assert.strictEqual(second.action.command!.id, 'test2');
662
value.dispose();
663
});
664
});
665
666
test('Quick Fix, code action data conversion', async () => {
667
return runWithFakedTimers({ useFakeTimers: true }, async () => {
668
disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, {
669
provideCodeActions(): vscode.CodeAction[] {
670
return [
671
{
672
title: 'Testing1',
673
command: { title: 'Testing1Command', command: 'test1' },
674
kind: types.CodeActionKind.Empty.append('test.scope')
675
}
676
];
677
}
678
}));
679
680
await rpcProtocol.sync();
681
const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.Default }, Progress.None, CancellationToken.None);
682
const { validActions: actions } = value;
683
assert.strictEqual(actions.length, 1);
684
const [first] = actions;
685
assert.strictEqual(first.action.title, 'Testing1');
686
assert.strictEqual(first.action.command!.title, 'Testing1Command');
687
assert.strictEqual(first.action.command!.id, 'test1');
688
assert.strictEqual(first.action.kind, 'test.scope');
689
value.dispose();
690
});
691
});
692
693
694
test('Cannot read property \'id\' of undefined, #29469', async () => {
695
return runWithFakedTimers({ useFakeTimers: true }, async () => {
696
disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, new class implements vscode.CodeActionProvider {
697
provideCodeActions(): any {
698
return [
699
undefined,
700
null,
701
{ command: 'test', title: 'Testing' }
702
];
703
}
704
}));
705
706
await rpcProtocol.sync();
707
const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.Default }, Progress.None, CancellationToken.None);
708
const { validActions: actions } = value;
709
assert.strictEqual(actions.length, 1);
710
value.dispose();
711
});
712
});
713
714
test('Quick Fix, evil provider', async () => {
715
return runWithFakedTimers({ useFakeTimers: true }, async () => {
716
disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, new class implements vscode.CodeActionProvider {
717
provideCodeActions(): any {
718
throw new Error('evil');
719
}
720
}));
721
disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, new class implements vscode.CodeActionProvider {
722
provideCodeActions(): any {
723
return [{ command: 'test', title: 'Testing' }];
724
}
725
}));
726
727
await rpcProtocol.sync();
728
const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.QuickFix }, Progress.None, CancellationToken.None);
729
const { validActions: actions } = value;
730
assert.strictEqual(actions.length, 1);
731
value.dispose();
732
});
733
});
734
735
// --- navigate types
736
737
test('Navigate types, evil provider', async () => {
738
739
disposables.add(extHost.registerWorkspaceSymbolProvider(defaultExtension, new class implements vscode.WorkspaceSymbolProvider {
740
provideWorkspaceSymbols(): any {
741
throw new Error('evil');
742
}
743
}));
744
745
disposables.add(extHost.registerWorkspaceSymbolProvider(defaultExtension, new class implements vscode.WorkspaceSymbolProvider {
746
provideWorkspaceSymbols(): any {
747
return [new types.SymbolInformation('testing', types.SymbolKind.Array, new types.Range(0, 0, 1, 1))];
748
}
749
}));
750
751
await rpcProtocol.sync();
752
const value = await getWorkspaceSymbols('');
753
assert.strictEqual(value.length, 1);
754
const [first] = value;
755
assert.strictEqual(first.symbol.name, 'testing');
756
});
757
758
test('Navigate types, de-duplicate results', async () => {
759
const uri = URI.from({ scheme: 'foo', path: '/some/path' });
760
disposables.add(extHost.registerWorkspaceSymbolProvider(defaultExtension, new class implements vscode.WorkspaceSymbolProvider {
761
provideWorkspaceSymbols(): any {
762
return [new types.SymbolInformation('ONE', types.SymbolKind.Array, undefined, new types.Location(uri, new types.Range(0, 0, 1, 1)))];
763
}
764
}));
765
766
disposables.add(extHost.registerWorkspaceSymbolProvider(defaultExtension, new class implements vscode.WorkspaceSymbolProvider {
767
provideWorkspaceSymbols(): any {
768
return [new types.SymbolInformation('ONE', types.SymbolKind.Array, undefined, new types.Location(uri, new types.Range(0, 0, 1, 1)))]; // get de-duped
769
}
770
}));
771
772
disposables.add(extHost.registerWorkspaceSymbolProvider(defaultExtension, new class implements vscode.WorkspaceSymbolProvider {
773
provideWorkspaceSymbols(): any {
774
return [new types.SymbolInformation('ONE', types.SymbolKind.Array, undefined, new types.Location(uri, undefined!))]; // NO dedupe because of resolve
775
}
776
resolveWorkspaceSymbol(a: vscode.SymbolInformation) {
777
return a;
778
}
779
}));
780
781
disposables.add(extHost.registerWorkspaceSymbolProvider(defaultExtension, new class implements vscode.WorkspaceSymbolProvider {
782
provideWorkspaceSymbols(): any {
783
return [new types.SymbolInformation('ONE', types.SymbolKind.Struct, undefined, new types.Location(uri, new types.Range(0, 0, 1, 1)))]; // NO dedupe because of kind
784
}
785
}));
786
787
await rpcProtocol.sync();
788
const value = await getWorkspaceSymbols('');
789
assert.strictEqual(value.length, 3);
790
});
791
792
// --- rename
793
794
test('Rename, evil provider 0/2', async () => {
795
796
disposables.add(extHost.registerRenameProvider(defaultExtension, defaultSelector, new class implements vscode.RenameProvider {
797
provideRenameEdits(): any {
798
throw new class Foo { };
799
}
800
}));
801
802
await rpcProtocol.sync();
803
try {
804
await rename(languageFeaturesService.renameProvider, model, new EditorPosition(1, 1), 'newName');
805
throw Error();
806
}
807
catch (err) {
808
// expected
809
}
810
});
811
812
test('Rename, evil provider 1/2', async () => {
813
814
disposables.add(extHost.registerRenameProvider(defaultExtension, defaultSelector, new class implements vscode.RenameProvider {
815
provideRenameEdits(): any {
816
throw Error('evil');
817
}
818
}));
819
820
await rpcProtocol.sync();
821
const value = await rename(languageFeaturesService.renameProvider, model, new EditorPosition(1, 1), 'newName');
822
assert.strictEqual(value.rejectReason, 'evil');
823
});
824
825
test('Rename, evil provider 2/2', async () => {
826
827
disposables.add(extHost.registerRenameProvider(defaultExtension, '*', new class implements vscode.RenameProvider {
828
provideRenameEdits(): any {
829
throw Error('evil');
830
}
831
}));
832
833
disposables.add(extHost.registerRenameProvider(defaultExtension, defaultSelector, new class implements vscode.RenameProvider {
834
provideRenameEdits(): any {
835
const edit = new types.WorkspaceEdit();
836
edit.replace(model.uri, new types.Range(0, 0, 0, 0), 'testing');
837
return edit;
838
}
839
}));
840
841
await rpcProtocol.sync();
842
const value = await rename(languageFeaturesService.renameProvider, model, new EditorPosition(1, 1), 'newName');
843
assert.strictEqual(value.edits.length, 1);
844
});
845
846
test('Rename, ordering', async () => {
847
848
disposables.add(extHost.registerRenameProvider(defaultExtension, '*', new class implements vscode.RenameProvider {
849
provideRenameEdits(): any {
850
const edit = new types.WorkspaceEdit();
851
edit.replace(model.uri, new types.Range(0, 0, 0, 0), 'testing');
852
edit.replace(model.uri, new types.Range(1, 0, 1, 0), 'testing');
853
return edit;
854
}
855
}));
856
857
disposables.add(extHost.registerRenameProvider(defaultExtension, defaultSelector, new class implements vscode.RenameProvider {
858
provideRenameEdits(): any {
859
return;
860
}
861
}));
862
863
await rpcProtocol.sync();
864
const value = await rename(languageFeaturesService.renameProvider, model, new EditorPosition(1, 1), 'newName');
865
// least relevant rename provider
866
assert.strictEqual(value.edits.length, 2);
867
});
868
869
test('Multiple RenameProviders don\'t respect all possible PrepareRename handlers 1/2, #98352', async function () {
870
871
const called = [false, false, false, false];
872
873
disposables.add(extHost.registerRenameProvider(defaultExtension, defaultSelector, new class implements vscode.RenameProvider {
874
prepareRename(document: vscode.TextDocument, position: vscode.Position,): vscode.ProviderResult<vscode.Range> {
875
called[0] = true;
876
const range = document.getWordRangeAtPosition(position);
877
return range;
878
}
879
880
provideRenameEdits(): vscode.ProviderResult<vscode.WorkspaceEdit> {
881
called[1] = true;
882
return undefined;
883
}
884
}));
885
886
disposables.add(extHost.registerRenameProvider(defaultExtension, defaultSelector, new class implements vscode.RenameProvider {
887
prepareRename(document: vscode.TextDocument, position: vscode.Position,): vscode.ProviderResult<vscode.Range> {
888
called[2] = true;
889
return Promise.reject('Cannot rename this symbol2.');
890
}
891
provideRenameEdits(): vscode.ProviderResult<vscode.WorkspaceEdit> {
892
called[3] = true;
893
return undefined;
894
}
895
}));
896
897
await rpcProtocol.sync();
898
await rename(languageFeaturesService.renameProvider, model, new EditorPosition(1, 1), 'newName');
899
900
assert.deepStrictEqual(called, [true, true, true, false]);
901
});
902
903
test('Multiple RenameProviders don\'t respect all possible PrepareRename handlers 2/2, #98352', async function () {
904
905
const called = [false, false, false];
906
907
disposables.add(extHost.registerRenameProvider(defaultExtension, defaultSelector, new class implements vscode.RenameProvider {
908
prepareRename(document: vscode.TextDocument, position: vscode.Position,): vscode.ProviderResult<vscode.Range> {
909
called[0] = true;
910
const range = document.getWordRangeAtPosition(position);
911
return range;
912
}
913
914
provideRenameEdits(): vscode.ProviderResult<vscode.WorkspaceEdit> {
915
called[1] = true;
916
return undefined;
917
}
918
}));
919
920
disposables.add(extHost.registerRenameProvider(defaultExtension, defaultSelector, new class implements vscode.RenameProvider {
921
922
provideRenameEdits(document: vscode.TextDocument, position: vscode.Position, newName: string,): vscode.ProviderResult<vscode.WorkspaceEdit> {
923
called[2] = true;
924
return new types.WorkspaceEdit();
925
}
926
}));
927
928
await rpcProtocol.sync();
929
await rename(languageFeaturesService.renameProvider, model, new EditorPosition(1, 1), 'newName');
930
931
// first provider has NO prepare which means it is taken by default
932
assert.deepStrictEqual(called, [false, false, true]);
933
});
934
935
// --- parameter hints
936
937
test('Parameter Hints, order', async () => {
938
939
disposables.add(extHost.registerSignatureHelpProvider(defaultExtension, defaultSelector, new class implements vscode.SignatureHelpProvider {
940
provideSignatureHelp(): any {
941
return undefined;
942
}
943
}, []));
944
945
disposables.add(extHost.registerSignatureHelpProvider(defaultExtension, defaultSelector, new class implements vscode.SignatureHelpProvider {
946
provideSignatureHelp(): vscode.SignatureHelp {
947
return {
948
signatures: [],
949
activeParameter: 0,
950
activeSignature: 0
951
};
952
}
953
}, []));
954
955
await rpcProtocol.sync();
956
const value = await provideSignatureHelp(languageFeaturesService.signatureHelpProvider, model, new EditorPosition(1, 1), { triggerKind: languages.SignatureHelpTriggerKind.Invoke, isRetrigger: false }, CancellationToken.None);
957
assert.ok(value);
958
});
959
960
test('Parameter Hints, evil provider', async () => {
961
962
disposables.add(extHost.registerSignatureHelpProvider(defaultExtension, defaultSelector, new class implements vscode.SignatureHelpProvider {
963
provideSignatureHelp(): any {
964
throw new Error('evil');
965
}
966
}, []));
967
968
await rpcProtocol.sync();
969
const value = await provideSignatureHelp(languageFeaturesService.signatureHelpProvider, model, new EditorPosition(1, 1), { triggerKind: languages.SignatureHelpTriggerKind.Invoke, isRetrigger: false }, CancellationToken.None);
970
assert.strictEqual(value, undefined);
971
});
972
973
// --- suggestions
974
975
test('Suggest, order 1/3', async () => {
976
return runWithFakedTimers({ useFakeTimers: true }, async () => {
977
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, '*', new class implements vscode.CompletionItemProvider {
978
provideCompletionItems(): any {
979
return [new types.CompletionItem('testing1')];
980
}
981
}, []));
982
983
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider {
984
provideCompletionItems(): any {
985
return [new types.CompletionItem('testing2')];
986
}
987
}, []));
988
989
await rpcProtocol.sync();
990
const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set<languages.CompletionItemKind>().add(languages.CompletionItemKind.Snippet)));
991
assert.strictEqual(value.items.length, 1);
992
assert.strictEqual(value.items[0].completion.insertText, 'testing2');
993
value.disposable.dispose();
994
});
995
});
996
997
test('Suggest, order 2/3', async () => {
998
return runWithFakedTimers({ useFakeTimers: true }, async () => {
999
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, '*', new class implements vscode.CompletionItemProvider {
1000
provideCompletionItems(): any {
1001
return [new types.CompletionItem('weak-selector')]; // weaker selector but result
1002
}
1003
}, []));
1004
1005
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider {
1006
provideCompletionItems(): any {
1007
return []; // stronger selector but not a good result;
1008
}
1009
}, []));
1010
1011
await rpcProtocol.sync();
1012
const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set<languages.CompletionItemKind>().add(languages.CompletionItemKind.Snippet)));
1013
assert.strictEqual(value.items.length, 1);
1014
assert.strictEqual(value.items[0].completion.insertText, 'weak-selector');
1015
value.disposable.dispose();
1016
});
1017
});
1018
1019
test('Suggest, order 3/3', async () => {
1020
return runWithFakedTimers({ useFakeTimers: true }, async () => {
1021
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider {
1022
provideCompletionItems(): any {
1023
return [new types.CompletionItem('strong-1')];
1024
}
1025
}, []));
1026
1027
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider {
1028
provideCompletionItems(): any {
1029
return [new types.CompletionItem('strong-2')];
1030
}
1031
}, []));
1032
1033
await rpcProtocol.sync();
1034
const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set<languages.CompletionItemKind>().add(languages.CompletionItemKind.Snippet)));
1035
assert.strictEqual(value.items.length, 2);
1036
assert.strictEqual(value.items[0].completion.insertText, 'strong-1'); // sort by label
1037
assert.strictEqual(value.items[1].completion.insertText, 'strong-2');
1038
value.disposable.dispose();
1039
});
1040
});
1041
1042
test('Suggest, evil provider', async () => {
1043
return runWithFakedTimers({ useFakeTimers: true }, async () => {
1044
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider {
1045
provideCompletionItems(): any {
1046
throw new Error('evil');
1047
}
1048
}, []));
1049
1050
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider {
1051
provideCompletionItems(): any {
1052
return [new types.CompletionItem('testing')];
1053
}
1054
}, []));
1055
1056
1057
await rpcProtocol.sync();
1058
const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set<languages.CompletionItemKind>().add(languages.CompletionItemKind.Snippet)));
1059
assert.strictEqual(value.items[0].container.incomplete, false);
1060
value.disposable.dispose();
1061
});
1062
});
1063
1064
test('Suggest, CompletionList', async () => {
1065
return runWithFakedTimers({ useFakeTimers: true }, async () => {
1066
disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider {
1067
provideCompletionItems(): any {
1068
return new types.CompletionList([<any>new types.CompletionItem('hello')], true);
1069
}
1070
}, []));
1071
1072
await rpcProtocol.sync();
1073
await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set<languages.CompletionItemKind>().add(languages.CompletionItemKind.Snippet))).then(model => {
1074
assert.strictEqual(model.items[0].container.incomplete, true);
1075
model.disposable.dispose();
1076
});
1077
});
1078
});
1079
1080
// --- format
1081
1082
const NullWorkerService = new class extends mock<IEditorWorkerService>() {
1083
override computeMoreMinimalEdits(resource: URI, edits: languages.TextEdit[] | null | undefined): Promise<languages.TextEdit[] | undefined> {
1084
return Promise.resolve(edits ?? undefined);
1085
}
1086
};
1087
1088
test('Format Doc, data conversion', async () => {
1089
disposables.add(extHost.registerDocumentFormattingEditProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentFormattingEditProvider {
1090
provideDocumentFormattingEdits(): any {
1091
return [new types.TextEdit(new types.Range(0, 0, 0, 0), 'testing'), types.TextEdit.setEndOfLine(types.EndOfLine.LF)];
1092
}
1093
}));
1094
1095
await rpcProtocol.sync();
1096
const value = (await getDocumentFormattingEditsUntilResult(NullWorkerService, languageFeaturesService, model, { insertSpaces: true, tabSize: 4 }, CancellationToken.None))!;
1097
assert.strictEqual(value.length, 2);
1098
const [first, second] = value;
1099
assert.strictEqual(first.text, 'testing');
1100
assert.deepStrictEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 });
1101
assert.strictEqual(second.eol, EndOfLineSequence.LF);
1102
assert.strictEqual(second.text, '');
1103
assert.deepStrictEqual(second.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 });
1104
});
1105
1106
test('Format Doc, evil provider', async () => {
1107
disposables.add(extHost.registerDocumentFormattingEditProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentFormattingEditProvider {
1108
provideDocumentFormattingEdits(): any {
1109
throw new Error('evil');
1110
}
1111
}));
1112
1113
await rpcProtocol.sync();
1114
return getDocumentFormattingEditsUntilResult(NullWorkerService, languageFeaturesService, model, { insertSpaces: true, tabSize: 4 }, CancellationToken.None);
1115
});
1116
1117
test('Format Doc, order', async () => {
1118
1119
disposables.add(extHost.registerDocumentFormattingEditProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentFormattingEditProvider {
1120
provideDocumentFormattingEdits(): any {
1121
return undefined;
1122
}
1123
}));
1124
1125
disposables.add(extHost.registerDocumentFormattingEditProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentFormattingEditProvider {
1126
provideDocumentFormattingEdits(): any {
1127
return [new types.TextEdit(new types.Range(0, 0, 0, 0), 'testing')];
1128
}
1129
}));
1130
1131
disposables.add(extHost.registerDocumentFormattingEditProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentFormattingEditProvider {
1132
provideDocumentFormattingEdits(): any {
1133
return undefined;
1134
}
1135
}));
1136
1137
await rpcProtocol.sync();
1138
const value = (await getDocumentFormattingEditsUntilResult(NullWorkerService, languageFeaturesService, model, { insertSpaces: true, tabSize: 4 }, CancellationToken.None))!;
1139
assert.strictEqual(value.length, 1);
1140
const [first] = value;
1141
assert.strictEqual(first.text, 'testing');
1142
assert.deepStrictEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 });
1143
});
1144
1145
test('Format Range, data conversion', async () => {
1146
disposables.add(extHost.registerDocumentRangeFormattingEditProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentRangeFormattingEditProvider {
1147
provideDocumentRangeFormattingEdits(): any {
1148
return [new types.TextEdit(new types.Range(0, 0, 0, 0), 'testing')];
1149
}
1150
}));
1151
1152
await rpcProtocol.sync();
1153
const value = (await getDocumentRangeFormattingEditsUntilResult(NullWorkerService, languageFeaturesService, model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }, CancellationToken.None))!;
1154
assert.strictEqual(value.length, 1);
1155
const [first] = value;
1156
assert.strictEqual(first.text, 'testing');
1157
assert.deepStrictEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 });
1158
});
1159
1160
test('Format Range, + format_doc', async () => {
1161
disposables.add(extHost.registerDocumentRangeFormattingEditProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentRangeFormattingEditProvider {
1162
provideDocumentRangeFormattingEdits(): any {
1163
return [new types.TextEdit(new types.Range(0, 0, 0, 0), 'range')];
1164
}
1165
}));
1166
disposables.add(extHost.registerDocumentRangeFormattingEditProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentRangeFormattingEditProvider {
1167
provideDocumentRangeFormattingEdits(): any {
1168
return [new types.TextEdit(new types.Range(2, 3, 4, 5), 'range2')];
1169
}
1170
}));
1171
disposables.add(extHost.registerDocumentFormattingEditProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentFormattingEditProvider {
1172
provideDocumentFormattingEdits(): any {
1173
return [new types.TextEdit(new types.Range(0, 0, 1, 1), 'doc')];
1174
}
1175
}));
1176
await rpcProtocol.sync();
1177
const value = (await getDocumentRangeFormattingEditsUntilResult(NullWorkerService, languageFeaturesService, model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }, CancellationToken.None))!;
1178
assert.strictEqual(value.length, 1);
1179
const [first] = value;
1180
assert.strictEqual(first.text, 'range2');
1181
assert.strictEqual(first.range.startLineNumber, 3);
1182
assert.strictEqual(first.range.startColumn, 4);
1183
assert.strictEqual(first.range.endLineNumber, 5);
1184
assert.strictEqual(first.range.endColumn, 6);
1185
});
1186
1187
test('Format Range, evil provider', async () => {
1188
disposables.add(extHost.registerDocumentRangeFormattingEditProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentRangeFormattingEditProvider {
1189
provideDocumentRangeFormattingEdits(): any {
1190
throw new Error('evil');
1191
}
1192
}));
1193
1194
await rpcProtocol.sync();
1195
return getDocumentRangeFormattingEditsUntilResult(NullWorkerService, languageFeaturesService, model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }, CancellationToken.None);
1196
});
1197
1198
test('Format on Type, data conversion', async () => {
1199
1200
disposables.add(extHost.registerOnTypeFormattingEditProvider(defaultExtension, defaultSelector, new class implements vscode.OnTypeFormattingEditProvider {
1201
provideOnTypeFormattingEdits(): any {
1202
return [new types.TextEdit(new types.Range(0, 0, 0, 0), arguments[2])];
1203
}
1204
}, [';']));
1205
1206
await rpcProtocol.sync();
1207
const value = (await getOnTypeFormattingEdits(NullWorkerService, languageFeaturesService, model, new EditorPosition(1, 1), ';', { insertSpaces: true, tabSize: 2 }, CancellationToken.None))!;
1208
assert.strictEqual(value.length, 1);
1209
const [first] = value;
1210
assert.strictEqual(first.text, ';');
1211
assert.deepStrictEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 });
1212
});
1213
1214
test('Links, data conversion', async () => {
1215
1216
disposables.add(extHost.registerDocumentLinkProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentLinkProvider {
1217
provideDocumentLinks() {
1218
const link = new types.DocumentLink(new types.Range(0, 0, 1, 1), URI.parse('foo:bar#3'));
1219
link.tooltip = 'tooltip';
1220
return [link];
1221
}
1222
}));
1223
1224
await rpcProtocol.sync();
1225
const { links } = disposables.add(await getLinks(languageFeaturesService.linkProvider, model, CancellationToken.None));
1226
assert.strictEqual(links.length, 1);
1227
const [first] = links;
1228
assert.strictEqual(first.url?.toString(), 'foo:bar#3');
1229
assert.deepStrictEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 2, endColumn: 2 });
1230
assert.strictEqual(first.tooltip, 'tooltip');
1231
});
1232
1233
test('Links, evil provider', async () => {
1234
1235
disposables.add(extHost.registerDocumentLinkProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentLinkProvider {
1236
provideDocumentLinks() {
1237
return [new types.DocumentLink(new types.Range(0, 0, 1, 1), URI.parse('foo:bar#3'))];
1238
}
1239
}));
1240
1241
disposables.add(extHost.registerDocumentLinkProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentLinkProvider {
1242
provideDocumentLinks(): any {
1243
throw new Error();
1244
}
1245
}));
1246
1247
await rpcProtocol.sync();
1248
const { links } = disposables.add(await getLinks(languageFeaturesService.linkProvider, model, CancellationToken.None));
1249
assert.strictEqual(links.length, 1);
1250
const [first] = links;
1251
assert.strictEqual(first.url?.toString(), 'foo:bar#3');
1252
assert.deepStrictEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 2, endColumn: 2 });
1253
});
1254
1255
test('Document colors, data conversion', async () => {
1256
1257
disposables.add(extHost.registerColorProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentColorProvider {
1258
provideDocumentColors(): vscode.ColorInformation[] {
1259
return [new types.ColorInformation(new types.Range(0, 0, 0, 20), new types.Color(0.1, 0.2, 0.3, 0.4))];
1260
}
1261
provideColorPresentations(color: vscode.Color, context: { range: vscode.Range; document: vscode.TextDocument }): vscode.ColorPresentation[] {
1262
return [];
1263
}
1264
}));
1265
1266
await rpcProtocol.sync();
1267
const value = await getColors(languageFeaturesService.colorProvider, model, CancellationToken.None);
1268
assert.strictEqual(value.length, 1);
1269
const [first] = value;
1270
assert.deepStrictEqual(first.colorInfo.color, { red: 0.1, green: 0.2, blue: 0.3, alpha: 0.4 });
1271
assert.deepStrictEqual(first.colorInfo.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 21 });
1272
});
1273
1274
// -- selection ranges
1275
1276
test('Selection Ranges, data conversion', async () => {
1277
disposables.add(extHost.registerSelectionRangeProvider(defaultExtension, defaultSelector, new class implements vscode.SelectionRangeProvider {
1278
provideSelectionRanges() {
1279
return [
1280
new types.SelectionRange(new types.Range(0, 10, 0, 18), new types.SelectionRange(new types.Range(0, 2, 0, 20))),
1281
];
1282
}
1283
}));
1284
1285
await rpcProtocol.sync();
1286
1287
provideSelectionRanges(languageFeaturesService.selectionRangeProvider, model, [new Position(1, 17)], { selectLeadingAndTrailingWhitespace: true, selectSubwords: true }, CancellationToken.None).then(ranges => {
1288
assert.strictEqual(ranges.length, 1);
1289
assert.ok(ranges[0].length >= 2);
1290
});
1291
});
1292
1293
test('Selection Ranges, bad data', async () => {
1294
1295
try {
1296
const _a = new types.SelectionRange(new types.Range(0, 10, 0, 18),
1297
new types.SelectionRange(new types.Range(0, 11, 0, 18))
1298
);
1299
assert.ok(false, String(_a));
1300
} catch (err) {
1301
assert.ok(true);
1302
}
1303
1304
});
1305
});
1306
1307