Toward First‑Class Extensibility in Zed: A Rhai‑Based Proposal #40049
Replies: 2 comments
-
I'd rather see official support for WIT (Wasm Interface Type - see wit-bindgen) bindings for the Zed extensions API for other mainstream languages, along with an expanded Zed extension API with more capabilities, and improved tooling for extension development.
Both JavaScript and Python are documented on the WebAssembly Component Model website as being usable for WASM component creation. This means Zed extensions could be developed in JavaScript and Python (and other languages) and compile as WASM components, the same way Zed's currently supported Rust extension system works.
Is the compile step really the bottleneck? With Zed's existing Wasmtime engine, extensions can already be reloaded without restarting the entire Zed editor (as seen today when you install an extension). A dev-mode hot-reloading system for extensions could be built with the current architecture without adding a scripting language.
I think a GPUI-based UI contribution for extensions would be a better fit than WebView for Zed's performance goals.
Furthermore, this makes Rhai less viable for LLM-assisted code generation compared to mainstream languages. Overall, Rhai seems redundant with Wasmtime and the WebAssembly Component Model that Zed already uses for extensions, while also being less production battle-tested than mainstream languages. I think a better direction for Zed's extension development would be focusing on expanding the extension API itself, bringing support for authoring extensions to other mainstream languages via the WebAssembly Component Model, and improving the development flow for extensions (i.e. hot reloading in Wasmtime). If you haven't already, I recommend reading Life of a Zed Extension: Rust, WIT, Wasm and watching the associated YouTube video to help understand the current extension architecture. |
Beta Was this translation helpful? Give feedback.
-
One more thing: This proposal appears to lack any mention of how a Rhai scripting system would reconcile with Zed's evolving extension API versioning (a problem they have already meticulously solved with the WIT-based approach). How would you ensure Rhai scripts remain working, validate version compatibility, and maintain access to the latest extension API features and changes, as Zed's extension API evolves version-after-version? Especially with the idea of "project/user‑level script packs published as extensions." To give an example from the "Needed Host API Additions" in your proposal:
What happens when "current project/worktree metadata" changes its structure in a new Zed version, but active Rhai scripts are still expecting the old structure? |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Toward First‑Class Extensibility in Zed: A Rhai‑Based Proposal
Summary
Zed’s extensions are safe, fast, and portable via Rust→WASM (WASI + WIT). That model protects performance and reliability, but it makes “live, programmable” customization (the Emacs/Lisp experience) harder: most logic lives in compiled Rust, and today’s extension hooks focus on LSP/DAP rather than general editor events. This document:
The Problem
Extension
trait primarily covers LSP/DAP/context servers/slash commands. There’s no stable, general set of editor events for scripts to observe and react to (buffer open/save/selection changes, etc.).Today’s Extension Model (Pointers)
crates/extension_api/src/extension_api.rs
:Extension
trait and host functions (e.g.,run_slash_command
,complete_slash_command_argument
,language_server_command
).crates/extension_api/wit/since_v0.6.0/extension.wit
: WIT definitions for the extension world; especiallyresource worktree
(e.g.,read-text-file
,which
,shell-env
),process::command
,http-client
, etc.crates/extension_api/src/process.rs
,crates/extension_api/src/http_client.rs
,crates/extension_api/src/settings.rs
.docs/src/extensions/developing-extensions.md
: how Zed extensions are structured and built.docs/src/extensions/slash-commands.md
: adding slash commands.docs/src/configuring-zed.md
: JSONC config files (user and project).extensions/slash-commands-example
: minimal Rust extension with slash commands.extensions/test-extension
: showsprocess::Command
, platform detection, simple FS operations inside the extension working directory.extension.toml
: manifest and capability declarations (e.g.,[[capabilities]] kind = "process:exec"
).These pieces create a safe and portable extension system, but we lack:
Goals: Extensibility, Customizability, Introspection
Emacs’ magic is not only “Lisp,” but the combination of a live runtime, a rich set of editor hooks, and a small, composable API surface. Zed can get close within its model.
Why Rhai?
Rhai (https://github.com/rhaiscript/rhai, https://rhai.rs) is an embeddable scripting engine written in Rust, designed for safety and tight host control. It fits Zed’s WASM+capabilities model.
Pros
wasm32-wasi
inside a Zed extension; no C FFI.Engine::set_max_operations
(hard budget on instruction count).Engine::on_progress
(kill long runs; timebox execution).Engine::set_max_call_levels
,set_max_expr_depths
,set_max_string_size
,set_max_array_size
,set_max_map_size
,set_max_modules
,set_max_variables
.Cons
eval_file
and filesystem module loading aren’t available; you supply script text via the host (Zed’sWorktree.read_text_file
) and/or implement a custom module resolver.Why Not Lua (First)?
Lua is mature, fast to start, widely known (esp. for editors). But, for Zed specifically:
io
/os
and lock down the standard library. It’s doable, but easier to get wrong than Rhai’s “no I/O unless exposed” by default.Lua remains attractive for user familiarity (and #13491 explicitly requests it), but Rhai integrates more cleanly with Zed’s existing WASM+WIT architecture and safety posture.
Other Options (Brief)
process::Command
), not ideal to embed in Zed’s WASM core or inside extensions (CPython + GIL + size + security).Proposal: A “Rhai Scripting” Extension
A single extension that embeds Rhai and exposes a small, safe surface for scripts to define commands/macros and light text transforms.
User experience
.zed/scripts/*.rhai
in a project and/or user‑level folder./rhai:reload
— compile/reload scripts./rhai:run <name> [args…]
— run a named script function./rhai:eval <code…>
— for quick experiments (capability‑guarded).Engine design
Engine
per extension instance, configured with strict safety:set_max_operations(N)
with a small N (e.g., a few thousand ops) for interactive commands.on_progress
with a time budget (e.g., terminate >50–100ms).set_max_*
limits for memory and depth to prevent abuse.Worktree.read_text_file
(see WITworktree.read-text-file
incrates/extension_api/wit/since_v0.6.0/extension.wit
), not the filesystem.Engine::compile
and cache by path; expose/rhai:reload
to rebuild on demand.ModuleResolver
that resolvesimport
by calling back into the host to read from.zed/scripts/modules/
viaWorktree
.Minimal host API surface (no new Zed API required)
run_slash_command
incrates/extension_api/src/extension_api.rs
).SlashCommandOutput
for visibility.process::Command
(requires manifest capabilities) if the user opts in.Incremental hot‑reload
/rhai:reload
.docs/src/extensions/developing-extensions.md
).Capabilities and security
[[capabilities]]
inextension.toml
(e.g.,process:exec
).Skeleton (Illustrative Only)
Cargo.toml (extension)
Rust entry
extension.toml
(capabilities minimal)Needed Host API Additions (to unlock Emacs‑like use)
These align with open issues/discussions and keep the surface small and safe:
With these in place, the Rhai extension can:
Comparison Recap
Rhai
Lua
Others (Starlark/JS/Python)
process::Command
), not in‑core.Phased Plan
Phase 1 (no host changes)
.zed/scripts/*.rhai
.Phase 2 (small host additions)
Phase 3 (richer capabilities)
References (Issues & Discussions)
Code Pointers (Stable Names)
crates/extension_api/src/extension_api.rs
(Extension
,register_extension!
,run_slash_command
,complete_slash_command_argument
).crates/extension_api/wit/since_v0.6.0/extension.wit
.docs/src/extensions/developing-extensions.md
.docs/src/extensions/slash-commands.md
.docs/src/configuring-zed.md
.extensions/slash-commands-example
.extensions/test-extension
.Closing
Zed’s Rust→WASM foundation is the right bedrock for speed and safety. A small, dynamic scripting layer—implemented as a Rhai‑powered extension—can deliver everyday hackability without compromising that bedrock. Start minimal with slash commands and reloadable scripts; evolve toward Emacs‑like power by adding a small, capability‑gated host surface for actions and events. This path aligns with active community requests and keeps Zed fast, safe, and delightfully programmable.
Beta Was this translation helpful? Give feedback.
All reactions