From 7cf524fa0396e4f18fc91a57b9fcf8a82252d651 Mon Sep 17 00:00:00 2001 From: Georgii Dolzhykov Date: Mon, 18 Jan 2021 15:50:52 +0200 Subject: [PATCH] Smarter formatting of HTML class attribute (#7865) * html: break class attr onto multiple lines, but try keeping consecutive classes with the same prefix on one line * add changelog * add more tests * improve doc structure to fix an issue with the closing quote * Update changelog_unreleased/html/pr-7865.md Co-Authored-By: Jed Fox * handle classes with leading _ and - * update snapshots * Lint * Update pr-7865.md * Update changelog entry Co-authored-by: Jed Fox Co-authored-by: fisker Cheung --- changelog_unreleased/html/7865.md | 22 ++ src/language-html/syntax-attribute.js | 46 +++- .../angular/__snapshots__/jsfmt.spec.js.snap | 233 ++++++++++++---- .../__snapshots__/jsfmt.spec.js.snap | 260 +++++++++++++++++- tests/html/attributes/class-bem1.html | 10 + tests/html/attributes/class-bem2.html | 21 ++ .../html/attributes/class-leading-dashes.html | 3 + .../attributes/class-many-short-names.html | 4 + .../attributes/class-print-width-edge.html | 2 + .../tags/__snapshots__/jsfmt.spec.js.snap | 26 +- 10 files changed, 561 insertions(+), 66 deletions(-) create mode 100644 changelog_unreleased/html/7865.md create mode 100644 tests/html/attributes/class-bem1.html create mode 100644 tests/html/attributes/class-bem2.html create mode 100644 tests/html/attributes/class-leading-dashes.html create mode 100644 tests/html/attributes/class-many-short-names.html create mode 100644 tests/html/attributes/class-print-width-edge.html diff --git a/changelog_unreleased/html/7865.md b/changelog_unreleased/html/7865.md new file mode 100644 index 000000000..95a225f04 --- /dev/null +++ b/changelog_unreleased/html/7865.md @@ -0,0 +1,22 @@ +#### Prefix-based multiline formatting for the `class` attribute (#7865 by @thorn0) + +Formatting of HTML class names will now keep classes on one line until the line length limit is reached; at that point, consecutive classes with the same prefix will be grouped together on each line. For layout frameworks such as Bootstrap and Tailwind CSS, which add many classes to an element, this is important for readability and maintainability vs. the previous behavior (keeping all classes on one line) or e.g. putting each class on its own line. + + +```html + +
+ + +
+``` diff --git a/src/language-html/syntax-attribute.js b/src/language-html/syntax-attribute.js index bc2802a32..3180ae3b2 100644 --- a/src/language-html/syntax-attribute.js +++ b/src/language-html/syntax-attribute.js @@ -2,7 +2,7 @@ const parseSrcset = require("parse-srcset"); const { - builders: { ifBreak, join, line }, + builders: { group, ifBreak, indent, join, line, softline }, } = require("../document"); function printImgSrcset(value) { @@ -59,8 +59,50 @@ function printImgSrcset(value) { ); } +const prefixDelimiters = ["__", "--", "_", "-"]; + +function getClassPrefix(className) { + const startIndex = className.search(/[^_-]/); + if (startIndex !== -1) { + for (const delimiter of prefixDelimiters) { + const delimiterIndex = className.indexOf(delimiter, startIndex); + if (delimiterIndex !== -1) { + return className.slice(0, delimiterIndex); + } + } + } + return className; +} + function printClassNames(value) { - return value.trim().split(/\s+/).join(" "); + const classNames = value.trim().split(/\s+/); + + // Try keeping consecutive classes with the same prefix on one line. + const groupedByPrefix = []; + let previousPrefix; + for (let i = 0; i < classNames.length; i++) { + const prefix = getClassPrefix(classNames[i]); + if ( + prefix !== previousPrefix && + // "home-link" and "home-link_blue_yes" should be considered same-prefix + prefix !== classNames[i - 1] + ) { + groupedByPrefix.push([]); + } + groupedByPrefix[groupedByPrefix.length - 1].push(classNames[i]); + previousPrefix = prefix; + } + + return [ + indent([ + softline, + join( + line, + groupedByPrefix.map((classNames) => group(join(line, classNames))) + ), + ]), + softline, + ]; } module.exports = { diff --git a/tests/angular/angular/__snapshots__/jsfmt.spec.js.snap b/tests/angular/angular/__snapshots__/jsfmt.spec.js.snap index 2eb34a77d..437a6d4fb 100644 --- a/tests/angular/angular/__snapshots__/jsfmt.spec.js.snap +++ b/tests/angular/angular/__snapshots__/jsfmt.spec.js.snap @@ -110,7 +110,9 @@ printWidth: 1
{{ copyTypes[ @@ -8051,7 +8062,9 @@ can be found in the LICENSE file at http://angular.io/license

top @@ -8072,7 +8085,9 @@ can be found in the LICENSE file at http://angular.io/license [hidden]="isUnchanged")

{{ title @@ -8119,7 +8134,9 @@ can be found in the LICENSE file at http://angular.io/license (keyup)=" (0) " - class="context" + class=" + context + " > Type something: @@ -8132,7 +8149,9 @@ can be found in the LICENSE file at http://angular.io/license
top @@ -8154,7 +8173,9 @@ can be found in the LICENSE file at http://angular.io/license )

top @@ -8428,7 +8463,9 @@ can be found in the LICENSE file at http://angular.io/license /> top @@ -8505,7 +8542,9 @@ can be found in the LICENSE file at http://angular.io/license top @@ -8667,7 +8706,9 @@ can be found in the LICENSE file at http://angular.io/license

top @@ -8761,7 +8802,9 @@ can be found in the LICENSE file at http://angular.io/license
top @@ -8777,7 +8820,11 @@ can be found in the LICENSE file at http://angular.io/license
Bad curly @@ -8786,7 +8833,11 @@ can be found in the LICENSE file at http://angular.io/license
top @@ -8890,7 +8945,9 @@ can be found in the LICENSE file at http://angular.io/license top @@ -8964,7 +9021,9 @@ can be found in the LICENSE file at http://angular.io/license
Click me @@ -9022,7 +9083,9 @@ can be found in the LICENSE file at http://angular.io/license
top @@ -9079,7 +9142,9 @@ can be found in the LICENSE file at http://angular.io/license
top @@ -9151,7 +9216,9 @@ bindon-ngModel (ngModelChange)="setUppercaseName($event)" top @@ -9275,7 +9342,11 @@ bindon-ngModel
Bad curly @@ -9293,7 +9364,9 @@ bindon-ngModel
top @@ -9426,7 +9499,9 @@ bindon-ngModel
top @@ -9554,7 +9629,9 @@ bindon-ngModel
top @@ -9569,7 +9646,9 @@ bindon-ngModel
top @@ -9618,7 +9701,9 @@ bindon-ngModel separator

top @@ -9709,7 +9798,9 @@ bindon-ngModel trackBy

top @@ -9986,7 +10089,9 @@ bindon-ngModel
top @@ -10054,7 +10159,9 @@ bindon-ngModel > top @@ -10107,7 +10214,9 @@ bindon-ngModel }} top @@ -10185,7 +10294,9 @@ bindon-ngModel
top @@ -10282,7 +10393,9 @@ See console log:
top @@ -10319,7 +10432,9 @@ See console log:
top @@ -10371,7 +10486,9 @@ See console log:
top @@ -10433,7 +10550,9 @@ See console log:

top @@ -10456,13 +10575,17 @@ can be found in the LICENSE file at http://angular.io/license #heroForm="ngForm" >
+ +
+
+
+
+ +=====================================output===================================== +
+ Foo +
+ +
+
+
+
+
+ +================================================================================ +`; + +exports[`class-bem2.html format 1`] = ` +====================================options===================================== +parsers: ["html"] +printWidth: 80 + | printWidth +=====================================input====================================== +
+
+

+ ... +

+
+
+ +=====================================output===================================== +
+
+

+ ... +

+
+
+ +================================================================================ +`; + +exports[`class-leading-dashes.html format 1`] = ` +====================================options===================================== +parsers: ["html"] +printWidth: 80 + | printWidth +=====================================input====================================== + + + + +=====================================output===================================== + + + + +================================================================================ +`; + +exports[`class-many-short-names.html format 1`] = ` +====================================options===================================== +parsers: ["html"] +printWidth: 80 + | printWidth +=====================================input====================================== + + +=====================================output===================================== + + +================================================================================ +`; + exports[`class-names.html format 1`] = ` ====================================options===================================== parsers: ["html"] @@ -296,11 +469,41 @@ width="205px" alt="Yahoo"/>
=====================================output===================================== - + @@ -313,10 +516,34 @@ width="205px" alt="Yahoo"/>

================================================================================ `; +exports[`class-print-width-edge.html format 1`] = ` +====================================options===================================== +parsers: ["html"] +printWidth: 80 + | printWidth +=====================================input====================================== + + +=====================================output===================================== + + +================================================================================ +`; + exports[`dobule-quotes.html format 1`] = ` ====================================options===================================== parsers: ["html"] diff --git a/tests/html/attributes/class-bem1.html b/tests/html/attributes/class-bem1.html new file mode 100644 index 000000000..66eba1c9b --- /dev/null +++ b/tests/html/attributes/class-bem1.html @@ -0,0 +1,10 @@ +
Foo
+ +
+
+
+
diff --git a/tests/html/attributes/class-bem2.html b/tests/html/attributes/class-bem2.html new file mode 100644 index 000000000..b1fa92d9d --- /dev/null +++ b/tests/html/attributes/class-bem2.html @@ -0,0 +1,21 @@ +
+
+

+ ... +

+
+
diff --git a/tests/html/attributes/class-leading-dashes.html b/tests/html/attributes/class-leading-dashes.html new file mode 100644 index 000000000..3520dc338 --- /dev/null +++ b/tests/html/attributes/class-leading-dashes.html @@ -0,0 +1,3 @@ + + + diff --git a/tests/html/attributes/class-many-short-names.html b/tests/html/attributes/class-many-short-names.html new file mode 100644 index 000000000..ac51d5aa1 --- /dev/null +++ b/tests/html/attributes/class-many-short-names.html @@ -0,0 +1,4 @@ + diff --git a/tests/html/attributes/class-print-width-edge.html b/tests/html/attributes/class-print-width-edge.html new file mode 100644 index 000000000..ecd71dd96 --- /dev/null +++ b/tests/html/attributes/class-print-width-edge.html @@ -0,0 +1,2 @@ + diff --git a/tests/html/tags/__snapshots__/jsfmt.spec.js.snap b/tests/html/tags/__snapshots__/jsfmt.spec.js.snap index 5e7a41abb..77ee9accb 100644 --- a/tests/html/tags/__snapshots__/jsfmt.spec.js.snap +++ b/tests/html/tags/__snapshots__/jsfmt.spec.js.snap @@ -3316,14 +3316,18 @@ printWidth: 1
    123
  • First
  • 456
  • Second
  • @@ -3465,13 +3469,25 @@ printWidth: 1