From d5b9ccbc61800961fbc651b1915cf13cc5990c92 Mon Sep 17 00:00:00 2001 From: Jon Egeland Date: Thu, 21 Dec 2023 18:25:52 +0000 Subject: [PATCH] feat(css_formatter): support for attribute selectors --- .../src/css/auxiliary/attribute_matcher.rs | 19 +- .../css/auxiliary/attribute_matcher_value.rs | 9 +- .../src/css/auxiliary/attribute_name.rs | 9 +- .../src/css/lists/rule_list.rs | 2 +- .../src/css/selectors/attribute_selector.rs | 22 +- .../css/selectors/attribute_selector.css | 114 ++++++++ .../css/selectors/attribute_selector.css.snap | 276 ++++++++++++++++++ 7 files changed, 438 insertions(+), 13 deletions(-) create mode 100644 crates/biome_css_formatter/tests/specs/css/selectors/attribute_selector.css create mode 100644 crates/biome_css_formatter/tests/specs/css/selectors/attribute_selector.css.snap diff --git a/crates/biome_css_formatter/src/css/auxiliary/attribute_matcher.rs b/crates/biome_css_formatter/src/css/auxiliary/attribute_matcher.rs index cc77261294f2..2ae403cff77c 100644 --- a/crates/biome_css_formatter/src/css/auxiliary/attribute_matcher.rs +++ b/crates/biome_css_formatter/src/css/auxiliary/attribute_matcher.rs @@ -1,10 +1,23 @@ use crate::prelude::*; -use biome_css_syntax::CssAttributeMatcher; -use biome_rowan::AstNode; +use biome_css_syntax::{CssAttributeMatcher, CssAttributeMatcherFields}; +use biome_formatter::write; + #[derive(Debug, Clone, Default)] pub(crate) struct FormatCssAttributeMatcher; impl FormatNodeRule for FormatCssAttributeMatcher { fn fmt_fields(&self, node: &CssAttributeMatcher, f: &mut CssFormatter) -> FormatResult<()> { - format_verbatim_node(node.syntax()).fmt(f) + let CssAttributeMatcherFields { + operator, + value, + modifier, + } = node.as_fields(); + + write!(f, [operator.format(), value.format()])?; + + if modifier.is_some() { + write!(f, [space(), modifier.format()])?; + } + + Ok(()) } } diff --git a/crates/biome_css_formatter/src/css/auxiliary/attribute_matcher_value.rs b/crates/biome_css_formatter/src/css/auxiliary/attribute_matcher_value.rs index de08c81a5c4e..2269b406380c 100644 --- a/crates/biome_css_formatter/src/css/auxiliary/attribute_matcher_value.rs +++ b/crates/biome_css_formatter/src/css/auxiliary/attribute_matcher_value.rs @@ -1,6 +1,7 @@ use crate::prelude::*; -use biome_css_syntax::CssAttributeMatcherValue; -use biome_rowan::AstNode; +use biome_css_syntax::{CssAttributeMatcherValue, CssAttributeMatcherValueFields}; +use biome_formatter::write; + #[derive(Debug, Clone, Default)] pub(crate) struct FormatCssAttributeMatcherValue; impl FormatNodeRule for FormatCssAttributeMatcherValue { @@ -9,6 +10,8 @@ impl FormatNodeRule for FormatCssAttributeMatcherValue node: &CssAttributeMatcherValue, f: &mut CssFormatter, ) -> FormatResult<()> { - format_verbatim_node(node.syntax()).fmt(f) + let CssAttributeMatcherValueFields { name } = node.as_fields(); + + write!(f, [name.format()]) } } diff --git a/crates/biome_css_formatter/src/css/auxiliary/attribute_name.rs b/crates/biome_css_formatter/src/css/auxiliary/attribute_name.rs index 513502c2add9..9e36b4d7b79a 100644 --- a/crates/biome_css_formatter/src/css/auxiliary/attribute_name.rs +++ b/crates/biome_css_formatter/src/css/auxiliary/attribute_name.rs @@ -1,10 +1,13 @@ use crate::prelude::*; -use biome_css_syntax::CssAttributeName; -use biome_rowan::AstNode; +use biome_css_syntax::{CssAttributeName, CssAttributeNameFields}; +use biome_formatter::write; + #[derive(Debug, Clone, Default)] pub(crate) struct FormatCssAttributeName; impl FormatNodeRule for FormatCssAttributeName { fn fmt_fields(&self, node: &CssAttributeName, f: &mut CssFormatter) -> FormatResult<()> { - format_verbatim_node(node.syntax()).fmt(f) + let CssAttributeNameFields { namespace, name } = node.as_fields(); + + write!(f, [namespace.format(), name.format()]) } } diff --git a/crates/biome_css_formatter/src/css/lists/rule_list.rs b/crates/biome_css_formatter/src/css/lists/rule_list.rs index 79b72f8629af..9c0a6489d093 100644 --- a/crates/biome_css_formatter/src/css/lists/rule_list.rs +++ b/crates/biome_css_formatter/src/css/lists/rule_list.rs @@ -8,7 +8,7 @@ impl FormatRule for FormatCssRuleList { let mut join = f.join_nodes_with_hardline(); for rule in node { - join.entry(rule.syntax(), &rule.format()); + join.entry(rule.syntax(), &format_or_verbatim(rule.format())); } join.finish() diff --git a/crates/biome_css_formatter/src/css/selectors/attribute_selector.rs b/crates/biome_css_formatter/src/css/selectors/attribute_selector.rs index 444e80dffb6a..93918e19e166 100644 --- a/crates/biome_css_formatter/src/css/selectors/attribute_selector.rs +++ b/crates/biome_css_formatter/src/css/selectors/attribute_selector.rs @@ -1,10 +1,26 @@ use crate::prelude::*; -use biome_css_syntax::CssAttributeSelector; -use biome_rowan::AstNode; +use biome_css_syntax::{CssAttributeSelector, CssAttributeSelectorFields}; +use biome_formatter::write; + #[derive(Debug, Clone, Default)] pub(crate) struct FormatCssAttributeSelector; impl FormatNodeRule for FormatCssAttributeSelector { fn fmt_fields(&self, node: &CssAttributeSelector, f: &mut CssFormatter) -> FormatResult<()> { - format_verbatim_node(node.syntax()).fmt(f) + let CssAttributeSelectorFields { + l_brack_token, + name, + matcher, + r_brack_token, + } = node.as_fields(); + + write!( + f, + [ + l_brack_token.format(), + name.format(), + matcher.format(), + r_brack_token.format() + ] + ) } } diff --git a/crates/biome_css_formatter/tests/specs/css/selectors/attribute_selector.css b/crates/biome_css_formatter/tests/specs/css/selectors/attribute_selector.css new file mode 100644 index 000000000000..7eb299674e70 --- /dev/null +++ b/crates/biome_css_formatter/tests/specs/css/selectors/attribute_selector.css @@ -0,0 +1,114 @@ +[attr] {} + +[ attr ] {} + + +[ svg | a ] {} + +[ foo|att = val] {} + +[ *| att] {} + +[ |att] {} + +input[type="radio" i] {} +img[alt~="person" i][src*="lorem" i] {} +input[type="radio" s] {} +img[alt~="person" s][src*="lorem" s] {} + + +a[id = test] {} +a[id= "test"] {} +a[id = 'test'] {} +a[id= func("foo")] {} +a[class= "(╯°□°)╯︵ ┻━┻" ]{} + +[lang] {} +[ lang] {} +[lang ] {} +[ lang ] {} +[ lang ] {} +[ +lang +] {} +span[lang] {} +span[ lang] {} +span[lang ] {} +span[ lang ] {} +span[ lang ] {} +span[lang='pt'] {} +span[lang ='pt'] {} +span[lang= 'pt'] {} +span[lang = 'pt'] {} +span[lang = 'pt'] {} +span[lang='pt' ] {} +span[lang='pt' ] {} +span[ +lang += +'pt' +] {} +span[ lang ~= 'en-us' ] {} +span[ lang ~= 'en-us' ] {} +span[ lang |='zh' ] {} +span[ +lang +~= +'en-us' +] {} +a[ href ^= '#' ] {} +a[ href *= 'example' ] {} +a[ +href +*= +'example' +] {} +input[ type = 'radio' i ] {} +input[ type = 'radio' i ] {} +input[ type ~= 'radio' i ] {} +input[ type ~= 'radio' i ] {} +input[ +type +~= +'radio' +i +] {} +img[ alt = 'person' ][ src = 'lorem' ] {} +img[ alt = 'person' ][ src = 'lorem' ] {} +img[ alt ~= 'person' ][ src *= 'lorem' ] {} +img[ alt ~= 'person' ][ src *= 'lorem' ] {} +img[ +alt +~= +'person' +][ +src +*= +'lorem' +] {} + +[foo|att=val] {} +[ foo | att = val ] {} +[ foo | att = val ] {} +[ +foo +| +att += +val +] {} +[*|att] {} +[ * | att ] {} +[ * | att ] {} +[ +* +| +att +] {} +[|att] {} +[ | att ] {} +[ | att ] {} +[ +| +att +] {} \ No newline at end of file diff --git a/crates/biome_css_formatter/tests/specs/css/selectors/attribute_selector.css.snap b/crates/biome_css_formatter/tests/specs/css/selectors/attribute_selector.css.snap new file mode 100644 index 000000000000..e7d10d408083 --- /dev/null +++ b/crates/biome_css_formatter/tests/specs/css/selectors/attribute_selector.css.snap @@ -0,0 +1,276 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: css/selectors/attribute_selector.css +--- + +# Input + +```css +[attr] {} + +[ attr ] {} + + +[ svg | a ] {} + +[ foo|att = val] {} + +[ *| att] {} + +[ |att] {} + +input[type="radio" i] {} +img[alt~="person" i][src*="lorem" i] {} +input[type="radio" s] {} +img[alt~="person" s][src*="lorem" s] {} + + +a[id = test] {} +a[id= "test"] {} +a[id = 'test'] {} +a[id= func("foo")] {} +a[class= "(╯°□°)╯︵ ┻━┻" ]{} + +[lang] {} +[ lang] {} +[lang ] {} +[ lang ] {} +[ lang ] {} +[ +lang +] {} +span[lang] {} +span[ lang] {} +span[lang ] {} +span[ lang ] {} +span[ lang ] {} +span[lang='pt'] {} +span[lang ='pt'] {} +span[lang= 'pt'] {} +span[lang = 'pt'] {} +span[lang = 'pt'] {} +span[lang='pt' ] {} +span[lang='pt' ] {} +span[ +lang += +'pt' +] {} +span[ lang ~= 'en-us' ] {} +span[ lang ~= 'en-us' ] {} +span[ lang |='zh' ] {} +span[ +lang +~= +'en-us' +] {} +a[ href ^= '#' ] {} +a[ href *= 'example' ] {} +a[ +href +*= +'example' +] {} +input[ type = 'radio' i ] {} +input[ type = 'radio' i ] {} +input[ type ~= 'radio' i ] {} +input[ type ~= 'radio' i ] {} +input[ +type +~= +'radio' +i +] {} +img[ alt = 'person' ][ src = 'lorem' ] {} +img[ alt = 'person' ][ src = 'lorem' ] {} +img[ alt ~= 'person' ][ src *= 'lorem' ] {} +img[ alt ~= 'person' ][ src *= 'lorem' ] {} +img[ +alt +~= +'person' +][ +src +*= +'lorem' +] {} + +[foo|att=val] {} +[ foo | att = val ] {} +[ foo | att = val ] {} +[ +foo +| +att += +val +] {} +[*|att] {} +[ * | att ] {} +[ * | att ] {} +[ +* +| +att +] {} +[|att] {} +[ | att ] {} +[ | att ] {} +[ +| +att +] {} +``` + + +============================= + +# Outputs + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +----- + +```css +[attr] { +} + +[attr] { +} + +[svg|a] { +} + +[foo|att=val] { +} + +[*|att] { +} + +[|att] { +} + +input[type="radio" i] { +} +img[alt~="person" i][src*="lorem" i] { +} +input[type="radio" s] { +} +img[alt~="person" s][src*="lorem" s] { +} + +a[id=test] { +} +a[id="test"] { +} +a[id='test'] { +} +a[id= func("foo")] {} +a[class="(╯°□°)╯︵ ┻━┻"] { +} + +[lang] { +} +[lang] { +} +[lang] { +} +[lang] { +} +[lang] { +} +[lang] { +} +span[lang] { +} +span[lang] { +} +span[lang] { +} +span[lang] { +} +span[lang] { +} +span[lang='pt'] { +} +span[lang='pt'] { +} +span[lang='pt'] { +} +span[lang='pt'] { +} +span[lang='pt'] { +} +span[lang='pt'] { +} +span[lang='pt'] { +} +span[lang='pt'] { +} +span[lang~='en-us'] { +} +span[lang~='en-us'] { +} +span[lang|='zh'] { +} +span[lang~='en-us'] { +} +a[href^='#'] { +} +a[href*='example'] { +} +a[href*='example'] { +} +input[type='radio' i] { +} +input[type='radio' i] { +} +input[type~='radio' i] { +} +input[type~='radio' i] { +} +input[type~='radio' i] { +} +img[alt='person'][src='lorem'] { +} +img[alt='person'][src='lorem'] { +} +img[alt~='person'][src*='lorem'] { +} +img[alt~='person'][src*='lorem'] { +} +img[alt~='person'][src*='lorem'] { +} + +[foo|att=val] { +} +[foo|att=val] { +} +[foo|att=val] { +} +[foo|att=val] { +} +[*|att] { +} +[*|att] { +} +[*|att] { +} +[*|att] { +} +[|att] { +} +[|att] { +} +[|att] { +} +[|att] { +} +``` + +