Skip to content

Commit eb3e2a7

Browse files
authored
Merge pull request #58 from flavorjones/flavorjones-work-on-selectors
Improve selectors, fix complex selector parsing issues
2 parents 24042c2 + 45b5ece commit eb3e2a7

File tree

5 files changed

+365
-13
lines changed

5 files changed

+365
-13
lines changed

lib/syntax_tree/css/format.rb

+37
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,43 @@ def visit_type_selector(node)
7676
node.value.format(q)
7777
end
7878
end
79+
80+
# Visit a Selectors::ClassSelector node.
81+
def visit_class_selector(node)
82+
q.text(".")
83+
node.value.format(q)
84+
end
85+
86+
# Visit a Selectors::Combinator node.
87+
def visit_combinator(node)
88+
node.value.format(q)
89+
end
90+
91+
# Visit a Selectors::ComplexSelector node.
92+
def visit_complex_selector(node)
93+
q.group do
94+
node.child_nodes.each_with_index do |child_node, j|
95+
q.text(" ") unless j == 0
96+
child_node.format(q)
97+
end
98+
end
99+
end
100+
101+
# Visit a Selectors::CompoundSelector node.
102+
def visit_compound_selector(node)
103+
q.group do
104+
node.type.format(q) if node.type
105+
node.subclasses.each do |subclass|
106+
subclass.format(q)
107+
end
108+
# TODO: pseudo-elements
109+
end
110+
end
111+
112+
def visit_wqname(node)
113+
node.prefix.format(q) if node.prefix
114+
node.name.format(q)
115+
end
79116
end
80117
end
81118
end

lib/syntax_tree/css/pretty_print.rb

+56
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,62 @@ def visit_wqname(node)
421421
end
422422
end
423423

424+
# Visit a Selectors::Combinator node.
425+
def visit_combinator(node)
426+
token("combinator") do
427+
q.breakable
428+
q.pp(node.value)
429+
end
430+
end
431+
432+
# Visit a Selectors::ComplexSelector node.
433+
def visit_complex_selector(node)
434+
token("complex-selector") do
435+
node.child_nodes.each do |child|
436+
q.breakable
437+
q.pp(child)
438+
end
439+
end
440+
end
441+
442+
# Visit a Selectors::CompoundSelector node.
443+
def visit_compound_selector(node)
444+
token("compound-selector") do
445+
q.breakable
446+
q.pp(node.type)
447+
448+
q.breakable
449+
q.text("(subclasses")
450+
451+
if node.subclasses.any?
452+
q.nest(2) do
453+
q.breakable
454+
q.seplist(node.subclasses) { |subclass| q.pp(subclass) }
455+
end
456+
457+
q.breakable("")
458+
end
459+
460+
q.text(")")
461+
462+
q.breakable("")
463+
q.text("(pseudo-elements")
464+
465+
if node.pseudo_elements.any?
466+
q.nest(2) do
467+
q.breakable
468+
q.seplist(node.pseudo_elements) do |pseudo_element|
469+
q.pp(pseudo_element)
470+
end
471+
end
472+
473+
q.breakable("")
474+
end
475+
476+
q.text(")")
477+
end
478+
end
479+
424480
private
425481

426482
def token(name)

lib/syntax_tree/css/selectors.rb

+84-13
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,73 @@ def deconstruct_keys(keys)
7171
end
7272
end
7373

74-
Combinator = Struct.new(:value, keyword_init: true)
75-
ComplexSelector = Struct.new(:left, :combinator, :right, keyword_init: true)
76-
CompoundSelector = Struct.new(:type, :subclasses, :pseudo_elements, keyword_init: true)
74+
class Combinator < Node
75+
attr_reader :value
76+
77+
def initialize(value:)
78+
@value = value
79+
end
80+
81+
def accept(visitor)
82+
visitor.visit_combinator(self)
83+
end
84+
85+
def child_nodes
86+
[value]
87+
end
88+
89+
alias deconstruct child_nodes
90+
91+
def deconstruct_keys(keys)
92+
{ value: value }
93+
end
94+
end
95+
96+
class ComplexSelector < Node
97+
attr_reader :child_nodes
98+
99+
def initialize(child_nodes:)
100+
@child_nodes = child_nodes
101+
end
102+
103+
def accept(visitor)
104+
visitor.visit_complex_selector(self)
105+
end
106+
107+
alias deconstruct child_nodes
108+
109+
def deconstruct_keys(keys)
110+
{ child_nodes: child_nodes }
111+
end
112+
end
113+
114+
class CompoundSelector < Node
115+
attr_reader :type, :subclasses, :pseudo_elements
116+
117+
def initialize(type:, subclasses:, pseudo_elements:)
118+
@type = type
119+
@subclasses = subclasses
120+
@pseudo_elements = pseudo_elements
121+
end
122+
123+
def accept(visitor)
124+
visitor.visit_compound_selector(self)
125+
end
126+
127+
def child_nodes
128+
[type, subclasses, pseudo_elements].flatten
129+
end
130+
131+
alias deconstruct child_nodes
132+
133+
def deconstruct_keys(keys)
134+
{
135+
type: type,
136+
subclasses: subclasses,
137+
pseudo_elements: pseudo_elements
138+
}
139+
end
140+
end
77141

78142
# The ID of an element, e.g., #foo
79143
# https://www.w3.org/TR/selectors-4/#typedef-id-selector
@@ -267,27 +331,30 @@ def relative_selector_list
267331

268332
# <complex-selector> = <compound-selector> [ <combinator>? <compound-selector> ]*
269333
def complex_selector
270-
left = compound_selector
334+
child_nodes = [compound_selector]
271335

272336
loop do
273-
if (combinator = maybe { combinator })
274-
ComplexSelector.new(left: left, combinator: combinator, right: compound_selector)
275-
elsif (right = maybe { compound_selector })
276-
ComplexSelector.new(left: left, combinator: nil, right: right)
337+
if (c = maybe { combinator })
338+
child_nodes << c
339+
end
340+
if (s = maybe { compound_selector })
341+
child_nodes << s
277342
else
278343
break
279344
end
280345
end
281346

282-
left
347+
if child_nodes.length > 1
348+
ComplexSelector.new(child_nodes: child_nodes)
349+
else
350+
child_nodes.first
351+
end
283352
end
284353

285354
# <relative-selector> = <combinator>? <complex-selector>
286355
def relative_selector
287-
combinator = maybe { combinator }
288-
289-
if combinator
290-
RelativeSelector.new(combinator: combinator, complex_selector: complex_selector)
356+
if (c = maybe { combinator })
357+
RelativeSelector.new(combinator: c, complex_selector: complex_selector)
291358
else
292359
complex_selector
293360
end
@@ -296,6 +363,8 @@ def relative_selector
296363
# <compound-selector> = [ <type-selector>? <subclass-selector>*
297364
# [ <pseudo-element-selector> <pseudo-class-selector>* ]* ]!
298365
def compound_selector
366+
consume_whitespace
367+
299368
type = maybe { type_selector }
300369
subclasses = []
301370

@@ -332,6 +401,8 @@ def simple_selector
332401

333402
# <combinator> = '>' | '+' | '~' | [ '|' '|' ]
334403
def combinator
404+
consume_whitespace
405+
335406
value =
336407
options do
337408
maybe { consume(">") } ||

lib/syntax_tree/css/visitor.rb

+9
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,15 @@ def visit_child_nodes(node)
132132
# Visit a Selectors::ClassSelector node.
133133
alias visit_class_selector visit_child_nodes
134134

135+
# Visit a Selectors::Combinator node.
136+
alias visit_combinator visit_child_nodes
137+
138+
# Visit a Selectors::ComplexSelector node.
139+
alias visit_complex_selector visit_child_nodes
140+
141+
# Visit a Selectors::CompoundSelector node.
142+
alias visit_compound_selector visit_child_nodes
143+
135144
# Visit a Selectors::IdSelector node.
136145
alias visit_id_selector visit_child_nodes
137146

0 commit comments

Comments
 (0)