提交 9073a3cc 编写于 作者: J Johannes Rieken

chore - consolidate markdown rendering more, reduce innerHTML usage, related...

chore - consolidate markdown rendering more, reduce innerHTML usage, related https://github.com/microsoft/vscode/issues/106395
上级 f711344b
......@@ -25,13 +25,16 @@ export interface MarkedOptions extends marked.MarkedOptions {
}
export interface MarkdownRenderOptions extends FormattedTextRenderOptions {
codeBlockRenderer?: (modeId: string, value: string) => Promise<string>;
codeBlockRenderer?: (modeId: string, value: string) => Promise<HTMLElement>;
codeBlockRenderCallback?: () => void;
baseUrl?: URI;
}
/**
* Create html nodes for the given content element.
* Low-level way create a html element from a markdown string.
*
* **Note** that for most cases you should be using [`MarkdownRenderer`](./src/vs/editor/browser/core/markdownRenderer.ts)
* which comes with support for pretty code block rendering and which uses the default way of handling links.
*/
export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRenderOptions = {}, markedOptions: MarkedOptions = {}): HTMLElement {
const element = createElement(options);
......@@ -158,12 +161,11 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
// but update the node with the real result later.
const id = defaultGenerator.nextId();
const promise = Promise.all([value, withInnerHTML]).then(values => {
const strValue = values[0];
const span = element.querySelector(`div[data-code="${id}"]`);
const span = <HTMLDivElement>element.querySelector(`div[data-code="${id}"]`);
if (span) {
span.innerHTML = strValue;
DOM.reset(span, values[0]);
}
}).catch(err => {
}).catch(_err => {
// ignore
});
......
......@@ -12,7 +12,7 @@ import { tokenizeToString } from 'vs/editor/common/modes/textToHtmlTokenizer';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { Emitter } from 'vs/base/common/event';
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { TokenizationRegistry } from 'vs/editor/common/modes';
import { ITokenizationSupport, TokenizationRegistry } from 'vs/editor/common/modes';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { URI } from 'vs/base/common/uri';
......@@ -22,11 +22,22 @@ export interface IMarkdownRenderResult extends IDisposable {
export interface IMarkdownRendererOptions {
editor?: ICodeEditor;
baseUrl?: URI
baseUrl?: URI;
codeBlockFontFamily?: string;
}
/**
* Markdown renderer that can render codeblocks with the editor mechanics. This
* renderer should always be preferred.
*/
export class MarkdownRenderer {
private static _ttpTokenizer = window.trustedTypes?.createPolicy('tokenizeToString', {
createHTML(value: string, tokenizer: ITokenizationSupport | undefined) {
return tokenizeToString(value, tokenizer);
}
});
private readonly _onDidRenderCodeBlock = new Emitter<void>();
readonly onDidRenderCodeBlock = this._onDidRenderCodeBlock.event;
......@@ -47,7 +58,7 @@ export class MarkdownRenderer {
if (!markdown) {
element = document.createElement('span');
} else {
element = renderMarkdown(markdown, this._getOptions(disposeables), markedOptions);
element = renderMarkdown(markdown, this._getRenderOptions(disposeables), markedOptions);
}
return {
......@@ -56,7 +67,7 @@ export class MarkdownRenderer {
};
}
protected _getOptions(disposeables: DisposableStore): MarkdownRenderOptions {
protected _getRenderOptions(disposeables: DisposableStore): MarkdownRenderOptions {
return {
baseUrl: this._options.baseUrl,
codeBlockRenderer: async (languageAlias, value) => {
......@@ -74,16 +85,27 @@ export class MarkdownRenderer {
}
this._modeService.triggerMode(modeId);
const tokenization = await TokenizationRegistry.getPromise(modeId) ?? undefined;
const code = tokenizeToString(value, tokenization);
return this._options.editor
? `<span style="font-family: ${this._options.editor.getOption(EditorOption.fontInfo).fontFamily}">${code}</span>`
: `<span>${code}</span>`;
const element = document.createElement('span');
element.innerHTML = MarkdownRenderer._ttpTokenizer
? MarkdownRenderer._ttpTokenizer.createHTML(value, tokenization) as unknown as string
: tokenizeToString(value, tokenization);
// use "good" font
let fontFamily = this._options.codeBlockFontFamily;
if (this._options.editor) {
fontFamily = this._options.editor.getOption(EditorOption.fontInfo).fontFamily;
}
if (fontFamily) {
element.style.fontFamily = fontFamily;
}
return element;
},
codeBlockRenderCallback: () => this._onDidRenderCodeBlock.fire(),
actionHandler: {
callback: (content) => {
this._openerService.open(content, { fromUserGesture: true }).catch(onUnexpectedError);
},
callback: (content) => this._openerService.open(content, { fromUserGesture: true }).catch(onUnexpectedError),
disposeables
}
};
......
......@@ -21,7 +21,7 @@ import { ColorPickerWidget } from 'vs/editor/contrib/colorPicker/colorPickerWidg
import { getHover } from 'vs/editor/contrib/hover/getHover';
import { HoverOperation, HoverStartMode, IHoverComputer } from 'vs/editor/contrib/hover/hoverOperation';
import { ContentHoverWidget } from 'vs/editor/contrib/hover/hoverWidgets';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer';
import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { coalesce, isNonEmptyArray, asArray } from 'vs/base/common/arrays';
import { IMarker, IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers';
......
......@@ -9,7 +9,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { HoverOperation, HoverStartMode, IHoverComputer } from 'vs/editor/contrib/hover/hoverOperation';
import { GlyphHoverWidget } from 'vs/editor/contrib/hover/hoverWidgets';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IOpenerService, NullOpenerService } from 'vs/platform/opener/common/opener';
import { asArray } from 'vs/base/common/arrays';
......
......@@ -14,7 +14,7 @@ import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentW
import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions';
import * as modes from 'vs/editor/common/modes';
import { IModeService } from 'vs/editor/common/services/modeService';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer';
import { Context } from 'vs/editor/contrib/parameterHints/provideSignatureHelp';
import * as nls from 'vs/nls';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
......
......@@ -25,7 +25,7 @@ import { attachListStyler } from 'vs/platform/theme/common/styler';
import { IThemeService, IColorTheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { registerColor, editorWidgetBackground, listFocusBackground, activeContrastBorder, listHighlightForeground, editorForeground, editorWidgetBorder, focusBorder, textLinkForeground, textCodeBlockBackground } from 'vs/platform/theme/common/colorRegistry';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { TimeoutTimer, CancelablePromise, createCancelablePromise, disposableTimeout } from 'vs/base/common/async';
......
......@@ -10,7 +10,7 @@ import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableEle
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { CompletionItem } from './suggest';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer';
import { MarkdownString } from 'vs/base/common/htmlContent';
import { Codicon } from 'vs/base/common/codicons';
import { Emitter, Event } from 'vs/base/common/event';
......
......@@ -13,7 +13,7 @@ import { URI } from 'vs/base/common/uri';
import { ITextModel } from 'vs/editor/common/model';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ICommentService } from 'vs/workbench/contrib/comments/browser/commentService';
......
......@@ -22,7 +22,7 @@ import { ITextModel } from 'vs/editor/common/model';
import * as modes from 'vs/editor/common/modes';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer';
import { peekViewBorder } from 'vs/editor/contrib/peekView/peekView';
import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/zoneWidget';
import * as nls from 'vs/nls';
......
......@@ -14,7 +14,7 @@ import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
import { URI } from 'vs/base/common/uri';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { handleANSIOutput } from 'vs/workbench/contrib/notebook/browser/view/output/transforms/errorTransform';
import { dirname } from 'vs/base/common/resources';
......
......@@ -12,7 +12,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { BOTTOM_CELL_TOOLBAR_GAP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CELL_MARGIN, CELL_TOP_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants';
import { EditorFoldingStateDelegate } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel';
import { CellFindMatch, ICellViewModel, MarkdownCellLayoutChangeEvent, MarkdownCellLayoutInfo, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer';
import { BaseCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel';
import { NotebookCellStateChangedEvent, NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
......
......@@ -26,7 +26,7 @@ import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/mode
import { CellKind, NotebookCellMetadata, INotebookSearchOptions, ICellRange, NotebookCellsChangeType, ICell, NotebookCellTextModelSplice, CellEditType, IProcessedOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { FoldingRegions } from 'vs/editor/contrib/folding/foldingRanges';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer';
import { dirname } from 'vs/base/common/resources';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { MultiModelEditStackElement, SingleModelEditStackElement } from 'vs/editor/common/model/editStack';
......
......@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { DisposableStore } from 'vs/base/common/lifecycle';
import { renderMarkdown } from 'vs/base/browser/markdownRenderer';
import { MarkdownRenderOptions } from 'vs/base/browser/markdownRenderer';
import { Event, Emitter } from 'vs/base/common/event';
import * as dom from 'vs/base/browser/dom';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
......@@ -18,6 +18,8 @@ import { AnchorPosition } from 'vs/base/browser/ui/contextview/contextview';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { MarkdownString } from 'vs/base/common/htmlContent';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer';
const $ = dom.$;
......@@ -52,6 +54,7 @@ export class HoverWidget extends Widget {
@IConfigurationService private readonly _configurationService: IConfigurationService,
@IOpenerService private readonly _openerService: IOpenerService,
@IWorkbenchLayoutService private readonly _workbenchLayoutService: IWorkbenchLayoutService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
) {
super();
......@@ -81,22 +84,30 @@ export class HoverWidget extends Widget {
const rowElement = $('div.hover-row.markdown-hover');
const contentsElement = $('div.hover-contents');
const markdown = typeof options.text === 'string' ? new MarkdownString().appendText(options.text) : options.text;
const markdownElement = renderMarkdown(markdown, {
const myRenderOptions: MarkdownRenderOptions = {
actionHandler: {
callback: (content) => this._linkHandler(content),
disposeables: this._messageListeners
},
codeBlockRenderer: async (_, value) => {
const fontFamily = this._configurationService.getValue<IEditorOptions>('editor').fontFamily || EDITOR_FONT_DEFAULTS.fontFamily;
return `<span style="font-family: ${fontFamily}; white-space: nowrap">${value.replace(/\n/g, '<br>')}</span>`;
},
codeBlockRenderCallback: () => {
contentsElement.classList.add('code-hover-contents');
// This changes the dimensions of the hover so trigger a layout
this._onRequestLayout.fire();
}
};
const mdRenderer = this._instantiationService.createInstance(class extends MarkdownRenderer {
_getRenderOptions(dispoables: DisposableStore) {
const value = super._getRenderOptions(dispoables);
return { ...value, ...myRenderOptions };
}
}, {
codeBlockFontFamily: this._configurationService.getValue<IEditorOptions>('editor').fontFamily || EDITOR_FONT_DEFAULTS.fontFamily
});
contentsElement.appendChild(markdownElement);
const { element } = mdRenderer.render(markdown);
contentsElement.appendChild(element);
rowElement.appendChild(contentsElement);
this._hover.contentsDomNode.appendChild(rowElement);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册