Language support for Native SDK .native markup in
Zed. Highlighting comes from a Tree-sitter grammar; diagnostics, hover and
completion come from the SDK's own language server.
Highlighting covers the parts a generic XML grammar would leave flat: {binding} expressions,
on-press="msg:{payload}" handlers, args="title trend=flat" template arguments and literal
values. Templates show up in the outline, and bracket matching, auto-indent, <!-- comment
toggling and tag wrapping all work.
The language server is the SDK's native markup lsp. Nothing gets downloaded, so the binary that
checks your markup is the one your project builds with.
Not published yet. Clone the repo, open Zed's Extensions page, choose Install Dev Extension
and pick the directory. Zed clones the grammar and builds both halves itself, installing the
wasm32-wasip2 Rust target through rustup if you don't have it.
The language server needs native on your PATH (bun add -g @native-sdk/cli). A project-local
node_modules/.bin/native is used first if there is one. Without either, you still get
highlighting.
From driving native markup lsp 0.1.0 over stdio:
- Diagnostics update on every keystroke and clear when you fix them.
- Only one diagnostic per file. Two unrelated errors give you the first one, then the next after you fix it.
- Completion offers 77 elements after
<and 62 attributes inside a tag, scoped to the element. - Attribute values get no completions, even though a wrong value produces a diagnostic listing the valid ones.
- Hover returns real documentation for elements and attributes.
- Bindings and message tags are not checked against your
ModelandMsg. Runnative markup checkin the app directory for that, withzig-out/model-contract.zonfresh fromnative test. Given the same file in the same directory, the CLI reportsbinding does not name a model fieldand the server reports nothing.
languages/native/— language config and the.scmqueriessrc/native_sdk.rs— the extension binary, which resolves the language servertest/samples/— markup covering every shape the queries capture
The grammar is in tree-sitter-native, pinned here by commit.
just check validates the manifests and builds the extension. just check-queries runs the .scm
files against a sibling tree-sitter-native checkout, which is what CI does at the pinned revision.
When changing grammar and queries together, just use-local-grammar repoints the manifest at your
local checkout and its current HEAD. Rebuild the dev extension to pick it up, and put the line back
before committing.
Highlighting needs the grammar even though the server exists, because Zed builds it from Tree-sitter queries and ignores LSP semantic tokens.
Two repos, and this one pins the other, so the order matters.
- Tag the grammar first. In
tree-sitter-native,just bump X.Y.Z(the version is compiled intoparser.c, so it edits two files and regenerates), commit, thenjust release vX.Y.Z. - Point
revinextension.tomlat that commit's SHA. Zed clones by revision, so a tag name would resolve but the SHA is what belongs in the manifest. - Bump
versioninextension.tomlandCargo.tomltogether.just check-versionsenforces that they match, and the tag names both. just release vX.Y.Zhere.
Then get it into the registry, by either route. Both produce the same pull request.
By hand: in a checkout of your zed-industries/extensions fork, just init-submodule extensions/native-sdk, check the submodule out at the tag's SHA, bump version in
extensions.toml, and open the PR. Do not use git submodule update --remote — it follows the
default branch tip rather than your tag, and extensions.toml must carry the version that
extension.toml declares at the pinned commit.
By workflow: create a classic PAT with repo and workflow scopes, gh secret set COMMITTER_TOKEN, run the Publish workflow with the tag, then revoke the token and delete the
secret. The workflow is dispatch-only so the token never has to live in the repository between
releases. workflow scope is not optional: the fork contains .github/workflows/, and GitHub
rejects pushes touching those without it.
MIT