Skip to content

fix(parser): collect definitions inside at-rule bodies (#38) - #43

Merged
jo16oh merged 5 commits into
mainfrom
38-fix-collect-definitions-inside-media-queries
May 4, 2026
Merged

fix(parser): collect definitions inside at-rule bodies (#38)#43
jo16oh merged 5 commits into
mainfrom
38-fix-collect-definitions-inside-media-queries

Conversation

@jo16oh

@jo16oh jo16oh commented May 3, 2026

Copy link
Copy Markdown
Owner

Closes #38.

Previously, the parser only descended into selector blocks. Definitions and var() references inside any at-rule (@media, @property, @supports, @keyframes, …) were invisible to LSP completion / hover / go-to-definition / rename and to the no-undefined-variable-use / no-inconsistent-variable-definition rules. This PR makes at-rule bodies first-class.

Changes

  1. At-rule bodies (c13d868, 37dcfde) — every at-rule body except @property (special-cased below) is parsed by the main loop just like a selector block. Statement-form at-rules (@import, @charset, @namespace) terminate at ; and never open a body. Definitions and var() references inside @media, @supports, @keyframes, @font-face, @page, etc. now flow through unchanged.

  2. Searcher hardening (4f96693) — VariableUsages::matches used value.contains("var(") plus a substring scan, which fired on --ident inside strings / url(...) and on idents glued to a preceding token (font--name). Replaced with a state-aware scanner that skips strings / comments / url(...) and only fires at a real token boundary (case-insensitive on var(/url().

  3. @property rules (84da143) — synthesise one Property per @property: ident points at the prelude --name, value at the initial-value declaration (empty if absent). These now flow through completion / hover / rename / undefined-variable checks like any other definition.

… group rules (#38)

Treat @media, @supports, @container, @layer, @scope, and @starting-style
as transparent wrappers: consume only the prelude and let the main loop
descend into the body so inner property declarations are collected. Other
at-rules (@Property, @import, @font-face, …) keep being skipped whole.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@jo16oh jo16oh linked an issue May 3, 2026 that may be closed by this pull request
jo16oh and others added 3 commits May 3, 2026 20:20
…le references

`VariableUsages::matches` previously did `value.contains("var(")` and a
naive substring scan for `--ident`, both of which fired on content that
sits inside string literals or `url(...)` tokens (e.g. `content: "var(--x)"`,
`url("file--name.png")`). It also matched `--ident` glued to the tail of a
preceding identifier (`font--name`).

Replace it with a single state-aware scanner that walks the value once,
skipping `"…"`/`'…'` strings, `/* … */` comments, and `url(…)` token
contents, and only fires on `var(`/`--ident` at a real token boundary.
Also accepts `Var(` / `URL(` case-insensitively to match CSS rules.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Phase 2 of #38: synthesise one Property per @Property rule, with
ident pointing at the prelude --name and value pointing at the
initial-value declaration (or empty if absent). Custom properties
declared via @Property now show up in LSP completion / hover /
go-to-definition / rename, and don't trip no-undefined-variable-use
when referenced.

Other at-rules continue to be skipped.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Drop the `is_transparent_at_rule` whitelist (and the now-orphan
`skip_at_rule_body` helper). With `@property` keeping its dedicated
prelude-name + initial-value synthesis path, every other at-rule body
is now parsed by the main loop just like a selector block.

Definitions and `var()` references inside CSS at-rules previously left
opaque (`@keyframes`, `@font-face`, `@page`, `@counter-style`,
`@font-feature-values`, …) now flow through the same downstream
pipeline. Statement-form at-rules (`@import`, `@charset`, `@namespace`)
terminate at the `;` in `skip_at_rule_prelude`, so no body is ever
opened for them.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@jo16oh jo16oh changed the title feat(parser): collect definitions inside @media and other conditional group rules feat(parser): collect definitions inside at-rule bodies (#38) May 4, 2026
… name

`scan_at_property_rule` skipped only spaces, tabs, and newlines before
reading the prelude name, so a valid comment like
`@property /* doc */ --logo { … }` left the scanner at `/`, the
identifier loop bailed immediately, and the helper returned `None`.
The body was then consumed by the main loop as if it were a selector
block, silently dropping `--logo` as a definition and leaking
`initial-value: red` as a regular property declaration.

Replace `skip_inline_and_newlines` with a `skip_trivia` helper that
also consumes `/* … */` comments. Single call site, behaviour
unchanged for the existing whitespace-only inputs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@jo16oh jo16oh changed the title feat(parser): collect definitions inside at-rule bodies (#38) fix(parser): collect definitions inside at-rule bodies (#38) May 4, 2026
@jo16oh
jo16oh merged commit 8662ae0 into main May 4, 2026
8 checks passed
Sign up for free to 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.

fix: collect definitions inside media queries

1 participant