/** * LSP Client Implementation * * Manages connections to language servers using JSON-RPC 2.0 over stdio. * Handles server lifecycle, message buffering, and request/response matching. */ import type { DevContainerContext } from './devcontainer.js'; import type { LspServerConfig } from './servers.js'; /** Default timeout (ms) for LSP requests. Override with OMC_LSP_TIMEOUT_MS env var. */ export declare const DEFAULT_LSP_REQUEST_TIMEOUT_MS: number; export declare function getLspRequestTimeout(serverConfig: Pick, method: string, baseTimeout?: number): number; export interface Position { line: number; character: number; } export interface Range { start: Position; end: Position; } export interface Location { uri: string; range: Range; } export interface TextDocumentIdentifier { uri: string; } export interface TextDocumentPositionParams { textDocument: TextDocumentIdentifier; position: Position; } export interface Hover { contents: string | { kind: string; value: string; } | Array; range?: Range; } export interface Diagnostic { range: Range; severity?: number; code?: string | number; source?: string; message: string; } export interface DocumentSymbol { name: string; kind: number; range: Range; selectionRange: Range; children?: DocumentSymbol[]; } export interface SymbolInformation { name: string; kind: number; location: Location; containerName?: string; } export interface WorkspaceEdit { changes?: Record>; documentChanges?: Array<{ textDocument: TextDocumentIdentifier; edits: Array<{ range: Range; newText: string; }>; }>; } export interface CodeAction { title: string; kind?: string; diagnostics?: Diagnostic[]; isPreferred?: boolean; edit?: WorkspaceEdit; command?: { title: string; command: string; arguments?: unknown[]; }; } /** * LSP Client class */ export declare class LspClient { private static readonly MAX_BUFFER_SIZE; private process; private requestId; private pendingRequests; private buffer; private openDocuments; private diagnostics; private diagnosticWaiters; private workspaceRoot; private serverConfig; private devContainerContext; private initialized; constructor(workspaceRoot: string, serverConfig: LspServerConfig, devContainerContext?: DevContainerContext | null); /** * Start the LSP server and initialize the connection */ connect(): Promise; /** * Synchronously kill the LSP server process. * Used in process exit handlers where async operations are not possible. */ forceKill(): void; /** * Disconnect from the LSP server */ disconnect(): Promise; /** * Reject all pending requests with the given error. * Called on process exit to avoid dangling unresolved promises. */ private rejectPendingRequests; /** * Handle incoming data from the server */ private handleData; /** * Handle a parsed JSON-RPC message */ private handleMessage; /** * Handle server notifications */ private handleNotification; /** * Send a request to the server */ private request; /** * Send a notification to the server (no response expected) */ private notify; /** * Initialize the LSP connection */ private initialize; /** * Open a document for editing */ openDocument(filePath: string): Promise; /** * Close a document */ closeDocument(filePath: string): void; /** * Get the language ID for a file */ private getLanguageId; /** * Convert file path to URI and ensure document is open */ private prepareDocument; /** * Get hover information at a position */ hover(filePath: string, line: number, character: number): Promise; /** * Go to definition */ definition(filePath: string, line: number, character: number): Promise; /** * Find all references */ references(filePath: string, line: number, character: number, includeDeclaration?: boolean): Promise; /** * Get document symbols */ documentSymbols(filePath: string): Promise; /** * Search workspace symbols */ workspaceSymbols(query: string): Promise; /** * Get diagnostics for a file */ getDiagnostics(filePath: string): Diagnostic[]; /** * Wait for the server to publish diagnostics for a file. * Resolves as soon as textDocument/publishDiagnostics fires for the URI, * or after `timeoutMs` milliseconds (whichever comes first). * This replaces fixed-delay sleeps with a notification-driven approach. */ waitForDiagnostics(filePath: string, timeoutMs?: number): Promise; /** * Prepare rename (check if rename is valid) */ prepareRename(filePath: string, line: number, character: number): Promise; /** * Rename a symbol */ rename(filePath: string, line: number, character: number, newName: string): Promise; /** * Get code actions */ codeActions(filePath: string, range: Range, diagnostics?: Diagnostic[]): Promise; private getServerWorkspaceRoot; private getWorkspaceRootUri; private toServerUri; private toHostUri; private translateIncomingPayload; private translateIncomingValue; } /** Idle timeout: disconnect LSP clients unused for 5 minutes */ export declare const IDLE_TIMEOUT_MS: number; /** Check for idle clients every 60 seconds */ export declare const IDLE_CHECK_INTERVAL_MS: number; /** * Client manager - maintains a pool of LSP clients per workspace/server * with idle eviction to free resources and in-flight request protection. */ export declare class LspClientManager { private clients; private lastUsed; private inFlightCount; private idleDeadlines; private idleTimer; constructor(); /** * Register process exit/signal handlers to kill all spawned LSP server processes. * Prevents orphaned language server processes (e.g. kotlin-language-server) * when the MCP bridge process exits or a claude session ends. */ private registerCleanupHandlers; /** * Get or create a client for a file */ getClientForFile(filePath: string): Promise; /** * Run a function with in-flight tracking for the client serving filePath. * While the function is running, the client is protected from idle eviction. * The lastUsed timestamp is refreshed on both entry and exit. */ runWithClientLease(filePath: string, fn: (client: LspClient) => Promise): Promise; private touchClient; private scheduleIdleDeadline; private clearIdleDeadline; /** * Find the workspace root for a file */ private findWorkspaceRoot; /** * Start periodic idle check */ private startIdleCheck; /** * Evict clients that haven't been used within IDLE_TIMEOUT_MS. * Clients with in-flight requests are never evicted. */ private evictIdleClients; private evictClientIfIdle; /** * Disconnect all clients and stop idle checking. * Uses Promise.allSettled so one failing disconnect doesn't block others. * Maps are always cleared regardless of individual disconnect failures. */ disconnectAll(): Promise; /** Expose in-flight count for testing */ getInFlightCount(key: string): number; /** Expose client count for testing */ get clientCount(): number; /** Trigger idle eviction manually (exposed for testing) */ triggerEviction(): void; } export declare const lspClientManager: LspClientManager; /** * Disconnect all LSP clients and free resources. * Exported for use in session-end hooks. */ export declare function disconnectAll(): Promise; //# sourceMappingURL=client.d.ts.map