diff --git a/src/vs/base/browser/htmlContentRenderer.ts b/src/vs/base/browser/htmlContentRenderer.ts
index cf346270dca..26ebffbc1b7 100644
--- a/src/vs/base/browser/htmlContentRenderer.ts
+++ b/src/vs/base/browser/htmlContentRenderer.ts
@@ -11,6 +11,7 @@ import * as marked from 'vs/base/common/marked/marked';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { IDisposable } from 'vs/base/common/lifecycle';
import { onUnexpectedError } from 'vs/base/common/errors';
+import { URI } from 'vs/base/common/uri';
export interface IContentActionHandler {
callback: (content: string, event?: IMouseEvent) => void;
@@ -52,6 +53,14 @@ export function renderFormattedText(formattedText: string, options: RenderOption
export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions = {}): HTMLElement {
const element = createElement(options);
+ const _href = function (href: string): string {
+ const data = markdown.uris && markdown.uris[href];
+ if (data) {
+ href = URI.revive(data).toString(true);
+ }
+ return href;
+ };
+
// signal to code-block render that the
// element has been created
let signalInnerHTML: Function;
@@ -59,6 +68,7 @@ export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions
const renderer = new marked.Renderer();
renderer.image = (href: string, title: string, text: string) => {
+ href = _href(href);
let dimensions: string[] = [];
if (href) {
const splitted = href.split('|').map(s => s.trim());
@@ -99,6 +109,7 @@ export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions
if (href === text) { // raw link case
text = removeMarkdownEscapes(text);
}
+ href = _href(href);
title = removeMarkdownEscapes(title);
href = removeMarkdownEscapes(href);
if (
diff --git a/src/vs/base/common/htmlContent.ts b/src/vs/base/common/htmlContent.ts
index f470327b88b..0ec7be4955a 100644
--- a/src/vs/base/common/htmlContent.ts
+++ b/src/vs/base/common/htmlContent.ts
@@ -4,10 +4,12 @@
*--------------------------------------------------------------------------------------------*/
import { equals } from 'vs/base/common/arrays';
+import { UriComponents } from 'vs/base/common/uri';
export interface IMarkdownString {
value: string;
isTrusted?: boolean;
+ uris?: { [href: string]: UriComponents };
}
export class MarkdownString implements IMarkdownString {
diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts
index 1a6f3755419..429c70fbabb 100644
--- a/src/vs/monaco.d.ts
+++ b/src/vs/monaco.d.ts
@@ -392,9 +392,13 @@ declare namespace monaco {
static readonly WinCtrl: number;
static chord(firstPart: number, secondPart: number): number;
}
+
export interface IMarkdownString {
value: string;
isTrusted?: boolean;
+ uris?: {
+ [href: string]: UriComponents;
+ };
}
export interface IKeyboardEvent {
diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts
index d655abe4e72..8d3b6988fb1 100644
--- a/src/vs/workbench/api/node/extHost.protocol.ts
+++ b/src/vs/workbench/api/node/extHost.protocol.ts
@@ -274,12 +274,6 @@ export interface ISerializedSignatureHelpProviderMetadata {
readonly retriggerCharacters: ReadonlyArray;
}
-export interface IMarkdownStringDto {
- isTrusted: boolean;
- value: string;
- uris: { [n: string]: UriComponents };
-}
-
export interface MainThreadLanguageFeaturesShape extends IDisposable {
$unregister(handle: number): void;
$registerOutlineSupport(handle: number, selector: ISerializedDocumentFilter[], extensionId: string): void;
diff --git a/src/vs/workbench/api/node/extHostTypeConverters.ts b/src/vs/workbench/api/node/extHostTypeConverters.ts
index 7db09363924..0bbe5c7e66b 100644
--- a/src/vs/workbench/api/node/extHostTypeConverters.ts
+++ b/src/vs/workbench/api/node/extHostTypeConverters.ts
@@ -19,7 +19,7 @@ import { IRange } from 'vs/editor/common/core/range';
import { ISelection } from 'vs/editor/common/core/selection';
import * as htmlContent from 'vs/base/common/htmlContent';
import * as languageSelector from 'vs/editor/common/modes/languageSelector';
-import { WorkspaceEditDto, ResourceTextEditDto, ResourceFileEditDto, IMarkdownStringDto } from 'vs/workbench/api/node/extHost.protocol';
+import { WorkspaceEditDto, ResourceTextEditDto, ResourceFileEditDto } from 'vs/workbench/api/node/extHost.protocol';
import { MarkerSeverity, IRelatedInformation, IMarkerData, MarkerTag } from 'vs/platform/markers/common/markers';
import { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
@@ -210,38 +210,32 @@ export namespace MarkdownString {
}
export function from(markup: vscode.MarkdownString | vscode.MarkedString): htmlContent.IMarkdownString {
+ let res: htmlContent.IMarkdownString;
if (isCodeblock(markup)) {
const { language, value } = markup;
- return { value: '```' + language + '\n' + value + '\n```\n' };
+ res = { value: '```' + language + '\n' + value + '\n```\n' };
} else if (htmlContent.isMarkdownString(markup)) {
- return markup;
+ res = markup;
} else if (typeof markup === 'string') {
- return { value: markup };
+ res = { value: markup };
} else {
- return { value: '' };
+ res = { value: '' };
}
- }
- export function from2(markup: vscode.MarkedString | vscode.MarkdownString): IMarkdownStringDto {
- let { value, isTrusted } = from(markup);
-
- let uris = Object.create(null);
+ // extract uris into a separate object
+ res.uris = Object.create(null);
let renderer = new marked.Renderer();
-
renderer.image = renderer.link = (href: string): string => {
try {
- uris[href] = URI.parse(href, true);
+ res.uris[href] = URI.parse(href, true);
} catch (e) {
// ignore
}
return '';
};
- marked(value, { renderer });
- return {
- isTrusted,
- value,
- uris
- };
+ marked(res.value, { renderer });
+
+ return res;
}
export function to(value: htmlContent.IMarkdownString): vscode.MarkdownString {
diff --git a/src/vs/workbench/test/electron-browser/api/extHostTypeConverter.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTypeConverter.test.ts
new file mode 100644
index 00000000000..3b217231a5f
--- /dev/null
+++ b/src/vs/workbench/test/electron-browser/api/extHostTypeConverter.test.ts
@@ -0,0 +1,59 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+
+import * as assert from 'assert';
+import { MarkdownString } from 'vs/workbench/api/node/extHostTypeConverters';
+import { isEmptyObject } from 'vs/base/common/types';
+import { size } from 'vs/base/common/collections';
+
+suite('ExtHostTypeConverter', function () {
+
+ test('MarkdownConvert - uris', function () {
+
+ let data = MarkdownString.from('Hello');
+ assert.equal(isEmptyObject(data.uris), true);
+ assert.equal(data.value, 'Hello');
+
+ data = MarkdownString.from('Hello [link](foo)');
+ assert.equal(data.value, 'Hello [link](foo)');
+ assert.equal(isEmptyObject(data.uris), true); // no scheme, no uri
+
+ data = MarkdownString.from('Hello [link](www.noscheme.bad)');
+ assert.equal(data.value, 'Hello [link](www.noscheme.bad)');
+ assert.equal(isEmptyObject(data.uris), true); // no scheme, no uri
+
+ data = MarkdownString.from('Hello [link](foo:path)');
+ assert.equal(data.value, 'Hello [link](foo:path)');
+ assert.equal(size(data.uris), 1);
+ assert.ok(!!data.uris['foo:path']);
+
+ data = MarkdownString.from('hello@foo.bar');
+ assert.equal(data.value, 'hello@foo.bar');
+ assert.equal(size(data.uris), 1);
+ assert.ok(!!data.uris['mailto:hello@foo.bar']);
+
+ data = MarkdownString.from('*hello* [click](command:me)');
+ assert.equal(data.value, '*hello* [click](command:me)');
+ assert.equal(size(data.uris), 1);
+ assert.ok(!!data.uris['command:me']);
+
+ data = MarkdownString.from('*hello* [click](file:///somepath/here). [click](file:///somepath/here)');
+ assert.equal(data.value, '*hello* [click](file:///somepath/here). [click](file:///somepath/here)');
+ assert.equal(size(data.uris), 1);
+ assert.ok(!!data.uris['file:///somepath/here']);
+
+ data = MarkdownString.from('*hello* [click](file:///somepath/here). [click](file:///somepath/here)');
+ assert.equal(data.value, '*hello* [click](file:///somepath/here). [click](file:///somepath/here)');
+ assert.equal(size(data.uris), 1);
+ assert.ok(!!data.uris['file:///somepath/here']);
+
+ data = MarkdownString.from('*hello* [click](file:///somepath/here). [click](file:///somepath/here2)');
+ assert.equal(data.value, '*hello* [click](file:///somepath/here). [click](file:///somepath/here2)');
+ assert.equal(size(data.uris), 2);
+ assert.ok(!!data.uris['file:///somepath/here']);
+ assert.ok(!!data.uris['file:///somepath/here2']);
+ });
+});