From 36b95b9086f8d78a2a3ea5657463ac0ed36de9e4 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Wed, 17 May 2023 19:58:55 +0000 Subject: [PATCH] Experimentally use `forEach*CommentRange` for both uses of `getJSDocCommentRanges`. --- src/compiler/parser.ts | 12 +++++++--- src/compiler/utilities.ts | 47 ++++++++++++++++++++++++------------- src/services/textChanges.ts | 12 ++++++---- 3 files changed, 48 insertions(+), 23 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index f49b48bb442..d0173b56188 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -92,6 +92,7 @@ import { firstOrUndefined, forEach, ForEachChildNodes, + forEachJSDocCommentRangeOfNode, ForInOrOfStatement, ForInStatement, ForOfStatement, @@ -104,7 +105,6 @@ import { getBaseFileName, getBinaryOperatorPrecedence, getFullWidth, - getJSDocCommentRanges, getLanguageVariant, getLastChild, getLeadingCommentRanges, @@ -166,6 +166,7 @@ import { IterationStatement, JSDoc, JSDocAllType, + JSDocArray, JSDocAugmentsTag, JSDocAuthorTag, JSDocCallbackTag, @@ -239,7 +240,6 @@ import { LiteralLikeNode, LiteralTypeNode, map, - mapDefined, MappedTypeNode, MemberExpression, MetaProperty, @@ -1813,7 +1813,13 @@ namespace Parser { let hasDeprecatedTag = false; function addJSDocComment(node: T): T { Debug.assert(!node.jsDoc); // Should only be called once per node - const jsDoc = mapDefined(getJSDocCommentRanges(node, sourceText), comment => JSDocParser.parseJSDocComment(node, comment.pos, comment.end - comment.pos)); + const jsDoc: JSDocArray = []; + forEachJSDocCommentRangeOfNode(node, sourceText, (pos, end, _kind, _hasTrailingNewline, jsDoc) => { + const comment = JSDocParser.parseJSDocComment(node, pos, end - pos); + if (comment) { + jsDoc.push(comment); + } + }, jsDoc); if (jsDoc.length) node.jsDoc = jsDoc; if (hasDeprecatedTag) { hasDeprecatedTag = false; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 54b8f927866..a99dfadecbe 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -60,6 +60,7 @@ import { CommentDirective, CommentDirectivesMap, CommentDirectiveType, + CommentKind, CommentRange, compareStringsCaseSensitive, compareValues, @@ -149,6 +150,8 @@ import { forEachAncestorDirectory, forEachChild, forEachChildRecursively, + forEachLeadingCommentRange, + forEachTrailingCommentRange, ForInOrOfStatement, ForInStatement, ForOfStatement, @@ -196,7 +199,6 @@ import { getSnippetElement, getStringComparer, getSymbolId, - getTrailingCommentRanges, HasExpressionInitializer, hasExtension, HasFlowNode, @@ -2395,21 +2397,34 @@ export function getLeadingCommentRangesOfNode(node: Node, sourceFileOfNode: Sour } /** @internal */ -export function getJSDocCommentRanges(node: Node, text: string) { - const commentRanges = (node.kind === SyntaxKind.Parameter || - node.kind === SyntaxKind.TypeParameter || - node.kind === SyntaxKind.FunctionExpression || - node.kind === SyntaxKind.ArrowFunction || - node.kind === SyntaxKind.ParenthesizedExpression || - node.kind === SyntaxKind.VariableDeclaration || - node.kind === SyntaxKind.ExportSpecifier) ? - concatenate(getTrailingCommentRanges(text, node.pos), getLeadingCommentRanges(text, node.pos)) : - getLeadingCommentRanges(text, node.pos); - // True if the comment starts with '/**' but not if it is '/**/' - return filter(commentRanges, comment => - text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk && - text.charCodeAt(comment.pos + 2) === CharacterCodes.asterisk && - text.charCodeAt(comment.pos + 3) !== CharacterCodes.slash); +export function forEachJSDocCommentRangeOfNode({ kind, pos }: Node, text: string, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, state: T) => U, state?: T): U | undefined { + let result: U | undefined; + switch (kind) { + case SyntaxKind.Parameter: + case SyntaxKind.TypeParameter: + case SyntaxKind.FunctionExpression: + case SyntaxKind.ArrowFunction: + case SyntaxKind.ParenthesizedExpression: + case SyntaxKind.VariableDeclaration: + case SyntaxKind.ExportSpecifier: + result = forEachTrailingCommentRange(text, pos, runCallback, state!); + if (result) { + return result; + } + } + result = forEachLeadingCommentRange(text, pos, runCallback, state!); + if (result) { + return result; + } + + function runCallback(pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, state: T) { + // Only passes through comments that start with '/**', but not '/**/' + if (kind === SyntaxKind.MultiLineCommentTrivia && + text.charCodeAt(pos + 2) === CharacterCodes.asterisk && + text.charCodeAt(pos + 3) !== CharacterCodes.slash) { + return cb(pos, end, kind, hasTrailingNewLine, state); + } + } } /** @internal */ diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 3579b47fbcf..41de36a6d00 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -38,13 +38,13 @@ import { firstOrUndefined, flatMap, flatMapToMutable, + forEachJSDocCommentRangeOfNode, formatting, FunctionDeclaration, FunctionExpression, getAncestor, getFirstNonSpaceCharacterPosition, getFormatCodeSettingsForWriting, - getJSDocCommentRanges, getLeadingCommentRanges, getLineAndCharacterOfPosition, getLineOfLocalPosition, @@ -361,9 +361,13 @@ function getAdjustedStartPosition(sourceFile: SourceFile, node: Node, options: C return rangeContainsPosition(node, pos) ? pos : startPos; } if (leadingTriviaOption === LeadingTriviaOption.JSDoc) { - const JSDocComments = getJSDocCommentRanges(node, sourceFile.text); - if (JSDocComments?.length) { - return getLineStartPositionForPosition(JSDocComments[0].pos, sourceFile); + let commentLineStart = -1; + forEachJSDocCommentRangeOfNode(node, sourceFile.text, pos => { + commentLineStart = getLineStartPositionForPosition(pos, sourceFile); + return true; + }); + if (commentLineStart >= 0) { + return commentLineStart; } } const fullStart = node.getFullStart();