You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
:lang() under Mode::Html and Mode::Xhtml now accepts a * subtag in any
position, which is what Selectors 4's RFC 4647 extended filtering allows: :lang(*-CH) — "any language as written in Switzerland" — was rejected as "a
wildcard outside the final subtag", and now matches de-CH and fr-Latn-CH.
A leading * stands for the tag's first subtag, so the walk starts one
subtag in rather than anchoring with starts-with, and :lang(*-CH)
therefore does not match the tag ch itself. A * anywhere else moves past
nothing — every range subtag after the first is already searched for through
the whole remaining tag — so it drops out of the translation: :lang(de-*-DE)
emits exactly what :lang(de-DE) emits, as :lang(en-*) already emitted what :lang(en) does. Mode::Generic still takes only * and a final en-*,
since it hands the range to XPath's lang(), a prefix match with nowhere to
put the rest; its error now says so.
Changed
An option or optgroup inside a disabled select is now :disabled rather
than :enabled, and an option is now disabled by the nearest optgroup
above it rather than only by a parent one. Both are rules HTML added to
"actually disabled" after this translation was written: an optgroup or option "whose nearest ancestor select is disabled" is actually disabled,
and an option is disabled by the nearest ancestor that settles it — the
walk ends at a select, hr, datalist or option, so an option in a <datalist> inside a disabled select stays :enabled, and an option
nested below its optgroup rather than directly under it is disabled. This
changes what :disabled, :enabled, option:disabled, option:enabled, optgroup:disabled and optgroup:enabled select over a document with a
disabled select in it, which is the ordinary way a whole control is
switched off, as well as the expressions they translate to. One corner is
approximated, as the legend counting already is: HTML's walk also gives up
once it has passed a second optgroup, which XPath cannot count, so an option two optgroups deep — non-conforming markup — inside a disabled select is :disabled here.
The error for a namespace prefix that is not an XML NCName now names the
rule rather than an operation the prefix cannot have: \31 ns|div gives a namespace prefix that is not an XPath name (`1ns`) where it
previously gave "a namespace prefix that needs quoting". Quoting is what a local name that cannot be a node test gets — it folds into a name() or local-name() comparison — so the old wording named the very fallback the
prefix is being refused for. The README now records why that fallback stops
at the prefix: it is an exact rewrite of a node test, whereas comparing a
whole prefix:name against name() (which some other translators emit
here) would match on how the document spells its prefix, where every
prefix this crate emits is resolved by what the caller bound it to — and
nothing the caller binds could help, since an XPath expression cannot name
such a prefix at all. Which selectors translate is unchanged.
The Mode::Generic error for a wildcard it cannot place now names the reason
rather than only the placement: the :lang() language range "*-CH" (a wildcard outside the final subtag, which XPath's lang() cannot express).
The range is no longer an error everywhere, so the message has to say which
translator it is beyond.
The caret in a parse error's message now points at the token the message
names, rather than at the position the parse stopped on. [a=b c] reports unexpected `c` at byte 5, the c, where it previously reported byte 4,
the space in front of it; :nth-child(foo) reports unexpected `foo` at
byte 11 rather than byte 14, the ) past it; and a::part(b) reports `part` is not a supported pseudo-class or pseudo-element at byte 3, the
name, rather than byte 8, the argument. Error::Parse's offset is
therefore usable as a span: where the message echoes a piece of the selector,
the offset is where that piece was written. Positions already on what they
named — div.-5, reported on the -5 rather than on the . — do not move.
A selector group whose first token cannot start a compound is now reported by
that token rather than as ParseErrorKind::EmptySelector: #1abc gives unexpected `#1abc` where it previously gave "the selector is empty", which
sent the reader looking for a missing selector instead of at the
digit-leading identifier that is there. It is also the message p#1abc has
always given, so the same mistake no longer reports two ways depending on
whether a type selector precedes it. EmptySelector now means only what it
says: "", " ", "a, , b".
The README listed :link/:any-link as matching an a or area with an href without saying that the link element, which also carries an href,
is outside that set — the difference a reader hits first, since other
translators (selectr) do match it. It now says so, and the exclusion is
quoted from HTML's own wording ("all a elements that have an href
attribute, and all area elements that have an href attribute, must match
one of :link and :visited") where the translation makes it, so the decision
is not re-opened from memory. Both HTML fixtures grew a <link href> in a head, pinning at the document level what link:link → link[0] already
pinned as a string. No behaviour changed.
The README and the code left the :disabled/:enabled element set open to
being read as type-sensitive, since it sits beside :required, :read-write and :placeholder-shown, where an input's type does decide.
It is not: HTML's disabled attribute has no "Applies to" list of type
states, so <input type="hidden" disabled> is :disabled, and neither half
of the pair emits a @type test at all. Other translators (selectr) drop
Hidden from both halves, a carve-out the spec does not make. The README now
says the set is by element alone, the constant holding it says why, and tests/html_mode.rs asserts the absence of the @type test rather than
leaving it to be noticed. Both HTML fixtures grew a disabled hidden input
and the differential fixture a disabled Hidden/RANGE pair, so a document
pins it too. No behaviour changed.
[attr$=value] now spaces the subtraction in the expression it translates
to: a[href$="pdf"] gives substring(@href, string-length(@href) - 2) = 'pdf' where it previously gave string-length(@href)-2. The two parse
identically as XPath, so only the string differs — but the string is the
contract. This was the crate's last unspaced binary operator; every other one
it emits already carried a space on both sides. selectr 0.7-0 made the same
change, so the two remain comparable string-for-string.