diff --git a/src/language-js/comments/attach/handle-while-like-comments.js b/src/language-js/comments/attach/handle-while-like-comments.js new file mode 100644 index 000000000..427810899 --- /dev/null +++ b/src/language-js/comments/attach/handle-while-like-comments.js @@ -0,0 +1,60 @@ +import { + addLeadingComment, + addTrailingComment, +} from "../../../main/comments/utilities.js"; +import getNextNonSpaceNonCommentCharacter from "../../../utilities/get-next-non-space-non-comment-character.js"; +import { locEnd } from "../../loc.js"; +import { addBlockStatementFirstComment } from "./utilities.js"; + +/** +@import {CommentContext} from "../handle-comments.js" +*/ + +/** +@param {CommentContext} param0 +*/ +function handleWhileLikeComments({ + comment, + precedingNode, + enclosingNode, + followingNode, + text, +}) { + if ( + !( + (enclosingNode?.type === "WhileStatement" || + enclosingNode?.type === "WithStatement") && + followingNode + ) + ) { + return false; + } + + // We unfortunately have no way using the AST or location of nodes to know + // if the comment is positioned before the condition parenthesis: + // while (a /* comment */) {} + // The only workaround I found is to look at the next character to see if + // it is a ). + const nextCharacter = getNextNonSpaceNonCommentCharacter( + text, + locEnd(comment), + ); + if (nextCharacter === ")") { + addTrailingComment(precedingNode, comment); + return true; + } + + if (followingNode.type === "BlockStatement") { + addBlockStatementFirstComment(followingNode, comment); + return true; + } + + if (enclosingNode.body === followingNode) { + addLeadingComment(followingNode, comment); + return true; + } + + return false; +} + +export { handleWhileLikeComments }; diff --git a/src/language-js/comments/attach/utilities.js b/src/language-js/comments/attach/utilities.js new file mode 100644 index 000000000..cefb97fdb --- /dev/null +++ b/src/language-js/comments/attach/utilities.js @@ -0,0 +1,38 @@ +import { + addDanglingComment, + addLeadingComment, +} from "../../../main/comments/utilities.js"; + +/** +@import {Node, Comment, NodeMap} from "../../types/estree.js"; +*/ + +/** + * @param {Node} node + * @returns {void} + */ +function addBlockOrNotComment(node, comment) { + if (node.type === "BlockStatement") { + addBlockStatementFirstComment(node, comment); + } else { + addLeadingComment(node, comment); + } +} + +/** + * @param {Node} node + * @returns {void} + */ +function addBlockStatementFirstComment(node, comment) { + // @ts-expect-error + const firstNonEmptyNode = (node.body || node.properties).find( + ({ type }) => type !== "EmptyStatement", + ); + if (firstNonEmptyNode) { + addLeadingComment(firstNonEmptyNode, comment); + } else { + addDanglingComment(node, comment); + } +} + +export { addBlockOrNotComment, addBlockStatementFirstComment }; diff --git a/src/language-js/comments/handle-comments.js b/src/language-js/comments/handle-comments.js index 3a1177721..eabb4eb12 100644 --- a/src/language-js/comments/handle-comments.js +++ b/src/language-js/comments/handle-comments.js @@ -29,6 +29,11 @@ import { isUnionType, } from "../utilities/node-types.js"; import { stripComments } from "../utilities/strip-comments.js"; +import { handleWhileLikeComments } from "./attach/handle-while-like-comments.js"; +import { + addBlockOrNotComment, + addBlockStatementFirstComment, +} from "./attach/utilities.js"; /** @import {Node, Comment, NodeMap} from "../types/estree.js"; @@ -69,7 +74,7 @@ function handleOwnLineComment(context) { handleLastFunctionParameterComments, handleMemberExpressionComments, handleIfStatementComments, - handleWhileComments, + handleWhileLikeComments, handleTryStatementComments, handleClassComments, handleForComments, @@ -99,7 +104,7 @@ function handleEndOfLineComment(context) { handleConditionalExpressionComments, handleModuleSpecifiersComments, handleIfStatementComments, - handleWhileComments, + handleWhileLikeComments, handleTryStatementComments, handleClassComments, handleLabeledStatementComments, @@ -126,7 +131,7 @@ function handleRemainingComment(context) { handleCommentInEmptyParens, handleIgnoreComments, handleIfStatementComments, - handleWhileComments, + handleWhileLikeComments, handleMethodNameComments, handleOnlyComments, handleTSMappedTypeComments, @@ -137,34 +142,6 @@ function handleRemainingComment(context) { ].some((fn) => fn(context)); } -/** - * @param {Node} node - * @returns {void} - */ -function addBlockStatementFirstComment(node, comment) { - // @ts-expect-error - const firstNonEmptyNode = (node.body || node.properties).find( - ({ type }) => type !== "EmptyStatement", - ); - if (firstNonEmptyNode) { - addLeadingComment(firstNonEmptyNode, comment); - } else { - addDanglingComment(node, comment); - } -} - -/** - * @param {Node} node - * @returns {void} - */ -function addBlockOrNotComment(node, comment) { - if (node.type === "BlockStatement") { - addBlockStatementFirstComment(node, comment); - } else { - addLeadingComment(node, comment); - } -} - function handleClosureTypeCastComments({ comment, followingNode }) { if (followingNode && isTypeCastComment(comment)) { addLeadingComment(followingNode, comment); @@ -318,44 +295,6 @@ function handleIfStatementComments({ return false; } -function handleWhileComments({ - comment, - precedingNode, - enclosingNode, - followingNode, - text, -}) { - if (enclosingNode?.type !== "WhileStatement" || !followingNode) { - return false; - } - - // We unfortunately have no way using the AST or location of nodes to know - // if the comment is positioned before the condition parenthesis: - // while (a /* comment */) {} - // The only workaround I found is to look at the next character to see if - // it is a ). - const nextCharacter = getNextNonSpaceNonCommentCharacter( - text, - locEnd(comment), - ); - if (nextCharacter === ")") { - addTrailingComment(precedingNode, comment); - return true; - } - - if (followingNode.type === "BlockStatement") { - addBlockStatementFirstComment(followingNode, comment); - return true; - } - - if (enclosingNode.body === followingNode) { - addLeadingComment(followingNode, comment); - return true; - } - - return false; -} - // Same as IfStatement but for TryStatement function handleTryStatementComments({ comment, diff --git a/src/language-js/print/do-while-statement.js b/src/language-js/print/do-while-statement.js new file mode 100644 index 000000000..281757f39 --- /dev/null +++ b/src/language-js/print/do-while-statement.js @@ -0,0 +1,22 @@ +import { group, hardline } from "../../document/index.js"; +import { + adjustClause, + printDoWhileStatementCondition, +} from "./miscellaneous.js"; + +function printDoWhileStatement(path, options, print) { + const { body } = path.node; + const clause = adjustClause(body, print("body")); + const doBody = group(["do", clause]); + + return [ + doBody, + body.type === "BlockStatement" ? " " : hardline, + "while (", + printDoWhileStatementCondition(path, options, print), + ")", + options.semi ? ";" : "", + ]; +} + +export { printDoWhileStatement }; diff --git a/src/language-js/print/estree.js b/src/language-js/print/estree.js index 175f6a689..2be2e53b2 100644 --- a/src/language-js/print/estree.js +++ b/src/language-js/print/estree.js @@ -32,6 +32,7 @@ import { printClassMethod, printClassProperty, } from "./class.js"; +import { printDoWhileStatement } from "./do-while-statement.js"; import { printExpressionStatement } from "./expression-statement.js"; import { printForStatement } from "./for-statement.js"; import { printFunction, printMethod } from "./function.js"; @@ -66,10 +67,7 @@ import { printTernary } from "./ternary.js"; import { printCatchClause, printTryStatement } from "./try-statement.js"; import { printTypeAnnotationProperty } from "./type-annotation.js"; import { printVariableDeclaration } from "./variable-declaration.js"; -import { - printDoWhileStatement, - printWhileStatement, -} from "./while-statement.js"; +import { printWhileStatement } from "./while-statement.js"; /** * @import AstPath from "../../common/ast-path.js" @@ -253,17 +251,11 @@ function printEstree(path, options, print, args) { return printTernary(path, options, print, args); case "VariableDeclaration": return printVariableDeclaration(path, options, print); - case "WithStatement": - return group([ - "with (", - print("object"), - ")", - adjustClause(node.body, print("body")), - ]); case "IfStatement": return printIfStatement(path, options, print); case "ForStatement": return printForStatement(path, options, print); + case "WithStatement": case "WhileStatement": return printWhileStatement(path, options, print); case "DoWhileStatement": diff --git a/src/language-js/print/miscellaneous.js b/src/language-js/print/miscellaneous.js index 9f2c9157c..ccd93e620 100644 --- a/src/language-js/print/miscellaneous.js +++ b/src/language-js/print/miscellaneous.js @@ -168,10 +168,12 @@ function shouldInlineCondition(node) { return node.type === "LogicalExpression"; } -function printIfOrWhileCondition(path, options, print) { - const conditionDoc = print("test"); +function printIfOrWhileConditionOrWithStatementObject(path, options, print) { + const { node } = path; + const property = node.type === "WithStatement" ? "object" : "test"; + const conditionDoc = print(property); - if (shouldInlineCondition(path.node.test)) { + if (shouldInlineCondition(node[property])) { return conditionDoc; } @@ -213,10 +215,10 @@ export { printDanglingCommentsInList, printDeclareToken, printDefiniteToken, - printIfOrWhileCondition as printDoWhileStatementCondition, - printIfOrWhileCondition as printIfStatementCondition, + printIfOrWhileConditionOrWithStatementObject as printDoWhileStatementCondition, + printIfOrWhileConditionOrWithStatementObject as printIfStatementCondition, printOptionalToken, printTrailingComma, printTypeScriptAccessibilityToken, - printIfOrWhileCondition as printWhileStatementCondition, + printIfOrWhileConditionOrWithStatementObject as printWhileStatementCondition, }; diff --git a/src/language-js/print/while-statement.js b/src/language-js/print/while-statement.js index cb7888876..33151e39e 100644 --- a/src/language-js/print/while-statement.js +++ b/src/language-js/print/while-statement.js @@ -1,32 +1,17 @@ -import { group, hardline } from "../../document/index.js"; -import { - adjustClause, - printDoWhileStatementCondition, - printWhileStatementCondition, -} from "./miscellaneous.js"; +import { group } from "../../document/index.js"; +import { adjustClause, printWhileStatementCondition } from "./miscellaneous.js"; function printWhileStatement(path, options, print) { + const { node } = path; + const keyword = node.type === "WithStatement" ? "with" : "while"; + return group([ - "while (", + keyword, + " (", printWhileStatementCondition(path, options, print), ")", - adjustClause(path.node.body, print("body")), + adjustClause(node.body, print("body")), ]); } -function printDoWhileStatement(path, options, print) { - const { body } = path.node; - const clause = adjustClause(body, print("body")); - const doBody = group(["do", clause]); - - return [ - doBody, - body.type === "BlockStatement" ? " " : hardline, - "while (", - printDoWhileStatementCondition(path, options, print), - ")", - options.semi ? ";" : "", - ]; -} - -export { printDoWhileStatement, printWhileStatement }; +export { printWhileStatement }; diff --git a/tests/format/js/comments/__snapshots__/format.test.js.snap b/tests/format/js/comments/__snapshots__/format.test.js.snap index 25c73b34e..442cfdc99 100644 --- a/tests/format/js/comments/__snapshots__/format.test.js.snap +++ b/tests/format/js/comments/__snapshots__/format.test.js.snap @@ -7403,128 +7403,3 @@ const foo3 = 123; ================================================================================ `; - -exports[`while.js - {"semi":false} format 1`] = ` -====================================options===================================== -parsers: ["babel", "flow", "typescript"] -printWidth: 80 -semi: false - | printWidth -=====================================input====================================== -while( - true - // Comment - ) {} - -while(true)// Comment -{} - -while(true){}// Comment - -while(true)/*Comment*/{} - -while( - true // Comment - && true // Comment - ){} - -while(true) {} // comment - -while(true) /* comment */ ++x; - -while(1) // Comment - foo(); - -=====================================output===================================== -while ( - true - // Comment -) {} - -while (true) { - // Comment -} - -while (true) {} // Comment - -while (true) { - /*Comment*/ -} - -while ( - true && // Comment - true // Comment -) {} - -while (true) {} // comment - -while (true) /* comment */ ++x - -while (1) - // Comment - foo() - -================================================================================ -`; - -exports[`while.js format 1`] = ` -====================================options===================================== -parsers: ["babel", "flow", "typescript"] -printWidth: 80 - | printWidth -=====================================input====================================== -while( - true - // Comment - ) {} - -while(true)// Comment -{} - -while(true){}// Comment - -while(true)/*Comment*/{} - -while( - true // Comment - && true // Comment - ){} - -while(true) {} // comment - -while(true) /* comment */ ++x; - -while(1) // Comment - foo(); - -=====================================output===================================== -while ( - true - // Comment -) {} - -while (true) { - // Comment -} - -while (true) {} // Comment - -while (true) { - /*Comment*/ -} - -while ( - true && // Comment - true // Comment -) {} - -while (true) {} // comment - -while (true) /* comment */ ++x; - -while (1) - // Comment - foo(); - -================================================================================ -`; diff --git a/tests/format/js/comments/while-like/__snapshots__/format.test.js.snap b/tests/format/js/comments/while-like/__snapshots__/format.test.js.snap new file mode 100644 index 000000000..247ea68c9 --- /dev/null +++ b/tests/format/js/comments/while-like/__snapshots__/format.test.js.snap @@ -0,0 +1,129 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`while.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "flow", "typescript"] +printWidth: 80 + | printWidth +=====================================input====================================== +while( + true + // 1 + ) {} + +while(true)// 2 +{} + +while(true){}// 3 + +while(true)/*4*/{} + +while( + true // 5 + && true // 52 + ){} + +while(true) {} // 6 + +while(true) /* 7 */ ++x; + +while(1) // 8 + foo(); + +=====================================output===================================== +while ( + true + // 1 +) {} + +while (true) { + // 2 +} + +while (true) {} // 3 + +while (true) { + /*4*/ +} + +while ( + true && // 5 + true // 52 +) {} + +while (true) {} // 6 + +while (true) /* 7 */ ++x; + +while (1) + // 8 + foo(); + +================================================================================ +`; + +exports[`with.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "flow", "typescript"] +printWidth: 80 + | printWidth +=====================================input====================================== +with( + foo + // 1 + ) {} + +with(foo)// 2 +{} + +with(foo){}// 3 + +with(foo)/*4*/{} + +with( + foo // 5 + ?? bar // 52 + ){} + +with(foo) {} // 6 + +with(foo) /* 7 */ ++x; + +with(1) // 8 + foo(); + +=====================================output===================================== +with ( + foo + // 1 +) { +} + +with (foo) { + // 2 +} + +with (foo) { +} // 3 + +with (foo) { + /*4*/ +} + +with ( + foo ?? // 5 + bar // 52 +) { +} + +with (foo) { +} // 6 + +with (foo) /* 7 */ ++x; + +with (1) + // 8 + foo(); + +================================================================================ +`; diff --git a/tests/format/js/comments/while-like/format.test.js b/tests/format/js/comments/while-like/format.test.js new file mode 100644 index 000000000..67c251cce --- /dev/null +++ b/tests/format/js/comments/while-like/format.test.js @@ -0,0 +1 @@ +runFormatTest(import.meta, ["babel", "flow", "typescript"]); diff --git a/tests/format/js/comments/while-like/while.js b/tests/format/js/comments/while-like/while.js new file mode 100644 index 000000000..666bf0e57 --- /dev/null +++ b/tests/format/js/comments/while-like/while.js @@ -0,0 +1,23 @@ +while( + true + // 1 + ) {} + +while(true)// 2 +{} + +while(true){}// 3 + +while(true)/*4*/{} + +while( + true // 5 + && true // 52 + ){} + +while(true) {} // 6 + +while(true) /* 7 */ ++x; + +while(1) // 8 + foo(); diff --git a/tests/format/js/comments/while-like/with.js b/tests/format/js/comments/while-like/with.js new file mode 100644 index 000000000..08fa325a7 --- /dev/null +++ b/tests/format/js/comments/while-like/with.js @@ -0,0 +1,23 @@ +with( + foo + // 1 + ) {} + +with(foo)// 2 +{} + +with(foo){}// 3 + +with(foo)/*4*/{} + +with( + foo // 5 + ?? bar // 52 + ){} + +with(foo) {} // 6 + +with(foo) /* 7 */ ++x; + +with(1) // 8 + foo(); diff --git a/tests/format/js/comments/while.js b/tests/format/js/comments/while.js deleted file mode 100644 index e63395a57..000000000 --- a/tests/format/js/comments/while.js +++ /dev/null @@ -1,23 +0,0 @@ -while( - true - // Comment - ) {} - -while(true)// Comment -{} - -while(true){}// Comment - -while(true)/*Comment*/{} - -while( - true // Comment - && true // Comment - ){} - -while(true) {} // comment - -while(true) /* comment */ ++x; - -while(1) // Comment - foo();