README.md
September 3, 2026 · View on GitHub
██╗ ██╗███████╗ ██████╗ ██████╗ ██████╗ ███████╗ ███████╗███████╗██╗ ██╗
██║ ██║██╔════╝██╔════╝██╔═══██╗██╔══██╗██╔════╝ ╚══███╔╝██╔════╝██║ ██║
██║ ██║███████╗██║ ██║ ██║██║ ██║█████╗█████╗ ███╔╝ ███████╗███████║
╚██╗ ██╔╝╚════██║██║ ██║ ██║██║ ██║██╔══╝╚════╝███╔╝ ╚════██║██╔══██║
╚████╔╝ ███████║╚██████╗╚██████╔╝██████╔╝███████╗ ███████╗███████║██║ ██║
╚═══╝ ╚══════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚══════╝╚══════╝╚═╝ ╚═╝
[VS CODE EXTENSION // NEON GRAMMAR // COMPLETE BUILTIN SURFACE // LSP]
"Open a
.zshrc. The whole shell lights up — every builtin, every world-first extension."
VS Code / VSCodium support for zshrs — the first-ever Rust rewrite of zsh: a compiled, JIT'd, massively parallel shell. A standalone TextMate grammar (it owns the zshrs language id — not a shellscript reskin), filetype detection for zsh dotfiles, language-server integration via zshrs --lsp, one-key running, and full debugging (breakpoints, stepping, variables) via zshrs --dap.
Read the Docs · Engineering Report · zshrs · vscode-stryke
[0x00] OVERVIEW
vscode-zsh is the VS Code / VSCodium extension for zshrs. It provides:
- Filetype detection — zsh dotfiles (
.zshrc,.zshenv,.zprofile,.zlogin,.zlogout,.zpreztorc),*.zsh/*.zsh-themefiles, and files whose first line is a zsh / zshrs shebang (#!/usr/bin/env zsh). - Syntax highlighting — a standalone TextMate grammar (
source.zshrs) with its own language id, so it owns the language rather than reskinning the built-in shell grammar. - Language server —
zshrs --lspvia vscode-languageclient (diagnostics, hover, completion — whatever the server provides). - Run —
zshrs: Run File(Ctrl+F5) executes the active script in a terminal. - Debugging — breakpoints, stepping, call stack, variables, and watch via zshrs's native connect-back debug adapter (
zshrs --dap), bridged to VS Code.
The grammar is generated (scripts/gen_grammar.sh) directly from the zshrs binary's own reflection tables (zshrs --dump-reflection), so it carries the language's real surface and never drifts:
- 138 builtins —
.builtinskeys, minus keyword names, minus extension names - 112 extensions —
.extensionskeys, given their own scope (the zshrs world-first additions) - 245 special variables —
.special_varskeys
Created by MenkeTechnologies.
[0x01] FEATURE MATRIX
| Capability | Status |
|---|---|
| Filetype detection — dotfiles | Implemented — contributes.languages filenames map |
Filetype detection — *.zsh / *.zsh-theme | Implemented — contributes.languages extension map |
| Filetype detection — shebang | Implemented — firstLine regex ^#!.*\bzsh\b |
| Syntax highlighting | Implemented — TextMate grammar (source.zshrs), own language id |
| Comments / brackets / autoclose | Implemented — language-configuration.json |
| Indentation | Implemented — brace-based indentationRules |
| Language server | Implemented — zshrs --lsp via vscode-languageclient |
| Run | Implemented — zshrs: Run File (Ctrl+F5 / editor-title ▶) runs zshrs <file> in a terminal |
| Debugging | Implemented — breakpoints, step over/into/out, call stack, scopes, variables, watch/hover, via zshrs --dap (native connect-back DAP, bridged to VS Code) |
| Config | zshrs.path, zshrs.lsp.enabled, zshrs.lsp.args |
The extension resolves the
zshrsbinary from$PATHplus the common install locations (~/.cargo/bin,/opt/homebrew/bin,/usr/local/bin,~/.local/bin) — so it works even when the editor is launched from the macOS Dock / Finder, which doesn't inherit your shell$PATH. Build zshrs withcargo build(installs to~/.cargo/bin). If it lives elsewhere, setzshrs.pathto the absolute path.
[0x02] INSTALL
This extension is not yet on the Marketplace. Build and install the .vsix locally:
git clone https://github.com/MenkeTechnologies/vscode-zsh
cd vscode-zsh
npm install
npx @vscode/vsce package # produces vscode-zsh-<version>.vsix
code --install-extension vscode-zsh-*.vsix
Or drop the folder into your extensions dir for development:
git clone https://github.com/MenkeTechnologies/vscode-zsh \
~/.vscode/extensions/vscode-zsh
Open any .zshrc or .zsh file — it lights up. The language server starts automatically when zshrs is on $PATH.
[0x03] RUN & DEBUG
Run — open a zsh script and press Ctrl+F5, click the ▶ in the editor
title bar, or run zshrs: Run File from the command palette. The file is saved
and executed as zshrs <file> in an integrated terminal.
Debug — set breakpoints in the gutter and press F5, click the debug
icon in the editor title bar, or run zshrs: Debug File from the command
palette. No launch.json is required: F5 on a zsh script debugs the active file.
You get breakpoints, step over/into/out, call stack, scopes, variables, watch
expressions, and hover-to-evaluate.
zshrs's debug adapter (zshrs --dap HOST:PORT) is connect-back — it dials
into a listener the IDE provides (the same model it uses in JetBrains). The
extension runs that listener, spawns zshrs --dap 127.0.0.1:<port>, and pipes it
to VS Code's debugger, so it works the same as any other VS Code debug session.
For a saved configuration, add to .vscode/launch.json:
{
"type": "zshrs",
"request": "launch",
"name": "zshrs: Debug Current File",
"program": "${file}",
"cwd": "${workspaceFolder}",
"stopOnEntry": false,
"args": []
}
Launch attributes: program, args, cwd, stopOnEntry, and zshrsPath
(override the binary for one session). The binary is resolved the same way as the
language server, so it works under the macOS GUI $PATH.
[0x04] SYNTAX // SCOPES
The grammar maps zshrs tokens to standard TextMate scopes, so every VS Code theme colors them:
| Token group | Scope | Sample |
|---|---|---|
| Control flow | keyword.control.zshrs | if then fi for while case esac function return |
| Declarations | storage.modifier.zshrs | typeset local export declare readonly integer float |
| Builtins (138) | support.function.builtin.zshrs | bindkey autoload zstyle compadd setopt zle … |
| Extensions (112) | support.function.extension.zshrs | base64 async await barrier clone … |
| Special variables (245) | variable.language.zshrs | PATH HOME PWD RANDOM … |
| Sigil variables | variable.other.zshrs | $foo ${bar} $1 $? $@ $# $$ $! $* |
| Operators / pipes / redirects | keyword.operator.zshrs | | || && ;; > >> << >& =~ |
Strings (single / double / backtick), here-docs (<<EOF, <<-, <<'EOF'), $var / ${...} interpolation, escapes, command substitution $(...), and numbers are all scoped too.
[0x05] LANGUAGE SERVER
The extension launches zshrs --lsp (stdio JSON-RPC) through vscode-languageclient. Configure it in Settings:
| Setting | Default | Effect |
|---|---|---|
zshrs.path | zshrs | Path to the zshrs executable |
zshrs.lsp.enabled | true | Start the language server (set false for highlighting only) |
zshrs.lsp.args | ["--lsp"] | Args passed to start the server |
If the binary is missing, the extension shows one non-fatal warning and syntax highlighting keeps working.
[0x06] REGENERATING THE GRAMMAR
The builtin / extension / special-variable surface is generated from the live binary so it never drifts. After a zshrs upgrade:
./scripts/gen_grammar.sh # rewrites syntaxes/zshrs.tmLanguage.json
npm run gen # same thing via npm
Verify it still tokenizes correctly with the real VS Code grammar engine, and run the extension's unit tests — the same commands CI runs:
npm install
node scripts/tokenize_test.js # vscode-textmate + vscode-oniguruma scope assertions
node --test scripts/resolver_test.js scripts/activate_test.js scripts/dapbridge_test.js
resolver_test.js covers binary resolution against a temporary PATH,
activate_test.js drives extension.js against a stub vscode module (the LSP
is spawned as bare zshrs --lsp; a missing binary never constructs the client;
run + debug are registered under the zshrs debug type; the config provider
fills in the active file for F5 with no launch.json), and dapbridge_test.js
pipes a DAP initialize through the bridge to a real zshrs --dap, skipping
when the binary is absent.
[0x07] LAYOUT
vscode-zsh/
├── package.json # extension manifest — languages, grammars, configuration,
│ # commands, keybindings, menus, breakpoints, debuggers
├── language-configuration.json # comments, brackets, autoclose, indent rules
├── extension.js # activation: LSP client (zshrs --lsp), the zshrs.run /
│ # zshrs.debug commands, and the debug adapter factory
├── lib/resolveBinary.js # vscode-free resolver: $PATH + the common install dirs
├── lib/dapBridge.js # vscode-free rendezvous server bridging VS Code's DAP
│ # client to zshrs's connect-back `zshrs --dap HOST:PORT`
├── syntaxes/zshrs.tmLanguage.json # generated grammar — builtins, extensions, special vars
├── scripts/gen_grammar.sh # regenerates the grammar from the zshrs binary
├── scripts/tokenize_test.js # tokenizes a sample with vscode-textmate + asserts scopes
├── scripts/resolver_test.js # binary resolution against a temp PATH
├── scripts/dapbridge_test.js # the bridge piping two sockets end to end
├── scripts/activate_test.js # activation against a stub `vscode` module
└── tests/ # repo hygiene checks for README, docs and the workflow
[0x08] LICENSE
MIT © MenkeTechnologies