Loosen author tag parsing (#41820)

* Loosen author tag parsing

Also make the code more efficient and easier to read.

1. Arbitrary text, except for newline and @, is allowed for the author name.
2. Arbirtrary text, except for newline, is allowed for the email
address.
3. Newline is treated as a match for an open <

I tried to allow newlines in the author and email, but it was ambiguous
with the end of the tag.

I also got rid of the two lookaheads and unified the trailing comment
handling.

Fixes #41804

* remove hardmode test since it did not reveal anything new
This commit is contained in:
Nathan Shively-Sanders
2020-12-08 13:25:03 -08:00
committed by GitHub
parent 69143ecc5b
commit d8c8e4ff06
3 changed files with 305 additions and 53 deletions
+17 -49
View File
@@ -7740,63 +7740,31 @@ namespace ts {
}
function parseAuthorTag(start: number, tagName: Identifier, indent: number, indentText: string): JSDocAuthorTag {
const authorInfoWithEmail = tryParse(() => tryParseAuthorNameAndEmail());
if (!authorInfoWithEmail) {
const end = getNodePos();
return finishNode(factory.createJSDocAuthorTag(tagName, parseTrailingTagComments(start, end, indent, indentText)), start, end);
}
let comments = authorInfoWithEmail;
if (lookAhead(() => nextToken() !== SyntaxKind.NewLineTrivia)) {
const comment = parseTagComments(indent);
if (comment) {
comments += comment;
}
}
return finishNode(factory.createJSDocAuthorTag(tagName, comments), start);
const comments = parseAuthorNameAndEmail() + (parseTrailingTagComments(start, end, indent, indentText) || "");
return finishNode(factory.createJSDocAuthorTag(tagName, comments || undefined), start);
}
function tryParseAuthorNameAndEmail(): string | undefined {
function parseAuthorNameAndEmail(): string {
const comments: string[] = [];
let seenLessThan = false;
let seenGreaterThan = false;
let inEmail = false;
let token = scanner.getToken();
loop: while (true) {
switch (token) {
case SyntaxKind.Identifier:
case SyntaxKind.WhitespaceTrivia:
case SyntaxKind.DotToken:
case SyntaxKind.AtToken:
comments.push(scanner.getTokenText());
break;
case SyntaxKind.LessThanToken:
if (seenLessThan || seenGreaterThan) {
return;
}
seenLessThan = true;
comments.push(scanner.getTokenText());
break;
case SyntaxKind.GreaterThanToken:
if (!seenLessThan || seenGreaterThan) {
return;
}
seenGreaterThan = true;
comments.push(scanner.getTokenText());
scanner.setTextPos(scanner.getTokenPos() + 1);
break loop;
case SyntaxKind.NewLineTrivia:
case SyntaxKind.EndOfFileToken:
break loop;
while (token !== SyntaxKind.EndOfFileToken && token !== SyntaxKind.NewLineTrivia) {
if (token === SyntaxKind.LessThanToken) {
inEmail = true;
}
else if (token === SyntaxKind.AtToken && !inEmail) {
break;
}
else if (token === SyntaxKind.GreaterThanToken && inEmail) {
comments.push(scanner.getTokenText());
scanner.setTextPos(scanner.getTokenPos() + 1);
break;
}
comments.push(scanner.getTokenText());
token = nextTokenJSDoc();
}
if (seenLessThan && seenGreaterThan) {
return comments.length === 0 ? undefined : comments.join("");
}
return comments.join("");
}
function parseImplementsTag(start: number, tagName: Identifier, margin: number, indentText: string): JSDocImplementsTag {
+15
View File
@@ -319,6 +319,21 @@ namespace ts {
`/**
* @author John Doe <john.doe@example.com>
* @author John Doe <john.doe@example.com> unexpected comment
* @author 108 <108@actionbutton.net> Video Games Forever
* @author Multiple Ats <email@quoting@how@does@it@work>
* @author Multiple Open Carets <hi<there@<>
* @author Multiple Close Carets <probably>invalid>but>who>cares>
* @author Unclosed Carets <joe@sloppy.gov
* @author Multiple @author On One <one@two.three> @author Line
* @author @author @author Empty authors
* @author
* @author
* Comments
* @author Early Close Caret > <a@b>
* @author No Line Breaks:
* <the.email@address> must be on the same line to parse
* @author Long Comment <long@comment.org> I
* want to keep commenting down here, I dunno.
*/`);
parsesCorrectly("consecutive newline tokens",
@@ -1,7 +1,7 @@
{
"kind": "JSDocComment",
"pos": 0,
"end": 112,
"end": 738,
"flags": "JSDoc",
"modifierFlagsCache": 0,
"transformFlags": 0,
@@ -25,7 +25,7 @@
"1": {
"kind": "JSDocAuthorTag",
"pos": 50,
"end": 110,
"end": 112,
"modifierFlagsCache": 0,
"transformFlags": 0,
"tagName": {
@@ -38,9 +38,278 @@
},
"comment": "John Doe <john.doe@example.com> unexpected comment"
},
"length": 2,
"2": {
"kind": "JSDocAuthorTag",
"pos": 112,
"end": 170,
"modifierFlagsCache": 0,
"transformFlags": 0,
"tagName": {
"kind": "Identifier",
"pos": 113,
"end": 119,
"modifierFlagsCache": 0,
"transformFlags": 0,
"escapedText": "author"
},
"comment": "108 <108@actionbutton.net> Video Games Forever"
},
"3": {
"kind": "JSDocAuthorTag",
"pos": 170,
"end": 227,
"modifierFlagsCache": 0,
"transformFlags": 0,
"tagName": {
"kind": "Identifier",
"pos": 171,
"end": 177,
"modifierFlagsCache": 0,
"transformFlags": 0,
"escapedText": "author"
},
"comment": "Multiple Ats <email@quoting@how@does@it@work>"
},
"4": {
"kind": "JSDocAuthorTag",
"pos": 227,
"end": 272,
"modifierFlagsCache": 0,
"transformFlags": 0,
"tagName": {
"kind": "Identifier",
"pos": 228,
"end": 234,
"modifierFlagsCache": 0,
"transformFlags": 0,
"escapedText": "author"
},
"comment": "Multiple Open Carets <hi<there@<>"
},
"5": {
"kind": "JSDocAuthorTag",
"pos": 272,
"end": 338,
"modifierFlagsCache": 0,
"transformFlags": 0,
"tagName": {
"kind": "Identifier",
"pos": 273,
"end": 279,
"modifierFlagsCache": 0,
"transformFlags": 0,
"escapedText": "author"
},
"comment": "Multiple Close Carets <probably>invalid>but>who>cares>"
},
"6": {
"kind": "JSDocAuthorTag",
"pos": 338,
"end": 381,
"modifierFlagsCache": 0,
"transformFlags": 0,
"tagName": {
"kind": "Identifier",
"pos": 339,
"end": 345,
"modifierFlagsCache": 0,
"transformFlags": 0,
"escapedText": "author"
},
"comment": "Unclosed Carets <joe@sloppy.gov"
},
"7": {
"kind": "JSDocAuthorTag",
"pos": 381,
"end": 398,
"modifierFlagsCache": 0,
"transformFlags": 0,
"tagName": {
"kind": "Identifier",
"pos": 382,
"end": 388,
"modifierFlagsCache": 0,
"transformFlags": 0,
"escapedText": "author"
},
"comment": "Multiple "
},
"8": {
"kind": "JSDocAuthorTag",
"pos": 398,
"end": 429,
"modifierFlagsCache": 0,
"transformFlags": 0,
"tagName": {
"kind": "Identifier",
"pos": 399,
"end": 405,
"modifierFlagsCache": 0,
"transformFlags": 0,
"escapedText": "author"
},
"comment": "On One <one@two.three>"
},
"9": {
"kind": "JSDocAuthorTag",
"pos": 429,
"end": 445,
"modifierFlagsCache": 0,
"transformFlags": 0,
"tagName": {
"kind": "Identifier",
"pos": 430,
"end": 436,
"modifierFlagsCache": 0,
"transformFlags": 0,
"escapedText": "author"
},
"comment": "Line"
},
"10": {
"kind": "JSDocAuthorTag",
"pos": 445,
"end": 453,
"modifierFlagsCache": 0,
"transformFlags": 0,
"tagName": {
"kind": "Identifier",
"pos": 446,
"end": 452,
"modifierFlagsCache": 0,
"transformFlags": 0,
"escapedText": "author"
}
},
"11": {
"kind": "JSDocAuthorTag",
"pos": 453,
"end": 461,
"modifierFlagsCache": 0,
"transformFlags": 0,
"tagName": {
"kind": "Identifier",
"pos": 454,
"end": 460,
"modifierFlagsCache": 0,
"transformFlags": 0,
"escapedText": "author"
}
},
"12": {
"kind": "JSDocAuthorTag",
"pos": 461,
"end": 486,
"modifierFlagsCache": 0,
"transformFlags": 0,
"tagName": {
"kind": "Identifier",
"pos": 462,
"end": 468,
"modifierFlagsCache": 0,
"transformFlags": 0,
"escapedText": "author"
},
"comment": "Empty authors"
},
"13": {
"kind": "JSDocAuthorTag",
"pos": 486,
"end": 497,
"modifierFlagsCache": 0,
"transformFlags": 0,
"tagName": {
"kind": "Identifier",
"pos": 487,
"end": 493,
"modifierFlagsCache": 0,
"transformFlags": 0,
"escapedText": "author"
}
},
"14": {
"kind": "JSDocAuthorTag",
"pos": 497,
"end": 522,
"modifierFlagsCache": 0,
"transformFlags": 0,
"tagName": {
"kind": "Identifier",
"pos": 498,
"end": 504,
"modifierFlagsCache": 0,
"transformFlags": 0,
"escapedText": "author"
},
"comment": "Comments"
},
"15": {
"kind": "JSDocAuthorTag",
"pos": 522,
"end": 559,
"modifierFlagsCache": 0,
"transformFlags": 0,
"tagName": {
"kind": "Identifier",
"pos": 523,
"end": 529,
"modifierFlagsCache": 0,
"transformFlags": 0,
"escapedText": "author"
},
"comment": "Early Close Caret > <a@b>"
},
"16": {
"kind": "JSDocAuthorTag",
"pos": 559,
"end": 598,
"modifierFlagsCache": 0,
"transformFlags": 0,
"tagName": {
"kind": "Identifier",
"pos": 560,
"end": 566,
"modifierFlagsCache": 0,
"transformFlags": 0,
"escapedText": "author"
},
"comment": "No Line Breaks:<the.email"
},
"17": {
"kind": "JSDocTag",
"pos": 598,
"end": 606,
"modifierFlagsCache": 0,
"transformFlags": 0,
"tagName": {
"kind": "Identifier",
"pos": 599,
"end": 606,
"modifierFlagsCache": 0,
"transformFlags": 0,
"escapedText": "address"
},
"comment": "> must be on the same line to parse"
},
"18": {
"kind": "JSDocAuthorTag",
"pos": 645,
"end": 736,
"modifierFlagsCache": 0,
"transformFlags": 0,
"tagName": {
"kind": "Identifier",
"pos": 646,
"end": 652,
"modifierFlagsCache": 0,
"transformFlags": 0,
"escapedText": "author"
},
"comment": "Long Comment <long@comment.org> I\nwant to keep commenting down here, I dunno."
},
"length": 19,
"pos": 7,
"end": 110,
"end": 736,
"hasTrailingComma": false,
"transformFlags": 0
}