Skip to content

feat(lsp): show resolved color on hover - #39

Merged
jo16oh merged 28 commits into
mainfrom
22-feat-show-resolved-color-in-vscode
May 1, 2026
Merged

feat(lsp): show resolved color on hover#39
jo16oh merged 28 commits into
mainfrom
22-feat-show-resolved-color-in-vscode

Conversation

@jo16oh

@jo16oh jo16oh commented Apr 30, 2026

Copy link
Copy Markdown
Owner

#22

jo16oh and others added 15 commits April 28, 2026 18:29
Register a textDocument/documentColor LSP provider that walks var()
tokens in property values, recursively resolves them, and emits
ColorInformation when the resolved value is a color. Definition sites
(--*) are skipped to avoid duplicating VSCode's built-in CSS color
provider.

colorPresentation returns an empty array — color picker editing is not
supported in this iteration.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
VSCode's ColorDetector only re-queries documentColor for files whose
content changed. When a definition file is edited, usage files in other
editors keep stale swatches because their text didn't change.

When the client supports dynamic registration, register the color
provider via client/registerCapability and re-register on changes that
may affect cross-file color resolution. VSCode's
colorProviderRegistry.onDidChange triggers ColorDetector to recompute
on every open editor; back-to-back unregister+register coalesces into a
single recompute (no flicker, no duplicates).

Falls back to static ColorProviderCapability::Simple(true) when the
client doesn't advertise dynamic_registration.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replaces the abandoned textDocument/documentColor implementation with a
hover-based preview. var(--name) usages now show the resolved value on
hover, with a markdown color swatch when the value parses as a color.
Multiple definitions across files are listed with their locations.

Hover is re-queried by the client on every cursor move, so cross-file
edits propagate without needing a workspace refresh request — the missing
piece that broke documentColor in Helix and Zed. See
docs/lsp-document-color-refresh.md for background.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… form

Hover was running each definition through lightningcss's serializer,
which rewrites colors into their shortest equivalent form (`hsl(0, 100%,
50%)` → `red`, `#00ff00` → `#0f0`). For users who write tokens in hsl or
hwb, the hovered text no longer matched the source.

Walk var() chains using the parsed token list but return each terminal
definition's raw source text. Fallback resolution (when the var is
undefined and the call has a fallback) still goes through the serializer
since there is no source definition to copy from.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
A bare `\n` between entries collapsed onto one line in markdown — the
list-bullet rendering that previously separated them was removed when
the `- ` prefix was dropped. Switch to `  \n` (trailing two spaces) so
each definition renders on its own line.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…bility

Zed's hover renderer ignores the trailing-two-space hard break that
VSCode honors, collapsing multiple definitions onto one line. A blank
line (paragraph break) is rendered consistently across VSCode, Zed,
and Helix.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Helix renders \n\n as a true paragraph break with a blank line between
entries, while VSCode and Zed collapse it to a single line gap. There
is no markdown sequence that produces the same visual spacing in all
three clients, so capture client_info.name from the initialize request
and pick the separator per client: trailing two-space hard break for
Helix, paragraph break for everyone else.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Reuse the hover formatting helpers to populate each completion item's
documentation field. Single-def items show the resolved value with a
swatch when applicable; multi-def items list every definition with its
file:line location, matching what hover renders. The detail field still
shows just the last value so the inline preview stays compact.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
VSCode renders the inline SVG data URI in hover popups but blocks it in
the completion documentation panel, which appears to suppress the popup
entirely. Plumb an include_swatch flag through the formatters and pass
false from completion so the documentation contains text-only entries.
Hover behavior is unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds a faint "cvk" description to the right of each completion label,
making our items visually distinguishable from those provided by other
CSS language servers.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Move the inline value display to labelDetails.detail and gate it on
single-definition variables. When multiple definitions exist, the inline
value is ambiguous, so the value is shown only in the hover and the
completion documentation panel.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@jo16oh jo16oh linked an issue Apr 30, 2026 that may be closed by this pull request
jo16oh and others added 12 commits April 30, 2026 10:24
The hover-color/components/app.css fixture was reformatted from
single-line to multi-line in d44b24a, but lsp_hover.rs was still
addressing the old layout, so every hover position request landed
past end-of-line and the server returned null. Realign the line/col
arguments and the inline layout comment.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace bespoke string-scanning (extract_enclosing_var_call,
resolve_var_call) with a check against the Searcher's already-tracked
variable usages: locate the property whose value byte range contains
the cursor, then look up the Var token by name in its parsed token
list. Drops the substring re-parse path entirely.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Move format_single, format_multi_def, resolve_to_raw_value,
multi_def_separator and the swatch_markdown helper out of hover.rs
into a new var_markdown module so completion no longer reaches across
into the hover module for them.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Both hover content and completion documentation are forms of variable
description, so name the module after that role.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Drop the line-scanning extract_variable_at_cursor + name-keyed
find_var_by_name lookup in favour of walking the matched property's
parsed Var tokens in source order, locating each ident inside
value.raw and checking whether the cursor falls within its --name
span. Restores byte_range_to_lsp_range now that the LSP range comes
from absolute byte offsets.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Drop the separate relative-cursor calculation and the named
name_start/name_end intermediates: thread search_from through scan,
compute the var's absolute name range once, and pick the matching
range with find against the absolute cursor.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Make multi_def_separator private inside description.rs so callers
no longer need to pre-resolve the separator string. Both hover and
completion now thread Option<&str> client_name through their helper
chains; format_multi_def picks the appropriate separator internally.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@jo16oh
jo16oh force-pushed the 22-feat-show-resolved-color-in-vscode branch from 6e9dc22 to efa3102 Compare May 1, 2026 06:19
@jo16oh
jo16oh merged commit 48af598 into main May 1, 2026
24 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.

feat: Show resolved color for variable usages

1 participant