-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Migrate LSP client to vscode-jsonrpc for improved stability #1095
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Conversation
Replace custom JSON-RPC implementation with vscode-jsonrpc library. Use MessageConnection with StreamMessageReader/Writer. Implement Bun↔Node stream bridges for compatibility. Preserve all existing functionality (warmup, cleanup, capabilities). Net reduction of ~60 lines while improving protocol handling.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 issue found across 3 files
Confidence score: 3/5
- Potential for unhandled rejections if the timeout isn't cleared after a successful response in
src/tools/lsp/client.ts, which could cause noisy errors in normal use - Score reflects a moderate, user-impacting risk tied to request lifecycle handling, but limited in scope to timeout cleanup
- Pay close attention to
src/tools/lsp/client.ts- ensure timeouts are cleared on successful responses
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="src/tools/lsp/client.ts">
<violation number="1" location="src/tools/lsp/client.ts:344">
P2: Clear the timeout when the request finishes to avoid unhandled rejections after a successful response.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| const timeoutPromise = new Promise<never>((_, reject) => { | ||
| setTimeout(() => { | ||
| if (this.pending.has(id)) { | ||
| this.pending.delete(id) | ||
| const stderr = this.stderrBuffer.slice(-5).join("\n") | ||
| reject(new Error(`LSP request timeout (method: ${method})` + (stderr ? `\nrecent stderr: ${stderr}` : ""))) | ||
| } | ||
| }, 15000) | ||
| const stderr = this.stderrBuffer.slice(-5).join("\n") | ||
| reject(new Error(`LSP request timeout (method: ${method})` + (stderr ? `\nrecent stderr: ${stderr}` : ""))) | ||
| }, this.REQUEST_TIMEOUT) | ||
| }) | ||
| } | ||
|
|
||
| private notify(method: string, params?: unknown): void { | ||
| if (!this.proc) return | ||
| if (this.processExited || this.proc.exitCode !== null) return | ||
| const requestPromise = this.connection.sendRequest(method, params) as Promise<T> | ||
|
|
||
| const msg = JSON.stringify({ jsonrpc: "2.0", method, params }) | ||
| this.proc.stdin.write(`Content-Length: ${Buffer.byteLength(msg)}\r\n\r\n${msg}`) | ||
| return Promise.race([requestPromise, timeoutPromise]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: Clear the timeout when the request finishes to avoid unhandled rejections after a successful response.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/tools/lsp/client.ts, line 344:
<comment>Clear the timeout when the request finishes to avoid unhandled rejections after a successful response.</comment>
<file context>
@@ -294,142 +328,41 @@ export class LSPClient {
-
- return new Promise((resolve, reject) => {
- this.pending.set(id, { resolve, reject })
+ const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout(() => {
- if (this.pending.has(id)) {
</file context>
| const timeoutPromise = new Promise<never>((_, reject) => { | |
| setTimeout(() => { | |
| if (this.pending.has(id)) { | |
| this.pending.delete(id) | |
| const stderr = this.stderrBuffer.slice(-5).join("\n") | |
| reject(new Error(`LSP request timeout (method: ${method})` + (stderr ? `\nrecent stderr: ${stderr}` : ""))) | |
| } | |
| }, 15000) | |
| const stderr = this.stderrBuffer.slice(-5).join("\n") | |
| reject(new Error(`LSP request timeout (method: ${method})` + (stderr ? `\nrecent stderr: ${stderr}` : ""))) | |
| }, this.REQUEST_TIMEOUT) | |
| }) | |
| } | |
| private notify(method: string, params?: unknown): void { | |
| if (!this.proc) return | |
| if (this.processExited || this.proc.exitCode !== null) return | |
| const requestPromise = this.connection.sendRequest(method, params) as Promise<T> | |
| const msg = JSON.stringify({ jsonrpc: "2.0", method, params }) | |
| this.proc.stdin.write(`Content-Length: ${Buffer.byteLength(msg)}\r\n\r\n${msg}`) | |
| return Promise.race([requestPromise, timeoutPromise]) | |
| let timeoutId: ReturnType<typeof setTimeout> | undefined | |
| const timeoutPromise = new Promise<never>((_, reject) => { | |
| timeoutId = setTimeout(() => { | |
| const stderr = this.stderrBuffer.slice(-5).join("\n") | |
| reject(new Error(`LSP request timeout (method: ${method})` + (stderr ? `\nrecent stderr: ${stderr}` : ""))) | |
| }, this.REQUEST_TIMEOUT) | |
| }) | |
| const requestPromise = this.connection.sendRequest(method, params) as Promise<T> | |
| try { | |
| return await Promise.race([requestPromise, timeoutPromise]) | |
| } finally { | |
| if (timeoutId) clearTimeout(timeoutId) | |
| } |
Summary
vscode-jsonrpclibraryMessageConnectionAPIChanges
Content-Lengthheader parsing, buffer management, and request tracking codeMessageConnectionwithStreamMessageReader/WriterReadable/Writablewrappers to connect Bun's Web Streams to Node.js streams (required by vscode-jsonrpc)warmupClient(),cleanupTempDirectoryClients(), SIGBREAK handler, 15s timeout, and all LSP capabilities (codeAction, workspace/configuration, etc.)Testing
bun run typecheck bun run build bun test src/tools/lsp/Integration tested with
typescript-language-server:Related Issues
Summary by cubic
Migrates the LSP client to vscode-jsonrpc for more reliable protocol handling and simpler code. Uses MessageConnection with Bun↔Node stream bridges to enable stable LSP server connection reuse.
Refactors
Dependencies
Written for commit 3f939f9. Summary will update on new commits.