diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index 7bc30503..3aa72410 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -1037,7 +1037,50 @@ class A { }} ``` -[#5682]: https://github.com/prettier/prettier/pull/5682 +#### Handlebars: Fix handling of whitespace and line breaks ([#6354] by [@chadian]) + +This fixes a variety of whitespace and line break usecases within handlebars and Glimmer templates. + + +```hbs +// Input +{{name}} + +Some sentence with {{dynamic}} expressions. + + + +sometimes{{nogaps}}areimportant +{{name}} is your name + +// Output (Prettier stable) + +{{name}} +Some sentence with +{{dynamic}} +expressions. + + + +sometimes +{{nogaps}} +areimportant + +{{name}} +is your name + +// Output (Prettier master) +{{name}} + +Some sentence with {{dynamic}} expressions. + + + +sometimes{{nogaps}}areimportant + +{{name}} is your name +``` + [#5910]: https://github.com/prettier/prettier/pull/5910 [#6033]: https://github.com/prettier/prettier/pull/6033 [#6186]: https://github.com/prettier/prettier/pull/6186 @@ -1087,3 +1130,4 @@ class A { [@squidfunk]: https://github.com/squidfunk [@vjeux]: https://github.com/vjeux [@selvazhagan]: https://github.com/selvazhagan +[@chadian]: https://github.com/chadian diff --git a/src/language-handlebars/printer-glimmer.js b/src/language-handlebars/printer-glimmer.js index d559b6c9..55631ea7 100644 --- a/src/language-handlebars/printer-glimmer.js +++ b/src/language-handlebars/printer-glimmer.js @@ -43,19 +43,11 @@ function printChildren(path, options, print) { const isWhitespace = isWhitespaceNode(childNode); if (isWhitespace && isLastNodeInMultiNodeList) { - return concat([print(childPath, options, print)]); - } else if ( - isFirstNode || - isPreviousNodeOfSomeType(childPath, [ - "ElementNode", - "CommentStatement", - "MustacheCommentStatement", - "BlockStatement" - ]) - ) { + return print(childPath, options, print); + } else if (isFirstNode) { return concat([softline, print(childPath, options, print)]); } - return concat([print(childPath, options, print)]); + return print(childPath, options, print); }, "children") ); } @@ -72,9 +64,7 @@ function print(path, options, print) { case "Block": case "Program": case "Template": { - return group( - join(softline, path.map(print, "body").filter(text => text !== "")) - ); + return group(concat(path.map(print, "body").filter(text => text !== ""))); } case "ElementNode": { const tagFirstChar = n.tag[0]; @@ -82,8 +72,14 @@ function print(path, options, print) { const isGlimmerComponent = tagFirstChar.toUpperCase() === tagFirstChar || isLocal; const hasChildren = n.children.length > 0; + + const hasNonWhitespaceChildren = n.children.some( + n => !isWhitespaceNode(n) + ); + const isVoid = - (isGlimmerComponent && !hasChildren) || voidTags.indexOf(n.tag) !== -1; + (isGlimmerComponent && (!hasChildren || !hasNonWhitespaceChildren)) || + voidTags.indexOf(n.tag) !== -1; const closeTagForNoBreak = isVoid ? concat([" />", softline]) : ">"; const closeTagForBreak = isVoid ? "/>" : ">"; const getParams = (path, print) => @@ -100,6 +96,8 @@ function print(path, options, print) { ]) ); + const nextNode = getNextNode(path); + return concat([ group( concat([ @@ -111,13 +109,18 @@ function print(path, options, print) { ifBreak(closeTagForBreak, closeTagForNoBreak) ]) ), - group( - concat([ - indent(printChildren(path, options, print)), - ifBreak(hasChildren ? hardline : "", ""), - !isVoid ? concat([""]) : "" - ]) - ) + !isVoid + ? group( + concat([ + hasNonWhitespaceChildren + ? indent(printChildren(path, options, print)) + : "", + ifBreak(hasChildren ? hardline : "", ""), + concat([""]) + ]) + ) + : "", + nextNode && nextNode.type === "ElementNode" ? hardline : "" ]); } case "BlockStatement": { @@ -156,6 +159,7 @@ function print(path, options, print) { const hasNonWhitespaceChildren = n.program.body.some( n => !isWhitespaceNode(n) ); + return concat([ printOpenBlock(path, print), group( @@ -196,7 +200,9 @@ function print(path, options, print) { return concat([n.name]); } const value = path.call(print, "value"); - const quotedValue = isText ? printStringLiteral(value, options) : value; + const quotedValue = isText + ? printStringLiteral(value.parts.join(), options) + : value; return concat([n.name, "=", quotedValue]); } case "ConcatStatement": { @@ -222,25 +228,54 @@ function print(path, options, print) { return concat([n.key, "=", path.call(print, "value")]); } case "TextNode": { + const maxLineBreaksToPreserve = 2; + const isFirstElement = !getPreviousNode(path); + const isLastElement = !getNextNode(path); const isWhitespaceOnly = !/\S/.test(n.chars); + const lineBreaksCount = countNewLines(n.chars); + const hasBlockParent = path.getParentNode(0).type === "Block"; + const hasElementParent = path.getParentNode(0).type === "ElementNode"; + const hasTemplateParent = path.getParentNode(0).type === "Template"; + + let leadingLineBreaksCount = countLeadingNewLines(n.chars); + let trailingLineBreaksCount = countTrailingNewLines(n.chars); if ( + (isFirstElement || isLastElement) && isWhitespaceOnly && - isPreviousNodeOfSomeType(path, ["MustacheStatement", "TextNode"]) + (hasBlockParent || hasElementParent || hasTemplateParent) ) { - return " "; + return ""; + } + + if (isWhitespaceOnly && lineBreaksCount) { + leadingLineBreaksCount = Math.min( + lineBreaksCount, + maxLineBreaksToPreserve + ); + trailingLineBreaksCount = 0; + } else { + if ( + isNextNodeOfType(path, "ElementNode") || + isNextNodeOfType(path, "BlockStatement") + ) { + trailingLineBreaksCount = Math.max(trailingLineBreaksCount, 1); + } + + if ( + isPreviousNodeOfSomeType(path, ["ElementNode"]) || + isPreviousNodeOfSomeType(path, ["BlockStatement"]) + ) { + leadingLineBreaksCount = Math.max(leadingLineBreaksCount, 1); + } } let leadingSpace = ""; let trailingSpace = ""; - if (isNextNodeOfType(path, "MustacheStatement")) { - trailingSpace = " "; - } - - // preserve a space inside of an attribute node where whitespace present, when next to mustache statement. + // preserve a space inside of an attribute node where whitespace present, + // when next to mustache statement. const inAttrNode = path.stack.indexOf("attributes") >= 0; - if (inAttrNode) { const parentNode = path.getParentNode(0); const isConcat = parentNode.type === "ConcatStatement"; @@ -262,10 +297,41 @@ function print(path, options, print) { } } } + } else { + if ( + trailingLineBreaksCount === 0 && + isNextNodeOfType(path, "MustacheStatement") + ) { + trailingSpace = " "; + } + + if ( + leadingLineBreaksCount === 0 && + isPreviousNodeOfSomeType(path, ["MustacheStatement"]) + ) { + leadingSpace = " "; + } + + if (isFirstElement) { + leadingLineBreaksCount = 0; + leadingSpace = ""; + } + + if (isLastElement) { + trailingLineBreaksCount = 0; + trailingSpace = ""; + } } - return n.chars - .replace(/^\s+/, leadingSpace) - .replace(/\s+$/, trailingSpace); + + return concat( + [ + ...generateHardlines(leadingLineBreaksCount, maxLineBreaksToPreserve), + n.chars + .replace(/^[\s ]+/g, leadingSpace) + .replace(/[\s ]+$/, trailingSpace), + ...generateHardlines(trailingLineBreaksCount, maxLineBreaksToPreserve) + ].filter(Boolean) + ); } case "MustacheCommentStatement": { const dashes = n.value.indexOf("}}") > -1 ? "--" : ""; @@ -405,7 +471,7 @@ function getPreviousNode(path) { const node = path.getValue(); const parentNode = path.getParentNode(0); - const children = parentNode.children; + const children = parentNode.children || parentNode.body; if (children) { const nodeIndex = children.indexOf(node); if (nodeIndex > 0) { @@ -419,7 +485,7 @@ function getNextNode(path) { const node = path.getValue(); const parentNode = path.getParentNode(0); - const children = parentNode.children; + const children = parentNode.children || parentNode.body; if (children) { const nodeIndex = children.indexOf(node); if (nodeIndex < children.length) { @@ -445,6 +511,7 @@ function isNextNodeOfType(path, type) { function clean(ast, newObj) { delete newObj.loc; + delete newObj.selfClosing; // (Glimmer/HTML) ignore TextNode whitespace if (ast.type === "TextNode") { @@ -455,6 +522,30 @@ function clean(ast, newObj) { } } +function countNewLines(string) { + /* istanbul ignore next */ + string = typeof string === "string" ? string : ""; + return string.split("\n").length - 1; +} + +function countLeadingNewLines(string) { + /* istanbul ignore next */ + string = typeof string === "string" ? string : ""; + const newLines = (string.match(/^([^\S\r\n]*[\r\n])+/g) || [])[0] || ""; + return countNewLines(newLines); +} + +function countTrailingNewLines(string) { + /* istanbul ignore next */ + string = typeof string === "string" ? string : ""; + const newLines = (string.match(/([\r\n][^\S\r\n]*)+$/g) || [])[0] || ""; + return countNewLines(newLines); +} + +function generateHardlines(number = 0, max = 0) { + return new Array(Math.min(number, max)).fill(hardline); +} + module.exports = { print, massageAstNode: clean diff --git a/tests/glimmer/preserved-spaces-and-breaks.hbs b/tests/glimmer/preserved-spaces-and-breaks.hbs new file mode 100644 index 00000000..5c4fb885 --- /dev/null +++ b/tests/glimmer/preserved-spaces-and-breaks.hbs @@ -0,0 +1,44 @@ + +{{name}} + + + +Some sentence with {{dynamic}} expressions. + +sometimes{{nogaps}}areimportant + + +{{name}} is your name + + +{{#block}} +{{/block}} + + +{{#block}} + some {{text}} +{{/block}} + +{{#block}} + + some {{text}} +{{/block}} + + + + + + some {{text}} + + + + + some {{text}} + + +
+
+ +
+ +
diff --git a/tests/handlebars-basics/__snapshots__/jsfmt.spec.js.snap b/tests/handlebars-basics/__snapshots__/jsfmt.spec.js.snap index 54bb4c9a..fbaf980c 100644 --- a/tests/handlebars-basics/__snapshots__/jsfmt.spec.js.snap +++ b/tests/handlebars-basics/__snapshots__/jsfmt.spec.js.snap @@ -28,6 +28,7 @@ printWidth: 80 +
================================================================================ `; @@ -61,6 +62,7 @@ singleQuote: true +
================================================================================ `; @@ -99,28 +101,33 @@ printWidth: 80 =====================================output===================================== -{{@greeting}} -, -{{@name}} -! +{{@greeting}}, {{@name}}! +
+
hello + + + + + +
Hello
@@ -162,28 +169,33 @@ singleQuote: true =====================================output===================================== -{{@greeting}} -, -{{@name}} -! +{{@greeting}}, {{@name}}! +
+
hello + + + + + +
Hello
@@ -264,6 +276,7 @@ printWidth: 80

By {{author.name}}

+
{{body}}
@@ -295,6 +308,7 @@ singleQuote: true

By {{author.name}}

+
{{body}}
diff --git a/tests/handlebars-block-statement/__snapshots__/jsfmt.spec.js.snap b/tests/handlebars-block-statement/__snapshots__/jsfmt.spec.js.snap index 65e4cbb5..6c7aeb52 100644 --- a/tests/handlebars-block-statement/__snapshots__/jsfmt.spec.js.snap +++ b/tests/handlebars-block-statement/__snapshots__/jsfmt.spec.js.snap @@ -63,11 +63,13 @@ printWidth: 80 {{#block param hashKey=hashValue as |blockParam|}} Hello {{/block}} + {{#block almost80CharacterLongPositionalParamThatIsFirstAlmost80Chars helloWorldParam key=here }}{{/block}} + {{#block param param @@ -80,6 +82,7 @@ printWidth: 80 }} Hello {{/block}} + {{#block param param @@ -93,6 +96,7 @@ printWidth: 80 }} Hello {{/block}} + {{#block param param @@ -110,6 +114,7 @@ printWidth: 80 }} Hello {{/block}} + {{#block hashKey=HashValue hashKey=hashValue @@ -119,27 +124,33 @@ printWidth: 80 }} Hello {{/block}} + {{#block}} {{#block}} hello {{/block}} {{/block}} + {{#block}} {{#block param}} hello {{/block}} {{/block}} + {{#block param}} {{#block param}} hello {{/block}} {{/block}} + {{#block}} hello {{/block}} + {{firstName}} + {{firstName}} {{lastName}} @@ -210,11 +221,13 @@ singleQuote: true {{#block param hashKey=hashValue as |blockParam|}} Hello {{/block}} + {{#block almost80CharacterLongPositionalParamThatIsFirstAlmost80Chars helloWorldParam key=here }}{{/block}} + {{#block param param @@ -227,6 +240,7 @@ singleQuote: true }} Hello {{/block}} + {{#block param param @@ -240,6 +254,7 @@ singleQuote: true }} Hello {{/block}} + {{#block param param @@ -257,6 +272,7 @@ singleQuote: true }} Hello {{/block}} + {{#block hashKey=HashValue hashKey=hashValue @@ -266,27 +282,33 @@ singleQuote: true }} Hello {{/block}} + {{#block}} {{#block}} hello {{/block}} {{/block}} + {{#block}} {{#block param}} hello {{/block}} {{/block}} + {{#block param}} {{#block param}} hello {{/block}} {{/block}} + {{#block}} hello {{/block}} + {{firstName}} + {{firstName}} {{lastName}} @@ -471,6 +493,7 @@ printWidth: 80 Go to bed! {{/if}} +

{{#if a}} A @@ -478,6 +501,7 @@ printWidth: 80 B {{/if}}

+ {{#if a}} b {{else if c}} @@ -485,6 +509,7 @@ printWidth: 80 {{else}} e {{/if}} + {{#if a}} b {{else if c}} @@ -496,6 +521,7 @@ printWidth: 80 {{/if}} e {{/if}} + {{#if a}} b {{else if c}} @@ -507,6 +533,7 @@ printWidth: 80 {{else}} j {{/if}} +
{{#if a}} b @@ -516,6 +543,7 @@ printWidth: 80 e {{/if}}
+
{{#if a}} @@ -527,6 +555,7 @@ printWidth: 80 {{/if}}
+ {{#if a}} b {{else}} @@ -534,6 +563,7 @@ printWidth: 80 e {{/each}} {{/if}} + {{#if a}} {{#if b}} ab @@ -541,6 +571,7 @@ printWidth: 80 ac {{/if}} {{/if}} + {{#if a}} a
@@ -684,6 +715,7 @@ singleQuote: true Go to bed! {{/if}} +

{{#if a}} A @@ -691,6 +723,7 @@ singleQuote: true B {{/if}}

+ {{#if a}} b {{else if c}} @@ -698,6 +731,7 @@ singleQuote: true {{else}} e {{/if}} + {{#if a}} b {{else if c}} @@ -709,6 +743,7 @@ singleQuote: true {{/if}} e {{/if}} + {{#if a}} b {{else if c}} @@ -720,6 +755,7 @@ singleQuote: true {{else}} j {{/if}} +
{{#if a}} b @@ -729,6 +765,7 @@ singleQuote: true e {{/if}}
+
{{#if a}} @@ -740,6 +777,7 @@ singleQuote: true {{/if}}
+ {{#if a}} b {{else}} @@ -747,6 +785,7 @@ singleQuote: true e {{/each}} {{/if}} + {{#if a}} {{#if b}} ab @@ -754,6 +793,7 @@ singleQuote: true ac {{/if}} {{/if}} + {{#if a}} a
diff --git a/tests/handlebars-comment/__snapshots__/jsfmt.spec.js.snap b/tests/handlebars-comment/__snapshots__/jsfmt.spec.js.snap index 0f1f94ce..619dac97 100644 --- a/tests/handlebars-comment/__snapshots__/jsfmt.spec.js.snap +++ b/tests/handlebars-comment/__snapshots__/jsfmt.spec.js.snap @@ -30,11 +30,13 @@ printWidth: 80 {{#if @foo}} Foo {{/if}} + {{! Bar }} {{#if @bar}} Bar {{/if}}
+
{{! This comment will not be in the output }} {{!-- This comment as }} and will not be in the output --}} diff --git a/tests/handlebars-concat-statement/__snapshots__/jsfmt.spec.js.snap b/tests/handlebars-concat-statement/__snapshots__/jsfmt.spec.js.snap index f256f7b9..4be7aaf8 100644 --- a/tests/handlebars-concat-statement/__snapshots__/jsfmt.spec.js.snap +++ b/tests/handlebars-concat-statement/__snapshots__/jsfmt.spec.js.snap @@ -30,6 +30,7 @@ printWidth: 80
Hello
+
+
@@ -89,6 +92,7 @@ singleQuote: true
Hello
+
+
diff --git a/tests/handlebars-element-node/__snapshots__/jsfmt.spec.js.snap b/tests/handlebars-element-node/__snapshots__/jsfmt.spec.js.snap index dc7672c5..f7914071 100644 --- a/tests/handlebars-element-node/__snapshots__/jsfmt.spec.js.snap +++ b/tests/handlebars-element-node/__snapshots__/jsfmt.spec.js.snap @@ -47,29 +47,37 @@ printWidth: 80
Hello
+
Hello
+
hi
+
A long enough string to trigger a line break that would prevent wrapping.
+
A long enough string to trigger a line break that would prevent wrapping more.
+
A long enough string to trigger a line break that would prevent wrapping more and more.
+
{{#block}} {{hello}} {{/block}}
+
{{hello}}
+
================================================================================ @@ -123,29 +131,37 @@ singleQuote: true
Hello
+
Hello
+
hi
+
A long enough string to trigger a line break that would prevent wrapping.
+
A long enough string to trigger a line break that would prevent wrapping more.
+
A long enough string to trigger a line break that would prevent wrapping more and more.
+
{{#block}} {{hello}} {{/block}}
+
{{hello}}
+
================================================================================ diff --git a/tests/handlebars-mustache-statement/__snapshots__/jsfmt.spec.js.snap b/tests/handlebars-mustache-statement/__snapshots__/jsfmt.spec.js.snap index 1f3254ee..13a4e586 100644 --- a/tests/handlebars-mustache-statement/__snapshots__/jsfmt.spec.js.snap +++ b/tests/handlebars-mustache-statement/__snapshots__/jsfmt.spec.js.snap @@ -26,6 +26,7 @@ printWidth: 80
Hello
+
Hello
+
Hello
+
Hello
+
Hello
+
Hello
+
- Welcome to the + Welcome to the + Ember.js Guides . This documentation will take you from total beginner to Ember expert.

+ {{! newlines text }}
hi @@ -95,39 +97,52 @@ printWidth: 80 are you fine today?
+ {{! newlines text spaced }}
space above space below
+ {{! newlines elems spaced }}
space above + space below
+ {{! newlines mixed }}
- hi + hi + there + how - are + are + you + are you fine today?
+ {{! newlines elems }}
- hi
+ + hi + +
+
================================================================================ diff --git a/tests/handlebars-sub-expression/__snapshots__/jsfmt.spec.js.snap b/tests/handlebars-sub-expression/__snapshots__/jsfmt.spec.js.snap index b22c2c8d..9b29346f 100644 --- a/tests/handlebars-sub-expression/__snapshots__/jsfmt.spec.js.snap +++ b/tests/handlebars-sub-expression/__snapshots__/jsfmt.spec.js.snap @@ -63,6 +63,7 @@ printWidth: 80 ) }} >
+ {{#block (concat (service) @@ -84,6 +85,7 @@ printWidth: 80 hashPair=(does not need a line break due to being under 80 chars long) ) }}{{/block}} + {{foobar-sub-component/foobar-foo hook="stringLiteral" foo=(t @@ -160,6 +162,7 @@ singleQuote: true ) }} >
+ {{#block (concat (service) @@ -181,6 +184,7 @@ singleQuote: true hashPair=(does not need a line break due to being under 80 chars long) ) }}{{/block}} + {{foobar-sub-component/foobar-foo hook='stringLiteral' foo=(t diff --git a/tests/handlebars-text-wrap/__snapshots__/jsfmt.spec.js.snap b/tests/handlebars-text-wrap/__snapshots__/jsfmt.spec.js.snap index 31a65197..4fc3c28e 100644 --- a/tests/handlebars-text-wrap/__snapshots__/jsfmt.spec.js.snap +++ b/tests/handlebars-text-wrap/__snapshots__/jsfmt.spec.js.snap @@ -157,6 +157,34 @@ printWidth: 80
Some text that would need to wrap on to a new line in order to display correctly and nicely
+ +{{! Wrapping tags }} +
+ + f + + + + f + + + + f + + + + f + + + + f + + + + f + +
+ {{! Wrapping tags }}
@@ -178,121 +206,125 @@ printWidth: 80 f
-{{! Wrapping tags }} -
- - f - - - f - - - f - - - f - - - f - - - f - -
+ {{! Wrapping tags }}
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + f
+
- before
+ before +
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur at mollis lorem.
after
+
before{stuff}after{stuff}after{stuff}after{stuff}after{stuff}after{stuff}{stuff}{stuff}after{stuff}after
+
- before {{stuff}}after {{stuff}}after {{stuff}}after {{stuff}}after {{stuff - }}after {{stuff}} {{stuff}} {{stuff}}after {{stuff}}after + before {{stuff}} after {{stuff}} after {{stuff}} after {{stuff}} after {{stuff + }} after {{stuff}} {{stuff}} {{stuff}} after {{stuff}} after
+
- Please state your + Please state your + name - and + and + occupation - for the board of + for the board of + school directors.
+
- First second third
Something
+
First
- Second
+ Second +
Third
+
- First
+ First +
Second
Third
+ {{! leading whitespace }}
First Second Third Fourth Fifth Sixth Seventh Eighth Ninth Tenth Eleventh Twelfth Thirteenth Fourteenth
+ {{! trailing whitespace }}
First Second Third Fourth Fifth Sixth Seventh Eighth Ninth Tenth Eleventh Twelfth Thirteenth Fourteenth
+ {{! no leading or trailing whitespace }}
First Second Third Fourth Fifth Sixth Seventh Eighth Ninth Tenth Eleventh Twelfth Thirteenth Fourteenth
+ {{! translation leave text around tag }}
First , - ( + ( + Second )
+
First second third fourth fifth sixth seventh , - ( + ( + Second )
+ {{! this really should split across lines }}
before{{stuff}}after{{stuff}}after{{stuff}}after{{stuff}}after{{stuff }}after{{stuff}}after{{stuff}}after{{stuff}}after{{stuff}}after{{stuff }}after{{stuff}}after{{stuff}}after{{stuff}}after{{stuff}}after
+ {{! solitary whitespace }}
-
+ {{! whitespace on newline }}
First
+
Second
+
Third
+ {{! around multiline element }}
- Before
+ Before +
{"Enough text to make this element wrap on to multiple lines when formatting"}
After
+ {{! around multiline element second pass }}
- Before{" "}
+ Before{" "} +
{ "Enough text to make this element wrap on to multiple lines when formatting" } @@ -333,34 +371,45 @@ printWidth: 80 {" "} After
+ {{! dont preserve blank lines when contains text }}
Zeroth
+
First
+ Second
+ {{! multiple expressions }}
- {{header}} {{body}} {{footer}} + {{header}} + {{body}} + {{footer}}
+ {{! single expression child tags }}
- You currently have + You currently have + {{dashboardStr}} - and + and + {{userStr}}
+ {{! expression does not break }}
texty text text text text text text text text text text text {{this.props.type }}
+ // FIXME ================================================================================ `; diff --git a/tests/handlebars-whitespace/__snapshots__/jsfmt.spec.js.snap b/tests/handlebars-whitespace/__snapshots__/jsfmt.spec.js.snap index 9dbdbda3..3ebddb6c 100644 --- a/tests/handlebars-whitespace/__snapshots__/jsfmt.spec.js.snap +++ b/tests/handlebars-whitespace/__snapshots__/jsfmt.spec.js.snap @@ -36,10 +36,12 @@ printWidth: 40 =====================================output===================================== {{! after }} - foo + foo + bar + {{! before }} @@ -47,17 +49,21 @@ printWidth: 40 foo + {{! within }} - foo + foo + bar + {{! break components }}

- foo + foo + bar bar bar

@@ -73,10 +79,13 @@ printWidth: 40 nope
+
- hello + hello + hi + sdkflsdfjk @@ -110,31 +119,27 @@ hey =====================================output=====================================

Hi {{firstName}} {{lastName - }}, welcome! + }} , welcome!

{{#component propA}} - for{{propB}}do{{propC}}f + for {{propB}} do {{propC}} f {{/component}} {{#component propA}} - for - {{propB}} + for {{propB}} name - do - {{propC}} - f + do {{propC}} f {{/component}} -{{propA}} -{{propB}} -{{propC}} -{{propD}} +{{propA}} {{propB}} +{{propC}}{{propD}} {{propE}} {{propF}} {{propG}}{{propH}} + hey ================================================================================ `; @@ -294,22 +299,22 @@ conubia nostra, per inceptos himenaeos. Donec in ornare velit.

vestibulum facilisis in porta turpis. Ut faucibus lectus sit amet urna consectetur dignissim. Sam vitae neque quis ex dapibus faucibus at sed ligula. Nulla sit amet aliquet nibh. Vestibulum at congue mi. Suspendisse vitae odio vitae massa hendrerit mattis sed eget dui. -Sed eu scelerisque neque. Donec +Sed eu scelerisque neque. Donec + maximus rhoncus pellentesque. Aenean purus turpis, vehicula euismod ante vel, ultricies eleifend dui. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec in ornare velit.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce cursus massa vel augue vestibulum facilisis in porta turpis. Ut faucibus lectus sit amet urna consectetur dignissim. Sam vitae neque quis ex dapibus faucibus at sed ligula. Nulla sit amet aliquet nibh. Vestibulum at congue mi. Suspendisse vitae odio vitae massa hendrerit mattis sed eget dui. -Sed eu scelerisque neque. Donec +Sed eu scelerisque neque. Donec + maximus @@ -365,10 +370,10 @@ printWidth: 40 This is your name: {{name}}. - This is your name: {{name}}(employee) + This is your name: {{name}} (employee) - This is your name: {{name}}({{role}}) + This is your name: {{name}} ({{role}}) ================================================================================ `; @@ -410,6 +415,7 @@ printWidth: 40 123 +

@@ -468,40 +474,44 @@ printWidth: 40 - - - - - - - - -
- A - - B - - C -
- - - - - - - - -
- A - - B - - C -
- - - +
+ + + + + + + +
+ A + + B + + C +
+ + + + + + + + + + + +
+ A + + B + + C +
+ + + +