sparklemotion / nokogiri

Nokogiri (鋸) makes it easy and painless to work with XML and HTML from Ruby.
https://nokogiri.org/
MIT License
6.12k stars 901 forks source link

[Nokogiri should accept multiple selectors as arguments to the `:not` CSS pseudo-class #3207

Open flavorjones opened 2 months ago

flavorjones commented 2 months ago

Please describe the bug

CSS pseudo-class :not should be able to accept multiple selectors as arguments:

https://developer.mozilla.org/en-US/docs/Web/CSS/:not

Raised by @stoivo in a comment in #3193

Help us reproduce what you're seeing

#! /usr/bin/env ruby

require "bundler/inline"

gemfile do
  source "https://rubygems.org"
  gem "nokogiri", path: "."
end

parser = Nokogiri::CSS::Parser.new
pp parser.parse("div:not(.one,.two)")

emits:

/home/flavorjones/code/oss/nokogiri/lib/nokogiri/css/parser_extras.rb:86:in `on_error': unexpected ',' after '#<Nokogiri::CSS::Node:0x0000756541fec678>' (Nokogiri::CSS::SyntaxError)

Expected behavior

https://developer.mozilla.org/en-US/docs/Web/CSS/:not#description says that:

You can negate several selectors at the same time. Example: :not(.foo, .bar) is equivalent to :not(.foo):not(.bar).

Given

parser.parse("div:not(.one)").first.to_xpath("//", Nokogiri::CSS::XPathVisitor.new)
# //div[not(contains(concat(' ',normalize-space(@class),' '),' one '))]

then two selectors should transpile to

parser.parse('div:not(.foo):not(.bar)').first.to_xpath("//", Nokogiri::CSS::XPathVisitor.new)
# //div[not(contains(concat(' ',normalize-space(@class),' '),' foo ')) and not(contains(concat(' ',normalize-space(@class),' '),' bar '))]
flavorjones commented 1 month ago

I've updated this to be a feature, since in Selectors Level 3, :not only accepted a single argument. But I'm working on supporting some Level 4 features including this.