diff --git a/src/vs/editor/common/modes/languageFeatureRegistry.ts b/src/vs/editor/common/modes/languageFeatureRegistry.ts index 4278ec10e3a8a..cc59121efe088 100644 --- a/src/vs/editor/common/modes/languageFeatureRegistry.ts +++ b/src/vs/editor/common/modes/languageFeatureRegistry.ts @@ -4,7 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter, Event } from 'vs/base/common/event'; +import { hash } from 'vs/base/common/hash'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { LRUCache } from 'vs/base/common/map'; +import { MovingAverage } from 'vs/base/common/numbers'; import { ITextModel } from 'vs/editor/common/model'; import { LanguageSelector, score } from 'vs/editor/common/modes/languageSelector'; import { shouldSynchronizeModel } from 'vs/editor/common/services/modelService'; @@ -174,3 +177,42 @@ export class LanguageFeatureRegistry { } } } + + +export class LanguageFeatureRequestDelays { + + private readonly _cache = new LRUCache(50, 0.7); + + constructor( + private readonly _registry: LanguageFeatureRegistry, + readonly min: number, + readonly max: number = Number.MAX_SAFE_INTEGER, + ) { } + + private _key(model: ITextModel): string { + return model.id + hash(this._registry.all(model)); + } + + private _clamp(value: number | undefined): number { + if (value === undefined) { + return this.min; + } else { + return Math.min(this.max, Math.max(this.min, Math.floor(value * 1.3))); + } + } + + get(model: ITextModel): number { + const key = this._key(model); + return this._clamp(this._cache.get(key)?.value); + } + + update(model: ITextModel, value: number) { + const key = this._key(model); + let avg = this._cache.get(key); + if (!avg) { + avg = new MovingAverage(); + this._cache.set(key, avg); + } + avg.update(value); + } +} diff --git a/src/vs/editor/contrib/documentSymbols/outlineModel.ts b/src/vs/editor/contrib/documentSymbols/outlineModel.ts index d9e102ee50c42..c85081502dd39 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineModel.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineModel.ts @@ -14,8 +14,8 @@ import { ITextModel } from 'vs/editor/common/model'; import { DocumentSymbol, DocumentSymbolProvider, DocumentSymbolProviderRegistry } from 'vs/editor/common/modes'; import { MarkerSeverity } from 'vs/platform/markers/common/markers'; import { Iterable } from 'vs/base/common/iterator'; -import { MovingAverage } from 'vs/base/common/numbers'; import { URI } from 'vs/base/common/uri'; +import { LanguageFeatureRequestDelays } from 'vs/editor/common/modes/languageFeatureRegistry'; export abstract class TreeElement { @@ -208,7 +208,7 @@ export class OutlineGroup extends TreeElement { export class OutlineModel extends TreeElement { - private static readonly _requestDurations = new LRUCache(50, 0.7); + private static readonly _requestDurations = new LanguageFeatureRequestDelays(DocumentSymbolProviderRegistry, 350); private static readonly _requests = new LRUCache, model: OutlineModel | undefined }>(9, 0.75); private static readonly _keys = new class { @@ -252,13 +252,7 @@ export class OutlineModel extends TreeElement { // keep moving average of request durations const now = Date.now(); data.promise.then(() => { - let key = this._keys.for(textModel, false); - let avg = this._requestDurations.get(key); - if (!avg) { - avg = new MovingAverage(); - this._requestDurations.set(key, avg); - } - avg.update(Date.now() - now); + this._requestDurations.update(textModel, Date.now() - now); }); } @@ -290,14 +284,7 @@ export class OutlineModel extends TreeElement { } static getRequestDelay(textModel: ITextModel | null): number { - if (!textModel) { - return 350; - } - const avg = this._requestDurations.get(this._keys.for(textModel, false)); - if (!avg) { - return 350; - } - return Math.max(350, Math.floor(1.3 * avg.value)); + return textModel ? this._requestDurations.get(textModel) : this._requestDurations.min; } private static _create(textModel: ITextModel, token: CancellationToken): Promise {