summaryrefslogtreecommitdiffhomepage
path: root/packages/core/src/lsp/server.ts
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-02 21:10:09 +0900
committerAdam Malczewski <[email protected]>2026-06-02 21:10:09 +0900
commitd9f53727845dface3e6d8a84ba2270b1de55482b (patch)
tree6d42f0a0fbda15057296992e78c4b4e12046f9ed /packages/core/src/lsp/server.ts
parent80212bfb009eaf71a4743310dee6ed08b8f7e1da (diff)
parent9d8cf7005ba4c0bb8ade0775f54c2557aa1c5683 (diff)
downloaddispatch-d9f53727845dface3e6d8a84ba2270b1de55482b.tar.gz
dispatch-d9f53727845dface3e6d8a84ba2270b1de55482b.zip
Merge branch 'dev' into feat/cs-code-search-tool
# Conflicts: # packages/api/src/agent-manager.ts # packages/api/tests/agent-manager.test.ts # packages/frontend/src/lib/components/ToolPermissions.svelte # packages/frontend/src/lib/settings.svelte.ts
Diffstat (limited to 'packages/core/src/lsp/server.ts')
-rw-r--r--packages/core/src/lsp/server.ts68
1 files changed, 68 insertions, 0 deletions
diff --git a/packages/core/src/lsp/server.ts b/packages/core/src/lsp/server.ts
new file mode 100644
index 0000000..1fb002e
--- /dev/null
+++ b/packages/core/src/lsp/server.ts
@@ -0,0 +1,68 @@
+import { type ChildProcessWithoutNullStreams, spawn } from "node:child_process";
+import type { LspServerConfig } from "../types/index.js";
+import type { LspServerHandle } from "./client.js";
+
+/**
+ * A resolved, ready-to-spawn LSP server derived from a `dispatch.toml`
+ * `[lsp.<id>]` entry. Config-driven only — dispatch ships no builtin server
+ * registry and performs no auto-download (unlike opencode). The declared
+ * executable (`command[0]`) must already be on PATH.
+ */
+export interface ResolvedLspServer {
+ id: string;
+ /** Extensions (with leading dot) this server attaches to, e.g. `".luau"`. */
+ extensions: string[];
+ /** Launch the server over stdio rooted at `root`. */
+ spawn(root: string): LspServerHandle;
+}
+
+/**
+ * Spawn a child process for an LSP server over stdio. Inherits `process.env`
+ * (so a PATH-resident `rojo` is visible to luau-lsp's sourcemap autogenerate)
+ * and merges any `env` from the server config on top.
+ */
+function spawnServer(
+ command: string[],
+ cwd: string,
+ env: Record<string, string> | undefined,
+ initialization: Record<string, unknown> | undefined,
+): LspServerHandle {
+ const [cmd, ...args] = command;
+ if (!cmd) throw new Error("LSP server command is empty");
+ const proc = spawn(cmd, args, {
+ cwd,
+ env: { ...process.env, ...env },
+ stdio: ["pipe", "pipe", "pipe"],
+ }) as ChildProcessWithoutNullStreams;
+ return {
+ process: proc,
+ ...(initialization ? { initialization } : {}),
+ };
+}
+
+/**
+ * Turn the parsed `dispatch.toml` `lsp` block into a list of spawnable
+ * servers. Disabled entries are dropped. Entries with no `command`/`extensions`
+ * are skipped defensively (the config validator already enforces these, but we
+ * guard here too so a hand-built config object can't crash the manager).
+ */
+export function resolveServersFromConfig(
+ lsp: Record<string, LspServerConfig> | undefined,
+): ResolvedLspServer[] {
+ if (!lsp) return [];
+ const servers: ResolvedLspServer[] = [];
+ for (const [id, entry] of Object.entries(lsp)) {
+ if (entry.disabled) continue;
+ if (!entry.command || entry.command.length === 0) continue;
+ if (!entry.extensions || entry.extensions.length === 0) continue;
+ const command = entry.command;
+ const env = entry.env;
+ const initialization = entry.initialization;
+ servers.push({
+ id,
+ extensions: entry.extensions,
+ spawn: (root: string) => spawnServer(command, root, env, initialization),
+ });
+ }
+ return servers;
+}