import type { LspServerSpec, ResolvedCommand } from "./servers.js";
/** LSP diagnostic shape (the subset we render). */
export interface LspDiagnostic {
    range: {
        start: {
            line: number;
            character: number;
        };
        end: {
            line: number;
            character: number;
        };
    };
    severity?: number;
    message: string;
    source?: string;
    code?: string | number;
}
/**
 * Canonical cache key for a `file://` URI.
 *
 * Diagnostics live in a Map keyed by URI: we `set` what the server sends and
 * `get` what we built from the edited path, so the two must be the SAME STRING.
 * There is no single canonical spelling of a file URI, and on Windows we and
 * tsserver disagreed in two independent ways at once. Measured on CI:
 *
 *   we sent:      file:///c:/Users/RUNNER%7E1/…/main.ts
 *   server sent:  file:///c%3A/Users/RUNNER~1/…/main.ts
 *
 * `~` is an RFC 3986 unreserved character that `pathToFileURL` percent-encodes
 * and tsserver leaves literal; the drive colon is the reverse. Every lookup
 * missed, so the diagnostics arrived (the wire trace shows `diagnostics=1`)
 * and were then dropped on the floor — reported to the user as a clean file,
 * because LSP degrades silently by design. 8.3 short names like `RUNNER~1` are
 * not exotic: that IS the Windows temp path on GitHub runners, and `PROGRA~1`
 * and friends show up in real user paths.
 *
 * Fix: decode percent-escapes and lower-case the drive letter, so both
 * spellings collapse to one key. Used ONLY as a Map key — the URI actually put
 * on the wire is still the properly encoded one from `pathToFileURL`.
 *
 * Windows paths are also case-insensitive, but case is deliberately NOT folded:
 * both sides derive from the same path string, and folding would break the
 * genuinely case-sensitive POSIX servers that share this code.
 */
export declare function normalizeUri(uri: string): string;
/**
 * One language-server process bound to one project root. Owns document sync
 * (didOpen / didChange / didSave with per-uri version counters), the
 * push-diagnostics cache, and LSP 3.17 pull diagnostics with the
 * push-vs-pull race that the POC proved necessary for rust-analyzer.
 */
export declare class LspClient {
    private readonly spec;
    private readonly rootPath;
    private readonly proc;
    private readonly conn;
    private readonly versions;
    private readonly published;
    private waiters;
    private hasPullDiagnostics;
    private readonly activeProgressTokens;
    private alive;
    private readonly initializationOptions;
    private stderrBuffer;
    constructor(spec: LspServerSpec, rootPath: string, command: ResolvedCommand);
    get isAlive(): boolean;
    /** True while the server reports indexing/analysis through LSP work progress. */
    get hasActiveProgress(): boolean;
    /**
     * Drain the server's stderr into a bounded ring, and mirror it to the debug
     * log so a misbehaving server is diagnosable from `~/.gg/*.log` alone.
     * Bounded because a looping server can emit stderr without limit.
     */
    private captureStderr;
    /** Most recent server stderr, for failure diagnostics. */
    stderrTail(): string;
    initialize(timeoutMs: number): Promise<void>;
    /**
     * Sync `content` into the server's overlay for `filePath` — didOpen the
     * first time, didChange (full text) + didSave afterwards. Clears the
     * push-diagnostics cache for the uri so a subsequent collect waits for a
     * report computed against THIS content rather than a stale one.
     */
    syncDocument(filePath: string, content: string): string;
    /**
     * Current diagnostics for `uri`, racing the push channel (next
     * publishDiagnostics after the last sync) against a pull-diagnostics poll
     * loop when the server supports LSP 3.17 pull. Returns null on timeout.
     */
    collectDiagnostics(uri: string, timeoutMs: number): Promise<LspDiagnostic[] | null>;
    /**
     * Graceful shutdown/exit handshake with SIGKILL fallback. Synchronous so it
     * is safe inside `process.on("exit")` handlers: the shutdown request and
     * exit notification are written immediately; the SIGKILL timer covers
     * servers that ignore them (and stdin EOF reaps them when we die first).
     */
    shutdown(): void;
    /**
     * Force-kill the server and everything it spawned, immediately.
     *
     * For a server that never completed the handshake, the polite
     * `shutdown`/`exit` sequence is pointless — it has already proven it isn't
     * answering — so skip straight to the kill.
     *
     * Uses `killProcessTree`, not `proc.kill()`: language servers spawn children
     * (typescript-language-server runs tsserver), Windows has no process groups,
     * and killing only the parent leaves those children alive holding file
     * handles in the project directory.
     */
    terminate(): void;
    private markDead;
    private waitForPublish;
    private pullDiagnostics;
}
//# sourceMappingURL=client.d.ts.map