diff --git a/src/vs/editor/common/services/modelService.ts b/src/vs/editor/common/services/modelService.ts index 8eef1818ee0..4b96565c68a 100644 --- a/src/vs/editor/common/services/modelService.ts +++ b/src/vs/editor/common/services/modelService.ts @@ -32,6 +32,8 @@ import { SemanticTokensProviderStyling, toMultilineTokens2 } from 'vs/editor/com import { getDocumentSemanticTokens, hasDocumentSemanticTokensProvider, isSemanticTokens, isSemanticTokensEdits } from 'vs/editor/common/services/getSemanticTokens'; import { equals } from 'vs/base/common/objects'; import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; +import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce'; +import { StopWatch } from 'vs/base/common/stopwatch'; export interface IEditorSemanticHighlightingOptions { enabled: true | false | 'configuredByTheme'; @@ -163,7 +165,8 @@ export class ModelService extends Disposable implements IModelService { @ILogService private readonly _logService: ILogService, @IUndoRedoService private readonly _undoRedoService: IUndoRedoService, @ILanguageService private readonly _languageService: ILanguageService, - @ILanguageConfigurationService private readonly _languageConfigurationService: ILanguageConfigurationService + @ILanguageConfigurationService private readonly _languageConfigurationService: ILanguageConfigurationService, + @ILanguageFeatureDebounceService private readonly _languageFeatureDebounceService: ILanguageFeatureDebounceService ) { super(); this._modelCreationOptionsByLanguageAndResource = Object.create(null); @@ -175,7 +178,7 @@ export class ModelService extends Disposable implements IModelService { this._register(this._configurationService.onDidChangeConfiguration(() => this._updateModelOptions())); this._updateModelOptions(); - this._register(new SemanticColoringFeature(this, this._themeService, this._configurationService, this._semanticStyling)); + this._register(new SemanticColoringFeature(this._semanticStyling, this, this._themeService, this._configurationService, this._languageFeatureDebounceService)); } private static _readModelOptions(config: IRawConfig, isForSimpleWidget: boolean): ITextModelCreationOptions { @@ -652,13 +655,19 @@ class SemanticColoringFeature extends Disposable { private readonly _watchers: Record; private readonly _semanticStyling: SemanticStyling; - constructor(modelService: IModelService, themeService: IThemeService, configurationService: IConfigurationService, semanticStyling: SemanticStyling) { + constructor( + semanticStyling: SemanticStyling, + @IModelService modelService: IModelService, + @IThemeService themeService: IThemeService, + @IConfigurationService configurationService: IConfigurationService, + @ILanguageFeatureDebounceService languageFeatureDebounceService: ILanguageFeatureDebounceService + ) { super(); this._watchers = Object.create(null); this._semanticStyling = semanticStyling; const register = (model: ITextModel) => { - this._watchers[model.uri.toString()] = new ModelSemanticColoring(model, themeService, this._semanticStyling); + this._watchers[model.uri.toString()] = new ModelSemanticColoring(model, this._semanticStyling, themeService, languageFeatureDebounceService); }; const deregister = (model: ITextModel, modelSemanticColoring: ModelSemanticColoring) => { modelSemanticColoring.dispose(); @@ -736,30 +745,38 @@ class SemanticTokensResponse { export class ModelSemanticColoring extends Disposable { - public static FETCH_DOCUMENT_SEMANTIC_TOKENS_DELAY = 300; + public static REQUEST_MIN_DELAY = 300; + public static REQUEST_MAX_DELAY = 2000; private _isDisposed: boolean; private readonly _model: ITextModel; private readonly _semanticStyling: SemanticStyling; + private readonly _debounceInformation: IFeatureDebounceInformation; private readonly _fetchDocumentSemanticTokens: RunOnceScheduler; private _currentDocumentResponse: SemanticTokensResponse | null; private _currentDocumentRequestCancellationTokenSource: CancellationTokenSource | null; private _documentProvidersChangeListeners: IDisposable[]; - constructor(model: ITextModel, themeService: IThemeService, stylingProvider: SemanticStyling) { + constructor( + model: ITextModel, + stylingProvider: SemanticStyling, + @IThemeService themeService: IThemeService, + @ILanguageFeatureDebounceService languageFeatureDebounceService: ILanguageFeatureDebounceService + ) { super(); this._isDisposed = false; this._model = model; this._semanticStyling = stylingProvider; - this._fetchDocumentSemanticTokens = this._register(new RunOnceScheduler(() => this._fetchDocumentSemanticTokensNow(), ModelSemanticColoring.FETCH_DOCUMENT_SEMANTIC_TOKENS_DELAY)); + this._debounceInformation = languageFeatureDebounceService.for(DocumentSemanticTokensProviderRegistry, 'DocumentSemanticTokens', { min: ModelSemanticColoring.REQUEST_MIN_DELAY, max: ModelSemanticColoring.REQUEST_MAX_DELAY }); + this._fetchDocumentSemanticTokens = this._register(new RunOnceScheduler(() => this._fetchDocumentSemanticTokensNow(), ModelSemanticColoring.REQUEST_MIN_DELAY)); this._currentDocumentResponse = null; this._currentDocumentRequestCancellationTokenSource = null; this._documentProvidersChangeListeners = []; this._register(this._model.onDidChangeContent(() => { if (!this._fetchDocumentSemanticTokens.isScheduled()) { - this._fetchDocumentSemanticTokens.schedule(); + this._fetchDocumentSemanticTokens.schedule(this._debounceInformation.get(this._model)); } })); this._register(this._model.onDidChangeLanguage(() => { @@ -775,6 +792,7 @@ export class ModelSemanticColoring extends Disposable { this._setDocumentSemanticTokens(null, null, null, []); this._fetchDocumentSemanticTokens.schedule(0); })); + const bindDocumentChangeListeners = () => { dispose(this._documentProvidersChangeListeners); this._documentProvidersChangeListeners = []; @@ -787,13 +805,13 @@ export class ModelSemanticColoring extends Disposable { bindDocumentChangeListeners(); this._register(DocumentSemanticTokensProviderRegistry.onDidChange(() => { bindDocumentChangeListeners(); - this._fetchDocumentSemanticTokens.schedule(); + this._fetchDocumentSemanticTokens.schedule(this._debounceInformation.get(this._model)); })); this._register(themeService.onDidColorThemeChange(_ => { // clear out existing tokens this._setDocumentSemanticTokens(null, null, null, []); - this._fetchDocumentSemanticTokens.schedule(); + this._fetchDocumentSemanticTokens.schedule(this._debounceInformation.get(this._model)); })); this._fetchDocumentSemanticTokens.schedule(0); @@ -840,7 +858,9 @@ export class ModelSemanticColoring extends Disposable { pendingChanges.push(e); }); + const sw = new StopWatch(false); request.then((res) => { + this._debounceInformation.update(this._model, sw.elapsed()); this._currentDocumentRequestCancellationTokenSource = null; contentChangeListener.dispose(); @@ -865,7 +885,7 @@ export class ModelSemanticColoring extends Disposable { if (pendingChanges.length > 0) { // More changes occurred while the request was running if (!this._fetchDocumentSemanticTokens.isScheduled()) { - this._fetchDocumentSemanticTokens.schedule(); + this._fetchDocumentSemanticTokens.schedule(this._debounceInformation.get(this._model)); } } }); @@ -881,7 +901,7 @@ export class ModelSemanticColoring extends Disposable { const currentResponse = this._currentDocumentResponse; const rescheduleIfNeeded = () => { if (pendingChanges.length > 0 && !this._fetchDocumentSemanticTokens.isScheduled()) { - this._fetchDocumentSemanticTokens.schedule(); + this._fetchDocumentSemanticTokens.schedule(this._debounceInformation.get(this._model)); } }; diff --git a/src/vs/editor/test/common/services/modelService.test.ts b/src/vs/editor/test/common/services/modelService.test.ts index 2d8e6c4bef0..b29147d662d 100644 --- a/src/vs/editor/test/common/services/modelService.test.ts +++ b/src/vs/editor/test/common/services/modelService.test.ts @@ -13,7 +13,7 @@ import { Selection } from 'vs/editor/common/core/selection'; import { createStringBuilder } from 'vs/editor/common/core/stringBuilder'; import { DefaultEndOfLine, ITextModel } from 'vs/editor/common/model'; import { createTextBuffer } from 'vs/editor/common/model/textModel'; -import { ModelSemanticColoring, ModelService } from 'vs/editor/common/services/modelService'; +import { ModelService } from 'vs/editor/common/services/modelService'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { TestColorTheme, TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { NullLogService } from 'vs/platform/log/common/log'; @@ -33,6 +33,8 @@ import { ILanguageService } from 'vs/editor/common/services/language'; import { TestTextResourcePropertiesService } from 'vs/editor/test/common/services/testTextResourcePropertiesService'; import { TestLanguageConfigurationService } from 'vs/editor/test/common/modes/testLanguageConfigurationService'; import { getDocumentSemanticTokens, isSemanticTokens } from 'vs/editor/common/services/getSemanticTokens'; +import { LanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce'; +import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; const GENERATE_TESTS = false; @@ -47,14 +49,16 @@ suite('ModelService', () => { configService.setUserConfiguration('files', { 'eol': '\r\n' }, URI.file(platform.isWindows ? 'c:\\myroot' : '/myroot')); const dialogService = new TestDialogService(); + const logService = new NullLogService(); modelService = disposables.add(new ModelService( configService, new TestTextResourcePropertiesService(configService), new TestThemeService(), - new NullLogService(), + logService, new UndoRedoService(dialogService, new TestNotificationService()), disposables.add(new LanguageService()), - new TestLanguageConfigurationService() + new TestLanguageConfigurationService(), + new LanguageFeatureDebounceService(logService) )); }); @@ -409,158 +413,160 @@ suite('ModelService', () => { suite('ModelSemanticColoring', () => { const disposables = new DisposableStore(); - const ORIGINAL_FETCH_DOCUMENT_SEMANTIC_TOKENS_DELAY = ModelSemanticColoring.FETCH_DOCUMENT_SEMANTIC_TOKENS_DELAY; let modelService: IModelService; let languageService: ILanguageService; setup(() => { - ModelSemanticColoring.FETCH_DOCUMENT_SEMANTIC_TOKENS_DELAY = 0; - const configService = new TestConfigurationService({ editor: { semanticHighlighting: true } }); const themeService = new TestThemeService(); themeService.setTheme(new TestColorTheme({}, ColorScheme.DARK, true)); + const logService = new NullLogService(); modelService = disposables.add(new ModelService( configService, new TestTextResourcePropertiesService(configService), themeService, - new NullLogService(), + logService, new UndoRedoService(new TestDialogService(), new TestNotificationService()), disposables.add(new LanguageService()), - new TestLanguageConfigurationService() + new TestLanguageConfigurationService(), + new LanguageFeatureDebounceService(logService) )); languageService = disposables.add(new LanguageService(false)); }); teardown(() => { disposables.clear(); - ModelSemanticColoring.FETCH_DOCUMENT_SEMANTIC_TOKENS_DELAY = ORIGINAL_FETCH_DOCUMENT_SEMANTIC_TOKENS_DELAY; }); test('DocumentSemanticTokens should be fetched when the result is empty if there are pending changes', async () => { + await runWithFakedTimers({}, async () => { - disposables.add(ModesRegistry.registerLanguage({ id: 'testMode' })); + disposables.add(ModesRegistry.registerLanguage({ id: 'testMode' })); - const inFirstCall = new Barrier(); - const delayFirstResult = new Barrier(); - const secondResultProvided = new Barrier(); - let callCount = 0; + const inFirstCall = new Barrier(); + const delayFirstResult = new Barrier(); + const secondResultProvided = new Barrier(); + let callCount = 0; - disposables.add(DocumentSemanticTokensProviderRegistry.register('testMode', new class implements DocumentSemanticTokensProvider { - getLegend(): SemanticTokensLegend { - return { tokenTypes: ['class'], tokenModifiers: [] }; - } - async provideDocumentSemanticTokens(model: ITextModel, lastResultId: string | null, token: CancellationToken): Promise { - callCount++; - if (callCount === 1) { - assert.ok('called once'); - inFirstCall.open(); - await delayFirstResult.wait(); - await timeout(0); // wait for the simple scheduler to fire to check that we do actually get rescheduled - return null; + disposables.add(DocumentSemanticTokensProviderRegistry.register('testMode', new class implements DocumentSemanticTokensProvider { + getLegend(): SemanticTokensLegend { + return { tokenTypes: ['class'], tokenModifiers: [] }; } - if (callCount === 2) { - assert.ok('called twice'); - secondResultProvided.open(); - return null; + async provideDocumentSemanticTokens(model: ITextModel, lastResultId: string | null, token: CancellationToken): Promise { + callCount++; + if (callCount === 1) { + assert.ok('called once'); + inFirstCall.open(); + await delayFirstResult.wait(); + await timeout(0); // wait for the simple scheduler to fire to check that we do actually get rescheduled + return null; + } + if (callCount === 2) { + assert.ok('called twice'); + secondResultProvided.open(); + return null; + } + assert.fail('Unexpected call'); } - assert.fail('Unexpected call'); - } - releaseDocumentSemanticTokens(resultId: string | undefined): void { - } - })); + releaseDocumentSemanticTokens(resultId: string | undefined): void { + } + })); - const textModel = disposables.add(modelService.createModel('Hello world', languageService.createById('testMode'))); + const textModel = disposables.add(modelService.createModel('Hello world', languageService.createById('testMode'))); - // wait for the provider to be called - await inFirstCall.wait(); + // wait for the provider to be called + await inFirstCall.wait(); - // the provider is now in the provide call - // change the text buffer while the provider is running - textModel.applyEdits([{ range: new Range(1, 1, 1, 1), text: 'x' }]); + // the provider is now in the provide call + // change the text buffer while the provider is running + textModel.applyEdits([{ range: new Range(1, 1, 1, 1), text: 'x' }]); - // let the provider finish its first result - delayFirstResult.open(); + // let the provider finish its first result + delayFirstResult.open(); - // we need to check that the provider is called again, even if it returns null - await secondResultProvided.wait(); + // we need to check that the provider is called again, even if it returns null + await secondResultProvided.wait(); - // assert that it got called twice - assert.strictEqual(callCount, 2); + // assert that it got called twice + assert.strictEqual(callCount, 2); + }); }); test('DocumentSemanticTokens should be pick the token provider with actual items', async () => { + await runWithFakedTimers({}, async () => { - let callCount = 0; - disposables.add(ModesRegistry.registerLanguage({ id: 'testMode2' })); - disposables.add(DocumentSemanticTokensProviderRegistry.register('testMode2', new class implements DocumentSemanticTokensProvider { - getLegend(): SemanticTokensLegend { - return { tokenTypes: ['class1'], tokenModifiers: [] }; - } - async provideDocumentSemanticTokens(model: ITextModel, lastResultId: string | null, token: CancellationToken): Promise { - callCount++; - // For a secondary request return a different value - if (lastResultId) { + let callCount = 0; + disposables.add(ModesRegistry.registerLanguage({ id: 'testMode2' })); + disposables.add(DocumentSemanticTokensProviderRegistry.register('testMode2', new class implements DocumentSemanticTokensProvider { + getLegend(): SemanticTokensLegend { + return { tokenTypes: ['class1'], tokenModifiers: [] }; + } + async provideDocumentSemanticTokens(model: ITextModel, lastResultId: string | null, token: CancellationToken): Promise { + callCount++; + // For a secondary request return a different value + if (lastResultId) { + return { + data: new Uint32Array([2, 1, 1, 1, 1, 0, 2, 1, 1, 1]) + }; + } return { - data: new Uint32Array([2, 1, 1, 1, 1, 0, 2, 1, 1, 1]) + resultId: '1', + data: new Uint32Array([0, 1, 1, 1, 1, 0, 2, 1, 1, 1]) }; } - return { - resultId: '1', - data: new Uint32Array([0, 1, 1, 1, 1, 0, 2, 1, 1, 1]) - }; - } - releaseDocumentSemanticTokens(resultId: string | undefined): void { - } - })); - disposables.add(DocumentSemanticTokensProviderRegistry.register('testMode2', new class implements DocumentSemanticTokensProvider { - getLegend(): SemanticTokensLegend { - return { tokenTypes: ['class2'], tokenModifiers: [] }; - } - async provideDocumentSemanticTokens(model: ITextModel, lastResultId: string | null, token: CancellationToken): Promise { - callCount++; - return null; - } - releaseDocumentSemanticTokens(resultId: string | undefined): void { - } - })); + releaseDocumentSemanticTokens(resultId: string | undefined): void { + } + })); + disposables.add(DocumentSemanticTokensProviderRegistry.register('testMode2', new class implements DocumentSemanticTokensProvider { + getLegend(): SemanticTokensLegend { + return { tokenTypes: ['class2'], tokenModifiers: [] }; + } + async provideDocumentSemanticTokens(model: ITextModel, lastResultId: string | null, token: CancellationToken): Promise { + callCount++; + return null; + } + releaseDocumentSemanticTokens(resultId: string | undefined): void { + } + })); - function toArr(arr: Uint32Array): number[] { - const result: number[] = []; - for (let i = 0; i < arr.length; i++) { - result[i] = arr[i]; + function toArr(arr: Uint32Array): number[] { + const result: number[] = []; + for (let i = 0; i < arr.length; i++) { + result[i] = arr[i]; + } + return result; } - return result; - } - const textModel = modelService.createModel('Hello world 2', languageService.createById('testMode2')); - try { - let result = await getDocumentSemanticTokens(textModel, null, null, CancellationToken.None); - assert.ok(result, `We should have tokens (1)`); - assert.ok(result.tokens, `Tokens are found from multiple providers (1)`); - assert.ok(isSemanticTokens(result.tokens), `Tokens are full (1)`); - assert.ok(result.tokens.resultId, `Token result id found from multiple providers (1)`); - assert.deepStrictEqual(toArr(result.tokens.data), [0, 1, 1, 1, 1, 0, 2, 1, 1, 1], `Token data returned for multiple providers (1)`); - assert.deepStrictEqual(callCount, 2, `Called both token providers (1)`); - assert.deepStrictEqual(result.provider.getLegend(), { tokenTypes: ['class1'], tokenModifiers: [] }, `Legend matches the tokens (1)`); + const textModel = modelService.createModel('Hello world 2', languageService.createById('testMode2')); + try { + let result = await getDocumentSemanticTokens(textModel, null, null, CancellationToken.None); + assert.ok(result, `We should have tokens (1)`); + assert.ok(result.tokens, `Tokens are found from multiple providers (1)`); + assert.ok(isSemanticTokens(result.tokens), `Tokens are full (1)`); + assert.ok(result.tokens.resultId, `Token result id found from multiple providers (1)`); + assert.deepStrictEqual(toArr(result.tokens.data), [0, 1, 1, 1, 1, 0, 2, 1, 1, 1], `Token data returned for multiple providers (1)`); + assert.deepStrictEqual(callCount, 2, `Called both token providers (1)`); + assert.deepStrictEqual(result.provider.getLegend(), { tokenTypes: ['class1'], tokenModifiers: [] }, `Legend matches the tokens (1)`); - // Make a second request. Make sure we get the secondary value - result = await getDocumentSemanticTokens(textModel, result.provider, result.tokens.resultId, CancellationToken.None); - assert.ok(result, `We should have tokens (2)`); - assert.ok(result.tokens, `Tokens are found from multiple providers (2)`); - assert.ok(isSemanticTokens(result.tokens), `Tokens are full (2)`); - assert.ok(!result.tokens.resultId, `Token result id found from multiple providers (2)`); - assert.deepStrictEqual(toArr(result.tokens.data), [2, 1, 1, 1, 1, 0, 2, 1, 1, 1], `Token data returned for multiple providers (2)`); - assert.deepStrictEqual(callCount, 4, `Called both token providers (2)`); - assert.deepStrictEqual(result.provider.getLegend(), { tokenTypes: ['class1'], tokenModifiers: [] }, `Legend matches the tokens (2)`); - } finally { - disposables.clear(); + // Make a second request. Make sure we get the secondary value + result = await getDocumentSemanticTokens(textModel, result.provider, result.tokens.resultId, CancellationToken.None); + assert.ok(result, `We should have tokens (2)`); + assert.ok(result.tokens, `Tokens are found from multiple providers (2)`); + assert.ok(isSemanticTokens(result.tokens), `Tokens are full (2)`); + assert.ok(!result.tokens.resultId, `Token result id found from multiple providers (2)`); + assert.deepStrictEqual(toArr(result.tokens.data), [2, 1, 1, 1, 1, 0, 2, 1, 1, 1], `Token data returned for multiple providers (2)`); + assert.deepStrictEqual(callCount, 4, `Called both token providers (2)`); + assert.deepStrictEqual(result.provider.getLegend(), { tokenTypes: ['class1'], tokenModifiers: [] }, `Legend matches the tokens (2)`); + } finally { + disposables.clear(); - // Wait for scheduler to finish - await timeout(0); + // Wait for scheduler to finish + await timeout(0); - // Now dispose the text model - textModel.dispose(); - } + // Now dispose the text model + textModel.dispose(); + } + }); }); }); diff --git a/src/vs/workbench/api/test/browser/mainThreadDocumentsAndEditors.test.ts b/src/vs/workbench/api/test/browser/mainThreadDocumentsAndEditors.test.ts index dc1a0ccf00a..7a806032e78 100644 --- a/src/vs/workbench/api/test/browser/mainThreadDocumentsAndEditors.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadDocumentsAndEditors.test.ts @@ -32,6 +32,7 @@ import { TestLanguageConfigurationService } from 'vs/editor/test/common/modes/te import { TextModel } from 'vs/editor/common/model/textModel'; import { LanguageService } from 'vs/editor/common/services/languageService'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { LanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce'; suite('MainThreadDocumentsAndEditors', () => { @@ -61,6 +62,7 @@ suite('MainThreadDocumentsAndEditors', () => { const notificationService = new TestNotificationService(); const undoRedoService = new UndoRedoService(dialogService, notificationService); const themeService = new TestThemeService(); + const logService = new NullLogService(); modelService = new ModelService( configService, new TestTextResourcePropertiesService(configService), @@ -68,7 +70,8 @@ suite('MainThreadDocumentsAndEditors', () => { new NullLogService(), undoRedoService, disposables.add(new LanguageService()), - new TestLanguageConfigurationService() + new TestLanguageConfigurationService(), + new LanguageFeatureDebounceService(logService) ); codeEditorService = new TestCodeEditorService(themeService); textFileService = new class extends mock() { diff --git a/src/vs/workbench/api/test/browser/mainThreadEditors.test.ts b/src/vs/workbench/api/test/browser/mainThreadEditors.test.ts index f19ee326a02..e8aa953f46a 100644 --- a/src/vs/workbench/api/test/browser/mainThreadEditors.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadEditors.test.ts @@ -55,6 +55,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { TestLanguageConfigurationService } from 'vs/editor/test/common/modes/testLanguageConfigurationService'; import { LanguageService } from 'vs/editor/common/services/languageService'; +import { LanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce'; suite('MainThreadEditors', () => { @@ -83,14 +84,16 @@ suite('MainThreadEditors', () => { const notificationService = new TestNotificationService(); const undoRedoService = new UndoRedoService(dialogService, notificationService); const themeService = new TestThemeService(); + const logService = new NullLogService(); modelService = new ModelService( configService, new TestTextResourcePropertiesService(configService), themeService, - new NullLogService(), + logService, undoRedoService, disposables.add(new LanguageService()), - new TestLanguageConfigurationService() + new TestLanguageConfigurationService(), + new LanguageFeatureDebounceService(logService) ); diff --git a/src/vs/workbench/contrib/search/test/electron-browser/textsearch.perf.integrationTest.ts b/src/vs/workbench/contrib/search/test/electron-browser/textsearch.perf.integrationTest.ts index 3518187ad56..f46f9f2b3e4 100644 --- a/src/vs/workbench/contrib/search/test/electron-browser/textsearch.perf.integrationTest.ts +++ b/src/vs/workbench/contrib/search/test/electron-browser/textsearch.perf.integrationTest.ts @@ -41,6 +41,7 @@ import { IUntitledTextEditorService, UntitledTextEditorService } from 'vs/workbe import { TestEditorGroupsService, TestEditorService } from 'vs/workbench/test/browser/workbenchTestServices'; import { TestContextService, TestTextResourcePropertiesService } from 'vs/workbench/test/common/workbenchTestServices'; import { TestEnvironmentService } from 'vs/workbench/test/electron-browser/workbenchTestServices'; +import { LanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce'; // declare var __dirname: string; @@ -87,7 +88,8 @@ suite.skip('TextSearch performance (integration)', () => { logService, undoRedoService, new LanguageService(), - new TestLanguageConfigurationService() + new TestLanguageConfigurationService(), + new LanguageFeatureDebounceService(logService) ), ], [ diff --git a/src/vs/workbench/services/model/common/modelService.ts b/src/vs/workbench/services/model/common/modelService.ts index 72199c1c35d..6f99dd8ac86 100644 --- a/src/vs/workbench/services/model/common/modelService.ts +++ b/src/vs/workbench/services/model/common/modelService.ts @@ -15,6 +15,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; +import { ILanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce'; export class WorkbenchModelService extends ModelService { constructor( @@ -25,9 +26,10 @@ export class WorkbenchModelService extends ModelService { @IUndoRedoService undoRedoService: IUndoRedoService, @ILanguageConfigurationService languageConfigurationService: ILanguageConfigurationService, @ILanguageService languageService: ILanguageService, + @ILanguageFeatureDebounceService languageFeatureDebounceService: ILanguageFeatureDebounceService, @IPathService private readonly _pathService: IPathService, ) { - super(configurationService, resourcePropertiesService, themeService, logService, undoRedoService, languageService, languageConfigurationService); + super(configurationService, resourcePropertiesService, themeService, logService, undoRedoService, languageService, languageConfigurationService, languageFeatureDebounceService); } protected override _schemaShouldMaintainUndoRedoElements(resource: URI) {