diff --git a/extensions/typescript-language-features/src/languageFeatures/inlayHints.ts b/extensions/typescript-language-features/src/languageFeatures/inlayHints.ts index 1d240960c65..009a4b48f63 100644 --- a/extensions/typescript-language-features/src/languageFeatures/inlayHints.ts +++ b/extensions/typescript-language-features/src/languageFeatures/inlayHints.ts @@ -28,7 +28,7 @@ class TypeScriptInlayHintsProvider extends Disposable implements vscode.InlayHin public static readonly minVersion = API.v440; - private readonly _onDidChangeInlayHints = new vscode.EventEmitter(); + private readonly _onDidChangeInlayHints = new vscode.EventEmitter(); public readonly onDidChangeInlayHints = this._onDidChangeInlayHints.event; constructor( @@ -40,7 +40,7 @@ class TypeScriptInlayHintsProvider extends Disposable implements vscode.InlayHin this._register(vscode.workspace.onDidChangeConfiguration(e => { if (inlayHintSettingNames.some(settingName => e.affectsConfiguration(modeId + '.' + settingName))) { - this._onDidChangeInlayHints.fire(undefined); + this._onDidChangeInlayHints.fire(); } })); } @@ -57,7 +57,8 @@ class TypeScriptInlayHintsProvider extends Disposable implements vscode.InlayHin if (modelUri === vscode.window.activeTextEditor?.document.uri.toString()) { for (const visibleEditor of vscode.window.visibleTextEditors) { if (isSupportedLanguageMode(visibleEditor.document) && visibleEditor.document.uri.toString() !== modelUri) { - this._onDidChangeInlayHints.fire(visibleEditor.document.uri); + this._onDidChangeInlayHints.fire(); + break; } } } diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 4326948b352..f7dae451105 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -1755,7 +1755,7 @@ export interface InlayHint { } export interface InlayHintsProvider { - onDidChangeInlayHints?: Event; + onDidChangeInlayHints?: Event; provideInlayHints(model: model.ITextModel, range: Range, token: CancellationToken): ProviderResult; } diff --git a/src/vs/editor/contrib/inlayHints/inlayHintsController.ts b/src/vs/editor/contrib/inlayHints/inlayHintsController.ts index e95ebe3d478..ba203264d24 100644 --- a/src/vs/editor/contrib/inlayHints/inlayHintsController.ts +++ b/src/vs/editor/contrib/inlayHints/inlayHintsController.ts @@ -8,7 +8,7 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance import { onUnexpectedExternalError } from 'vs/base/common/errors'; import { hash } from 'vs/base/common/hash'; import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; -import { LRUCache } from 'vs/base/common/map'; +import { LRUCache, ResourceMap } from 'vs/base/common/map'; import { IRange } from 'vs/base/common/range'; import { assertType } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; @@ -20,7 +20,7 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { IContentDecorationRenderOptions, IDecorationRenderOptions, IEditorContribution } from 'vs/editor/common/editorCommon'; import { IModelDeltaDecoration, ITextModel, IWordAtPosition, TrackedRangeStickiness } from 'vs/editor/common/model'; -import { InlayHint, InlayHintKind, InlayHintsProviderRegistry } from 'vs/editor/common/modes'; +import { InlayHint, InlayHintKind, InlayHintsProvider, InlayHintsProviderRegistry } from 'vs/editor/common/modes'; import { LanguageFeatureRequestDelays } from 'vs/editor/common/modes/languageFeatureRegistry'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; @@ -29,18 +29,49 @@ import { themeColorFromId } from 'vs/platform/theme/common/themeService'; const MAX_DECORATORS = 1500; -export async function getInlayHints(model: ITextModel, ranges: Range[], token: CancellationToken): Promise { +class RequestMap { + + private readonly _data = new ResourceMap>(); + + push(model: ITextModel, provider: T): void { + const value = this._data.get(model.uri); + if (value === undefined) { + this._data.set(model.uri, new Set([provider])); + } else { + value.add(provider); + } + } + + pop(model: ITextModel, provider: T): void { + const value = this._data.get(model.uri); + if (value) { + value.delete(provider); + if (value.size === 0) { + this._data.delete(model.uri); + } + } + } + + has(model: ITextModel, provider: T): boolean { + return Boolean(this._data.get(model.uri)?.has(provider)); + } +} + +export async function getInlayHints(model: ITextModel, ranges: Range[], requests: RequestMap, token: CancellationToken): Promise { const all: InlayHint[][] = []; const providers = InlayHintsProviderRegistry.ordered(model).reverse(); const promises = providers.map(provider => ranges.map(async range => { try { + requests.push(model, provider); const result = await provider.provideInlayHints(model, range, token); if (result?.length) { all.push(result.filter(hint => range.containsPosition(hint.position))); } } catch (err) { onUnexpectedExternalError(err); + } finally { + requests.pop(model, provider); } })); @@ -122,6 +153,8 @@ export class InlayHintsController implements IEditorContribution { this._updateHintsDecorators([model.getFullModelRange()], cached); } + const requests = new RequestMap(); + const scheduler = new RunOnceScheduler(async () => { const t1 = Date.now(); @@ -129,7 +162,7 @@ export class InlayHintsController implements IEditorContribution { this._sessionDisposables.add(toDisposable(() => cts.dispose(true))); const ranges = this._getHintsRanges(); - const result = await getInlayHints(model, ranges, cts.token); + const result = await getInlayHints(model, ranges, requests, cts.token); scheduler.delay = this._getInlayHintsDelays.update(model, Date.now() - t1); if (cts.token.isCancellationRequested) { return; @@ -151,8 +184,8 @@ export class InlayHintsController implements IEditorContribution { this._sessionDisposables.add(providerListener); for (const provider of InlayHintsProviderRegistry.all(model)) { if (typeof provider.onDidChangeInlayHints === 'function') { - providerListener.add(provider.onDidChangeInlayHints(uri => { - if (!uri || uri.toString() === model.uri.toString()) { + providerListener.add(provider.onDidChangeInlayHints(() => { + if (!requests.has(model, provider)) { scheduler.schedule(); } })); @@ -315,7 +348,7 @@ CommandsRegistry.registerCommand('_executeInlayHintProvider', async (accessor, . const ref = await accessor.get(ITextModelService).createModelReference(uri); try { - const data = await getInlayHints(ref.object.textEditorModel, [Range.lift(range)], CancellationToken.None); + const data = await getInlayHints(ref.object.textEditorModel, [Range.lift(range)], new RequestMap(), CancellationToken.None); return data; } finally { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 6ef6eb0a853..caed5c22435 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -6653,7 +6653,7 @@ declare namespace monaco.languages { } export interface InlayHintsProvider { - onDidChangeInlayHints?: IEvent; + onDidChangeInlayHints?: IEvent; provideInlayHints(model: editor.ITextModel, range: Range, token: CancellationToken): ProviderResult; } diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index f5f576fe65c..e38d4a2ab6c 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1844,7 +1844,8 @@ declare module 'vscode' { * An optional event to signal that inlay hints have changed. * @see {@link EventEmitter} */ - onDidChangeInlayHints?: Event; + //todo@API needs proper doc (like others) + onDidChangeInlayHints?: Event; /** * diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index cf7262eb262..bec8a7e9f20 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -16,14 +16,13 @@ import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageCo import { LanguageConfiguration, IndentationRule, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration'; import { IModeService } from 'vs/editor/common/services/modeService'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; -import { URI, UriComponents } from 'vs/base/common/uri'; +import { URI } from 'vs/base/common/uri'; import { Selection } from 'vs/editor/common/core/selection'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import * as callh from 'vs/workbench/contrib/callHierarchy/common/callHierarchy'; import * as typeh from 'vs/workbench/contrib/typeHierarchy/common/typeHierarchy'; import { mixin } from 'vs/base/common/objects'; import { decodeSemanticTokensDto } from 'vs/editor/common/services/semanticTokensDto'; -import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; @extHostNamedCustomer(MainContext.MainThreadLanguageFeatures) export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesShape { @@ -35,7 +34,6 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha constructor( extHostContext: IExtHostContext, @IModeService modeService: IModeService, - @IUriIdentityService private readonly _uriIdentService: IUriIdentityService, ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostLanguageFeatures); this._modeService = modeService; @@ -568,10 +566,10 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha this._registrations.set(handle, modes.InlayHintsProviderRegistry.register(selector, provider)); } - $emitInlayHintsEvent(eventHandle: number, event?: UriComponents): void { + $emitInlayHintsEvent(eventHandle: number): void { const obj = this._registrations.get(eventHandle); if (obj instanceof Emitter) { - obj.fire(event && this._uriIdentService.asCanonicalUri(URI.revive(event))); + obj.fire(undefined); } } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 2549cfef3d2..95be5a6d27a 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -412,7 +412,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { $registerInlineCompletionsSupport(handle: number, selector: IDocumentFilterDto[]): void; $registerSignatureHelpProvider(handle: number, selector: IDocumentFilterDto[], metadata: ISignatureHelpProviderMetadataDto): void; $registerInlayHintsProvider(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void; - $emitInlayHintsEvent(eventHandle: number, event?: UriComponents): void; + $emitInlayHintsEvent(eventHandle: number): void; $registerDocumentLinkProvider(handle: number, selector: IDocumentFilterDto[], supportsResolve: boolean): void; $registerDocumentColorProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerFoldingRangeProvider(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void; diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index b0c78753fda..667289e83ad 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -1988,7 +1988,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF let result = this._createDisposable(handle); if (eventHandle !== undefined) { - const subscription = provider.onDidChangeInlayHints!(uri => this._proxy.$emitInlayHintsEvent(eventHandle, uri)); + const subscription = provider.onDidChangeInlayHints!(uri => this._proxy.$emitInlayHintsEvent(eventHandle)); result = Disposable.from(result, subscription); } return result;