introduce MarkdownRenderer to avoid code duplication and to have the one place for keeping the renderer options, #11877

This commit is contained in:
Johannes Rieken
2017-09-08 11:29:21 +02:00
parent 9dd403ecc6
commit 48bc6f18ed
5 changed files with 69 additions and 54 deletions

View File

@@ -47,7 +47,7 @@ export function renderFormattedText(formattedText: string, options: RenderOption
* @param content a html element description
* @param actionCallback a callback function for any action links in the string. Argument is the zero-based index of the clicked action.
*/
export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions = {}): Node {
export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions = {}): HTMLElement {
const element = createElement(options);
const { codeBlockRenderer, actionCallback } = options;

View File

@@ -23,6 +23,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { editorHoverHighlight, editorHoverBackground, editorHoverBorder, textLinkForeground, textCodeBlockBackground } from 'vs/platform/theme/common/colorRegistry';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/browser/markdownRenderer';
@editorContribution
export class ModesHoverController implements editorCommon.IEditorContribution {
@@ -64,9 +65,9 @@ export class ModesHoverController implements editorCommon.IEditorContribution {
this._hideWidgets();
}
}));
this._contentWidget = new ModesContentHoverWidget(editor, openerService, modeService);
this._glyphWidget = new ModesGlyphHoverWidget(editor, openerService, modeService);
const renderer = new MarkdownRenderer(editor, modeService, openerService);
this._contentWidget = new ModesContentHoverWidget(editor, renderer);
this._glyphWidget = new ModesGlyphHoverWidget(editor, renderer);
}
}

View File

@@ -5,22 +5,17 @@
'use strict';
import * as nls from 'vs/nls';
import URI from 'vs/base/common/uri';
import { onUnexpectedError } from 'vs/base/common/errors';
import * as dom from 'vs/base/browser/dom';
import { TPromise } from 'vs/base/common/winjs.base';
import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer';
import { IOpenerService, NullOpenerService } from 'vs/platform/opener/common/opener';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IRange, Range } from 'vs/editor/common/core/range';
import { Position } from 'vs/editor/common/core/position';
import { HoverProviderRegistry, Hover, IColor, IColorFormatter } from 'vs/editor/common/modes';
import { tokenizeToString } from 'vs/editor/common/modes/textToHtmlTokenizer';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { getHover } from '../common/hover';
import { HoverOperation, IHoverComputer } from './hoverOperation';
import { ContentHoverWidget } from './hoverWidgets';
import { IMarkdownString, MarkdownString, isEmptyMarkdownString } from 'vs/base/common/htmlContent';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/browser/markdownRenderer';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations';
import { ColorPickerModel } from 'vs/editor/contrib/colorPicker/browser/colorPickerModel';
import { ColorPickerWidget } from 'vs/editor/contrib/colorPicker/browser/colorPickerWidget';
@@ -166,22 +161,20 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
private _hoverOperation: HoverOperation<HoverPart[]>;
private _highlightDecorations: string[];
private _isChangingDecorations: boolean;
private _openerService: IOpenerService;
private _modeService: IModeService;
private _markdownRenderer: MarkdownRenderer;
private _shouldFocus: boolean;
private _colorPicker: ColorPickerWidget;
private renderDisposable: IDisposable = EmptyDisposable;
private toDispose: IDisposable[];
constructor(editor: ICodeEditor, openerService: IOpenerService, modeService: IModeService) {
constructor(editor: ICodeEditor, markdownRenderner: MarkdownRenderer) {
super(ModesContentHoverWidget.ID, editor);
this._computer = new ModesContentComputer(this._editor);
this._highlightDecorations = [];
this._isChangingDecorations = false;
this._openerService = openerService || NullOpenerService;
this._modeService = modeService;
this._markdownRenderer = markdownRenderner;
this._hoverOperation = new HoverOperation(
this._computer,
@@ -312,24 +305,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
msg.contents
.filter(contents => !isEmptyMarkdownString(contents))
.forEach(contents => {
const renderedContents = renderMarkdown(contents, {
actionCallback: (content) => {
this._openerService.open(URI.parse(content)).then(void 0, onUnexpectedError);
},
codeBlockRenderer: (languageAlias, value): string | TPromise<string> => {
// In markdown,
// it is possible that we stumble upon language aliases (e.g.js instead of javascript)
// it is possible no alias is given in which case we fall back to the current editor lang
const modeId = languageAlias
? this._modeService.getModeIdForLanguageName(languageAlias)
: this._editor.getModel().getLanguageIdentifier().language;
return this._modeService.getOrCreateMode(modeId).then(_ => {
return tokenizeToString(value, modeId);
});
}
});
const renderedContents = this._markdownRenderer.render(contents);
fragment.appendChild($('div.hover-row', null, renderedContents));
});
} else {

View File

@@ -8,13 +8,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { HoverOperation, IHoverComputer } from './hoverOperation';
import { GlyphHoverWidget } from './hoverWidgets';
import { $ } from 'vs/base/browser/dom';
import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer';
import { IOpenerService, NullOpenerService } from 'vs/platform/opener/common/opener';
import URI from 'vs/base/common/uri';
import { onUnexpectedError } from 'vs/base/common/errors';
import { TPromise } from 'vs/base/common/winjs.base';
import { IModeService } from 'vs/editor/common/services/modeService';
import { tokenizeToString } from 'vs/editor/common/modes/textToHtmlTokenizer';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/browser/markdownRenderer';
import { IMarkdownString, isEmptyMarkdownString } from 'vs/base/common/htmlContent';
export interface IHoverMessage {
@@ -94,16 +88,16 @@ export class ModesGlyphHoverWidget extends GlyphHoverWidget {
private _messages: IHoverMessage[];
private _lastLineNumber: number;
private _markdownRenderer: MarkdownRenderer;
private _computer: MarginComputer;
private _hoverOperation: HoverOperation<IHoverMessage[]>;
constructor(editor: ICodeEditor, private openerService: IOpenerService, private modeService: IModeService) {
constructor(editor: ICodeEditor, markdownRenderer: MarkdownRenderer) {
super(ModesGlyphHoverWidget.ID, editor);
this.openerService = openerService || NullOpenerService;
this._lastLineNumber = -1;
this._markdownRenderer = markdownRenderer;
this._computer = new MarginComputer(this._editor);
this._hoverOperation = new HoverOperation(
@@ -166,17 +160,7 @@ export class ModesGlyphHoverWidget extends GlyphHoverWidget {
const fragment = document.createDocumentFragment();
messages.forEach((msg) => {
const renderedContents = renderMarkdown(msg.value, {
actionCallback: content => this.openerService.open(URI.parse(content)).then(undefined, onUnexpectedError),
codeBlockRenderer: (languageAlias, value): string | TPromise<string> => {
// In markdown, it is possible that we stumble upon language aliases (e.g. js instead of javascript)
const modeId = this.modeService.getModeIdForLanguageName(languageAlias);
return this.modeService.getOrCreateMode(modeId).then(_ => {
return tokenizeToString(value, modeId);
});
}
});
const renderedContents = this._markdownRenderer.render(msg.value);
fragment.appendChild($('div.hover-row', null, renderedContents));
});

View File

@@ -0,0 +1,54 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { renderMarkdown, RenderOptions } from 'vs/base/browser/htmlContentRenderer';
import { IOpenerService, NullOpenerService } from 'vs/platform/opener/common/opener';
import { IModeService } from 'vs/editor/common/services/modeService';
import URI from 'vs/base/common/uri';
import { onUnexpectedError } from 'vs/base/common/errors';
import { tokenizeToString } from 'vs/editor/common/modes/textToHtmlTokenizer';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { optional } from 'vs/platform/instantiation/common/instantiation';
export class MarkdownRenderer {
private readonly _options: RenderOptions;
constructor(
editor: ICodeEditor,
@IModeService private readonly _modeService: IModeService,
@optional(IOpenerService) private readonly _openerService: IOpenerService = NullOpenerService,
) {
this._options = {
actionCallback: (content) => {
this._openerService.open(URI.parse(content)).then(void 0, onUnexpectedError);
},
codeBlockRenderer: (languageAlias, value): string | TPromise<string> => {
// In markdown,
// it is possible that we stumble upon language aliases (e.g.js instead of javascript)
// it is possible no alias is given in which case we fall back to the current editor lang
const modeId = languageAlias
? this._modeService.getModeIdForLanguageName(languageAlias)
: editor.getModel().getLanguageIdentifier().language;
return this._modeService.getOrCreateMode(modeId).then(_ => {
return tokenizeToString(value, modeId);
});
}
};
}
render(markdown: IMarkdownString, options?: RenderOptions): HTMLElement {
if (options) {
return renderMarkdown(markdown, { ...options, ...this._options });
} else {
return renderMarkdown(markdown, this._options);
}
}
}