From 5b4571f786cef0e5828657a6ba8c6c0848739064 Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Wed, 5 Jun 2024 11:40:42 -0400 Subject: [PATCH 1/2] Parse arguments for pseudo-class functions so that the AST doesn't just contain tokens. Previously the arguments were something like ``` [(ident-token "div"), (comma-token), (whitespace-token " "), (ident-token "span"), (delim-token "."), (ident-token "wide"), (comma-token), (whitespace-token " "), (delim-token "."), (ident-token "hidden")] ``` Now those tokens will be represented in the AST as ``` [(type-selector (wqname (ident-token "div"))), (compound-selector (type (type-selector (wqname (ident-token "span")))) (subclasses (class-selector (ident-token "wide")) ) (pseudo-elements) ), (class-selector (ident-token "hidden"))] ``` --- lib/syntax_tree/css/selectors.rb | 3 ++- test/selectors_test.rb | 30 ++++++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/lib/syntax_tree/css/selectors.rb b/lib/syntax_tree/css/selectors.rb index 32bf420..e7ec96f 100644 --- a/lib/syntax_tree/css/selectors.rb +++ b/lib/syntax_tree/css/selectors.rb @@ -509,7 +509,8 @@ def pseudo_class_selector PseudoClassSelector.new(value: consume(IdentToken)) in Function node = consume(Function) - function = PseudoClassFunction.new(name: node.name, arguments: node.value) + arguments = Selectors.new(node.value).parse + function = PseudoClassFunction.new(name: node.name, arguments: arguments) PseudoClassSelector.new(value: function) else raise MissingTokenError, "Expected pseudo class selector to produce something" diff --git a/test/selectors_test.rb b/test/selectors_test.rb index b7a54e6..203ccb6 100644 --- a/test/selectors_test.rb +++ b/test/selectors_test.rb @@ -82,7 +82,7 @@ class SelectorsTest < Minitest::Spec end end - it "parses a compound selector with a pseudo-class" do + it "parses a compound selector with a pseudo-class selector" do actual = parse_selectors("div.flex:hover") assert_pattern do @@ -98,6 +98,33 @@ class SelectorsTest < Minitest::Spec end end + it "parses a compound selector with a pseudo-class function" do + actual = parse_selectors(".flex:not(div, span.wide, .hidden)") + + assert_pattern do + actual => [ + Selectors::CompoundSelector[ + type: nil, + subclasses: [ + Selectors::ClassSelector[value: { value: "flex" }], + Selectors::PseudoClassSelector[ + value: Selectors::PseudoClassFunction[ + name: "not", + arguments: [ + Selectors::TypeSelector[value: { name: { value: "div" } }], + Selectors::CompoundSelector[ + Selectors::TypeSelector[value: { name: { value: "span" } }], + Selectors::ClassSelector[value: { value: "wide" }], + ], + ], + ], + ], + ], + ] + ] + end + end + it "parses a compound selector with pseudo-elements and pseudo-classes" do actual = parse_selectors("div.flex:hover::first-line:last-child:active::first-letter") @@ -208,7 +235,6 @@ class SelectorsTest < Minitest::Spec ] end end - end describe "formatting" do From c689a6ca5be1ea54acb8b8f3c4a8ca4c44aba63d Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Wed, 5 Jun 2024 11:44:59 -0400 Subject: [PATCH 2/2] Format pseudo-class functions with complex arguments --- lib/syntax_tree/css/format.rb | 11 ++++++++++- lib/syntax_tree/css/pretty_print.rb | 18 +++++++++++++++--- test/selectors_test.rb | 7 +++++++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/lib/syntax_tree/css/format.rb b/lib/syntax_tree/css/format.rb index a940f69..1896219 100644 --- a/lib/syntax_tree/css/format.rb +++ b/lib/syntax_tree/css/format.rb @@ -100,6 +100,16 @@ def visit_pseudo_class_selector(node) node.value.format(q) end + # Visit a Selectors::PseudoClassFunction node. + def visit_pseudo_class_function(node) + q.text(node.name) + q.text("(") + q.seplist(node.arguments, -> { q.text(", ") }) do |selector| + selector.format(q) + end + q.text(")") + end + # Visit a Selectors::PseudoElementSelector node. def visit_pseudo_element_selector(node) q.text(":") @@ -127,7 +137,6 @@ def visit_compound_selector(node) node.child_nodes.each do |node_| node_.format(q) end - # TODO: pseudo-elements end end diff --git a/lib/syntax_tree/css/pretty_print.rb b/lib/syntax_tree/css/pretty_print.rb index 0094006..cc22424 100644 --- a/lib/syntax_tree/css/pretty_print.rb +++ b/lib/syntax_tree/css/pretty_print.rb @@ -380,10 +380,19 @@ def visit_pseudo_class_function(node) q.breakable q.pp(node.name) + q.breakable + q.text("(arguments") + if node.arguments.any? - q.breakable - q.seplist(node.arguments) { |argument| q.pp(argument) } + q.nest(2) do + q.breakable + q.seplist(node.arguments) { |argument| q.pp(argument) } + end + + q.breakable("") end + + q.text(")") end end @@ -443,7 +452,10 @@ def visit_complex_selector(node) def visit_compound_selector(node) token("compound-selector") do q.breakable - q.pp(node.type) + token("type") do + q.breakable + q.pp(node.type) + end q.breakable q.text("(subclasses") diff --git a/test/selectors_test.rb b/test/selectors_test.rb index 203ccb6..bcd3bc1 100644 --- a/test/selectors_test.rb +++ b/test/selectors_test.rb @@ -253,6 +253,13 @@ class SelectorsTest < Minitest::Spec ) end + it "with a pseudo-class function" do + assert_selector_format( + ".flex:not(div, span.wide, .hidden)", + ".flex:not(div, span.wide, .hidden)", + ) + end + it "with class selectors" do assert_selector_format( "div.flex.text-xl",