Skip to content

Commit f9f7b3e

Browse files
authored
Merge pull request #61 from flavorjones/flavorjones-complex-selector-whitespace2
Include an explicit Combinator in the AST for implicit descendant
2 parents 03a7d6c + 4ee952e commit f9f7b3e

File tree

4 files changed

+86
-25
lines changed

4 files changed

+86
-25
lines changed

lib/syntax_tree/css/format.rb

+12-2
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,24 @@ def visit_pseudo_element_selector(node)
108108

109109
# Visit a Selectors::Combinator node.
110110
def visit_combinator(node)
111-
node.value.format(q)
111+
case node.value
112+
when WhitespaceToken
113+
q.text(" ")
114+
when Array
115+
q.text(" ")
116+
node.value.each { |val| val.format(q) }
117+
q.text(" ")
118+
else
119+
q.text(" ")
120+
node.value.format(q)
121+
q.text(" ")
122+
end
112123
end
113124

114125
# Visit a Selectors::ComplexSelector node.
115126
def visit_complex_selector(node)
116127
q.group do
117128
node.child_nodes.each_with_index do |child_node, j|
118-
q.text(" ") unless j == 0
119129
child_node.format(q)
120130
end
121131
end

lib/syntax_tree/css/pretty_print.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,7 @@ def visit_wqname(node)
423423

424424
# Visit a Selectors::Combinator node.
425425
def visit_combinator(node)
426-
token("combinator") do
426+
token(node.class::PP_NAME) do
427427
q.breakable
428428
q.pp(node.value)
429429
end

lib/syntax_tree/css/selectors.rb

+52-18
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,36 @@ def deconstruct_keys(keys)
9393
end
9494
end
9595

96+
# §15.1 https://www.w3.org/TR/selectors-4/#descendant-combinators
97+
class DescendantCombinator < Combinator
98+
TOKEN = WhitespaceToken
99+
PP_NAME = "descendant-combinator"
100+
end
101+
102+
# §15.2 https://www.w3.org/TR/selectors-4/#child-combinators
103+
class ChildCombinator < Combinator
104+
TOKEN = ">"
105+
PP_NAME = "child-combinator"
106+
end
107+
108+
# §15.3 https://www.w3.org/TR/selectors-4/#adjacent-sibling-combinators
109+
class NextSiblingCombinator < Combinator
110+
TOKEN = "+"
111+
PP_NAME = "next-sibling-combinator"
112+
end
113+
114+
# §15.4 https://www.w3.org/TR/selectors-4/#general-sibling-combinators
115+
class SubsequentSiblingCombinator < Combinator
116+
TOKEN = "~"
117+
PP_NAME = "subsequent-sibling-combinator"
118+
end
119+
120+
# §16.1 https://www.w3.org/TR/selectors-4/#the-column-combinator
121+
class ColumnSiblingCombinator < Combinator
122+
TOKEN = ["|", "|"]
123+
PP_NAME = "column-sibling-combinator"
124+
end
125+
96126
class ComplexSelector < Node
97127
attr_reader :child_nodes
98128

@@ -333,12 +363,12 @@ def relative_selector_list
333363
def complex_selector
334364
child_nodes = [compound_selector]
335365

366+
combinator_ = nil
367+
compound_selector_ = nil
336368
loop do
337-
if (c = maybe { combinator })
338-
child_nodes << c
339-
end
340-
if (s = maybe { compound_selector })
341-
child_nodes << s
369+
if maybe { (combinator_ = combinator) && (compound_selector_ = compound_selector) }
370+
child_nodes << combinator_
371+
child_nodes << compound_selector_
342372
else
343373
break
344374
end
@@ -363,8 +393,6 @@ def relative_selector
363393
# <compound-selector> = [ <type-selector>? <subclass-selector>*
364394
# [ <pseudo-element-selector> <pseudo-class-selector>* ]* ]!
365395
def compound_selector
366-
consume_whitespace
367-
368396
type = maybe { type_selector }
369397
subclasses = []
370398

@@ -401,17 +429,13 @@ def simple_selector
401429

402430
# <combinator> = '>' | '+' | '~' | [ '|' '|' ]
403431
def combinator
404-
consume_whitespace
405-
406-
value =
407-
options do
408-
maybe { consume(">") } ||
409-
maybe { consume("+") } ||
410-
maybe { consume("~") } ||
411-
maybe { consume("|", "|") }
412-
end
413-
414-
Combinator.new(value: value)
432+
options do
433+
maybe { consume_combinator(ChildCombinator) } ||
434+
maybe { consume_combinator(NextSiblingCombinator) } ||
435+
maybe { consume_combinator(SubsequentSiblingCombinator) } ||
436+
maybe { consume_combinator(ColumnSiblingCombinator) } ||
437+
maybe { consume_combinator(DescendantCombinator) }
438+
end
415439
end
416440

417441
# <type-selector> = <wq-name> | <ns-prefix>? '*'
@@ -554,6 +578,16 @@ def one_or_more
554578
end
555579
end
556580

581+
def consume_combinator(combinator_class)
582+
eat_whitespace = (combinator_class::TOKEN != WhitespaceToken)
583+
584+
consume_whitespace if eat_whitespace
585+
result = consume(*combinator_class::TOKEN)
586+
consume_whitespace if eat_whitespace
587+
588+
combinator_class.new(value: result)
589+
end
590+
557591
def consume(*values)
558592
result =
559593
values.map do |value|

test/selectors_test.rb

+21-4
Original file line numberDiff line numberDiff line change
@@ -128,15 +128,23 @@ class SelectorsTest < Minitest::Spec
128128
end
129129

130130
it "parses a complex selector" do
131-
actual = parse_selectors("section>table")
131+
actual = parse_selectors("a b > c + d ~ e || f")
132132

133133
assert_pattern do
134134
actual => [
135135
Selectors::ComplexSelector[
136136
child_nodes: [
137-
Selectors::TypeSelector[value: { name: { value: "section" } }],
138-
Selectors::Combinator[value: { value: ">" }],
139-
Selectors::TypeSelector[value: { name: { value: "table" } }]
137+
Selectors::TypeSelector[value: { name: { value: "a" } }],
138+
Selectors::DescendantCombinator,
139+
Selectors::TypeSelector[value: { name: { value: "b" } }],
140+
Selectors::ChildCombinator,
141+
Selectors::TypeSelector[value: { name: { value: "c" } }],
142+
Selectors::NextSiblingCombinator,
143+
Selectors::TypeSelector[value: { name: { value: "d" } }],
144+
Selectors::SubsequentSiblingCombinator,
145+
Selectors::TypeSelector[value: { name: { value: "e" } }],
146+
Selectors::ColumnSiblingCombinator,
147+
Selectors::TypeSelector[value: { name: { value: "f" } }],
140148
]
141149
]
142150
]
@@ -185,6 +193,7 @@ class SelectorsTest < Minitest::Spec
185193
Selectors::ComplexSelector[
186194
child_nodes: [
187195
Selectors::TypeSelector[value: { name: { value: "section" } }],
196+
Selectors::Combinator[value: { value: " " }],
188197
Selectors::TypeSelector[value: { name: { value: "table" } }],
189198
]
190199
]
@@ -202,6 +211,7 @@ class SelectorsTest < Minitest::Spec
202211
Selectors::TypeSelector[value: { name: { value: "section" } }],
203212
Selectors::Combinator[value: { value: ">" }],
204213
Selectors::TypeSelector[value: { name: { value: "table" } }],
214+
Selectors::Combinator[value: { value: " " }],
205215
Selectors::TypeSelector[value: { name: { value: "tr" } }]
206216
]
207217
]
@@ -249,6 +259,13 @@ class SelectorsTest < Minitest::Spec
249259
".outer section.foo > table.bar tr",
250260
)
251261
end
262+
263+
it "handles all the combinators" do
264+
assert_selector_format(
265+
"a b > c + d ~ e || f",
266+
"a b > c + d ~ e || f",
267+
)
268+
end
252269
end
253270

254271
private

0 commit comments

Comments
 (0)