Skip to content

Commit d4116a8

Browse files
committed
tweak markdown string conversion and rendering
1 parent 0b99867 commit d4116a8

6 files changed

Lines changed: 88 additions & 24 deletions

File tree

src/vs/base/browser/htmlContentRenderer.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import * as marked from 'vs/base/common/marked/marked';
1111
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
1212
import { IDisposable } from 'vs/base/common/lifecycle';
1313
import { onUnexpectedError } from 'vs/base/common/errors';
14+
import { URI } from 'vs/base/common/uri';
1415

1516
export interface IContentActionHandler {
1617
callback: (content: string, event?: IMouseEvent) => void;
@@ -52,13 +53,22 @@ export function renderFormattedText(formattedText: string, options: RenderOption
5253
export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions = {}): HTMLElement {
5354
const element = createElement(options);
5455

56+
const _href = function (href: string): string {
57+
const data = markdown.uris && markdown.uris[href];
58+
if (data) {
59+
href = URI.revive(data).toString(true);
60+
}
61+
return href;
62+
};
63+
5564
// signal to code-block render that the
5665
// element has been created
5766
let signalInnerHTML: Function;
5867
const withInnerHTML = new Promise(c => signalInnerHTML = c);
5968

6069
const renderer = new marked.Renderer();
6170
renderer.image = (href: string, title: string, text: string) => {
71+
href = _href(href);
6272
let dimensions: string[] = [];
6373
if (href) {
6474
const splitted = href.split('|').map(s => s.trim());
@@ -99,6 +109,7 @@ export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions
99109
if (href === text) { // raw link case
100110
text = removeMarkdownEscapes(text);
101111
}
112+
href = _href(href);
102113
title = removeMarkdownEscapes(title);
103114
href = removeMarkdownEscapes(href);
104115
if (

src/vs/base/common/htmlContent.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { equals } from 'vs/base/common/arrays';
7+
import { UriComponents } from 'vs/base/common/uri';
78

89
export interface IMarkdownString {
910
value: string;
1011
isTrusted?: boolean;
12+
uris?: { [href: string]: UriComponents };
1113
}
1214

1315
export class MarkdownString implements IMarkdownString {

src/vs/monaco.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,9 +392,13 @@ declare namespace monaco {
392392
static readonly WinCtrl: number;
393393
static chord(firstPart: number, secondPart: number): number;
394394
}
395+
395396
export interface IMarkdownString {
396397
value: string;
397398
isTrusted?: boolean;
399+
uris?: {
400+
[href: string]: UriComponents;
401+
};
398402
}
399403

400404
export interface IKeyboardEvent {

src/vs/workbench/api/node/extHost.protocol.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -274,12 +274,6 @@ export interface ISerializedSignatureHelpProviderMetadata {
274274
readonly retriggerCharacters: ReadonlyArray<string>;
275275
}
276276

277-
export interface IMarkdownStringDto {
278-
isTrusted: boolean;
279-
value: string;
280-
uris: { [n: string]: UriComponents };
281-
}
282-
283277
export interface MainThreadLanguageFeaturesShape extends IDisposable {
284278
$unregister(handle: number): void;
285279
$registerOutlineSupport(handle: number, selector: ISerializedDocumentFilter[], extensionId: string): void;

src/vs/workbench/api/node/extHostTypeConverters.ts

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { IRange } from 'vs/editor/common/core/range';
1919
import { ISelection } from 'vs/editor/common/core/selection';
2020
import * as htmlContent from 'vs/base/common/htmlContent';
2121
import * as languageSelector from 'vs/editor/common/modes/languageSelector';
22-
import { WorkspaceEditDto, ResourceTextEditDto, ResourceFileEditDto, IMarkdownStringDto } from 'vs/workbench/api/node/extHost.protocol';
22+
import { WorkspaceEditDto, ResourceTextEditDto, ResourceFileEditDto } from 'vs/workbench/api/node/extHost.protocol';
2323
import { MarkerSeverity, IRelatedInformation, IMarkerData, MarkerTag } from 'vs/platform/markers/common/markers';
2424
import { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
2525
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
@@ -210,38 +210,32 @@ export namespace MarkdownString {
210210
}
211211

212212
export function from(markup: vscode.MarkdownString | vscode.MarkedString): htmlContent.IMarkdownString {
213+
let res: htmlContent.IMarkdownString;
213214
if (isCodeblock(markup)) {
214215
const { language, value } = markup;
215-
return { value: '```' + language + '\n' + value + '\n```\n' };
216+
res = { value: '```' + language + '\n' + value + '\n```\n' };
216217
} else if (htmlContent.isMarkdownString(markup)) {
217-
return markup;
218+
res = markup;
218219
} else if (typeof markup === 'string') {
219-
return { value: <string>markup };
220+
res = { value: <string>markup };
220221
} else {
221-
return { value: '' };
222+
res = { value: '' };
222223
}
223-
}
224-
225-
export function from2(markup: vscode.MarkedString | vscode.MarkdownString): IMarkdownStringDto {
226-
let { value, isTrusted } = from(markup);
227224

228-
let uris = Object.create(null);
225+
// extract uris into a separate object
226+
res.uris = Object.create(null);
229227
let renderer = new marked.Renderer();
230-
231228
renderer.image = renderer.link = (href: string): string => {
232229
try {
233-
uris[href] = URI.parse(href, true);
230+
res.uris[href] = URI.parse(href, true);
234231
} catch (e) {
235232
// ignore
236233
}
237234
return '';
238235
};
239-
marked(value, { renderer });
240-
return {
241-
isTrusted,
242-
value,
243-
uris
244-
};
236+
marked(res.value, { renderer });
237+
238+
return res;
245239
}
246240

247241
export function to(value: htmlContent.IMarkdownString): vscode.MarkdownString {
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
7+
import * as assert from 'assert';
8+
import { MarkdownString } from 'vs/workbench/api/node/extHostTypeConverters';
9+
import { isEmptyObject } from 'vs/base/common/types';
10+
import { size } from 'vs/base/common/collections';
11+
12+
suite('ExtHostTypeConverter', function () {
13+
14+
test('MarkdownConvert - uris', function () {
15+
16+
let data = MarkdownString.from('Hello');
17+
assert.equal(isEmptyObject(data.uris), true);
18+
assert.equal(data.value, 'Hello');
19+
20+
data = MarkdownString.from('Hello [link](foo)');
21+
assert.equal(data.value, 'Hello [link](foo)');
22+
assert.equal(isEmptyObject(data.uris), true); // no scheme, no uri
23+
24+
data = MarkdownString.from('Hello [link](www.noscheme.bad)');
25+
assert.equal(data.value, 'Hello [link](www.noscheme.bad)');
26+
assert.equal(isEmptyObject(data.uris), true); // no scheme, no uri
27+
28+
data = MarkdownString.from('Hello [link](foo:path)');
29+
assert.equal(data.value, 'Hello [link](foo:path)');
30+
assert.equal(size(data.uris), 1);
31+
assert.ok(!!data.uris['foo:path']);
32+
33+
data = MarkdownString.from('hello@foo.bar');
34+
assert.equal(data.value, 'hello@foo.bar');
35+
assert.equal(size(data.uris), 1);
36+
assert.ok(!!data.uris['mailto:hello@foo.bar']);
37+
38+
data = MarkdownString.from('*hello* [click](command:me)');
39+
assert.equal(data.value, '*hello* [click](command:me)');
40+
assert.equal(size(data.uris), 1);
41+
assert.ok(!!data.uris['command:me']);
42+
43+
data = MarkdownString.from('*hello* [click](file:///somepath/here). [click](file:///somepath/here)');
44+
assert.equal(data.value, '*hello* [click](file:///somepath/here). [click](file:///somepath/here)');
45+
assert.equal(size(data.uris), 1);
46+
assert.ok(!!data.uris['file:///somepath/here']);
47+
48+
data = MarkdownString.from('*hello* [click](file:///somepath/here). [click](file:///somepath/here)');
49+
assert.equal(data.value, '*hello* [click](file:///somepath/here). [click](file:///somepath/here)');
50+
assert.equal(size(data.uris), 1);
51+
assert.ok(!!data.uris['file:///somepath/here']);
52+
53+
data = MarkdownString.from('*hello* [click](file:///somepath/here). [click](file:///somepath/here2)');
54+
assert.equal(data.value, '*hello* [click](file:///somepath/here). [click](file:///somepath/here2)');
55+
assert.equal(size(data.uris), 2);
56+
assert.ok(!!data.uris['file:///somepath/here']);
57+
assert.ok(!!data.uris['file:///somepath/here2']);
58+
});
59+
});

0 commit comments

Comments
 (0)