diff --git a/src/vs/base/common/htmlContent.ts b/src/vs/base/common/htmlContent.ts
index faf018947a0..97411ab5f58 100644
--- a/src/vs/base/common/htmlContent.ts
+++ b/src/vs/base/common/htmlContent.ts
@@ -6,6 +6,7 @@
import { illegalArgument } from 'vs/base/common/errors';
import { escapeIcons } from 'vs/base/common/iconLabels';
import { isEqual } from 'vs/base/common/resources';
+import { escapeRegExpCharacters } from 'vs/base/common/strings';
import { URI, UriComponents } from 'vs/base/common/uri';
export interface IMarkdownString {
@@ -73,6 +74,29 @@ export class MarkdownString implements IMarkdownString {
this.value += '\n```\n';
return this;
}
+
+ appendLink(target: URI | URL | string, label: string, title?: string): MarkdownString {
+ this.value += '[';
+ this.value += this._escape(label, ']');
+ this.value += '](';
+ this.value += this._escape(String(target), ')');
+ if (title) {
+ this.value += ` "${this._escape(this._escape(title, '"'), ')')}"`;
+ }
+ this.value += ')';
+ return this;
+ }
+
+ private _escape(value: string, ch: string): string {
+ const r = new RegExp(escapeRegExpCharacters(ch), 'g');
+ return value.replace(r, (match, offset) => {
+ if (value.charAt(offset - 1) !== '\\') {
+ return `\\${match}`;
+ } else {
+ return match;
+ }
+ });
+ }
}
export function isEmptyMarkdownString(oneOrMany: IMarkdownString | IMarkdownString[] | null | undefined): boolean {
@@ -112,7 +136,7 @@ export function markdownStringEqual(a: IMarkdownString, b: IMarkdownString): boo
export function escapeMarkdownSyntaxTokens(text: string): string {
// escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash
- return text.replace(/[\\`*_{}[\]()#+\-!]/g, '\\$&');
+ return text.replace(/[\\`* _{ } [\]()# +\-!]/g, '\\$&');
}
export function removeMarkdownEscapes(text: string): string {
diff --git a/src/vs/base/test/common/markdownString.test.ts b/src/vs/base/test/common/markdownString.test.ts
index 7d9d64f2b69..b008091a46f 100644
--- a/src/vs/base/test/common/markdownString.test.ts
+++ b/src/vs/base/test/common/markdownString.test.ts
@@ -28,6 +28,40 @@ suite('MarkdownString', () => {
assert.strictEqual(mds.value, '\\# foo\n\n\\*bar\\*');
});
+ test('appendLink', function () {
+
+ function assertLink(target: string, label: string, title: string | undefined, expected: string) {
+ const mds = new MarkdownString();
+ mds.appendLink(target, label, title);
+ assert.strictEqual(mds.value, expected);
+ }
+
+ assertLink(
+ 'https://example.com\\()', 'hello', undefined,
+ '[hello](https://example.com\\(\\))'
+ );
+ assertLink(
+ 'https://example.com', 'hello', 'title',
+ '[hello](https://example.com "title")'
+ );
+ assertLink(
+ 'foo)', 'hello]', undefined,
+ '[hello\\]](foo\\))'
+ );
+ assertLink(
+ 'foo\\)', 'hello]', undefined,
+ '[hello\\]](foo\\))'
+ );
+ assertLink(
+ 'fo)o', 'hell]o', undefined,
+ '[hell\\]o](fo\\)o)'
+ );
+ assertLink(
+ 'foo)', 'hello]', 'title"',
+ '[hello\\]](foo\\) "title\\"")'
+ );
+ });
+
suite('ThemeIcons', () => {
suite('Support On', () => {