diff --git a/src/language-js/print/expression-statement.js b/src/language-js/print/expression-statement.js index 156ec614a..df6e2e123 100644 --- a/src/language-js/print/expression-statement.js +++ b/src/language-js/print/expression-statement.js @@ -1,6 +1,6 @@ import { printComments, - printCommentsSeparately, + printLeadingComments, } from "../../main/comments/print.js"; import { isSingleHtmlEventHandlerExpressionStatement, @@ -57,17 +57,13 @@ function printExpressionStatement(path, options, print) { ); // Print the type cast comment separately and print `;` before it - // eslint-disable-next-line prettier-internal-rules/no-node-comments - const { comments } = node; - // eslint-disable-next-line prettier-internal-rules/no-node-comments - node.comments = [typeCastComment]; - const typeCastCommentDoc = printCommentsSeparately(path, options).leading; - // eslint-disable-next-line prettier-internal-rules/no-node-comments - node.comments = comments; - // Make it printed, so `printComments` will ignore it - options[Symbol.for("printedComments")].add(typeCastComment); + const typeCastCommentDoc = printLeadingComments(path, options, { + filter: (comment) => comment === typeCastComment, + }); - return printComments(path, [";", typeCastCommentDoc, ...parts], options); + return printComments(path, [";", typeCastCommentDoc, ...parts], options, { + filter: (comment) => comment !== typeCastComment, + }); } parts.unshift(";"); diff --git a/src/main/ast-to-doc.js b/src/main/ast-to-doc.js index 641681403..7b77f6e10 100644 --- a/src/main/ast-to-doc.js +++ b/src/main/ast-to-doc.js @@ -1,5 +1,6 @@ import AstPath from "../common/ast-path.js"; import { cursor, inheritLabel } from "../document/index.js"; +import isNonEmptyArray from "../utilities/is-non-empty-array.js"; import isObject from "../utilities/is-object.js"; import { attachComments } from "./comments/attach.js"; import { ensureAllCommentsPrinted, printComments } from "./comments/print.js"; @@ -128,7 +129,11 @@ function callPluginPrintFunction(path, options, printPath, args, embeds) { // We let JSXElement print its comments itself because it adds () around // UnionTypeAnnotation has to align the child without the comments - if (printer.printComment && !printer.willPrintOwnComments?.(path, options)) { + if ( + printer.printComment && + isNonEmptyArray(node.comments) && + !printer.willPrintOwnComments?.(path, options) + ) { // printComments will call the plugin print function and check for // comments to print doc = printComments(path, doc, options); diff --git a/src/main/comments/print.js b/src/main/comments/print.js index e6d9e256b..8aca909bb 100644 --- a/src/main/comments/print.js +++ b/src/main/comments/print.js @@ -8,15 +8,17 @@ import { lineSuffix, } from "../../document/index.js"; import hasNewline from "../../utilities/has-newline.js"; -import isNonEmptyArray from "../../utilities/is-non-empty-array.js"; import isPreviousLineEmpty from "../../utilities/is-previous-line-empty.js"; import { skipSpaces } from "../../utilities/skip.js"; import skipNewline from "../../utilities/skip-newline.js"; /** - * @import AstPath from "../../common/ast-path.js" - * @import {Doc, HardLine, Line} from "../../document/index.js" - */ +@import AstPath from "../../common/ast-path.js" +@import {Doc, HardLine, Line} from "../../document/index.js" +@typedef {{ + filter?: (comment) => boolean, +}} CommentPrintOptions +*/ const returnTrue = () => true; @@ -111,10 +113,9 @@ function printTrailingComment(path, options, previousComment) { /** * @param {AstPath} path - * @param {{ + * @param {CommentPrintOptions & { * indent?: boolean, * marker?: symbol | string, - * filter?: (comment) => boolean, * }} [danglingCommentsPrintOptions] * @returns {Doc} */ @@ -123,89 +124,133 @@ function printDanglingComments( options, danglingCommentsPrintOptions = {}, ) { - const { node } = path; - - if (!isNonEmptyArray(node?.comments)) { - return ""; - } - const { indent: shouldIndent = false, marker, filter = returnTrue, } = danglingCommentsPrintOptions; + const danglingComments = new Set( + path.node?.comments?.filter( + (comment) => + !( + comment.leading || + comment.trailing || + comment.marker !== marker || + !filter(comment) + ), + ), + ); - const parts = []; - path.each(({ node: comment }) => { - if ( - comment.leading || - comment.trailing || - comment.marker !== marker || - !filter(comment) - ) { - return; - } - - parts.push(printComment(path, options)); - }, "comments"); - - if (parts.length === 0) { + if (danglingComments.size === 0) { return ""; } + const parts = path + .map( + ({ node: comment }) => + danglingComments.has(comment) ? printComment(path, options) : "", + "comments", + ) + .filter(Boolean); + const doc = join(hardline, parts); return shouldIndent ? indent([hardline, doc]) : doc; } /** -@returns {{leading?: Doc, trailing?: Doc}} +@param {AstPath} path +@param {CommentPrintOptions} [printOptions] +@returns {Doc} */ -function printCommentsSeparately(path, options) { - const value = path.node; - if (!value) { - return {}; - } - - const ignored = options[Symbol.for("printedComments")]; - const comments = (value.comments || []).filter( - (comment) => !ignored.has(comment), +function printLeadingComments(path, options, printOptions) { + const printed = options[Symbol.for("printedComments")]; + const filter = printOptions?.filter ?? returnTrue; + const leadingComments = new Set( + path.node?.comments?.filter( + (comment) => !printed?.has(comment) && comment.leading && filter(comment), + ), ); - if (comments.length === 0) { - return { leading: "", trailing: "" }; + if (leadingComments.size === 0) { + return ""; } - const leadingParts = []; - const trailingParts = []; + return path + .map( + ({ node: comment }) => + leadingComments.has(comment) ? printLeadingComment(path, options) : "", + "comments", + ) + .filter(Boolean); +} + +/** +@param {AstPath} path +@param {CommentPrintOptions} [printOptions] +@returns {Doc} +*/ +function printTrailingComments(path, options, printOptions) { + const comments = path.node?.comments; + const trailingComments = new Set( + comments?.filter((comment) => comment.trailing), + ); + const printed = options[Symbol.for("printedComments")]; + const filter = printOptions?.filter ?? returnTrue; + const commentsShouldPrint = new Set( + comments?.filter( + (comment) => + trailingComments.has(comment) && + !printed?.has(comment) && + filter(comment), + ), + ); + + if (commentsShouldPrint.size === 0) { + return ""; + } + + const docs = []; let printedTrailingComment; - path.each(() => { - const comment = path.node; - if (ignored?.has(comment)) { + path.each(({ node: comment }) => { + if (!trailingComments.has(comment)) { return; } - const { leading, trailing } = comment; - if (leading) { - leadingParts.push(printLeadingComment(path, options)); - } else if (trailing) { - printedTrailingComment = printTrailingComment( - path, - options, - printedTrailingComment, - ); - trailingParts.push(printedTrailingComment.doc); + printedTrailingComment = printTrailingComment( + path, + options, + printedTrailingComment, + ); + + if (commentsShouldPrint.has(comment)) { + docs.push(printedTrailingComment.doc); } }, "comments"); - return { leading: leadingParts, trailing: trailingParts }; + return docs; } -function printComments(path, doc, options) { - const { leading, trailing } = printCommentsSeparately(path, options); - if (!leading && !trailing) { - return doc; - } - return inheritLabel(doc, (doc) => [leading, doc, trailing]); +/** +@param {CommentPrintOptions} [printOptions] +@returns {{leading: Doc, trailing: Doc}} +*/ +function printCommentsSeparately(path, options, printOptions) { + return { + leading: printLeadingComments(path, options, printOptions), + trailing: printTrailingComments(path, options, printOptions), + }; +} + +/** +@param {CommentPrintOptions} [printOptions] +@returns {Doc} +*/ +function printComments(path, doc, options, printOptions) { + const leading = printLeadingComments(path, options, printOptions); + const trailing = printTrailingComments(path, options, printOptions); + return leading || trailing + ? inheritLabel(doc, (doc) => [leading, doc, trailing]) + : doc; } function ensureAllCommentsPrinted(options) { @@ -231,4 +276,5 @@ export { printComments, printCommentsSeparately, printDanglingComments, + printLeadingComments, };