Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts
4779 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 * as dom from '../../../../base/browser/dom.js';
7
import { asArray, compareBy, numberComparator } from '../../../../base/common/arrays.js';
8
import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js';
9
import { IMarkdownString, isEmptyMarkdownString, MarkdownString } from '../../../../base/common/htmlContent.js';
10
import { DisposableStore, toDisposable } from '../../../../base/common/lifecycle.js';
11
import { IMarkdownRendererService } from '../../../../platform/markdown/browser/markdownRenderer.js';
12
import { DECREASE_HOVER_VERBOSITY_ACTION_ID, INCREASE_HOVER_VERBOSITY_ACTION_ID } from './hoverActionIds.js';
13
import { ICodeEditor } from '../../../browser/editorBrowser.js';
14
import { Position } from '../../../common/core/position.js';
15
import { Range } from '../../../common/core/range.js';
16
import { IModelDecoration, ITextModel } from '../../../common/model.js';
17
import { HoverAnchor, HoverAnchorType, HoverRangeAnchor, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart, IRenderedHoverPart, IRenderedHoverParts, RenderedHoverParts } from './hoverTypes.js';
18
import * as nls from '../../../../nls.js';
19
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
20
import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js';
21
import { EditorOption } from '../../../common/config/editorOptions.js';
22
import { Hover, HoverContext, HoverProvider, HoverVerbosityAction } from '../../../common/languages.js';
23
import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js';
24
import { Codicon } from '../../../../base/common/codicons.js';
25
import { ThemeIcon } from '../../../../base/common/themables.js';
26
import { onUnexpectedExternalError } from '../../../../base/common/errors.js';
27
import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';
28
import { ClickAction, HoverPosition, KeyDownAction } from '../../../../base/browser/ui/hover/hoverWidget.js';
29
import { KeyCode } from '../../../../base/common/keyCodes.js';
30
import { IHoverService, WorkbenchHoverDelegate } from '../../../../platform/hover/browser/hover.js';
31
import { AsyncIterableProducer } from '../../../../base/common/async.js';
32
import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js';
33
import { getHoverProviderResultsAsAsyncIterable } from './getHover.js';
34
import { ICommandService } from '../../../../platform/commands/common/commands.js';
35
import { HoverStartSource } from './hoverOperation.js';
36
import { ScrollEvent } from '../../../../base/common/scrollable.js';
37
38
const $ = dom.$;
39
const increaseHoverVerbosityIcon = registerIcon('hover-increase-verbosity', Codicon.add, nls.localize('increaseHoverVerbosity', 'Icon for increaseing hover verbosity.'));
40
const decreaseHoverVerbosityIcon = registerIcon('hover-decrease-verbosity', Codicon.remove, nls.localize('decreaseHoverVerbosity', 'Icon for decreasing hover verbosity.'));
41
42
export class MarkdownHover implements IHoverPart {
43
44
constructor(
45
public readonly owner: IEditorHoverParticipant<MarkdownHover>,
46
public readonly range: Range,
47
public readonly contents: IMarkdownString[],
48
public readonly isBeforeContent: boolean,
49
public readonly ordinal: number,
50
public readonly source: HoverSource | undefined = undefined,
51
) { }
52
53
public isValidForHoverAnchor(anchor: HoverAnchor): boolean {
54
return (
55
anchor.type === HoverAnchorType.Range
56
&& this.range.startColumn <= anchor.range.startColumn
57
&& this.range.endColumn >= anchor.range.endColumn
58
);
59
}
60
}
61
62
class HoverSource {
63
64
constructor(
65
readonly hover: Hover,
66
readonly hoverProvider: HoverProvider,
67
readonly hoverPosition: Position,
68
) { }
69
70
public supportsVerbosityAction(hoverVerbosityAction: HoverVerbosityAction): boolean {
71
switch (hoverVerbosityAction) {
72
case HoverVerbosityAction.Increase:
73
return this.hover.canIncreaseVerbosity ?? false;
74
case HoverVerbosityAction.Decrease:
75
return this.hover.canDecreaseVerbosity ?? false;
76
}
77
}
78
}
79
80
export class MarkdownHoverParticipant implements IEditorHoverParticipant<MarkdownHover> {
81
82
public readonly hoverOrdinal: number = 3;
83
84
private _renderedHoverParts: MarkdownRenderedHoverParts | undefined;
85
86
constructor(
87
protected readonly _editor: ICodeEditor,
88
@IMarkdownRendererService private readonly _markdownRendererService: IMarkdownRendererService,
89
@IConfigurationService private readonly _configurationService: IConfigurationService,
90
@ILanguageFeaturesService protected readonly _languageFeaturesService: ILanguageFeaturesService,
91
@IKeybindingService private readonly _keybindingService: IKeybindingService,
92
@IHoverService private readonly _hoverService: IHoverService,
93
@ICommandService private readonly _commandService: ICommandService,
94
) { }
95
96
public createLoadingMessage(anchor: HoverAnchor): MarkdownHover | null {
97
return new MarkdownHover(this, anchor.range, [new MarkdownString().appendText(nls.localize('modesContentHover.loading', "Loading..."))], false, 2000);
98
}
99
100
public computeSync(anchor: HoverAnchor, lineDecorations: IModelDecoration[]): MarkdownHover[] {
101
if (!this._editor.hasModel() || anchor.type !== HoverAnchorType.Range) {
102
return [];
103
}
104
105
const model = this._editor.getModel();
106
const lineNumber = anchor.range.startLineNumber;
107
const maxColumn = model.getLineMaxColumn(lineNumber);
108
const result: MarkdownHover[] = [];
109
110
let index = 1000;
111
112
const lineLength = model.getLineLength(lineNumber);
113
const languageId = model.getLanguageIdAtPosition(anchor.range.startLineNumber, anchor.range.startColumn);
114
const stopRenderingLineAfter = this._editor.getOption(EditorOption.stopRenderingLineAfter);
115
const maxTokenizationLineLength = this._configurationService.getValue<number>('editor.maxTokenizationLineLength', {
116
overrideIdentifier: languageId
117
});
118
let stopRenderingMessage = false;
119
if (stopRenderingLineAfter >= 0 && lineLength > stopRenderingLineAfter && anchor.range.startColumn >= stopRenderingLineAfter) {
120
stopRenderingMessage = true;
121
result.push(new MarkdownHover(this, anchor.range, [{
122
value: nls.localize('stopped rendering', "Rendering paused for long line for performance reasons. This can be configured via `editor.stopRenderingLineAfter`.")
123
}], false, index++));
124
}
125
if (!stopRenderingMessage && typeof maxTokenizationLineLength === 'number' && lineLength >= maxTokenizationLineLength) {
126
result.push(new MarkdownHover(this, anchor.range, [{
127
value: nls.localize('too many characters', "Tokenization is skipped for long lines for performance reasons. This can be configured via `editor.maxTokenizationLineLength`.")
128
}], false, index++));
129
}
130
131
let isBeforeContent = false;
132
133
for (const d of lineDecorations) {
134
const startColumn = (d.range.startLineNumber === lineNumber) ? d.range.startColumn : 1;
135
const endColumn = (d.range.endLineNumber === lineNumber) ? d.range.endColumn : maxColumn;
136
137
const hoverMessage = d.options.hoverMessage;
138
if (!hoverMessage || isEmptyMarkdownString(hoverMessage)) {
139
continue;
140
}
141
142
if (d.options.beforeContentClassName) {
143
isBeforeContent = true;
144
}
145
146
const range = new Range(anchor.range.startLineNumber, startColumn, anchor.range.startLineNumber, endColumn);
147
result.push(new MarkdownHover(this, range, asArray(hoverMessage), isBeforeContent, index++));
148
}
149
150
return result;
151
}
152
153
public computeAsync(anchor: HoverAnchor, lineDecorations: IModelDecoration[], source: HoverStartSource, token: CancellationToken): AsyncIterable<MarkdownHover> {
154
if (!this._editor.hasModel() || anchor.type !== HoverAnchorType.Range) {
155
return AsyncIterableProducer.EMPTY;
156
}
157
158
const model = this._editor.getModel();
159
160
const hoverProviderRegistry = this._languageFeaturesService.hoverProvider;
161
if (!hoverProviderRegistry.has(model)) {
162
return AsyncIterableProducer.EMPTY;
163
}
164
return this._getMarkdownHovers(hoverProviderRegistry, model, anchor, token);
165
}
166
167
private async *_getMarkdownHovers(hoverProviderRegistry: LanguageFeatureRegistry<HoverProvider>, model: ITextModel, anchor: HoverRangeAnchor, token: CancellationToken): AsyncIterable<MarkdownHover> {
168
const position = anchor.range.getStartPosition();
169
const hoverProviderResults = getHoverProviderResultsAsAsyncIterable(hoverProviderRegistry, model, position, token);
170
171
for await (const item of hoverProviderResults) {
172
if (!isEmptyMarkdownString(item.hover.contents)) {
173
const range = item.hover.range ? Range.lift(item.hover.range) : anchor.range;
174
const hoverSource = new HoverSource(item.hover, item.provider, position);
175
yield new MarkdownHover(this, range, item.hover.contents, false, item.ordinal, hoverSource);
176
}
177
}
178
}
179
180
public renderHoverParts(context: IEditorHoverRenderContext, hoverParts: MarkdownHover[]): IRenderedHoverParts<MarkdownHover> {
181
this._renderedHoverParts = new MarkdownRenderedHoverParts(
182
hoverParts,
183
context.fragment,
184
this,
185
this._editor,
186
this._commandService,
187
this._keybindingService,
188
this._hoverService,
189
this._configurationService,
190
this._markdownRendererService,
191
context.onContentsChanged
192
);
193
return this._renderedHoverParts;
194
}
195
196
public handleScroll(e: ScrollEvent): void {
197
this._renderedHoverParts?.handleScroll(e);
198
}
199
200
public getAccessibleContent(hoverPart: MarkdownHover): string {
201
return this._renderedHoverParts?.getAccessibleContent(hoverPart) ?? '';
202
}
203
204
public doesMarkdownHoverAtIndexSupportVerbosityAction(index: number, action: HoverVerbosityAction): boolean {
205
return this._renderedHoverParts?.doesMarkdownHoverAtIndexSupportVerbosityAction(index, action) ?? false;
206
}
207
208
public updateMarkdownHoverVerbosityLevel(action: HoverVerbosityAction, index: number): Promise<{ hoverPart: MarkdownHover; hoverElement: HTMLElement } | undefined> {
209
return Promise.resolve(this._renderedHoverParts?.updateMarkdownHoverPartVerbosityLevel(action, index));
210
}
211
}
212
213
class RenderedMarkdownHoverPart implements IRenderedHoverPart<MarkdownHover> {
214
215
constructor(
216
public readonly hoverPart: MarkdownHover,
217
public readonly hoverElement: HTMLElement,
218
public readonly disposables: DisposableStore,
219
public readonly actionsContainer?: HTMLElement
220
) { }
221
222
get hoverAccessibleContent(): string {
223
return this.hoverElement.innerText.trim();
224
}
225
226
dispose(): void {
227
this.disposables.dispose();
228
}
229
}
230
231
class MarkdownRenderedHoverParts implements IRenderedHoverParts<MarkdownHover> {
232
233
public renderedHoverParts: RenderedMarkdownHoverPart[];
234
235
private _ongoingHoverOperations: Map<HoverProvider, { verbosityDelta: number; tokenSource: CancellationTokenSource }> = new Map();
236
237
private readonly _disposables = new DisposableStore();
238
239
constructor(
240
hoverParts: MarkdownHover[],
241
hoverPartsContainer: DocumentFragment,
242
private readonly _hoverParticipant: MarkdownHoverParticipant,
243
private readonly _editor: ICodeEditor,
244
private readonly _commandService: ICommandService,
245
private readonly _keybindingService: IKeybindingService,
246
private readonly _hoverService: IHoverService,
247
private readonly _configurationService: IConfigurationService,
248
private readonly _markdownRendererService: IMarkdownRendererService,
249
private readonly _onFinishedRendering: () => void,
250
) {
251
this.renderedHoverParts = this._renderHoverParts(hoverParts, hoverPartsContainer, this._onFinishedRendering);
252
this._disposables.add(toDisposable(() => {
253
this.renderedHoverParts.forEach(renderedHoverPart => {
254
renderedHoverPart.dispose();
255
});
256
this._ongoingHoverOperations.forEach(operation => {
257
operation.tokenSource.dispose(true);
258
});
259
}));
260
}
261
262
private _renderHoverParts(
263
hoverParts: MarkdownHover[],
264
hoverPartsContainer: DocumentFragment,
265
onFinishedRendering: () => void,
266
): RenderedMarkdownHoverPart[] {
267
hoverParts.sort(compareBy(hover => hover.ordinal, numberComparator));
268
return hoverParts.map(hoverPart => {
269
const renderedHoverPart = this._renderHoverPart(hoverPart, onFinishedRendering);
270
hoverPartsContainer.appendChild(renderedHoverPart.hoverElement);
271
return renderedHoverPart;
272
});
273
}
274
275
private _renderHoverPart(
276
hoverPart: MarkdownHover,
277
onFinishedRendering: () => void
278
): RenderedMarkdownHoverPart {
279
280
const renderedMarkdownPart = this._renderMarkdownHover(hoverPart, onFinishedRendering);
281
const renderedMarkdownElement = renderedMarkdownPart.hoverElement;
282
const hoverSource = hoverPart.source;
283
const disposables = new DisposableStore();
284
disposables.add(renderedMarkdownPart);
285
286
if (!hoverSource) {
287
return new RenderedMarkdownHoverPart(hoverPart, renderedMarkdownElement, disposables);
288
}
289
290
const canIncreaseVerbosity = hoverSource.supportsVerbosityAction(HoverVerbosityAction.Increase);
291
const canDecreaseVerbosity = hoverSource.supportsVerbosityAction(HoverVerbosityAction.Decrease);
292
293
if (!canIncreaseVerbosity && !canDecreaseVerbosity) {
294
return new RenderedMarkdownHoverPart(hoverPart, renderedMarkdownElement, disposables);
295
}
296
297
const actionsContainer = $('div.verbosity-actions');
298
renderedMarkdownElement.prepend(actionsContainer);
299
const actionsContainerInner = $('div.verbosity-actions-inner');
300
actionsContainer.append(actionsContainerInner);
301
disposables.add(this._renderHoverExpansionAction(actionsContainerInner, HoverVerbosityAction.Increase, canIncreaseVerbosity));
302
disposables.add(this._renderHoverExpansionAction(actionsContainerInner, HoverVerbosityAction.Decrease, canDecreaseVerbosity));
303
return new RenderedMarkdownHoverPart(hoverPart, renderedMarkdownElement, disposables, actionsContainerInner);
304
}
305
306
private _renderMarkdownHover(
307
markdownHover: MarkdownHover,
308
onFinishedRendering: () => void
309
): IRenderedHoverPart<MarkdownHover> {
310
const renderedMarkdownHover = renderMarkdown(
311
this._editor,
312
markdownHover,
313
this._markdownRendererService,
314
onFinishedRendering,
315
);
316
return renderedMarkdownHover;
317
}
318
319
private _renderHoverExpansionAction(container: HTMLElement, action: HoverVerbosityAction, actionEnabled: boolean): DisposableStore {
320
const store = new DisposableStore();
321
const isActionIncrease = action === HoverVerbosityAction.Increase;
322
const actionElement = dom.append(container, $(ThemeIcon.asCSSSelector(isActionIncrease ? increaseHoverVerbosityIcon : decreaseHoverVerbosityIcon)));
323
actionElement.tabIndex = 0;
324
const hoverDelegate = store.add(new WorkbenchHoverDelegate('mouse', undefined, { target: container, position: { hoverPosition: HoverPosition.LEFT } }, this._configurationService, this._hoverService));
325
store.add(this._hoverService.setupManagedHover(hoverDelegate, actionElement, labelForHoverVerbosityAction(this._keybindingService, action)));
326
if (!actionEnabled) {
327
actionElement.classList.add('disabled');
328
return store;
329
}
330
actionElement.classList.add('enabled');
331
const actionFunction = () => this._commandService.executeCommand(action === HoverVerbosityAction.Increase ? INCREASE_HOVER_VERBOSITY_ACTION_ID : DECREASE_HOVER_VERBOSITY_ACTION_ID, { focus: true });
332
store.add(new ClickAction(actionElement, actionFunction));
333
store.add(new KeyDownAction(actionElement, actionFunction, [KeyCode.Enter, KeyCode.Space]));
334
return store;
335
}
336
337
public handleScroll(e: ScrollEvent): void {
338
this.renderedHoverParts.forEach(renderedHoverPart => {
339
const actionsContainerInner = renderedHoverPart.actionsContainer;
340
if (!actionsContainerInner) {
341
return;
342
}
343
const hoverElement = renderedHoverPart.hoverElement;
344
const topOfHoverScrollPosition = e.scrollTop;
345
const bottomOfHoverScrollPosition = topOfHoverScrollPosition + e.height;
346
const topOfRenderedPart = hoverElement.offsetTop;
347
const hoverElementHeight = hoverElement.clientHeight;
348
const bottomOfRenderedPart = topOfRenderedPart + hoverElementHeight;
349
const iconsHeight = 22;
350
let top: number;
351
if (bottomOfRenderedPart <= bottomOfHoverScrollPosition || topOfRenderedPart >= bottomOfHoverScrollPosition) {
352
top = hoverElementHeight - iconsHeight;
353
} else {
354
top = bottomOfHoverScrollPosition - topOfRenderedPart - iconsHeight;
355
}
356
actionsContainerInner.style.top = `${top}px`;
357
});
358
}
359
360
public async updateMarkdownHoverPartVerbosityLevel(action: HoverVerbosityAction, index: number): Promise<{ hoverPart: MarkdownHover; hoverElement: HTMLElement } | undefined> {
361
const model = this._editor.getModel();
362
if (!model) {
363
return undefined;
364
}
365
const hoverRenderedPart = this._getRenderedHoverPartAtIndex(index);
366
const hoverSource = hoverRenderedPart?.hoverPart.source;
367
if (!hoverRenderedPart || !hoverSource?.supportsVerbosityAction(action)) {
368
return undefined;
369
}
370
const newHover = await this._fetchHover(hoverSource, model, action);
371
if (!newHover) {
372
return undefined;
373
}
374
const newHoverSource = new HoverSource(newHover, hoverSource.hoverProvider, hoverSource.hoverPosition);
375
const initialHoverPart = hoverRenderedPart.hoverPart;
376
const newHoverPart = new MarkdownHover(
377
this._hoverParticipant,
378
initialHoverPart.range,
379
newHover.contents,
380
initialHoverPart.isBeforeContent,
381
initialHoverPart.ordinal,
382
newHoverSource
383
);
384
const newHoverRenderedPart = this._updateRenderedHoverPart(index, newHoverPart);
385
if (!newHoverRenderedPart) {
386
return undefined;
387
}
388
return {
389
hoverPart: newHoverPart,
390
hoverElement: newHoverRenderedPart.hoverElement
391
};
392
}
393
394
public getAccessibleContent(hoverPart: MarkdownHover): string | undefined {
395
const renderedHoverPartIndex = this.renderedHoverParts.findIndex(renderedHoverPart => renderedHoverPart.hoverPart === hoverPart);
396
if (renderedHoverPartIndex === -1) {
397
return undefined;
398
}
399
const renderedHoverPart = this._getRenderedHoverPartAtIndex(renderedHoverPartIndex);
400
if (!renderedHoverPart) {
401
return undefined;
402
}
403
const hoverElementInnerText = renderedHoverPart.hoverElement.innerText;
404
const accessibleContent = hoverElementInnerText.replace(/[^\S\n\r]+/gu, ' ');
405
return accessibleContent;
406
}
407
408
public doesMarkdownHoverAtIndexSupportVerbosityAction(index: number, action: HoverVerbosityAction): boolean {
409
const hoverRenderedPart = this._getRenderedHoverPartAtIndex(index);
410
const hoverSource = hoverRenderedPart?.hoverPart.source;
411
if (!hoverRenderedPart || !hoverSource?.supportsVerbosityAction(action)) {
412
return false;
413
}
414
return true;
415
}
416
417
private async _fetchHover(hoverSource: HoverSource, model: ITextModel, action: HoverVerbosityAction): Promise<Hover | null | undefined> {
418
let verbosityDelta = action === HoverVerbosityAction.Increase ? 1 : -1;
419
const provider = hoverSource.hoverProvider;
420
const ongoingHoverOperation = this._ongoingHoverOperations.get(provider);
421
if (ongoingHoverOperation) {
422
ongoingHoverOperation.tokenSource.cancel();
423
verbosityDelta += ongoingHoverOperation.verbosityDelta;
424
}
425
const tokenSource = new CancellationTokenSource();
426
this._ongoingHoverOperations.set(provider, { verbosityDelta, tokenSource });
427
const context: HoverContext = { verbosityRequest: { verbosityDelta, previousHover: hoverSource.hover } };
428
let hover: Hover | null | undefined;
429
try {
430
hover = await Promise.resolve(provider.provideHover(model, hoverSource.hoverPosition, tokenSource.token, context));
431
} catch (e) {
432
onUnexpectedExternalError(e);
433
}
434
tokenSource.dispose();
435
this._ongoingHoverOperations.delete(provider);
436
return hover;
437
}
438
439
private _updateRenderedHoverPart(index: number, hoverPart: MarkdownHover): RenderedMarkdownHoverPart | undefined {
440
if (index >= this.renderedHoverParts.length || index < 0) {
441
return undefined;
442
}
443
const renderedHoverPart = this._renderHoverPart(hoverPart, this._onFinishedRendering);
444
const currentRenderedHoverPart = this.renderedHoverParts[index];
445
const currentRenderedMarkdown = currentRenderedHoverPart.hoverElement;
446
const renderedMarkdown = renderedHoverPart.hoverElement;
447
const renderedChildrenElements = Array.from(renderedMarkdown.children);
448
currentRenderedMarkdown.replaceChildren(...renderedChildrenElements);
449
const newRenderedHoverPart = new RenderedMarkdownHoverPart(
450
hoverPart,
451
currentRenderedMarkdown,
452
renderedHoverPart.disposables,
453
renderedHoverPart.actionsContainer
454
);
455
currentRenderedHoverPart.dispose();
456
this.renderedHoverParts[index] = newRenderedHoverPart;
457
return newRenderedHoverPart;
458
}
459
460
private _getRenderedHoverPartAtIndex(index: number): RenderedMarkdownHoverPart | undefined {
461
return this.renderedHoverParts[index];
462
}
463
464
public dispose(): void {
465
this._disposables.dispose();
466
}
467
}
468
469
export function renderMarkdownHovers(
470
context: IEditorHoverRenderContext,
471
markdownHovers: MarkdownHover[],
472
editor: ICodeEditor,
473
markdownRendererService: IMarkdownRendererService,
474
): IRenderedHoverParts<MarkdownHover> {
475
476
// Sort hover parts to keep them stable since they might come in async, out-of-order
477
markdownHovers.sort(compareBy(hover => hover.ordinal, numberComparator));
478
const renderedHoverParts: IRenderedHoverPart<MarkdownHover>[] = [];
479
for (const markdownHover of markdownHovers) {
480
const renderedHoverPart = renderMarkdown(
481
editor,
482
markdownHover,
483
markdownRendererService,
484
context.onContentsChanged,
485
);
486
context.fragment.appendChild(renderedHoverPart.hoverElement);
487
renderedHoverParts.push(renderedHoverPart);
488
}
489
return new RenderedHoverParts(renderedHoverParts);
490
}
491
492
function renderMarkdown(
493
editor: ICodeEditor,
494
markdownHover: MarkdownHover,
495
markdownRendererService: IMarkdownRendererService,
496
onFinishedRendering: () => void,
497
): IRenderedHoverPart<MarkdownHover> {
498
const disposables = new DisposableStore();
499
const renderedMarkdown = $('div.hover-row');
500
const renderedMarkdownContents = $('div.hover-row-contents');
501
renderedMarkdown.appendChild(renderedMarkdownContents);
502
const markdownStrings = markdownHover.contents;
503
for (const markdownString of markdownStrings) {
504
if (isEmptyMarkdownString(markdownString)) {
505
continue;
506
}
507
const markdownHoverElement = $('div.markdown-hover');
508
const hoverContentsElement = dom.append(markdownHoverElement, $('div.hover-contents'));
509
510
const renderedContents = disposables.add(markdownRendererService.render(markdownString, {
511
context: editor,
512
asyncRenderCallback: () => {
513
hoverContentsElement.className = 'hover-contents code-hover-contents';
514
onFinishedRendering();
515
}
516
}));
517
hoverContentsElement.appendChild(renderedContents.element);
518
renderedMarkdownContents.appendChild(markdownHoverElement);
519
}
520
const renderedHoverPart: IRenderedHoverPart<MarkdownHover> = {
521
hoverPart: markdownHover,
522
hoverElement: renderedMarkdown,
523
dispose() { disposables.dispose(); }
524
};
525
return renderedHoverPart;
526
}
527
528
export function labelForHoverVerbosityAction(keybindingService: IKeybindingService, action: HoverVerbosityAction): string {
529
switch (action) {
530
case HoverVerbosityAction.Increase:
531
return keybindingService.appendKeybinding(nls.localize('increaseVerbosity', "Increase Hover Verbosity"), INCREASE_HOVER_VERBOSITY_ACTION_ID);
532
case HoverVerbosityAction.Decrease:
533
return keybindingService.appendKeybinding(nls.localize('decreaseVerbosity', "Decrease Hover Verbosity"), DECREASE_HOVER_VERBOSITY_ACTION_ID);
534
}
535
}
536
537