feat(config): add per-repo .pi-lsp.json server overrides
Users can now drop a .pi-lsp.json at any ancestor of their working files to add new LSP servers, override built-in ones, or disable servers entirely. The nearest config (walking upward) wins. - New src/config.ts: walks upward for .pi-lsp.json, parses, and merges with the built-in registry. Cached per config-file path with mtime invalidation. Falls back to built-ins on parse error. - Merge rules: matching id shallow-merges (user wins); new id appends (must include match/command/args/rootMarkers); `disable` filters at the end. - src/root.ts: pickServer() now resolves servers via the per-repo registry. Adds findServerById(filePath, id) and re-exports getServersForPath() for callers. - src/daemon.ts: getOrCreateEntry() resolves serverId against the filePath's config so spawned servers reflect repo overrides. - index.ts and cli.ts: replace direct `servers` imports with path-aware getServersForPath() lookups. - Tests: 9 new unit tests covering merge semantics, walk-up discovery, mtime invalidation, and graceful fallback. - Docs: README "Per-Repo Config" section + AGENTS.md updates.
This commit is contained in:
@@ -6,9 +6,8 @@ import * as fs from "node:fs";
|
||||
import * as net from "node:net";
|
||||
import * as path from "node:path";
|
||||
import { LspClient } from "./client.ts";
|
||||
import { findRoot, pathToUri } from "./root.ts";
|
||||
import { findRoot, findServerById, pathToUri } from "./root.ts";
|
||||
import type { ServerConfig } from "./types.ts";
|
||||
import { servers } from "../server.ts";
|
||||
import {
|
||||
logPath,
|
||||
socketPath,
|
||||
@@ -51,25 +50,17 @@ function log(...args: unknown[]) {
|
||||
);
|
||||
}
|
||||
|
||||
// Find Server By ID - Looks up a ServerConfig from the registry by ID.
|
||||
// Throws if the ID is not registered.
|
||||
function findServerById(serverId: string): ServerConfig {
|
||||
const server = servers.find((s) => s.id === serverId);
|
||||
if (!server) {
|
||||
throw new Error(`Unknown server ID: "${serverId}". Registered: ${servers.map((s) => s.id).join(", ")}`);
|
||||
}
|
||||
return server;
|
||||
}
|
||||
|
||||
// Get Or Create Entry - Looks up the cached client for a server+file,
|
||||
// spawning a fresh LspClient if needed. The returned entry is guaranteed
|
||||
// to have its `ready` promise resolved before the caller uses it.
|
||||
// to have its `ready` promise resolved before the caller uses it. The
|
||||
// server registry is resolved against any `.pi-lsp.json` reachable from
|
||||
// `filePath`, so per-repo config overrides take effect at spawn time.
|
||||
async function getOrCreateEntry(
|
||||
filePath: string,
|
||||
serverId: string,
|
||||
launch: LaunchContext,
|
||||
): Promise<ClientEntry> {
|
||||
const server = findServerById(serverId);
|
||||
const server = findServerById(filePath, serverId);
|
||||
const rootDir = findRoot(filePath, server.rootMarkers);
|
||||
const key = `${server.id}::${rootDir}`;
|
||||
const existing = entries.get(key);
|
||||
|
||||
Reference in New Issue
Block a user