diff --git a/src/vs/editor/browser/services/editorWorkerService.ts b/src/vs/editor/browser/services/editorWorkerService.ts index 48389526cff..e6eed8af108 100644 --- a/src/vs/editor/browser/services/editorWorkerService.ts +++ b/src/vs/editor/browser/services/editorWorkerService.ts @@ -79,7 +79,7 @@ export class EditorWorkerService extends Disposable implements IEditorWorkerServ }); } })); - this._register(modes.CompletionProviderRegistry.register('*', new WordBasedCompletionItemProvider(this._workerManager, configurationService, this._modelService, languageConfigurationService))); + this._register(languageFeaturesService.completionProvider.register('*', new WordBasedCompletionItemProvider(this._workerManager, configurationService, this._modelService, languageConfigurationService))); } public override dispose(): void { diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 1fd40dccc37..ceb1d3ad5f8 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -1945,7 +1945,7 @@ export class EditorModeContext extends Disposable { this._register(_editor.onDidChangeModelLanguage(update)); // update when registries change - this._register(modes.CompletionProviderRegistry.onDidChange(update)); + this._register(_languageFeaturesService.completionProvider.onDidChange(update)); this._register(modes.CodeActionProviderRegistry.onDidChange(update)); this._register(_languageFeaturesService.codeLensProvider.onDidChange(update)); this._register(_languageFeaturesService.definitionProvider.onDidChange(update)); @@ -1999,7 +1999,7 @@ export class EditorModeContext extends Disposable { } this._contextKeyService.bufferChangeEvents(() => { this._langId.set(model.getLanguageId()); - this._hasCompletionItemProvider.set(modes.CompletionProviderRegistry.has(model)); + this._hasCompletionItemProvider.set(this._languageFeaturesService.completionProvider.has(model)); this._hasCodeActionsProvider.set(modes.CodeActionProviderRegistry.has(model)); this._hasCodeLensProvider.set(this._languageFeaturesService.codeLensProvider.has(model)); this._hasDefinitionProvider.set(this._languageFeaturesService.definitionProvider.has(model)); diff --git a/src/vs/editor/common/languages.ts b/src/vs/editor/common/languages.ts index f4a4f78c171..3d3786119e6 100644 --- a/src/vs/editor/common/languages.ts +++ b/src/vs/editor/common/languages.ts @@ -1887,11 +1887,6 @@ export interface DocumentRangeSemanticTokensProvider { // --- feature registries ------ -/** - * @internal - */ -export const CompletionProviderRegistry = new LanguageFeatureRegistry(); - /** * @internal */ diff --git a/src/vs/editor/common/services/languageFeatures.ts b/src/vs/editor/common/services/languageFeatures.ts index f2b399fdff1..4ca8495067b 100644 --- a/src/vs/editor/common/services/languageFeatures.ts +++ b/src/vs/editor/common/services/languageFeatures.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry'; -import { CodeLensProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentRangeFormattingEditProvider, DocumentSymbolProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, LinkProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider } from 'vs/editor/common/languages'; +import { CodeLensProvider, CompletionItemProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentRangeFormattingEditProvider, DocumentSymbolProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, LinkProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider } from 'vs/editor/common/languages'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export const ILanguageFeaturesService = createDecorator('ILanguageFeaturesService'); @@ -62,4 +62,6 @@ export interface ILanguageFeaturesService { // --- completions readonly inlineCompletionsProvider: LanguageFeatureRegistry; + + readonly completionProvider: LanguageFeatureRegistry; } diff --git a/src/vs/editor/common/services/languageFeaturesService.ts b/src/vs/editor/common/services/languageFeaturesService.ts index a84e6016393..ac5a1e9ed17 100644 --- a/src/vs/editor/common/services/languageFeaturesService.ts +++ b/src/vs/editor/common/services/languageFeaturesService.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry'; -import { CodeLensProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentRangeFormattingEditProvider, DocumentSymbolProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, LinkProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider } from 'vs/editor/common/languages'; +import { CodeLensProvider, CompletionItemProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentRangeFormattingEditProvider, DocumentSymbolProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, LinkProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider } from 'vs/editor/common/languages'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -export class LanguageFeatureService implements ILanguageFeaturesService { +export class LanguageFeaturesService implements ILanguageFeaturesService { declare _serviceBrand: undefined; @@ -52,6 +52,8 @@ export class LanguageFeatureService implements ILanguageFeaturesService { readonly inlineCompletionsProvider = new LanguageFeatureRegistry(); + readonly completionProvider = new LanguageFeatureRegistry(); + } -registerSingleton(ILanguageFeaturesService, LanguageFeatureService, true); +registerSingleton(ILanguageFeaturesService, LanguageFeaturesService, true); diff --git a/src/vs/editor/contrib/documentSymbols/test/browser/outlineModel.test.ts b/src/vs/editor/contrib/documentSymbols/test/browser/outlineModel.test.ts index c607828759f..d801c2c7722 100644 --- a/src/vs/editor/contrib/documentSymbols/test/browser/outlineModel.test.ts +++ b/src/vs/editor/contrib/documentSymbols/test/browser/outlineModel.test.ts @@ -10,7 +10,7 @@ import { URI } from 'vs/base/common/uri'; import { Range } from 'vs/editor/common/core/range'; import { DocumentSymbol, SymbolKind } from 'vs/editor/common/languages'; import { LanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce'; -import { LanguageFeatureService } from 'vs/editor/common/services/languageFeaturesService'; +import { LanguageFeaturesService } from 'vs/editor/common/services/languageFeaturesService'; import { IModelService } from 'vs/editor/common/services/model'; import { createModelServices, createTextModel } from 'vs/editor/test/common/testTextModel'; import { NullLogService } from 'vs/platform/log/common/log'; @@ -20,7 +20,7 @@ import { OutlineElement, OutlineGroup, OutlineModel, OutlineModelService } from suite('OutlineModel', function () { let disposables = new DisposableStore(); - const languageFeaturesService = new LanguageFeatureService(); + const languageFeaturesService = new LanguageFeaturesService(); teardown(function () { disposables.clear(); diff --git a/src/vs/editor/contrib/inlineCompletions/test/browser/inlineCompletionsProvider.test.ts b/src/vs/editor/contrib/inlineCompletions/test/browser/inlineCompletionsProvider.test.ts index 7102ad51ccb..0c27b6a637d 100644 --- a/src/vs/editor/contrib/inlineCompletions/test/browser/inlineCompletionsProvider.test.ts +++ b/src/vs/editor/contrib/inlineCompletions/test/browser/inlineCompletionsProvider.test.ts @@ -11,7 +11,7 @@ import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/uti import { Range } from 'vs/editor/common/core/range'; import { InlineCompletionsProvider, InlineCompletionTriggerKind } from 'vs/editor/common/languages'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; -import { LanguageFeatureService } from 'vs/editor/common/services/languageFeaturesService'; +import { LanguageFeaturesService } from 'vs/editor/common/services/languageFeaturesService'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; import { SharedInlineCompletionCache } from 'vs/editor/contrib/inlineCompletions/browser/ghostTextModel'; import { InlineCompletionsModel } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel'; @@ -551,7 +551,7 @@ async function withAsyncTestCodeEditorAndInlineCompletionsModel( try { if (options.provider) { - const languageFeaturesService = new LanguageFeatureService(); + const languageFeaturesService = new LanguageFeaturesService(); if (!options.serviceCollection) { options.serviceCollection = new ServiceCollection(); } diff --git a/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts b/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts index 88aa76a501b..107dd45f74c 100644 --- a/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts +++ b/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts @@ -9,7 +9,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { mock } from 'vs/base/test/common/mock'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; import { Range } from 'vs/editor/common/core/range'; -import { CompletionItemKind, CompletionItemProvider, CompletionProviderRegistry } from 'vs/editor/common/languages'; +import { CompletionItemKind, CompletionItemProvider } from 'vs/editor/common/languages'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; import { SharedInlineCompletionCache } from 'vs/editor/contrib/inlineCompletions/browser/ghostTextModel'; @@ -33,6 +33,8 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { minimizeInlineCompletion } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel'; import { rangeStartsWith } from 'vs/editor/contrib/inlineCompletions/browser/suggestWidgetInlineCompletionProvider'; +import { LanguageFeaturesService } from 'vs/editor/common/services/languageFeaturesService'; +import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; suite('Suggest Widget Model', () => { test('rangeStartsWith', () => { @@ -144,7 +146,6 @@ async function withAsyncTestCodeEditorAndInlineCompletionsModel( await runWithFakedTimers({ useFakeTimers: options.fakeClock }, async () => { const disposableStore = new DisposableStore(); - try { const serviceCollection = new ServiceCollection( [ITelemetryService, NullTelemetryService], @@ -173,7 +174,9 @@ async function withAsyncTestCodeEditorAndInlineCompletionsModel( ); if (options.provider) { - const d = CompletionProviderRegistry.register({ pattern: '**' }, options.provider); + const languageFeaturesService = new LanguageFeaturesService(); + serviceCollection.set(ILanguageFeaturesService, languageFeaturesService); + const d = languageFeaturesService.completionProvider.register({ pattern: '**' }, options.provider); disposableStore.add(d); } diff --git a/src/vs/editor/contrib/snippet/browser/snippetController2.ts b/src/vs/editor/contrib/snippet/browser/snippetController2.ts index 009b9ac838e..712e5c72526 100644 --- a/src/vs/editor/contrib/snippet/browser/snippetController2.ts +++ b/src/vs/editor/contrib/snippet/browser/snippetController2.ts @@ -17,6 +17,7 @@ import { showSimpleSuggestions } from 'vs/editor/contrib/suggest/browser/suggest import { OvertypingCapturer } from 'vs/editor/contrib/suggest/browser/suggestOvertypingCapturer'; import { localize } from 'vs/nls'; import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ILogService } from 'vs/platform/log/common/log'; import { SnippetSession } from './snippetSession'; @@ -64,6 +65,7 @@ export class SnippetController2 implements IEditorContribution { constructor( private readonly _editor: ICodeEditor, + @IInstantiationService private readonly _instantiationService: IInstantiationService, @ILogService private readonly _logService: ILogService, @IContextKeyService contextKeyService: IContextKeyService ) { @@ -184,7 +186,7 @@ export class SnippetController2 implements IEditorContribution { const [first] = choice.options; - showSimpleSuggestions(this._editor, choice.options.map((option, i) => { + this._instantiationService.invokeFunction(showSimpleSuggestions, this._editor, choice.options.map((option, i) => { // let before = choice.options.slice(0, i); // let after = choice.options.slice(i); diff --git a/src/vs/editor/contrib/snippet/test/browser/snippetController2.old.test.ts b/src/vs/editor/contrib/snippet/test/browser/snippetController2.old.test.ts index a64a5377895..6a8495d6abf 100644 --- a/src/vs/editor/contrib/snippet/test/browser/snippetController2.old.test.ts +++ b/src/vs/editor/contrib/snippet/test/browser/snippetController2.old.test.ts @@ -10,6 +10,7 @@ import { Selection } from 'vs/editor/common/core/selection'; import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2'; import { ITestCodeEditor, withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { ILabelService } from 'vs/platform/label/common/label'; import { NullLogService } from 'vs/platform/log/common/log'; @@ -19,9 +20,10 @@ class TestSnippetController extends SnippetController2 { constructor( editor: ICodeEditor, + @IInstantiationService instaService: IInstantiationService, @IContextKeyService private readonly _contextKeyService: IContextKeyService ) { - super(editor, new NullLogService(), _contextKeyService); + super(editor, instaService, new NullLogService(), _contextKeyService); } isInSnippetMode(): boolean { diff --git a/src/vs/editor/contrib/snippet/test/browser/snippetController2.test.ts b/src/vs/editor/contrib/snippet/test/browser/snippetController2.test.ts index 8b0da384326..c6604be5fb7 100644 --- a/src/vs/editor/contrib/snippet/test/browser/snippetController2.test.ts +++ b/src/vs/editor/contrib/snippet/test/browser/snippetController2.test.ts @@ -12,10 +12,13 @@ import { TextModel } from 'vs/editor/common/model/textModel'; import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2'; import { createTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { createTextModel } from 'vs/editor/test/common/testTextModel'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { ILabelService } from 'vs/platform/label/common/label'; -import { NullLogService } from 'vs/platform/log/common/log'; +import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; suite('SnippetController2', function () { @@ -37,7 +40,7 @@ suite('SnippetController2', function () { let editor: ICodeEditor; let model: TextModel; let contextKeys: MockContextKeyService; - let logService = new NullLogService(); + let instaService: IInstantiationService; setup(function () { contextKeys = new MockContextKeyService(); @@ -45,7 +48,10 @@ suite('SnippetController2', function () { const serviceCollection = new ServiceCollection( [ILabelService, new class extends mock() { }], [IWorkspaceContextService, new class extends mock() { }], + [ILogService, new NullLogService()], + [IContextKeyService, contextKeys], ); + instaService = new InstantiationService(serviceCollection); editor = createTestCodeEditor(model, { serviceCollection }); editor.setSelections([new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5)]); assert.strictEqual(model.getEOL(), '\n'); @@ -56,13 +62,13 @@ suite('SnippetController2', function () { }); test('creation', () => { - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); assertContextKeys(contextKeys, false, false, false); ctrl.dispose(); }); test('insert, insert -> abort', function () { - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); ctrl.insert('foo${1:bar}foo$0'); assertContextKeys(contextKeys, true, false, true); @@ -74,7 +80,7 @@ suite('SnippetController2', function () { }); test('insert, insert -> tab, tab, done', function () { - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); ctrl.insert('${1:one}${2:two}$0'); assertContextKeys(contextKeys, true, false, true); @@ -92,7 +98,7 @@ suite('SnippetController2', function () { }); test('insert, insert -> cursor moves out (left/right)', function () { - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); ctrl.insert('foo${1:bar}foo$0'); assertContextKeys(contextKeys, true, false, true); @@ -104,7 +110,7 @@ suite('SnippetController2', function () { }); test('insert, insert -> cursor moves out (up/down)', function () { - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); ctrl.insert('foo${1:bar}foo$0'); assertContextKeys(contextKeys, true, false, true); @@ -116,7 +122,7 @@ suite('SnippetController2', function () { }); test('insert, insert -> cursors collapse', function () { - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); ctrl.insert('foo${1:bar}foo$0'); assert.strictEqual(SnippetController2.InSnippetMode.getValue(contextKeys), true); @@ -128,7 +134,7 @@ suite('SnippetController2', function () { }); test('insert, insert plain text -> no snippet mode', function () { - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); ctrl.insert('foobar'); assertContextKeys(contextKeys, false, false, false); @@ -136,7 +142,7 @@ suite('SnippetController2', function () { }); test('insert, delete snippet text', function () { - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); ctrl.insert('${1:foobar}$0'); assertContextKeys(contextKeys, true, false, true); @@ -160,7 +166,7 @@ suite('SnippetController2', function () { }); test('insert, nested snippet', function () { - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); ctrl.insert('${1:foobar}$0'); assertContextKeys(contextKeys, true, false, true); assertSelections(editor, new Selection(1, 1, 1, 7), new Selection(2, 5, 2, 11)); @@ -179,7 +185,7 @@ suite('SnippetController2', function () { }); test('insert, nested plain text', function () { - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); ctrl.insert('${1:foobar}$0'); assertContextKeys(contextKeys, true, false, true); assertSelections(editor, new Selection(1, 1, 1, 7), new Selection(2, 5, 2, 11)); @@ -194,7 +200,7 @@ suite('SnippetController2', function () { }); test('Nested snippets without final placeholder jumps to next outer placeholder, #27898', function () { - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); ctrl.insert('for(const ${1:element} of ${2:array}) {$0}'); assertContextKeys(contextKeys, true, false, true); @@ -213,7 +219,7 @@ suite('SnippetController2', function () { }); test('Inconsistent tab stop behaviour with recursive snippets and tab / shift tab, #27543', function () { - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); ctrl.insert('1_calize(${1:nl}, \'${2:value}\')$0'); assertContextKeys(contextKeys, true, false, true); @@ -237,7 +243,7 @@ suite('SnippetController2', function () { }); test('Snippet tabstop selecting content of previously entered variable only works when separated by space, #23728', function () { - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); editor.setSelection(new Selection(1, 1, 1, 1)); @@ -255,7 +261,7 @@ suite('SnippetController2', function () { }); test('HTML Snippets Combine, #32211', function () { - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); model.updateOptions({ insertSpaces: false, tabSize: 4, trimAutoWhitespace: false }); @@ -286,7 +292,7 @@ suite('SnippetController2', function () { }); test('Problems with nested snippet insertion #39594', function () { - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); editor.setSelection(new Selection(1, 1, 1, 1)); @@ -303,7 +309,7 @@ suite('SnippetController2', function () { test('Problems with nested snippet insertion #39594', function () { // ensure selection-change-to-cancel logic isn't too aggressive - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); model.setValue('a-\naaa-'); editor.setSelections([new Selection(2, 5, 2, 5), new Selection(1, 3, 1, 3)]); @@ -315,7 +321,7 @@ suite('SnippetController2', function () { test('“Nested” snippets terminating abruptly in VSCode 1.19.2. #42012', function () { - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); editor.setSelection(new Selection(1, 1, 1, 1)); ctrl.insert('var ${2:${1:name}} = ${1:name} + 1;${0}'); @@ -329,7 +335,7 @@ suite('SnippetController2', function () { test('Placeholders order #58267', function () { - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); editor.setSelection(new Selection(1, 1, 1, 1)); ctrl.insert('\\pth{$1}$0'); @@ -358,7 +364,7 @@ suite('SnippetController2', function () { }); test('Must tab through deleted tab stops in snippets #31619', function () { - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); editor.setSelection(new Selection(1, 1, 1, 1)); ctrl.insert('foo${1:a${2:bar}baz}end$0'); @@ -373,7 +379,7 @@ suite('SnippetController2', function () { }); test('Cancelling snippet mode should discard added cursors #68512 (soft cancel)', function () { - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); editor.setSelection(new Selection(1, 1, 1, 1)); @@ -393,7 +399,7 @@ suite('SnippetController2', function () { }); test('Cancelling snippet mode should discard added cursors #68512 (hard cancel)', function () { - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); editor.setSelection(new Selection(1, 1, 1, 1)); @@ -413,7 +419,7 @@ suite('SnippetController2', function () { }); test('User defined snippet tab stops ignored #72862', function () { - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); editor.setSelection(new Selection(1, 1, 1, 1)); @@ -422,7 +428,7 @@ suite('SnippetController2', function () { }); test('Optional tabstop in snippets #72358', function () { - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); editor.setSelection(new Selection(1, 1, 1, 1)); @@ -440,7 +446,7 @@ suite('SnippetController2', function () { }); test('issue #90135: confusing trim whitespace edits', function () { - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); CoreEditingCommands.Tab.runEditorCommand(null, editor, null); @@ -449,7 +455,7 @@ suite('SnippetController2', function () { }); test('leading TAB by snippets won\'t replace by spaces #101870', function () { - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); model.updateOptions({ insertSpaces: true, tabSize: 4 }); ctrl.insert('\tHello World\n\tNew Line'); @@ -457,7 +463,7 @@ suite('SnippetController2', function () { }); test('leading TAB by snippets won\'t replace by spaces #101870 (part 2)', function () { - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); model.updateOptions({ insertSpaces: true, tabSize: 4 }); ctrl.insert('\tHello World\n\tNew Line\n${1:\tmore}'); @@ -468,7 +474,7 @@ suite('SnippetController2', function () { { // HAPPY - no nested snippet - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); model.updateOptions({ insertSpaces: true, tabSize: 4 }); ctrl.insert('$1\n\n${1/([A-Za-z0-9]+): ([A-Za-z]+).*/$1: \'$2\',/gm}'); @@ -479,7 +485,7 @@ suite('SnippetController2', function () { assert.strictEqual(model.getValue(), `foo: number;\n\nfoo: 'number',`); } - const ctrl = new SnippetController2(editor, logService, contextKeys); + const ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); model.updateOptions({ insertSpaces: true, tabSize: 4 }); ctrl.insert('$1\n\n${1/([A-Za-z0-9]+): ([A-Za-z]+).*/$1: \'$2\',/gm}'); diff --git a/src/vs/editor/contrib/suggest/browser/suggest.ts b/src/vs/editor/contrib/suggest/browser/suggest.ts index e566ebb6cbf..aaaa54dcc79 100644 --- a/src/vs/editor/contrib/suggest/browser/suggest.ts +++ b/src/vs/editor/contrib/suggest/browser/suggest.ts @@ -22,6 +22,9 @@ import { localize } from 'vs/nls'; import { MenuId } from 'vs/platform/actions/common/actions'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry'; +import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; export const Context = { Visible: new RawContextKey('suggestWidgetVisible', false, localize('suggestWidgetVisible', "Whether suggestion are visible")), @@ -192,6 +195,7 @@ export class CompletionItemModel { } export async function provideSuggestionItems( + registry: LanguageFeatureRegistry, model: ITextModel, position: Position, options: CompletionOptions = CompletionOptions.default, @@ -263,7 +267,7 @@ export async function provideSuggestionItems( // add suggestions from contributed providers - providers are ordered in groups of // equal score and once a group produces a result the process stops // get provider groups, always add snippet suggestion provider - for (let providerGroup of modes.CompletionProviderRegistry.orderedGroups(model)) { + for (let providerGroup of registry.orderedGroups(model)) { // for each support in the group ask for suggestions let didAddResult = false; @@ -359,6 +363,7 @@ CommandsRegistry.registerCommand('_executeCompletionItemProvider', async (access assertType(typeof triggerCharacter === 'string' || !triggerCharacter); assertType(typeof maxItemsToResolve === 'number' || !maxItemsToResolve); + const { completionProvider } = accessor.get(ILanguageFeaturesService); const ref = await accessor.get(ITextModelService).createModelReference(uri); try { @@ -368,7 +373,7 @@ CommandsRegistry.registerCommand('_executeCompletionItemProvider', async (access }; const resolving: Promise[] = []; - const completions = await provideSuggestionItems(ref.object.textEditorModel, Position.lift(position), undefined, { triggerCharacter, triggerKind: triggerCharacter ? modes.CompletionTriggerKind.TriggerCharacter : modes.CompletionTriggerKind.Invoke }); + const completions = await provideSuggestionItems(completionProvider, ref.object.textEditorModel, Position.lift(position), undefined, { triggerCharacter, triggerKind: triggerCharacter ? modes.CompletionTriggerKind.TriggerCharacter : modes.CompletionTriggerKind.Invoke }); for (const item of completions.items) { if (resolving.length < (maxItemsToResolve ?? 0)) { resolving.push(item.resolve(CancellationToken.None)); @@ -394,21 +399,30 @@ interface SuggestController extends IEditorContribution { triggerSuggest(onlyFrom?: Set): void; } -const _provider = new class implements modes.CompletionItemProvider { - onlyOnceSuggestions: modes.CompletionItem[] = []; +const _once = new WeakMap(); - provideCompletionItems(): modes.CompletionList { - let suggestions = this.onlyOnceSuggestions.slice(0); - let result = { suggestions }; - this.onlyOnceSuggestions.length = 0; - return result; - } -}; +export function showSimpleSuggestions(accessor: ServicesAccessor, editor: ICodeEditor, suggestions: modes.CompletionItem[]) { -modes.CompletionProviderRegistry.register('*', _provider); + const { completionProvider } = accessor.get(ILanguageFeaturesService); + + const _provider = new class implements modes.CompletionItemProvider { + + onlyOnceSuggestions: modes.CompletionItem[] = []; + + provideCompletionItems(): modes.CompletionList { + let suggestions = this.onlyOnceSuggestions.slice(0); + let result = { suggestions }; + this.onlyOnceSuggestions.length = 0; + dispo.dispose(); + return result; + } + }; + + const dispo = completionProvider.register('*', _provider); + _once.get(editor)?.dispose(); + _once.set(editor, dispo); -export function showSimpleSuggestions(editor: ICodeEditor, suggestions: modes.CompletionItem[]) { setTimeout(() => { _provider.onlyOnceSuggestions.push(...suggestions); editor.getContribution('editor.contrib.suggestController')?.triggerSuggest(new Set().add(_provider)); diff --git a/src/vs/editor/contrib/suggest/browser/suggestModel.ts b/src/vs/editor/contrib/suggest/browser/suggestModel.ts index d753f697f14..9bafb0256b6 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestModel.ts @@ -15,7 +15,7 @@ import { CursorChangeReason, ICursorSelectionChangedEvent } from 'vs/editor/comm import { IPosition, Position } from 'vs/editor/common/core/position'; import { Selection } from 'vs/editor/common/core/selection'; import { ITextModel } from 'vs/editor/common/model'; -import { CompletionContext, CompletionItemKind, CompletionItemProvider, CompletionProviderRegistry, CompletionTriggerKind, StandardTokenType } from 'vs/editor/common/languages'; +import { CompletionContext, CompletionItemKind, CompletionItemProvider, CompletionTriggerKind, StandardTokenType } from 'vs/editor/common/languages'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2'; import { WordDistance } from 'vs/editor/contrib/suggest/browser/wordDistance'; @@ -27,6 +27,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { CompletionModel } from './completionModel'; import { CompletionDurations, CompletionItem, CompletionOptions, getSnippetSuggestSupport, getSuggestionComparator, provideSuggestionItems, SnippetSortOrder } from './suggest'; import { IWordAtPosition } from 'vs/editor/common/core/wordHelper'; +import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; export interface ICancelEvent { readonly retrigger: boolean; @@ -166,6 +167,7 @@ export class SuggestModel implements IDisposable { @ILogService private readonly _logService: ILogService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IConfigurationService private readonly _configurationService: IConfigurationService, + @ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService, ) { this._currentSelection = this._editor.getSelection() || new Selection(1, 1, 1, 1); @@ -182,7 +184,7 @@ export class SuggestModel implements IDisposable { this._updateTriggerCharacters(); this._updateQuickSuggest(); })); - this._toDispose.add(CompletionProviderRegistry.onDidChange(() => { + this._toDispose.add(this._languageFeaturesService.completionProvider.onDidChange(() => { this._updateTriggerCharacters(); this._updateActiveSuggestSession(); })); @@ -243,7 +245,7 @@ export class SuggestModel implements IDisposable { } const supportsByTriggerCharacter = new Map>(); - for (const support of CompletionProviderRegistry.all(this._editor.getModel())) { + for (const support of this._languageFeaturesService.completionProvider.all(this._editor.getModel())) { for (const ch of support.triggerCharacters || []) { let set = supportsByTriggerCharacter.get(ch); if (!set) { @@ -322,7 +324,7 @@ export class SuggestModel implements IDisposable { private _updateActiveSuggestSession(): void { if (this._state !== State.Idle) { - if (!this._editor.hasModel() || !CompletionProviderRegistry.has(this._editor.getModel())) { + if (!this._editor.hasModel() || !this._languageFeaturesService.completionProvider.has(this._editor.getModel())) { this.cancel(); } else { this.trigger({ auto: this._state === State.Auto, shy: false }, true); @@ -423,7 +425,7 @@ export class SuggestModel implements IDisposable { return; } - if (!CompletionProviderRegistry.has(model)) { + if (!this._languageFeaturesService.completionProvider.has(model)) { return; } @@ -501,6 +503,7 @@ export class SuggestModel implements IDisposable { const wordDistance = WordDistance.create(this._editorWorkerService, this._editor); const completions = provideSuggestionItems( + this._languageFeaturesService.completionProvider, model, this._editor.getPosition(), new CompletionOptions(snippetSortOrder, itemKindFilter, onlyFrom, showDeprecated), @@ -655,7 +658,7 @@ export class SuggestModel implements IDisposable { // Select those providers have not contributed to this completion model and re-trigger completions for // them. Also adopt the existing items and merge them into the new completion model - const inactiveProvider = new Set(CompletionProviderRegistry.all(this._editor.getModel()!)); + const inactiveProvider = new Set(this._languageFeaturesService.completionProvider.all(this._editor.getModel()!)); for (let provider of this._completionModel.allProvider) { inactiveProvider.delete(provider); } diff --git a/src/vs/editor/contrib/suggest/test/browser/suggest.test.ts b/src/vs/editor/contrib/suggest/test/browser/suggest.test.ts index 8728ae06637..04987101122 100644 --- a/src/vs/editor/contrib/suggest/test/browser/suggest.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/suggest.test.ts @@ -8,20 +8,22 @@ import { URI } from 'vs/base/common/uri'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { TextModel } from 'vs/editor/common/model/textModel'; -import { CompletionItemKind, CompletionItemProvider, CompletionProviderRegistry } from 'vs/editor/common/languages'; +import { CompletionItemKind, CompletionItemProvider } from 'vs/editor/common/languages'; import { CompletionOptions, provideSuggestionItems, SnippetSortOrder } from 'vs/editor/contrib/suggest/browser/suggest'; import { createTextModel } from 'vs/editor/test/common/testTextModel'; +import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry'; suite('Suggest', function () { let model: TextModel; let registration: IDisposable; + let registry: LanguageFeatureRegistry; setup(function () { - + registry = new LanguageFeatureRegistry(); model = createTextModel('FOO\nbar\BAR\nfoo', undefined, undefined, URI.parse('foo:bar/path')); - registration = CompletionProviderRegistry.register({ pattern: 'bar/path', scheme: 'foo' }, { + registration = registry.register({ pattern: 'bar/path', scheme: 'foo' }, { provideCompletionItems(_doc, pos) { return { incomplete: false, @@ -52,7 +54,7 @@ suite('Suggest', function () { }); test('sort - snippet inline', async function () { - const { items } = await provideSuggestionItems(model, new Position(1, 1), new CompletionOptions(SnippetSortOrder.Inline)); + const { items } = await provideSuggestionItems(registry, model, new Position(1, 1), new CompletionOptions(SnippetSortOrder.Inline)); assert.strictEqual(items.length, 3); assert.strictEqual(items[0].completion.label, 'aaa'); assert.strictEqual(items[1].completion.label, 'fff'); @@ -60,7 +62,7 @@ suite('Suggest', function () { }); test('sort - snippet top', async function () { - const { items } = await provideSuggestionItems(model, new Position(1, 1), new CompletionOptions(SnippetSortOrder.Top)); + const { items } = await provideSuggestionItems(registry, model, new Position(1, 1), new CompletionOptions(SnippetSortOrder.Top)); assert.strictEqual(items.length, 3); assert.strictEqual(items[0].completion.label, 'aaa'); assert.strictEqual(items[1].completion.label, 'zzz'); @@ -68,7 +70,7 @@ suite('Suggest', function () { }); test('sort - snippet bottom', async function () { - const { items } = await provideSuggestionItems(model, new Position(1, 1), new CompletionOptions(SnippetSortOrder.Bottom)); + const { items } = await provideSuggestionItems(registry, model, new Position(1, 1), new CompletionOptions(SnippetSortOrder.Bottom)); assert.strictEqual(items.length, 3); assert.strictEqual(items[0].completion.label, 'fff'); assert.strictEqual(items[1].completion.label, 'aaa'); @@ -76,7 +78,7 @@ suite('Suggest', function () { }); test('sort - snippet none', async function () { - const { items } = await provideSuggestionItems(model, new Position(1, 1), new CompletionOptions(undefined, new Set().add(CompletionItemKind.Snippet))); + const { items } = await provideSuggestionItems(registry, model, new Position(1, 1), new CompletionOptions(undefined, new Set().add(CompletionItemKind.Snippet))); assert.strictEqual(items.length, 1); assert.strictEqual(items[0].completion.label, 'fff'); }); @@ -97,9 +99,9 @@ suite('Suggest', function () { }; } }; - const registration = CompletionProviderRegistry.register({ pattern: 'bar/path', scheme: 'foo' }, foo); + const registration = registry.register({ pattern: 'bar/path', scheme: 'foo' }, foo); - provideSuggestionItems(model, new Position(1, 1), new CompletionOptions(undefined, undefined, new Set().add(foo))).then(({ items }) => { + provideSuggestionItems(registry, model, new Position(1, 1), new CompletionOptions(undefined, undefined, new Set().add(foo))).then(({ items }) => { registration.dispose(); assert.strictEqual(items.length, 1); @@ -137,8 +139,8 @@ suite('Suggest', function () { } }; - const registration = CompletionProviderRegistry.register({ pattern: 'bar/path', scheme: 'foo' }, foo); - const { items } = await provideSuggestionItems(model, new Position(0, 0), new CompletionOptions(undefined, undefined, new Set().add(foo))); + const registration = registry.register({ pattern: 'bar/path', scheme: 'foo' }, foo); + const { items } = await provideSuggestionItems(registry, model, new Position(0, 0), new CompletionOptions(undefined, undefined, new Set().add(foo))); registration.dispose(); assert.strictEqual(items.length, 2); diff --git a/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts b/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts index 964d311568c..0bdcf236e44 100644 --- a/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts @@ -12,7 +12,7 @@ import { mock } from 'vs/base/test/common/mock'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { TextModel } from 'vs/editor/common/model/textModel'; -import { CompletionItemInsertTextRule, CompletionItemKind, CompletionProviderRegistry } from 'vs/editor/common/languages'; +import { CompletionItemInsertTextRule, CompletionItemKind } from 'vs/editor/common/languages'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2'; import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestController'; @@ -29,6 +29,8 @@ import { InMemoryStorageService, IStorageService } from 'vs/platform/storage/com import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { LanguageFeaturesService } from 'vs/editor/common/services/languageFeaturesService'; +import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; suite('SuggestController', function () { @@ -37,6 +39,8 @@ suite('SuggestController', function () { let controller: SuggestController; let editor: ITestCodeEditor; let model: TextModel; + const languageFeaturesService = new LanguageFeaturesService(); + teardown(function () { disposables.clear(); @@ -45,6 +49,7 @@ suite('SuggestController', function () { setup(function () { const serviceCollection = new ServiceCollection( + [ILanguageFeaturesService, languageFeaturesService], [ITelemetryService, NullTelemetryService], [ILogService, new NullLogService()], [IStorageService, new InMemoryStorageService()], @@ -78,7 +83,7 @@ suite('SuggestController', function () { }); test('postfix completion reports incorrect position #86984', async function () { - disposables.add(CompletionProviderRegistry.register({ scheme: 'test-ctrl' }, { + disposables.add(languageFeaturesService.completionProvider.register({ scheme: 'test-ctrl' }, { provideCompletionItems(doc, pos) { return { suggestions: [{ @@ -114,7 +119,7 @@ suite('SuggestController', function () { test('use additionalTextEdits sync when possible', async function () { - disposables.add(CompletionProviderRegistry.register({ scheme: 'test-ctrl' }, { + disposables.add(languageFeaturesService.completionProvider.register({ scheme: 'test-ctrl' }, { provideCompletionItems(doc, pos) { return { suggestions: [{ @@ -155,7 +160,7 @@ suite('SuggestController', function () { let resolveCallCount = 0; - disposables.add(CompletionProviderRegistry.register({ scheme: 'test-ctrl' }, { + disposables.add(languageFeaturesService.completionProvider.register({ scheme: 'test-ctrl' }, { provideCompletionItems(doc, pos) { return { suggestions: [{ @@ -207,7 +212,7 @@ suite('SuggestController', function () { let resolveCallCount = 0; let resolve: Function = () => { }; - disposables.add(CompletionProviderRegistry.register({ scheme: 'test-ctrl' }, { + disposables.add(languageFeaturesService.completionProvider.register({ scheme: 'test-ctrl' }, { provideCompletionItems(doc, pos) { return { suggestions: [{ @@ -263,7 +268,7 @@ suite('SuggestController', function () { let resolveCallCount = 0; let resolve: Function = () => { }; - disposables.add(CompletionProviderRegistry.register({ scheme: 'test-ctrl' }, { + disposables.add(languageFeaturesService.completionProvider.register({ scheme: 'test-ctrl' }, { provideCompletionItems(doc, pos) { return { suggestions: [{ @@ -312,7 +317,7 @@ suite('SuggestController', function () { let resolveCallCount = 0; let resolve: Function = () => { }; - disposables.add(CompletionProviderRegistry.register({ scheme: 'test-ctrl' }, { + disposables.add(languageFeaturesService.completionProvider.register({ scheme: 'test-ctrl' }, { provideCompletionItems(doc, pos) { return { suggestions: [{ @@ -366,7 +371,7 @@ suite('SuggestController', function () { test('resolve additionalTextEdits async when needed (cancel)', async function () { let resolve: Function[] = []; - disposables.add(CompletionProviderRegistry.register({ scheme: 'test-ctrl' }, { + disposables.add(languageFeaturesService.completionProvider.register({ scheme: 'test-ctrl' }, { provideCompletionItems(doc, pos) { return { suggestions: [{ diff --git a/src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts b/src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts index 1fb6cbc2675..b86f7f03239 100644 --- a/src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts @@ -15,10 +15,9 @@ import { Selection } from 'vs/editor/common/core/selection'; import { Handler } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; -import { CompletionItemKind, CompletionItemProvider, CompletionList, CompletionProviderRegistry, CompletionTriggerKind, EncodedTokenizationResult, IState, MetadataConsts, TokenizationRegistry } from 'vs/editor/common/languages'; +import { CompletionItemKind, CompletionItemProvider, CompletionList, CompletionTriggerKind, EncodedTokenizationResult, IState, MetadataConsts, TokenizationRegistry } from 'vs/editor/common/languages'; import { LanguageConfigurationRegistry } from 'vs/editor/common/languages/languageConfigurationRegistry'; import { NullState } from 'vs/editor/common/languages/nullMode'; -import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2'; import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestController'; @@ -28,22 +27,24 @@ import { ISelectedSuggestion } from 'vs/editor/contrib/suggest/browser/suggestWi import { createTestCodeEditor, ITestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { createModelServices, createTextModel, instantiateTextModel } from 'vs/editor/test/common/testTextModel'; import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; -import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { MockContextKeyService, MockKeybindingService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; +import { MockKeybindingService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { ILabelService } from 'vs/platform/label/common/label'; -import { NullLogService } from 'vs/platform/log/common/log'; import { InMemoryStorageService, IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { LanguageFeaturesService } from 'vs/editor/common/services/languageFeaturesService'; +import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -function createMockEditor(model: TextModel): ITestCodeEditor { +function createMockEditor(model: TextModel, languageFeaturesService: ILanguageFeaturesService): ITestCodeEditor { + const editor = createTestCodeEditor(model, { serviceCollection: new ServiceCollection( + [ILanguageFeaturesService, languageFeaturesService], [ITelemetryService, NullTelemetryService], [IStorageService, new InMemoryStorageService()], [IKeybindingService, new MockKeybindingService()], @@ -109,7 +110,7 @@ suite('SuggestModel - Context', function () { const assertAutoTrigger = (model: TextModel, offset: number, expected: boolean, message?: string): void => { const pos = model.getPositionAt(offset); - const editor = createMockEditor(model); + const editor = createMockEditor(model, new LanguageFeaturesService()); editor.setPosition(pos); assert.strictEqual(LineContext.shouldAutoTrigger(editor), expected, message); editor.dispose(); @@ -189,6 +190,8 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { let disposables: DisposableStore; let model: TextModel; + const languageFeaturesService = new LanguageFeaturesService(); + let registry = languageFeaturesService.completionProvider; setup(function () { disposables = new DisposableStore(); @@ -203,24 +206,8 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { function withOracle(callback: (model: SuggestModel, editor: ITestCodeEditor) => any): Promise { return new Promise((resolve, reject) => { - const editor = createMockEditor(model); - const oracle = new SuggestModel( - editor, - new class extends mock() { - override computeWordRanges() { - return Promise.resolve({}); - } - }, - new class extends mock() { - override readText() { - return Promise.resolve('CLIPPY'); - } - }, - NullTelemetryService, - new NullLogService(), - new MockContextKeyService(), - new TestConfigurationService() - ); + const editor = createMockEditor(model, languageFeaturesService); + const oracle = editor.invokeWithinContext(accessor => accessor.get(IInstantiationService).createInstance(SuggestModel, editor)); disposables.add(oracle); disposables.add(editor); @@ -286,7 +273,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { test('events - suggest/empty', function () { - disposables.add(CompletionProviderRegistry.register({ scheme: 'test' }, alwaysEmptySupport)); + disposables.add(registry.register({ scheme: 'test' }, alwaysEmptySupport)); return withOracle(model => { return Promise.all([ @@ -308,7 +295,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { test('trigger - on type', function () { - disposables.add(CompletionProviderRegistry.register({ scheme: 'test' }, alwaysSomethingSupport)); + disposables.add(registry.register({ scheme: 'test' }, alwaysSomethingSupport)); return withOracle((model, editor) => { return assertEvent(model.onDidSuggest, () => { @@ -327,7 +314,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { test('#17400: Keep filtering suggestModel.ts after space', function () { - disposables.add(CompletionProviderRegistry.register({ scheme: 'test' }, { + disposables.add(registry.register({ scheme: 'test' }, { provideCompletionItems(doc, pos): CompletionList { return { incomplete: false, @@ -377,7 +364,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { test('#21484: Trigger character always force a new completion session', function () { - disposables.add(CompletionProviderRegistry.register({ scheme: 'test' }, { + disposables.add(registry.register({ scheme: 'test' }, { provideCompletionItems(doc, pos): CompletionList { return { incomplete: false, @@ -391,7 +378,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { } })); - disposables.add(CompletionProviderRegistry.register({ scheme: 'test' }, { + disposables.add(registry.register({ scheme: 'test' }, { triggerCharacters: ['.'], provideCompletionItems(doc, pos): CompletionList { return { @@ -439,7 +426,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { test('Intellisense Completion doesn\'t respect space after equal sign (.html file), #29353 [1/2]', function () { - disposables.add(CompletionProviderRegistry.register({ scheme: 'test' }, alwaysSomethingSupport)); + disposables.add(registry.register({ scheme: 'test' }, alwaysSomethingSupport)); return withOracle((model, editor) => { @@ -464,7 +451,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { test('Intellisense Completion doesn\'t respect space after equal sign (.html file), #29353 [2/2]', function () { - disposables.add(CompletionProviderRegistry.register({ scheme: 'test' }, alwaysSomethingSupport)); + disposables.add(registry.register({ scheme: 'test' }, alwaysSomethingSupport)); return withOracle((model, editor) => { @@ -489,7 +476,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { test('Incomplete suggestion results cause re-triggering when typing w/o further context, #28400 (1/2)', function () { - disposables.add(CompletionProviderRegistry.register({ scheme: 'test' }, { + disposables.add(registry.register({ scheme: 'test' }, { provideCompletionItems(doc, pos): CompletionList { return { incomplete: true, @@ -526,7 +513,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { test('Incomplete suggestion results cause re-triggering when typing w/o further context, #28400 (2/2)', function () { - disposables.add(CompletionProviderRegistry.register({ scheme: 'test' }, { + disposables.add(registry.register({ scheme: 'test' }, { provideCompletionItems(doc, pos): CompletionList { return { incomplete: true, @@ -569,7 +556,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { test('Trigger character is provided in suggest context', function () { let triggerCharacter = ''; - disposables.add(CompletionProviderRegistry.register({ scheme: 'test' }, { + disposables.add(registry.register({ scheme: 'test' }, { triggerCharacters: ['.'], provideCompletionItems(doc, pos, context): CompletionList { assert.strictEqual(context.triggerKind, CompletionTriggerKind.TriggerCharacter); @@ -602,7 +589,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { }); test('Mac press and hold accent character insertion does not update suggestions, #35269', function () { - disposables.add(CompletionProviderRegistry.register({ scheme: 'test' }, { + disposables.add(registry.register({ scheme: 'test' }, { provideCompletionItems(doc, pos): CompletionList { return { incomplete: true, @@ -645,7 +632,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { }); test('Backspace should not always cancel code completion, #36491', function () { - disposables.add(CompletionProviderRegistry.register({ scheme: 'test' }, alwaysSomethingSupport)); + disposables.add(registry.register({ scheme: 'test' }, alwaysSomethingSupport)); return withOracle(async (model, editor) => { await assertEvent(model.onDidSuggest, () => { @@ -674,7 +661,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { }); test('Text changes for completion CodeAction are affected by the completion #39893', function () { - disposables.add(CompletionProviderRegistry.register({ scheme: 'test' }, { + disposables.add(registry.register({ scheme: 'test' }, { provideCompletionItems(doc, pos): CompletionList { return { incomplete: true, @@ -724,7 +711,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { test('Completion unexpectedly triggers on second keypress of an edit group in a snippet #43523', function () { - disposables.add(CompletionProviderRegistry.register({ scheme: 'test' }, alwaysSomethingSupport)); + disposables.add(registry.register({ scheme: 'test' }, alwaysSomethingSupport)); return withOracle((model, editor) => { return assertEvent(model.onDidSuggest, () => { @@ -748,7 +735,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { let disposeA = 0; let disposeB = 0; - disposables.add(CompletionProviderRegistry.register({ scheme: 'test' }, { + disposables.add(registry.register({ scheme: 'test' }, { provideCompletionItems(doc, pos) { return { incomplete: true, @@ -763,7 +750,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { }; } })); - disposables.add(CompletionProviderRegistry.register({ scheme: 'test' }, { + disposables.add(registry.register({ scheme: 'test' }, { provideCompletionItems(doc, pos) { return { incomplete: false, @@ -817,7 +804,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { let countA = 0; let countB = 0; - disposables.add(CompletionProviderRegistry.register({ scheme: 'test' }, { + disposables.add(registry.register({ scheme: 'test' }, { provideCompletionItems(doc, pos) { countA += 1; return { @@ -831,7 +818,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { }; } })); - disposables.add(CompletionProviderRegistry.register({ scheme: 'test' }, { + disposables.add(registry.register({ scheme: 'test' }, { provideCompletionItems(doc, pos) { countB += 1; if (!doc.getWordUntilPosition(pos).word.startsWith('a')) { @@ -880,7 +867,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { test('registerCompletionItemProvider with letters as trigger characters block other completion items to show up #127815', async function () { - disposables.add(CompletionProviderRegistry.register({ scheme: 'test' }, { + disposables.add(registry.register({ scheme: 'test' }, { provideCompletionItems(doc, pos) { return { suggestions: [{ @@ -892,7 +879,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { }; } })); - disposables.add(CompletionProviderRegistry.register({ scheme: 'test' }, { + disposables.add(registry.register({ scheme: 'test' }, { triggerCharacters: ['a', '.'], provideCompletionItems(doc, pos) { return { diff --git a/src/vs/editor/contrib/suggest/test/browser/wordDistance.test.ts b/src/vs/editor/contrib/suggest/test/browser/wordDistance.test.ts index 8caefe114eb..1cad87a9f97 100644 --- a/src/vs/editor/contrib/suggest/test/browser/wordDistance.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/wordDistance.test.ts @@ -25,7 +25,7 @@ import { createTextModel } from 'vs/editor/test/common/testTextModel'; import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; import { TestLanguageConfigurationService } from 'vs/editor/test/common/modes/testLanguageConfigurationService'; import { NullLogService } from 'vs/platform/log/common/log'; -import { LanguageFeatureService } from 'vs/editor/common/services/languageFeaturesService'; +import { LanguageFeaturesService } from 'vs/editor/common/services/languageFeaturesService'; suite('suggest, word distance', function () { @@ -68,7 +68,7 @@ suite('suggest, word distance', function () { private _worker = new EditorSimpleWorker(new class extends mock() { }, null); constructor() { - super(modelService, new class extends mock() { }, new NullLogService(), new TestLanguageConfigurationService(), new LanguageFeatureService()); + super(modelService, new class extends mock() { }, new NullLogService(), new TestLanguageConfigurationService(), new LanguageFeaturesService()); this._worker.acceptNewModel({ url: model.uri.toString(), lines: model.getLinesContent(), diff --git a/src/vs/editor/standalone/browser/standaloneLanguages.ts b/src/vs/editor/standalone/browser/standaloneLanguages.ts index 89644e5848b..3dc57f7bc9e 100644 --- a/src/vs/editor/standalone/browser/standaloneLanguages.ts +++ b/src/vs/editor/standalone/browser/standaloneLanguages.ts @@ -566,7 +566,8 @@ export function registerLinkProvider(languageId: string, provider: languages.Lin * Register a completion item provider (use by e.g. suggestions). */ export function registerCompletionItemProvider(languageId: string, provider: languages.CompletionItemProvider): IDisposable { - return languages.CompletionProviderRegistry.register(languageId, provider); + const languageFeaturesService = StandaloneServices.get(ILanguageFeaturesService); + return languageFeaturesService.completionProvider.register(languageId, provider); } /** diff --git a/src/vs/editor/test/browser/testCodeEditor.ts b/src/vs/editor/test/browser/testCodeEditor.ts index d9ed93506ed..ddfe521eb5c 100644 --- a/src/vs/editor/test/browser/testCodeEditor.ts +++ b/src/vs/editor/test/browser/testCodeEditor.ts @@ -47,7 +47,7 @@ import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService'; import { EditorConfiguration, IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; import { ILanguageFeatureDebounceService, LanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; -import { LanguageFeatureService } from 'vs/editor/common/services/languageFeaturesService'; +import { LanguageFeaturesService } from 'vs/editor/common/services/languageFeaturesService'; export interface ITestCodeEditor extends IActiveCodeEditor { getViewModel(): ViewModel | undefined; @@ -175,7 +175,7 @@ export function createCodeEditorServices(disposables: DisposableStore, services: define(ICommandService, TestCommandService); define(ITelemetryService, NullTelemetryServiceShape); define(ILanguageFeatureDebounceService, LanguageFeatureDebounceService); - define(ILanguageFeaturesService, LanguageFeatureService); + define(ILanguageFeaturesService, LanguageFeaturesService); const instantiationService = new TestInstantiationService(services); disposables.add(toDisposable(() => { diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 89941ba635c..7c51642fe8c 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -511,7 +511,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha }); }; } - this._registrations.set(handle, modes.CompletionProviderRegistry.register(selector, provider)); + this._registrations.set(handle, this._languageFeaturesService.completionProvider.register(selector, provider)); } $registerInlineCompletionsSupport(handle: number, selector: IDocumentFilterDto[]): void { diff --git a/src/vs/workbench/api/test/browser/extHostApiCommands.test.ts b/src/vs/workbench/api/test/browser/extHostApiCommands.test.ts index 49c0b32822f..d1e7caa8879 100644 --- a/src/vs/workbench/api/test/browser/extHostApiCommands.test.ts +++ b/src/vs/workbench/api/test/browser/extHostApiCommands.test.ts @@ -56,7 +56,7 @@ import 'vs/editor/contrib/suggest/browser/suggest'; import 'vs/editor/contrib/rename/browser/rename'; import 'vs/editor/contrib/inlayHints/browser/inlayHintsController'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; -import { LanguageFeatureService } from 'vs/editor/common/services/languageFeaturesService'; +import { LanguageFeaturesService } from 'vs/editor/common/services/languageFeaturesService'; function assertRejects(fn: () => Promise, message: string = 'Expected rejection') { return fn().then(() => assert.ok(false, message), _err => assert.ok(true)); @@ -96,7 +96,7 @@ suite('ExtHostLanguageFeatureCommands', function () { let insta: IInstantiationService; rpcProtocol = new TestRPCProtocol(); const services = new ServiceCollection(); - services.set(ILanguageFeaturesService, new SyncDescriptor(LanguageFeatureService)); + services.set(ILanguageFeaturesService, new SyncDescriptor(LanguageFeaturesService)); services.set(IExtensionService, new class extends mock() { override async activateByEvent() { diff --git a/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts b/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts index 1ebb4f6a3cd..a34bc44ae6a 100644 --- a/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts @@ -52,7 +52,7 @@ import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSyste import { URITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService'; import { OutlineModel } from 'vs/editor/contrib/documentSymbols/browser/outlineModel'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; -import { LanguageFeatureService } from 'vs/editor/common/services/languageFeaturesService'; +import { LanguageFeaturesService } from 'vs/editor/common/services/languageFeaturesService'; suite('ExtHostLanguageFeatures', function () { @@ -79,7 +79,7 @@ suite('ExtHostLanguageFeatures', function () { rpcProtocol = new TestRPCProtocol(); - languageFeaturesService = new LanguageFeatureService(); + languageFeaturesService = new LanguageFeaturesService(); // Use IInstantiationService to get typechecking when instantiating let inst: IInstantiationService; @@ -952,7 +952,7 @@ suite('ExtHostLanguageFeatures', function () { }, [])); await rpcProtocol.sync(); - const { items } = await provideSuggestionItems(model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(modes.CompletionItemKind.Snippet))); + const { items } = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(modes.CompletionItemKind.Snippet))); assert.strictEqual(items.length, 1); assert.strictEqual(items[0].completion.insertText, 'testing2'); }); @@ -972,7 +972,7 @@ suite('ExtHostLanguageFeatures', function () { }, [])); await rpcProtocol.sync(); - const { items } = await provideSuggestionItems(model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(modes.CompletionItemKind.Snippet))); + const { items } = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(modes.CompletionItemKind.Snippet))); assert.strictEqual(items.length, 1); assert.strictEqual(items[0].completion.insertText, 'weak-selector'); }); @@ -992,7 +992,7 @@ suite('ExtHostLanguageFeatures', function () { }, [])); await rpcProtocol.sync(); - const { items } = await provideSuggestionItems(model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(modes.CompletionItemKind.Snippet))); + const { items } = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(modes.CompletionItemKind.Snippet))); assert.strictEqual(items.length, 2); assert.strictEqual(items[0].completion.insertText, 'strong-1'); // sort by label assert.strictEqual(items[1].completion.insertText, 'strong-2'); @@ -1014,7 +1014,7 @@ suite('ExtHostLanguageFeatures', function () { await rpcProtocol.sync(); - const { items } = await provideSuggestionItems(model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(modes.CompletionItemKind.Snippet))); + const { items } = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(modes.CompletionItemKind.Snippet))); assert.strictEqual(items[0].container.incomplete, false); }); @@ -1027,7 +1027,7 @@ suite('ExtHostLanguageFeatures', function () { }, [])); await rpcProtocol.sync(); - provideSuggestionItems(model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(modes.CompletionItemKind.Snippet))).then(model => { + await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(modes.CompletionItemKind.Snippet))).then(model => { assert.strictEqual(model.items[0].container.incomplete, true); }); }); diff --git a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts index e7724dec70b..37dcd9f9c2d 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts @@ -39,6 +39,7 @@ import { HistoryNavigator } from 'vs/base/common/history'; import { createAndBindHistoryNavigationWidgetScopedContextKeyService, IHistoryNavigationContext } from 'vs/platform/history/browser/contextScopedHistoryWidget'; import { IHistoryNavigationWidget } from 'vs/base/browser/history'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; export interface SuggestResultsProvider { /** @@ -125,6 +126,7 @@ export class SuggestEnabledInput extends Widget implements IThemable { @IInstantiationService defaultInstantiationService: IInstantiationService, @IModelService modelService: IModelService, @IContextKeyService contextKeyService: IContextKeyService, + @ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService, ) { super(); @@ -195,7 +197,7 @@ export class SuggestEnabledInput extends Widget implements IThemable { this.setValue(options.value || ''); - this._register(modes.CompletionProviderRegistry.register({ scheme: scopeHandle.scheme, pattern: '**/' + scopeHandle.path, hasAccessToAllModels: true }, { + this._register(languageFeaturesService.completionProvider.register({ scheme: scopeHandle.scheme, pattern: '**/' + scopeHandle.path, hasAccessToAllModels: true }, { triggerCharacters: validatedSuggestProvider.triggerCharacters, provideCompletionItems: (model: ITextModel, position: Position, _context: modes.CompletionContext) => { let query = model.getValue(); @@ -314,8 +316,9 @@ export class SuggestEnabledInputWithHistory extends SuggestEnabledInput implemen @IInstantiationService instantiationService: IInstantiationService, @IModelService modelService: IModelService, @IContextKeyService contextKeyService: IContextKeyService, + @ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService, ) { - super(id, parent, suggestionProvider, ariaLabel, resourceHandle, suggestOptions, instantiationService, modelService, contextKeyService); + super(id, parent, suggestionProvider, ariaLabel, resourceHandle, suggestOptions, instantiationService, modelService, contextKeyService, languageFeaturesService); this.history = new HistoryNavigator(history, 100); } @@ -391,8 +394,9 @@ export class ContextScopedSuggestEnabledInputWithHistory extends SuggestEnabledI @IInstantiationService instantiationService: IInstantiationService, @IModelService modelService: IModelService, @IContextKeyService contextKeyService: IContextKeyService, + @ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService, ) { - super(options, instantiationService, modelService, contextKeyService); + super(options, instantiationService, modelService, contextKeyService, languageFeaturesService); const { historyNavigationBackwardsEnablement, historyNavigationForwardsEnablement } = this.historyContext; this._register(this.inputWidget.onDidChangeCursorPosition(({ position }) => { diff --git a/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts b/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts index 6cbb8681b4d..62f8e21bd24 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts @@ -22,7 +22,7 @@ import { ServicesAccessor, EditorCommand, registerEditorCommand } from 'vs/edito import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { IModelService } from 'vs/editor/common/services/model'; import { URI as uri } from 'vs/base/common/uri'; -import { CompletionProviderRegistry, CompletionList, CompletionContext, CompletionItemKind } from 'vs/editor/common/languages'; +import { CompletionList, CompletionContext, CompletionItemKind } from 'vs/editor/common/languages'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ITextModel } from 'vs/editor/common/model'; import { provideSuggestionItems, CompletionOptions } from 'vs/editor/contrib/suggest/browser/suggest'; @@ -38,6 +38,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEditorOptions, EditorOption } from 'vs/editor/common/config/editorOptions'; import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry'; +import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; const $ = dom.$; const IPrivateBreakpointWidgetService = createDecorator('privateBreakpointWidgetService'); @@ -97,7 +98,8 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi @IInstantiationService private readonly instantiationService: IInstantiationService, @IModelService private readonly modelService: IModelService, @ICodeEditorService private readonly codeEditorService: ICodeEditorService, - @IConfigurationService private readonly _configurationService: IConfigurationService + @IConfigurationService private readonly _configurationService: IConfigurationService, + @ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService, ) { super(editor, { showFrame: true, showArrow: false, frameWidth: 1, isAccessible: true }); @@ -245,12 +247,12 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi this.input.getModel().onDidChangeContent(() => setDecorations()); this.themeService.onDidColorThemeChange(() => setDecorations()); - this.toDispose.push(CompletionProviderRegistry.register({ scheme: DEBUG_SCHEME, hasAccessToAllModels: true }, { + this.toDispose.push(this.languageFeaturesService.completionProvider.register({ scheme: DEBUG_SCHEME, hasAccessToAllModels: true }, { provideCompletionItems: (model: ITextModel, position: Position, _context: CompletionContext, token: CancellationToken): Promise => { let suggestionsPromise: Promise; const underlyingModel = this.editor.getModel(); if (underlyingModel && (this.context === Context.CONDITION || (this.context === Context.LOG_MESSAGE && isCurlyBracketOpen(this.input)))) { - suggestionsPromise = provideSuggestionItems(underlyingModel, new Position(this.lineNumber, 1), new CompletionOptions(undefined, new Set().add(CompletionItemKind.Snippet)), _context, token).then(suggestions => { + suggestionsPromise = provideSuggestionItems(this.languageFeaturesService.completionProvider, underlyingModel, new Position(this.lineNumber, 1), new CompletionOptions(undefined, new Set().add(CompletionItemKind.Snippet)), _context, token).then(suggestions => { let overwriteBefore = 0; if (this.context === Context.CONDITION) { diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 7fc4f3da6b1..61d0bdc38bd 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -30,7 +30,7 @@ import { Range } from 'vs/editor/common/core/range'; import { IDecorationOptions } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ITextModel } from 'vs/editor/common/model'; -import { CompletionContext, CompletionItem, CompletionItemInsertTextRule, CompletionItemKind, CompletionItemKinds, CompletionList, CompletionProviderRegistry } from 'vs/editor/common/languages'; +import { CompletionContext, CompletionItem, CompletionItemInsertTextRule, CompletionItemKind, CompletionItemKinds, CompletionList } from 'vs/editor/common/languages'; import { IModelService } from 'vs/editor/common/services/model'; import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfiguration'; import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestController'; @@ -64,6 +64,7 @@ import { ReplAccessibilityProvider, ReplDataSource, ReplDelegate, ReplEvaluation import { CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_REPL, CONTEXT_MULTI_SESSION_REPL, DEBUG_SCHEME, getStateLabel, IDebugConfiguration, IDebugService, IDebugSession, IReplElement, REPL_VIEW_ID, State } from 'vs/workbench/contrib/debug/common/debug'; import { ReplGroup } from 'vs/workbench/contrib/debug/common/replModel'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; const $ = dom.$; @@ -125,7 +126,8 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { @IKeybindingService keybindingService: IKeybindingService, @IOpenerService openerService: IOpenerService, @ITelemetryService telemetryService: ITelemetryService, - @IMenuService menuService: IMenuService + @IMenuService menuService: IMenuService, + @ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService, ) { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); @@ -219,7 +221,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { this.completionItemProvider.dispose(); } if (session.capabilities.supportsCompletionsRequest) { - this.completionItemProvider = CompletionProviderRegistry.register({ scheme: DEBUG_SCHEME, pattern: '**/replinput', hasAccessToAllModels: true }, { + this.completionItemProvider = this.languageFeaturesService.completionProvider.register({ scheme: DEBUG_SCHEME, pattern: '**/replinput', hasAccessToAllModels: true }, { triggerCharacters: session.capabilities.completionTriggerCharacters || ['.'], provideCompletionItems: async (_: ITextModel, position: Position, _context: CompletionContext, token: CancellationToken): Promise => { // Disable history navigation because up and down are used to navigate through the suggest widget diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsCompletionItemsProvider.ts b/src/vs/workbench/contrib/extensions/browser/extensionsCompletionItemsProvider.ts index 72e577dcc51..3f8c429d93f 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsCompletionItemsProvider.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsCompletionItemsProvider.ts @@ -9,19 +9,21 @@ import { getLocation, parse } from 'vs/base/common/json'; import { Disposable } from 'vs/base/common/lifecycle'; import { Position } from 'vs/editor/common/core/position'; import { ITextModel } from 'vs/editor/common/model'; -import { CompletionContext, CompletionList, CompletionProviderRegistry, CompletionItemKind, CompletionItem } from 'vs/editor/common/languages'; +import { CompletionContext, CompletionList, CompletionItemKind, CompletionItem } from 'vs/editor/common/languages'; import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { Range } from 'vs/editor/common/core/range'; +import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; export class ExtensionsCompletionItemsProvider extends Disposable implements IWorkbenchContribution { constructor( @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, + @ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService, ) { super(); - this._register(CompletionProviderRegistry.register({ language: 'jsonc', pattern: '**/settings.json' }, { + this._register(languageFeaturesService.completionProvider.register({ language: 'jsonc', pattern: '**/settings.json' }, { provideCompletionItems: async (model: ITextModel, position: Position, _context: CompletionContext, token: CancellationToken): Promise => { const getWordRangeAtPosition = (model: ITextModel, position: Position): Range | null => { const wordAtPosition = model.getWordAtPosition(position); diff --git a/src/vs/workbench/contrib/snippets/browser/tabCompletion.ts b/src/vs/workbench/contrib/snippets/browser/tabCompletion.ts index 14d5cddd6cd..04f325ab208 100644 --- a/src/vs/workbench/contrib/snippets/browser/tabCompletion.ts +++ b/src/vs/workbench/contrib/snippets/browser/tabCompletion.ts @@ -21,6 +21,7 @@ import { SnippetCompletion } from './snippetCompletionProvider'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { EditorState, CodeEditorStateFlag } from 'vs/editor/contrib/editorState/browser/editorState'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; export class TabCompletionController implements IEditorContribution { @@ -39,6 +40,7 @@ export class TabCompletionController implements IEditorContribution { constructor( private readonly _editor: ICodeEditor, + @IInstantiationService private readonly _instantiationService: IInstantiationService, @ISnippetsService private readonly _snippetService: ISnippetsService, @IClipboardService private readonly _clipboardService: IClipboardService, @IContextKeyService contextKeyService: IContextKeyService, @@ -148,7 +150,7 @@ export class TabCompletionController implements IEditorContribution { } else if (this._activeSnippets.length > 1) { // two or more -> show IntelliSense box const position = this._editor.getPosition(); - showSimpleSuggestions(this._editor, this._activeSnippets.map(snippet => { + this._instantiationService.invokeFunction(showSimpleSuggestions, this._editor, this._activeSnippets.map(snippet => { const range = Range.fromPositions(position.delta(0, -snippet.prefix.length), position); return new SnippetCompletion(snippet, range); })); diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 0ea70043809..8b3611d7326 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -152,7 +152,7 @@ import { ILanguageFeatureDebounceService, LanguageFeatureDebounceService } from import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IChange, IDiffComputationResult } from 'vs/editor/common/diff/diffComputer'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; -import { LanguageFeatureService } from 'vs/editor/common/services/languageFeaturesService'; +import { LanguageFeaturesService } from 'vs/editor/common/services/languageFeaturesService'; export function createFileEditorInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput { return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined, undefined, undefined, undefined, undefined); @@ -238,7 +238,7 @@ export function workbenchInstantiationService( instantiationService.stub(IAccessibilityService, accessibilityService); instantiationService.stub(IFileDialogService, instantiationService.createInstance(TestFileDialogService)); instantiationService.stub(ILanguageService, disposables.add(instantiationService.createInstance(LanguageService))); - instantiationService.stub(ILanguageFeaturesService, new LanguageFeatureService()); + instantiationService.stub(ILanguageFeaturesService, new LanguageFeaturesService()); instantiationService.stub(ILanguageFeatureDebounceService, instantiationService.createInstance(LanguageFeatureDebounceService)); instantiationService.stub(IHistoryService, new TestHistoryService()); instantiationService.stub(ITextResourcePropertiesService, new TestTextResourcePropertiesService(configService));