Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 26 additions & 7 deletions src/services/jsDoc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,20 @@ namespace ts.JsDoc {
// from Array<T> - Array<string> and Array<number>
const parts: SymbolDisplayPart[][] = [];
forEachUnique(declarations, declaration => {
for (const { comment } of getCommentHavingNodes(declaration)) {
if (comment === undefined) continue;
const newparts = getDisplayPartsFromComment(comment, checker);
for (const jsdoc of getCommentHavingNodes(declaration)) {
// skip comments containing @typedefs since they're not associated with particular declarations
// Exceptions:
// - @typedefs are themselves declarations with associated comments
// - @param or @return indicate that the author thinks of it as a 'local' @typedef that's part of the function documentation
if (jsdoc.comment === undefined
|| isJSDoc(jsdoc)
&& declaration.kind !== SyntaxKind.JSDocTypedefTag && declaration.kind !== SyntaxKind.JSDocCallbackTag
&& jsdoc.tags
&& jsdoc.tags.some(t => t.kind === SyntaxKind.JSDocTypedefTag || t.kind === SyntaxKind.JSDocCallbackTag)
&& !jsdoc.tags.some(t => t.kind === SyntaxKind.JSDocParameterTag || t.kind === SyntaxKind.JSDocReturnTag)) {
Comment on lines +104 to +105
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth an explanatory comment? This looks puzzling without the context of the conversation we had in this PR.

continue;
}
const newparts = getDisplayPartsFromComment(jsdoc.comment, checker);
if (!contains(parts, newparts, isIdenticalListOfDisplayParts)) {
parts.push(newparts);
}
Expand Down Expand Up @@ -122,13 +133,21 @@ namespace ts.JsDoc {

export function getJsDocTagsFromDeclarations(declarations?: Declaration[], checker?: TypeChecker): JSDocTagInfo[] {
// Only collect doc comments from duplicate declarations once.
const tags: JSDocTagInfo[] = [];
const infos: JSDocTagInfo[] = [];
forEachUnique(declarations, declaration => {
for (const tag of getJSDocTags(declaration)) {
tags.push({ name: tag.tagName.text, text: getCommentDisplayParts(tag, checker) });
const tags = getJSDocTags(declaration);
// skip comments containing @typedefs since they're not associated with particular declarations
// Exceptions:
// - @param or @return indicate that the author thinks of it as a 'local' @typedef that's part of the function documentation
if (tags.some(t => t.kind === SyntaxKind.JSDocTypedefTag || t.kind === SyntaxKind.JSDocCallbackTag)
&& !tags.some(t => t.kind === SyntaxKind.JSDocParameterTag || t.kind === SyntaxKind.JSDocReturnTag)) {
return;
}
for (const tag of tags) {
infos.push({ name: tag.tagName.text, text: getCommentDisplayParts(tag, checker) });
}
});
return tags;
return infos;
}

function getDisplayPartsFromComment(comment: string | readonly JSDocComment[], checker: TypeChecker | undefined): SymbolDisplayPart[] {
Expand Down
210 changes: 210 additions & 0 deletions tests/baselines/reference/quickInfoTypedefTag.baseline
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
[
{
"marker": {
"fileName": "/tests/cases/fourslash/a.js",
"position": 114,
"name": "1"
},
"quickInfo": {
"kind": "function",
"kindModifiers": "",
"textSpan": {
"start": 113,
"length": 1
},
"displayParts": [
{
"text": "function",
"kind": "keyword"
},
{
"text": " ",
"kind": "space"
},
{
"text": "f",
"kind": "functionName"
},
{
"text": "(",
"kind": "punctuation"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": ":",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "void",
"kind": "keyword"
}
],
"documentation": []
}
},
{
"marker": {
"fileName": "/tests/cases/fourslash/a.js",
"position": 316,
"name": "2"
},
"quickInfo": {
"kind": "function",
"kindModifiers": "",
"textSpan": {
"start": 315,
"length": 1
},
"displayParts": [
{
"text": "function",
"kind": "keyword"
},
{
"text": " ",
"kind": "space"
},
{
"text": "g",
"kind": "functionName"
},
{
"text": "(",
"kind": "punctuation"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": ":",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "void",
"kind": "keyword"
}
],
"documentation": []
}
},
{
"marker": {
"fileName": "/tests/cases/fourslash/a.js",
"position": 472,
"name": "3"
},
"quickInfo": {
"kind": "function",
"kindModifiers": "",
"textSpan": {
"start": 471,
"length": 1
},
"displayParts": [
{
"text": "function",
"kind": "keyword"
},
{
"text": " ",
"kind": "space"
},
{
"text": "h",
"kind": "functionName"
},
{
"text": "(",
"kind": "punctuation"
},
{
"text": "keep",
"kind": "parameterName"
},
{
"text": ":",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "Local",
"kind": "aliasName"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": ":",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "void",
"kind": "keyword"
}
],
"documentation": [
{
"text": "The whole thing is kept",
"kind": "text"
}
],
"tags": [
{
"name": "param",
"text": [
{
"text": "keep",
"kind": "text"
}
]
},
{
"name": "typedef",
"text": [
{
"text": "Local",
"kind": "aliasName"
},
{
"text": " ",
"kind": "space"
},
{
"text": "kept too",
"kind": "text"
}
]
},
{
"name": "returns",
"text": [
{
"text": "also kept",
"kind": "text"
}
]
}
]
}
}
]
27 changes: 27 additions & 0 deletions tests/cases/fourslash/quickInfoTypedefTag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/// <reference path='fourslash.ts' />
// @allowJs: true
// @Filename: a.js
//// /**
//// * The typedef tag should not appear in the quickinfo.
//// * @typedef {{ foo: 'foo' }} Foo
//// */
//// function f() { }
//// f/*1*/()
//// /**
//// * A removed comment
//// * @tag Usage shows that non-param tags in comments explain the typedef instead of using it
//// * @typedef {{ nope: any }} Nope not here
//// * @tag comment 2
//// */
//// function g() { }
//// g/*2*/()
//// /**
//// * The whole thing is kept
//// * @param {Local} keep
//// * @typedef {{ local: any }} Local kept too
//// * @returns {void} also kept
//// */
//// function h(keep) { }
//// h/*3*/({ nope: 1 })

verify.baselineQuickInfo()