From 75bc3bcc73952e63d05a7c0392134c55a3d1bc36 Mon Sep 17 00:00:00 2001 From: claude Date: Tue, 9 Jun 2026 09:18:31 +0000 Subject: [PATCH] fix(typst): highlight idents after #keyword and wire tok-attributeName MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - codeIdentTokenizer: extend guard to scan back through the keyword word and accept when '#' immediately precedes it, so 'text' in '#set text(...)' and 'heading' in '#show heading:' are highlighted as function names - classHighlighter: add tags.attributeName → tok-attributeName mapping; all 26 themes already define .tok-attributeName colours but the tag was never mapped to the class, leaving named arg keys (columns:, caption:) completely unstyled Co-Authored-By: Claude Sonnet 4.6 --- .../source-editor/extensions/class-highlighter.ts | 1 + .../js/features/source-editor/lezer-typst/tokens.mjs | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/services/web/frontend/js/features/source-editor/extensions/class-highlighter.ts b/services/web/frontend/js/features/source-editor/extensions/class-highlighter.ts index 895b29b4a9..afef81b233 100644 --- a/services/web/frontend/js/features/source-editor/extensions/class-highlighter.ts +++ b/services/web/frontend/js/features/source-editor/extensions/class-highlighter.ts @@ -46,5 +46,6 @@ export const classHighlighter = tagHighlighter([ { tag: tags.invalid, class: 'tok-invalid' }, { tag: tags.punctuation, class: 'tok-punctuation' }, // additional + { tag: tags.attributeName, class: 'tok-attributeName' }, { tag: tags.attributeValue, class: 'tok-attributeValue' }, ]) diff --git a/services/web/frontend/js/features/source-editor/lezer-typst/tokens.mjs b/services/web/frontend/js/features/source-editor/lezer-typst/tokens.mjs index 54d6b26668..a29d433106 100644 --- a/services/web/frontend/js/features/source-editor/lezer-typst/tokens.mjs +++ b/services/web/frontend/js/features/source-editor/lezer-typst/tokens.mjs @@ -321,12 +321,17 @@ export const codeIdentTokenizer = new ExternalTokenizer( if (!stack.canShift(CodeIdent)) return // Guard: only fire in code context. - // Walk back past whitespace (including newlines inside multi-line arg lists) - // to the nearest non-space character and check that it is a code-mode delimiter. + // Walk back past whitespace to the nearest non-space character. let back = -1 while (input.peek(back) === SPACE || input.peek(back) === TAB || input.peek(back) === NEWLINE) back-- const prev = input.peek(back) - if (prev !== HASH && prev !== DOT && prev !== OPEN_PAREN && prev !== COMMA) return + if (prev !== HASH && prev !== DOT && prev !== OPEN_PAREN && prev !== COMMA) { + // May be after a keyword like '#set' or '#show': scan back through the + // keyword word itself and check that '#' immediately precedes it. + if (!isIdentTail(prev)) return + while (isIdentTail(input.peek(back))) back-- + if (input.peek(back) !== HASH) return + } // Must start with an identifier head character. if (!isIdentHead(input.next)) return