Skip to content

feat(selector): support special chars in class names (#150)#162

Merged
pranaygp merged 2 commits into
masterfrom
worktree-agent-a916c4c89adb43c37
May 17, 2026
Merged

feat(selector): support special chars in class names (#150)#162
pranaygp merged 2 commits into
masterfrom
worktree-agent-a916c4c89adb43c37

Conversation

@pranaygp
Copy link
Copy Markdown
Owner

Summary

Closes #150.

Tailwind-style class names containing :, /, or non-ASCII characters were not handled correctly:

  • findSelector stopped at /, so bg-red-500/50 was cut off mid-word.
  • findDefinition built a regex from the raw selector value, so md:flex would not match .md\:flex in compiled CSS (and : was unescaped at the regex level).
  • The chained-suffix matcher used [\w-], which excluded escaped chars like \: or \/ in subsequent class/id components.

Changes

  • server/src/core/findSelector.ts — drop / from the start-of-word boundary set; the existing loop already passed through : and non-ASCII chars.
  • server/src/core/findDefinition.ts — introduce escapeSelectorForRegex that escapes regex meta-chars and emits \\?: / \\?/ so source classes like md:flex match compiled symbols like .md\:flex. Extend the chained-suffix character class to include \\, :, and /.
  • tests/fixture/tailwind.css + tests/fixture/tailwind.html — new fixtures with Tailwind-style classes and a Unicode class name.
  • tests/src/findSelector.test.ts + tests/src/findDefinition.test.ts — 8 new tests covering md:flex, bg-red-500/50, café, and style:sm (the example from the issue).

Test plan

  • yarn build passes with zero TS errors.
  • yarn test — full suite (32 tests, including 8 new ones) passes.
  • yarn lint clean.
  • Existing pseudo-class behavior (:hover, ::before) still recognized as pseudo, not class.

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings May 16, 2026 20:32
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends the selector parsing + definition lookup logic to better support Tailwind-style class names containing special characters (notably : and /) and adds fixtures/tests to cover these cases (including a Unicode class name).

Changes:

  • Update findSelector word-boundary scanning so / can be part of a class token (e.g. bg-red-500/50).
  • Update findDefinition regex construction to escape selector values correctly and to match compiled CSS escapes like \.md\:flex.
  • Add Tailwind-oriented HTML/CSS fixtures plus new unit tests for md:flex, bg-red-500/50, style:sm, and café.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
server/src/core/findSelector.ts Adjusts selector token expansion rules to allow / inside class names.
server/src/core/findDefinition.ts Escapes selector values for regex matching and broadens chained-suffix matching for Tailwind escapes.
tests/src/findSelector.test.ts Adds fixture-driven tests for special-character and Unicode class extraction.
tests/src/findDefinition.test.ts Adds definition-lookup tests for escaped Tailwind-style selectors.
tests/fixture/tailwind.html New HTML fixture containing Tailwind-like and Unicode class names.
tests/fixture/tailwind.css New CSS fixture with escaped selector forms (e.g. .md\\:flex, .bg-red-500\\/50).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 31 to 41
// expand selection to this word specifically
// NOTE: `/` is intentionally not a boundary so Tailwind-style class names
// like `bg-red-500/50` (and escaped forms like `md\:flex`) are captured.
while (
start > 0 &&
text.charAt(start - 1) !== " " &&
text.charAt(start - 1) !== "'" &&
text.charAt(start - 1) !== '"' &&
text.charAt(start - 1) !== "\n" &&
text.charAt(start - 1) !== "/" &&
text.charAt(start - 1) !== "<"
)
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 7e05e2a. The backward scan now treats / as a boundary only when the previous-previous char is < — i.e. only in the </ closing-tag sequence — so </div> resolves to div and aligns with the HTML scanner's EndTag offset, while Tailwind slash-values like bg-red-500/50 keep flowing through. Added a </h1> test case to findSelector.test.ts (run across html/jsx/php fixtures) to lock the behavior down.

Comment thread server/src/core/findDefinition.ts Outdated
Comment on lines +87 to +90
// Suffix matcher: allow chained selectors, including class/id names that
// contain CSS-escaped chars like `\:` or `\/` (Tailwind).
selection +=
"(\\[[^\\]]*\\]|:{1,2}[\\w-()]+|\\.[\\w\\\\:/-]+|#[\\w\\\\:/-]+)*\\s*";
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 7e05e2a. The chained-suffix matcher now uses a Unicode-aware identifier class ([\p{L}\p{N}_\\:/-]) with the u flag on both symbolRegexp and fileRegexp, so chained selectors with non-ASCII names like .foo.café and h1.café resolve correctly. Two ancillary tweaks required by switching to u: (a) escapeSelectorForRegex no longer emits a backslash before : or / (they're not regex meta-chars, and identity escapes are a SyntaxError under u); (b) the pseudo-class char range was reordered from [\w-()] to [\w()-] so \w- isn't read as a range. Added .foo.café and h1.café rules to tailwind.css plus matching tests resolving foo and h1 against those chained rules.

pranaygp added a commit that referenced this pull request May 17, 2026
…ctors

Address PR #162 review feedback:

- findSelector: treat `</` as a backward-scan boundary (only when `/` is
  preceded by `<`), so `</div>` resolves to tag `div` and aligns with
  the HTML scanner's EndTag offset. Tailwind slash-values like
  `bg-red-500/50` keep working because `/` is only a boundary in the
  closing-tag sequence.
- findDefinition: switch the chained-suffix matcher to Unicode property
  escapes (`\p{L}\p{N}_`) with the `u` flag, so chained class/id names
  containing non-ASCII chars (e.g. `.foo.café`, `h1.café`) match. Adjust
  `escapeSelectorForRegex` to emit `:` and `/` unescaped (they are not
  regex meta-chars, and identity escapes are SyntaxErrors under `u`).
  Reorder the pseudo-class char range so `\w-` is not interpreted as a
  range under `u`.

Tests:
- Add `</h1>` closing-tag case to `findSelector` for html/jsx/php.
- Add chained Unicode fixtures (`.foo.café`, `h1.café`) and matching
  `findDefinition` cases.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
pranaygp and others added 2 commits May 16, 2026 17:37
Tailwind-style class names containing `:`, `/`, or non-ASCII characters
(e.g. `md:flex`, `bg-red-500/50`, `café`) were not captured by the
word-boundary scan in findSelector, and compiled CSS using backslash
escapes (`.md\:flex`) was not matched by findDefinition.

- findSelector: drop `/` from the start boundary so slash-value modifiers
  are captured (the existing loop already passed through `:` and
  non-ASCII chars).
- findDefinition: regex-escape the selector value and accept an optional
  backslash before `:` or `/` so source classes like `md:flex` match
  compiled symbols like `.md\:flex`. Extend the chained-suffix character
  class to include `\\`, `:`, and `/`.
- Add tailwind.{css,html} fixtures and tests covering `md:flex`,
  `bg-red-500/50`, `café`, and `style:sm` for both findSelector and
  findDefinition.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ctors

Address PR #162 review feedback:

- findSelector: treat `</` as a backward-scan boundary (only when `/` is
  preceded by `<`), so `</div>` resolves to tag `div` and aligns with
  the HTML scanner's EndTag offset. Tailwind slash-values like
  `bg-red-500/50` keep working because `/` is only a boundary in the
  closing-tag sequence.
- findDefinition: switch the chained-suffix matcher to Unicode property
  escapes (`\p{L}\p{N}_`) with the `u` flag, so chained class/id names
  containing non-ASCII chars (e.g. `.foo.café`, `h1.café`) match. Adjust
  `escapeSelectorForRegex` to emit `:` and `/` unescaped (they are not
  regex meta-chars, and identity escapes are SyntaxErrors under `u`).
  Reorder the pseudo-class char range so `\w-` is not interpreted as a
  range under `u`.

Tests:
- Add `</h1>` closing-tag case to `findSelector` for html/jsx/php.
- Add chained Unicode fixtures (`.foo.café`, `h1.café`) and matching
  `findDefinition` cases.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@pranaygp pranaygp force-pushed the worktree-agent-a916c4c89adb43c37 branch from 7e05e2a to 36064fc Compare May 17, 2026 00:38
@pranaygp pranaygp merged commit d4b8cc8 into master May 17, 2026
2 checks passed
@pranaygp pranaygp deleted the worktree-agent-a916c4c89adb43c37 branch May 17, 2026 00:39
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.

Support for finding class names including special symbols

2 participants