diff --git a/extensions/typescript-language-features/src/languageFeatures/util/textRendering.ts b/extensions/typescript-language-features/src/languageFeatures/util/textRendering.ts index bc4b7ad1b5f..2918a7de54c 100644 --- a/extensions/typescript-language-features/src/languageFeatures/util/textRendering.ts +++ b/extensions/typescript-language-features/src/languageFeatures/util/textRendering.ts @@ -49,9 +49,13 @@ function getTagBodyText( return '```\n' + text + '\n```'; } - const text = convertLinkTags(tag.text, filePathConverter); + let text = convertLinkTags(tag.text, filePathConverter); switch (tag.name) { case 'example': { + // Example text does not support `{@link}` as it is considered code. + // TODO: should we support it if it appears outside of an explicit code block? + text = asPlainText(tag.text); + // check for caption tags, fix for #79704 const captionTagMatches = text.match(/(.*?)<\/caption>\s*(\r\n|\n)/); if (captionTagMatches && captionTagMatches.index === 0) { @@ -132,6 +136,13 @@ function getTagBody(tag: Proto.JSDocTagInfo, filePathConverter: IFilePathToResou return (convertLinkTags(tag.text, filePathConverter)).split(/^(\S+)\s*-?\s*/); } +function asPlainText(parts: readonly Proto.SymbolDisplayPart[] | string): string { + if (typeof parts === 'string') { + return parts; + } + return parts.map(part => part.text).join(''); +} + export function asPlainTextWithLinks( parts: readonly Proto.SymbolDisplayPart[] | string, filePathConverter: IFilePathToResourceConverter, diff --git a/extensions/typescript-language-features/src/test/unit/textRendering.test.ts b/extensions/typescript-language-features/src/test/unit/textRendering.test.ts index 2354bb7f589..b13f682f715 100644 --- a/extensions/typescript-language-features/src/test/unit/textRendering.test.ts +++ b/extensions/typescript-language-features/src/test/unit/textRendering.test.ts @@ -14,7 +14,7 @@ const noopToResource: IFilePathToResourceConverter = { }; suite('typescript.previewer', () => { - test('Should ignore hyphens after a param tag', async () => { + test('Should ignore hyphens after a param tag', () => { assert.strictEqual( tagsToMarkdown([ { @@ -25,7 +25,7 @@ suite('typescript.previewer', () => { '*@param* `a` — b'); }); - test('Should parse url jsdoc @link', async () => { + test('Should parse url jsdoc @link', () => { assert.strictEqual( documentationToMarkdown( 'x {@link http://www.example.com/foo} y {@link https://api.jquery.com/bind/#bind-eventType-eventData-handler} z', @@ -35,7 +35,7 @@ suite('typescript.previewer', () => { 'x [http://www.example.com/foo](http://www.example.com/foo) y [https://api.jquery.com/bind/#bind-eventType-eventData-handler](https://api.jquery.com/bind/#bind-eventType-eventData-handler) z'); }); - test('Should parse url jsdoc @link with text', async () => { + test('Should parse url jsdoc @link with text', () => { assert.strictEqual( documentationToMarkdown( 'x {@link http://www.example.com/foo abc xyz} y {@link http://www.example.com/bar|b a z} z', @@ -45,7 +45,7 @@ suite('typescript.previewer', () => { 'x [abc xyz](http://www.example.com/foo) y [b a z](http://www.example.com/bar) z'); }); - test('Should treat @linkcode jsdocs links as monospace', async () => { + test('Should treat @linkcode jsdocs links as monospace', () => { assert.strictEqual( documentationToMarkdown( 'x {@linkcode http://www.example.com/foo} y {@linkplain http://www.example.com/bar} z', @@ -55,7 +55,7 @@ suite('typescript.previewer', () => { 'x [`http://www.example.com/foo`](http://www.example.com/foo) y [http://www.example.com/bar](http://www.example.com/bar) z'); }); - test('Should parse url jsdoc @link in param tag', async () => { + test('Should parse url jsdoc @link in param tag', () => { assert.strictEqual( tagsToMarkdown([ { @@ -66,7 +66,7 @@ suite('typescript.previewer', () => { '*@param* `a` — x [abc xyz](http://www.example.com/foo) y [b a z](http://www.example.com/bar) z'); }); - test('Should ignore unclosed jsdocs @link', async () => { + test('Should ignore unclosed jsdocs @link', () => { assert.strictEqual( documentationToMarkdown( 'x {@link http://www.example.com/foo y {@link http://www.example.com/bar bar} z', @@ -76,7 +76,7 @@ suite('typescript.previewer', () => { 'x {@link http://www.example.com/foo y [bar](http://www.example.com/bar) z'); }); - test('Should support non-ascii characters in parameter name (#90108)', async () => { + test('Should support non-ascii characters in parameter name (#90108)', () => { assert.strictEqual( tagsToMarkdown([ { @@ -135,7 +135,35 @@ suite('typescript.previewer', () => { ); }); - test('Should render @linkcode symbol name as code', async () => { + test('Should not render @link inside of @example #187768', () => { + assert.strictEqual( + tagsToMarkdown([ + { + "name": "example", + "text": [ + { + "text": "1 + 1 ", + "kind": "text" + }, + { + "text": "{@link ", + "kind": "link" + }, + { + "text": "foo", + "kind": "linkName" + }, + { + "text": "}", + "kind": "link" + } + ] + } + ], noopToResource), + '*@example* \n```\n1 + 1 {@link foo}\n```'); + }); + + test('Should render @linkcode symbol name as code', () => { assert.strictEqual( asPlainTextWithLinks([ { "text": "a ", "kind": "text" }, @@ -155,7 +183,7 @@ suite('typescript.previewer', () => { 'a [`dog`](command:_typescript.openJsDocLink?%5B%7B%22file%22%3A%7B%22path%22%3A%22%2Fpath%2Ffile.ts%22%2C%22scheme%22%3A%22file%22%7D%2C%22position%22%3A%7B%22line%22%3A6%2C%22character%22%3A4%7D%7D%5D) b'); }); - test('Should render @linkcode text as code', async () => { + test('Should render @linkcode text as code', () => { assert.strictEqual( asPlainTextWithLinks([ { "text": "a ", "kind": "text" },