diff options
| author | Adam Malczewski <[email protected]> | 2026-06-02 17:52:14 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-02 17:52:14 +0900 |
| commit | 062d01bd2f5c3ab6de7747dc5028e66b81dac6f5 (patch) | |
| tree | 6097df0d53265f1a5e734aadab75c0334cb8e0e7 /packages/core/src/lsp/language.ts | |
| parent | b3aca3efe9e8cda79db6e2c7fa20482880ed16c3 (diff) | |
| download | dispatch-062d01bd2f5c3ab6de7747dc5028e66b81dac6f5.tar.gz dispatch-062d01bd2f5c3ab6de7747dc5028e66b81dac6f5.zip | |
feat(lsp): add config-driven LSP support (Roblox Luau via luau-lsp)
Add Language Server Protocol integration modeled on opencode's, wired for
this codebase's plain-TypeScript tool/agent architecture.
Core (@dispatch/core):
- lsp/client.ts: LSP/JSON-RPC client over stdio (vscode-jsonrpc) with the
initialize handshake, didOpen/didChange sync, push + pull diagnostics
(textDocument/diagnostic, workspace/diagnostic), and a generic request()
passthrough for hover/definition/references/documentSymbol.
- lsp/server.ts: resolves dispatch.toml [lsp] entries into spawn specs.
Config-driven only — no builtin registry, no auto-download.
- lsp/manager.ts: process-wide LspManager owning client lifecycles, keyed
by root+serverID, lazy spawn + reuse + graceful shutdown.
- lsp/language.ts: extension->languageId map incl. .luau -> "luau".
- lsp/diagnostic.ts: error-only <diagnostics> block formatting (1-based).
- tools/lsp.ts: on-demand 'lsp' tool (1-based coords -> 0-based wire).
- write-file.ts: optional onAfterWrite hook for diagnostics-on-write.
- config schema: validate [lsp] block; DispatchConfig.lsp + LspServerConfig.
API (@dispatch/api):
- AgentManager owns one LspManager; per-working-directory server cache
cleared on config reload; diagnostics appended to write_file results;
'lsp' tool gated by new perm_lsp setting; shutdownAll on destroy().
Config:
- dispatch.toml: documented, commented [lsp.luau-lsp] Roblox example.
Tests: fake-lsp-server fixture + client/manager/server/diagnostic/schema/
tool/write-hook suites, plus an opt-in real-binary luau-lsp smoke test
(auto-skipped when luau-lsp is absent). 652 pass; biome + 3 typechecks green.
Diffstat (limited to 'packages/core/src/lsp/language.ts')
| -rw-r--r-- | packages/core/src/lsp/language.ts | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/packages/core/src/lsp/language.ts b/packages/core/src/lsp/language.ts new file mode 100644 index 0000000..3e9fe68 --- /dev/null +++ b/packages/core/src/lsp/language.ts @@ -0,0 +1,72 @@ +/** + * File-extension → LSP `languageId` map. + * + * The LSP `textDocument/didOpen` notification carries a `languageId` string + * that tells the server how to parse the document. This table is a trimmed + * port of opencode's `lsp/language.ts`, with one critical addition for this + * project: `.luau` → `"luau"`. Roblox Luau sources use the `.luau` extension, + * which standard Lua tooling does not recognise — luau-lsp expects the + * `"luau"` languageId. + * + * Extensions are looked up with their leading dot (e.g. `".luau"`). Unknown + * extensions fall back to `"plaintext"` at the call site. + */ +export const LANGUAGE_EXTENSIONS: Record<string, string> = { + // Luau (Roblox) — the reason this module exists. Keep first for visibility. + ".luau": "luau", + ".lua": "lua", + // A pragmatic subset of common languages, mirroring opencode's table so a + // user can point an arbitrary LSP server at this codebase and have the + // right languageId reported. + ".c": "c", + ".cpp": "cpp", + ".cc": "cpp", + ".cxx": "cpp", + ".h": "c", + ".hpp": "cpp", + ".cs": "csharp", + ".css": "css", + ".dart": "dart", + ".go": "go", + ".html": "html", + ".htm": "html", + ".java": "java", + ".js": "javascript", + ".jsx": "javascriptreact", + ".json": "json", + ".jsonc": "jsonc", + ".kt": "kotlin", + ".kts": "kotlin", + ".md": "markdown", + ".markdown": "markdown", + ".php": "php", + ".py": "python", + ".rb": "ruby", + ".rs": "rust", + ".scss": "scss", + ".sass": "sass", + ".sh": "shellscript", + ".bash": "shellscript", + ".zsh": "shellscript", + ".sql": "sql", + ".svelte": "svelte", + ".swift": "swift", + ".toml": "toml", + ".ts": "typescript", + ".tsx": "typescriptreact", + ".mts": "typescript", + ".cts": "typescript", + ".vue": "vue", + ".xml": "xml", + ".yaml": "yaml", + ".yml": "yaml", + ".zig": "zig", +}; + +/** + * Resolve the LSP `languageId` for a file path's extension, falling back to + * `"plaintext"` when the extension is unknown. + */ +export function languageIdForExtension(extension: string): string { + return LANGUAGE_EXTENSIONS[extension] ?? "plaintext"; +} |
