diff --git a/src/language-html/print/children.js b/src/language-html/print/children.js new file mode 100644 index 000000000..02d97a54a --- /dev/null +++ b/src/language-html/print/children.js @@ -0,0 +1,210 @@ +"use strict"; + +const { + builders: { breakParent, group, ifBreak, line, softline, hardline }, + utils: { replaceTextEndOfLine }, +} = require("../../document/index.js"); +const { locStart, locEnd } = require("../loc.js"); +const { + forceBreakChildren, + forceNextEmptyLine, + isTextLikeNode, + hasPrettierIgnore, + preferHardlineAsLeadingSpaces, +} = require("../utils.js"); +const { + printOpeningTagPrefix, + needsToBorrowNextOpeningTagStartMarker, + printOpeningTagStartMarker, + needsToBorrowPrevClosingTagEndMarker, + printClosingTagEndMarker, + printClosingTagSuffix, + needsToBorrowParentClosingTagStartMarker, +} = require("./tag.js"); + +function printChild(childPath, options, print) { + const child = childPath.getValue(); + + if (hasPrettierIgnore(child)) { + return [ + printOpeningTagPrefix(child, options), + ...replaceTextEndOfLine( + options.originalText.slice( + locStart(child) + + (child.prev && needsToBorrowNextOpeningTagStartMarker(child.prev) + ? printOpeningTagStartMarker(child).length + : 0), + locEnd(child) - + (child.next && needsToBorrowPrevClosingTagEndMarker(child.next) + ? printClosingTagEndMarker(child, options).length + : 0) + ) + ), + printClosingTagSuffix(child, options), + ]; + } + + return print(); +} + +function printBetweenLine(prevNode, nextNode) { + return isTextLikeNode(prevNode) && isTextLikeNode(nextNode) + ? prevNode.isTrailingSpaceSensitive + ? prevNode.hasTrailingSpaces + ? preferHardlineAsLeadingSpaces(nextNode) + ? hardline + : line + : "" + : preferHardlineAsLeadingSpaces(nextNode) + ? hardline + : softline + : (needsToBorrowNextOpeningTagStartMarker(prevNode) && + (hasPrettierIgnore(nextNode) || + /** + * 123 + */ + nextNode.firstChild || + /** + * 123 + */ + nextNode.isSelfClosing || + /** + * 123 0))) || + /** + * 123 + */ + (prevNode.type === "element" && + prevNode.isSelfClosing && + needsToBorrowPrevClosingTagEndMarker(nextNode)) + ? "" + : !nextNode.isLeadingSpaceSensitive || + preferHardlineAsLeadingSpaces(nextNode) || + /** + * Want to write us a letter? Use ourmailing address. + */ + (needsToBorrowPrevClosingTagEndMarker(nextNode) && + prevNode.lastChild && + needsToBorrowParentClosingTagStartMarker(prevNode.lastChild) && + prevNode.lastChild.lastChild && + needsToBorrowParentClosingTagStartMarker(prevNode.lastChild.lastChild)) + ? hardline + : nextNode.hasLeadingSpaces + ? line + : softline; +} + +function printChildren(path, options, print) { + const node = path.getValue(); + + if (forceBreakChildren(node)) { + return [ + breakParent, + + ...path.map((childPath) => { + const childNode = childPath.getValue(); + const prevBetweenLine = !childNode.prev + ? "" + : printBetweenLine(childNode.prev, childNode); + return [ + !prevBetweenLine + ? "" + : [ + prevBetweenLine, + forceNextEmptyLine(childNode.prev) ? hardline : "", + ], + printChild(childPath, options, print), + ]; + }, "children"), + ]; + } + + const groupIds = node.children.map(() => Symbol("")); + return path.map((childPath, childIndex) => { + const childNode = childPath.getValue(); + + if (isTextLikeNode(childNode)) { + if (childNode.prev && isTextLikeNode(childNode.prev)) { + const prevBetweenLine = printBetweenLine(childNode.prev, childNode); + if (prevBetweenLine) { + if (forceNextEmptyLine(childNode.prev)) { + return [hardline, hardline, printChild(childPath, options, print)]; + } + return [prevBetweenLine, printChild(childPath, options, print)]; + } + } + return printChild(childPath, options, print); + } + + const prevParts = []; + const leadingParts = []; + const trailingParts = []; + const nextParts = []; + + const prevBetweenLine = childNode.prev + ? printBetweenLine(childNode.prev, childNode) + : ""; + + const nextBetweenLine = childNode.next + ? printBetweenLine(childNode, childNode.next) + : ""; + + if (prevBetweenLine) { + if (forceNextEmptyLine(childNode.prev)) { + prevParts.push(hardline, hardline); + } else if (prevBetweenLine === hardline) { + prevParts.push(hardline); + } else { + if (isTextLikeNode(childNode.prev)) { + leadingParts.push(prevBetweenLine); + } else { + leadingParts.push( + ifBreak("", softline, { + groupId: groupIds[childIndex - 1], + }) + ); + } + } + } + + if (nextBetweenLine) { + if (forceNextEmptyLine(childNode)) { + if (isTextLikeNode(childNode.next)) { + nextParts.push(hardline, hardline); + } + } else if (nextBetweenLine === hardline) { + if (isTextLikeNode(childNode.next)) { + nextParts.push(hardline); + } + } else { + trailingParts.push(nextBetweenLine); + } + } + + return [ + ...prevParts, + group([ + ...leadingParts, + group([printChild(childPath, options, print), ...trailingParts], { + id: groupIds[childIndex], + }), + ]), + ...nextParts, + ]; + }, "children"); +} + +module.exports = { printChildren }; diff --git a/src/language-html/print/element.js b/src/language-html/print/element.js new file mode 100644 index 000000000..98f63c05a --- /dev/null +++ b/src/language-html/print/element.js @@ -0,0 +1,176 @@ +"use strict"; + +const { + builders: { + breakParent, + dedentToRoot, + group, + ifBreak, + indentIfBreak, + indent, + line, + softline, + }, + utils: { replaceTextEndOfLine }, +} = require("../../document/index.js"); +const getNodeContent = require("../get-node-content.js"); +const { + shouldPreserveContent, + isScriptLikeTag, + isVueCustomBlock, + countParents, + forceBreakContent, +} = require("../utils.js"); +const { + printOpeningTagPrefix, + printOpeningTag, + printClosingTagSuffix, + printClosingTag, + needsToBorrowPrevClosingTagEndMarker, + needsToBorrowLastChildClosingTagEndMarker, +} = require("./tag.js"); +const { printChildren } = require("./children.js"); + +function printElement(path, options, print) { + const node = path.getValue(); + + if (shouldPreserveContent(node, options)) { + return [ + printOpeningTagPrefix(node, options), + group(printOpeningTag(path, options, print)), + ...replaceTextEndOfLine(getNodeContent(node, options)), + ...printClosingTag(node, options), + printClosingTagSuffix(node, options), + ]; + } + /** + * do not break: + * + *
{{ + * ~ + * interpolation + * }}
+ * ~ + * + * exception: break if the opening tag breaks + * + *
{{ + * interpolation + * }}
+ */ + const shouldHugContent = + node.children.length === 1 && + node.firstChild.type === "interpolation" && + node.firstChild.isLeadingSpaceSensitive && + !node.firstChild.hasLeadingSpaces && + node.lastChild.isTrailingSpaceSensitive && + !node.lastChild.hasTrailingSpaces; + + const attrGroupId = Symbol("element-attr-group-id"); + + const printTag = (doc) => + group([ + group(printOpeningTag(path, options, print), { id: attrGroupId }), + doc, + printClosingTag(node, options), + ]); + + const printChildrenDoc = (childrenDoc) => { + if (shouldHugContent) { + return indentIfBreak(childrenDoc, { groupId: attrGroupId }); + } + if ( + (isScriptLikeTag(node) || isVueCustomBlock(node, options)) && + node.parent.type === "root" && + options.parser === "vue" && + !options.vueIndentScriptAndStyle + ) { + return childrenDoc; + } + return indent(childrenDoc); + }; + + const printLineBeforeChildren = () => { + if (shouldHugContent) { + return ifBreak(softline, "", { groupId: attrGroupId }); + } + if ( + node.firstChild.hasLeadingSpaces && + node.firstChild.isLeadingSpaceSensitive + ) { + return line; + } + if ( + node.firstChild.type === "text" && + node.isWhitespaceSensitive && + node.isIndentationSensitive + ) { + return dedentToRoot(softline); + } + return softline; + }; + + const printLineAfterChildren = () => { + const needsToBorrow = node.next + ? needsToBorrowPrevClosingTagEndMarker(node.next) + : needsToBorrowLastChildClosingTagEndMarker(node.parent); + if (needsToBorrow) { + if ( + node.lastChild.hasTrailingSpaces && + node.lastChild.isTrailingSpaceSensitive + ) { + return " "; + } + return ""; + } + if (shouldHugContent) { + return ifBreak(softline, "", { groupId: attrGroupId }); + } + if ( + node.lastChild.hasTrailingSpaces && + node.lastChild.isTrailingSpaceSensitive + ) { + return line; + } + if ( + (node.lastChild.type === "comment" || + (node.lastChild.type === "text" && + node.isWhitespaceSensitive && + node.isIndentationSensitive)) && + new RegExp( + `\\n[\\t ]{${ + options.tabWidth * + countParents( + path, + (node) => node.parent && node.parent.type !== "root" + ) + }}$` + ).test(node.lastChild.value) + ) { + return ""; + } + return softline; + }; + + if (node.children.length === 0) { + return printTag( + node.hasDanglingSpaces && node.isDanglingSpaceSensitive ? line : "" + ); + } + + return printTag([ + forceBreakContent(node) ? breakParent : "", + printChildrenDoc([ + printLineBeforeChildren(), + printChildren(path, options, print), + ]), + printLineAfterChildren(), + ]); +} + +module.exports = { printElement }; diff --git a/src/language-html/printer-html.js b/src/language-html/printer-html.js index 86e57437a..101dce2d1 100644 --- a/src/language-html/printer-html.js +++ b/src/language-html/printer-html.js @@ -5,34 +5,12 @@ */ const { - builders: { - breakParent, - dedentToRoot, - fill, - group, - hardline, - ifBreak, - indentIfBreak, - indent, - line, - literalline, - softline, - }, + builders: { fill, group, hardline, literalline }, utils: { cleanDoc, getDocParts, isConcat, replaceTextEndOfLine }, } = require("../document/index.js"); const clean = require("./clean.js"); const { countChars, - countParents, - forceBreakChildren, - forceBreakContent, - forceNextEmptyLine, - hasPrettierIgnore, - isVueCustomBlock, - isScriptLikeTag, - isTextLikeNode, - preferHardlineAsLeadingSpaces, - shouldPreserveContent, unescapeQuoteEntities, getTextValueParts, } = require("./utils.js"); @@ -41,20 +19,13 @@ const { insertPragma } = require("./pragma.js"); const { locStart, locEnd } = require("./loc.js"); const embed = require("./embed.js"); const { - printClosingTagEndMarker, printClosingTagSuffix, - printClosingTag, printClosingTagEnd, - needsToBorrowPrevClosingTagEndMarker, - needsToBorrowLastChildClosingTagEndMarker, - needsToBorrowParentClosingTagStartMarker, printOpeningTagPrefix, - printOpeningTag, printOpeningTagStart, - printOpeningTagStartMarker, - needsToBorrowNextOpeningTagStartMarker, } = require("./print/tag.js"); -const getNodeContent = require("./get-node-content.js"); +const { printElement } = require("./print/element.js"); +const { printChildren } = require("./print/children.js"); function genericPrint(path, options, print) { const node = path.getValue(); @@ -153,335 +124,6 @@ function genericPrint(path, options, print) { } } -function printElement(path, options, print) { - const node = path.getValue(); - - if (shouldPreserveContent(node, options)) { - return [ - printOpeningTagPrefix(node, options), - group(printOpeningTag(path, options, print)), - ...replaceTextEndOfLine(getNodeContent(node, options)), - ...printClosingTag(node, options), - printClosingTagSuffix(node, options), - ]; - } - /** - * do not break: - * - *
{{ - * ~ - * interpolation - * }}
- * ~ - * - * exception: break if the opening tag breaks - * - *
{{ - * interpolation - * }}
- */ - const shouldHugContent = - node.children.length === 1 && - node.firstChild.type === "interpolation" && - node.firstChild.isLeadingSpaceSensitive && - !node.firstChild.hasLeadingSpaces && - node.lastChild.isTrailingSpaceSensitive && - !node.lastChild.hasTrailingSpaces; - - const attrGroupId = Symbol("element-attr-group-id"); - - const printTag = (doc) => - group([ - group(printOpeningTag(path, options, print), { id: attrGroupId }), - doc, - printClosingTag(node, options), - ]); - - const printChildrenDoc = (childrenDoc) => { - if (shouldHugContent) { - return indentIfBreak(childrenDoc, { groupId: attrGroupId }); - } - if ( - (isScriptLikeTag(node) || isVueCustomBlock(node, options)) && - node.parent.type === "root" && - options.parser === "vue" && - !options.vueIndentScriptAndStyle - ) { - return childrenDoc; - } - return indent(childrenDoc); - }; - - const printLineBeforeChildren = () => { - if (shouldHugContent) { - return ifBreak(softline, "", { groupId: attrGroupId }); - } - if ( - node.firstChild.hasLeadingSpaces && - node.firstChild.isLeadingSpaceSensitive - ) { - return line; - } - if ( - node.firstChild.type === "text" && - node.isWhitespaceSensitive && - node.isIndentationSensitive - ) { - return dedentToRoot(softline); - } - return softline; - }; - - const printLineAfterChildren = () => { - const needsToBorrow = node.next - ? needsToBorrowPrevClosingTagEndMarker(node.next) - : needsToBorrowLastChildClosingTagEndMarker(node.parent); - if (needsToBorrow) { - if ( - node.lastChild.hasTrailingSpaces && - node.lastChild.isTrailingSpaceSensitive - ) { - return " "; - } - return ""; - } - if (shouldHugContent) { - return ifBreak(softline, "", { groupId: attrGroupId }); - } - if ( - node.lastChild.hasTrailingSpaces && - node.lastChild.isTrailingSpaceSensitive - ) { - return line; - } - if ( - (node.lastChild.type === "comment" || - (node.lastChild.type === "text" && - node.isWhitespaceSensitive && - node.isIndentationSensitive)) && - new RegExp( - `\\n[\\t ]{${ - options.tabWidth * - countParents( - path, - (node) => node.parent && node.parent.type !== "root" - ) - }}$` - ).test(node.lastChild.value) - ) { - return ""; - } - return softline; - }; - - if (node.children.length === 0) { - return printTag( - node.hasDanglingSpaces && node.isDanglingSpaceSensitive ? line : "" - ); - } - - return printTag([ - forceBreakContent(node) ? breakParent : "", - printChildrenDoc([ - printLineBeforeChildren(), - printChildren(path, options, print), - ]), - printLineAfterChildren(), - ]); -} - -function printChildren(path, options, print) { - const node = path.getValue(); - - if (forceBreakChildren(node)) { - return [ - breakParent, - - ...path.map((childPath) => { - const childNode = childPath.getValue(); - const prevBetweenLine = !childNode.prev - ? "" - : printBetweenLine(childNode.prev, childNode); - return [ - !prevBetweenLine - ? "" - : [ - prevBetweenLine, - forceNextEmptyLine(childNode.prev) ? hardline : "", - ], - printChild(childPath), - ]; - }, "children"), - ]; - } - - const groupIds = node.children.map(() => Symbol("")); - return path.map((childPath, childIndex) => { - const childNode = childPath.getValue(); - - if (isTextLikeNode(childNode)) { - if (childNode.prev && isTextLikeNode(childNode.prev)) { - const prevBetweenLine = printBetweenLine(childNode.prev, childNode); - if (prevBetweenLine) { - if (forceNextEmptyLine(childNode.prev)) { - return [hardline, hardline, printChild(childPath)]; - } - return [prevBetweenLine, printChild(childPath)]; - } - } - return printChild(childPath); - } - - const prevParts = []; - const leadingParts = []; - const trailingParts = []; - const nextParts = []; - - const prevBetweenLine = childNode.prev - ? printBetweenLine(childNode.prev, childNode) - : ""; - - const nextBetweenLine = childNode.next - ? printBetweenLine(childNode, childNode.next) - : ""; - - if (prevBetweenLine) { - if (forceNextEmptyLine(childNode.prev)) { - prevParts.push(hardline, hardline); - } else if (prevBetweenLine === hardline) { - prevParts.push(hardline); - } else { - if (isTextLikeNode(childNode.prev)) { - leadingParts.push(prevBetweenLine); - } else { - leadingParts.push( - ifBreak("", softline, { - groupId: groupIds[childIndex - 1], - }) - ); - } - } - } - - if (nextBetweenLine) { - if (forceNextEmptyLine(childNode)) { - if (isTextLikeNode(childNode.next)) { - nextParts.push(hardline, hardline); - } - } else if (nextBetweenLine === hardline) { - if (isTextLikeNode(childNode.next)) { - nextParts.push(hardline); - } - } else { - trailingParts.push(nextBetweenLine); - } - } - - return [ - ...prevParts, - group([ - ...leadingParts, - group([printChild(childPath), ...trailingParts], { - id: groupIds[childIndex], - }), - ]), - ...nextParts, - ]; - }, "children"); - - function printChild(childPath) { - const child = childPath.getValue(); - - if (hasPrettierIgnore(child)) { - return [ - printOpeningTagPrefix(child, options), - ...replaceTextEndOfLine( - options.originalText.slice( - locStart(child) + - (child.prev && needsToBorrowNextOpeningTagStartMarker(child.prev) - ? printOpeningTagStartMarker(child).length - : 0), - locEnd(child) - - (child.next && needsToBorrowPrevClosingTagEndMarker(child.next) - ? printClosingTagEndMarker(child, options).length - : 0) - ) - ), - printClosingTagSuffix(child, options), - ]; - } - - return print(); - } - - function printBetweenLine(prevNode, nextNode) { - return isTextLikeNode(prevNode) && isTextLikeNode(nextNode) - ? prevNode.isTrailingSpaceSensitive - ? prevNode.hasTrailingSpaces - ? preferHardlineAsLeadingSpaces(nextNode) - ? hardline - : line - : "" - : preferHardlineAsLeadingSpaces(nextNode) - ? hardline - : softline - : (needsToBorrowNextOpeningTagStartMarker(prevNode) && - (hasPrettierIgnore(nextNode) || - /** - * 123 - */ - nextNode.firstChild || - /** - * 123 - */ - nextNode.isSelfClosing || - /** - * 123 0))) || - /** - * 123 - */ - (prevNode.type === "element" && - prevNode.isSelfClosing && - needsToBorrowPrevClosingTagEndMarker(nextNode)) - ? "" - : !nextNode.isLeadingSpaceSensitive || - preferHardlineAsLeadingSpaces(nextNode) || - /** - * Want to write us a letter? Use ourmailing address. - */ - (needsToBorrowPrevClosingTagEndMarker(nextNode) && - prevNode.lastChild && - needsToBorrowParentClosingTagStartMarker(prevNode.lastChild) && - prevNode.lastChild.lastChild && - needsToBorrowParentClosingTagStartMarker( - prevNode.lastChild.lastChild - )) - ? hardline - : nextNode.hasLeadingSpaces - ? line - : softline; - } -} - module.exports = { preprocess, print: genericPrint,