Skip to content

Prepare for Zed extension registry submission - #1

Merged
ickc merged 8 commits into
mainfrom
prepare-registry-submission
Jul 28, 2026
Merged

Prepare for Zed extension registry submission#1
ickc merged 8 commits into
mainfrom
prepare-registry-submission

Conversation

@ickc

@ickc ickc commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Independent review pass ahead of submitting to the Zed extension registry, plus the fixes it turned up. Six atomic commits.

What was already fine

extension.toml has every required field; id = "taskpaper" is unclaimed in the registry and contains no zed/extension substring; commit is an accepted alias for rev; MIT LICENSE sits at the repo root (mandatory since Oct 2025); the language server is downloaded rather than shipped; zed_extension_api = "0.7.0" is current; grammars/ and target/ are gitignored; the grammar pin is pushed and the grammar is unchanged since it.

Fixes

wasm32-wasip2 — Zed's extension builder compiles with wasip2 (crates/extension/src/extension_builder.rs:29), so the pre-publish "does it still compile" check in pixi.toml/CI was exercising a target Zed no longer uses. Verified locally that the exact command Zed runs (cargo build --release --target wasm32-wasip2 at the repo root) builds only the root package and lands zed_taskpaper.wasm where the builder looks for it.

URI escapinguri_of_path escaped everything outside the unreserved set; clients (Zed via the url crate) leave sub-delims and :@ alone. For Q1(2026).taskpaper or work+home.taskpaper our URI string differed from Zed's for the same file. Documents are keyed by URI string, so a workspace-wide tag rename emitted the open buffer's edits and the same file's on-disk edits under two keys — both applied, the second against ranges the first had already shifted.

Windows paths — we ship binaries for two Windows targets, but path_of_uri turned file:///C:/Users/... into /C:/Users/... and uri_of_path split on / only. The workspace index is built from the root URI, so on Windows it came back empty: no cross-file diagnostics, no project symbols, no workspace rename, no completion usage counts. Per-buffer features were unaffected, which is why it degraded quietly. Drive-letter and UNC forms both handled now; \ stays a separator only on Windows since it is a legal filename character elsewhere.

README — it is the registry listing page, so it can no longer open with "not yet on the Zed extension registry". Registry install first, explicit binary-resolution order (including the GitHub-releases download on first use and that everything else works without it), checkout instructions as fallback, one note that shortcuts are macOS-flavoured, and the keymap snippet fixed to be an array of context blocks.

Versioningtree-sitter.json's metadata.version lands in generated parser.c as .metadata.patch_version, so keeping it in lockstep made every release a grammar change (regenerate, push, re-pin, push, tag). The grammar is pinned by commit, so the lockstep bought nothing. It now moves only when the grammar does; 0.3.4 is a one-commit bump with no re-pin. MAINTAINER.md also gains the zed-industries/extensions submission flow.

Verification

pixi run ci green (25/25 corpus, 24 Rust tests, clippy -D warnings, both builds). The Windows-gated code and its two tests type-check under cargo check --target x86_64-pc-windows-msvc --workspace --all-targets, but the PathBuf round-trip assertions only run on the Windows CI job — that is the check to watch here.

Reviewed, not changed

  • Startup walks the project root recursively and then reads every .taskpaper file a second time in publish_workspace_diagnostics; both run before the main loop, so a large repo delays the first responses. Depth caps at 8 and symlinks are followed.
  • Index never drops files deleted on disk; no didChangeWatchedFiles registration.
  • handle_request propagates a params-deserialize failure out of main_loop, exiting the server.
  • Completions carry no explicit textEdit, so with word_characters = ["@", "-"] the replace range is client-derived.
  • scanner.c: calloc in external_scanner_create is not null-checked.

After merge

git tag v0.3.4 && git push origin v0.3.4 so the release assets exist, then open the zed-industries/extensions PR against this commit with version = "0.3.4".

ickc added 6 commits July 28, 2026 13:28
Zed's extension builder compiles extensions with wasm32-wasip2 (see
crates/extension/src/extension_builder.rs), not wasip1, so the local and
CI "does the extension still compile" check was exercising a target Zed
no longer builds with.
uri_of_path escaped every byte outside the unreserved set, but LSP
clients (Zed via the url crate) leave sub-delims and ":@" alone. For a
file whose name contains one of them — "Q1(2026).taskpaper",
"work+home.taskpaper" — our URI string differed from the client's for
the same file.

Documents are keyed by URI string, so the two spellings looked like two
documents. In a workspace-wide tag rename that meant the open buffer's
edits and the same file's on-disk edits were emitted under both keys and
both applied, the second set against ranges the first had already
shifted. It also left a stale duplicate in the diagnostics panel.
We ship taskpaper-ls binaries for two Windows targets, but the URI
helpers were POSIX-only: path_of_uri turned "file:///C:/Users/..." into
"/C:/Users/...", and uri_of_path split on "/" only, so a "C:\..." path
became a single percent-encoded segment.

The workspace index is built from the root URI, so on Windows it came
back empty: no cross-file diagnostics, no project symbols, no
workspace-wide tag rename, no usage counts in tag completion. Per-buffer
features were unaffected, which is why it degraded quietly.

Handle the drive-letter path and the UNC authority form, and split on
both separators when building a URI. The "\" separator stays Windows-only
since it is a legal filename character elsewhere.
The README is the extension's listing page, so it can no longer open
with "not yet on the Zed extension registry" and dev-extension install
steps. Lead with registry install, spell out the binary resolution order
(including that the language server is downloaded from GitHub releases on
first use, and that everything else works without it), and keep the
checkout instructions as the fallback.

Also note once that shortcuts are written for macOS, and fix the keymap
snippet: Zed's keymap file is an array of context blocks.
tree-sitter.json's metadata.version lands in generated parser.c as
.metadata.patch_version, so keeping it in lockstep made every release a
grammar change: regenerate, push, re-pin [grammars.taskpaper] commit,
push again, then tag. The grammar is pinned by commit, so the lockstep
bought nothing. Bump it only when the grammar itself changes.

Also document the zed-industries/extensions submission flow, since the
version in that repo's extensions.toml has to match extension.toml at the
submitted commit.
Covers the unreleased highlighting fix (notes under a done task keep the
comment style; a done project's name stays bold) plus the fixes on this
branch. The registry entry's version must match extension.toml at the
submitted commit, so the submission points here rather than at v0.3.3.

tree-sitter.json stays at 0.3.3: the grammar is unchanged, so the pin in
extension.toml stands.
@ickc
ickc marked this pull request as ready for review July 28, 2026 12:41
@ickc
ickc requested a review from Copilot July 28, 2026 12:41

Copilot AI 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.

Pull request overview

Prepares the TaskPaper Zed extension for registry submission by aligning build tooling with Zed’s current WASM target, fixing file-URI/path handling (incl. Windows + escaping consistency), and updating release/versioning and documentation to match the intended publish flow.

Changes:

  • Switch extension WASM build target from wasm32-wasip1 to wasm32-wasip2 in bootstrap scripts, pixi tasks, and CI.
  • Rework file:// URI ⇄ native path conversion to match client escaping behavior and correctly support Windows drive-letter and UNC paths (plus new tests).
  • Update README and maintainer/versioning guidance for registry submission and release tagging.

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
scripts/bootstrap-rust.sh Installs the wasm32-wasip2 target used by Zed’s extension builder.
README.md Updates install/usage docs for registry-first flow and clarifies LS binary resolution.
pixi.toml Bumps version and updates the local/CI “build wasm” check to wasm32-wasip2.
MAINTAINER.md Revises versioning guidance (decoupling tree-sitter metadata) and documents registry submission steps.
extension.toml Bumps extension version to 0.3.4.
crates/taskpaper-ls/src/workspace.rs Fixes URI escaping + Windows drive/UNC handling; adds round-trip tests.
crates/taskpaper-ls/Cargo.toml Bumps crate version to 0.3.4.
Cargo.toml Bumps root package version to 0.3.4.
Cargo.lock Updates locked package versions for 0.3.4.
.github/workflows/ci.yml Updates installed wasm target to wasm32-wasip2 for Linux/macOS CI jobs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/taskpaper-ls/src/workspace.rs
ickc added 2 commits July 28, 2026 13:52
Review asked why "^" and "\" are not escaped. They are not in the url
crate's path set, so escaping them would create the very mismatch the
previous commit removed. What actually happens is narrower and worth
recording: WHATWG allows characters RFC 3986 forbids, lsp_types::Uri is
RFC 3986-strict, so those names have no URI spelling both ends agree on
and uri_of_path returns None. Callers already skip such files.
Every handler reached its params through `extract(...)?`, so a message
whose params failed to deserialize propagated out of main_loop and exited
the process. That is reachable, not theoretical: a .taskpaper file whose
name holds a character RFC 3986 forbids ("a^b.taskpaper") gets a URI that
lsp_types::Uri rejects, so opening it killed the server rather than
skipping the file. The same applied to a workspace root such as
"notes[2026]", via initialize.

Requests now answer InvalidParams to the request's id, notifications log
and drop (there is no id to answer), and unparseable initialize params
cost the workspace root instead of the session. The rename handler's
own invalid-tag-name error folds into the same path.

The regression test drives main_loop over an in-memory connection: it
fails on the previous commit with RecvError, the channel having gone away
with the server.
@ickc
ickc merged commit 5bc2f6f into main Jul 28, 2026
4 checks passed
@ickc
ickc deleted the prepare-registry-submission branch July 28, 2026 13:25
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.

2 participants