Path: blob/main/src/vs/editor/contrib/inlayHints/browser/inlayHintsHover.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { AsyncIterableProducer } from '../../../../base/common/async.js';6import { CancellationToken } from '../../../../base/common/cancellation.js';7import { IMarkdownString, isEmptyMarkdownString, MarkdownString } from '../../../../base/common/htmlContent.js';8import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from '../../../browser/editorBrowser.js';9import { Position } from '../../../common/core/position.js';10import { IModelDecoration } from '../../../common/model.js';11import { ModelDecorationInjectedTextOptions } from '../../../common/model/textModel.js';12import { HoverAnchor, HoverForeignElementAnchor, IEditorHoverParticipant } from '../../hover/browser/hoverTypes.js';13import { ILanguageService } from '../../../common/languages/language.js';14import { ITextModelService } from '../../../common/services/resolverService.js';15import { getHoverProviderResultsAsAsyncIterable } from '../../hover/browser/getHover.js';16import { MarkdownHover, MarkdownHoverParticipant } from '../../hover/browser/markdownHoverParticipant.js';17import { RenderedInlayHintLabelPart, InlayHintsController } from './inlayHintsController.js';18import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';19import { IOpenerService } from '../../../../platform/opener/common/opener.js';20import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js';21import { EditorOption } from '../../../common/config/editorOptions.js';22import { localize } from '../../../../nls.js';23import * as platform from '../../../../base/common/platform.js';24import { asCommandLink } from './inlayHints.js';25import { isNonEmptyArray } from '../../../../base/common/arrays.js';26import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';27import { IHoverService } from '../../../../platform/hover/browser/hover.js';28import { ICommandService } from '../../../../platform/commands/common/commands.js';29import { HoverStartSource } from '../../hover/browser/hoverOperation.js';3031class InlayHintsHoverAnchor extends HoverForeignElementAnchor {32constructor(33readonly part: RenderedInlayHintLabelPart,34owner: InlayHintsHover,35initialMousePosX: number | undefined,36initialMousePosY: number | undefined37) {38super(10, owner, part.item.anchor.range, initialMousePosX, initialMousePosY, true);39}40}4142export class InlayHintsHover extends MarkdownHoverParticipant implements IEditorHoverParticipant<MarkdownHover> {4344public override readonly hoverOrdinal: number = 6;4546constructor(47editor: ICodeEditor,48@ILanguageService languageService: ILanguageService,49@IOpenerService openerService: IOpenerService,50@IKeybindingService keybindingService: IKeybindingService,51@IHoverService hoverService: IHoverService,52@IConfigurationService configurationService: IConfigurationService,53@ITextModelService private readonly _resolverService: ITextModelService,54@ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService,55@ICommandService commandService: ICommandService56) {57super(editor, languageService, openerService, configurationService, languageFeaturesService, keybindingService, hoverService, commandService);58}5960suggestHoverAnchor(mouseEvent: IEditorMouseEvent): HoverAnchor | null {61const controller = InlayHintsController.get(this._editor);62if (!controller) {63return null;64}65if (mouseEvent.target.type !== MouseTargetType.CONTENT_TEXT) {66return null;67}68const options = mouseEvent.target.detail.injectedText?.options;69if (!(options instanceof ModelDecorationInjectedTextOptions && options.attachedData instanceof RenderedInlayHintLabelPart)) {70return null;71}72return new InlayHintsHoverAnchor(options.attachedData, this, mouseEvent.event.posx, mouseEvent.event.posy);73}7475override computeSync(): MarkdownHover[] {76return [];77}7879override computeAsync(anchor: HoverAnchor, _lineDecorations: IModelDecoration[], source: HoverStartSource, token: CancellationToken): AsyncIterableProducer<MarkdownHover> {80if (!(anchor instanceof InlayHintsHoverAnchor)) {81return AsyncIterableProducer.EMPTY;82}8384return new AsyncIterableProducer<MarkdownHover>(async executor => {8586const { part } = anchor;87await part.item.resolve(token);8889if (token.isCancellationRequested) {90return;91}9293// (1) Inlay Tooltip94let itemTooltip: IMarkdownString | undefined;95if (typeof part.item.hint.tooltip === 'string') {96itemTooltip = new MarkdownString().appendText(part.item.hint.tooltip);97} else if (part.item.hint.tooltip) {98itemTooltip = part.item.hint.tooltip;99}100if (itemTooltip) {101executor.emitOne(new MarkdownHover(this, anchor.range, [itemTooltip], false, 0));102}103// (1.2) Inlay dbl-click gesture104if (isNonEmptyArray(part.item.hint.textEdits)) {105executor.emitOne(new MarkdownHover(this, anchor.range, [new MarkdownString().appendText(localize('hint.dbl', "Double-click to insert"))], false, 10001));106}107108// (2) Inlay Label Part Tooltip109let partTooltip: IMarkdownString | undefined;110if (typeof part.part.tooltip === 'string') {111partTooltip = new MarkdownString().appendText(part.part.tooltip);112} else if (part.part.tooltip) {113partTooltip = part.part.tooltip;114}115if (partTooltip) {116executor.emitOne(new MarkdownHover(this, anchor.range, [partTooltip], false, 1));117}118119// (2.2) Inlay Label Part Help Hover120if (part.part.location || part.part.command) {121let linkHint: MarkdownString | undefined;122const useMetaKey = this._editor.getOption(EditorOption.multiCursorModifier) === 'altKey';123const kb = useMetaKey124? platform.isMacintosh125? localize('links.navigate.kb.meta.mac', "cmd + click")126: localize('links.navigate.kb.meta', "ctrl + click")127: platform.isMacintosh128? localize('links.navigate.kb.alt.mac', "option + click")129: localize('links.navigate.kb.alt', "alt + click");130131if (part.part.location && part.part.command) {132linkHint = new MarkdownString().appendText(localize('hint.defAndCommand', 'Go to Definition ({0}), right click for more', kb));133} else if (part.part.location) {134linkHint = new MarkdownString().appendText(localize('hint.def', 'Go to Definition ({0})', kb));135} else if (part.part.command) {136linkHint = new MarkdownString(`[${localize('hint.cmd', "Execute Command")}](${asCommandLink(part.part.command)} "${part.part.command.title}") (${kb})`, { isTrusted: true });137}138if (linkHint) {139executor.emitOne(new MarkdownHover(this, anchor.range, [linkHint], false, 10000));140}141}142143144// (3) Inlay Label Part Location tooltip145const iterable = this._resolveInlayHintLabelPartHover(part, token);146for await (const item of iterable) {147executor.emitOne(item);148}149});150}151152private async *_resolveInlayHintLabelPartHover(part: RenderedInlayHintLabelPart, token: CancellationToken): AsyncIterable<MarkdownHover> {153if (!part.part.location) {154return;155}156157const { uri, range } = part.part.location;158const ref = await this._resolverService.createModelReference(uri);159try {160const model = ref.object.textEditorModel;161if (!this._languageFeaturesService.hoverProvider.has(model)) {162return;163}164165for await (const item of getHoverProviderResultsAsAsyncIterable(this._languageFeaturesService.hoverProvider, model, new Position(range.startLineNumber, range.startColumn), token)) {166if (!isEmptyMarkdownString(item.hover.contents)) {167yield new MarkdownHover(this, part.item.anchor.range, item.hover.contents, false, 2 + item.ordinal);168}169}170} finally {171ref.dispose();172}173}174}175176177