Skip to content

Commit

Permalink
Merge pull request #61 from flavorjones/flavorjones-complex-selector-…
Browse files Browse the repository at this point in the history
…whitespace2

Include an explicit Combinator in the AST for implicit descendant
  • Loading branch information
kddnewton authored Jun 28, 2024
2 parents 03a7d6c + 4ee952e commit f9f7b3e
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 25 deletions.
14 changes: 12 additions & 2 deletions lib/syntax_tree/css/format.rb
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,24 @@ def visit_pseudo_element_selector(node)

# Visit a Selectors::Combinator node.
def visit_combinator(node)
node.value.format(q)
case node.value
when WhitespaceToken
q.text(" ")
when Array
q.text(" ")
node.value.each { |val| val.format(q) }
q.text(" ")
else
q.text(" ")
node.value.format(q)
q.text(" ")
end
end

# Visit a Selectors::ComplexSelector node.
def visit_complex_selector(node)
q.group do
node.child_nodes.each_with_index do |child_node, j|
q.text(" ") unless j == 0
child_node.format(q)
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/syntax_tree/css/pretty_print.rb
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ def visit_wqname(node)

# Visit a Selectors::Combinator node.
def visit_combinator(node)
token("combinator") do
token(node.class::PP_NAME) do
q.breakable
q.pp(node.value)
end
Expand Down
70 changes: 52 additions & 18 deletions lib/syntax_tree/css/selectors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,36 @@ def deconstruct_keys(keys)
end
end

# §15.1 https://www.w3.org/TR/selectors-4/#descendant-combinators
class DescendantCombinator < Combinator
TOKEN = WhitespaceToken
PP_NAME = "descendant-combinator"
end

# §15.2 https://www.w3.org/TR/selectors-4/#child-combinators
class ChildCombinator < Combinator
TOKEN = ">"
PP_NAME = "child-combinator"
end

# §15.3 https://www.w3.org/TR/selectors-4/#adjacent-sibling-combinators
class NextSiblingCombinator < Combinator
TOKEN = "+"
PP_NAME = "next-sibling-combinator"
end

# §15.4 https://www.w3.org/TR/selectors-4/#general-sibling-combinators
class SubsequentSiblingCombinator < Combinator
TOKEN = "~"
PP_NAME = "subsequent-sibling-combinator"
end

# §16.1 https://www.w3.org/TR/selectors-4/#the-column-combinator
class ColumnSiblingCombinator < Combinator
TOKEN = ["|", "|"]
PP_NAME = "column-sibling-combinator"
end

class ComplexSelector < Node
attr_reader :child_nodes

Expand Down Expand Up @@ -333,12 +363,12 @@ def relative_selector_list
def complex_selector
child_nodes = [compound_selector]

combinator_ = nil
compound_selector_ = nil
loop do
if (c = maybe { combinator })
child_nodes << c
end
if (s = maybe { compound_selector })
child_nodes << s
if maybe { (combinator_ = combinator) && (compound_selector_ = compound_selector) }
child_nodes << combinator_
child_nodes << compound_selector_
else
break
end
Expand All @@ -363,8 +393,6 @@ def relative_selector
# <compound-selector> = [ <type-selector>? <subclass-selector>*
# [ <pseudo-element-selector> <pseudo-class-selector>* ]* ]!
def compound_selector
consume_whitespace

type = maybe { type_selector }
subclasses = []

Expand Down Expand Up @@ -401,17 +429,13 @@ def simple_selector

# <combinator> = '>' | '+' | '~' | [ '|' '|' ]
def combinator
consume_whitespace

value =
options do
maybe { consume(">") } ||
maybe { consume("+") } ||
maybe { consume("~") } ||
maybe { consume("|", "|") }
end

Combinator.new(value: value)
options do
maybe { consume_combinator(ChildCombinator) } ||
maybe { consume_combinator(NextSiblingCombinator) } ||
maybe { consume_combinator(SubsequentSiblingCombinator) } ||
maybe { consume_combinator(ColumnSiblingCombinator) } ||
maybe { consume_combinator(DescendantCombinator) }
end
end

# <type-selector> = <wq-name> | <ns-prefix>? '*'
Expand Down Expand Up @@ -554,6 +578,16 @@ def one_or_more
end
end

def consume_combinator(combinator_class)
eat_whitespace = (combinator_class::TOKEN != WhitespaceToken)

consume_whitespace if eat_whitespace
result = consume(*combinator_class::TOKEN)
consume_whitespace if eat_whitespace

combinator_class.new(value: result)
end

def consume(*values)
result =
values.map do |value|
Expand Down
25 changes: 21 additions & 4 deletions test/selectors_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -128,15 +128,23 @@ class SelectorsTest < Minitest::Spec
end

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

assert_pattern do
actual => [
Selectors::ComplexSelector[
child_nodes: [
Selectors::TypeSelector[value: { name: { value: "section" } }],
Selectors::Combinator[value: { value: ">" }],
Selectors::TypeSelector[value: { name: { value: "table" } }]
Selectors::TypeSelector[value: { name: { value: "a" } }],
Selectors::DescendantCombinator,
Selectors::TypeSelector[value: { name: { value: "b" } }],
Selectors::ChildCombinator,
Selectors::TypeSelector[value: { name: { value: "c" } }],
Selectors::NextSiblingCombinator,
Selectors::TypeSelector[value: { name: { value: "d" } }],
Selectors::SubsequentSiblingCombinator,
Selectors::TypeSelector[value: { name: { value: "e" } }],
Selectors::ColumnSiblingCombinator,
Selectors::TypeSelector[value: { name: { value: "f" } }],
]
]
]
Expand Down Expand Up @@ -185,6 +193,7 @@ class SelectorsTest < Minitest::Spec
Selectors::ComplexSelector[
child_nodes: [
Selectors::TypeSelector[value: { name: { value: "section" } }],
Selectors::Combinator[value: { value: " " }],
Selectors::TypeSelector[value: { name: { value: "table" } }],
]
]
Expand All @@ -202,6 +211,7 @@ class SelectorsTest < Minitest::Spec
Selectors::TypeSelector[value: { name: { value: "section" } }],
Selectors::Combinator[value: { value: ">" }],
Selectors::TypeSelector[value: { name: { value: "table" } }],
Selectors::Combinator[value: { value: " " }],
Selectors::TypeSelector[value: { name: { value: "tr" } }]
]
]
Expand Down Expand Up @@ -249,6 +259,13 @@ class SelectorsTest < Minitest::Spec
".outer section.foo > table.bar tr",
)
end

it "handles all the combinators" do
assert_selector_format(
"a b > c + d ~ e || f",
"a b > c + d ~ e || f",
)
end
end

private
Expand Down

0 comments on commit f9f7b3e

Please sign in to comment.