Skip to content

Commit

Permalink
Scanner:skip @ in backticks or with bad whitespace
Browse files Browse the repository at this point in the history
Outside backticks, @ only starts a new tag after whitespace and before
non-whitespace. The scanner now checks for this instead of the parser.
  • Loading branch information
sandersn committed Mar 4, 2023
1 parent 3d96fa9 commit 9f4daf5
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 39 deletions.
25 changes: 5 additions & 20 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2165,8 +2165,8 @@ namespace Parser {
return currentToken = scanner.scanJsDocToken();
}

function nextTokenJSDocBig(): JSDocSyntaxKind { // TODO: nextTokenJSDocCommentText
return currentToken = scanner.scanBigJsDocToken();
function nextTokenJSDocBig(inBackticks: boolean): JSDocSyntaxKind { // TODO: nextTokenJSDocCommentText
return currentToken = scanner.scanBigJsDocToken(inBackticks);
}

function reScanGreaterToken(): SyntaxKind {
Expand Down Expand Up @@ -8680,7 +8680,7 @@ namespace Parser {
break;
}
if (state === JSDocState.SavingComments) {
nextTokenJSDocBig();
nextTokenJSDocBig(/*inBackticks*/ false);
}
else {
nextTokenJSDoc();
Expand Down Expand Up @@ -8869,7 +8869,6 @@ namespace Parser {
const parts: JSDocComment[] = [];
let linkEnd;
let state = JSDocState.BeginningOfLine;
let previousWhitespace = true;
let margin: number | undefined;
function pushComment(text: string) {
if (!margin) {
Expand Down Expand Up @@ -8901,14 +8900,8 @@ namespace Parser {
indent = 0;
break;
case SyntaxKind.AtToken:
if (state === JSDocState.SavingBackticks // TODO: nextTokenJSDocBig should be able to skip @ inside backticks
|| state === JSDocState.SavingComments && (!previousWhitespace || lookAhead(isNextJSDocTokenWhitespace))) {
// @ doesn't start a new tag inside ``, and inside a comment, only after whitespace or not before whitespace
comments.push(scanner.getTokenText());
break;
}
scanner.setTextPos(scanner.getTextPos() - 1);
// falls through
break loop;
case SyntaxKind.EndOfFileToken:
// Done
break loop;
Expand Down Expand Up @@ -8965,11 +8958,8 @@ namespace Parser {
pushComment(scanner.getTokenText());
break;
}
// TODO: nextTokenJSDocBig always returns Identifier, even when that token ends with some whitespace.
// Make this hack less hacky: call a isWhitespace function, and importantly, the state *currently* being SavingComments doesn't mean that the previous call was for a big token
previousWhitespace = token() === SyntaxKind.WhitespaceTrivia || ((state === JSDocState.SavingComments || state === JSDocState.SavingBackticks) && tok === SyntaxKind.Identifier && scanner.getTokenValue().at(-1) === " ");
if (state === JSDocState.SavingComments || state === JSDocState.SavingBackticks) { // TODO: Add another scanner method for scanning over the introductory " *" after BeginningOfLine
tok = nextTokenJSDocBig(); // TODO: Maybe SawAsterisk could also call nextTokenJSDocBig?
tok = nextTokenJSDocBig(state === JSDocState.SavingBackticks); // TODO: Maybe SawAsterisk could also call nextTokenJSDocBig?
} // TODO: Maybe nextTokenJSDocBig is backward-compatible enough to just call all the time
else {
tok = nextTokenJSDoc();
Expand All @@ -8989,11 +8979,6 @@ namespace Parser {
}
}

function isNextJSDocTokenWhitespace() {
const next = nextTokenJSDoc();
return next === SyntaxKind.WhitespaceTrivia || next === SyntaxKind.NewLineTrivia;
}

function parseJSDocLink(start: number) {
const linkType = tryParse(parseJSDocLinkPrefix);
if (!linkType) {
Expand Down
35 changes: 18 additions & 17 deletions src/compiler/scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export interface Scanner {
reScanInvalidIdentifier(): SyntaxKind;
scanJsxToken(): JsxTokenSyntaxKind;
scanJsDocToken(): JSDocSyntaxKind;
scanBigJsDocToken(): JSDocSyntaxKind; // TODO: Should only be the Big Token kinds
scanBigJsDocToken(inBackticks: boolean): JSDocSyntaxKind;
scan(): SyntaxKind;

getText(): string;
Expand Down Expand Up @@ -2457,32 +2457,33 @@ export function createScanner(languageVersion: ScriptTarget,
return scanJsxAttributeValue();
}

/** TODO: might need to return WhitespaceTrivia if only whitespace was encountered? */
function scanBigJsDocToken(): JSDocSyntaxKind { // can be configurable to skip almost everything (except newline and backtick) if backticks is true
function scanBigJsDocToken(inBackticks: boolean): JSDocSyntaxKind {
startPos = tokenPos = pos;
tokenFlags = TokenFlags.None;
if (pos >= end) {
return token = SyntaxKind.EndOfFileToken;
}

let ch = codePointAt(text, pos);
while (pos < end) {
if (ch !== CharacterCodes.lineFeed && ch !== CharacterCodes.at && ch !== CharacterCodes.backtick && ch !== CharacterCodes.openBrace) {
// TODO: We can also be smarter about openBrace, backtick and at by looking at a tiny amount of context
pos++;
}
else {
break;
// TODO: Probably need to increment pos in the initial part to avoid a double read
// TODO: Need to increment `pos += charSize(ch)`,
for (let ch = codePointAt(text, pos);
pos < end && (ch !== CharacterCodes.lineFeed && ch !== CharacterCodes.carriageReturn && ch !== CharacterCodes.backtick);
ch = codePointAt(text, ++pos)) {
if (!inBackticks) {
if (ch === CharacterCodes.openBrace) {
break;
}
else if (ch === CharacterCodes.at
&& pos - 1 >= 0 && isWhiteSpaceSingleLine(codePointAt(text, pos - 1))
&& !(pos + 1 < end && isWhiteSpaceLike(codePointAt(text, pos + 1)))) {
// @ doesn't start a new tag inside ``, and elsewhere, only after whitespace and before non-whitespace
break;
}
}
ch = codePointAt(text, pos);
}
if (pos === tokenPos) {
return scanJsDocToken();
}
else {
// TODO: Make sure this is right (and in the right place)
tokenValue = text.substring(tokenPos, pos);
}
tokenValue = text.substring(tokenPos, pos);
return token = SyntaxKind.Identifier;
}

Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8350,7 +8350,7 @@ declare namespace ts {
reScanInvalidIdentifier(): SyntaxKind;
scanJsxToken(): JsxTokenSyntaxKind;
scanJsDocToken(): JSDocSyntaxKind;
scanBigJsDocToken(): JSDocSyntaxKind;
scanBigJsDocToken(inBackticks: boolean): JSDocSyntaxKind;
scan(): SyntaxKind;
getText(): string;
setText(text: string | undefined, start?: number, length?: number): void;
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4407,7 +4407,7 @@ declare namespace ts {
reScanInvalidIdentifier(): SyntaxKind;
scanJsxToken(): JsxTokenSyntaxKind;
scanJsDocToken(): JSDocSyntaxKind;
scanBigJsDocToken(): JSDocSyntaxKind;
scanBigJsDocToken(inBackticks: boolean): JSDocSyntaxKind;
scan(): SyntaxKind;
getText(): string;
setText(text: string | undefined, start?: number, length?: number): void;
Expand Down

0 comments on commit 9f4daf5

Please sign in to comment.