Repository: sveltejs/prettier-plugin-svelte
Pull request #221 (2021-05-10 08:40:38)

(fix) better print of long attribute values with mustache tags


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a1bc713..17bec53 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
 
 * (fix) adjust snip regex to not break commented-out script/style tags ([#212](https://github.com/sveltejs/prettier-plugin-svelte/issues/212))
 * (fix) Don't snip style tag that is inside script tag ([#219](https://github.com/sveltejs/prettier-plugin-svelte/issues/219), [#70](https://github.com/sveltejs/prettier-plugin-svelte/issues/70))
+* (fix) Better formatting of long attribute values with mustache tags inbetween ([#221](https://github.com/sveltejs/prettier-plugin-svelte/pull/221))
 
 ## 2.2.0
 
diff --git a/src/print/helpers.ts b/src/print/helpers.ts
index 2cff58e..35192da 100644
--- a/src/print/helpers.ts
+++ b/src/print/helpers.ts
@@ -1,5 +1,5 @@
 import { ASTNode, Node } from './nodes';
-import { FastPath } from 'prettier';
+import { Doc, FastPath } from 'prettier';
 import { formattableAttributes } from '../lib/elements';
 
 /**
@@ -33,3 +33,18 @@ export function findLastIndex<T>(isMatch: (item: T, idx: number) => boolean, ite
 
     return -1;
 }
+
+export function replaceEndOfLineWith(text: string, replacement: Doc) {
+    const parts: Doc[] = [];
+    for (const part of text.split('\n')) {
+        if (parts.length > 0) {
+            parts.push(replacement);
+        }
+        if (part.endsWith('\r')) {
+            parts.push(part.slice(0, -1));
+        } else {
+            parts.push(part);
+        }
+    }
+    return parts;
+}
diff --git a/src/print/index.ts b/src/print/index.ts
index 077af9a..4d2ab62 100644
--- a/src/print/index.ts
+++ b/src/print/index.ts
@@ -5,7 +5,7 @@ import { getText } from '../lib/getText';
 import { hasSnippedContent, unsnipContent } from '../lib/snipTagContent';
 import { parseSortOrder, SortOrderPart } from '../options';
 import { isEmptyDoc, isLine, trim, trimRight } from './doc-helpers';
-import { flatten, isASTNode, isPreTagContent } from './helpers';
+import { flatten, isASTNode, isPreTagContent, replaceEndOfLineWith } from './helpers';
 import {
     checkWhitespaceAtEndOfSvelteBlock,
     checkWhitespaceAtStartOfSvelteBlock,
@@ -159,7 +159,13 @@ export function print(path: FastPath, options: ParserOptions, print: PrintFn): D
                  */
                 return fill(splitTextToDocs(node));
             } else {
-                return getUnencodedText(node);
+                const rawText = getUnencodedText(node);
+                if (path.getParentNode().type === 'Attribute') {
+                    // Direct child of attribute value -> add literallines at end of lines
+                    // so that other things don't break in unexpected places
+                    return concat(replaceEndOfLineWith(rawText, literalline));
+                }
+                return rawText;
             }
         case 'Element':
         case 'InlineComponent':
diff --git a/src/print/node-helpers.ts b/src/print/node-helpers.ts
index 8c8aa7e..ca25ba3 100644
--- a/src/print/node-helpers.ts
+++ b/src/print/node-helpers.ts
@@ -19,6 +19,7 @@ import {
     PendingBlockNode,
     ThenBlockNode,
     CommentNode,
+    SlotTemplateNode,
 } from './nodes';
 import { blockElements, TagName } from '../lib/elements';
 import { FastPath, ParserOptions } from 'prettier';
@@ -149,7 +150,14 @@ export function isIgnoreDirective(node: Node | undefined | null): boolean {
 }
 
 export function printRaw(
-    node: ElementNode | InlineComponentNode | SlotNode | WindowNode | HeadNode | TitleNode,
+    node:
+        | ElementNode
+        | InlineComponentNode
+        | SlotNode
+        | WindowNode
+        | HeadNode
+        | TitleNode
+        | SlotTemplateNode,
     originalText: string,
     stripLeadingAndTrailingNewline: boolean = false,
 ): string {
diff --git a/src/print/nodes.ts b/src/print/nodes.ts
index 6b0f5fc..7f33043 100644
--- a/src/print/nodes.ts
+++ b/src/print/nodes.ts
@@ -257,6 +257,7 @@ export interface SlotTemplateNode extends BaseNode {
     type: 'SlotTemplate';
     name: string;
     attributes: Node[];
+    children: Node[];
 }
 
 export type Node =


Test patch:
diff --git a/test/printer/samples/attributes-long-text-with-mustache.html b/test/printer/samples/attributes-long-text-with-mustache.html
new file mode 100644
index 0000000..8cd42c6
--- /dev/null
+++ b/test/printer/samples/attributes-long-text-with-mustache.html
@@ -0,0 +1,10 @@
+<button
+    style="color: {color};font-size: {fontSize};--bg-color: {bgColor}; --bg-hover: {bgHover};
+    --bg-active: {bgActive};--radius: {round};--ripple: {rippleColor};height: {height};width: {width};min-width: {width};
+    --shadow: {shadows[shadow]};
+    --shadow-h: {shadows[shadowHover]};
+    --shadow-a: {shadows[shadowActive]};
+    --opacity: {disabled ? 0.5 : 1.0}
+    
+       I'm unaffected"
+/>