diff --git a/src/vs/editor/contrib/hover/browser/hover.ts b/src/vs/editor/contrib/hover/browser/hover.ts index f402286dde1221325db6eb6b580587967bbe4a9f..f6b0be751373674f6ebf0c5d377f60d076120011 100644 --- a/src/vs/editor/contrib/hover/browser/hover.ts +++ b/src/vs/editor/contrib/hover/browser/hover.ts @@ -60,7 +60,7 @@ class ModesHoverController implements editorCommon.IEditorContribution { })); this._contentWidget = new ModesContentHoverWidget(editor, openerService, modeService); - this._glyphWidget = new ModesGlyphHoverWidget(editor); + this._glyphWidget = new ModesGlyphHoverWidget(editor, openerService, modeService); } } diff --git a/src/vs/editor/contrib/hover/browser/hoverWidgets.ts b/src/vs/editor/contrib/hover/browser/hoverWidgets.ts index 841e7e25a00f341b64bef53c7dfd2b77f1ff0cfd..06a3bab592f86b1397bbae259cc2d1c7006db4dc 100644 --- a/src/vs/editor/contrib/hover/browser/hoverWidgets.ts +++ b/src/vs/editor/contrib/hover/browser/hoverWidgets.ts @@ -156,8 +156,8 @@ export class GlyphHoverWidget extends Widget implements editorBrowser.IOverlayWi private _id: string; protected _editor: editorBrowser.ICodeEditor; - protected _isVisible: boolean; - protected _domNode: HTMLElement; + private _isVisible: boolean; + private _domNode: HTMLElement; protected _showAtLineNumber: number; constructor(id: string, editor: editorBrowser.ICodeEditor) { @@ -167,23 +167,30 @@ export class GlyphHoverWidget extends Widget implements editorBrowser.IOverlayWi this._isVisible = false; this._domNode = document.createElement('div'); - this._domNode.className = 'monaco-editor-hover monaco-editor-background'; - this._domNode.style.display = 'none'; + this._domNode.className = 'monaco-editor-hover hidden'; this._domNode.setAttribute('aria-hidden', 'true'); this._domNode.setAttribute('role', 'presentation'); this._showAtLineNumber = -1; - this._editor.applyFontInfo(this._domNode); this._register(this._editor.onDidChangeConfiguration((e:IConfigurationChangedEvent) => { if (e.fontInfo) { - this._editor.applyFontInfo(this._domNode); + this.updateFont(); } })); this._editor.addOverlayWidget(this); } + protected get isVisible(): boolean { + return this._isVisible; + } + + protected set isVisible(value: boolean) { + this._isVisible = value; + toggleClass(this._domNode, 'hidden', !this._isVisible); + } + public getId(): string { return this._id; } @@ -195,25 +202,26 @@ export class GlyphHoverWidget extends Widget implements editorBrowser.IOverlayWi public showAt(lineNumber: number): void { this._showAtLineNumber = lineNumber; - if (!this._isVisible) { - this._isVisible = true; - this._domNode.style.display = 'block'; + if (!this.isVisible) { + this.isVisible = true; } - let editorLayout = this._editor.getLayoutInfo(); - let topForLineNumber = this._editor.getTopForLineNumber(this._showAtLineNumber); - let editorScrollTop = this._editor.getScrollTop(); + const editorLayout = this._editor.getLayoutInfo(); + const topForLineNumber = this._editor.getTopForLineNumber(this._showAtLineNumber); + const editorScrollTop = this._editor.getScrollTop(); + const lineHeight = this._editor.getConfiguration().lineHeight; + const nodeHeight = this._domNode.clientHeight; + const top = topForLineNumber - editorScrollTop - ((nodeHeight - lineHeight) / 2); - this._domNode.style.left = (editorLayout.glyphMarginLeft + editorLayout.glyphMarginWidth) + 'px'; - this._domNode.style.top = (topForLineNumber - editorScrollTop) + 'px'; + this._domNode.style.left = `${ editorLayout.glyphMarginLeft + editorLayout.glyphMarginWidth }px`; + this._domNode.style.top = `${ Math.max(Math.round(top), 0) }px`; } public hide(): void { - if (!this._isVisible) { + if (!this.isVisible) { return; } - this._isVisible = false; - this._domNode.style.display = 'none'; + this.isVisible = false; } public getPosition():editorBrowser.IOverlayWidgetPosition { @@ -224,4 +232,17 @@ export class GlyphHoverWidget extends Widget implements editorBrowser.IOverlayWi this._editor.removeOverlayWidget(this); super.dispose(); } + + private updateFont(): void { + const codeTags: HTMLPhraseElement[] = Array.prototype.slice.call(this._domNode.getElementsByTagName('code')); + const codeClasses: HTMLElement[] = Array.prototype.slice.call(this._domNode.getElementsByClassName('code')); + + [...codeTags, ...codeClasses].forEach(node => this._editor.applyFontInfo(node)); + } + + protected updateContents(node: Node): void { + this._domNode.textContent = ''; + this._domNode.appendChild(node); + this.updateFont(); + } } diff --git a/src/vs/editor/contrib/hover/browser/modesGlyphHover.ts b/src/vs/editor/contrib/hover/browser/modesGlyphHover.ts index 1e25ca5b47c8d39b69b7758b0ef27529e3dc4d61..44e4ab30fd1c45c90edd33fc14a4f14ad3e5278e 100644 --- a/src/vs/editor/contrib/hover/browser/modesGlyphHover.ts +++ b/src/vs/editor/contrib/hover/browser/modesGlyphHover.ts @@ -8,6 +8,13 @@ import {IModelDecoration, IRange} from 'vs/editor/common/editorCommon'; import {ICodeEditor} from 'vs/editor/browser/editorBrowser'; import {HoverOperation, IHoverComputer} from './hoverOperation'; import {GlyphHoverWidget} from './hoverWidgets'; +import {$} from 'vs/base/browser/dom'; +import {renderMarkedString} from 'vs/base/browser/htmlContentRenderer'; +import {IOpenerService, NullOpenerService} from 'vs/platform/opener/common/opener'; +import URI from 'vs/base/common/uri'; +import {TPromise} from 'vs/base/common/winjs.base'; +import {IModeService} from 'vs/editor/common/services/modeService'; +import {tokenizeToString} from 'vs/editor/common/modes/textToHtmlTokenizer'; export interface IHoverMessage { value?: string; @@ -77,9 +84,11 @@ export class ModesGlyphHoverWidget extends GlyphHoverWidget { private _computer: MarginComputer; private _hoverOperation: HoverOperation; - constructor(editor: ICodeEditor) { + constructor(editor: ICodeEditor, private openerService: IOpenerService, private modeService: IModeService) { super(ModesGlyphHoverWidget.ID, editor); + this.openerService = openerService || NullOpenerService; + this._lastLineNumber = -1; this._computer = new MarginComputer(this._editor); @@ -99,7 +108,7 @@ export class ModesGlyphHoverWidget extends GlyphHoverWidget { } public onModelDecorationsChanged(): void { - if (this._isVisible) { + if (this.isVisible) { // The decorations have changed and the hover is visible, // we need to recompute the displayed text this._hoverOperation.cancel(); @@ -141,29 +150,25 @@ export class ModesGlyphHoverWidget extends GlyphHoverWidget { private _renderMessages(lineNumber: number, messages: IHoverMessage[]): void { - var fragment = document.createDocumentFragment(); + const fragment = document.createDocumentFragment(); messages.forEach((msg) => { - - var row:HTMLElement = document.createElement('div'); - var span:HTMLElement = null; - - if (msg.className) { - span = document.createElement('span'); - span.textContent = msg.value; - span.className = msg.className; - row.appendChild(span); - } else { - row.textContent = msg.value; - } - - fragment.appendChild(row); + const renderedContents = renderMarkedString(msg.value, { + actionCallback: content => this.openerService.open(URI.parse(content)), + codeBlockRenderer: (modeId, value): string | TPromise => { + const mode = this.modeService.getMode(modeId || this._editor.getModel().getModeId()); + const getMode = mode => mode ? TPromise.as(mode) : this.modeService.getOrCreateMode(modeId); + + return getMode(mode) + .then(null, err => null) + .then(mode => `
${ tokenizeToString(value, mode) }
`); + } + }); + + fragment.appendChild($('div.hover-row', null, renderedContents)); }); - this._domNode.textContent = ''; - this._domNode.appendChild(fragment); - - // show + this.updateContents(fragment); this.showAt(lineNumber); } } \ No newline at end of file