Skip to content

Implement Selectors Level 4: support complex selectors inside :has(), :is(), :where(), and selector-accepting pseudo-elements - #75

Merged
jaraco merged 9 commits into
mainfrom
copilot/implement-selectors-level-4
Apr 27, 2026
Merged

Implement Selectors Level 4: support complex selectors inside :has(), :is(), :where(), and selector-accepting pseudo-elements#75
jaraco merged 9 commits into
mainfrom
copilot/implement-selectors-level-4

Conversation

CopilotAI commented Apr 21, 2026

Copy link
Copy Markdown
Contributor

The selector parser treated pseudo-class function arguments (:has(), :is(), :where(), etc.) as simple expressions (an+b-style), rejecting attribute selectors and pseudo-classes inside them. Additionally, CSS4 pseudo-elements that accept selector arguments (::slotted(), ::cue(), ::cue-region()) were not supported.

# Previously failed:
fieldset:has(option[value="foo"]:checked) {}
fieldset:has(option:checked) {}
x::slotted(:is(div)) {}
x::slotted(.foo:hover) {}
# Already worked:
fieldset:has(option) {}

Changes

  • Constants.selector_pseudos — new frozenset of Level 4 pseudo-classes that accept full selector lists: :has(, :is(, :where(, :matches(, :any(

  • Constants.selector_pseudo_elements — new frozenset of CSS4 pseudo-elements that accept full selector arguments: ::slotted(, ::cue(, ::cue-region(

  • _pseudo() — detects selector-accepting pseudo-classes and pushes a pseudo-class-has context returning simple_selector_sequence (which permits attrib, pseudo, HASH, class) instead of the restricted expressionstart; also handles selector_pseudo_elements by pushing a pseudo-element context with simple_selector_sequence

  • _ident() — handles pseudo-class-has context by treating identifiers as type-selectors, returning simple_selector_sequence2 + combinator to allow compound selector chaining

  • _S() — spaces inside pseudo-class-has when a combinator is expected are treated as descendant combinators, enabling :has(div span)

  • _char() — new ) handler for pseudo-class-has context to properly close the function and restore outer selector state; fixes the return value when closing pseudo-class-has inside a pseudo-element context (e.g. ::slotted(:is(div))); adds a handler to close selector-accepting pseudo-elements (e.g. ::slotted(.foo))

CopilotAI linked an issue Apr 21, 2026 that may be closed by this pull request
Agent-Logs-Url: https://github.com/jaraco/cssutils/sessions/9d729229-24d0-4f45-886f-0d097b79d00b
Co-authored-by: jaraco <308610+jaraco@users.noreply.github.com>
CopilotAI changed the title [WIP] Add support for has(arbitrary-selector) in Selectors Level 4Implement Selectors Level 4: support complex selectors inside :has(), :is(), :where()Apr 21, 2026
CopilotAI requested a review from jaracoApril 21, 2026 01:11
@jaraco
jaracoforce-pushed the copilot/implement-selectors-level-4 branch from fb2e584 to 7bcdbf7CompareApril 27, 2026 19:53
@jaraco
jaraco marked this pull request as ready for review April 27, 2026 19:54
@jaraco

Copy link
Copy Markdown
Owner

@copilot Add a newsfragment (towncrier style).

CopilotAI commented Apr 27, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot Add a newsfragment (towncrier style).

Added newsfragments/66.feature.rst in commit b7efeec.

Comment threadcssutils/css/selector.py Outdated

CopilotAI commented Apr 27, 2026

Copy link
Copy Markdown
ContributorAuthor

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • http://pypi.org:80/
    • Triggering command: /usr/bin/python python -m pytest tests/ -q --tb=short --cov=cssutils/css/selector --cov-report=term-missing (http block)

If you need me to access, download, or install something from one of these locations, you can either:

CopilotAI changed the title Implement Selectors Level 4: support complex selectors inside :has(), :is(), :where()Implement Selectors Level 4: support complex selectors inside :has(), :is(), :where(), and selector-accepting pseudo-elementsApr 27, 2026
@jaraco

Copy link
Copy Markdown
Owner

@copilot During your investigation on the coverage issue, you observed that the uncovered code was modeled after other parts of the code that lacked coverage. Please review the PR to ensure that there's no duplication introduced by the proposed change (that any common behaviors are refactored to be shared instead of copied); there may be none.

CopilotAI commented Apr 27, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot During your investigation on the coverage issue, you observed that the uncovered code was modeled after other parts of the code that lacked coverage. Please review the PR to ensure that there...

Reviewed and found two instances of duplication, both now eliminated in c3a084d:

  1. _pseudo(): The two if-blocks that both called self.context.append(...) then return Constants.simple_selector_sequence (one for selector_pseudos, one for selector_pseudo_elements) were merged into a single if/elif/else with one shared append+return.

  2. _char(): The separate pseudo-element + combinator in expected closing block (which had the same append/pop/check structure as the existing expression-closing block) was removed. Its trigger condition was folded into the existing block's condition as an or clause. A comment was added to clarify that context is intentionally checked pre-pop — it identifies the type of pseudo that was just closed (element vs class), which governs what tokens may follow.

@jaraco

Copy link
Copy Markdown
Owner

@copilot Remove .gitignore and LICENSE; These concerns are managed outside the repo.

CopilotAI commented Apr 27, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot Remove .gitignore and LICENSE; These concerns are managed outside the repo.

Removed both files in commit 2e7a0c4.

@jaraco
jaraco merged commit e8a95f0 into mainApr 27, 2026
28 checks passed
@jaraco
jaraco deleted the copilot/implement-selectors-level-4 branch April 27, 2026 20:34
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement (parts of) Selectors Level 4

2 participants

@jaraco