apex-code 0.0.1-alpha.4 → 0.0.1-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +78 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +10 -1
- package/dist/cli.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +2 -2
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +20 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +91 -1
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +17 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +28 -4
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/lsp/client.d.ts +76 -0
- package/dist/core/lsp/client.d.ts.map +1 -0
- package/dist/core/lsp/client.js +469 -0
- package/dist/core/lsp/client.js.map +1 -0
- package/dist/core/lsp/diagnostics.d.ts +47 -0
- package/dist/core/lsp/diagnostics.d.ts.map +1 -0
- package/dist/core/lsp/diagnostics.js +224 -0
- package/dist/core/lsp/diagnostics.js.map +1 -0
- package/dist/core/lsp/pool.d.ts +48 -0
- package/dist/core/lsp/pool.d.ts.map +1 -0
- package/dist/core/lsp/pool.js +150 -0
- package/dist/core/lsp/pool.js.map +1 -0
- package/dist/core/lsp/registry.d.ts +49 -0
- package/dist/core/lsp/registry.d.ts.map +1 -0
- package/dist/core/lsp/registry.js +215 -0
- package/dist/core/lsp/registry.js.map +1 -0
- package/dist/core/lsp/status.d.ts +23 -0
- package/dist/core/lsp/status.d.ts.map +1 -0
- package/dist/core/lsp/status.js +35 -0
- package/dist/core/lsp/status.js.map +1 -0
- package/dist/core/lsp/sync.d.ts +13 -0
- package/dist/core/lsp/sync.d.ts.map +1 -0
- package/dist/core/lsp/sync.js +71 -0
- package/dist/core/lsp/sync.js.map +1 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +15 -0
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/project-trust.d.ts.map +1 -1
- package/dist/core/project-trust.js +1 -1
- package/dist/core/project-trust.js.map +1 -1
- package/dist/core/sandbox/cli-launch.d.ts +43 -0
- package/dist/core/sandbox/cli-launch.d.ts.map +1 -1
- package/dist/core/sandbox/cli-launch.js +70 -3
- package/dist/core/sandbox/cli-launch.js.map +1 -1
- package/dist/core/sandbox/cli-supervisor.d.ts +2 -0
- package/dist/core/sandbox/cli-supervisor.d.ts.map +1 -1
- package/dist/core/sandbox/cli-supervisor.js +1 -0
- package/dist/core/sandbox/cli-supervisor.js.map +1 -1
- package/dist/core/sdk.d.ts +4 -0
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +10 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/settings-manager.d.ts +3 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +3 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts +19 -7
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +77 -19
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +1 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +3 -3
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/contract.d.ts +29 -2
- package/dist/core/tools/contract.d.ts.map +1 -1
- package/dist/core/tools/contract.js.map +1 -1
- package/dist/core/tools/diagnostics.d.ts +39 -0
- package/dist/core/tools/diagnostics.d.ts.map +1 -0
- package/dist/core/tools/diagnostics.js +62 -0
- package/dist/core/tools/diagnostics.js.map +1 -0
- package/dist/core/tools/edit.d.ts +10 -0
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +17 -8
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/index.d.ts +20 -3
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +18 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/lsp.d.ts +62 -0
- package/dist/core/tools/lsp.d.ts.map +1 -0
- package/dist/core/tools/lsp.js +216 -0
- package/dist/core/tools/lsp.js.map +1 -0
- package/dist/core/tools/skill-search.d.ts +32 -0
- package/dist/core/tools/skill-search.d.ts.map +1 -0
- package/dist/core/tools/skill-search.js +56 -0
- package/dist/core/tools/skill-search.js.map +1 -0
- package/dist/core/tools/write.d.ts +10 -0
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +16 -3
- package/dist/core/tools/write.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts +49 -1
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +235 -1
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.js +2 -1
- package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +1 -0
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +19 -6
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +172 -89
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/splash-header.d.ts +41 -0
- package/dist/modes/interactive/components/splash-header.d.ts.map +1 -0
- package/dist/modes/interactive/components/splash-header.js +145 -0
- package/dist/modes/interactive/components/splash-header.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +19 -15
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/apex.json +94 -0
- package/dist/modes/interactive/theme/light.json +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +6 -1
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +2 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/themes/apex-logo.d.ts +21 -0
- package/dist/themes/apex-logo.d.ts.map +1 -0
- package/dist/themes/apex-logo.js +31 -0
- package/dist/themes/apex-logo.js.map +1 -0
- package/dist/utils/changelog.d.ts +7 -2
- package/dist/utils/changelog.d.ts.map +1 -1
- package/dist/utils/changelog.js +82 -13
- package/dist/utils/changelog.js.map +1 -1
- package/docs/skills.md +54 -4
- package/npm-shrinkwrap.json +5 -5
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/core/lsp/client.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ChildProcess } from \"node:child_process\";\nimport type { Readable, Writable } from \"node:stream\";\nimport { pathToFileURL } from \"node:url\";\nimport { spawnProcess } from \"../../utils/child-process.ts\";\n\nexport const LSP_MAX_HEADER_BYTES = 8 * 1024;\nexport const LSP_MAX_MESSAGE_BYTES = 8 * 1024 * 1024;\nexport const LSP_MAX_STDERR_TAIL_BYTES = 64 * 1024;\n\nconst DEFAULT_INITIALIZATION_TIMEOUT_MS = 10_000;\nconst DEFAULT_REQUEST_TIMEOUT_MS = 10_000;\nconst SHUTDOWN_TIMEOUT_MS = 2_000;\nconst TERMINATE_GRACE_MS = 500;\nconst KILL_GRACE_MS = 1_000;\n\nexport interface LspClientOptions {\n\treadonly command: string;\n\treadonly args?: readonly string[];\n\treadonly cwd: string;\n\treadonly env?: NodeJS.ProcessEnv;\n\treadonly initializationTimeoutMs?: number;\n\treadonly requestTimeoutMs?: number;\n}\n\nexport interface LspNotification {\n\treadonly method: string;\n\treadonly params?: unknown;\n}\n\nexport type LspTextDocumentSyncKind = \"none\" | \"full\" | \"incremental\";\n\nexport interface LspTextDocumentSync {\n\treadonly kind: LspTextDocumentSyncKind;\n\treadonly openClose: boolean;\n\treadonly save: boolean;\n\treadonly saveIncludeText: boolean;\n}\n\nexport class LspJsonRpcError extends Error {\n\treadonly code: number;\n\treadonly data?: unknown;\n\n\tconstructor(error: { code: number; message: string; data?: unknown }) {\n\t\tsuper(error.message);\n\t\tthis.name = \"LspJsonRpcError\";\n\t\tthis.code = error.code;\n\t\tthis.data = error.data;\n\t}\n}\n\ntype JsonRpcId = number;\ntype PendingRequest = {\n\tresolve(value: unknown): void;\n\treject(reason: Error): void;\n};\n\ntype ClientState = \"idle\" | \"starting\" | \"ready\" | \"closing\" | \"closed\" | \"failed\";\n\ntype JsonRpcMessage = Record<string, unknown>;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isJsonRpcId(value: unknown): value is string | number {\n\treturn typeof value === \"string\" || (typeof value === \"number\" && Number.isSafeInteger(value));\n}\n\nfunction normalizeError(error: unknown): Error {\n\treturn error instanceof Error ? error : new Error(String(error));\n}\n\nfunction parseTextDocumentSync(value: unknown): LspTextDocumentSync {\n\tlet kind: LspTextDocumentSyncKind = \"none\";\n\tlet openClose = false;\n\tlet save = false;\n\tlet saveIncludeText = false;\n\tif (typeof value === \"number\") {\n\t\tkind = value === 1 ? \"full\" : value === 2 ? \"incremental\" : \"none\";\n\t} else if (isRecord(value)) {\n\t\topenClose = value.openClose === true;\n\t\tkind = value.change === 1 ? \"full\" : value.change === 2 ? \"incremental\" : \"none\";\n\t\tif (value.save === true) save = true;\n\t\telse if (isRecord(value.save)) {\n\t\t\tsave = true;\n\t\t\tsaveIncludeText = value.save.includeText === true;\n\t\t}\n\t}\n\treturn { kind, openClose, save, saveIncludeText };\n}\n\nfunction validateTimeout(value: number | undefined, fallback: number, label: string): number {\n\tconst timeout = value ?? fallback;\n\tif (!Number.isSafeInteger(timeout) || timeout < 1) throw new Error(`${label} must be a positive safe integer.`);\n\treturn timeout;\n}\n\nfunction delay(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** A bounded JSON-RPC 2.0 client for one language server's stdio. */\nexport class LspClient {\n\tprivate readonly options: LspClientOptions;\n\tprivate child: ChildProcess | undefined;\n\tprivate stdin: Writable | undefined;\n\tprivate stdout: Readable | undefined;\n\tprivate input = Buffer.alloc(0);\n\tprivate stderrTail = Buffer.alloc(0);\n\tprivate nextRequestId = 1;\n\tprivate readonly pending = new Map<JsonRpcId, PendingRequest>();\n\tprivate readonly notificationListeners = new Set<(notification: LspNotification) => void>();\n\tprivate readonly failureListeners = new Set<(error: Error) => void>();\n\tprivate startPromise: Promise<void> | undefined;\n\tprivate disposePromise: Promise<void> | undefined;\n\tprivate state: ClientState = \"idle\";\n\tprivate failure: Error | undefined;\n\tprivate writeChain: Promise<void> = Promise.resolve();\n\tprivate syncCapabilities: LspTextDocumentSync = {\n\t\tkind: \"none\",\n\t\topenClose: false,\n\t\tsave: false,\n\t\tsaveIncludeText: false,\n\t};\n\n\tconstructor(options: LspClientOptions) {\n\t\tthis.options = options;\n\t}\n\n\tget capturedStderr(): string {\n\t\treturn this.stderrTail.toString(\"utf8\");\n\t}\n\n\t/** Text-document synchronization negotiated during initialization. */\n\tget textDocumentSync(): LspTextDocumentSync {\n\t\treturn this.syncCapabilities;\n\t}\n\n\tonNotification(listener: (notification: LspNotification) => void): () => void {\n\t\tthis.notificationListeners.add(listener);\n\t\treturn () => this.notificationListeners.delete(listener);\n\t}\n\n\t/** Subscribe to the first terminal protocol or process failure. */\n\tonFailure(listener: (error: Error) => void): () => void {\n\t\tthis.failureListeners.add(listener);\n\t\tif (this.failure) listener(this.failure);\n\t\treturn () => this.failureListeners.delete(listener);\n\t}\n\n\tstart(): Promise<void> {\n\t\tif (this.failure) return Promise.reject(this.failure);\n\t\tif (this.state === \"closing\" || this.state === \"closed\") {\n\t\t\treturn Promise.reject(new Error(\"Language server client is disposed.\"));\n\t\t}\n\t\tif (!this.startPromise) {\n\t\t\tthis.state = \"starting\";\n\t\t\tthis.startPromise = this.initialize().catch(async (error) => {\n\t\t\t\tconst failure = normalizeError(error);\n\t\t\t\tthis.fail(failure);\n\t\t\t\tawait this.terminateChild();\n\t\t\t\tthrow failure;\n\t\t\t});\n\t\t}\n\t\treturn this.startPromise;\n\t}\n\n\tasync request(method: string, params?: unknown, signal?: AbortSignal): Promise<unknown> {\n\t\tawait this.start();\n\t\tif (this.state !== \"ready\") throw this.failure ?? new Error(\"Language server did not become ready.\");\n\t\treturn this.sendRequest(method, params, signal, this.requestTimeoutMs());\n\t}\n\n\tnotify(method: string, params?: unknown): void {\n\t\tif (this.failure) throw this.failure;\n\t\tif (this.state !== \"ready\") throw new Error(\"Language server is not ready.\");\n\t\tvoid this.enqueueWrite({ jsonrpc: \"2.0\", method, ...(params === undefined ? {} : { params }) }).catch((error) => {\n\t\t\tthis.fail(normalizeError(error));\n\t\t});\n\t}\n\n\tdispose(): Promise<void> {\n\t\tif (!this.disposePromise) this.disposePromise = this.disposeProcess();\n\t\treturn this.disposePromise;\n\t}\n\n\tprivate initializationTimeoutMs(): number {\n\t\treturn validateTimeout(\n\t\t\tthis.options.initializationTimeoutMs,\n\t\t\tDEFAULT_INITIALIZATION_TIMEOUT_MS,\n\t\t\t\"initializationTimeoutMs\",\n\t\t);\n\t}\n\n\tprivate requestTimeoutMs(): number {\n\t\treturn validateTimeout(this.options.requestTimeoutMs, DEFAULT_REQUEST_TIMEOUT_MS, \"requestTimeoutMs\");\n\t}\n\n\tprivate async initialize(): Promise<void> {\n\t\tconst child = spawnProcess(this.options.command, [...(this.options.args ?? [])], {\n\t\t\tcwd: this.options.cwd,\n\t\t\tenv: this.options.env,\n\t\t\tstdio: [\"pipe\", \"pipe\", \"pipe\"],\n\t\t});\n\t\tif (!child.stdin || !child.stdout) {\n\t\t\tchild.kill();\n\t\t\tthrow new Error(\"Language server must expose piped stdin and stdout.\");\n\t\t}\n\t\tthis.child = child;\n\t\tthis.stdin = child.stdin;\n\t\tthis.stdout = child.stdout;\n\t\tthis.stdout.on(\"data\", (chunk: Buffer | string) => this.onData(chunk));\n\t\tchild.stderr?.on(\"data\", (chunk: Buffer | string) => this.captureStderr(chunk));\n\t\tchild.stdin.on(\"error\", (error) => this.fail(error));\n\t\tchild.once(\"error\", (error) => this.fail(error));\n\t\tchild.once(\"exit\", (code, signal) => {\n\t\t\tif (this.state !== \"closing\" && this.state !== \"closed\") {\n\t\t\t\tthis.fail(new Error(`Language server exited before shutdown (code=${code} signal=${signal}).`));\n\t\t\t}\n\t\t});\n\n\t\tconst initializeResult = await this.sendRequest(\n\t\t\t\"initialize\",\n\t\t\t{\n\t\t\t\tprocessId: process.pid,\n\t\t\t\tclientInfo: { name: \"Apex Code\" },\n\t\t\t\trootUri: pathToFileURL(this.options.cwd).href,\n\t\t\t\tcapabilities: {\n\t\t\t\t\tgeneral: { positionEncodings: [\"utf-16\"] },\n\t\t\t\t\tworkspace: { workspaceFolders: false },\n\t\t\t\t\ttextDocument: {\n\t\t\t\t\t\tdefinition: {},\n\t\t\t\t\t\treferences: {},\n\t\t\t\t\t\tdocumentSymbol: {},\n\t\t\t\t\t\tpublishDiagnostics: { versionSupport: true },\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tworkspaceFolders: null,\n\t\t\t},\n\t\t\tundefined,\n\t\t\tthis.initializationTimeoutMs(),\n\t\t\t\"Language server initialize timed out\",\n\t\t);\n\t\tif (!isRecord(initializeResult) || !isRecord(initializeResult.capabilities)) {\n\t\t\tthrow new Error(\"Language server returned an invalid initialize result.\");\n\t\t}\n\t\tthis.syncCapabilities = parseTextDocumentSync(initializeResult.capabilities.textDocumentSync);\n\t\tif (this.state !== \"starting\") throw new Error(\"Language server initialization was interrupted.\");\n\t\tawait this.enqueueWrite({ jsonrpc: \"2.0\", method: \"initialized\", params: {} });\n\t\tthis.state = \"ready\";\n\t}\n\n\tprivate async disposeProcess(): Promise<void> {\n\t\tif (this.state === \"closed\") return;\n\t\tconst wasReady = this.state === \"ready\";\n\t\tthis.state = \"closing\";\n\t\tthis.rejectPending(new Error(\"Language server client is closing.\"));\n\n\t\tif (wasReady) {\n\t\t\ttry {\n\t\t\t\tawait this.sendRequest(\n\t\t\t\t\t\"shutdown\",\n\t\t\t\t\tundefined,\n\t\t\t\t\tundefined,\n\t\t\t\t\tSHUTDOWN_TIMEOUT_MS,\n\t\t\t\t\t\"Language server shutdown timed out\",\n\t\t\t\t\ttrue,\n\t\t\t\t);\n\t\t\t\tawait this.enqueueWrite({ jsonrpc: \"2.0\", method: \"exit\" });\n\t\t\t} catch {\n\t\t\t\t// Bounded termination below is the fallback for an uncooperative server.\n\t\t\t}\n\t\t}\n\t\tawait this.terminateChild();\n\t\tthis.stdin?.end();\n\t\tthis.state = \"closed\";\n\t}\n\n\tprivate async terminateChild(): Promise<void> {\n\t\tconst child = this.child;\n\t\tif (!child || child.exitCode !== null || child.signalCode !== null) return;\n\t\tconst exited = new Promise<void>((resolve) => child.once(\"exit\", () => resolve()));\n\t\tconst graceful = await Promise.race([exited.then(() => true), delay(TERMINATE_GRACE_MS).then(() => false)]);\n\t\tif (graceful) return;\n\t\tchild.kill(\"SIGTERM\");\n\t\tconst terminated = await Promise.race([exited.then(() => true), delay(KILL_GRACE_MS).then(() => false)]);\n\t\tif (terminated) return;\n\t\tchild.kill(\"SIGKILL\");\n\t\tawait exited;\n\t}\n\n\tprivate sendRequest(\n\t\tmethod: string,\n\t\tparams?: unknown,\n\t\tsignal?: AbortSignal,\n\t\ttimeoutMs?: number,\n\t\ttimeoutMessage = `Language server request ${method} timed out`,\n\t\tallowClosing = false,\n\t): Promise<unknown> {\n\t\tif (this.failure && !allowClosing) return Promise.reject(this.failure);\n\t\tif ((!allowClosing && (this.state === \"closing\" || this.state === \"closed\")) || this.state === \"failed\") {\n\t\t\treturn Promise.reject(this.failure ?? new Error(\"Language server client is disposed.\"));\n\t\t}\n\t\tconst id = this.nextRequestId++;\n\t\tif (!Number.isSafeInteger(id)) return Promise.reject(new Error(\"Language server request id space exhausted.\"));\n\t\tif (signal?.aborted)\n\t\t\treturn Promise.reject(normalizeError(signal.reason ?? new Error(\"Language server request aborted.\")));\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tlet timer: NodeJS.Timeout | undefined;\n\t\t\tconst settle = (fn: () => void) => {\n\t\t\t\tif (timer) clearTimeout(timer);\n\t\t\t\tsignal?.removeEventListener(\"abort\", abort);\n\t\t\t\tfn();\n\t\t\t};\n\t\t\tconst abort = () => {\n\t\t\t\tif (!this.pending.delete(id)) return;\n\t\t\t\tvoid this.enqueueWrite({ jsonrpc: \"2.0\", method: \"$/cancelRequest\", params: { id } }).catch(() => {});\n\t\t\t\tsettle(() => reject(normalizeError(signal?.reason ?? new Error(\"Language server request aborted.\"))));\n\t\t\t};\n\t\t\tthis.pending.set(id, {\n\t\t\t\tresolve: (value) => settle(() => resolve(value)),\n\t\t\t\treject: (error) => settle(() => reject(error)),\n\t\t\t});\n\t\t\tsignal?.addEventListener(\"abort\", abort, { once: true });\n\t\t\tif (timeoutMs !== undefined) {\n\t\t\t\ttimer = setTimeout(() => {\n\t\t\t\t\tif (!this.pending.delete(id)) return;\n\t\t\t\t\tvoid this.enqueueWrite({ jsonrpc: \"2.0\", method: \"$/cancelRequest\", params: { id } }).catch(() => {});\n\t\t\t\t\tsettle(() => reject(new Error(`${timeoutMessage} after ${timeoutMs}ms.`)));\n\t\t\t\t}, timeoutMs);\n\t\t\t}\n\t\t\tvoid this.enqueueWrite({ jsonrpc: \"2.0\", id, method, ...(params === undefined ? {} : { params }) }).catch(\n\t\t\t\t(error) => {\n\t\t\t\t\tconst pending = this.pending.get(id);\n\t\t\t\t\tif (!pending) return;\n\t\t\t\t\tthis.pending.delete(id);\n\t\t\t\t\tpending.reject(normalizeError(error));\n\t\t\t\t},\n\t\t\t);\n\t\t});\n\t}\n\n\tprivate enqueueWrite(message: object): Promise<void> {\n\t\tconst body = Buffer.from(JSON.stringify(message), \"utf8\");\n\t\tif (body.length > LSP_MAX_MESSAGE_BYTES)\n\t\t\treturn Promise.reject(new Error(\"Language server message exceeds 8 MiB.\"));\n\t\tconst frame = Buffer.concat([Buffer.from(`Content-Length: ${body.length}\\r\\n\\r\\n`, \"ascii\"), body]);\n\t\tconst writing = this.writeChain.then(() => this.writeFrame(frame));\n\t\tthis.writeChain = writing.catch(() => {});\n\t\treturn writing;\n\t}\n\n\tprivate writeFrame(frame: Buffer): Promise<void> {\n\t\tconst stream = this.stdin;\n\t\tif (!stream || stream.destroyed || !stream.writable) {\n\t\t\treturn Promise.reject(new Error(\"Language server stdin is not writable.\"));\n\t\t}\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tlet waitingForDrain = false;\n\t\t\tconst cleanup = () => {\n\t\t\t\tstream.removeListener(\"error\", onError);\n\t\t\t\tstream.removeListener(\"drain\", onDrain);\n\t\t\t};\n\t\t\tconst onError = (error: Error) => {\n\t\t\t\tcleanup();\n\t\t\t\treject(error);\n\t\t\t};\n\t\t\tconst onDrain = () => {\n\t\t\t\twaitingForDrain = false;\n\t\t\t\tcleanup();\n\t\t\t\tresolve();\n\t\t\t};\n\t\t\tstream.once(\"error\", onError);\n\t\t\tconst accepted = stream.write(frame, (error) => {\n\t\t\t\tif (error) onError(error);\n\t\t\t\telse if (!waitingForDrain) {\n\t\t\t\t\tcleanup();\n\t\t\t\t\tresolve();\n\t\t\t\t}\n\t\t\t});\n\t\t\tif (!accepted) {\n\t\t\t\twaitingForDrain = true;\n\t\t\t\tstream.once(\"drain\", onDrain);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate onData(chunk: Buffer | string): void {\n\t\tif (this.failure) return;\n\t\tthis.input = Buffer.concat([this.input, typeof chunk === \"string\" ? Buffer.from(chunk) : chunk]);\n\t\twhile (true) {\n\t\t\tconst headerEnd = this.input.indexOf(\"\\r\\n\\r\\n\");\n\t\t\tif (headerEnd < 0) {\n\t\t\t\tif (this.input.length > LSP_MAX_HEADER_BYTES) this.fail(new Error(\"Language server header exceeds 8 KiB.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (headerEnd > LSP_MAX_HEADER_BYTES) {\n\t\t\t\tthis.fail(new Error(\"Language server header exceeds 8 KiB.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst header = this.input.subarray(0, headerEnd).toString(\"ascii\");\n\t\t\tconst lengths = [...header.matchAll(/(?:^|\\r\\n)Content-Length:\\s*(\\d+)(?=\\r\\n|$)/gi)];\n\t\t\tif (lengths.length !== 1) {\n\t\t\t\tthis.fail(new Error(\"Language server frame must contain exactly one Content-Length header.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst contentLength = Number(lengths[0][1]);\n\t\t\tif (!Number.isSafeInteger(contentLength) || contentLength > LSP_MAX_MESSAGE_BYTES) {\n\t\t\t\tthis.fail(new Error(\"Language server message exceeds 8 MiB.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst bodyStart = headerEnd + 4;\n\t\t\tif (this.input.length < bodyStart + contentLength) return;\n\t\t\tconst body = this.input.subarray(bodyStart, bodyStart + contentLength).toString(\"utf8\");\n\t\t\tthis.input = this.input.subarray(bodyStart + contentLength);\n\t\t\ttry {\n\t\t\t\tconst parsed: unknown = JSON.parse(body);\n\t\t\t\tif (!isRecord(parsed)) throw new Error(\"Language server JSON-RPC message must be an object.\");\n\t\t\t\tthis.onMessage(parsed);\n\t\t\t} catch (error) {\n\t\t\t\tthis.fail(normalizeError(error));\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate onMessage(message: JsonRpcMessage): void {\n\t\tif (message.jsonrpc !== \"2.0\") {\n\t\t\tthis.fail(new Error('Language server message must declare jsonrpc \"2.0\".'));\n\t\t\treturn;\n\t\t}\n\t\tif (typeof message.method === \"string\") {\n\t\t\tif (message.id !== undefined && message.id !== null) {\n\t\t\t\tvoid this.handleServerRequest(message);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst notification: LspNotification = {\n\t\t\t\tmethod: message.method,\n\t\t\t\t...(message.params === undefined ? {} : { params: message.params }),\n\t\t\t};\n\t\t\tfor (const listener of this.notificationListeners) listener(notification);\n\t\t\treturn;\n\t\t}\n\t\tif (typeof message.id !== \"number\" || !Number.isSafeInteger(message.id)) return;\n\t\tconst pending = this.pending.get(message.id);\n\t\tif (!pending) return;\n\t\tconst hasResult = Object.hasOwn(message, \"result\");\n\t\tconst hasError = Object.hasOwn(message, \"error\");\n\t\tif (hasResult === hasError) {\n\t\t\tthis.fail(new Error(\"Language server response must contain exactly one of result or error.\"));\n\t\t\treturn;\n\t\t}\n\t\tthis.pending.delete(message.id);\n\t\tif (hasError) {\n\t\t\tconst error = message.error;\n\t\t\tif (!isRecord(error) || !Number.isSafeInteger(error.code) || typeof error.message !== \"string\") {\n\t\t\t\tthis.fail(new Error(\"Language server returned an invalid JSON-RPC error.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpending.reject(new LspJsonRpcError({ code: error.code as number, message: error.message, data: error.data }));\n\t\t\treturn;\n\t\t}\n\t\tpending.resolve(message.result);\n\t}\n\n\tprivate async handleServerRequest(message: JsonRpcMessage): Promise<void> {\n\t\tif (!isJsonRpcId(message.id) || typeof message.method !== \"string\") return;\n\t\tlet result: unknown;\n\t\tlet error: { code: number; message: string } | undefined;\n\t\tswitch (message.method) {\n\t\t\tcase \"workspace/configuration\": {\n\t\t\t\tconst params = message.params;\n\t\t\t\tif (!isRecord(params) || !Array.isArray(params.items)) error = { code: -32602, message: \"Invalid params\" };\n\t\t\t\telse result = params.items.map(() => null);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"workspace/workspaceFolders\":\n\t\t\tcase \"window/showMessageRequest\":\n\t\t\t\tresult = null;\n\t\t\t\tbreak;\n\t\t\tcase \"client/registerCapability\":\n\t\t\tcase \"client/unregisterCapability\":\n\t\t\tcase \"window/workDoneProgress/create\":\n\t\t\tcase \"workspace/semanticTokens/refresh\":\n\t\t\tcase \"workspace/inlayHint/refresh\":\n\t\t\tcase \"workspace/diagnostic/refresh\":\n\t\t\tcase \"workspace/codeLens/refresh\":\n\t\t\t\tresult = null;\n\t\t\t\tbreak;\n\t\t\tcase \"workspace/applyEdit\":\n\t\t\t\tresult = { applied: false, failureReason: \"Apex Code does not accept server-initiated workspace edits.\" };\n\t\t\t\tbreak;\n\t\t\tcase \"window/showDocument\":\n\t\t\t\tresult = { success: false };\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\terror = { code: -32601, message: \"Method not found\" };\n\t\t}\n\t\tawait this.enqueueWrite({\n\t\t\tjsonrpc: \"2.0\",\n\t\t\tid: message.id,\n\t\t\t...(error ? { error } : { result }),\n\t\t}).catch((writeError) => this.fail(normalizeError(writeError)));\n\t}\n\n\tprivate captureStderr(chunk: Buffer | string): void {\n\t\tconst bytes = typeof chunk === \"string\" ? Buffer.from(chunk) : chunk;\n\t\tthis.stderrTail = Buffer.concat([this.stderrTail, bytes]);\n\t\tif (this.stderrTail.length > LSP_MAX_STDERR_TAIL_BYTES) {\n\t\t\tthis.stderrTail = this.stderrTail.subarray(this.stderrTail.length - LSP_MAX_STDERR_TAIL_BYTES);\n\t\t}\n\t}\n\n\tprivate rejectPending(error: Error): void {\n\t\tfor (const request of this.pending.values()) request.reject(error);\n\t\tthis.pending.clear();\n\t}\n\n\tprivate fail(error: Error): void {\n\t\tif (this.state === \"closed\") return;\n\t\tconst firstFailure = this.failure === undefined;\n\t\tthis.failure ??= error;\n\t\tif (this.state !== \"closing\") this.state = \"failed\";\n\t\tthis.rejectPending(this.failure);\n\t\tif (firstFailure) for (const listener of this.failureListeners) listener(this.failure);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { DiagnosticUnavailableKind } from "../tools/contract.ts";
|
|
2
|
+
import type { LspPool } from "./pool.ts";
|
|
3
|
+
export interface DiagnosticPosition {
|
|
4
|
+
readonly line: number;
|
|
5
|
+
readonly character: number;
|
|
6
|
+
}
|
|
7
|
+
export interface DiagnosticRange {
|
|
8
|
+
readonly start: DiagnosticPosition;
|
|
9
|
+
readonly end: DiagnosticPosition;
|
|
10
|
+
}
|
|
11
|
+
export interface Diagnostic {
|
|
12
|
+
readonly range: DiagnosticRange;
|
|
13
|
+
readonly severity?: number;
|
|
14
|
+
readonly code?: string | number;
|
|
15
|
+
readonly source?: string;
|
|
16
|
+
readonly message: string;
|
|
17
|
+
}
|
|
18
|
+
export type DiagnosticsOutcome = {
|
|
19
|
+
readonly status: "ok";
|
|
20
|
+
readonly serverId: string;
|
|
21
|
+
readonly diagnostics: readonly Diagnostic[];
|
|
22
|
+
readonly truncated: boolean;
|
|
23
|
+
} | {
|
|
24
|
+
readonly status: "unavailable";
|
|
25
|
+
readonly serverId?: string;
|
|
26
|
+
readonly unavailableKind: DiagnosticUnavailableKind;
|
|
27
|
+
readonly reason: string;
|
|
28
|
+
};
|
|
29
|
+
/** Synchronizes disk snapshots and attributes diagnostics only to their exact document version. */
|
|
30
|
+
export declare class LspDiagnostics {
|
|
31
|
+
private readonly pool;
|
|
32
|
+
private readonly sync;
|
|
33
|
+
private readonly waiters;
|
|
34
|
+
private readonly unsubscribeByClient;
|
|
35
|
+
private disposed;
|
|
36
|
+
constructor(pool: LspPool);
|
|
37
|
+
afterMutation(path: string, signal?: AbortSignal): Promise<DiagnosticsOutcome>;
|
|
38
|
+
dispose(): Promise<void>;
|
|
39
|
+
private readonly versions;
|
|
40
|
+
private uri;
|
|
41
|
+
private peekNextVersion;
|
|
42
|
+
private subscribe;
|
|
43
|
+
private waitFor;
|
|
44
|
+
private onNotification;
|
|
45
|
+
private settle;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=diagnostics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../../src/core/lsp/diagnostics.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAEtE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzC,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,KAAK,EAAE,kBAAkB,CAAC;IACnC,QAAQ,CAAC,GAAG,EAAE,kBAAkB,CAAC;CACjC;AAED,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,kBAAkB,GAC3B;IACA,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,SAAS,UAAU,EAAE,CAAC;IAC5C,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC3B,GACD;IACA,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,eAAe,EAAE,yBAAyB,CAAC;IACpD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACvB,CAAC;AAqDL,mGAAmG;AACnG,qBAAa,cAAc;IAC1B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAU;IAC/B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAyB;IAC9C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAC1D,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAoC;IACxE,OAAO,CAAC,QAAQ,CAAS;IAEzB,YAAY,IAAI,EAAE,OAAO,EAExB;IAEK,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA8EnF;IAEK,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAe7B;IAED,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IAEtD,OAAO,CAAC,GAAG;IAIX,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,SAAS;IAQjB,OAAO,CAAC,OAAO;IAgDf,OAAO,CAAC,cAAc;IAyBtB,OAAO,CAAC,MAAM;CAQd","sourcesContent":["import { pathToFileURL } from \"node:url\";\nimport type { DiagnosticUnavailableKind } from \"../tools/contract.ts\";\nimport type { LspClient, LspNotification } from \"./client.ts\";\nimport type { LspPool } from \"./pool.ts\";\nimport { LspDocumentSync } from \"./sync.ts\";\n\nexport interface DiagnosticPosition {\n\treadonly line: number;\n\treadonly character: number;\n}\n\nexport interface DiagnosticRange {\n\treadonly start: DiagnosticPosition;\n\treadonly end: DiagnosticPosition;\n}\n\nexport interface Diagnostic {\n\treadonly range: DiagnosticRange;\n\treadonly severity?: number;\n\treadonly code?: string | number;\n\treadonly source?: string;\n\treadonly message: string;\n}\n\nexport type DiagnosticsOutcome =\n\t| {\n\t\t\treadonly status: \"ok\";\n\t\t\treadonly serverId: string;\n\t\t\treadonly diagnostics: readonly Diagnostic[];\n\t\t\treadonly truncated: boolean;\n\t }\n\t| {\n\t\t\treadonly status: \"unavailable\";\n\t\t\treadonly serverId?: string;\n\t\t\treadonly unavailableKind: DiagnosticUnavailableKind;\n\t\t\treadonly reason: string;\n\t };\n\ninterface PublishDiagnostics {\n\treadonly uri: string;\n\treadonly version?: number;\n\treadonly diagnostics: readonly Diagnostic[];\n}\n\ninterface Waiter {\n\treadonly version: number;\n\treadonly serverId: string;\n\treadonly resolve: (outcome: DiagnosticsOutcome) => void;\n\treadonly timer: NodeJS.Timeout;\n\treadonly removeAbort: () => void;\n}\n\nconst MAX_DIAGNOSTICS = 1_000;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isPosition(value: unknown): value is DiagnosticPosition {\n\treturn (\n\t\tisRecord(value) &&\n\t\tNumber.isSafeInteger(value.line) &&\n\t\t(value.line as number) >= 0 &&\n\t\tNumber.isSafeInteger(value.character) &&\n\t\t(value.character as number) >= 0\n\t);\n}\n\nfunction isDiagnostic(value: unknown): value is Diagnostic {\n\tif (!isRecord(value) || typeof value.message !== \"string\" || !isRecord(value.range)) return false;\n\tif (!isPosition(value.range.start) || !isPosition(value.range.end)) return false;\n\tif (value.severity !== undefined && !Number.isSafeInteger(value.severity)) return false;\n\tif (value.code !== undefined && typeof value.code !== \"string\" && typeof value.code !== \"number\") return false;\n\tif (value.source !== undefined && typeof value.source !== \"string\") return false;\n\treturn true;\n}\n\nfunction parsePublish(notification: LspNotification): PublishDiagnostics | undefined {\n\tif (notification.method !== \"textDocument/publishDiagnostics\" || !isRecord(notification.params)) return undefined;\n\tconst { uri, version, diagnostics } = notification.params;\n\tif (typeof uri !== \"string\" || !Array.isArray(diagnostics) || !diagnostics.every(isDiagnostic)) return undefined;\n\tif (version !== undefined && (!Number.isSafeInteger(version) || (version as number) < 0)) return undefined;\n\treturn { uri, ...(version === undefined ? {} : { version: version as number }), diagnostics };\n}\n\nfunction unavailableReason(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n\n/** Synchronizes disk snapshots and attributes diagnostics only to their exact document version. */\nexport class LspDiagnostics {\n\tprivate readonly pool: LspPool;\n\tprivate readonly sync = new LspDocumentSync();\n\tprivate readonly waiters = new Map<string, Set<Waiter>>();\n\tprivate readonly unsubscribeByClient = new Map<LspClient, () => void>();\n\tprivate disposed = false;\n\n\tconstructor(pool: LspPool) {\n\t\tthis.pool = pool;\n\t}\n\n\tasync afterMutation(path: string, signal?: AbortSignal): Promise<DiagnosticsOutcome> {\n\t\tif (this.disposed)\n\t\t\treturn { status: \"unavailable\", unavailableKind: \"disposed\", reason: \"diagnostics are disposed\" };\n\t\tlet selectedServerId: string | undefined;\n\t\ttry {\n\t\t\tconst connection = await this.pool.acquire(path);\n\t\t\tif (this.disposed) {\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"unavailable\",\n\t\t\t\t\t...(connection === undefined ? {} : { serverId: connection.selection.server.id }),\n\t\t\t\t\tunavailableKind: \"disposed\",\n\t\t\t\t\treason: \"diagnostics are disposed\",\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (!connection)\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"unavailable\",\n\t\t\t\t\tunavailableKind: \"no-server\",\n\t\t\t\t\treason: \"no language server configured for path\",\n\t\t\t\t};\n\t\t\tconst serverId = connection.selection.server.id;\n\t\t\tselectedServerId = serverId;\n\t\t\t// `connection.selection.path` is the registry's canonicalized form of `path`\n\t\t\t// (symlinks resolved -- see `registry.ts`'s `selectLspServerForPath`). It must\n\t\t\t// be the one URI-keying basis used everywhere below: `sync.synchronize()` opens\n\t\t\t// the document under this canonical path, so a waiter keyed by the caller's raw\n\t\t\t// `path` would never match the publish it's waiting for wherever the two forms\n\t\t\t// differ (e.g. macOS's /tmp -> /private/tmp), timing out instead of resolving.\n\t\t\tconst canonicalPath = connection.selection.path;\n\t\t\tthis.subscribe(connection.client);\n\t\t\tconst nextVersion = this.peekNextVersion(canonicalPath);\n\t\t\tconst pending = this.waitFor(\n\t\t\t\tthis.uri(canonicalPath),\n\t\t\t\tnextVersion,\n\t\t\t\tserverId,\n\t\t\t\tconnection.selection.server.diagnosticsTimeoutMs,\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tconst document = this.sync.synchronize(\n\t\t\t\tconnection.client,\n\t\t\t\tconnection.selection.path,\n\t\t\t\tconnection.selection.languageId,\n\t\t\t);\n\t\t\tif (!document) {\n\t\t\t\tpending.cancel();\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"unavailable\",\n\t\t\t\t\tserverId,\n\t\t\t\t\tunavailableKind: \"unsupported-sync\",\n\t\t\t\t\treason: \"language server does not support text document synchronization\",\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (document.version !== nextVersion) {\n\t\t\t\tpending.cancel();\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"unavailable\",\n\t\t\t\t\tserverId,\n\t\t\t\t\tunavailableKind: \"server-failed\",\n\t\t\t\t\treason: \"document synchronization version changed unexpectedly\",\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn await pending.outcome;\n\t\t} catch (error) {\n\t\t\tif (this.disposed) {\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"unavailable\",\n\t\t\t\t\t...(selectedServerId === undefined ? {} : { serverId: selectedServerId }),\n\t\t\t\t\tunavailableKind: \"disposed\",\n\t\t\t\t\treason: \"diagnostics are disposed\",\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tstatus: \"unavailable\",\n\t\t\t\t...(selectedServerId === undefined ? {} : { serverId: selectedServerId }),\n\t\t\t\tunavailableKind: \"server-failed\",\n\t\t\t\treason: unavailableReason(error),\n\t\t\t};\n\t\t}\n\t}\n\n\tasync dispose(): Promise<void> {\n\t\tif (this.disposed) return;\n\t\tthis.disposed = true;\n\t\tthis.sync.dispose();\n\t\tfor (const unsubscribe of this.unsubscribeByClient.values()) unsubscribe();\n\t\tthis.unsubscribeByClient.clear();\n\t\tfor (const [uri, waiters] of this.waiters) {\n\t\t\tfor (const waiter of [...waiters])\n\t\t\t\tthis.settle(uri, waiter, {\n\t\t\t\t\tstatus: \"unavailable\",\n\t\t\t\t\tserverId: waiter.serverId,\n\t\t\t\t\tunavailableKind: \"disposed\",\n\t\t\t\t\treason: \"diagnostics are disposed\",\n\t\t\t\t});\n\t\t}\n\t}\n\n\tprivate readonly versions = new Map<string, number>();\n\n\tprivate uri(path: string): string {\n\t\treturn pathToFileURL(path).href;\n\t}\n\n\tprivate peekNextVersion(path: string): number {\n\t\tconst uri = this.uri(path);\n\t\tconst version = (this.versions.get(uri) ?? 0) + 1;\n\t\tthis.versions.set(uri, version);\n\t\treturn version;\n\t}\n\n\tprivate subscribe(client: LspClient): void {\n\t\tif (this.unsubscribeByClient.has(client)) return;\n\t\tthis.unsubscribeByClient.set(\n\t\t\tclient,\n\t\t\tclient.onNotification((notification) => this.onNotification(notification)),\n\t\t);\n\t}\n\n\tprivate waitFor(\n\t\turi: string,\n\t\tversion: number,\n\t\tserverId: string,\n\t\ttimeoutMs: number,\n\t\tsignal?: AbortSignal,\n\t): {\n\t\treadonly outcome: Promise<DiagnosticsOutcome>;\n\t\treadonly cancel: () => void;\n\t} {\n\t\tlet waiter: Waiter;\n\t\tconst outcome = new Promise<DiagnosticsOutcome>((resolve) => {\n\t\t\tconst abort = () =>\n\t\t\t\tthis.settle(uri, waiter, {\n\t\t\t\t\tstatus: \"unavailable\",\n\t\t\t\t\tserverId,\n\t\t\t\t\tunavailableKind: \"aborted\",\n\t\t\t\t\treason: \"diagnostics aborted\",\n\t\t\t\t});\n\t\t\tconst timer = setTimeout(\n\t\t\t\t() =>\n\t\t\t\t\tthis.settle(uri, waiter, {\n\t\t\t\t\t\tstatus: \"unavailable\",\n\t\t\t\t\t\tserverId,\n\t\t\t\t\t\tunavailableKind: \"timed-out\",\n\t\t\t\t\t\treason: `timed out after ${timeoutMs}ms`,\n\t\t\t\t\t}),\n\t\t\t\ttimeoutMs,\n\t\t\t);\n\t\t\tsignal?.addEventListener(\"abort\", abort, { once: true });\n\t\t\twaiter = { version, serverId, resolve, timer, removeAbort: () => signal?.removeEventListener(\"abort\", abort) };\n\t\t\tconst waiters = this.waiters.get(uri) ?? new Set<Waiter>();\n\t\t\twaiters.add(waiter);\n\t\t\tthis.waiters.set(uri, waiters);\n\t\t\tif (signal?.aborted) abort();\n\t\t});\n\t\treturn {\n\t\t\toutcome,\n\t\t\tcancel: () =>\n\t\t\t\tthis.settle(uri, waiter, {\n\t\t\t\t\tstatus: \"unavailable\",\n\t\t\t\t\tserverId,\n\t\t\t\t\tunavailableKind: \"aborted\",\n\t\t\t\t\treason: \"diagnostics cancelled\",\n\t\t\t\t}),\n\t\t};\n\t}\n\n\tprivate onNotification(notification: LspNotification): void {\n\t\tconst publish = parsePublish(notification);\n\t\tif (!publish) return;\n\t\tconst waiters = this.waiters.get(publish.uri);\n\t\tif (!waiters) return;\n\t\tfor (const waiter of [...waiters]) {\n\t\t\tif (publish.version === undefined || publish.version < waiter.version) continue;\n\t\t\tif (publish.version > waiter.version) {\n\t\t\t\tthis.settle(publish.uri, waiter, {\n\t\t\t\t\tstatus: \"unavailable\",\n\t\t\t\t\tserverId: waiter.serverId,\n\t\t\t\t\tunavailableKind: \"superseded\",\n\t\t\t\t\treason: \"diagnostics were superseded by a newer document version\",\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthis.settle(publish.uri, waiter, {\n\t\t\t\tstatus: \"ok\",\n\t\t\t\tserverId: waiter.serverId,\n\t\t\t\tdiagnostics: publish.diagnostics.slice(0, MAX_DIAGNOSTICS),\n\t\t\t\ttruncated: publish.diagnostics.length > MAX_DIAGNOSTICS,\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate settle(uri: string, waiter: Waiter, outcome: DiagnosticsOutcome): void {\n\t\tconst waiters = this.waiters.get(uri);\n\t\tif (!waiters?.delete(waiter)) return;\n\t\tif (waiters.size === 0) this.waiters.delete(uri);\n\t\tclearTimeout(waiter.timer);\n\t\twaiter.removeAbort();\n\t\twaiter.resolve(outcome);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { pathToFileURL } from "node:url";
|
|
2
|
+
import { LspDocumentSync } from "./sync.js";
|
|
3
|
+
const MAX_DIAGNOSTICS = 1_000;
|
|
4
|
+
function isRecord(value) {
|
|
5
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6
|
+
}
|
|
7
|
+
function isPosition(value) {
|
|
8
|
+
return (isRecord(value) &&
|
|
9
|
+
Number.isSafeInteger(value.line) &&
|
|
10
|
+
value.line >= 0 &&
|
|
11
|
+
Number.isSafeInteger(value.character) &&
|
|
12
|
+
value.character >= 0);
|
|
13
|
+
}
|
|
14
|
+
function isDiagnostic(value) {
|
|
15
|
+
if (!isRecord(value) || typeof value.message !== "string" || !isRecord(value.range))
|
|
16
|
+
return false;
|
|
17
|
+
if (!isPosition(value.range.start) || !isPosition(value.range.end))
|
|
18
|
+
return false;
|
|
19
|
+
if (value.severity !== undefined && !Number.isSafeInteger(value.severity))
|
|
20
|
+
return false;
|
|
21
|
+
if (value.code !== undefined && typeof value.code !== "string" && typeof value.code !== "number")
|
|
22
|
+
return false;
|
|
23
|
+
if (value.source !== undefined && typeof value.source !== "string")
|
|
24
|
+
return false;
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
function parsePublish(notification) {
|
|
28
|
+
if (notification.method !== "textDocument/publishDiagnostics" || !isRecord(notification.params))
|
|
29
|
+
return undefined;
|
|
30
|
+
const { uri, version, diagnostics } = notification.params;
|
|
31
|
+
if (typeof uri !== "string" || !Array.isArray(diagnostics) || !diagnostics.every(isDiagnostic))
|
|
32
|
+
return undefined;
|
|
33
|
+
if (version !== undefined && (!Number.isSafeInteger(version) || version < 0))
|
|
34
|
+
return undefined;
|
|
35
|
+
return { uri, ...(version === undefined ? {} : { version: version }), diagnostics };
|
|
36
|
+
}
|
|
37
|
+
function unavailableReason(error) {
|
|
38
|
+
return error instanceof Error ? error.message : String(error);
|
|
39
|
+
}
|
|
40
|
+
/** Synchronizes disk snapshots and attributes diagnostics only to their exact document version. */
|
|
41
|
+
export class LspDiagnostics {
|
|
42
|
+
pool;
|
|
43
|
+
sync = new LspDocumentSync();
|
|
44
|
+
waiters = new Map();
|
|
45
|
+
unsubscribeByClient = new Map();
|
|
46
|
+
disposed = false;
|
|
47
|
+
constructor(pool) {
|
|
48
|
+
this.pool = pool;
|
|
49
|
+
}
|
|
50
|
+
async afterMutation(path, signal) {
|
|
51
|
+
if (this.disposed)
|
|
52
|
+
return { status: "unavailable", unavailableKind: "disposed", reason: "diagnostics are disposed" };
|
|
53
|
+
let selectedServerId;
|
|
54
|
+
try {
|
|
55
|
+
const connection = await this.pool.acquire(path);
|
|
56
|
+
if (this.disposed) {
|
|
57
|
+
return {
|
|
58
|
+
status: "unavailable",
|
|
59
|
+
...(connection === undefined ? {} : { serverId: connection.selection.server.id }),
|
|
60
|
+
unavailableKind: "disposed",
|
|
61
|
+
reason: "diagnostics are disposed",
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
if (!connection)
|
|
65
|
+
return {
|
|
66
|
+
status: "unavailable",
|
|
67
|
+
unavailableKind: "no-server",
|
|
68
|
+
reason: "no language server configured for path",
|
|
69
|
+
};
|
|
70
|
+
const serverId = connection.selection.server.id;
|
|
71
|
+
selectedServerId = serverId;
|
|
72
|
+
// `connection.selection.path` is the registry's canonicalized form of `path`
|
|
73
|
+
// (symlinks resolved -- see `registry.ts`'s `selectLspServerForPath`). It must
|
|
74
|
+
// be the one URI-keying basis used everywhere below: `sync.synchronize()` opens
|
|
75
|
+
// the document under this canonical path, so a waiter keyed by the caller's raw
|
|
76
|
+
// `path` would never match the publish it's waiting for wherever the two forms
|
|
77
|
+
// differ (e.g. macOS's /tmp -> /private/tmp), timing out instead of resolving.
|
|
78
|
+
const canonicalPath = connection.selection.path;
|
|
79
|
+
this.subscribe(connection.client);
|
|
80
|
+
const nextVersion = this.peekNextVersion(canonicalPath);
|
|
81
|
+
const pending = this.waitFor(this.uri(canonicalPath), nextVersion, serverId, connection.selection.server.diagnosticsTimeoutMs, signal);
|
|
82
|
+
const document = this.sync.synchronize(connection.client, connection.selection.path, connection.selection.languageId);
|
|
83
|
+
if (!document) {
|
|
84
|
+
pending.cancel();
|
|
85
|
+
return {
|
|
86
|
+
status: "unavailable",
|
|
87
|
+
serverId,
|
|
88
|
+
unavailableKind: "unsupported-sync",
|
|
89
|
+
reason: "language server does not support text document synchronization",
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
if (document.version !== nextVersion) {
|
|
93
|
+
pending.cancel();
|
|
94
|
+
return {
|
|
95
|
+
status: "unavailable",
|
|
96
|
+
serverId,
|
|
97
|
+
unavailableKind: "server-failed",
|
|
98
|
+
reason: "document synchronization version changed unexpectedly",
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return await pending.outcome;
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
if (this.disposed) {
|
|
105
|
+
return {
|
|
106
|
+
status: "unavailable",
|
|
107
|
+
...(selectedServerId === undefined ? {} : { serverId: selectedServerId }),
|
|
108
|
+
unavailableKind: "disposed",
|
|
109
|
+
reason: "diagnostics are disposed",
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
status: "unavailable",
|
|
114
|
+
...(selectedServerId === undefined ? {} : { serverId: selectedServerId }),
|
|
115
|
+
unavailableKind: "server-failed",
|
|
116
|
+
reason: unavailableReason(error),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async dispose() {
|
|
121
|
+
if (this.disposed)
|
|
122
|
+
return;
|
|
123
|
+
this.disposed = true;
|
|
124
|
+
this.sync.dispose();
|
|
125
|
+
for (const unsubscribe of this.unsubscribeByClient.values())
|
|
126
|
+
unsubscribe();
|
|
127
|
+
this.unsubscribeByClient.clear();
|
|
128
|
+
for (const [uri, waiters] of this.waiters) {
|
|
129
|
+
for (const waiter of [...waiters])
|
|
130
|
+
this.settle(uri, waiter, {
|
|
131
|
+
status: "unavailable",
|
|
132
|
+
serverId: waiter.serverId,
|
|
133
|
+
unavailableKind: "disposed",
|
|
134
|
+
reason: "diagnostics are disposed",
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
versions = new Map();
|
|
139
|
+
uri(path) {
|
|
140
|
+
return pathToFileURL(path).href;
|
|
141
|
+
}
|
|
142
|
+
peekNextVersion(path) {
|
|
143
|
+
const uri = this.uri(path);
|
|
144
|
+
const version = (this.versions.get(uri) ?? 0) + 1;
|
|
145
|
+
this.versions.set(uri, version);
|
|
146
|
+
return version;
|
|
147
|
+
}
|
|
148
|
+
subscribe(client) {
|
|
149
|
+
if (this.unsubscribeByClient.has(client))
|
|
150
|
+
return;
|
|
151
|
+
this.unsubscribeByClient.set(client, client.onNotification((notification) => this.onNotification(notification)));
|
|
152
|
+
}
|
|
153
|
+
waitFor(uri, version, serverId, timeoutMs, signal) {
|
|
154
|
+
let waiter;
|
|
155
|
+
const outcome = new Promise((resolve) => {
|
|
156
|
+
const abort = () => this.settle(uri, waiter, {
|
|
157
|
+
status: "unavailable",
|
|
158
|
+
serverId,
|
|
159
|
+
unavailableKind: "aborted",
|
|
160
|
+
reason: "diagnostics aborted",
|
|
161
|
+
});
|
|
162
|
+
const timer = setTimeout(() => this.settle(uri, waiter, {
|
|
163
|
+
status: "unavailable",
|
|
164
|
+
serverId,
|
|
165
|
+
unavailableKind: "timed-out",
|
|
166
|
+
reason: `timed out after ${timeoutMs}ms`,
|
|
167
|
+
}), timeoutMs);
|
|
168
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
169
|
+
waiter = { version, serverId, resolve, timer, removeAbort: () => signal?.removeEventListener("abort", abort) };
|
|
170
|
+
const waiters = this.waiters.get(uri) ?? new Set();
|
|
171
|
+
waiters.add(waiter);
|
|
172
|
+
this.waiters.set(uri, waiters);
|
|
173
|
+
if (signal?.aborted)
|
|
174
|
+
abort();
|
|
175
|
+
});
|
|
176
|
+
return {
|
|
177
|
+
outcome,
|
|
178
|
+
cancel: () => this.settle(uri, waiter, {
|
|
179
|
+
status: "unavailable",
|
|
180
|
+
serverId,
|
|
181
|
+
unavailableKind: "aborted",
|
|
182
|
+
reason: "diagnostics cancelled",
|
|
183
|
+
}),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
onNotification(notification) {
|
|
187
|
+
const publish = parsePublish(notification);
|
|
188
|
+
if (!publish)
|
|
189
|
+
return;
|
|
190
|
+
const waiters = this.waiters.get(publish.uri);
|
|
191
|
+
if (!waiters)
|
|
192
|
+
return;
|
|
193
|
+
for (const waiter of [...waiters]) {
|
|
194
|
+
if (publish.version === undefined || publish.version < waiter.version)
|
|
195
|
+
continue;
|
|
196
|
+
if (publish.version > waiter.version) {
|
|
197
|
+
this.settle(publish.uri, waiter, {
|
|
198
|
+
status: "unavailable",
|
|
199
|
+
serverId: waiter.serverId,
|
|
200
|
+
unavailableKind: "superseded",
|
|
201
|
+
reason: "diagnostics were superseded by a newer document version",
|
|
202
|
+
});
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
this.settle(publish.uri, waiter, {
|
|
206
|
+
status: "ok",
|
|
207
|
+
serverId: waiter.serverId,
|
|
208
|
+
diagnostics: publish.diagnostics.slice(0, MAX_DIAGNOSTICS),
|
|
209
|
+
truncated: publish.diagnostics.length > MAX_DIAGNOSTICS,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
settle(uri, waiter, outcome) {
|
|
214
|
+
const waiters = this.waiters.get(uri);
|
|
215
|
+
if (!waiters?.delete(waiter))
|
|
216
|
+
return;
|
|
217
|
+
if (waiters.size === 0)
|
|
218
|
+
this.waiters.delete(uri);
|
|
219
|
+
clearTimeout(waiter.timer);
|
|
220
|
+
waiter.removeAbort();
|
|
221
|
+
waiter.resolve(outcome);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
//# sourceMappingURL=diagnostics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../../../src/core/lsp/diagnostics.ts"],"names":[],"mappings":"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","sourcesContent":["import { pathToFileURL } from \"node:url\";\nimport type { DiagnosticUnavailableKind } from \"../tools/contract.ts\";\nimport type { LspClient, LspNotification } from \"./client.ts\";\nimport type { LspPool } from \"./pool.ts\";\nimport { LspDocumentSync } from \"./sync.ts\";\n\nexport interface DiagnosticPosition {\n\treadonly line: number;\n\treadonly character: number;\n}\n\nexport interface DiagnosticRange {\n\treadonly start: DiagnosticPosition;\n\treadonly end: DiagnosticPosition;\n}\n\nexport interface Diagnostic {\n\treadonly range: DiagnosticRange;\n\treadonly severity?: number;\n\treadonly code?: string | number;\n\treadonly source?: string;\n\treadonly message: string;\n}\n\nexport type DiagnosticsOutcome =\n\t| {\n\t\t\treadonly status: \"ok\";\n\t\t\treadonly serverId: string;\n\t\t\treadonly diagnostics: readonly Diagnostic[];\n\t\t\treadonly truncated: boolean;\n\t }\n\t| {\n\t\t\treadonly status: \"unavailable\";\n\t\t\treadonly serverId?: string;\n\t\t\treadonly unavailableKind: DiagnosticUnavailableKind;\n\t\t\treadonly reason: string;\n\t };\n\ninterface PublishDiagnostics {\n\treadonly uri: string;\n\treadonly version?: number;\n\treadonly diagnostics: readonly Diagnostic[];\n}\n\ninterface Waiter {\n\treadonly version: number;\n\treadonly serverId: string;\n\treadonly resolve: (outcome: DiagnosticsOutcome) => void;\n\treadonly timer: NodeJS.Timeout;\n\treadonly removeAbort: () => void;\n}\n\nconst MAX_DIAGNOSTICS = 1_000;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isPosition(value: unknown): value is DiagnosticPosition {\n\treturn (\n\t\tisRecord(value) &&\n\t\tNumber.isSafeInteger(value.line) &&\n\t\t(value.line as number) >= 0 &&\n\t\tNumber.isSafeInteger(value.character) &&\n\t\t(value.character as number) >= 0\n\t);\n}\n\nfunction isDiagnostic(value: unknown): value is Diagnostic {\n\tif (!isRecord(value) || typeof value.message !== \"string\" || !isRecord(value.range)) return false;\n\tif (!isPosition(value.range.start) || !isPosition(value.range.end)) return false;\n\tif (value.severity !== undefined && !Number.isSafeInteger(value.severity)) return false;\n\tif (value.code !== undefined && typeof value.code !== \"string\" && typeof value.code !== \"number\") return false;\n\tif (value.source !== undefined && typeof value.source !== \"string\") return false;\n\treturn true;\n}\n\nfunction parsePublish(notification: LspNotification): PublishDiagnostics | undefined {\n\tif (notification.method !== \"textDocument/publishDiagnostics\" || !isRecord(notification.params)) return undefined;\n\tconst { uri, version, diagnostics } = notification.params;\n\tif (typeof uri !== \"string\" || !Array.isArray(diagnostics) || !diagnostics.every(isDiagnostic)) return undefined;\n\tif (version !== undefined && (!Number.isSafeInteger(version) || (version as number) < 0)) return undefined;\n\treturn { uri, ...(version === undefined ? {} : { version: version as number }), diagnostics };\n}\n\nfunction unavailableReason(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n\n/** Synchronizes disk snapshots and attributes diagnostics only to their exact document version. */\nexport class LspDiagnostics {\n\tprivate readonly pool: LspPool;\n\tprivate readonly sync = new LspDocumentSync();\n\tprivate readonly waiters = new Map<string, Set<Waiter>>();\n\tprivate readonly unsubscribeByClient = new Map<LspClient, () => void>();\n\tprivate disposed = false;\n\n\tconstructor(pool: LspPool) {\n\t\tthis.pool = pool;\n\t}\n\n\tasync afterMutation(path: string, signal?: AbortSignal): Promise<DiagnosticsOutcome> {\n\t\tif (this.disposed)\n\t\t\treturn { status: \"unavailable\", unavailableKind: \"disposed\", reason: \"diagnostics are disposed\" };\n\t\tlet selectedServerId: string | undefined;\n\t\ttry {\n\t\t\tconst connection = await this.pool.acquire(path);\n\t\t\tif (this.disposed) {\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"unavailable\",\n\t\t\t\t\t...(connection === undefined ? {} : { serverId: connection.selection.server.id }),\n\t\t\t\t\tunavailableKind: \"disposed\",\n\t\t\t\t\treason: \"diagnostics are disposed\",\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (!connection)\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"unavailable\",\n\t\t\t\t\tunavailableKind: \"no-server\",\n\t\t\t\t\treason: \"no language server configured for path\",\n\t\t\t\t};\n\t\t\tconst serverId = connection.selection.server.id;\n\t\t\tselectedServerId = serverId;\n\t\t\t// `connection.selection.path` is the registry's canonicalized form of `path`\n\t\t\t// (symlinks resolved -- see `registry.ts`'s `selectLspServerForPath`). It must\n\t\t\t// be the one URI-keying basis used everywhere below: `sync.synchronize()` opens\n\t\t\t// the document under this canonical path, so a waiter keyed by the caller's raw\n\t\t\t// `path` would never match the publish it's waiting for wherever the two forms\n\t\t\t// differ (e.g. macOS's /tmp -> /private/tmp), timing out instead of resolving.\n\t\t\tconst canonicalPath = connection.selection.path;\n\t\t\tthis.subscribe(connection.client);\n\t\t\tconst nextVersion = this.peekNextVersion(canonicalPath);\n\t\t\tconst pending = this.waitFor(\n\t\t\t\tthis.uri(canonicalPath),\n\t\t\t\tnextVersion,\n\t\t\t\tserverId,\n\t\t\t\tconnection.selection.server.diagnosticsTimeoutMs,\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tconst document = this.sync.synchronize(\n\t\t\t\tconnection.client,\n\t\t\t\tconnection.selection.path,\n\t\t\t\tconnection.selection.languageId,\n\t\t\t);\n\t\t\tif (!document) {\n\t\t\t\tpending.cancel();\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"unavailable\",\n\t\t\t\t\tserverId,\n\t\t\t\t\tunavailableKind: \"unsupported-sync\",\n\t\t\t\t\treason: \"language server does not support text document synchronization\",\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (document.version !== nextVersion) {\n\t\t\t\tpending.cancel();\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"unavailable\",\n\t\t\t\t\tserverId,\n\t\t\t\t\tunavailableKind: \"server-failed\",\n\t\t\t\t\treason: \"document synchronization version changed unexpectedly\",\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn await pending.outcome;\n\t\t} catch (error) {\n\t\t\tif (this.disposed) {\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"unavailable\",\n\t\t\t\t\t...(selectedServerId === undefined ? {} : { serverId: selectedServerId }),\n\t\t\t\t\tunavailableKind: \"disposed\",\n\t\t\t\t\treason: \"diagnostics are disposed\",\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tstatus: \"unavailable\",\n\t\t\t\t...(selectedServerId === undefined ? {} : { serverId: selectedServerId }),\n\t\t\t\tunavailableKind: \"server-failed\",\n\t\t\t\treason: unavailableReason(error),\n\t\t\t};\n\t\t}\n\t}\n\n\tasync dispose(): Promise<void> {\n\t\tif (this.disposed) return;\n\t\tthis.disposed = true;\n\t\tthis.sync.dispose();\n\t\tfor (const unsubscribe of this.unsubscribeByClient.values()) unsubscribe();\n\t\tthis.unsubscribeByClient.clear();\n\t\tfor (const [uri, waiters] of this.waiters) {\n\t\t\tfor (const waiter of [...waiters])\n\t\t\t\tthis.settle(uri, waiter, {\n\t\t\t\t\tstatus: \"unavailable\",\n\t\t\t\t\tserverId: waiter.serverId,\n\t\t\t\t\tunavailableKind: \"disposed\",\n\t\t\t\t\treason: \"diagnostics are disposed\",\n\t\t\t\t});\n\t\t}\n\t}\n\n\tprivate readonly versions = new Map<string, number>();\n\n\tprivate uri(path: string): string {\n\t\treturn pathToFileURL(path).href;\n\t}\n\n\tprivate peekNextVersion(path: string): number {\n\t\tconst uri = this.uri(path);\n\t\tconst version = (this.versions.get(uri) ?? 0) + 1;\n\t\tthis.versions.set(uri, version);\n\t\treturn version;\n\t}\n\n\tprivate subscribe(client: LspClient): void {\n\t\tif (this.unsubscribeByClient.has(client)) return;\n\t\tthis.unsubscribeByClient.set(\n\t\t\tclient,\n\t\t\tclient.onNotification((notification) => this.onNotification(notification)),\n\t\t);\n\t}\n\n\tprivate waitFor(\n\t\turi: string,\n\t\tversion: number,\n\t\tserverId: string,\n\t\ttimeoutMs: number,\n\t\tsignal?: AbortSignal,\n\t): {\n\t\treadonly outcome: Promise<DiagnosticsOutcome>;\n\t\treadonly cancel: () => void;\n\t} {\n\t\tlet waiter: Waiter;\n\t\tconst outcome = new Promise<DiagnosticsOutcome>((resolve) => {\n\t\t\tconst abort = () =>\n\t\t\t\tthis.settle(uri, waiter, {\n\t\t\t\t\tstatus: \"unavailable\",\n\t\t\t\t\tserverId,\n\t\t\t\t\tunavailableKind: \"aborted\",\n\t\t\t\t\treason: \"diagnostics aborted\",\n\t\t\t\t});\n\t\t\tconst timer = setTimeout(\n\t\t\t\t() =>\n\t\t\t\t\tthis.settle(uri, waiter, {\n\t\t\t\t\t\tstatus: \"unavailable\",\n\t\t\t\t\t\tserverId,\n\t\t\t\t\t\tunavailableKind: \"timed-out\",\n\t\t\t\t\t\treason: `timed out after ${timeoutMs}ms`,\n\t\t\t\t\t}),\n\t\t\t\ttimeoutMs,\n\t\t\t);\n\t\t\tsignal?.addEventListener(\"abort\", abort, { once: true });\n\t\t\twaiter = { version, serverId, resolve, timer, removeAbort: () => signal?.removeEventListener(\"abort\", abort) };\n\t\t\tconst waiters = this.waiters.get(uri) ?? new Set<Waiter>();\n\t\t\twaiters.add(waiter);\n\t\t\tthis.waiters.set(uri, waiters);\n\t\t\tif (signal?.aborted) abort();\n\t\t});\n\t\treturn {\n\t\t\toutcome,\n\t\t\tcancel: () =>\n\t\t\t\tthis.settle(uri, waiter, {\n\t\t\t\t\tstatus: \"unavailable\",\n\t\t\t\t\tserverId,\n\t\t\t\t\tunavailableKind: \"aborted\",\n\t\t\t\t\treason: \"diagnostics cancelled\",\n\t\t\t\t}),\n\t\t};\n\t}\n\n\tprivate onNotification(notification: LspNotification): void {\n\t\tconst publish = parsePublish(notification);\n\t\tif (!publish) return;\n\t\tconst waiters = this.waiters.get(publish.uri);\n\t\tif (!waiters) return;\n\t\tfor (const waiter of [...waiters]) {\n\t\t\tif (publish.version === undefined || publish.version < waiter.version) continue;\n\t\t\tif (publish.version > waiter.version) {\n\t\t\t\tthis.settle(publish.uri, waiter, {\n\t\t\t\t\tstatus: \"unavailable\",\n\t\t\t\t\tserverId: waiter.serverId,\n\t\t\t\t\tunavailableKind: \"superseded\",\n\t\t\t\t\treason: \"diagnostics were superseded by a newer document version\",\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthis.settle(publish.uri, waiter, {\n\t\t\t\tstatus: \"ok\",\n\t\t\t\tserverId: waiter.serverId,\n\t\t\t\tdiagnostics: publish.diagnostics.slice(0, MAX_DIAGNOSTICS),\n\t\t\t\ttruncated: publish.diagnostics.length > MAX_DIAGNOSTICS,\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate settle(uri: string, waiter: Waiter, outcome: DiagnosticsOutcome): void {\n\t\tconst waiters = this.waiters.get(uri);\n\t\tif (!waiters?.delete(waiter)) return;\n\t\tif (waiters.size === 0) this.waiters.delete(uri);\n\t\tclearTimeout(waiter.timer);\n\t\twaiter.removeAbort();\n\t\twaiter.resolve(outcome);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { LspClient } from "./client.ts";
|
|
2
|
+
import { type ResolvedLspRegistry, type SelectedLspServer } from "./registry.ts";
|
|
3
|
+
type PoolEntryState = "starting" | "ready" | "degraded" | "disabled" | "closed";
|
|
4
|
+
export interface LspPoolOptions {
|
|
5
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
6
|
+
/**
|
|
7
|
+
* Invoked synchronously on every server-root state transition (starting, ready,
|
|
8
|
+
* degraded, disabled, closed) so a caller can build a chronological status history
|
|
9
|
+
* across the pool's whole lifetime, not just a single snapshot (LSP.2).
|
|
10
|
+
*/
|
|
11
|
+
readonly onStatusChange?: (status: LspPoolStatus) => void;
|
|
12
|
+
}
|
|
13
|
+
export interface LspPoolConnection {
|
|
14
|
+
readonly client: LspClient;
|
|
15
|
+
readonly selection: SelectedLspServer;
|
|
16
|
+
}
|
|
17
|
+
export interface LspPoolStatus {
|
|
18
|
+
readonly serverId: string;
|
|
19
|
+
readonly root: string;
|
|
20
|
+
readonly state: PoolEntryState;
|
|
21
|
+
readonly attempts: number;
|
|
22
|
+
readonly reason?: string;
|
|
23
|
+
}
|
|
24
|
+
/** Owns and bounds language-server clients for one resolved workspace registry. */
|
|
25
|
+
export declare class LspPool {
|
|
26
|
+
private readonly registry;
|
|
27
|
+
private readonly options;
|
|
28
|
+
private readonly entries;
|
|
29
|
+
private startPromise;
|
|
30
|
+
private disposePromise;
|
|
31
|
+
private closing;
|
|
32
|
+
constructor(registry: ResolvedLspRegistry, options?: LspPoolOptions);
|
|
33
|
+
/** Eagerly initialize the workspace-root process for every configured server. */
|
|
34
|
+
start(): Promise<void>;
|
|
35
|
+
/** Select a file's server/root and return its ready shared client. */
|
|
36
|
+
acquire(path: string): Promise<LspPoolConnection | undefined>;
|
|
37
|
+
status(): readonly LspPoolStatus[];
|
|
38
|
+
dispose(): Promise<void>;
|
|
39
|
+
private getOrCreateEntry;
|
|
40
|
+
private ensureClient;
|
|
41
|
+
private onClientFailure;
|
|
42
|
+
private disable;
|
|
43
|
+
private toStatus;
|
|
44
|
+
private emitStatus;
|
|
45
|
+
private disposeEntries;
|
|
46
|
+
}
|
|
47
|
+
export {};
|
|
48
|
+
//# sourceMappingURL=pool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../../../src/core/lsp/pool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EACN,KAAK,mBAAmB,EAExB,KAAK,iBAAiB,EAEtB,MAAM,eAAe,CAAC;AAIvB,KAAK,cAAc,GAAG,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,UAAU,GAAG,QAAQ,CAAC;AAahF,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACjC;;;;OAIG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;CAC1D;AAED,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC;CACtC;AAED,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CACzB;AAUD,mFAAmF;AACnF,qBAAa,OAAO;IACnB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsB;IAC/C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IACxD,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,cAAc,CAA4B;IAClD,OAAO,CAAC,OAAO,CAAS;IAExB,YAAY,QAAQ,EAAE,mBAAmB,EAAE,OAAO,GAAE,cAAmB,EAGtE;IAED,iFAAiF;IACjF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAQrB;IAED,sEAAsE;IAChE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAMlE;IAED,MAAM,IAAI,SAAS,aAAa,EAAE,CAQjC;IAED,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAGvB;IAED,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,YAAY;IAgDpB,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,OAAO;IAMf,OAAO,CAAC,QAAQ;IAUhB,OAAO,CAAC,UAAU;YAIJ,cAAc;CAa5B","sourcesContent":["import { LspClient } from \"./client.ts\";\nimport {\n\ttype ResolvedLspRegistry,\n\ttype ResolvedLspServer,\n\ttype SelectedLspServer,\n\tselectLspServerForPath,\n} from \"./registry.ts\";\n\nconst MAX_ATTEMPTS = 3;\n\ntype PoolEntryState = \"starting\" | \"ready\" | \"degraded\" | \"disabled\" | \"closed\";\n\ninterface PoolEntry {\n\treadonly server: ResolvedLspServer;\n\treadonly root: string;\n\tattempts: number;\n\tstate: PoolEntryState;\n\treason?: string;\n\tclient?: LspClient;\n\tstartPromise?: Promise<LspClient>;\n\tunsubscribeFailure?: () => void;\n}\n\nexport interface LspPoolOptions {\n\treadonly env?: NodeJS.ProcessEnv;\n\t/**\n\t * Invoked synchronously on every server-root state transition (starting, ready,\n\t * degraded, disabled, closed) so a caller can build a chronological status history\n\t * across the pool's whole lifetime, not just a single snapshot (LSP.2).\n\t */\n\treadonly onStatusChange?: (status: LspPoolStatus) => void;\n}\n\nexport interface LspPoolConnection {\n\treadonly client: LspClient;\n\treadonly selection: SelectedLspServer;\n}\n\nexport interface LspPoolStatus {\n\treadonly serverId: string;\n\treadonly root: string;\n\treadonly state: PoolEntryState;\n\treadonly attempts: number;\n\treadonly reason?: string;\n}\n\nfunction entryKey(serverId: string, root: string): string {\n\treturn `${serverId}\\0${root}`;\n}\n\nfunction normalizeError(error: unknown): Error {\n\treturn error instanceof Error ? error : new Error(String(error));\n}\n\n/** Owns and bounds language-server clients for one resolved workspace registry. */\nexport class LspPool {\n\tprivate readonly registry: ResolvedLspRegistry;\n\tprivate readonly options: LspPoolOptions;\n\tprivate readonly entries = new Map<string, PoolEntry>();\n\tprivate startPromise: Promise<void> | undefined;\n\tprivate disposePromise: Promise<void> | undefined;\n\tprivate closing = false;\n\n\tconstructor(registry: ResolvedLspRegistry, options: LspPoolOptions = {}) {\n\t\tthis.registry = registry;\n\t\tthis.options = options;\n\t}\n\n\t/** Eagerly initialize the workspace-root process for every configured server. */\n\tstart(): Promise<void> {\n\t\tif (this.closing) return Promise.reject(new Error(\"LSP pool is disposed.\"));\n\t\tthis.startPromise ??= Promise.all(\n\t\t\tthis.registry.servers.map((server) =>\n\t\t\t\tthis.ensureClient(this.getOrCreateEntry(server, this.registry.workspace)),\n\t\t\t),\n\t\t).then(() => undefined);\n\t\treturn this.startPromise;\n\t}\n\n\t/** Select a file's server/root and return its ready shared client. */\n\tasync acquire(path: string): Promise<LspPoolConnection | undefined> {\n\t\tif (this.closing) throw new Error(\"LSP pool is disposed.\");\n\t\tconst selection = selectLspServerForPath(this.registry, path);\n\t\tif (!selection) return undefined;\n\t\tconst client = await this.ensureClient(this.getOrCreateEntry(selection.server, selection.root));\n\t\treturn { client, selection };\n\t}\n\n\tstatus(): readonly LspPoolStatus[] {\n\t\treturn [...this.entries.values()]\n\t\t\t.map((entry) => this.toStatus(entry))\n\t\t\t.sort((left, right) =>\n\t\t\t\tleft.serverId === right.serverId\n\t\t\t\t\t? left.root.localeCompare(right.root)\n\t\t\t\t\t: left.serverId.localeCompare(right.serverId),\n\t\t\t);\n\t}\n\n\tdispose(): Promise<void> {\n\t\tthis.disposePromise ??= this.disposeEntries();\n\t\treturn this.disposePromise;\n\t}\n\n\tprivate getOrCreateEntry(server: ResolvedLspServer, root: string): PoolEntry {\n\t\tconst key = entryKey(server.id, root);\n\t\tlet entry = this.entries.get(key);\n\t\tif (!entry) {\n\t\t\tentry = { server, root, attempts: 0, state: \"degraded\" };\n\t\t\tthis.entries.set(key, entry);\n\t\t}\n\t\treturn entry;\n\t}\n\n\tprivate ensureClient(entry: PoolEntry): Promise<LspClient> {\n\t\tif (entry.state === \"disabled\") {\n\t\t\treturn Promise.reject(\n\t\t\t\tnew Error(\n\t\t\t\t\t`LSP server ${entry.server.id} at ${entry.root} is disabled after ${entry.attempts} attempts: ${entry.reason}`,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (entry.state === \"closed\" || this.closing) return Promise.reject(new Error(\"LSP pool is disposed.\"));\n\t\tif (entry.state === \"ready\" && entry.client) return Promise.resolve(entry.client);\n\t\tif (entry.startPromise) return entry.startPromise;\n\t\tif (entry.attempts >= MAX_ATTEMPTS) {\n\t\t\tthis.disable(entry, entry.reason ?? \"attempt limit reached\");\n\t\t\treturn this.ensureClient(entry);\n\t\t}\n\n\t\tentry.attempts += 1;\n\t\tentry.state = \"starting\";\n\t\tentry.reason = undefined;\n\t\tthis.emitStatus(entry);\n\t\tconst client = new LspClient({\n\t\t\tcommand: entry.server.command,\n\t\t\targs: entry.server.args,\n\t\t\tcwd: entry.root,\n\t\t\tenv: this.options.env,\n\t\t\tinitializationTimeoutMs: entry.server.initializationTimeoutMs,\n\t\t\trequestTimeoutMs: entry.server.requestTimeoutMs,\n\t\t});\n\t\tentry.client = client;\n\t\tentry.unsubscribeFailure = client.onFailure((error) => this.onClientFailure(entry, client, error));\n\t\tentry.startPromise = client\n\t\t\t.start()\n\t\t\t.then(() => {\n\t\t\t\tif (this.closing) throw new Error(\"LSP pool is disposed.\");\n\t\t\t\tentry.state = \"ready\";\n\t\t\t\tthis.emitStatus(entry);\n\t\t\t\treturn client;\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tthis.onClientFailure(entry, client, normalizeError(error));\n\t\t\t\tthrow error;\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\tentry.startPromise = undefined;\n\t\t\t});\n\t\treturn entry.startPromise;\n\t}\n\n\tprivate onClientFailure(entry: PoolEntry, client: LspClient, error: Error): void {\n\t\tif (entry.client !== client || entry.state === \"closed\" || this.closing) return;\n\t\tentry.unsubscribeFailure?.();\n\t\tentry.unsubscribeFailure = undefined;\n\t\tentry.client = undefined;\n\t\tentry.reason = error.message;\n\t\tif (entry.attempts >= MAX_ATTEMPTS) {\n\t\t\tthis.disable(entry, error.message);\n\t\t} else {\n\t\t\tentry.state = \"degraded\";\n\t\t\tthis.emitStatus(entry);\n\t\t}\n\t\tvoid client.dispose();\n\t}\n\n\tprivate disable(entry: PoolEntry, reason: string): void {\n\t\tentry.state = \"disabled\";\n\t\tentry.reason = reason;\n\t\tthis.emitStatus(entry);\n\t}\n\n\tprivate toStatus(entry: PoolEntry): LspPoolStatus {\n\t\treturn {\n\t\t\tserverId: entry.server.id,\n\t\t\troot: entry.root,\n\t\t\tstate: entry.state,\n\t\t\tattempts: entry.attempts,\n\t\t\t...(entry.reason === undefined ? {} : { reason: entry.reason }),\n\t\t};\n\t}\n\n\tprivate emitStatus(entry: PoolEntry): void {\n\t\tthis.options.onStatusChange?.(this.toStatus(entry));\n\t}\n\n\tprivate async disposeEntries(): Promise<void> {\n\t\tthis.closing = true;\n\t\tawait Promise.allSettled(\n\t\t\t[...this.entries.values()].map(async (entry) => {\n\t\t\t\tentry.unsubscribeFailure?.();\n\t\t\t\tentry.unsubscribeFailure = undefined;\n\t\t\t\tawait entry.client?.dispose();\n\t\t\t\tentry.client = undefined;\n\t\t\t\tentry.state = \"closed\";\n\t\t\t\tthis.emitStatus(entry);\n\t\t\t}),\n\t\t);\n\t}\n}\n"]}
|