markdownHoverParticipant.ts 4.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import * as nls from 'vs/nls';
import * as dom from 'vs/base/browser/dom';
import { IMarkdownString, MarkdownString, isEmptyMarkdownString, markedStringsEquals } from 'vs/base/common/htmlContent';
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { Range } from 'vs/editor/common/core/range';
import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer';
import { asArray } from 'vs/base/common/arrays';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IModelDecoration, ITextModel } from 'vs/editor/common/model';
import { IEditorHover, IEditorHoverParticipant, IHoverPart } from 'vs/editor/contrib/hover/modesContentHover';
18 19 20 21
import { HoverProviderRegistry } from 'vs/editor/common/modes';
import { getHover } from 'vs/editor/contrib/hover/getHover';
import { Position } from 'vs/editor/common/core/position';
import { CancellationToken } from 'vs/base/common/cancellation';
22 23 24

const $ = dom.$;

A
Alexandru Dima 已提交
25
export class MarkdownHover implements IHoverPart {
26 27 28 29 30 31 32

	constructor(
		public readonly range: Range,
		public readonly contents: IMarkdownString[]
	) { }

	public equals(other: IHoverPart): boolean {
A
Alexandru Dima 已提交
33
		if (other instanceof MarkdownHover) {
34 35 36 37 38 39
			return markedStringsEquals(this.contents, other.contents);
		}
		return false;
	}
}

A
Alexandru Dima 已提交
40
export class MarkdownHoverParticipant implements IEditorHoverParticipant<MarkdownHover> {
41 42 43 44 45 46 47 48

	constructor(
		private readonly _editor: ICodeEditor,
		private readonly _hover: IEditorHover,
		@IModeService private readonly _modeService: IModeService,
		@IOpenerService private readonly _openerService: IOpenerService,
	) { }

49
	public createLoadingMessage(range: Range): MarkdownHover {
A
Alexandru Dima 已提交
50
		return new MarkdownHover(range, [new MarkdownString().appendText(nls.localize('modesContentHover.loading', "Loading..."))]);
51 52
	}

53
	public computeSync(hoverRange: Range, model: ITextModel, decoration: IModelDecoration): MarkdownHover | null {
54 55 56 57 58 59 60 61 62
		const hoverMessage = decoration.options.hoverMessage;
		if (!hoverMessage || isEmptyMarkdownString(hoverMessage)) {
			return null;
		}
		const lineNumber = hoverRange.startLineNumber;
		const maxColumn = model.getLineMaxColumn(lineNumber);
		const startColumn = (decoration.range.startLineNumber === lineNumber) ? decoration.range.startColumn : 1;
		const endColumn = (decoration.range.endLineNumber === lineNumber) ? decoration.range.endColumn : maxColumn;
		const range = new Range(hoverRange.startLineNumber, startColumn, hoverRange.startLineNumber, endColumn);
A
Alexandru Dima 已提交
63
		return new MarkdownHover(range, asArray(hoverMessage));
64 65
	}

66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
	public async computeAsync(range: Range, token: CancellationToken): Promise<MarkdownHover[]> {
		if (!this._editor.hasModel() || !range) {
			return Promise.resolve([]);
		}

		const model = this._editor.getModel();

		if (!HoverProviderRegistry.has(model)) {
			return Promise.resolve([]);
		}

		const hovers = await getHover(model, new Position(
			range.startLineNumber,
			range.startColumn
		), token);

		const result: MarkdownHover[] = [];
		for (const hover of hovers) {
			if (isEmptyMarkdownString(hover.contents)) {
				continue;
			}
			const rng = hover.range ? Range.lift(hover.range) : range;
			result.push(new MarkdownHover(rng, hover.contents));
		}
		return result;
	}

	public renderHoverParts(hoverParts: MarkdownHover[], fragment: DocumentFragment): IDisposable {
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
		const disposables = new DisposableStore();
		for (const hoverPart of hoverParts) {
			for (const contents of hoverPart.contents) {
				if (isEmptyMarkdownString(contents)) {
					continue;
				}
				const markdownHoverElement = $('div.hover-row.markdown-hover');
				const hoverContentsElement = dom.append(markdownHoverElement, $('div.hover-contents'));
				const renderer = disposables.add(new MarkdownRenderer({ editor: this._editor }, this._modeService, this._openerService));
				disposables.add(renderer.onDidRenderAsync(() => {
					hoverContentsElement.className = 'hover-contents code-hover-contents';
					this._hover.onContentsChanged();
				}));
				const renderedContents = disposables.add(renderer.render(contents));
				hoverContentsElement.appendChild(renderedContents.element);
				fragment.appendChild(markdownHoverElement);
			}
		}
		return disposables;
	}
}