mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Use a state enum to parse top-level jsdoc comments
Also use a `pushComments` function, which fixes a spacing bug.
This commit is contained in:
+57
-69
@@ -6151,10 +6151,10 @@ namespace ts {
|
||||
return comment;
|
||||
}
|
||||
|
||||
const enum TagState {
|
||||
const enum JSDocState {
|
||||
BeginningOfLine,
|
||||
SawAsterisk,
|
||||
SavingComments
|
||||
SavingComments,
|
||||
}
|
||||
|
||||
export function parseJSDocCommentWorker(start: number, length: number): JSDoc {
|
||||
@@ -6180,93 +6180,81 @@ namespace ts {
|
||||
scanner.scanRange(start + 3, length - 5, () => {
|
||||
// Initially we can parse out a tag. We also have seen a starting asterisk.
|
||||
// This is so that /** * @type */ doesn't parse.
|
||||
let canParseTag = true;
|
||||
let seenAsterisk = true;
|
||||
let advanceToken = true;
|
||||
let state = JSDocState.SawAsterisk;
|
||||
let margin: number | undefined = undefined;
|
||||
let indent = start - Math.max(content.lastIndexOf("\n", start), 0) + 4;
|
||||
let text: string;
|
||||
function pushComment(text: string) {
|
||||
if (!margin) {
|
||||
margin = indent;
|
||||
}
|
||||
comments.push(text);
|
||||
indent += text.length;
|
||||
}
|
||||
|
||||
nextJSDocToken();
|
||||
while (token() === SyntaxKind.WhitespaceTrivia) {
|
||||
nextJSDocToken();
|
||||
}
|
||||
if (token() === SyntaxKind.NewLineTrivia) {
|
||||
canParseTag = true;
|
||||
seenAsterisk = false;
|
||||
state = JSDocState.BeginningOfLine;
|
||||
nextJSDocToken();
|
||||
}
|
||||
while (token() !== SyntaxKind.EndOfFileToken) {
|
||||
switch (token()) {
|
||||
case SyntaxKind.AtToken:
|
||||
if (canParseTag) {
|
||||
if (state === JSDocState.BeginningOfLine || state === JSDocState.SawAsterisk) {
|
||||
removeTrailingNewlines(comments);
|
||||
parseTag(indent);
|
||||
// This will take us past the end of the line, so it's OK to parse a tag on the next pass through the loop
|
||||
// NOTE: According to usejsdoc.org, a tag goes to end of line, except the last tag. But real-world comments may break this rule.
|
||||
seenAsterisk = false;
|
||||
// NOTE: According to usejsdoc.org, a tag goes to end of line, except the last tag.
|
||||
// Real-world comments may break this rule, so "BeginningOfLine" will not be a real line beginning
|
||||
// for malformed examples like `/** @param {string} x @returns {number} the length */`
|
||||
state = JSDocState.BeginningOfLine;
|
||||
advanceToken = false;
|
||||
margin = undefined;
|
||||
indent++;
|
||||
}
|
||||
else {
|
||||
comments.push(scanner.getTokenText());
|
||||
pushComment(scanner.getTokenText());
|
||||
}
|
||||
indent++;
|
||||
break;
|
||||
|
||||
case SyntaxKind.NewLineTrivia:
|
||||
// After a line break, we can parse a tag, and we haven't seen an asterisk on the next line yet
|
||||
comments.push(scanner.getTokenText());
|
||||
canParseTag = true;
|
||||
seenAsterisk = false;
|
||||
state = JSDocState.BeginningOfLine;
|
||||
indent = 0;
|
||||
break;
|
||||
|
||||
case SyntaxKind.AsteriskToken:
|
||||
text = scanner.getTokenText();
|
||||
if (seenAsterisk) {
|
||||
const asterisk = scanner.getTokenText();
|
||||
if (state === JSDocState.SawAsterisk) {
|
||||
// If we've already seen an asterisk, then we can no longer parse a tag on this line
|
||||
canParseTag = false;
|
||||
comments.push(text);
|
||||
if (!margin) {
|
||||
margin = indent;
|
||||
}
|
||||
state = JSDocState.SavingComments;
|
||||
pushComment(asterisk);
|
||||
}
|
||||
else {
|
||||
// Ignore the first asterisk on a line
|
||||
state = JSDocState.SawAsterisk;
|
||||
indent += asterisk.length;
|
||||
}
|
||||
// Ignore the first asterisk on a line
|
||||
seenAsterisk = true;
|
||||
indent += text.length;
|
||||
break;
|
||||
|
||||
case SyntaxKind.Identifier:
|
||||
// Anything else is doc comment text. We just save it. Because it
|
||||
// wasn't a tag, we can no longer parse a tag on this line until we hit the next
|
||||
// line break.
|
||||
text = scanner.getTokenText();
|
||||
comments.push(text);
|
||||
if (!margin) {
|
||||
margin = indent;
|
||||
}
|
||||
canParseTag = false;
|
||||
indent += text.length;
|
||||
pushComment(scanner.getTokenText());
|
||||
state = JSDocState.SavingComments;
|
||||
break;
|
||||
|
||||
|
||||
case SyntaxKind.WhitespaceTrivia:
|
||||
// only collect whitespace if we *know* that we're just looking at comments, not a possible jsdoc tag
|
||||
text = scanner.getTokenText();
|
||||
if (!canParseTag || margin !== undefined && indent + text.length > margin) {
|
||||
comments.push(text.slice(margin - indent - 1));
|
||||
// only collect whitespace if we're already saving comments or have just crossed the comment indent margin
|
||||
const whitespace = scanner.getTokenText();
|
||||
if (state === JSDocState.SavingComments || margin !== undefined && indent + whitespace.length > margin) {
|
||||
comments.push(whitespace.slice(margin - indent - 1));
|
||||
}
|
||||
indent += text.length;
|
||||
indent += whitespace.length;
|
||||
break;
|
||||
|
||||
case SyntaxKind.EndOfFileToken:
|
||||
break;
|
||||
|
||||
default:
|
||||
text = scanner.getTokenText();
|
||||
comments.push(text);
|
||||
indent += text.length;
|
||||
pushComment(scanner.getTokenText());
|
||||
break;
|
||||
}
|
||||
if (advanceToken) {
|
||||
@@ -6365,10 +6353,8 @@ namespace ts {
|
||||
|
||||
function parseTagComments(indent: number) {
|
||||
const comments: string[] = [];
|
||||
let state = TagState.SawAsterisk;
|
||||
let done = false;
|
||||
let state = JSDocState.SawAsterisk;
|
||||
let margin: number | undefined;
|
||||
let text: string;
|
||||
function pushComment(text: string) {
|
||||
if (!margin) {
|
||||
margin = indent;
|
||||
@@ -6376,47 +6362,49 @@ namespace ts {
|
||||
comments.push(text);
|
||||
indent += text.length;
|
||||
}
|
||||
while (!done && token() !== SyntaxKind.EndOfFileToken) {
|
||||
text = scanner.getTokenText();
|
||||
while (token() !== SyntaxKind.AtToken && token() !== SyntaxKind.EndOfFileToken) {
|
||||
switch (token()) {
|
||||
case SyntaxKind.NewLineTrivia:
|
||||
if (state >= TagState.SawAsterisk) {
|
||||
state = TagState.BeginningOfLine;
|
||||
comments.push(text);
|
||||
if (state >= JSDocState.SawAsterisk) {
|
||||
state = JSDocState.BeginningOfLine;
|
||||
comments.push(scanner.getTokenText());
|
||||
}
|
||||
indent = 0;
|
||||
break;
|
||||
case SyntaxKind.AtToken:
|
||||
done = true;
|
||||
// Done
|
||||
break;
|
||||
case SyntaxKind.WhitespaceTrivia:
|
||||
if (state === TagState.SavingComments) {
|
||||
pushComment(text);
|
||||
if (state === JSDocState.SavingComments) {
|
||||
pushComment(scanner.getTokenText());
|
||||
}
|
||||
else {
|
||||
const whitespace = scanner.getTokenText();
|
||||
// if the whitespace crosses the margin, take only the whitespace that passes the margin
|
||||
if (margin !== undefined && indent + text.length > margin) {
|
||||
comments.push(text.slice(margin - indent - 1));
|
||||
if (margin !== undefined && indent + whitespace.length > margin) {
|
||||
comments.push(whitespace.slice(margin - indent - 1));
|
||||
}
|
||||
indent += text.length;
|
||||
indent += whitespace.length;
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.AsteriskToken:
|
||||
if (state === TagState.BeginningOfLine) {
|
||||
if (state === JSDocState.BeginningOfLine) {
|
||||
// leading asterisks start recording on the *next* (non-whitespace) token
|
||||
state = TagState.SawAsterisk;
|
||||
indent += text.length;
|
||||
state = JSDocState.SawAsterisk;
|
||||
indent += scanner.getTokenText().length;
|
||||
break;
|
||||
}
|
||||
// FALLTHROUGH otherwise to record the * as a comment
|
||||
default:
|
||||
state = TagState.SavingComments; // leading identifiers start recording as well
|
||||
pushComment(text);
|
||||
state = JSDocState.SavingComments; // leading identifiers start recording as well
|
||||
pushComment(scanner.getTokenText());
|
||||
break;
|
||||
}
|
||||
if (!done) {
|
||||
nextJSDocToken();
|
||||
if (token() === SyntaxKind.AtToken) {
|
||||
// Done
|
||||
break;
|
||||
}
|
||||
nextJSDocToken();
|
||||
}
|
||||
|
||||
removeLeadingNewlines(comments);
|
||||
|
||||
@@ -351,39 +351,39 @@ verify.completionListContains("multiply", "function multiply(a: number, b: numbe
|
||||
verify.completionListContains("f1", "function f1(a: number): any (+1 overload)", "fn f1 with number");
|
||||
|
||||
goTo.marker('28');
|
||||
verify.currentSignatureHelpDocCommentIs("This is subtract function{()=>string; } } f this is optional param f");
|
||||
verify.currentSignatureHelpDocCommentIs("This is subtract function{ () => string; } } f this is optional param f");
|
||||
verify.currentParameterHelpArgumentDocCommentIs("");
|
||||
goTo.marker('28q');
|
||||
verify.quickInfoIs("function subtract(a: number, b: number, c?: () => string, d?: () => string, e?: () => string, f?: () => string): void", "This is subtract function{()=>string; } } f this is optional param f");
|
||||
verify.quickInfoIs("function subtract(a: number, b: number, c?: () => string, d?: () => string, e?: () => string, f?: () => string): void", "This is subtract function{ () => string; } } f this is optional param f");
|
||||
goTo.marker('28aq');
|
||||
verify.quickInfoIs("(parameter) a: number", "");
|
||||
|
||||
goTo.marker('29');
|
||||
verify.currentSignatureHelpDocCommentIs("This is subtract function{()=>string; } } f this is optional param f");
|
||||
verify.currentSignatureHelpDocCommentIs("This is subtract function{ () => string; } } f this is optional param f");
|
||||
verify.currentParameterHelpArgumentDocCommentIs("this is about b");
|
||||
goTo.marker('29aq');
|
||||
verify.quickInfoIs("(parameter) b: number", "this is about b");
|
||||
|
||||
goTo.marker('30');
|
||||
verify.currentSignatureHelpDocCommentIs("This is subtract function{()=>string; } } f this is optional param f");
|
||||
verify.currentSignatureHelpDocCommentIs("This is subtract function{ () => string; } } f this is optional param f");
|
||||
verify.currentParameterHelpArgumentDocCommentIs("this is optional param c");
|
||||
goTo.marker('30aq');
|
||||
verify.quickInfoIs("(parameter) c: () => string", "this is optional param c");
|
||||
|
||||
goTo.marker('31');
|
||||
verify.currentSignatureHelpDocCommentIs("This is subtract function{()=>string; } } f this is optional param f");
|
||||
verify.currentSignatureHelpDocCommentIs("This is subtract function{ () => string; } } f this is optional param f");
|
||||
verify.currentParameterHelpArgumentDocCommentIs("this is optional param d");
|
||||
goTo.marker('31aq');
|
||||
verify.quickInfoIs("(parameter) d: () => string", "this is optional param d");
|
||||
|
||||
goTo.marker('32');
|
||||
verify.currentSignatureHelpDocCommentIs("This is subtract function{()=>string; } } f this is optional param f");
|
||||
verify.currentSignatureHelpDocCommentIs("This is subtract function{ () => string; } } f this is optional param f");
|
||||
verify.currentParameterHelpArgumentDocCommentIs("this is optional param e");
|
||||
goTo.marker('32aq');
|
||||
verify.quickInfoIs("(parameter) e: () => string", "this is optional param e");
|
||||
|
||||
goTo.marker('33');
|
||||
verify.currentSignatureHelpDocCommentIs("This is subtract function{()=>string; } } f this is optional param f");
|
||||
verify.currentSignatureHelpDocCommentIs("This is subtract function{ () => string; } } f this is optional param f");
|
||||
verify.currentParameterHelpArgumentDocCommentIs("");
|
||||
goTo.marker('33aq');
|
||||
verify.quickInfoIs("(parameter) f: () => string", "");
|
||||
|
||||
Reference in New Issue
Block a user