Skip to content

Commit 57a45cb

Browse files
authored
Merge pull request #59 from flavorjones/flavorjones-selector-formatting
additional CSS selector formatting
2 parents eb3e2a7 + c152737 commit 57a45cb

File tree

3 files changed

+128
-10
lines changed

3 files changed

+128
-10
lines changed

lib/syntax_tree/css/format.rb

+25-3
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ def visit_ident_token(node)
4747
q.text(node.value)
4848
end
4949

50+
# Visit a HashToken node.
51+
def visit_hash_token(node)
52+
q.text(node.value)
53+
end
54+
5055
# Visit a StyleRule node.
5156
def visit_style_rule(node)
5257
q.group do
@@ -77,12 +82,30 @@ def visit_type_selector(node)
7782
end
7883
end
7984

85+
# Visit a Selectors::IdSelector node.
86+
def visit_id_selector(node)
87+
q.text("#")
88+
node.value.format(q)
89+
end
90+
8091
# Visit a Selectors::ClassSelector node.
8192
def visit_class_selector(node)
8293
q.text(".")
8394
node.value.format(q)
8495
end
8596

97+
# Visit a Selectors::PseudoClassSelector node.
98+
def visit_pseudo_class_selector(node)
99+
q.text(":")
100+
node.value.format(q)
101+
end
102+
103+
# Visit a Selectors::PseudoElementSelector node.
104+
def visit_pseudo_element_selector(node)
105+
q.text(":")
106+
node.value.format(q)
107+
end
108+
86109
# Visit a Selectors::Combinator node.
87110
def visit_combinator(node)
88111
node.value.format(q)
@@ -101,9 +124,8 @@ def visit_complex_selector(node)
101124
# Visit a Selectors::CompoundSelector node.
102125
def visit_compound_selector(node)
103126
q.group do
104-
node.type.format(q) if node.type
105-
node.subclasses.each do |subclass|
106-
subclass.format(q)
127+
node.child_nodes.each do |node_|
128+
node_.format(q)
107129
end
108130
# TODO: pseudo-elements
109131
end

lib/syntax_tree/css/selectors.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ def accept(visitor)
125125
end
126126

127127
def child_nodes
128-
[type, subclasses, pseudo_elements].flatten
128+
[type, subclasses, pseudo_elements].compact.flatten
129129
end
130130

131131
alias deconstruct child_nodes

test/selectors_test.rb

+102-6
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,15 @@ class SelectorsTest < Minitest::Spec
2727
]
2828
]
2929
end
30+
31+
assert_pattern do
32+
actual => [
33+
Selectors::CompoundSelector[
34+
Selectors::ClassSelector[value: { value: "flex" }],
35+
Selectors::ClassSelector[value: { value: "text-xl" }]
36+
]
37+
]
38+
end
3039
end
3140

3241
it "parses a compound selector" do
@@ -41,6 +50,15 @@ class SelectorsTest < Minitest::Spec
4150
]
4251
]
4352
end
53+
54+
assert_pattern do
55+
actual => [
56+
Selectors::CompoundSelector[
57+
Selectors::TypeSelector[value: { name: { value: "div" } } ],
58+
Selectors::ClassSelector[value: { value: "flex" }],
59+
]
60+
]
61+
end
4462
end
4563

4664
it "parses a compound selector with a pseudo-element" do
@@ -54,9 +72,7 @@ class SelectorsTest < Minitest::Spec
5472
pseudo_elements: [
5573
[
5674
Selectors::PseudoElementSelector[
57-
Selectors::PseudoClassSelector[
58-
value: { value: "first-line" }
59-
]
75+
value: { value: { value: "first-line" } }
6076
],
6177
[]
6278
]
@@ -66,6 +82,51 @@ class SelectorsTest < Minitest::Spec
6682
end
6783
end
6884

85+
it "parses a compound selector with a pseudo-class" do
86+
actual = parse_selectors("div.flex:hover")
87+
88+
assert_pattern do
89+
actual => [
90+
Selectors::CompoundSelector[
91+
type: { value: { name: { value: "div" } } },
92+
subclasses: [
93+
Selectors::ClassSelector[value: { value: "flex" }],
94+
Selectors::PseudoClassSelector[value: { value: "hover" }],
95+
],
96+
]
97+
]
98+
end
99+
end
100+
101+
it "parses a compound selector with pseudo-elements and pseudo-classes" do
102+
actual = parse_selectors("div.flex:hover::first-line:last-child:active::first-letter")
103+
104+
assert_pattern do
105+
actual => [
106+
Selectors::CompoundSelector[
107+
type: { value: { name: { value: "div" } } },
108+
subclasses: [
109+
Selectors::ClassSelector[value: { value: "flex" }],
110+
Selectors::PseudoClassSelector[value: { value: "hover" }],
111+
],
112+
pseudo_elements: [
113+
[
114+
Selectors::PseudoElementSelector[value: { value: { value: "first-line" } }],
115+
[
116+
Selectors::PseudoClassSelector[value: { value: "last-child" }],
117+
Selectors::PseudoClassSelector[value: { value: "active" }],
118+
],
119+
],
120+
[
121+
Selectors::PseudoElementSelector[value: { value: { value: "first-letter" } }],
122+
[],
123+
]
124+
]
125+
]
126+
]
127+
end
128+
end
129+
69130
it "parses a complex selector" do
70131
actual = parse_selectors("section>table")
71132

@@ -151,8 +212,43 @@ class SelectorsTest < Minitest::Spec
151212
end
152213

153214
describe "formatting" do
154-
it "formats complex selectors" do
155-
assert_selector_format(".outer section.foo>table.bar tr", ".outer section.foo > table.bar tr")
215+
describe Selectors::CompoundSelector do
216+
it "with an id selector" do
217+
assert_selector_format(
218+
"div#foo",
219+
"div#foo",
220+
)
221+
end
222+
223+
it "with a pseudo-class selector" do
224+
assert_selector_format(
225+
"div:hover",
226+
"div:hover",
227+
)
228+
end
229+
230+
it "with class selectors" do
231+
assert_selector_format(
232+
"div.flex.text-xl",
233+
"div.flex.text-xl",
234+
)
235+
end
236+
237+
it "with pseudo-elements" do
238+
assert_selector_format(
239+
"div.flex:hover::first-line:last-child:active::first-letter",
240+
"div.flex:hover::first-line:last-child:active::first-letter",
241+
)
242+
end
243+
end
244+
245+
describe Selectors::ComplexSelector do
246+
it "with whitespace" do
247+
assert_selector_format(
248+
".outer section.foo>table.bar tr",
249+
".outer section.foo > table.bar tr",
250+
)
251+
end
156252
end
157253

158254
private
@@ -162,7 +258,7 @@ def assert_selector_format(selectors, expected)
162258

163259
io = StringIO.new
164260
selectors.each do |selector|
165-
selector.format(::PrettyPrint.new(io))
261+
selector.format(::PP.new(io))
166262
assert_equal(expected, io.string)
167263
end
168264
end

0 commit comments

Comments
 (0)