From bfb09bf214fcdb0e47ed7cb1c702281396c61753 Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Sun, 2 Oct 2022 11:11:44 -0400 Subject: [PATCH] Format pseudo-selector args like a function call (#13577) --- changelog_unreleased/css/13577.md | 32 ++++ src/language-css/printer-postcss.js | 7 +- .../indent/__snapshots__/jsfmt.spec.js.snap | 4 +- .../__snapshots__/jsfmt.spec.js.snap | 138 ++++++++++++++++++ tests/format/css/pseudo-call/is.css | 33 +++++ tests/format/css/pseudo-call/where.css | 19 +++ 6 files changed, 231 insertions(+), 2 deletions(-) create mode 100644 changelog_unreleased/css/13577.md create mode 100644 tests/format/css/pseudo-call/is.css create mode 100644 tests/format/css/pseudo-call/where.css diff --git a/changelog_unreleased/css/13577.md b/changelog_unreleased/css/13577.md new file mode 100644 index 000000000..e19fe8491 --- /dev/null +++ b/changelog_unreleased/css/13577.md @@ -0,0 +1,32 @@ +#### Fix formatting of long `:is`, `:where`, and `:not` selectors (#13577 by @j-f1) + +Pseudo-selectors like `:is`, `:where`, and `:not` that can take multiple selectors as arguments are now formatted like function calls are in other languages. Previously, no special significance was attached to the commas between their “arguments,” leading to confusing wrapping behavior. There are likely still improvements to be made here — please open an issue with some example code if you find something that doesn’t look as expected. + + +```css +/* Input */ +:where( + label > input:valid, + label > textarea:not(:empty), + label > button[disabled] +) ~ .errors > .error { display: none; } + +/* Prettier stable */ +:where(label > input:valid, label > textarea:not(:empty), label + > button[disabled]) + ~ .errors + > .error { + display: none; +} + +/* Prettier main */ +:where( + label > input:valid, + label > textarea:not(:empty), + label > button[disabled] + ) + ~ .errors + > .error { + display: none; +} +``` diff --git a/src/language-css/printer-postcss.js b/src/language-css/printer-postcss.js index 744646020..f8bbd0296 100644 --- a/src/language-css/printer-postcss.js +++ b/src/language-css/printer-postcss.js @@ -461,7 +461,12 @@ function genericPrint(path, options, print) { return [ maybeToLowerCase(node.value), isNonEmptyArray(node.nodes) - ? ["(", join(", ", path.map(print, "nodes")), ")"] + ? group([ + "(", + indent([softline, join([",", line], path.map(print, "nodes"))]), + softline, + ")", + ]) : "", ]; } diff --git a/tests/format/css/indent/__snapshots__/jsfmt.spec.js.snap b/tests/format/css/indent/__snapshots__/jsfmt.spec.js.snap index e7457a685..b00a54f5c 100644 --- a/tests/format/css/indent/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/css/indent/__snapshots__/jsfmt.spec.js.snap @@ -42,7 +42,9 @@ a { =====================================output===================================== a { - ~ .Pagination-itemWrapper:not(.is-separator):not([data-priority^="#{$priority}"]) + ~ .Pagination-itemWrapper:not(.is-separator):not( + [data-priority^="#{$priority}"] + ) ~ .Pagination-itemWrapper.is-separator[data-priority^="#{$priority}"] { display: flex; } diff --git a/tests/format/css/pseudo-call/__snapshots__/jsfmt.spec.js.snap b/tests/format/css/pseudo-call/__snapshots__/jsfmt.spec.js.snap index 50ebc77cb..5a3dbabd4 100644 --- a/tests/format/css/pseudo-call/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/css/pseudo-call/__snapshots__/jsfmt.spec.js.snap @@ -1,5 +1,86 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`is.css format 1`] = ` +====================================options===================================== +parsers: ["css"] +printWidth: 80 + | printWidth +=====================================input====================================== +:is(ol, ul, menu:unsupported) :is(ol, ul) { + color: green; +} + +:is(ol, ul) :is(ol, ul) ol { + list-style-type: lower-greek; + color: chocolate; +} + +:is(ol, ul, menu, dir) :is(ol, ul, menu, dir) :is(ul, menu, dir) { + list-style-type: square; +} + +/* Level 0 */ +h1 { + font-size: 30px; +} +/* Level 1 */ +:is(section, article, aside, nav) h1 { + font-size: 25px; +} +/* Level 2 */ +:is(section, article, aside, nav) :is(section, article, aside, nav) h1 { + font-size: 20px; +} +/* Level 3 */ +:is(section, article, aside, nav) :is(section, article, aside, nav) :is(section, article, aside, nav) h1 { + font-size: 15px; +} + +some-element:is(::before, ::after) { + display: block; +} + +=====================================output===================================== +:is(ol, ul, menu:unsupported) :is(ol, ul) { + color: green; +} + +:is(ol, ul) :is(ol, ul) ol { + list-style-type: lower-greek; + color: chocolate; +} + +:is(ol, ul, menu, dir) :is(ol, ul, menu, dir) :is(ul, menu, dir) { + list-style-type: square; +} + +/* Level 0 */ +h1 { + font-size: 30px; +} +/* Level 1 */ +:is(section, article, aside, nav) h1 { + font-size: 25px; +} +/* Level 2 */ +:is(section, article, aside, nav) :is(section, article, aside, nav) h1 { + font-size: 20px; +} +/* Level 3 */ +:is(section, article, aside, nav) + :is(section, article, aside, nav) + :is(section, article, aside, nav) + h1 { + font-size: 15px; +} + +some-element:is(::before, ::after) { + display: block; +} + +================================================================================ +`; + exports[`pseudo_call.css format 1`] = ` ====================================options===================================== parsers: ["css"] @@ -15,3 +96,60 @@ div:not(:last-child) { ================================================================================ `; + +exports[`where.css format 1`] = ` +====================================options===================================== +parsers: ["css"] +printWidth: 80 + | printWidth +=====================================input====================================== +:where(#p0:checked ~ #play:checked ~ #c1:checked, #p1:checked ~ #play:checked ~ #c2:checked, #p2:checked ~ #play:checked ~ #cO:checked) ~ #result > +#c { display: block; } + +:where(ol, ul, menu:unsupported) :where(ol, ul) { + color: green; +} + +:where(ol, ul) :where(ol, ul) ol { + list-style-type: lower-greek; + color: chocolate; +} + +:is(section.is-styling, aside.is-styling, footer.is-styling) a { + color: red; +} + +:where(section.where-styling, aside.where-styling, footer.where-styling) a { + color: orange; +} + +=====================================output===================================== +:where( + #p0:checked ~ #play:checked ~ #c1:checked, + #p1:checked ~ #play:checked ~ #c2:checked, + #p2:checked ~ #play:checked ~ #cO:checked + ) + ~ #result + > #c { + display: block; +} + +:where(ol, ul, menu:unsupported) :where(ol, ul) { + color: green; +} + +:where(ol, ul) :where(ol, ul) ol { + list-style-type: lower-greek; + color: chocolate; +} + +:is(section.is-styling, aside.is-styling, footer.is-styling) a { + color: red; +} + +:where(section.where-styling, aside.where-styling, footer.where-styling) a { + color: orange; +} + +================================================================================ +`; diff --git a/tests/format/css/pseudo-call/is.css b/tests/format/css/pseudo-call/is.css new file mode 100644 index 000000000..4b26a4aa1 --- /dev/null +++ b/tests/format/css/pseudo-call/is.css @@ -0,0 +1,33 @@ +:is(ol, ul, menu:unsupported) :is(ol, ul) { + color: green; +} + +:is(ol, ul) :is(ol, ul) ol { + list-style-type: lower-greek; + color: chocolate; +} + +:is(ol, ul, menu, dir) :is(ol, ul, menu, dir) :is(ul, menu, dir) { + list-style-type: square; +} + +/* Level 0 */ +h1 { + font-size: 30px; +} +/* Level 1 */ +:is(section, article, aside, nav) h1 { + font-size: 25px; +} +/* Level 2 */ +:is(section, article, aside, nav) :is(section, article, aside, nav) h1 { + font-size: 20px; +} +/* Level 3 */ +:is(section, article, aside, nav) :is(section, article, aside, nav) :is(section, article, aside, nav) h1 { + font-size: 15px; +} + +some-element:is(::before, ::after) { + display: block; +} diff --git a/tests/format/css/pseudo-call/where.css b/tests/format/css/pseudo-call/where.css new file mode 100644 index 000000000..a52b9af46 --- /dev/null +++ b/tests/format/css/pseudo-call/where.css @@ -0,0 +1,19 @@ +:where(#p0:checked ~ #play:checked ~ #c1:checked, #p1:checked ~ #play:checked ~ #c2:checked, #p2:checked ~ #play:checked ~ #cO:checked) ~ #result > +#c { display: block; } + +:where(ol, ul, menu:unsupported) :where(ol, ul) { + color: green; +} + +:where(ol, ul) :where(ol, ul) ol { + list-style-type: lower-greek; + color: chocolate; +} + +:is(section.is-styling, aside.is-styling, footer.is-styling) a { + color: red; +} + +:where(section.where-styling, aside.where-styling, footer.where-styling) a { + color: orange; +}