Skip to content

Commit a77c601

Browse files
author
Andy
authored
Parse comment on @Property tag and use as documentation comment (#21119)
* Parse comment on @Property tag and use as documentation comment * Fix comment parsing bug -- back up after seeing `@` character * Add test for indent * Don't default comment to ""
1 parent 658de7f commit a77c601

27 files changed

+122
-123
lines changed

src/compiler/parser.ts

+23-23
Original file line numberDiff line numberDiff line change
@@ -6260,7 +6260,6 @@ namespace ts {
62606260
scanner.scanRange(start + 3, length - 5, () => {
62616261
// Initially we can parse out a tag. We also have seen a starting asterisk.
62626262
// This is so that /** * @type */ doesn't parse.
6263-
let advanceToken = true;
62646263
let state = JSDocState.SawAsterisk;
62656264
let margin: number | undefined = undefined;
62666265
// + 4 for leading '/** '
@@ -6292,7 +6291,6 @@ namespace ts {
62926291
// Real-world comments may break this rule, so "BeginningOfLine" will not be a real line beginning
62936292
// for malformed examples like `/** @param {string} x @returns {number} the length */`
62946293
state = JSDocState.BeginningOfLine;
6295-
advanceToken = false;
62966294
margin = undefined;
62976295
indent++;
62986296
}
@@ -6344,13 +6342,7 @@ namespace ts {
63446342
pushComment(scanner.getTokenText());
63456343
break;
63466344
}
6347-
if (advanceToken) {
6348-
t = nextJSDocToken();
6349-
}
6350-
else {
6351-
advanceToken = true;
6352-
t = currentToken as JsDocSyntaxKind;
6353-
}
6345+
t = nextJSDocToken();
63546346
}
63556347
removeLeadingNewlines(comments);
63566348
removeTrailingNewlines(comments);
@@ -6446,10 +6438,11 @@ namespace ts {
64466438
// a badly malformed tag should not be added to the list of tags
64476439
return;
64486440
}
6449-
addTag(tag, parseTagComments(indent + tag.end - tag.pos));
6441+
tag.comment = parseTagComments(indent + tag.end - tag.pos);
6442+
addTag(tag);
64506443
}
64516444

6452-
function parseTagComments(indent: number) {
6445+
function parseTagComments(indent: number): string | undefined {
64536446
const comments: string[] = [];
64546447
let state = JSDocState.BeginningOfLine;
64556448
let margin: number | undefined;
@@ -6471,6 +6464,8 @@ namespace ts {
64716464
indent = 0;
64726465
break;
64736466
case SyntaxKind.AtToken:
6467+
scanner.setTextPos(scanner.getTextPos() - 1);
6468+
// falls through
64746469
case SyntaxKind.EndOfFileToken:
64756470
// Done
64766471
break loop;
@@ -6506,7 +6501,7 @@ namespace ts {
65066501

65076502
removeLeadingNewlines(comments);
65086503
removeTrailingNewlines(comments);
6509-
return comments;
6504+
return comments.length === 0 ? undefined : comments.join("");
65106505
}
65116506

65126507
function parseUnknownTag(atToken: AtToken, tagName: Identifier) {
@@ -6516,9 +6511,7 @@ namespace ts {
65166511
return finishNode(result);
65176512
}
65186513

6519-
function addTag(tag: JSDocTag, comments: string[]): void {
6520-
tag.comment = comments.join("");
6521-
6514+
function addTag(tag: JSDocTag): void {
65226515
if (!tags) {
65236516
tags = [tag];
65246517
tagsPos = tag.pos;
@@ -6563,9 +6556,7 @@ namespace ts {
65636556
}
65646557
}
65656558

6566-
function parseParameterOrPropertyTag(atToken: AtToken, tagName: Identifier, target: PropertyLikeParse.Parameter): JSDocParameterTag;
6567-
function parseParameterOrPropertyTag(atToken: AtToken, tagName: Identifier, target: PropertyLikeParse.Property): JSDocPropertyTag;
6568-
function parseParameterOrPropertyTag(atToken: AtToken, tagName: Identifier, target: PropertyLikeParse): JSDocPropertyLikeTag {
6559+
function parseParameterOrPropertyTag(atToken: AtToken, tagName: Identifier, target: PropertyLikeParse): JSDocParameterTag | JSDocPropertyTag {
65696560
let typeExpression = tryParseTypeExpression();
65706561
let isNameFirst = !typeExpression;
65716562
skipWhitespace();
@@ -6577,7 +6568,7 @@ namespace ts {
65776568
typeExpression = tryParseTypeExpression();
65786569
}
65796570

6580-
const result: JSDocPropertyLikeTag = target === PropertyLikeParse.Parameter ?
6571+
const result = target === PropertyLikeParse.Parameter ?
65816572
<JSDocParameterTag>createNode(SyntaxKind.JSDocParameterTag, atToken.pos) :
65826573
<JSDocPropertyTag>createNode(SyntaxKind.JSDocPropertyTag, atToken.pos);
65836574
const nestedTypeLiteral = parseNestedTypeLiteral(typeExpression, name);
@@ -6592,7 +6583,6 @@ namespace ts {
65926583
result.isNameFirst = isNameFirst;
65936584
result.isBracketed = isBracketed;
65946585
return finishNode(result);
6595-
65966586
}
65976587

65986588
function parseNestedTypeLiteral(typeExpression: JSDocTypeExpression, name: EntityName) {
@@ -6815,18 +6805,28 @@ namespace ts {
68156805
if (!tagName) {
68166806
return false;
68176807
}
6808+
let t: PropertyLikeParse;
68186809
switch (tagName.escapedText) {
68196810
case "type":
68206811
return target === PropertyLikeParse.Property && parseTypeTag(atToken, tagName);
68216812
case "prop":
68226813
case "property":
6823-
return target === PropertyLikeParse.Property && parseParameterOrPropertyTag(atToken, tagName, target);
6814+
t = PropertyLikeParse.Property;
6815+
break;
68246816
case "arg":
68256817
case "argument":
68266818
case "param":
6827-
return target === PropertyLikeParse.Parameter && parseParameterOrPropertyTag(atToken, tagName, target);
6819+
t = PropertyLikeParse.Parameter;
6820+
break;
6821+
default:
6822+
return false;
68286823
}
6829-
return false;
6824+
if (target !== t) {
6825+
return false;
6826+
}
6827+
const tag = parseParameterOrPropertyTag(atToken, tagName, target);
6828+
tag.comment = parseTagComments(tag.end - tag.pos);
6829+
return tag;
68306830
}
68316831

68326832
function parseTemplateTag(atToken: AtToken, tagName: Identifier): JSDocTemplateTag | undefined {

src/compiler/utilities.ts

+5-12
Original file line numberDiff line numberDiff line change
@@ -1586,42 +1586,35 @@ namespace ts {
15861586
((node as JSDocFunctionType).parameters[0].name as Identifier).escapedText === "new";
15871587
}
15881588

1589-
export function getAllJSDocs(node: Node): (JSDoc | JSDocTag)[] {
1590-
if (isJSDocTypedefTag(node)) {
1591-
return [node.parent];
1592-
}
1593-
return getJSDocCommentsAndTags(node);
1594-
}
1595-
1596-
export function getSourceOfAssignment(node: Node): Node {
1589+
function getSourceOfAssignment(node: Node): Node {
15971590
return isExpressionStatement(node) &&
15981591
node.expression && isBinaryExpression(node.expression) &&
15991592
node.expression.operatorToken.kind === SyntaxKind.EqualsToken &&
16001593
node.expression.right;
16011594
}
16021595

1603-
export function getSingleInitializerOfVariableStatement(node: Node, child?: Node): Node {
1596+
function getSingleInitializerOfVariableStatement(node: Node, child?: Node): Node {
16041597
return isVariableStatement(node) &&
16051598
node.declarationList.declarations.length > 0 &&
16061599
(!child || node.declarationList.declarations[0].initializer === child) &&
16071600
node.declarationList.declarations[0].initializer;
16081601
}
16091602

1610-
export function getSingleVariableOfVariableStatement(node: Node, child?: Node): Node {
1603+
function getSingleVariableOfVariableStatement(node: Node, child?: Node): Node {
16111604
return isVariableStatement(node) &&
16121605
node.declarationList.declarations.length > 0 &&
16131606
(!child || node.declarationList.declarations[0] === child) &&
16141607
node.declarationList.declarations[0];
16151608
}
16161609

1617-
export function getNestedModuleDeclaration(node: Node): Node {
1610+
function getNestedModuleDeclaration(node: Node): Node {
16181611
return node.kind === SyntaxKind.ModuleDeclaration &&
16191612
(node as ModuleDeclaration).body &&
16201613
(node as ModuleDeclaration).body.kind === SyntaxKind.ModuleDeclaration &&
16211614
(node as ModuleDeclaration).body;
16221615
}
16231616

1624-
export function getJSDocCommentsAndTags(node: Node): (JSDoc | JSDocTag)[] {
1617+
export function getJSDocCommentsAndTags(node: Node): ReadonlyArray<JSDoc | JSDocTag> {
16251618
let result: (JSDoc | JSDocTag)[] | undefined;
16261619
getJSDocCommentsAndTagsWorker(node);
16271620
return result || emptyArray;

src/services/jsDoc.ts

+25-11
Original file line numberDiff line numberDiff line change
@@ -52,20 +52,30 @@ namespace ts.JsDoc {
5252
// Eg. const a: Array<string> | Array<number>; a.length
5353
// The property length will have two declarations of property length coming
5454
// from Array<T> - Array<string> and Array<number>
55-
const documentationComment = <SymbolDisplayPart[]>[];
55+
const documentationComment: SymbolDisplayPart[] = [];
5656
forEachUnique(declarations, declaration => {
57-
forEach(getAllJSDocs(declaration), doc => {
58-
if (doc.comment) {
59-
if (documentationComment.length) {
60-
documentationComment.push(lineBreakPart());
61-
}
62-
documentationComment.push(textPart(doc.comment));
57+
for (const { comment } of getCommentHavingNodes(declaration)) {
58+
if (comment === undefined) continue;
59+
if (documentationComment.length) {
60+
documentationComment.push(lineBreakPart());
6361
}
64-
});
62+
documentationComment.push(textPart(comment));
63+
}
6564
});
6665
return documentationComment;
6766
}
6867

68+
function getCommentHavingNodes(declaration: Declaration): ReadonlyArray<JSDoc | JSDocTag> {
69+
switch (declaration.kind) {
70+
case SyntaxKind.JSDocPropertyTag:
71+
return [declaration as JSDocPropertyTag];
72+
case SyntaxKind.JSDocTypedefTag:
73+
return [(declaration as JSDocTypedefTag).parent];
74+
default:
75+
return getJSDocCommentsAndTags(declaration);
76+
}
77+
}
78+
6979
export function getJsDocTagsFromDeclarations(declarations?: Declaration[]): JSDocTagInfo[] {
7080
// Only collect doc comments from duplicate declarations once.
7181
const tags: JSDocTagInfo[] = [];
@@ -77,7 +87,7 @@ namespace ts.JsDoc {
7787
return tags;
7888
}
7989

80-
function getCommentText(tag: JSDocTag): string {
90+
function getCommentText(tag: JSDocTag): string | undefined {
8191
const { comment } = tag;
8292
switch (tag.kind) {
8393
case SyntaxKind.JSDocAugmentsTag:
@@ -96,11 +106,15 @@ namespace ts.JsDoc {
96106
}
97107

98108
function withNode(node: Node) {
99-
return `${node.getText()} ${comment}`;
109+
return addComment(node.getText());
100110
}
101111

102112
function withList(list: NodeArray<Node>): string {
103-
return `${list.map(x => x.getText())} ${comment}`;
113+
return addComment(list.map(x => x.getText()).join(", "));
114+
}
115+
116+
function addComment(s: string) {
117+
return comment === undefined ? s : `${s} ${comment}`;
104118
}
105119
}
106120

tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.leadingAsterisk.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@
2727
"pos": 15,
2828
"end": 21
2929
}
30-
},
31-
"comment": ""
30+
}
3231
},
3332
"length": 1,
3433
"pos": 8,

tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.noLeadingAsterisk.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@
2727
"pos": 15,
2828
"end": 21
2929
}
30-
},
31-
"comment": ""
30+
}
3231
},
3332
"length": 1,
3433
"pos": 8,

tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.noReturnType.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@
1717
"pos": 9,
1818
"end": 15,
1919
"escapedText": "return"
20-
},
21-
"comment": ""
20+
}
2221
},
2322
"length": 1,
2423
"pos": 8,

tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.oneParamTag.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@
3535
"escapedText": "name1"
3636
},
3737
"isNameFirst": false,
38-
"isBracketed": false,
39-
"comment": ""
38+
"isBracketed": false
4039
},
4140
"length": 1,
4241
"pos": 8,

tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramTagNameThenType1.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@
3535
"escapedText": "name1"
3636
},
3737
"isNameFirst": true,
38-
"isBracketed": false,
39-
"comment": ""
38+
"isBracketed": false
4039
},
4140
"length": 1,
4241
"pos": 8,

tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.paramWithoutType.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@
2525
"escapedText": "foo"
2626
},
2727
"isNameFirst": true,
28-
"isBracketed": false,
29-
"comment": ""
28+
"isBracketed": false
3029
},
3130
"length": 1,
3231
"pos": 8,

tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.returnTag1.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@
2727
"pos": 17,
2828
"end": 23
2929
}
30-
},
31-
"comment": ""
30+
}
3231
},
3332
"length": 1,
3433
"pos": 8,

tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.returnsTag1.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@
2727
"pos": 18,
2828
"end": 24
2929
}
30-
},
31-
"comment": ""
30+
}
3231
},
3332
"length": 1,
3433
"pos": 8,

tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@
3333
"length": 1,
3434
"pos": 18,
3535
"end": 20
36-
},
37-
"comment": ""
36+
}
3837
},
3938
"length": 1,
4039
"pos": 8,

tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag2.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@
4444
"length": 2,
4545
"pos": 18,
4646
"end": 22
47-
},
48-
"comment": ""
47+
}
4948
},
5049
"length": 1,
5150
"pos": 8,

tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag3.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@
4444
"length": 2,
4545
"pos": 18,
4646
"end": 23
47-
},
48-
"comment": ""
47+
}
4948
},
5049
"length": 1,
5150
"pos": 8,

tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag4.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@
4444
"length": 2,
4545
"pos": 18,
4646
"end": 23
47-
},
48-
"comment": ""
47+
}
4948
},
5049
"length": 1,
5150
"pos": 8,

tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.templateTag5.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@
4444
"length": 2,
4545
"pos": 18,
4646
"end": 24
47-
},
48-
"comment": ""
47+
}
4948
},
5049
"length": 1,
5150
"pos": 8,

tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.twoParamTag2.json

+2-4
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@
3535
"escapedText": "name1"
3636
},
3737
"isNameFirst": false,
38-
"isBracketed": false,
39-
"comment": ""
38+
"isBracketed": false
4039
},
4140
"1": {
4241
"kind": "JSDocParameterTag",
@@ -70,8 +69,7 @@
7069
"escapedText": "name2"
7170
},
7271
"isNameFirst": false,
73-
"isBracketed": false,
74-
"comment": ""
72+
"isBracketed": false
7573
},
7674
"length": 2,
7775
"pos": 8,

0 commit comments

Comments
 (0)