Zed companion (zed/) #5

Open
opened 2026-11-12 02:00:08 +01:00 by thatrichan · 4 comments
Owner

A Zed extension, Revue Companion, doing what the JetBrains plugin (jetbrains/) does, as far as Zed lets an extension: the branch's pull request from Revue inside the editor, and the way back, with Revue opening a pull request in the Zed window that has its branch.

Zed extensions are headless: they bring languages, language servers, themes, snippets, debuggers and MCP servers, and can run processes, but they can't add panels, menu items, Command Palette actions or gutter icons (the visual extension API is a proposal Zed says it won't get to soon). So the extension brings a small language server, revue-lsp, for every language, and everything goes through what Zed already shows of one: code actions (cmd-.), diagnostics with hover, and project symbols (cmd-t). Being plain LSP, revue-lsp could serve Helix too.

Features (as in the JetBrains plugin)

  • Revue: Open or Create Pull Request (a code action in any file): opens the current branch's pull request in Revue, or its New pull request page; revue --pull-request <folder> --branch <branch>.
  • Revue: Open File in Pull Request (a code action) at the cursor's line (LINK --file … --line …).
  • In the editor: your pending comments on the branch's pull request as hint diagnostics on their lines, the text on hover (--pending); a code action opens them in Revue. The pull request's changed lines the same way, off by default (Zed's own gutter shows changes against HEAD, not the pull request's base, and hints over every change are noisy).
  • Revue: Go to Pull Request File: the pull request's files as PR: <path> entries in project symbols (--branch-pull), each at its first change.
  • Revue: Add Pending Comment (a code action on the selection): LSP has no text input, so it opens Revue on those lines to write it there, once Revue can open its comment box directly from the command line (an editor question to add in thatrichan/revue).
  • Settings: lsp.revue.initialization_options.path (the revue executable; empty: PATH, then the install places the JetBrains plugin looks in), and whether to mark the changed lines.
  • Not possible in Zed: the Pull Requests list and its details page, toolbar and menu entries, a status bar item. Revue's own window covers those until Zed's extensions can draw.

It talks to Revue only through the revue command (Revue's docs/command-line.md, "What editors can ask"), and checks revue --version's editor <N> line before using it, as the JetBrains plugin does. No tokens enter the editor.

The way back: Revue reaching a running Zed

Zed has no server or RPC socket an outside program can ask, but revue-lsp runs inside every Zed project, so it registers that project:

  • It registers itself in the same folder as the VS Code (#1) and Neovim (#2) companions: <Revue's data folder>/companions/zed-<pid>.json with { "kind": "zed", "app": "Zed" | "Zed Preview", "pid", "port", "token", "version" }, listening on 127.0.0.1 on a free port. It removes the file on shutdown; Revue ignores a file whose pid isn't running or whose port refuses.
  • GET /revue-projects, as the other companions answer: the project's worktrees, each repository's root, branch and remotes. Each request carries the token from the file.
  • Opening is Zed's CLI: Revue runs zed --existing <repository>/<file>:<line> for the window that has the branch, or zed <folder> for a new one; the language server can't focus a window itself.
  • In Revue (thatrichan/revue): the companions module reads zed-* files too, and the editor chooser (thatrichan/revue#68) treats Zed like any other companion. Revue's src/apps.rs "open in Zed at a line" stays.

Where it lives

zed/ in this repository: the extension (extension.toml, Rust built to WASM with zed_extension_api) and revue-lsp (a Rust crate). The extension downloads the revue-lsp binary for the platform from the Forgejo release (or uses one on PATH). Tests: revue-lsp against a fake revue printing fixture JSON, driven over LSP. A release-zed workflow started by hand (patch, minor, major) builds revue-lsp for macOS (arm64, x86_64), Linux (x86_64, arm64) and Windows, tags zed-vX.Y.Z and attaches them, like release-jetbrains. The extension is published by a pull request to zed-industries/extensions; to check whether it takes a submodule hosted on Forgejo or needs a GitHub mirror.

Plan

  1. A spike: a stub extension and revue-lsp with one code action, to check in a current Zed that code actions, hint diagnostics with hover and project symbols show for every language, and whether the server can be declared for all languages or users must add it per language.
  2. revue-lsp: finding revue, --version, Open or Create Pull Request, Open File in Pull Request.
  3. Pending comments, Go to Pull Request File, the changed lines setting.
  4. Registration and /revue-projects, then thatrichan/revue#68 choosing it and opening with zed --existing.
  5. The release workflow, then publishing to Zed's extensions.
  6. Docs: Revue's docs/editors.md Zed section points to the extension, and Revue's stopgap is removed: editors/zed/ (the Revue: Pull Request task and its keymap), made when Zed seemed unable to have an extension.
A Zed extension, **Revue Companion**, doing what the JetBrains plugin ([`jetbrains/`](../src/branch/main/jetbrains)) does, as far as Zed lets an extension: the branch's pull request from Revue inside the editor, and the way back, with Revue opening a pull request in the Zed window that has its branch. Zed extensions are headless: they bring languages, language servers, themes, snippets, debuggers and MCP servers, and can run processes, but they can't add panels, menu items, Command Palette actions or gutter icons ([the visual extension API](https://github.com/zed-industries/zed/discussions/53403) is a proposal Zed says it won't get to soon). So the extension brings a small language server, `revue-lsp`, for every language, and everything goes through what Zed already shows of one: code actions (`cmd-.`), diagnostics with hover, and project symbols (`cmd-t`). Being plain LSP, `revue-lsp` could serve Helix too. ## Features (as in the JetBrains plugin) - **Revue: Open or Create Pull Request** (a code action in any file): opens the current branch's pull request in Revue, or its New pull request page; `revue --pull-request <folder> --branch <branch>`. - **Revue: Open File in Pull Request** (a code action) at the cursor's line (`LINK --file … --line …`). - **In the editor:** your pending comments on the branch's pull request as hint diagnostics on their lines, the text on hover (`--pending`); a code action opens them in Revue. The pull request's changed lines the same way, off by default (Zed's own gutter shows changes against HEAD, not the pull request's base, and hints over every change are noisy). - **Revue: Go to Pull Request File:** the pull request's files as `PR: <path>` entries in project symbols (`--branch-pull`), each at its first change. - **Revue: Add Pending Comment** (a code action on the selection): LSP has no text input, so it opens Revue on those lines to write it there, once Revue can open its comment box directly from the command line (an editor question to add in thatrichan/revue). - **Settings:** `lsp.revue.initialization_options.path` (the `revue` executable; empty: PATH, then the install places the JetBrains plugin looks in), and whether to mark the changed lines. - **Not possible in Zed:** the Pull Requests list and its details page, toolbar and menu entries, a status bar item. Revue's own window covers those until Zed's extensions can draw. It talks to Revue only through the `revue` command (Revue's `docs/command-line.md`, "What editors can ask"), and checks `revue --version`'s `editor <N>` line before using it, as the JetBrains plugin does. No tokens enter the editor. ## The way back: Revue reaching a running Zed Zed has no server or RPC socket an outside program can ask, but `revue-lsp` runs inside every Zed project, so it registers that project: - **It registers itself** in the same folder as the VS Code (#1) and Neovim (#2) companions: `<Revue's data folder>/companions/zed-<pid>.json` with `{ "kind": "zed", "app": "Zed" | "Zed Preview", "pid", "port", "token", "version" }`, listening on `127.0.0.1` on a free port. It removes the file on shutdown; Revue ignores a file whose pid isn't running or whose port refuses. - **`GET /revue-projects`**, as the other companions answer: the project's worktrees, each repository's root, branch and remotes. Each request carries the `token` from the file. - **Opening is Zed's CLI:** Revue runs `zed --existing <repository>/<file>:<line>` for the window that has the branch, or `zed <folder>` for a new one; the language server can't focus a window itself. - **In Revue (thatrichan/revue):** the `companions` module reads `zed-*` files too, and the editor chooser (thatrichan/revue#68) treats Zed like any other companion. Revue's `src/apps.rs` "open in Zed at a line" stays. ## Where it lives `zed/` in this repository: the extension (`extension.toml`, Rust built to WASM with `zed_extension_api`) and `revue-lsp` (a Rust crate). The extension downloads the `revue-lsp` binary for the platform from the Forgejo release (or uses one on PATH). Tests: `revue-lsp` against a fake `revue` printing fixture JSON, driven over LSP. A `release-zed` workflow started by hand (patch, minor, major) builds `revue-lsp` for macOS (arm64, x86_64), Linux (x86_64, arm64) and Windows, tags `zed-vX.Y.Z` and attaches them, like `release-jetbrains`. The extension is published by a pull request to `zed-industries/extensions`; to check whether it takes a submodule hosted on Forgejo or needs a GitHub mirror. ## Plan 1. A spike: a stub extension and `revue-lsp` with one code action, to check in a current Zed that code actions, hint diagnostics with hover and project symbols show for every language, and whether the server can be declared for all languages or users must add it per language. 2. `revue-lsp`: finding `revue`, `--version`, Open or Create Pull Request, Open File in Pull Request. 3. Pending comments, Go to Pull Request File, the changed lines setting. 4. Registration and `/revue-projects`, then thatrichan/revue#68 choosing it and opening with `zed --existing`. 5. The release workflow, then publishing to Zed's extensions. 6. Docs: Revue's `docs/editors.md` Zed section points to the extension, and Revue's stopgap is removed: `editors/zed/` (the **Revue: Pull Request** task and its keymap), made when Zed seemed unable to have an extension.
thatrichan changed title from Revue Companion for Zed to Zed companion (zed/) 2026-11-12 02:01:25 +01:00
Author
Owner

Plan steps 1 and 2 are in (merged in da5cf1f): zed/ with the extension, revue-lsp, and zed/PLAN.md.

What the spike found (from Zed's source; not yet seen in a running Zed, see below):

  • No way to declare a server for every language, and users can't add one per language; so extension.toml lists Zed's and the registry's 253 language names. One revue-lsp runs per worktree whatever the language.
  • Code actions, hint diagnostics with hover, project symbols and notifications all work as planned.
  • Zed's code actions button (inline_code_actions, on by default) shows wherever a server offers an action, and Zed doesn't send triggerKind, so the two Revue actions would put it on every line. To decide (see the plan).
  • The registry takes a submodule hosted on Forgejo with path = "zed": no GitHub mirror needed.

revue-lsp: finds revue (lsp.revue.initialization_options.path, PATH, the JetBrains plugin's places), gates on editor N, and offers Revue: Open or Create Pull Request and Revue: Open File in Pull Request as code actions in any file. 28 tests over LSP against a small Rust fake revue; task zed:test, zed:lint, zed:build.

To check by hand (this machine's Rust has no wasm32-wasip2): rustup target add wasm32-wasip2, task zed:build, point lsp.revue.binary.path at zed/target/release/revue-lsp, then zed: install dev extension on zed/ (the plan's "Checking it in Zed").

Next: step 3 (pending comments as hints, Go to Pull Request File in symbols). The way back waits on thatrichan/revue#80.

Plan steps 1 and 2 are in (merged in da5cf1f): `zed/` with the extension, `revue-lsp`, and `zed/PLAN.md`. **What the spike found** (from Zed's source; not yet seen in a running Zed, see below): - No way to declare a server for every language, and users can't add one per language; so `extension.toml` lists Zed's and the registry's 253 language names. One `revue-lsp` runs per worktree whatever the language. - Code actions, hint diagnostics with hover, project symbols and notifications all work as planned. - Zed's code actions button (`inline_code_actions`, on by default) shows wherever a server offers an action, and Zed doesn't send `triggerKind`, so the two Revue actions would put it on every line. To decide (see the plan). - The registry takes a submodule hosted on Forgejo with `path = "zed"`: no GitHub mirror needed. **revue-lsp:** finds `revue` (`lsp.revue.initialization_options.path`, PATH, the JetBrains plugin's places), gates on `editor N`, and offers **Revue: Open or Create Pull Request** and **Revue: Open File in Pull Request** as code actions in any file. 28 tests over LSP against a small Rust fake `revue`; `task zed:test`, `zed:lint`, `zed:build`. **To check by hand** (this machine's Rust has no `wasm32-wasip2`): `rustup target add wasm32-wasip2`, `task zed:build`, point `lsp.revue.binary.path` at `zed/target/release/revue-lsp`, then **zed: install dev extension** on `zed/` (the plan's "Checking it in Zed"). Next: step 3 (pending comments as hints, Go to Pull Request File in symbols). The way back waits on thatrichan/revue#80.
Author
Owner

Steps 3 and 4 are in (merged in d0e70ad):

  • The way back (thatrichan/revue#80, editor 9): revue-lsp listens on 127.0.0.1 with a fresh token, registers with revue --companion zed (only when the client says it's Zed), answers GET /revue-projects for its workspace folders, and runs --companion-gone on shutdown. Revue then opens with zed --existing.
  • Revue: Add Pending Comment (thatrichan/revue#81, editor 10): a code action on the selection or line that opens Revue's comment box on those lines (--write-comment), offered on the pull request's files.
  • Step 3: your pending comments as hints on their lines with the text on hover; the pull request's files as PR: <path> in project symbols (cmd-t); initialization_options.changes (off by default) marks the changed lines. The spike option is gone.

44 tests over LSP pass. Still to check in Zed (zed/PLAN.md → "Checking it in Zed"); the first thing: that Zed sends clientInfo.name "Zed" / "Zed Preview", or it never registers. Next: step 5 (CI, release-zed, publishing) and step 6 (docs).

Steps 3 and 4 are in (merged in d0e70ad): - **The way back** (thatrichan/revue#80, `editor 9`): `revue-lsp` listens on 127.0.0.1 with a fresh token, registers with `revue --companion zed` (only when the client says it's Zed), answers `GET /revue-projects` for its workspace folders, and runs `--companion-gone` on shutdown. Revue then opens with `zed --existing`. - **Revue: Add Pending Comment** (thatrichan/revue#81, `editor 10`): a code action on the selection or line that opens Revue's comment box on those lines (`--write-comment`), offered on the pull request's files. - **Step 3:** your pending comments as hints on their lines with the text on hover; the pull request's files as `PR: <path>` in project symbols (`cmd-t`); `initialization_options.changes` (off by default) marks the changed lines. The spike option is gone. 44 tests over LSP pass. Still to check in Zed (`zed/PLAN.md` → "Checking it in Zed"); the first thing: that Zed sends `clientInfo.name` "Zed" / "Zed Preview", or it never registers. Next: step 5 (CI, `release-zed`, publishing) and step 6 (docs).
Author
Owner

Steps 5 and 6 are in (merged in ce33eba), so every step of the plan is built:

  • CI (.forgejo/workflows/zed.yml): lint, tests and the extension's WASM build on every push touching zed/.
  • Release (.forgejo/workflows/release-zed.yml, by hand): bumps the versions, regenerates the language list, builds revue-lsp for Linux (x86_64, arm64, musl) and Windows, tags zed-vX.Y.Z and attaches them. macOS builds need a Mac runner named in the repository variable MACOS_RUNNER; without one, tick without macOS.
  • The code actions button: Revue's actions show on a file's first line, a selection, and the pull request's lines (changed or with a pending comment), not on every line; lsp.revue.initialization_options.actions_on_every_line brings that back.
  • The language list is regenerated from Zed's extension registry at each release (618 names now).
  • zed/README.md is the extension's page (install, settings, checking it, publishing to zed-industries/extensions), with LICENSE-MIT / LICENSE-APACHE copies the registry wants.

Revue's side (thatrichan/revue#80 and #81) is on Revue's main. Left: the first release, the pull request to Zed's extensions, and the "Checking it in Zed" list in a real Zed (this machine can't build the WASM).

Steps 5 and 6 are in (merged in ce33eba), so every step of the plan is built: - **CI** (`.forgejo/workflows/zed.yml`): lint, tests and the extension's WASM build on every push touching `zed/`. - **Release** (`.forgejo/workflows/release-zed.yml`, by hand): bumps the versions, regenerates the language list, builds `revue-lsp` for Linux (x86_64, arm64, musl) and Windows, tags `zed-vX.Y.Z` and attaches them. macOS builds need a Mac runner named in the repository variable `MACOS_RUNNER`; without one, tick **without macOS**. - **The code actions button**: Revue's actions show on a file's first line, a selection, and the pull request's lines (changed or with a pending comment), not on every line; `lsp.revue.initialization_options.actions_on_every_line` brings that back. - **The language list** is regenerated from Zed's extension registry at each release (618 names now). - `zed/README.md` is the extension's page (install, settings, checking it, publishing to `zed-industries/extensions`), with `LICENSE-MIT` / `LICENSE-APACHE` copies the registry wants. Revue's side (thatrichan/revue#80 and #81) is on Revue's main. Left: the first release, the pull request to Zed's extensions, and the "Checking it in Zed" list in a real Zed (this machine can't build the WASM).
Author
Owner

Released as zed 0.1.0 for Linux and Windows on the Forgejo release (no macOS runner yet). /revue-projects now lists repositories nested in a workspace folder. Not in Zed's extensions yet, so Revue's docs keep it under Coming soon. The ID decision, testing it in Zed, the registry pull request and a macOS build are in #9.

Released as **zed 0.1.0** for Linux and Windows on the [Forgejo release](https://forgejo.cleangame.dev/thatrichan/revue-companion/releases/tag/zed-v0.1.0) (no macOS runner yet). `/revue-projects` now lists repositories nested in a workspace folder. Not in Zed's extensions yet, so Revue's docs keep it under Coming soon. The ID decision, testing it in Zed, the registry pull request and a macOS build are in #9.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
thatrichan/revue-companion#5
No description provided.