Skip to content

feat: highlight SQL in CodeBlock with the ClickHouse WASM lexer - #1164

Open
alexey-milovidov wants to merge 2 commits into
mainfrom
codeblock-clickhouse-sql
Open

feat: highlight SQL in CodeBlock with the ClickHouse WASM lexer#1164
alexey-milovidov wants to merge 2 commits into
mainfrom
codeblock-clickhouse-sql

Conversation

@alexey-milovidov

Copy link
Copy Markdown
Member

What

CodeBlock now highlights language="sql" with ClickHouse's own lexer (src/Parsers/Lexer.cpp in ClickHouse/ClickHouse) compiled to WebAssembly — the same embedded payload (~9 KB base64, byte-identical), token classification and colors as the ClickHouse Web UI (programs/server/play.html) and the ClickHouse documentation. This ports ClickHouse/clickhouse-fiddle#81 to Click-UI's static code display, as discussed there.

  • Tokenization (sql/lexer.ts): the exact tokenizeSync from play.html, including the __heap_base buffer layout, max_query_size = 0 and the forward-progress guard.
  • Classification (sql/highlight.ts): the same keyword table and logic — keywords bold, function calls distinguished from identifiers by peeking at the next non-whitespace/non-comment token (sum( vs. x), strings, numbers, quoted identifiers, comments in italic.
  • Rainbow brackets colored by nesting depth with type-aware matching (SELECT ([)] colors nothing), digit-group underlines in numbers with ≥5 integer digits (1̲234̲567), and a red-wavy error style for a tail the lexer cannot process. The cursor-dependent features of the Web UI (matched-pair emphasis, identifier-under-cursor underline) do not apply to a read-only block and are not ported.
  • Colors: the play.html palette for both themes (light: darker variants of the terminal palette; dark: the xterm-16 values clickhouse-client renders to).

Rendering & fallback

The lexer path mirrors the DOM of the react-syntax-highlighter path (pre > code > one span per line, the same inline line-number spans), so line numbers, wrapping and the copy button behave identically. While the WASM module instantiates — or when WebAssembly is unavailable or the lexer rejects the text — CodeBlock keeps the previous highlight.js rendering, so a block is never left unstyled. Other languages are untouched.

Screenshots

New ClickHouseSqlHighlighting story (dark / light):

dark

light

Existing Playground story, before → after (note arrayJoin/arraySort/groupArray now recognized as functions, identifiers colored, rainbow brackets, bold keywords):

before

after

Also fixed

setupTests.ts imported TextEncoder from "util", which resolves to the browserify util package in node_modules — it has no TextEncoder, so the polyfill silently assigned undefined. It now imports from node:util and also polyfills TextDecoder (the lexer needs both under jsdom).

Verification

  • yarn typecheck passes
  • yarn vitest run: 550 tests pass (6 new CodeBlock tests: classification, function/identifier disambiguation, rainbow and broken-nesting brackets, digit-group underlines, error tail, line numbers, and the highlight.js fallback for other languages)
  • yarn lint: only pre-existing warnings
  • yarn build passes (including the dist health check)

🤖 Generated with Claude Code

Port the SQL highlighting of the ClickHouse Web UI
(programs/server/play.html) to CodeBlock: the query is tokenized by
ClickHouse's own lexer (src/Parsers/Lexer.cpp) compiled to WebAssembly
(~9 KB, byte-identical to the payload embedded in play.html), and the
tokens are classified exactly as the Web UI and clickhouse-client do it:
keywords bold, functions distinguished from identifiers by peeking at
the next token, strings, numbers, quoted identifiers, comments,
rainbow brackets by nesting depth (type-aware, so broken nesting
colors nothing), digit-group underlines in long numbers, and an error
style for an un-lexable tail. The colors are the play.html palette for
both the light and the dark theme.

The rendering mirrors the DOM of the react-syntax-highlighter path
(pre > code > one span per line, same inline line-number spans), so
line numbers, wrapping and the copy button behave identically. While
the WASM module instantiates - or when WebAssembly is unavailable or
the lexer rejects the text - CodeBlock falls back to the previous
highlight.js rendering, so a block is never left unstyled.

Also fix setupTests.ts: the browserify util package shadows Node's
built-in, so the TextEncoder polyfill was silently assigning
undefined; import from node:util and add TextDecoder.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Aug 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ba8a879

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@clickhouse/click-ui Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8f89803. Configure here.

</span>
)}
{showLineNumbers ? <span>{content}</span> : content}
</span>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Wrapped SQL lines render extra blanks

Medium Severity

When wrap is on and line numbers are shown, each SQL line becomes a flex row while a trailing newline stays inside the content span. With pre-wrap that newline still creates a line box, so rows look double-spaced and numbers no longer line up with the code.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 8f89803. Configure here.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

style={lineNumberStyle}
>
{lineIndex + 1}
</span>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

SQL line numbers lose numbers color

Low Severity

SQL line-number spans copy highlighter class names but only set layout styles. Token colors are inline, and those classes have no CSS, so the numbers inherit the pre text color instead of --codeblock-numbers (or the highlight.js comment color the other path uses).

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 8f89803. Configure here.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

minor

'MONTH',
'MOVE',
'MUTATION',
'NAN_SQL',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

NAN keyword never classified

Low Severity

The keyword set stores NAN_SQL (the C++ enum name used to avoid the NAN macro) instead of the SQL lexeme NAN. tokenKind uppercases the bare word and looks it up, so nan/NaN never match and are painted as identifiers.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 8f89803. Configure here.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

minor

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@workflow-authentication-public

Copy link
Copy Markdown
Contributor

Storybook Preview Deployed

✅ Preview URL: https://click-8fxq8s778-clickhouse.vercel.app

Built from commit: 8b874fe80f090489caf8c4bb25f327061dc1cca0

@XOP
XOP requested review from XOP, ariser and vineethasok August 20, 2026 16:03
@XOP XOP added the enhancement New feature or request label Aug 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants