Common Lisp Extension for Zed
August 24, 2026 · View on GitHub
Common Lisp language support for the Zed editor with integrated LSP server and Jupyter kernel support.

Features
- LSP Features: Syntax highlighting, autocomplete, hover documentation, goto-definition
- Smart Completion: Type-aware parameter snippets, package-qualified completions
- Multi-Package Support: Package labels, user symbols prioritized
- Interactive REPL: Built-in REPL with
Ctrl+Shift+Enter, shared state across files - Rich Output: Display markdown, tables, images, and JSON inline
- Jupyter Compatible: Optional Jupyter Lab/Notebook support
- Cross-Platform: macOS, Linux, and Windows
Prerequisites
- Zed
- SBCL on PATH (
sbcl --versionmust work in a new terminal). After installing on Windows, fully quit and reopen Zed so it inherits PATH.- macOS:
brew install sbcl - Linux:
apt install sbcl/dnf install sbcl/pacman -S sbcl - Windows: sbcl.org (x86-64), or Scoop
scoop install sbcl
- macOS:
- From source only: rustup with
wasm32-wasip2(rustup target add wasm32-wasip2). Zed loads the WASMmakeproduces; it does not compile the extension itself.
ECL is optional (lisp_impl in ~/.zed-cl/config.json) where sb-bsd-sockets is available.
Quicklisp is optional. The REPL starts without it; config.json is used when cl-json is installed.
Installation
You need two things: the Zed extension (extension.toml + extension.wasm + languages/) and native tools (zed-cl-lsp, zed-cl-kernel, zed-cl-index, zed-cl-repl). Zed does not compile either.
Pick your OS zip:
| Machine | Native zip |
|---|---|
| macOS Apple Silicon | zed-cl-macos-aarch64.zip |
| macOS Intel | zed-cl-macos-x86_64.zip |
| Linux x86_64 | zed-cl-linux-x86_64.zip |
| Windows x86_64 | zed-cl-windows-x86_64.zip |
From a GitHub Release (no Rust, no make)
Use this after a tagged release (for example v1.0.1).
- Open Releases and download:
zed-cl-extension.zip- the native zip for your machine (table above)
- Unzip
zed-cl-extension.zipsomewhere you will keep (Zed loads it from that path):- macOS / Linux:
~/zed-cl-extension - Windows:
%USERPROFILE%\zed-cl-extension
- macOS / Linux:
- Unzip the native tools into
~/.zed-cl/bin:
mkdir -p ~/.zed-cl/bin
unzip zed-cl-macos-aarch64.zip -d ~/.zed-cl/bin
chmod +x ~/.zed-cl/bin/*
Windows (PowerShell):
New-Item -ItemType Directory -Force "$env:USERPROFILE\.zed-cl\bin" | Out-Null
Expand-Archive -Force zed-cl-windows-x86_64.zip "$env:USERPROFILE\.zed-cl\bin"
- In Zed: command palette (
Cmd+Shift+P/Ctrl+Shift+P) →zed: install dev extension→ select the unzipped extension folder (it must containextension.tomlandextension.wasm). - Open a
.lispfile. If the status bar shows a language-server error, run Restart Server onzed-cl. If eval does not appear, runrepl: refresh kernelspecs.
You can skip step 3: the extension downloads the native zip from the same release. If that fails, put the binaries in ~/.zed-cl/bin as above.
From source
Needs rustup with wasm32-wasip2 (rustup target add wasm32-wasip2).
git clone https://github.com/etyurkin/zed-cl
cd zed-cl
make build
Then in Zed: zed: install dev extension → the extension/ directory (the one with extension.toml and extension.wasm). make writes native tools to ~/.zed-cl/bin and copies extension.wasm into extension/.
After Lisp or native LSP changes, run make bundle so Zed's work-dir binaries are not stale. If Zed recompiles the linked extension (for example after editing extension.toml), it stops zed-cl and does not start it again. Reopen the .lisp buffer, or command palette → language server: restart.
On Windows without Make, build the four native crates with cargo build --release --manifest-path src/zed-cl-lsp/Cargo.toml (and kernel/index/repl), copy *.exe into bin\ and %USERPROFILE%\.zed-cl\bin, build the WASM crate with cargo build --release --target wasm32-wasip2 --manifest-path extension/Cargo.toml, copy zed_commonlisp.wasm to extension\extension.wasm, then install the extension\ directory as above.
Windows: "Failed to resolve clang path"
install dev extension makes Zed compile the Tree-sitter grammar itself, which needs clang from the wasi-sdk. Zed downloads it on demand; when that fails you get Failed to resolve clang path, and the real cause is only in the log (zed: open log, search for wasi-sdk). Install the SDK yourself and point Zed at it:
curl.exe -L -o "$env:TEMP\wasi-sdk.tar.gz" `
https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/wasi-sdk-25.0-x86_64-windows.tar.gz
mkdir "$env:USERPROFILE\wasi-sdk" -Force
tar -xzf "$env:TEMP\wasi-sdk.tar.gz" -C "$env:USERPROFILE\wasi-sdk" --strip-components=1
setx WASI_SDK_PATH "$env:USERPROFILE\wasi-sdk"
Zed looks for %WASI_SDK_PATH%\bin\clang.exe. Use wasi-sdk 25 — that is the version Zed pins. Fully quit Zed and relaunch it from Explorer so it picks up the new variable. If an earlier attempt left a half-written cache, delete %LOCALAPPDATA%\Zed\extensions\build\wasi-sdk* first. Zed's asset list has no ARM64 Windows build, so on ARM64 this manual install is the only route (the x86_64 clang.exe runs under emulation).
Configuration
All configuration is stored in ~/.zed-cl/config.json using profiles:
{
"active_profile": "sbcl",
"profiles": {
"sbcl": {
"lisp_impl": "sbcl",
"system_index": "system-index.db",
"completion_package_whitelist": [
"CORE-KEYWORDS",
"COMMON-LISP",
"COMMON-LISP-USER"
]
}
}
}
Profile Settings
Each profile can configure:
lisp_impl- Common Lisp implementation ("sbcl"or"ecl")system_index- System index database filename (in~/.zed-cl/)completion_package_whitelist- Packages to show in completions
Multiple Profiles
Create different profiles for different workflows:
{
"active_profile": "sbcl-full",
"profiles": {
"sbcl-full": {
"lisp_impl": "sbcl",
"system_index": "sbcl-complete.db",
"completion_package_whitelist": ["CORE-KEYWORDS", "COMMON-LISP", "COMMON-LISP-USER"]
},
"ecl-dev": {
"lisp_impl": "ecl",
"system_index": "ecl-packages.db",
"completion_package_whitelist": ["COMMON-LISP", "COMMON-LISP-USER"]
},
"minimal": {
"lisp_impl": "sbcl",
"system_index": "system-index.db",
"completion_package_whitelist": ["COMMON-LISP"]
}
}
}
Switch profiles by changing active_profile and restarting Zed.
Completion Package Whitelist
Control which packages appear in completions. By default (when not set), shows all user-defined packages plus COMMON-LISP and KEYWORD.
Special values:
"CORE-KEYWORDS"- Only core keywords (excludes system keywords)"ALL-KEYWORDS"- All keywords including system ones
Using the Extension
LSP Features
Open any .lisp file and get:
- Autocomplete for Common Lisp built-ins and your code
- Hover documentation
- Goto-definition
- Package-aware completions
If the status bar says the language server is not running after you rebuild the extension, reopen the .lisp buffer or run language server: restart.
Interactive REPL
Inline evaluation:
- Open a
.lispfile - Select the form (or put the cursor on a one-line form)
- Press
Ctrl+Shift+Enter(repl: run) - See results inline
Zed sends the current line or the selection. It does not send file path or line number, so the kernel cannot guess the enclosing form. For a multi-line defun or progn, expand the selection first (editor: select larger syntax node, default Alt+Up) and then run, or wrap a region in a ; %% cell:
; %%
(progn
(format t "hello~%")
(+ 1 2 3))
; %%
With ; %% markers, repl: run sends the whole cell, same as Python # %%.
Example files (shared REPL — eval definitions, then call them from another buffer):
examples/common-lisp-examples.lisp— functions inCL-USERexamples/my-utils.lisp— packageMY-UTILS; eval this beforemy-utils:callsexamples/using-shared-repl.lisp— call those definitions from another fileexamples/rich-output-examples.lisp— markdown, tables, images
Terminal REPL (for development):
- Open command palette (
Cmd+Shift+P) - Type "Tasks: Spawn"
- Select "Common Lisp REPL"
- Get an interactive REPL in a terminal tab
All evaluations share a single REPL environment - definitions are automatically available in autocomplete.
Direct terminal connection (advanced):
sbcl --script scripts/connect-repl.lisp
This connects over TCP (127.0.0.1) to the shared master REPL. On Unix, ./scripts/connect-repl.sh wraps the same script with optional rlwrap.
Building a System Index (Optional)
Goto-definition works out-of-the-box for your workspace code. To enable goto-definition for external libraries (Quicklisp packages, SBCL built-ins, etc.), build a system index.
Quick Start
After make build, the indexer is ~/.zed-cl/bin/zed-cl-index (also bin/zed-cl-index in the clone).
For SBCL users - index SBCL sources:
# Using Makefile (indexes SBCL built-ins)
make build-system-index
# Or manually
~/.zed-cl/bin/zed-cl-index \
--source-dir /path/to/sbcl/src/code \
--output ~/.zed-cl/system-index.db \
--default-package COMMON-LISP
For all users - index Quicklisp packages:
# Example: Index Alexandria
zed-cl-index \
--source-dir ~/quicklisp/dists/quicklisp/software/alexandria-<version> \
--output ~/.zed-cl/system-index.db \
--default-package ALEXANDRIA
Finding SBCL Source
macOS (Homebrew):
$(brew --prefix sbcl)/share/sbcl/src
# Usually: /opt/homebrew/share/sbcl/src or /usr/local/share/sbcl/src
Linux:
/usr/share/sbcl/src # Debian/Ubuntu
/usr/share/sbcl-source/src # Some distributions
Indexer Commands
Build an index:
zed-cl-index \
--source-dir <PATH> # Directory containing .lisp files (searches recursively)
--output <DB_FILE> # Output database file (appends if exists)
--default-package <PACKAGE> # Default package for symbols without (in-package ...)
Query an index:
zed-cl-index \
--query \
--database <DB_FILE> # Database to query
--symbol <SYMBOL> # Symbol name (e.g., MAPCAR, FORMAT) [required]
--package <PACKAGE> # Package name (e.g., SB-IMPL) [optional - searches all packages if omitted]
Examples
Index SBCL standard library:
# Core runtime (list functions, sequences, etc.)
zed-cl-index \
--source-dir /opt/homebrew/share/sbcl/src/code \
--output ~/.zed-cl/system-index.db \
--default-package COMMON-LISP
# CLOS/MOP (classes, methods, generic functions)
zed-cl-index \
--source-dir /opt/homebrew/share/sbcl/src/pcl \
--output ~/.zed-cl/system-index.db \
--default-package COMMON-LISP
# Interpreter
zed-cl-index \
--source-dir /opt/homebrew/share/sbcl/src/interpreter \
--output ~/.zed-cl/system-index.db \
--default-package COMMON-LISP
Index Quicklisp libraries:
# Alexandria
zed-cl-index \
--source-dir ~/quicklisp/dists/quicklisp/software/alexandria-20241012-git \
--output ~/.zed-cl/system-index.db \
--default-package ALEXANDRIA
# Iterate
zed-cl-index \
--source-dir ~/quicklisp/dists/quicklisp/software/iterate-1.5.3 \
--output ~/.zed-cl/system-index.db \
--default-package ITERATE
Query the index:
# Find MAPCAR in a specific package
zed-cl-index --query \
--database ~/.zed-cl/system-index.db \
--package SB-IMPL \
--symbol MAPCAR
# Output:
# Looking up: SB-IMPL::MAPCAR
#
# Found 1 definition(s):
#
# [1] function in SB-IMPL
# File: /opt/homebrew/share/sbcl/src/code/list.lisp
# Position: line 1388, char 19
# Find MAPCAR in all packages (omit --package)
zed-cl-index --query \
--database ~/.zed-cl/system-index.db \
--symbol MAPCAR
# Output:
# Looking up: MAPCAR (in all packages)
#
# Found 1 definition(s) across packages:
#
# [1] function in SB-IMPL
# File: /opt/homebrew/share/sbcl/src/code/list.lisp
# Position: line 1388, char 19
Multiple System Indexes (Advanced)
Create different indexes for different projects:
# Minimal: Just SBCL core
zed-cl-index \
--source-dir /path/to/sbcl/src/code \
--output ~/.zed-cl/system-sbcl-only.db \
--default-package COMMON-LISP
# Full: SBCL + all your Quicklisp libraries
zed-cl-index \
--source-dir /path/to/sbcl/src/code \
--output ~/.zed-cl/system-full.db \
--default-package COMMON-LISP
# ... add more libraries
Switch between indexes by changing the system_index field in your active profile in ~/.zed-cl/config.json and restarting the extension.
How It Works
- User code: Automatically indexed when you open/save
.lispfiles →~/.zed-cl/user-index.db - System libraries: Manually indexed using
zed-cl-index→~/.zed-cl/system-index.db(or custom name) - Goto-definition search order:
- User index (your workspace code)
- System index (SBCL + libraries you indexed)
Development Commands
# Build
make build # Build Rust binaries and extension
make dev # Development mode
make check # Type check only
make test # Run tests
# Jupyter
make install-jupyter # Register kernel for Jupyter
make verify # Verify SBCL installation
# Maintenance
make clean # Clean build artifacts
make help # Show all commands
Process Management (Unix)
List running processes:
# Count each process type
ps aux | grep -c '[z]ed-cl-kernel' # Count kernels
ps aux | grep -c '[z]ed-cl-lsp' # Count LSP servers
ps aux | grep -c 'master-repl' # Count master REPLs
# Show detailed process list
ps aux | grep -E 'zed-cl-kernel|zed-cl-lsp|master-repl' | grep -v grep
Kill processes:
# Kill all zed-cl processes
pkill -f 'zed-cl-kernel' && pkill -f 'zed-cl-lsp' && pkill -f 'master-repl'
# Kill individual components
pkill -f 'zed-cl-kernel' # Kill only kernels
pkill -f 'zed-cl-lsp' # Kill only LSP
pkill -f 'master-repl' # Kill only master REPL
# Force kill if needed
pkill -9 -f 'zed-cl-kernel' && pkill -9 -f 'zed-cl-lsp' && pkill -9 -f 'master-repl'
Log Locations
LSP Server:
~/.zed-cl/logs/lsp.log- LSP debug logs~/.zed-cl/logs/master-repl.log- Master REPL logs
Zed Application:
~/Library/Logs/Zed/Zed.log(macOS)~/.local/share/zed/logs/Zed.log(Linux)%LOCALAPPDATA%\Zed\logs\Zed.log(Windows)
Database Indexes:
~/.zed-cl/system-index.db- System libraries (SBCL + manually indexed packages)~/.zed-cl/user-index.db- User workspace code (auto-indexed)
Architecture
┌───────────────────────┐
│ Master REPL Process │
│ (TCP 127.0.0.1) │
└──────────┬────────────┘
│
┌─────────┼──────────┐
│ │ │
┌───▼───┐ ┌──▼───┐ ┌───▼───┐
│Console│ │ Zed │ │ Zed │
│ REPL │ │ REPL │ │ LSP │
└───────┘ └──────┘ └───────┘
All components connect to a single master REPL over TCP on localhost. Code evaluated in any component is immediately available in all others.
License
MIT