diff --git a/src/vs/base/browser/htmlContentRenderer.ts b/src/vs/base/browser/htmlContentRenderer.ts
index 0732b6752e8..a7de1cefb84 100644
--- a/src/vs/base/browser/htmlContentRenderer.ts
+++ b/src/vs/base/browser/htmlContentRenderer.ts
@@ -6,23 +6,14 @@
'use strict';
import DOM = require('vs/base/browser/dom');
-import htmlContent = require('vs/base/common/htmlContent');
+import {IHTMLContentElement} from 'vs/base/common/htmlContent';
-/**
- * Deal with different types of content. See @renderHtml
- */
-export function renderHtml2(content:string, actionCallback?:(index:number, event:DOM.IMouseEvent)=>void):Node[];
-export function renderHtml2(content:htmlContent.IHTMLContentElement, actionCallback?:(index:number, event:DOM.IMouseEvent)=>void):Node[];
-export function renderHtml2(content:htmlContent.IHTMLContentElement[], actionCallback?:(index:number, event:DOM.IMouseEvent)=>void):Node[];
-export function renderHtml2(content:any, actionCallback?:(index:number, event:DOM.IMouseEvent)=>void):Node[] {
- if (typeof content === 'string') {
- return [document.createTextNode(content)];
- } else if (Array.isArray(content)) {
- return (content).map((piece) => renderHtml(piece, actionCallback));
- } else if (content) {
- return [renderHtml(content, actionCallback)];
- }
- return [];
+
+export type RenderableContent = string | IHTMLContentElement | IHTMLContentElement[];
+
+export interface RenderOptions {
+ actionCallback?: (index: number, event: DOM.IMouseEvent) => void;
+ codeBlockRenderer?: (modeId: string, value: string) => IHTMLContentElement;
}
/**
@@ -31,39 +22,51 @@ export function renderHtml2(content:any, actionCallback?:(index:number, event:DO
* @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 renderHtml(content:htmlContent.IHTMLContentElement, actionCallback?:(index:number, event:DOM.IMouseEvent)=>void, codeBlockRenderer?:(modeId:string, value:string) => htmlContent.IHTMLContentElement):Node {
+export function renderHtml(content: RenderableContent, options: RenderOptions = {}): Node {
+ if (typeof content === 'string') {
+ return _renderHtml({ text: content }, options);
+ } else if (Array.isArray(content)) {
+ return _renderHtml({ children: content }, options);
+ } else if (content) {
+ return _renderHtml(content, options);
+ }
+}
- if(content.isText) {
+function _renderHtml(content: IHTMLContentElement, options: RenderOptions = {}): Node {
+
+ let {codeBlockRenderer, actionCallback} = options;
+
+ if (content.isText) {
return document.createTextNode(content.text);
}
var tagName = getSafeTagName(content.tagName) || 'div';
var element = document.createElement(tagName);
- if(content.className) {
+ if (content.className) {
element.className = content.className;
}
- if(content.text) {
+ if (content.text) {
element.textContent = content.text;
}
- if(content.style) {
+ if (content.style) {
element.setAttribute('style', content.style);
}
- if(content.customStyle) {
+ if (content.customStyle) {
Object.keys(content.customStyle).forEach((key) => {
element.style[key] = content.customStyle[key];
});
}
if (content.code && codeBlockRenderer) {
let child = codeBlockRenderer(content.code.language, content.code.value);
- element.appendChild(renderHtml(child, actionCallback, codeBlockRenderer));
+ element.appendChild(renderHtml(child, options));
}
- if(content.children) {
+ if (content.children) {
content.children.forEach((child) => {
- element.appendChild(renderHtml(child, actionCallback, codeBlockRenderer));
+ element.appendChild(renderHtml(child, options));
});
}
- if(content.formattedText) {
+ if (content.formattedText) {
renderFormattedText(element, parseFormattedText(content.formattedText), actionCallback);
}
@@ -110,29 +113,29 @@ function getSafeTagName(tagName: string): string {
}
class StringStream {
- private source:string;
- private index:number;
+ private source: string;
+ private index: number;
- constructor(source:string) {
+ constructor(source: string) {
this.source = source;
this.index = 0;
}
- public eos():boolean {
+ public eos(): boolean {
return this.index >= this.source.length;
}
- public next():string {
+ public next(): string {
var next = this.peek();
this.advance();
return next;
}
- public peek():string {
+ public peek(): string {
return this.source[this.index];
}
- public advance():void {
+ public advance(): void {
this.index++;
}
}
@@ -150,24 +153,24 @@ enum FormatType {
interface IFormatParseTree {
type: FormatType;
- content?:string;
- index?:number;
+ content?: string;
+ index?: number;
children?: IFormatParseTree[];
}
-function renderFormattedText(element:Node, treeNode:IFormatParseTree, actionCallback?:(index:number, event:DOM.IMouseEvent)=>void) {
+function renderFormattedText(element: Node, treeNode: IFormatParseTree, actionCallback?: (index: number, event: DOM.IMouseEvent) => void) {
var child: Node;
- if(treeNode.type === FormatType.Text) {
+ if (treeNode.type === FormatType.Text) {
child = document.createTextNode(treeNode.content);
}
- else if(treeNode.type === FormatType.Bold) {
+ else if (treeNode.type === FormatType.Bold) {
child = document.createElement('b');
}
- else if(treeNode.type === FormatType.Italics) {
+ else if (treeNode.type === FormatType.Italics) {
child = document.createElement('i');
}
- else if(treeNode.type === FormatType.Action) {
+ else if (treeNode.type === FormatType.Action) {
var a = document.createElement('a');
a.href = '#';
DOM.addStandardDisposableListener(a, 'click', (event) => {
@@ -176,37 +179,37 @@ function renderFormattedText(element:Node, treeNode:IFormatParseTree, actionCall
child = a;
}
- else if(treeNode.type === FormatType.NewLine) {
+ else if (treeNode.type === FormatType.NewLine) {
child = document.createElement('br');
}
- else if(treeNode.type === FormatType.Root) {
+ else if (treeNode.type === FormatType.Root) {
child = element;
}
- if(element !== child) {
+ if (element !== child) {
element.appendChild(child);
}
- if(Array.isArray(treeNode.children)) {
+ if (Array.isArray(treeNode.children)) {
treeNode.children.forEach((nodeChild) => {
renderFormattedText(child, nodeChild, actionCallback);
});
}
}
-function parseFormattedText(content:string):IFormatParseTree {
+function parseFormattedText(content: string): IFormatParseTree {
- var root:IFormatParseTree = {
+ var root: IFormatParseTree = {
type: FormatType.Root,
children: []
};
var actionItemIndex = 0;
var current = root;
- var stack:IFormatParseTree[] = [];
+ var stack: IFormatParseTree[] = [];
var stream = new StringStream(content);
- while(!stream.eos()) {
+ while (!stream.eos()) {
var next = stream.next();
var isEscapedFormatType = (next === '\\' && formatTagType(stream.peek()) !== FormatType.Invalid);
@@ -214,23 +217,23 @@ function parseFormattedText(content:string):IFormatParseTree {
next = stream.next(); // unread the backslash if it escapes a format tag type
}
- if(!isEscapedFormatType && isFormatTag(next) && next === stream.peek()) {
+ if (!isEscapedFormatType && isFormatTag(next) && next === stream.peek()) {
stream.advance();
- if(current.type === FormatType.Text) {
+ if (current.type === FormatType.Text) {
current = stack.pop();
}
var type = formatTagType(next);
- if(current.type === type || (current.type === FormatType.Action && type === FormatType.ActionClose)) {
+ if (current.type === type || (current.type === FormatType.Action && type === FormatType.ActionClose)) {
current = stack.pop();
} else {
- var newCurrent:IFormatParseTree = {
+ var newCurrent: IFormatParseTree = {
type: type,
children: []
};
- if(type === FormatType.Action) {
+ if (type === FormatType.Action) {
newCurrent.index = actionItemIndex;
actionItemIndex++;
}
@@ -239,8 +242,8 @@ function parseFormattedText(content:string):IFormatParseTree {
stack.push(current);
current = newCurrent;
}
- } else if(next === '\n') {
- if(current.type === FormatType.Text) {
+ } else if (next === '\n') {
+ if (current.type === FormatType.Text) {
current = stack.pop();
}
@@ -249,8 +252,8 @@ function parseFormattedText(content:string):IFormatParseTree {
});
} else {
- if(current.type !== FormatType.Text) {
- var textCurrent:IFormatParseTree = {
+ if (current.type !== FormatType.Text) {
+ var textCurrent: IFormatParseTree = {
type: FormatType.Text,
content: next
};
@@ -264,23 +267,23 @@ function parseFormattedText(content:string):IFormatParseTree {
}
}
- if(current.type === FormatType.Text) {
+ if (current.type === FormatType.Text) {
current = stack.pop();
}
- if(stack.length) {
+ if (stack.length) {
// incorrectly formatted string literal
}
return root;
}
-function isFormatTag(char:string):boolean {
+function isFormatTag(char: string): boolean {
return formatTagType(char) !== FormatType.Invalid;
}
-function formatTagType(char:string):FormatType {
- switch(char) {
+function formatTagType(char: string): FormatType {
+ switch (char) {
case '*':
return FormatType.Bold;
case '_':
diff --git a/src/vs/base/test/browser/htmlContent.test.ts b/src/vs/base/test/browser/htmlContent.test.ts
index c473ed8504b..8171bfecedb 100644
--- a/src/vs/base/test/browser/htmlContent.test.ts
+++ b/src/vs/base/test/browser/htmlContent.test.ts
@@ -116,9 +116,11 @@ suite("HtmlContent", () => {
var callbackCalled = false;
var result:HTMLElement = renderHtml({
formattedText: '[[action]]'
- }, (index) => {
- assert.strictEqual(index, 0);
- callbackCalled = true;
+ }, {
+ actionCallback(index) {
+ assert.strictEqual(index, 0);
+ callbackCalled = true;
+ }
});
assert.strictEqual(result.innerHTML, 'action');
@@ -132,9 +134,11 @@ suite("HtmlContent", () => {
var callbackCalled = false;
var result:HTMLElement = renderHtml({
formattedText: '__**[[action]]**__'
- }, (index) => {
- assert.strictEqual(index, 0);
- callbackCalled = true;
+ }, {
+ actionCallback(index) {
+ assert.strictEqual(index, 0);
+ callbackCalled = true;
+ }
});
assert.strictEqual(result.innerHTML, 'action');
diff --git a/src/vs/editor/contrib/gotoError/browser/gotoError.ts b/src/vs/editor/contrib/gotoError/browser/gotoError.ts
index 6268a98d582..7cab4ce0561 100644
--- a/src/vs/editor/contrib/gotoError/browser/gotoError.ts
+++ b/src/vs/editor/contrib/gotoError/browser/gotoError.ts
@@ -255,9 +255,7 @@ class MarkerNavigationWidget extends ZoneWidget.ZoneWidget {
}
this._element.text(text);
var htmlElem = this._element.getHTMLElement();
- HtmlContentRenderer.renderHtml2(marker.message).forEach((node) => {
- htmlElem.appendChild(node);
- });
+ htmlElem.appendChild(HtmlContentRenderer.renderHtml(marker.message));
var mode = this.editor.getModel().getMode();
this._quickFixSection.hide();
diff --git a/src/vs/editor/contrib/hover/browser/modesContentHover.ts b/src/vs/editor/contrib/hover/browser/modesContentHover.ts
index e0e92c72331..7b6d9a40b37 100644
--- a/src/vs/editor/contrib/hover/browser/modesContentHover.ts
+++ b/src/vs/editor/contrib/hover/browser/modesContentHover.ts
@@ -236,13 +236,15 @@ export class ModesContentHoverWidget extends HoverWidget.ContentHoverWidget {
if(msg.htmlContent && msg.htmlContent.length > 0) {
msg.htmlContent.forEach((content) => {
- container.appendChild(renderHtml(content, undefined, (modeId, value) => {
- let mode: Modes.IMode;
- let model = this._editor.getModel();
- if (!model.isDisposed()) {
- mode = model.getMode();
+ container.appendChild(renderHtml(content, {
+ codeBlockRenderer: (modeId, value) => {
+ let mode: Modes.IMode;
+ let model = this._editor.getModel();
+ if (!model.isDisposed()) {
+ mode = model.getMode();
+ }
+ return tokenizeToHtmlContent(value, model.getMode());
}
- return tokenizeToHtmlContent(value, model.getMode());
}));
});
} else {