diff --git a/src/vs/base/common/htmlContent.ts b/src/vs/base/common/htmlContent.ts index 9e8a13a3a39375ed779e8a922ecd9fa8d13bef0a..663274bc6927aac0a2d6aa2bf0c9adba9a69ca0a 100644 --- a/src/vs/base/common/htmlContent.ts +++ b/src/vs/base/common/htmlContent.ts @@ -16,52 +16,52 @@ export interface IMarkdownString { } export class MarkdownString implements IMarkdownString { - private readonly _isTrusted: boolean; - private readonly _supportThemeIcons: boolean; + + public value: string; + public isTrusted?: boolean; + public supportThemeIcons?: boolean; constructor( - private _value: string = '', + value: string = '', isTrustedOrOptions: boolean | { isTrusted?: boolean, supportThemeIcons?: boolean } = false, ) { - if (typeof this._value !== 'string') { + this.value = value; + if (typeof this.value !== 'string') { throw illegalArgument('value'); } if (typeof isTrustedOrOptions === 'boolean') { - this._isTrusted = isTrustedOrOptions; - this._supportThemeIcons = false; + this.isTrusted = isTrustedOrOptions; + this.supportThemeIcons = false; } else { - this._isTrusted = isTrustedOrOptions.isTrusted ?? false; - this._supportThemeIcons = isTrustedOrOptions.supportThemeIcons ?? false; + this.isTrusted = isTrustedOrOptions.isTrusted ?? false; + this.supportThemeIcons = isTrustedOrOptions.supportThemeIcons ?? false; } } - get value() { return this._value; } - get isTrusted() { return this._isTrusted; } - get supportThemeIcons() { return this._supportThemeIcons; } - appendText(value: string): MarkdownString { // escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash - this._value += (this._supportThemeIcons ? escapeCodicons(value) : value) + this.value += (this.supportThemeIcons ? escapeCodicons(value) : value) .replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&') + .replace(/^([ \t]+)(.+)$/gm, (_match, g1, g2) => ' '.repeat(g1.length) + g2) + .replace(/^>/gm, '\\>') .replace(/\n/g, '\n\n'); return this; } appendMarkdown(value: string): MarkdownString { - this._value += value; - + this.value += value; return this; } appendCodeblock(langId: string, code: string): MarkdownString { - this._value += '\n```'; - this._value += langId; - this._value += '\n'; - this._value += code; - this._value += '\n```\n'; + this.value += '\n```'; + this.value += langId; + this.value += '\n'; + this.value += code; + this.value += '\n```\n'; return this; } } diff --git a/src/vs/base/test/common/markdownString.test.ts b/src/vs/base/test/common/markdownString.test.ts index f33f741f624ad6e118f90666541746d032424ff5..a76c30f2cc57c2fc4aca485760943fb57da03851 100644 --- a/src/vs/base/test/common/markdownString.test.ts +++ b/src/vs/base/test/common/markdownString.test.ts @@ -8,6 +8,18 @@ import { MarkdownString } from 'vs/base/common/htmlContent'; suite('MarkdownString', () => { + test('Escape leading whitespace', function () { + const mds = new MarkdownString(); + mds.appendText('Hello\n Not a code block'); + assert.equal(mds.value, 'Hello\n\n    Not a code block'); + }); + + test('MarkdownString.appendText doesn\'t escape quote #109040', function () { + const mds = new MarkdownString(); + mds.appendText('> Text\n>More'); + assert.equal(mds.value, '\\> Text\n\n\\>More'); + }); + test('appendText', () => { const mds = new MarkdownString(); diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 6eb858eee8415a6fb521bf3e8c0ad514e2c4834e..158eb96407d36bf7a9bc44112b33396203fbeac1 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -4,10 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { coalesceInPlace, equals } from 'vs/base/common/arrays'; -import { escapeCodicons } from 'vs/base/common/codicons'; import { illegalArgument } from 'vs/base/common/errors'; import { IRelativePattern } from 'vs/base/common/glob'; -import { isMarkdownString } from 'vs/base/common/htmlContent'; +import { isMarkdownString, MarkdownString as BaseMarkdownString } from 'vs/base/common/htmlContent'; import { ResourceMap } from 'vs/base/common/map'; import { isStringArray } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; @@ -1290,40 +1289,7 @@ export class CodeInset { @es5ClassCompat -export class MarkdownString { - - value: string; - isTrusted?: boolean; - readonly supportThemeIcons?: boolean; - - constructor(value?: string, supportThemeIcons: boolean = false) { - this.value = value ?? ''; - this.supportThemeIcons = supportThemeIcons; - } - - appendText(value: string): MarkdownString { - // escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash - this.value += (this.supportThemeIcons ? escapeCodicons(value) : value) - .replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&') - .replace(/\n/, '\n\n'); - - return this; - } - - appendMarkdown(value: string): MarkdownString { - this.value += value; - - return this; - } - - appendCodeblock(code: string, language: string = ''): MarkdownString { - this.value += '\n```'; - this.value += language; - this.value += '\n'; - this.value += code; - this.value += '\n```\n'; - return this; - } +export class MarkdownString extends BaseMarkdownString implements vscode.MarkdownString { static isMarkdownString(thing: any): thing is vscode.MarkdownString { if (thing instanceof MarkdownString) { @@ -1331,6 +1297,11 @@ export class MarkdownString { } return thing && thing.appendCodeblock && thing.appendMarkdown && thing.appendText && (thing.value !== undefined); } + + constructor(value?: string, supportThemeIcons: boolean = false) { + super(value ?? '', { supportThemeIcons }); + } + } @es5ClassCompat