>_EXECUTIVE SUMMARY
vscode-elisp is the VS Code / VSCodium extension for Emacs Lisp, backed by the elisp interpreter — a pattern/action engine written in Rust (POSIX / / -style union CLI). It ships a declarative language contribution (filetype, grammar, editor config), an LSP client that launches elisp --lsp, a debug adapter that launches elisp --dap, and a TextMate grammar (source.elisp) covering the Emacs Lisp surface.
Design principle: mirror the proven vscode-stryke runtime wiring. The LSP transport is omitted so the client spawns bare elisp --lsp and never appends --stdio (the arg-rejection / “connection got disposed” failure mode learned from vscode-stryke). The binary is resolved to an absolute path through $PATH plus common install dirs (~/.cargo/bin, /opt/homebrew/bin, …), so it works even when the editor is launched from the macOS Dock and doesn’t inherit the shell $PATH.
~COMPONENTS
| File | Responsibility |
|---|---|
package.json | Extension manifest — contributes.languages (id elisp, *.el, shebang firstLine), contributes.grammars (source.elisp), contributes.configuration (LSP settings), contributes.commands (run / debug), contributes.debuggers (type elisp), and the vscode-languageclient dependency. |
language-configuration.json | Line comment ;, paren / bracket pairs, auto-closing / surrounding pairs, symbol word pattern, and paren-based indentationRules. |
extension.js | Activates on onLanguage:elisp. Starts a LanguageClient running elisp --lsp over stdio, registers the run command (terminal elisp <file>), and registers a DebugAdapterDescriptorFactory launching elisp --dap. Missing binary → one non-fatal warning; highlighting still works. |
lib/resolveBinary.js | Pure (vscode-free) resolver that turns the elisp.path setting into an absolute, executable path — searching $PATH then the GUI-missed fallback dirs. Unit-tested in CI. |
syntaxes/elisp.tmLanguage.json | TextMate grammar — comments, shebang, strings, char literals, keyword symbols, numbers (#x / #o / #b), quoting sugar, special forms, builtin functions / subrs, and definition names. |
scripts/tokenize_test.js | Loads the grammar under vscode-textmate + vscode-oniguruma (the engine VS Code itself uses) and asserts the scope of 13 sample tokens. |
$SCOPE MAP
| Token group | TextMate scope | Sample |
|---|---|---|
| Special forms | keyword.control.elisp | defun defmacro let let* cond lambda setq if when unless while progn quote |
| Definition names | entity.name.function.elisp | the name in (defun NAME …) |
| Builtin functions (subrs) | support.function.elisp | car cdr cons list mapcar funcall apply format message + 1+ |
| Keyword symbols | constant.other.keyword.elisp | :test :key :initial-value |
| Char literals | constant.character.elisp | ?a ?\n ?\C-x |
| Constants | constant.language.elisp | t nil |
| Numbers | constant.numeric.elisp | 42 3.14 #xFF #o17 #b1010 |
| Quoting | keyword.operator.quote.elisp | ' ` , ,@ #' |
| Literals | string.* / comment.line.semicolon | strings, ; comments |
#VERIFICATION
The grammar is loaded under the exact engine VS Code uses at runtime — vscode-textmate driving vscode-oniguruma — and a sample is tokenized with the resulting scopes asserted. This proves the grammar compiles under oniguruma and classifies Emacs Lisp tokens correctly, including the tricky cases: an operator subr such as + distinguished from a symbol, the definition-name capture in (defun greet …), and a keyword symbol :kw against a char literal ?a.
| Sample token | Asserted scope |
|---|---|
defun | keyword.control.elisp |
greet | entity.name.function.elisp |
message / + | support.function.elisp |
:kw | constant.other.keyword.elisp |
?a | constant.character.elisp |
nil | constant.language.elisp |