A Zed extension that lets you surround selected text with code snippets — inspired by vscode-surround.
- Select the text you want to surround
- Open the Code Actions menu (
Ctrl+./Cmd+.) - Pick a surround operation (e.g., "Surround: try/catch")
- Edit any placeholder values (
condition,name,params, etc.)
| Action | Result |
|---|---|
| Surround: if | if (condition) { ... } |
| Surround: if/else | if (condition) { ... } else { } |
| Surround: try/catch | try { ... } catch (error) { } |
| Surround: try/finally | try { ... } finally { } |
| Surround: try/catch/finally | try { ... } catch (error) { } finally { } |
| Action | Result |
|---|---|
| Surround: for...of | for (const item of iterable) { ... } |
| Surround: for (indexed) | for (let i = 0; i < length; i++) { ... } |
| Surround: forEach | items.forEach((item) => { ... }) |
| Surround: forEach (async) | items.forEach(async (item) => { ... }) |
| Surround: forEach (function) | items.forEach(function (item) { ... }) |
| Surround: forEach (async function) | items.forEach(async function (item) { ... }) |
| Action | Result |
|---|---|
| Surround: arrow function | const name = (params) => { ... } |
| Surround: async arrow function | const name = async (params) => { ... } |
| Surround: function declaration | function name(params) { ... } |
| Surround: async function declaration | async function name(params) { ... } |
| Surround: function expression | const name = function (params) { ... } |
| Surround: async function expression | const name = async function (params) { ... } |
| Surround: IIFE | (function (params) { ... })(args) |
| Action | Result |
|---|---|
| Surround: HTML element | <element>...</element> |
| Action | Result |
|---|---|
| Surround: block comment | /* ... */ |
| Surround: #region | // #region name ... // #endregion |
| Surround: template literal | `...` |
| Surround: template literal variable | `${...}` |
| Surround: console.log | console.log(...) |
You can add your own surrounds, override built-ins, or disable ones you don't use via Zed's settings. Configure globally in ~/.config/zed/settings.json or per-project in .zed/settings.json:
{
"lsp": {
"surround-ls": {
"settings": {
"surrounds": [
{
"name": "myWrapper",
"label": "Surround: My Wrapper",
"template": "wrapper({\\n\\t{{selected}}\\n})",
"languages": ["typescript", "javascript"]
}
],
"overrides": {
"consoleLog": {
"template": "console.warn({{selected}});"
}
},
"disabled": ["iife", "region"]
}
}
}
}| Key | Description |
|---|---|
surrounds |
Array of custom surround templates to add |
overrides |
Override built-in templates by name (see table above for names) |
disabled |
Array of built-in names to hide |
| Token | Meaning |
|---|---|
{{selected}} |
Replaced with the selected text (required) |
\n |
Newline |
\t |
One indent level (auto-detected from your file) |
{
"name": "describe",
"label": "Surround: describe block",
"template": "describe('name', () => {\\n\\t{{selected}}\\n});",
"languages": ["typescript", "javascript", "typescriptreact", "javascriptreact"]
}The languages field is optional — omit it to make the surround available in all languages.
JavaScript, TypeScript, TSX, JSX, Python, Rust, Go, C, C++, Java, Ruby, PHP, C#, Swift, Kotlin, Dart, HTML, CSS, SCSS, Vue, Svelte, Markdown, JSON, YAML, TOML, Shell Script, Elixir, Lua, Zig
This extension implements a lightweight LSP (Language Server Protocol) server that provides Code Actions. When you select text and open the Code Actions menu, the server generates surround options that wrap your selection with the chosen template while preserving indentation.
# Install dependencies and build the language server
cd server
npm install
npm run build- Open Zed
- Open the Extensions panel (
Cmd+Shift+X) - Click Install Dev Extension
- Select this repo's root directory
After making changes to the server, build and copy it to Zed's extension directory in one step:
cd server
npm run devThen in Zed, run "language server: restart" from the command palette (Cmd+Shift+P).
Note: Zed extensions run from a sandboxed work directory, not your source repo.
npm run devbuilds the server and copies it to~/Library/Application Support/Zed/extensions/work/zed-surround/server/dist/. If you only runnpm run build, the language server won't pick up your changes.
├── extension.toml # Zed extension manifest
├── src/
│ └── surround.rs # Zed extension glue (launches the LSP binary)
└── server/
└── src/
├── server.ts # LSP server (code actions, config loading)
├── surrounds.ts # Built-in surround templates
└── config.ts # User config types, validation, and merging
- Build the server (
cd server && npm run build) - Restart the language server in Zed (command palette → "language server: restart")
- Open a supported file (e.g.
.ts,.js) - Select some text and open Code Actions (
Cmd+.) — you should see the surround options
To test custom surrounds, add config to .zed/settings.json in your test project:
{
"lsp": {
"surround-ls": {
"settings": {
"surrounds": [
{
"name": "test",
"label": "Surround: test wrapper",
"template": "test({\\n\\t{{selected}}\\n})"
}
]
}
}
}
}Check the LSP logs for warnings about invalid config (Zed menu → View → Toggle Language Server Logs).
MIT