7.14. LSP Server — Editor & AI Diagnostics
The daslang LSP server implements the
Language Server Protocol
for .das files. What it buys over the MCP tools:
push diagnostics — the compiler and lint report after every edit
with no explicit tool call — plus native go-to-definition, references,
hover, document / workspace symbols, call hierarchy, and
go-to-implementation.
Zero setup beyond a daslang build: no Python, no sgconfig, no tree-sitter, no MCP server.
7.14.1. Quick start
Claude Code sessions started at the daslang repository root (or at an
installed SDK root) load the server automatically — the checked-in
.claude/skills/daslang-lsp/.claude-plugin/plugin.json manifest
registers it on workspace trust. From any other directory, wire it in
explicitly with:
claude --plugin-dir /abs/path/to/daScript/utils/lsp/plugin
(The explicit form also takes precedence over the checked-in manifest when both are present, which is what you want while developing the server itself.)
Diagnostics then attach automatically to the next tool result after any
edit of a .das file, and the LSP tool exposes definition,
references, hover, documentSymbol, workspaceSymbol, implementation, and
call-hierarchy operations.
Any other stdio LSP client works too — point it at:
bin/watchdog --lsp
or, from a tree with no static exe built, at the same front under the interpreter:
bin/daslang utils/watchdog/main.das -- --lsp
Note
The plugin manifest names bin/watchdog on every platform. On
Windows the spawn resolves that bare name to watchdog.exe, and a
multi-config (Visual Studio) build copies the exe it just built from
bin/<Config>/ into bin/ as well, so the checked-in manifest
needs no local edit.
7.14.2. Diagnostics
One lint-profile compile serves both error classes:
Compile errors publish with severity Error, the numeric daslang error code (e.g.
30341), and the exact source range.On a clean compile, the repo’s paranoid / performance / style lint passes run on the same program: LINT and PERF rules publish as Warning, STYLE rules as Information. Repo lint policy is honored (
.lint_config, default-off rules,// nolintcomments).
dastest expect-files (negative tests that declare intentional compile
errors via expect directives) get a single informational note
instead of their intentional errors.
Unsaved buffers are compiled as-is: the server shadows every open document and overlays the buffer text over the on-disk content for both diagnostics and navigation, so clients that validate while typing get correct positions without saving.
7.14.4. Configuration
initializationOptions (all optional):
Key |
Description |
|---|---|
|
Path to the daslang binary. |
|
|
|
Passed as |
|
List; each entry passed as |
Compiler lookup order: initializationOptions.compiler →
$DASLANG_LSP_COMPILER → repo and installed-SDK layouts
(bin/Release/daslang[.exe], bin/daslang[.exe],
build/daslang, build/bin/daslang) under the workspace and the
supervisor’s own tree → daslang on PATH. All option paths
(compiler, project, project_root, load_module) are
absolutized against the workspace root at initialize — subtools spawn
with a per-request cwd, so relative paths would otherwise re-resolve
per file.
Set DASLANG_LSP_LOG=<path> for a message-by-message debug log
(default: daslang_lsp.log in the system temp directory).
7.14.5. Architecture
Two processes, hard split:
watchdog --lsp(utils/watchdog/lsp_front.das, in the static watchdog exe) — the endpoint the client spawns. Owns all session state: Content-Length framing, theinitializehandshake, the document shadow, debounce, and request dispatch. Zero language knowledge.utils/lsp/subtools/*.das— stateless batch tools (validate.das,nav.das). One fresh daslang process per request: argv in, LSP-shaped JSON out, exit.
No resident daslang process ever runs, by design: no macro-state leaks
across compiles, no binary or DLL locks while builds run, and a
compiler crash on a broken buffer costs one request instead of the
session. Full rationale and wave history: utils/lsp/ROADMAP.md.
7.14.6. Tests
tests/lsp/test_lsp_protocol.das drives the front over a stdio pipe,
through the interpreter host and through the static exe where it is
built: initialize → didOpen with broken buffer text against a clean disk
file (proving the unsaved-buffer overlay) → didChange back to clean →
definition → the call-hierarchy loop (prepare → incoming → outgoing) →
implementation → shutdown/exit:
bin/daslang dastest/dastest.das -- --test tests/lsp
tests/watchdog/test_lsp_front.das covers the cross-tree guard over
throwaway git-style trees.
See also
utils/lsp/README.md — setup and configuration details
MCP Server — AI Tool Integration — the MCP server (pull-style tools: introspection, navigation, execution, live-reload control)