From f83c4cc2f3413c525d50db56c927037c7106bd07 Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Tue, 4 Jun 2024 14:17:27 -0400 Subject: [PATCH 1/3] fix: CompoundSelector#child_nodes should not include nil type --- lib/syntax_tree/css/selectors.rb | 2 +- test/selectors_test.rb | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/syntax_tree/css/selectors.rb b/lib/syntax_tree/css/selectors.rb index 6a05b6f..32bf420 100644 --- a/lib/syntax_tree/css/selectors.rb +++ b/lib/syntax_tree/css/selectors.rb @@ -125,7 +125,7 @@ def accept(visitor) end def child_nodes - [type, subclasses, pseudo_elements].flatten + [type, subclasses, pseudo_elements].compact.flatten end alias deconstruct child_nodes diff --git a/test/selectors_test.rb b/test/selectors_test.rb index 138131e..d4aa5b5 100644 --- a/test/selectors_test.rb +++ b/test/selectors_test.rb @@ -27,6 +27,15 @@ class SelectorsTest < Minitest::Spec ] ] end + + assert_pattern do + actual => [ + Selectors::CompoundSelector[ + Selectors::ClassSelector[value: { value: "flex" }], + Selectors::ClassSelector[value: { value: "text-xl" }] + ] + ] + end end it "parses a compound selector" do @@ -41,6 +50,15 @@ class SelectorsTest < Minitest::Spec ] ] end + + assert_pattern do + actual => [ + Selectors::CompoundSelector[ + Selectors::TypeSelector[value: { name: { value: "div" } } ], + Selectors::ClassSelector[value: { value: "flex" }], + ] + ] + end end it "parses a compound selector with a pseudo-element" do From e2096b3261d78c2815d73e4d95a90e4ff815410d Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Tue, 4 Jun 2024 13:17:15 -0400 Subject: [PATCH 2/3] test: write test describing the current pseudo-element AST structure --- test/selectors_test.rb | 49 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/test/selectors_test.rb b/test/selectors_test.rb index d4aa5b5..27231a1 100644 --- a/test/selectors_test.rb +++ b/test/selectors_test.rb @@ -72,9 +72,7 @@ class SelectorsTest < Minitest::Spec pseudo_elements: [ [ Selectors::PseudoElementSelector[ - Selectors::PseudoClassSelector[ - value: { value: "first-line" } - ] + value: { value: { value: "first-line" } } ], [] ] @@ -84,6 +82,51 @@ class SelectorsTest < Minitest::Spec end end + it "parses a compound selector with a pseudo-class" do + actual = parse_selectors("div.flex:hover") + + assert_pattern do + actual => [ + Selectors::CompoundSelector[ + type: { value: { name: { value: "div" } } }, + subclasses: [ + Selectors::ClassSelector[value: { value: "flex" }], + Selectors::PseudoClassSelector[value: { value: "hover" }], + ], + ] + ] + 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") + + assert_pattern do + actual => [ + Selectors::CompoundSelector[ + type: { value: { name: { value: "div" } } }, + subclasses: [ + Selectors::ClassSelector[value: { value: "flex" }], + Selectors::PseudoClassSelector[value: { value: "hover" }], + ], + pseudo_elements: [ + [ + Selectors::PseudoElementSelector[value: { value: { value: "first-line" } }], + [ + Selectors::PseudoClassSelector[value: { value: "last-child" }], + Selectors::PseudoClassSelector[value: { value: "active" }], + ], + ], + [ + Selectors::PseudoElementSelector[value: { value: { value: "first-letter" } }], + [], + ] + ] + ] + ] + end + end + it "parses a complex selector" do actual = parse_selectors("section>table") From c1527377fda865ea493ab6933beb876a2845efd4 Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Tue, 4 Jun 2024 13:48:25 -0400 Subject: [PATCH 3/3] format: implement CSS selector formatting for more nodes Implement formatting for IdSelector, HashToken, PseudoClassSelector, and PseudoElementSelector. Also, add formatting test coverage. --- lib/syntax_tree/css/format.rb | 28 +++++++++++++++++++++--- test/selectors_test.rb | 41 ++++++++++++++++++++++++++++++++--- 2 files changed, 63 insertions(+), 6 deletions(-) diff --git a/lib/syntax_tree/css/format.rb b/lib/syntax_tree/css/format.rb index b7b1620..a940f69 100644 --- a/lib/syntax_tree/css/format.rb +++ b/lib/syntax_tree/css/format.rb @@ -47,6 +47,11 @@ def visit_ident_token(node) q.text(node.value) end + # Visit a HashToken node. + def visit_hash_token(node) + q.text(node.value) + end + # Visit a StyleRule node. def visit_style_rule(node) q.group do @@ -77,12 +82,30 @@ def visit_type_selector(node) end end + # Visit a Selectors::IdSelector node. + def visit_id_selector(node) + q.text("#") + node.value.format(q) + end + # Visit a Selectors::ClassSelector node. def visit_class_selector(node) q.text(".") node.value.format(q) end + # Visit a Selectors::PseudoClassSelector node. + def visit_pseudo_class_selector(node) + q.text(":") + node.value.format(q) + end + + # Visit a Selectors::PseudoElementSelector node. + def visit_pseudo_element_selector(node) + q.text(":") + node.value.format(q) + end + # Visit a Selectors::Combinator node. def visit_combinator(node) node.value.format(q) @@ -101,9 +124,8 @@ def visit_complex_selector(node) # Visit a Selectors::CompoundSelector node. def visit_compound_selector(node) q.group do - node.type.format(q) if node.type - node.subclasses.each do |subclass| - subclass.format(q) + node.child_nodes.each do |node_| + node_.format(q) end # TODO: pseudo-elements end diff --git a/test/selectors_test.rb b/test/selectors_test.rb index 27231a1..b7a54e6 100644 --- a/test/selectors_test.rb +++ b/test/selectors_test.rb @@ -212,8 +212,43 @@ class SelectorsTest < Minitest::Spec end describe "formatting" do - it "formats complex selectors" do - assert_selector_format(".outer section.foo>table.bar tr", ".outer section.foo > table.bar tr") + describe Selectors::CompoundSelector do + it "with an id selector" do + assert_selector_format( + "div#foo", + "div#foo", + ) + end + + it "with a pseudo-class selector" do + assert_selector_format( + "div:hover", + "div:hover", + ) + end + + it "with class selectors" do + assert_selector_format( + "div.flex.text-xl", + "div.flex.text-xl", + ) + end + + it "with pseudo-elements" do + assert_selector_format( + "div.flex:hover::first-line:last-child:active::first-letter", + "div.flex:hover::first-line:last-child:active::first-letter", + ) + end + end + + describe Selectors::ComplexSelector do + it "with whitespace" do + assert_selector_format( + ".outer section.foo>table.bar tr", + ".outer section.foo > table.bar tr", + ) + end end private @@ -223,7 +258,7 @@ def assert_selector_format(selectors, expected) io = StringIO.new selectors.each do |selector| - selector.format(::PrettyPrint.new(io)) + selector.format(::PP.new(io)) assert_equal(expected, io.string) end end