diff --git a/build/monaco/monaco.d.ts.recipe b/build/monaco/monaco.d.ts.recipe
index 9c2970d508b..cdaee801a9c 100644
--- a/build/monaco/monaco.d.ts.recipe
+++ b/build/monaco/monaco.d.ts.recipe
@@ -36,7 +36,7 @@ declare module monaco {
#include(vs/base/common/cancellation): CancellationTokenSource, CancellationToken
#include(vs/base/common/uri): URI
#include(vs/editor/common/standalone/standaloneBase): KeyCode, KeyMod
-#include(vs/base/common/htmlContent): MarkedString
+#include(vs/base/common/htmlContent): IMarkdownString
#include(vs/base/browser/keyboardEvent): IKeyboardEvent
#include(vs/base/browser/mouseEvent): IMouseEvent
#include(vs/editor/common/editorCommon): IScrollEvent
diff --git a/src/vs/base/browser/htmlContentRenderer.ts b/src/vs/base/browser/htmlContentRenderer.ts
index 6dd423dbec1..0da820a99f9 100644
--- a/src/vs/base/browser/htmlContentRenderer.ts
+++ b/src/vs/base/browser/htmlContentRenderer.ts
@@ -10,7 +10,7 @@ import * as DOM from 'vs/base/browser/dom';
import { defaultGenerator } from 'vs/base/common/idGenerator';
import { escape } from 'vs/base/common/strings';
import { TPromise } from 'vs/base/common/winjs.base';
-import { MarkedString, removeMarkdownEscapes } from 'vs/base/common/htmlContent';
+import { removeMarkdownEscapes, IMarkdownString } from 'vs/base/common/htmlContent';
import { marked } from 'vs/base/common/marked/marked';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
@@ -30,14 +30,6 @@ function createElement(options: RenderOptions): HTMLElement {
return element;
}
-
-export function renderMarkedString(markedString: MarkedString, options: RenderOptions = {}): Node {
- // this is sort of legacy given that we have full
- // support for markdown. Turn this into markdown
- // and continue
- return renderMarkdown(markedString, options);
-}
-
export function renderText(text: string, options: RenderOptions = {}): Node {
const element = createElement(options);
element.textContent = text;
@@ -56,7 +48,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: string, options: RenderOptions = {}): Node {
+export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions = {}): Node {
const element = createElement(options);
const { codeBlockRenderer, actionCallback } = options;
@@ -113,7 +105,9 @@ export function renderMarkdown(markdown: string, options: RenderOptions = {}): N
if (!href || href.match(/^data:|javascript:/i)) {
return text;
} else if (href.match(/^command:/i)) {
- return `${text} `;
+ return markdown.enableCommands
+ ? `${text} `
+ : text;
} else {
return `${text}`;
@@ -161,7 +155,7 @@ export function renderMarkdown(markdown: string, options: RenderOptions = {}): N
});
}
- element.innerHTML = marked(markdown, {
+ element.innerHTML = marked(markdown.value, {
sanitize: true,
renderer
});
diff --git a/src/vs/base/common/htmlContent.ts b/src/vs/base/common/htmlContent.ts
index c758efe59c8..f047d7effce 100644
--- a/src/vs/base/common/htmlContent.ts
+++ b/src/vs/base/common/htmlContent.ts
@@ -8,28 +8,75 @@
import { equals } from 'vs/base/common/arrays';
import { marked } from 'vs/base/common/marked/marked';
-/**
- * MarkedString can be used to render human readable text. It is either a markdown string
- * or a code-block that provides a language and a code snippet. Note that
- * markdown strings will be sanitized - that means html will be escaped.
- */
-export type MarkedString = string;
+export interface IMarkdownString {
+ value: string;
+ enableCommands?: true;
+}
-export function markedStringsEquals(a: MarkedString | MarkedString[], b: MarkedString | MarkedString[]): boolean {
+export class MarkdownString implements IMarkdownString {
+
+ static isMarkdownString(thing: any): thing is IMarkdownString {
+ if (thing instanceof MarkdownString) {
+ return true;
+ } else if (typeof thing === 'object') {
+ return typeof (thing).value === 'string'
+ && (typeof (thing).enableCommands === 'boolean' || (thing).enableCommands === void 0);
+ }
+ return false;
+ }
+
+ value: string;
+ enableCommands?: true;
+
+ constructor(value: string = '') {
+ this.value = value;
+ }
+
+ appendText(value: string): this {
+ this.value += textToMarkedString(value);
+ return this;
+ }
+
+ // appendMarkdown(value: string): this {
+ // this.value += value;
+ // return this;
+ // }
+
+ appendCodeblock(langId: string, code: string): this {
+ this.value += '```';
+ this.value += langId;
+ this.value += '\n';
+ this.value += code;
+ this.value += '```\n';
+ return this;
+ }
+}
+
+export function markedStringsEquals(a: IMarkdownString | IMarkdownString[], b: IMarkdownString | IMarkdownString[]): boolean {
if (!a && !b) {
return true;
} else if (!a || !b) {
return false;
- } else if (typeof a === 'string' && typeof b === 'string') {
- return a === b;
} else if (Array.isArray(a) && Array.isArray(b)) {
- return equals(a, b);
+ return equals(a, b, markdownStringEqual);
+ } else if (MarkdownString.isMarkdownString(a) && MarkdownString.isMarkdownString(b)) {
+ return markdownStringEqual(a, b);
} else {
return false;
}
}
-export function textToMarkedString(text: string): MarkedString {
+function markdownStringEqual(a: IMarkdownString, b: IMarkdownString): boolean {
+ if (a === b) {
+ return true;
+ } else if (!a || !b) {
+ return false;
+ } else {
+ return a.value === b.value && a.enableCommands === b.enableCommands;
+ }
+}
+
+export function textToMarkedString(text: string): string {
return text.replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&'); // escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash
}
@@ -40,7 +87,7 @@ export function removeMarkdownEscapes(text: string): string {
return text.replace(/\\([\\`*_{}[\]()#+\-.!])/g, '$1');
}
-export function containsCommandLink(value: MarkedString): boolean {
+export function containsCommandLink(value: string): boolean {
let uses = false;
const renderer = new marked.Renderer();
renderer.link = (href, title, text): string => {
diff --git a/src/vs/base/test/browser/htmlContent.test.ts b/src/vs/base/test/browser/htmlContent.test.ts
index 537777365e4..dcd1c4d5afb 100644
--- a/src/vs/base/test/browser/htmlContent.test.ts
+++ b/src/vs/base/test/browser/htmlContent.test.ts
@@ -87,35 +87,35 @@ suite('HtmlContent', () => {
assert.strictEqual(result.innerHTML, '**bold**');
});
test('image rendering conforms to default', () => {
- const markdown = ``;
+ const markdown = { value: `` };
const result: HTMLElement = renderMarkdown(markdown);
const renderer = new marked.Renderer();
- const imageFromMarked = marked(markdown, {
+ const imageFromMarked = marked(markdown.value, {
sanitize: true,
renderer
}).trim();
assert.strictEqual(result.innerHTML, imageFromMarked);
});
test('image rendering conforms to default without title', () => {
- const markdown = ``;
+ const markdown = { value: `` };
const result: HTMLElement = renderMarkdown(markdown);
const renderer = new marked.Renderer();
- const imageFromMarked = marked(markdown, {
+ const imageFromMarked = marked(markdown.value, {
sanitize: true,
renderer
}).trim();
assert.strictEqual(result.innerHTML, imageFromMarked);
});
test('image width from title params', () => {
- var result: HTMLElement = renderMarkdown(``);
+ var result: HTMLElement = renderMarkdown({ value: `` });
assert.strictEqual(result.innerHTML, `
`);
});
test('image height from title params', () => {
- var result: HTMLElement = renderMarkdown(``);
+ var result: HTMLElement = renderMarkdown({ value: `` });
assert.strictEqual(result.innerHTML, `
`);
});
test('image width and height from title params', () => {
- var result: HTMLElement = renderMarkdown(``);
+ var result: HTMLElement = renderMarkdown({ value: `` });
assert.strictEqual(result.innerHTML, `
`);
});
-});
\ No newline at end of file
+});
diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts
index b2a8bde84ba..be008c50178 100644
--- a/src/vs/editor/common/editorCommon.ts
+++ b/src/vs/editor/common/editorCommon.ts
@@ -5,7 +5,7 @@
'use strict';
import { BulkListenerCallback } from 'vs/base/common/eventEmitter';
-import { MarkedString } from 'vs/base/common/htmlContent';
+import { IMarkdownString } from 'vs/base/common/htmlContent';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
@@ -77,11 +77,11 @@ export interface IModelDecorationOptions {
/**
* Message to be rendered when hovering over the glyph margin decoration.
*/
- glyphMarginHoverMessage?: MarkedString | MarkedString[];
+ glyphMarginHoverMessage?: IMarkdownString | IMarkdownString[];
/**
- * Array of MarkedString to render as the decoration message.
+ * Array of MarkdownString to render as the decoration message.
*/
- hoverMessage?: MarkedString | MarkedString[];
+ hoverMessage?: IMarkdownString | IMarkdownString[];
/**
* Should the decoration expand to encompass a whole line.
*/
@@ -1728,7 +1728,7 @@ export interface IDecorationInstanceRenderOptions extends IThemeDecorationInstan
*/
export interface IDecorationOptions {
range: IRange;
- hoverMessage?: MarkedString | MarkedString[];
+ hoverMessage?: IMarkdownString | IMarkdownString[];
renderOptions?: IDecorationInstanceRenderOptions;
}
diff --git a/src/vs/editor/common/model/textModelWithDecorations.ts b/src/vs/editor/common/model/textModelWithDecorations.ts
index 932d6a2c6ec..d9f84771f84 100644
--- a/src/vs/editor/common/model/textModelWithDecorations.ts
+++ b/src/vs/editor/common/model/textModelWithDecorations.ts
@@ -5,7 +5,7 @@
'use strict';
import { onUnexpectedError } from 'vs/base/common/errors';
-import { MarkedString, markedStringsEquals } from 'vs/base/common/htmlContent';
+import { IMarkdownString, markedStringsEquals } from 'vs/base/common/htmlContent';
import * as strings from 'vs/base/common/strings';
import { CharCode } from 'vs/base/common/charCode';
import { Range, IRange } from 'vs/editor/common/core/range';
@@ -894,8 +894,8 @@ export class ModelDecorationOptions implements editorCommon.IModelDecorationOpti
readonly staticId: number;
readonly stickiness: editorCommon.TrackedRangeStickiness;
readonly className: string;
- readonly hoverMessage: MarkedString | MarkedString[];
- readonly glyphMarginHoverMessage: MarkedString | MarkedString[];
+ readonly hoverMessage: IMarkdownString | IMarkdownString[];
+ readonly glyphMarginHoverMessage: IMarkdownString | IMarkdownString[];
readonly isWholeLine: boolean;
readonly showIfCollapsed: boolean;
readonly overviewRuler: ModelDecorationOverviewRulerOptions;
@@ -911,7 +911,7 @@ export class ModelDecorationOptions implements editorCommon.IModelDecorationOpti
this.stickiness = options.stickiness || editorCommon.TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges;
this.className = options.className ? cleanClassName(options.className) : strings.empty;
this.hoverMessage = options.hoverMessage || [];
- this.glyphMarginHoverMessage = options.glyphMarginHoverMessage || strings.empty;
+ this.glyphMarginHoverMessage = options.glyphMarginHoverMessage || [];
this.isWholeLine = options.isWholeLine || false;
this.showIfCollapsed = options.showIfCollapsed || false;
this.overviewRuler = new ModelDecorationOverviewRulerOptions(options.overviewRuler);
diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts
index 3e82ee59652..4d7bebc731d 100644
--- a/src/vs/editor/common/modes.ts
+++ b/src/vs/editor/common/modes.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
-import { MarkedString } from 'vs/base/common/htmlContent';
+import { IMarkdownString } from 'vs/base/common/htmlContent';
import { IDisposable } from 'vs/base/common/lifecycle';
import URI from 'vs/base/common/uri';
import * as editorCommon from 'vs/editor/common/editorCommon';
@@ -160,7 +160,7 @@ export interface Hover {
/**
* The contents of this hover.
*/
- contents: MarkedString[];
+ contents: IMarkdownString[];
/**
* The range to which this hover applies. When missing, the
diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts
index 14e90ee2cfb..20565a52a3f 100644
--- a/src/vs/editor/common/services/modelServiceImpl.ts
+++ b/src/vs/editor/common/services/modelServiceImpl.ts
@@ -8,7 +8,7 @@ import * as nls from 'vs/nls';
import network = require('vs/base/common/network');
import Event, { Emitter } from 'vs/base/common/event';
import { EmitterEvent } from 'vs/base/common/eventEmitter';
-import { MarkedString } from 'vs/base/common/htmlContent';
+import { IMarkdownString } from 'vs/base/common/htmlContent';
import { IDisposable } from 'vs/base/common/lifecycle';
import Severity from 'vs/base/common/severity';
import URI from 'vs/base/common/uri';
@@ -136,7 +136,7 @@ class ModelMarkerHandler {
break;
}
- let hoverMessage: MarkedString[] = null;
+ let hoverMessage: IMarkdownString = null;
let { message, source } = marker;
if (typeof message === 'string') {
@@ -150,7 +150,7 @@ class ModelMarkerHandler {
}
}
- hoverMessage = ['```_\n' + message + '\n```'];
+ hoverMessage = { value: '```_\n' + message + '\n```' };
}
return {
diff --git a/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.ts b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.ts
index f78d1b59cef..bb47505032e 100644
--- a/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.ts
+++ b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclarationMouse.ts
@@ -9,7 +9,7 @@ import 'vs/css!./goToDeclarationMouse';
import * as nls from 'vs/nls';
import { Throttler } from 'vs/base/common/async';
import { onUnexpectedError } from 'vs/base/common/errors';
-import { MarkedString } from 'vs/base/common/htmlContent';
+import { MarkdownString } from 'vs/base/common/htmlContent';
import { TPromise } from 'vs/base/common/winjs.base';
import { IModeService } from 'vs/editor/common/services/modeService';
import { Range } from 'vs/editor/common/core/range';
@@ -112,7 +112,10 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
// Multiple results
if (results.length > 1) {
- this.addDecoration(new Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn), nls.localize('multipleResults', "Click to show {0} definitions.", results.length));
+ this.addDecoration(
+ new Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn),
+ new MarkdownString().appendText(nls.localize('multipleResults', "Click to show {0} definitions.", results.length))
+ );
}
// Single result
@@ -156,7 +159,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
this.addDecoration(
new Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn),
- '```' + this.modeService.getModeIdByFilenameOrFirstLine(textEditorModel.uri.fsPath) + '\n' + value + '\n```'
+ new MarkdownString().appendCodeblock(this.modeService.getModeIdByFilenameOrFirstLine(textEditorModel.uri.fsPath), value)
);
ref.dispose();
});
@@ -164,7 +167,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
}).done(undefined, onUnexpectedError);
}
- private addDecoration(range: Range, hoverMessage: MarkedString): void {
+ private addDecoration(range: Range, hoverMessage: MarkdownString): void {
const newDecorations: editorCommon.IModelDeltaDecoration = {
range: range,
diff --git a/src/vs/editor/contrib/hover/browser/modesContentHover.ts b/src/vs/editor/contrib/hover/browser/modesContentHover.ts
index 323f55d5fc6..a8119850568 100644
--- a/src/vs/editor/contrib/hover/browser/modesContentHover.ts
+++ b/src/vs/editor/contrib/hover/browser/modesContentHover.ts
@@ -9,7 +9,7 @@ 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 { renderMarkedString } from 'vs/base/browser/htmlContentRenderer';
+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';
@@ -20,7 +20,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { getHover } from '../common/hover';
import { HoverOperation, IHoverComputer } from './hoverOperation';
import { ContentHoverWidget } from './hoverWidgets';
-import { textToMarkedString, MarkedString } from 'vs/base/common/htmlContent';
+import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent';
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';
@@ -81,8 +81,8 @@ class ModesContentComputer implements IHoverComputer {
return [];
}
- const hasHoverContent = (contents: MarkedString | MarkedString[]) => {
- return contents && (!Array.isArray(contents) || (contents).length > 0);
+ const hasHoverContent = (contents: IMarkdownString | IMarkdownString[]) => {
+ return contents && (!Array.isArray(contents) || (contents).length > 0);
};
const colorDetector = ColorDetector.get(this._editor);
@@ -111,7 +111,7 @@ class ModesContentComputer implements IHoverComputer {
return null;
}
- let contents: MarkedString[];
+ let contents: IMarkdownString[];
if (d.options.hoverMessage) {
if (Array.isArray(d.options.hoverMessage)) {
@@ -155,7 +155,7 @@ class ModesContentComputer implements IHoverComputer {
private _getLoadingMessage(): HoverPart {
return {
range: this._range,
- contents: [textToMarkedString(nls.localize('modesContentHover.loading', "Loading..."))]
+ contents: [new MarkdownString().appendText(nls.localize('modesContentHover.loading', "Loading..."))]
};
}
}
@@ -316,7 +316,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
msg.contents
.filter(contents => !!contents)
.forEach(contents => {
- const renderedContents = renderMarkedString(contents, {
+ const renderedContents = renderMarkdown(contents, {
actionCallback: (content) => {
this._openerService.open(URI.parse(content)).then(void 0, onUnexpectedError);
},
diff --git a/src/vs/editor/contrib/hover/browser/modesGlyphHover.ts b/src/vs/editor/contrib/hover/browser/modesGlyphHover.ts
index 0900532ea51..7fdd61c2b15 100644
--- a/src/vs/editor/contrib/hover/browser/modesGlyphHover.ts
+++ b/src/vs/editor/contrib/hover/browser/modesGlyphHover.ts
@@ -8,17 +8,17 @@ 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 { 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 { MarkedString } from 'vs/base/common/htmlContent';
+import { IMarkdownString } from 'vs/base/common/htmlContent';
export interface IHoverMessage {
- value: MarkedString;
+ value: IMarkdownString;
}
class MarginComputer implements IHoverComputer {
@@ -42,10 +42,10 @@ class MarginComputer implements IHoverComputer {
}
public computeSync(): IHoverMessage[] {
- const hasHoverContent = (contents: MarkedString | MarkedString[]) => {
- return contents && (!Array.isArray(contents) || (contents).length > 0);
+ const hasHoverContent = (contents: IMarkdownString | IMarkdownString[]) => {
+ return contents && (!Array.isArray(contents) || (contents).length > 0);
};
- const toHoverMessage = (contents: MarkedString): IHoverMessage => {
+ const toHoverMessage = (contents: IMarkdownString): IHoverMessage => {
return {
value: contents
};
@@ -168,7 +168,7 @@ export class ModesGlyphHoverWidget extends GlyphHoverWidget {
const fragment = document.createDocumentFragment();
messages.forEach((msg) => {
- const renderedContents = renderMarkedString(msg.value, {
+ const renderedContents = renderMarkdown(msg.value, {
actionCallback: content => this.openerService.open(URI.parse(content)).then(undefined, onUnexpectedError),
codeBlockRenderer: (languageAlias, value): string | TPromise => {
// In markdown, it is possible that we stumble upon language aliases (e.g. js instead of javascript)
@@ -185,4 +185,4 @@ export class ModesGlyphHoverWidget extends GlyphHoverWidget {
this.updateContents(fragment);
this.showAt(lineNumber);
}
-}
\ No newline at end of file
+}
diff --git a/src/vs/editor/contrib/links/browser/links.ts b/src/vs/editor/contrib/links/browser/links.ts
index 53ed5659397..4c6eceb7ff6 100644
--- a/src/vs/editor/contrib/links/browser/links.ts
+++ b/src/vs/editor/contrib/links/browser/links.ts
@@ -26,21 +26,22 @@ import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegist
import { Position } from 'vs/editor/common/core/position';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations';
import { ClickLinkGesture, ClickLinkMouseEvent, ClickLinkKeyboardEvent } from 'vs/editor/contrib/goToDeclaration/browser/clickLinkGesture';
+import { MarkdownString } from 'vs/base/common/htmlContent';
-const HOVER_MESSAGE_GENERAL_META = (
+const HOVER_MESSAGE_GENERAL_META = new MarkdownString().appendText(
platform.isMacintosh
? nls.localize('links.navigate.mac', "Cmd + click to follow link")
: nls.localize('links.navigate', "Ctrl + click to follow link")
);
-const HOVER_MESSAGE_COMMAND_META = (
+const HOVER_MESSAGE_COMMAND_META = new MarkdownString().appendText(
platform.isMacintosh
? nls.localize('links.command.mac', "Cmd + click to execute command")
: nls.localize('links.command', "Ctrl + click to execute command")
);
-const HOVER_MESSAGE_GENERAL_ALT = nls.localize('links.navigate.al', "Alt + click to follow link");
-const HOVER_MESSAGE_COMMAND_ALT = nls.localize('links.command.al', "Alt + click to execute command");
+const HOVER_MESSAGE_GENERAL_ALT = new MarkdownString().appendText(nls.localize('links.navigate.al', "Alt + click to follow link"));
+const HOVER_MESSAGE_COMMAND_ALT = new MarkdownString().appendText(nls.localize('links.command.al', "Alt + click to execute command"));
const decoration = {
meta: ModelDecorationOptions.register({
diff --git a/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.ts b/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.ts
index c1bde40b2d7..3db81fda26c 100644
--- a/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.ts
+++ b/src/vs/editor/contrib/quickFix/browser/lightBulbWidget.ts
@@ -12,13 +12,14 @@ import * as dom from 'vs/base/browser/dom';
import { TrackedRangeStickiness } from 'vs/editor/common/editorCommon';
import { ICodeEditor, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { QuickFixComputeEvent } from './quickFixModel';
+import { IMarkdownString } from 'vs/base/common/htmlContent';
export class LightBulbWidget implements IDisposable {
private readonly _options = {
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
glyphMarginClassName: 'lightbulb-glyph',
- glyphMarginHoverMessage: undefined
+ glyphMarginHoverMessage: undefined
};
private readonly _editor: ICodeEditor;
@@ -90,7 +91,7 @@ export class LightBulbWidget implements IDisposable {
}
get title() {
- return this._options.glyphMarginHoverMessage;
+ return this._options.glyphMarginHoverMessage && this._options.glyphMarginHoverMessage.value;
}
show(e: QuickFixComputeEvent): void {
diff --git a/src/vs/editor/test/common/model/modelDecorations.test.ts b/src/vs/editor/test/common/model/modelDecorations.test.ts
index b148ac36e17..1b2264090f0 100644
--- a/src/vs/editor/test/common/model/modelDecorations.test.ts
+++ b/src/vs/editor/test/common/model/modelDecorations.test.ts
@@ -542,7 +542,7 @@ suite('deltaDecorations', () => {
endColumn: 1
},
options: {
- hoverMessage: 'hello1'
+ hoverMessage: { value: 'hello1' }
}
}]);
@@ -554,13 +554,13 @@ suite('deltaDecorations', () => {
endColumn: 1
},
options: {
- hoverMessage: 'hello2'
+ hoverMessage: { value: 'hello2' }
}
}]);
let actualDecoration = model.getDecorationOptions(ids[0]);
- assert.equal(actualDecoration.hoverMessage, 'hello2');
+ assert.deepEqual(actualDecoration.hoverMessage, { value: 'hello2' });
model.dispose();
});
diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts
index 9b898e112be..3cc8caa67f8 100644
--- a/src/vs/monaco.d.ts
+++ b/src/vs/monaco.d.ts
@@ -353,12 +353,10 @@ declare module monaco {
static readonly WinCtrl: number;
static chord(firstPart: number, secondPart: number): number;
}
- /**
- * MarkedString can be used to render human readable text. It is either a markdown string
- * or a code-block that provides a language and a code snippet. Note that
- * markdown strings will be sanitized - that means html will be escaped.
- */
- export type MarkedString = string;
+ export interface IMarkdownString {
+ value: string;
+ enableCommands?: true;
+ }
export interface IKeyboardEvent {
readonly browserEvent: KeyboardEvent;
@@ -1115,11 +1113,11 @@ declare module monaco.editor {
/**
* Message to be rendered when hovering over the glyph margin decoration.
*/
- glyphMarginHoverMessage?: MarkedString | MarkedString[];
+ glyphMarginHoverMessage?: IMarkdownString | IMarkdownString[];
/**
- * Array of MarkedString to render as the decoration message.
+ * Array of MarkdownString to render as the decoration message.
*/
- hoverMessage?: MarkedString | MarkedString[];
+ hoverMessage?: IMarkdownString | IMarkdownString[];
/**
* Should the decoration expand to encompass a whole line.
*/
@@ -4410,7 +4408,7 @@ declare module monaco.languages {
/**
* The contents of this hover.
*/
- contents: MarkedString[];
+ contents: IMarkdownString[];
/**
* The range to which this hover applies. When missing, the
* editor will use the range at the current position or the
diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts
index f6049cdab17..3e295379597 100644
--- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts
+++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts
@@ -204,7 +204,7 @@ class HoverAdapter {
// we wanna know which extension uses command links
// because that is a potential trick-attack on users
- if (result.contents.some(containsCommandLink)) {
+ if (result.contents.some(h => containsCommandLink(h.value))) {
this._telemetryLog('usesCommandLink', { from: 'hover' });
}
return result;
diff --git a/src/vs/workbench/api/node/extHostTypeConverters.ts b/src/vs/workbench/api/node/extHostTypeConverters.ts
index a0ea9970792..da4e8544b50 100644
--- a/src/vs/workbench/api/node/extHostTypeConverters.ts
+++ b/src/vs/workbench/api/node/extHostTypeConverters.ts
@@ -16,6 +16,7 @@ import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import { IPosition } from 'vs/editor/common/core/position';
import { IRange } from 'vs/editor/common/core/range';
import { ISelection } from 'vs/editor/common/core/selection';
+import { IMarkdownString } from 'vs/base/common/htmlContent';
export interface PositionLike {
line: number;
@@ -144,14 +145,17 @@ function isDecorationOptionsArr(something: vscode.Range[] | vscode.DecorationOpt
}
export namespace MarkedString {
- export function from(markup: vscode.MarkedString): string {
+ export function from(markup: vscode.MarkedString): IMarkdownString {
if (typeof markup === 'string' || !markup) {
- return markup;
+ return { value: markup, enableCommands: true };
} else {
const { language, value } = markup;
- return '```' + language + '\n' + value + '\n```';
+ return { value: '```' + language + '\n' + value + '\n```' };
}
}
+ export function to(value: IMarkdownString): vscode.MarkedString {
+ return value.value;
+ }
}
export function fromRangeOrRangeWithMessage(ranges: vscode.Range[] | vscode.DecorationOptions[]): IDecorationOptions[] {
@@ -272,7 +276,7 @@ export function fromHover(hover: vscode.Hover): modes.Hover {
}
export function toHover(info: modes.Hover): types.Hover {
- return new types.Hover(info.contents, toRange(info.range));
+ return new types.Hover(info.contents.map(MarkedString.to), toRange(info.range));
}
export function toDocumentHighlight(occurrence: modes.DocumentHighlight): types.DocumentHighlight {
diff --git a/src/vs/workbench/parts/debug/browser/debugEditorModelManager.ts b/src/vs/workbench/parts/debug/browser/debugEditorModelManager.ts
index 63db8dbc424..e229724a162 100644
--- a/src/vs/workbench/parts/debug/browser/debugEditorModelManager.ts
+++ b/src/vs/workbench/parts/debug/browser/debugEditorModelManager.ts
@@ -14,6 +14,7 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IDebugService, IBreakpoint, IRawBreakpoint, State } from 'vs/workbench/parts/debug/common/debug';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModelDecorationsChangedEvent } from 'vs/editor/common/model/textModelEvents';
+import { MarkdownString } from 'vs/base/common/htmlContent';
interface IDebugEditorModelData {
model: IModel;
@@ -284,7 +285,7 @@ export class DebugEditorModelManager implements IWorkbenchContribution {
if (result) {
result = objects.clone(result);
if (breakpoint.message) {
- result.glyphMarginHoverMessage = breakpoint.message;
+ result.glyphMarginHoverMessage = new MarkdownString().appendText(breakpoint.message);
}
if (breakpoint.column) {
result.beforeContentClassName = `debug-breakpoint-column ${result.glyphMarginClassName}-column`;
@@ -305,7 +306,7 @@ export class DebugEditorModelManager implements IWorkbenchContribution {
} else {
condition = breakpoint.condition ? breakpoint.condition : breakpoint.hitCondition;
}
- const glyphMarginHoverMessage = `\`\`\`${modeId}\n${condition}\`\`\``;
+ const glyphMarginHoverMessage = new MarkdownString().appendCodeblock(modeId, condition);
const glyphMarginClassName = 'debug-breakpoint-conditional-glyph';
const beforeContentClassName = breakpoint.column ? `debug-breakpoint-column ${glyphMarginClassName}-column` : undefined;
@@ -326,25 +327,25 @@ export class DebugEditorModelManager implements IWorkbenchContribution {
private static BREAKPOINT_DISABLED_DECORATION: IModelDecorationOptions = {
glyphMarginClassName: 'debug-breakpoint-disabled-glyph',
- glyphMarginHoverMessage: nls.localize('breakpointDisabledHover', "Disabled Breakpoint"),
+ glyphMarginHoverMessage: new MarkdownString().appendText(nls.localize('breakpointDisabledHover', "Disabled Breakpoint")),
stickiness
};
private static BREAKPOINT_UNVERIFIED_DECORATION: IModelDecorationOptions = {
glyphMarginClassName: 'debug-breakpoint-unverified-glyph',
- glyphMarginHoverMessage: nls.localize('breakpointUnverifieddHover', "Unverified Breakpoint"),
+ glyphMarginHoverMessage: new MarkdownString().appendText(nls.localize('breakpointUnverifieddHover', "Unverified Breakpoint")),
stickiness
};
private static BREAKPOINT_DIRTY_DECORATION: IModelDecorationOptions = {
glyphMarginClassName: 'debug-breakpoint-unverified-glyph',
- glyphMarginHoverMessage: nls.localize('breakpointDirtydHover', "Unverified breakpoint. File is modified, please restart debug session."),
+ glyphMarginHoverMessage: new MarkdownString().appendText(nls.localize('breakpointDirtydHover', "Unverified breakpoint. File is modified, please restart debug session.")),
stickiness
};
private static BREAKPOINT_UNSUPPORTED_DECORATION: IModelDecorationOptions = {
glyphMarginClassName: 'debug-breakpoint-unsupported-glyph',
- glyphMarginHoverMessage: nls.localize('breakpointUnsupported', "Conditional breakpoints not supported by this debug type"),
+ glyphMarginHoverMessage: new MarkdownString().appendText(nls.localize('breakpointUnsupported', "Conditional breakpoints not supported by this debug type")),
stickiness
};
diff --git a/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.ts b/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.ts
index 8f1daf2c030..6bc0e824977 100644
--- a/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.ts
+++ b/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.ts
@@ -7,7 +7,7 @@
import * as nls from 'vs/nls';
import { RunOnceScheduler } from 'vs/base/common/async';
-import { MarkedString } from 'vs/base/common/htmlContent';
+import { MarkdownString } from 'vs/base/common/htmlContent';
import { KeyCode, KeyMod, KeyChord, SimpleKeybinding } from 'vs/base/common/keyCodes';
import { Disposable } from 'vs/base/common/lifecycle';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
@@ -289,21 +289,21 @@ export class KeybindingEditorDecorationsRenderer extends Disposable {
}
private _createDecoration(isError: boolean, uiLabel: string, usLabel: string, model: editorCommon.IModel, keyNode: Node): editorCommon.IModelDeltaDecoration {
- let msg: MarkedString[];
+ let msg: MarkdownString;
let className: string;
let beforeContentClassName: string;
let overviewRulerColor: string;
if (isError) {
// this is the error case
- msg = [NLS_KB_LAYOUT_ERROR_MESSAGE];
+ msg = new MarkdownString().appendText(NLS_KB_LAYOUT_ERROR_MESSAGE);
className = 'keybindingError';
beforeContentClassName = 'inlineKeybindingError';
overviewRulerColor = 'rgba(250, 100, 100, 0.6)';
} else {
// this is the info case
if (usLabel && uiLabel !== usLabel) {
- msg = [
+ msg = new MarkdownString(
nls.localize({
key: 'defineKeybinding.kbLayoutLocalAndUSMessage',
comment: [
@@ -311,9 +311,9 @@ export class KeybindingEditorDecorationsRenderer extends Disposable {
'The placeholders will contain a keyboard combination e.g. Ctrl+Shift+/'
]
}, "**{0}** for your current keyboard layout (**{1}** for US standard).", uiLabel, usLabel)
- ];
+ );
} else {
- msg = [
+ msg = new MarkdownString(
nls.localize({
key: 'defineKeybinding.kbLayoutLocalMessage',
comment: [
@@ -321,7 +321,7 @@ export class KeybindingEditorDecorationsRenderer extends Disposable {
'The placeholder will contain a keyboard combination e.g. Ctrl+Shift+/'
]
}, "**{0}** for your current keyboard layout.", uiLabel)
- ];
+ );
}
className = 'keybindingInfo';
beforeContentClassName = 'inlineKeybindingInfo';
diff --git a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts
index 085a79ddb96..4dc8cb2614b 100644
--- a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts
+++ b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts
@@ -32,6 +32,7 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi
import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
+import { MarkdownString } from 'vs/base/common/htmlContent';
export interface IPreferencesRenderer extends IDisposable {
preferencesModel: IPreferencesEditorModel;
@@ -1022,7 +1023,7 @@ class UnsupportedWorkbenchSettingsRenderer extends Disposable {
stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
inlineClassName: 'dim-configuration',
beforeContentClassName: 'unsupportedWorkbenhSettingInfo',
- hoverMessage: nls.localize('unsupportedWorkbenchSetting', "This setting cannot be applied now. It will be applied when you open this folder directly.")
+ hoverMessage: new MarkdownString().appendText(nls.localize('unsupportedWorkbenchSetting', "This setting cannot be applied now. It will be applied when you open this folder directly."))
});
private createDecoration(range: IRange, model: editorCommon.IModel): editorCommon.IModelDeltaDecoration {
@@ -1097,4 +1098,4 @@ class WorkspaceConfigurationRenderer extends Disposable {
}
super.dispose();
}
-}
\ No newline at end of file
+}
diff --git a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts
index cd2ce8fa139..74c8fb9be87 100644
--- a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts
+++ b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts
@@ -34,6 +34,7 @@ import { Color } from 'vs/base/common/color';
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
+import { MarkdownString } from 'vs/base/common/htmlContent';
export class SettingsHeaderWidget extends Widget implements IViewZone {
@@ -621,7 +622,7 @@ export class EditPreferenceWidget extends Disposable {
newDecoration.push({
options: {
glyphMarginClassName: EditPreferenceWidget.GLYPH_MARGIN_CLASS_NAME,
- glyphMarginHoverMessage: hoverMessage,
+ glyphMarginHoverMessage: new MarkdownString().appendText(hoverMessage),
stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
},
range: {
@@ -646,4 +647,4 @@ export class EditPreferenceWidget extends Disposable {
this.hide();
super.dispose();
}
-}
\ No newline at end of file
+}
diff --git a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts
index 03afe2edff7..eda19b59181 100644
--- a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts
+++ b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts
@@ -446,8 +446,8 @@ suite('ExtHostLanguageFeatures', function () {
return getHover(model, new EditorPosition(1, 1)).then(value => {
assert.equal(value.length, 2);
let [first, second] = value as Hover[];
- assert.equal(first.contents[0], 'registered second');
- assert.equal(second.contents[0], 'registered first');
+ assert.equal(first.contents[0].value, 'registered second');
+ assert.equal(second.contents[0].value, 'registered first');
});
});
});