Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/contrib/inlayHints/browser/inlayHintsHover.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { AsyncIterableProducer } from '../../../../base/common/async.js';
7
import { CancellationToken } from '../../../../base/common/cancellation.js';
8
import { IMarkdownString, isEmptyMarkdownString, MarkdownString } from '../../../../base/common/htmlContent.js';
9
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from '../../../browser/editorBrowser.js';
10
import { Position } from '../../../common/core/position.js';
11
import { IModelDecoration } from '../../../common/model.js';
12
import { ModelDecorationInjectedTextOptions } from '../../../common/model/textModel.js';
13
import { HoverAnchor, HoverForeignElementAnchor, IEditorHoverParticipant } from '../../hover/browser/hoverTypes.js';
14
import { ILanguageService } from '../../../common/languages/language.js';
15
import { ITextModelService } from '../../../common/services/resolverService.js';
16
import { getHoverProviderResultsAsAsyncIterable } from '../../hover/browser/getHover.js';
17
import { MarkdownHover, MarkdownHoverParticipant } from '../../hover/browser/markdownHoverParticipant.js';
18
import { RenderedInlayHintLabelPart, InlayHintsController } from './inlayHintsController.js';
19
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
20
import { IOpenerService } from '../../../../platform/opener/common/opener.js';
21
import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js';
22
import { EditorOption } from '../../../common/config/editorOptions.js';
23
import { localize } from '../../../../nls.js';
24
import * as platform from '../../../../base/common/platform.js';
25
import { asCommandLink } from './inlayHints.js';
26
import { isNonEmptyArray } from '../../../../base/common/arrays.js';
27
import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';
28
import { IHoverService } from '../../../../platform/hover/browser/hover.js';
29
import { ICommandService } from '../../../../platform/commands/common/commands.js';
30
import { HoverStartSource } from '../../hover/browser/hoverOperation.js';
31
32
class InlayHintsHoverAnchor extends HoverForeignElementAnchor {
33
constructor(
34
readonly part: RenderedInlayHintLabelPart,
35
owner: InlayHintsHover,
36
initialMousePosX: number | undefined,
37
initialMousePosY: number | undefined
38
) {
39
super(10, owner, part.item.anchor.range, initialMousePosX, initialMousePosY, true);
40
}
41
}
42
43
export class InlayHintsHover extends MarkdownHoverParticipant implements IEditorHoverParticipant<MarkdownHover> {
44
45
public override readonly hoverOrdinal: number = 6;
46
47
constructor(
48
editor: ICodeEditor,
49
@ILanguageService languageService: ILanguageService,
50
@IOpenerService openerService: IOpenerService,
51
@IKeybindingService keybindingService: IKeybindingService,
52
@IHoverService hoverService: IHoverService,
53
@IConfigurationService configurationService: IConfigurationService,
54
@ITextModelService private readonly _resolverService: ITextModelService,
55
@ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService,
56
@ICommandService commandService: ICommandService
57
) {
58
super(editor, languageService, openerService, configurationService, languageFeaturesService, keybindingService, hoverService, commandService);
59
}
60
61
suggestHoverAnchor(mouseEvent: IEditorMouseEvent): HoverAnchor | null {
62
const controller = InlayHintsController.get(this._editor);
63
if (!controller) {
64
return null;
65
}
66
if (mouseEvent.target.type !== MouseTargetType.CONTENT_TEXT) {
67
return null;
68
}
69
const options = mouseEvent.target.detail.injectedText?.options;
70
if (!(options instanceof ModelDecorationInjectedTextOptions && options.attachedData instanceof RenderedInlayHintLabelPart)) {
71
return null;
72
}
73
return new InlayHintsHoverAnchor(options.attachedData, this, mouseEvent.event.posx, mouseEvent.event.posy);
74
}
75
76
override computeSync(): MarkdownHover[] {
77
return [];
78
}
79
80
override computeAsync(anchor: HoverAnchor, _lineDecorations: IModelDecoration[], source: HoverStartSource, token: CancellationToken): AsyncIterableProducer<MarkdownHover> {
81
if (!(anchor instanceof InlayHintsHoverAnchor)) {
82
return AsyncIterableProducer.EMPTY;
83
}
84
85
return new AsyncIterableProducer<MarkdownHover>(async executor => {
86
87
const { part } = anchor;
88
await part.item.resolve(token);
89
90
if (token.isCancellationRequested) {
91
return;
92
}
93
94
// (1) Inlay Tooltip
95
let itemTooltip: IMarkdownString | undefined;
96
if (typeof part.item.hint.tooltip === 'string') {
97
itemTooltip = new MarkdownString().appendText(part.item.hint.tooltip);
98
} else if (part.item.hint.tooltip) {
99
itemTooltip = part.item.hint.tooltip;
100
}
101
if (itemTooltip) {
102
executor.emitOne(new MarkdownHover(this, anchor.range, [itemTooltip], false, 0));
103
}
104
// (1.2) Inlay dbl-click gesture
105
if (isNonEmptyArray(part.item.hint.textEdits)) {
106
executor.emitOne(new MarkdownHover(this, anchor.range, [new MarkdownString().appendText(localize('hint.dbl', "Double-click to insert"))], false, 10001));
107
}
108
109
// (2) Inlay Label Part Tooltip
110
let partTooltip: IMarkdownString | undefined;
111
if (typeof part.part.tooltip === 'string') {
112
partTooltip = new MarkdownString().appendText(part.part.tooltip);
113
} else if (part.part.tooltip) {
114
partTooltip = part.part.tooltip;
115
}
116
if (partTooltip) {
117
executor.emitOne(new MarkdownHover(this, anchor.range, [partTooltip], false, 1));
118
}
119
120
// (2.2) Inlay Label Part Help Hover
121
if (part.part.location || part.part.command) {
122
let linkHint: MarkdownString | undefined;
123
const useMetaKey = this._editor.getOption(EditorOption.multiCursorModifier) === 'altKey';
124
const kb = useMetaKey
125
? platform.isMacintosh
126
? localize('links.navigate.kb.meta.mac', "cmd + click")
127
: localize('links.navigate.kb.meta', "ctrl + click")
128
: platform.isMacintosh
129
? localize('links.navigate.kb.alt.mac', "option + click")
130
: localize('links.navigate.kb.alt', "alt + click");
131
132
if (part.part.location && part.part.command) {
133
linkHint = new MarkdownString().appendText(localize('hint.defAndCommand', 'Go to Definition ({0}), right click for more', kb));
134
} else if (part.part.location) {
135
linkHint = new MarkdownString().appendText(localize('hint.def', 'Go to Definition ({0})', kb));
136
} else if (part.part.command) {
137
linkHint = new MarkdownString(`[${localize('hint.cmd', "Execute Command")}](${asCommandLink(part.part.command)} "${part.part.command.title}") (${kb})`, { isTrusted: true });
138
}
139
if (linkHint) {
140
executor.emitOne(new MarkdownHover(this, anchor.range, [linkHint], false, 10000));
141
}
142
}
143
144
145
// (3) Inlay Label Part Location tooltip
146
const iterable = this._resolveInlayHintLabelPartHover(part, token);
147
for await (const item of iterable) {
148
executor.emitOne(item);
149
}
150
});
151
}
152
153
private async *_resolveInlayHintLabelPartHover(part: RenderedInlayHintLabelPart, token: CancellationToken): AsyncIterable<MarkdownHover> {
154
if (!part.part.location) {
155
return;
156
}
157
158
const { uri, range } = part.part.location;
159
const ref = await this._resolverService.createModelReference(uri);
160
try {
161
const model = ref.object.textEditorModel;
162
if (!this._languageFeaturesService.hoverProvider.has(model)) {
163
return;
164
}
165
166
for await (const item of getHoverProviderResultsAsAsyncIterable(this._languageFeaturesService.hoverProvider, model, new Position(range.startLineNumber, range.startColumn), token)) {
167
if (!isEmptyMarkdownString(item.hover.contents)) {
168
yield new MarkdownHover(this, part.item.anchor.range, item.hover.contents, false, 2 + item.ordinal);
169
}
170
}
171
} finally {
172
ref.dispose();
173
}
174
}
175
}
176
177