Language server
zy lsp starts the local stdio language server. Editors exchange Content-Length-framed JSON-RPC messages on stdin/stdout; startup text and script output never enter the protocol stream. This is an initial LSP 3.17 implementation with diagnostics, formatting, and lexical editor features.
Available features
| Feature | Behavior |
|---|---|
| Unsaved source | Full-document open/change/close synchronization for local file URIs, including files not yet saved |
| Diagnostics | Same lexer/parser/module/known-contract rules as the CLI, including unsaved imported-file overlays |
| Dependency updates | Recheck all open roots after edits, saves, closes, and client-reported watched-file changes |
| Error locations | Zero-based UTF-16 ranges and stable Zy category codes; dependency errors appear on their source file |
| Formatting | Lossless spacing/block layout using core.FormatSource; no disk writes |
| Hover | Lexical declarations, parameters, loop variables, function signatures, built-ins, and library members |
| Completion | Visible names, keywords, libraries, imported exports, and annotated record fields/enum variants |
| References | References to a lexical binding within the current document |
| Rename | Private local binding rename; rejects exported APIs and conflicting existing names |
| Definition | Local declarations and imported exported declarations, including unsaved dependencies |
| Cancellation | Superseded validation snapshots are canceled; canceled editor requests receive -32800 |
| Version handling | Older document changes are ignored; stale editor results receive -32801 |
| Lifecycle | Initialize, shutdown, and exit; unsupported requests return method-not-found |
Diagnostics are produced without evaluating code. The checker validates known annotations and typed boundaries; dynamic names, collection elements, and host results still require execution. It reports the first error for each open root, deduplicates shared dependency errors, and clears obsolete publications. Formatting always uses the same four-space style as the CLI, regardless of editor tab preferences.
VS Code setup
- Build the executable with
go build -o bin/zy.exe .on Windows, orgo build -o bin/zy .on Linux/macOS. - From the language repository root, run
./editors/vscode/install.ps1to test, bundle, package, and install the local extension. - Reload VS Code and open any application folder containing
.zyfiles. Source development windows requirenpm run buildfirst. - Set the user-level Zy: Server Path to the absolute executable path, or put
zyon PATH. - In a trusted workspace, run Zy: Start Language Server. Use Format Document on a Zy file to format its current text. Zy: Stop Language Server disposes the client and watcher.
For automatic startup after opting in, set zy.languageServer.enabled to true. It defaults to false. A configuration change restarts an enabled server. The client passes the executable and lsp as separate arguments and does not launch a command shell. Relative executable paths and command strings are rejected. The server never executes Zy programs, but launching any configured host executable still requires a trusted workspace.
Syntax support works without a Zy binary. The installed VSIX bundles its language client; applications do not install npm dependencies. Building the extension from source requires npm, while using an existing VSIX does not. Go is not needed to run a prebuilt Zy server executable.
For another LSP-capable editor, configure the executable with arguments ["lsp"], stdio transport, language identifier zy, and .zy file association. Do not configure incremental changes: the advertised synchronization kind is full-document. The server is local-only and opens no listening port.
Bounds and lifecycle details
- Local
file:URIs only;untitled:, remote hosts/UNC paths, virtual documents, URI query strings, and URI fragments are unsupported. Unsaved text associated with a normal local filename is supported. - At most 64 open documents; source larger than 1 MiB receives a diagnostic. The retained oversized snapshot is capped just above that limit. Existing module count/depth/combined-source limits still apply.
- Protocol headers are limited to 8 KiB and message bodies to 8 MiB. Malformed framing closes the transport. Invalid JSON and requests receive JSON-RPC errors when framing permits a response.
- One validation worker processes immutable snapshots with a single latest-pending slot. Superseded results cannot overwrite a newer processed generation. All roots are rechecked for correctness; large workspaces may need a dependency index in a later version.
- Up to eight formatting/semantic operations can be in flight. Cancellation suppresses results immediately but cannot interrupt a parser call already running. Module traversal checks cancellation; synchronous host file I/O is not forcibly interruptible.
- Closing an overlay restores disk content for remaining importers. If no open root needs a previous error, the server publishes an empty diagnostic list. Duplicate physical-file overlays with conflicting content are rejected.
- The CLI JSON diagnostic API uses one-based Unicode code points; LSP converts to zero-based UTF-16. Consumers must not mix these coordinate systems.
- A proper client sends
shutdown, waits for its response, then sendsexit. Exit before shutdown is unsuccessful. Embedders cancelinglsp.Runmust also close their input stream to unblock an outstanding read.
Lexical editor behavior
Use Go to Definition (F12), Show Hover (Ctrl+K, Ctrl+I), and Trigger Suggest (Ctrl+Space) in VS Code. A dot triggers namespace/export completion automatically. Names from a nested block, function parameter, or loop stay within that scope. A let initializer resolves an existing outer binding before the new binding is declared. Imported namespaces expose only explicit export declarations; private bindings stay private. Open dependency text takes priority over disk.
The index does not execute scripts or infer dynamic runtime types. It uses explicit record annotations for field completion and typed signatures for hover. References and private-binding rename stay within one document; exported APIs and field names cannot be renamed. Rename rejects any conflicting binding and limits new names to 256 UTF-8 bytes. Definitions assigned later through a captured environment, computed members, collection methods, module aliases, and workspace-wide search remain unsupported. Local indexing requires parseable source; completion repairs a member name at a dot cursor, but unrelated syntax errors can limit results to the built-in catalog. No suggestions are returned inside comments or strings. VS Code may independently offer word-based suggestions; disable editor.wordBasedSuggestions to view only provider/snippet results.
Semantic requests use immutable document snapshots, share the eight-request limit with formatting, support cancellation, and return -32801 if an open document or dependency generation changes before their result is delivered. Disk-backed export lookup reads only the explicitly selected local .zy dependency, checks regular-file status, and enforces the 1 MiB source limit. It does not crawl arbitrary workspace files.
Validation and remaining scope
Go tests cover scope boundaries, shadowing, initializer binding, parameters, imported exports, unsaved overlays, protocol requests, UTF-16, stale dependency generations, comments/strings, lifecycle, framing, and limits. python tools/smoke_lsp.py bin/zy.exe also checks hover, completion, and definition against the compiled executable. Archive smoke tests run the same protocol checks.
Real VS Code extension-host tests exercise providers through editor commands, including unsaved dependency navigation, private export filtering, formatting edits, diagnostics, and shutdown. Run npm run test:integration in editors/vscode with ZY_EXECUTABLE set to an absolute compiled executable path; it uses an isolated temporary profile and workspace. The test runner defaults to VS Code 1.85.2; VSCODE_EXECUTABLE_PATH can select an existing installation. See the engineering review for visual observations and the remaining human trial gate.