zed-helixql: syntax highlighting for HelixDB with local tree-sitter CI parity

May 14, 2026

|repo-review

by Florian Narr

zed-helixql: syntax highlighting for HelixDB with local tree-sitter CI parity

zed-helixql is mine — I built it after getting tired of .hx files rendering as plain text in Zed. It's small, three commits, all from the same evening, but the third commit is the interesting one.

What it does

It's a Zed extension that adds syntax highlighting for HelixQL, the query language used by HelixDB — schema declarations (N::User, E::Follows), QUERY definitions, graph traversal steps (Out, InE, ShortestPath), and the usual literals and operators. No language server, just highlighting, indentation, brackets, and an outline view, activated on .hx / .hql files.

Why I starred it

I'm building on HelixDB at Klixpert and got sick of .hx files with no color. There's no existing Zed extension for it, so the fix was to write one. What made it worth writing up isn't the highlighting rules themselves — it's scripts/check.sh, which I added specifically because the first version of this extension shipped two bugs that only a real parser check would catch.

How it works

The extension doesn't ship its own grammar. extension.toml pins a specific commit of an upstream tree-sitter grammar:

[grammars.helixql]
repository = "https://github.com/benwoodward/tree-sitter-helixql"
commit = "259d2b68c56ae40ef7d190919ed49e0f17d1f3ee"

Zed compiles that grammar and runs the .scm query files in languages/helixql/ against it: highlights.scm maps node types to highlight scopes, indents.scm and brackets.scm handle editor behavior, outline.scm drives the symbol outline. This is all declarative — no code runs, just tree-sitter queries matching node patterns like (query_def name: (identifier) @function).

The problem with that setup: a .scm file that references a node type the grammar doesn't actually produce doesn't fail until Zed (or its extension-store CI) tries to compile and run the query. I hit this twice in one evening. The first commit's highlights.scm had:

(pre_filter    "PREFILTER" @function.builtin)

tree-sitter-helixql defines a pre_filter grammar rule, but nothing in the grammar actually references it, so tree-sitter never emits a pre_filter node type — the query fails to compile. Second bug, in brackets.scm:

("\"" @open "\"" @close)

Looks reasonable — match opening and closing quotes as bracket pairs. But string_literal in the grammar wraps its contents in token(...), which tree-sitter flattens into a single leaf node. The inner " characters aren't separate queryable nodes, so the pattern never matches anything valid and the query is rejected outright.

Both bugs are invisible from reading the .scm files — you need an actual tree-sitter instance loaded with the pinned grammar to catch them. That's what scripts/check.sh does. It clones tree-sitter-helixql into .local-test/, checks out the exact commit pinned in extension.toml, runs tree-sitter generate, and then runs every .scm file in languages/helixql/ through tree-sitter query <file> examples/sample.hx --scope source.helixql, failing loudly on the first broken query:

for f in languages/helixql/*.scm; do
  printf "%-40s" "$(basename "$f"):"
  if "$TS" query "$f" "$SAMPLE" --scope source.helixql >/dev/null 2>err.log; then
    echo "ok"
  else
    echo "FAIL"
    cat err.log
    fail=1
  fi
done

It also writes a parser-directories entry into ~/.config/tree-sitter/config.json pointing at .local-test, which is the only way --scope source.helixql resolves to the freshly generated parser instead of erroring on an unknown scope. examples/sample.hx is written to exercise every construct the highlight queries touch — schema blocks, all three step categories, a closure, a WHERE filter — so a query with a subtle mismatch has something to fail against.

Using it

Installing as a dev extension is the standard Zed flow:

cmd-shift-p  "zed: install dev extension"  pick the repo directory

Running the check before pushing:

npm i -g tree-sitter-cli
scripts/check.sh
brackets.scm:                          ok
highlights.scm:                        ok
indents.scm:                           ok
outline.scm:                           ok

That output is exactly what caught the pre_filter and quote-pair bugs in commits two and three — both fixed the same evening they were introduced, before either reached the zed-industries/extensions submission queue.

Rough edges

This is genuinely tiny — four .scm files, no language server, no diagnostics, no go-to-definition. It's highlighting only, and it says so up front. It hasn't been submitted to zed-industries/extensions yet, so right now it only works as a dev extension.

It also depends entirely on benwoodward/tree-sitter-helixql staying compatible. The pinned commit is unversioned upstream — if that grammar changes node names, check.sh will catch the break locally, but someone still has to notice and re-pin. There's no CI configured on the repo itself to run check.sh automatically; it's a pre-push habit, not an enforced gate.

Bottom line

If you write HelixQL in Zed, install it — it does the one thing it claims. The part worth stealing for any other tree-sitter-based Zed extension is scripts/check.sh: compiling your .scm queries against the actual pinned grammar before you push catches exactly the class of bug that's invisible in a code review and only shows up in Zed's extension-packaging CI.

floscom/zed-helixql on GitHub
floscom/zed-helixql