Skip to content

Latest commit

 

History

History
130 lines (97 loc) · 4.08 KB

File metadata and controls

130 lines (97 loc) · 4.08 KB

Architecture

Overview

The extension ships as two artefacts that talk over LSP (JSON-RPC over stdio):

  1. Zed extension (WASM)extension/ — compiled to wasm32-wasip1 and loaded by Zed.
  2. LSP server (native binary)lsp-server/ — compiled for the host platform (macOS, Linux, Windows).
Zed Editor
   │
   ├── loads extension/<…>.wasm
   │     └── implements zed_extension_api::Extension
   │           └── language_server_command() spawns zed-mermaid-lsp
   │
   └── speaks LSP with zed-mermaid-lsp
         └── code actions, edits, …

Why two binaries?

Zed extensions run inside a WASM sandbox, which forbids:

  • direct host filesystem access (needed to write .mermaid/<id>.mmd)
  • spawning native threads or pulling in crates with system dependencies
  • crates that don't compile for wasm32-wasip1

A native LSP has none of these constraints. This is the standard pattern in Zed (see rust-analyzer, pyright, …).

Code action lifecycle

sequenceDiagram
    participant U as User
    participant Z as Zed
    participant E as Extension WASM
    participant L as Native LSP

    Z->>E: load extension
    E->>L: spawn binary (stdio)
    Z->>L: initialize
    L-->>Z: capabilities (codeAction)

    U->>Z: opens file.md, cursor in ```mermaid```
    Z->>L: textDocument/didOpen
    Z->>L: textDocument/codeAction(range)
    L-->>Z: [{ title: "Render Mermaid Diagram", … }]

    U->>Z: Cmd+. → picks "Render"
    Z->>L: applies WorkspaceEdit
    L->>L: render SVG (pure-Rust crate)
    L->>L: save source → .mermaid/<id>.mmd
    Z-->>U: file modified
Loading

Document format

Raw block (before render)

```mermaid
flowchart LR
  A --> B
```

Rendered block

<!-- mermaid-render id=a1b2c3d4 -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 100">
  …
</svg>
<!-- /mermaid-render -->

The id is an FNV-1a hash of the source, truncated to 8 hex characters. This makes rendering idempotent: rendering the same source twice produces the same id, so re-rendering is harmless.

Source persistence

<workspace_root>/.mermaid/<id>.mmd holds the original mermaid source. Commit this directory to git so collaborators can re-edit the diagrams.

LSP modules

Module Responsibility
main.rs Logger init, starts tower-lsp-server on stdio
server.rs Implements LanguageServer, routes requests
document.rs Markdown parsing, locates raw / rendered blocks
code_actions.rs Builds CodeActions based on cursor position
renderer.rs Wraps the pure-Rust mermaid renderer crate
source_store.rs Reads/writes .mermaid/<id>.mmd and resolves workspace

Renderer choice

Picked: mermaid-rs-renderer v0.2 (1jehuang).

  • Pure Rust, no runtime dependency (no Node, no headless Chromium).
  • Minimal API: mermaid_rs_renderer::render(source) -> Result<String, _>.
  • 23 diagram types claimed; 100-1400× faster than mermaid-cli.

Alternatives considered:

  • merman (Latias94) — claims 1:1 parity with mermaid 11.12.3, but needs a render feature flag and exposes a more verbose API. Kept as a fallback if mermaid-rs-renderer ever falls short.
  • mermaid-to-svg (warpdotdev on GitHub) — not on crates.io at the time of the choice.

The renderer is isolated in lsp-server/src/renderer.rs. Switching crates is a one-file change.

LSP capabilities

Advertised in initialize:

  • text_document_sync = Full — clients re-send the whole document on change.
  • code_action_provider = { kinds: [refactor.rewrite], resolve: false }.

Future expansions (e.g. executeCommand-based actions, codeAction/resolve to defer rendering until the user actually accepts) are scoped out of the current version intentionally — see docs/usage.md for the trade-off.