diff --git a/changelog_unreleased/angular/16922-2.md b/changelog_unreleased/angular/16922-2.md
new file mode 100644
index 000000000..446699da2
--- /dev/null
+++ b/changelog_unreleased/angular/16922-2.md
@@ -0,0 +1,27 @@
+#### Improve line breaks inside ICU blocks (#16922 by @fisker)
+
+
+```html
+
+The author is {gender, select, male {male} female {female} other {other}}
+The author is male consectetur adipiscing elit, sed do eiusmod
+
+
+The author is {gender, select, male {male} female {female} other {other}
+}
+The author is
+ male consectetur adipiscing elit, sed do eiusmod
+
+
+The author is
+ {gender, select, male {male} female {female} other {other}}
+The author is
+ male consectetur adipiscing elit, sed do eiusmod
+```
diff --git a/changelog_unreleased/angular/16922.md b/changelog_unreleased/angular/16922.md
new file mode 100644
index 000000000..4c70bcff5
--- /dev/null
+++ b/changelog_unreleased/angular/16922.md
@@ -0,0 +1,36 @@
+#### Fix extra new line inside ICU blocks (#16922 by @fisker)
+
+
+```html
+
+{active, select,
+ true {
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp
+ }
+ false {
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp
+ }
+}
+
+
+{active, select,
+ true {
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp
+
+ }
+ false {
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp
+
+ }
+}
+
+
+{active, select,
+ true {
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp
+ }
+ false {
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp
+ }
+}
+```
diff --git a/src/language-html/ast.js b/src/language-html/ast.js
index bdcaf18b3..144471e01 100644
--- a/src/language-html/ast.js
+++ b/src/language-html/ast.js
@@ -11,6 +11,9 @@ const NON_ENUMERABLE_PROPERTIES = new Set(["parent"]);
// https://github.com/microsoft/TypeScript/issues/26811
class Node {
+ type;
+ parent;
+
constructor(nodeOrProperties = {}) {
for (const property of new Set([
...NON_ENUMERABLE_PROPERTIES,
@@ -51,7 +54,6 @@ class Node {
const mappedNodes = mapNodesIfChanged(nodes, (node) => node.map(fn));
if (newNode !== nodes) {
if (!newNode) {
- // @ts-expect-error
newNode = new Node({ parent: this.parent });
}
newNode.setProperty(NODES_KEY, mappedNodes);
@@ -96,21 +98,16 @@ class Node {
* @param {Object} [node]
*/
insertChildBefore(target, node) {
- // @ts-expect-error
- this.children.splice(
- // @ts-expect-error
- this.children.indexOf(target),
- 0,
- this.createChild(node),
- );
+ const children = this.$children;
+ children.splice(children.indexOf(target), 0, this.createChild(node));
}
/**
* @param {Node} [child]
*/
removeChild(child) {
- // @ts-expect-error
- this.children.splice(this.children.indexOf(child), 1);
+ const children = this.$children;
+ children.splice(children.indexOf(child), 1);
}
/**
@@ -118,32 +115,57 @@ class Node {
* @param {Object} [node]
*/
replaceChild(target, node) {
- // @ts-expect-error
- this.children[this.children.indexOf(target)] = this.createChild(node);
+ const children = this.$children;
+ children[children.indexOf(target)] = this.createChild(node);
}
clone() {
return new Node(this);
}
+ get #childrenProperty() {
+ if (this.type === "angularIcuCase") {
+ return "expression";
+ }
+
+ if (this.type === "angularIcuExpression") {
+ return "cases";
+ }
+
+ return "children";
+ }
+
+ // Use `$` prefix since `children` already exits in the original AST,
+ // Can't use `#children` either, since it need be public
+ // There are other children in different Node, see `#childrenProperty`
+ get $children() {
+ return this[this.#childrenProperty];
+ }
+
+ set $children(value) {
+ this[this.#childrenProperty] = value;
+ }
+
get firstChild() {
- // @ts-expect-error
- return this.children?.[0];
+ return this.$children?.[0];
}
get lastChild() {
- // @ts-expect-error
- return this.children?.[this.children.length - 1];
+ return this.$children?.at(-1);
+ }
+
+ get #siblings() {
+ return this.parent?.$children ?? [];
}
get prev() {
- // @ts-expect-error
- return this.parent?.children?.[this.parent.children.indexOf(this) - 1];
+ const siblings = this.#siblings;
+ return siblings[siblings.indexOf(this) - 1];
}
get next() {
- // @ts-expect-error
- return this.parent?.children?.[this.parent.children.indexOf(this) + 1];
+ const siblings = this.#siblings;
+ return siblings[siblings.indexOf(this) + 1];
}
// for element and attribute
diff --git a/src/language-html/print-preprocess.js b/src/language-html/print-preprocess.js
index 9e6dc9300..f0284e583 100644
--- a/src/language-html/print-preprocess.js
+++ b/src/language-html/print-preprocess.js
@@ -28,6 +28,7 @@ function preprocess(ast, options) {
for (const fn of PREPROCESS_PIPELINE) {
fn(ast, options);
}
+
return ast;
}
@@ -253,18 +254,20 @@ function extractInterpolation(ast, options) {
*/
function extractWhitespaces(ast /*, options*/) {
ast.walk((node) => {
- if (!node.children) {
+ const children = node.$children;
+
+ if (!children) {
return;
}
if (
- node.children.length === 0 ||
- (node.children.length === 1 &&
- node.children[0].type === "text" &&
- htmlWhitespaceUtils.trim(node.children[0].value).length === 0)
+ children.length === 0 ||
+ (children.length === 1 &&
+ children[0].type === "text" &&
+ htmlWhitespaceUtils.trim(children[0].value).length === 0)
) {
- node.hasDanglingSpaces = node.children.length > 0;
- node.children = [];
+ node.hasDanglingSpaces = children.length > 0;
+ node.$children = [];
return;
}
@@ -272,8 +275,8 @@ function extractWhitespaces(ast /*, options*/) {
const isIndentationSensitive = isIndentationSensitiveNode(node);
if (!isWhitespaceSensitive) {
- for (let i = 0; i < node.children.length; i++) {
- const child = node.children[i];
+ for (let i = 0; i < children.length; i++) {
+ const child = children[i];
if (child.type !== "text") {
continue;
}
diff --git a/src/language-html/print/angular-icu-expression.js b/src/language-html/print/angular-icu-expression.js
index f66553217..7789d49da 100644
--- a/src/language-html/print/angular-icu-expression.js
+++ b/src/language-html/print/angular-icu-expression.js
@@ -5,7 +5,6 @@ import {
line,
softline,
} from "../../document/builders.js";
-import htmlWhitespaceUtils from "../../utils/html-whitespace-utils.js";
import { printClosingTagEnd, printOpeningTagStart } from "./tag.js";
/*
@@ -37,20 +36,27 @@ function printAngularIcuExpression(path, options, print) {
function printAngularIcuCase(path, options, print) {
const { node } = path;
+
return [
node.value,
" {",
group([
indent([
softline,
- path.map(({ node }) => {
- // FIXME: We need a fundamental solution.
- // Like this: https://github.com/prettier/prettier/blob/c1ebae2e8303841e59bc02e9e719a93e964ab0e9/src/language-html/print-preprocess.js#L254
- if (node.type === "text" && !htmlWhitespaceUtils.trim(node.value)) {
- return "";
+ path.map(({ node, isLast }) => {
+ const parts = [print()];
+
+ if (node.type === "text") {
+ if (node.hasLeadingSpaces) {
+ parts.unshift(line);
+ }
+
+ if (node.hasTrailingSpaces && !isLast) {
+ parts.push(line);
+ }
}
- return print();
+ return parts;
}, "expression"),
]),
softline,
diff --git a/src/language-html/printer-html.js b/src/language-html/printer-html.js
index 0fe924ad0..713e15e6d 100644
--- a/src/language-html/printer-html.js
+++ b/src/language-html/printer-html.js
@@ -92,6 +92,7 @@ function genericPrint(path, options, print) {
const prefix = printOpeningTagPrefix(node, options);
const printed = getTextValueParts(node);
+
const suffix = printClosingTagSuffix(node, options);
// We cant use `fill([prefix, printed, suffix])` because it violates rule of fill: elements with odd indices must be line break
printed[0] = [prefix, printed[0]];
diff --git a/tests/format/angular/icu-expression/__snapshots__/format.test.js.snap b/tests/format/angular/icu-expression/__snapshots__/format.test.js.snap
index b80c3ad4e..4e4c64665 100644
--- a/tests/format/angular/icu-expression/__snapshots__/format.test.js.snap
+++ b/tests/format/angular/icu-expression/__snapshots__/format.test.js.snap
@@ -15,8 +15,8 @@ printWidth: 80
=0 {just now}
=1 {one minute ago}
other {{{minutes}} minutes ago}
-
-}
+ }
================================================================================
`;
@@ -118,11 +118,13 @@ printWidth: 80
=0 {Found no results}
=1 {Found one result}
other {
- Found results
+ />
+ results
}
}
@@ -140,8 +142,9 @@ printWidth: 80
=====================================output=====================================
The author is {gender, select, male {male} female {female} other {other}
-}
+ >The author is
+ {gender, select, male {male} female {female} other {other}}
================================================================================
`;
@@ -185,14 +188,39 @@ printWidth: 80
=0 {just now}
=1 {one minute ago}
other {
- {{minutes}} minutes ago by {gender, select,
- male {male}
- female {female}
- other {other}
- }
+ {{minutes}} minutes ago by
+ {gender, select, male {male} female {female} other {other}}
}
}
================================================================================
`;
+
+exports[`issue-16914.html format 1`] = `
+====================================options=====================================
+parsers: ["angular"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+{active, select,
+ true {
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp
+ }
+ false {
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp
+ }
+}
+
+=====================================output=====================================
+{active, select,
+ true {
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp
+ }
+ false {
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp
+ }
+}
+
+================================================================================
+`;
diff --git a/tests/format/angular/icu-expression/issue-16914.html b/tests/format/angular/icu-expression/issue-16914.html
new file mode 100644
index 000000000..5a5020f18
--- /dev/null
+++ b/tests/format/angular/icu-expression/issue-16914.html
@@ -0,0 +1,8 @@
+{active, select,
+ true {
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp
+ }
+ false {
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod temp
+ }
+}