Browse Source

Improve LSP server initialization with timeout handling and skip failed servers

🤖 Generated with [opencode](https://opencode.ai)

Co-Authored-By: opencode <[email protected]>
Dax Raad 8 months ago
parent
commit
e30fba0d3c

+ 0 - 3
packages/opencode/src/cli/cmd/run.ts

@@ -115,9 +115,6 @@ export const RunCommand = {
         })
 
         const { providerID, modelID } = await Provider.defaultModel()
-        setTimeout(() => {
-          Session.abort(session.id)
-        }, 8000)
         await Session.chat({
           sessionID: session.id,
           providerID,

+ 38 - 22
packages/opencode/src/lsp/client.ts

@@ -11,6 +11,7 @@ import { LANGUAGE_EXTENSIONS } from "./language"
 import { Bus } from "../bus"
 import z from "zod"
 import type { LSPServer } from "./server"
+import { NamedError } from "../util/error"
 
 export namespace LSPClient {
   const log = Log.create({ service: "lsp.client" })
@@ -19,6 +20,13 @@ export namespace LSPClient {
 
   export type Diagnostic = VSCodeDiagnostic
 
+  export const InitializeError = NamedError.create(
+    "LSPInitializeError",
+    z.object({
+      serverID: z.string(),
+    }),
+  )
+
   export const Event = {
     Diagnostics: Bus.event(
       "lsp.client.diagnostics",
@@ -52,32 +60,40 @@ export namespace LSPClient {
     })
     connection.listen()
 
-    await connection.sendRequest("initialize", {
-      processId: server.process.pid,
-      workspaceFolders: [
-        {
-          name: "workspace",
-          uri: "file://" + app.path.cwd,
-        },
-      ],
-      initializationOptions: {
-        ...server.initialization,
-      },
-      capabilities: {
-        workspace: {
-          configuration: true,
+    log.info("sending initialize", { id: serverID })
+    await Promise.race([
+      connection.sendRequest("initialize", {
+        processId: server.process.pid,
+        workspaceFolders: [
+          {
+            name: "workspace",
+            uri: "file://" + app.path.cwd,
+          },
+        ],
+        initializationOptions: {
+          ...server.initialization,
         },
-        textDocument: {
-          synchronization: {
-            didOpen: true,
-            didChange: true,
+        capabilities: {
+          workspace: {
+            configuration: true,
           },
-          publishDiagnostics: {
-            versionSupport: true,
+          textDocument: {
+            synchronization: {
+              didOpen: true,
+              didChange: true,
+            },
+            publishDiagnostics: {
+              versionSupport: true,
+            },
           },
         },
-      },
-    })
+      }),
+      new Promise((_, reject) => {
+        setTimeout(() => {
+          reject(new InitializeError({ serverID }))
+        }, 5_000)
+      }),
+    ])
     await connection.sendNotification("initialized", {})
     log.info("initialized")
 

+ 12 - 3
packages/opencode/src/lsp/index.ts

@@ -12,9 +12,10 @@ export namespace LSP {
     async () => {
       log.info("initializing")
       const clients = new Map<string, LSPClient.Info>()
-
+      const skip = new Set<string>()
       return {
         clients,
+        skip,
       }
     },
     async (state) => {
@@ -31,11 +32,19 @@ export namespace LSP {
       x.extensions.includes(extension),
     )
     for (const match of matches) {
+      if (s.skip.has(match.id)) continue
       const existing = s.clients.get(match.id)
       if (existing) continue
       const handle = await match.spawn(App.info())
-      if (!handle) continue
-      const client = await LSPClient.create(match.id, handle)
+      if (!handle) {
+        s.skip.add(match.id)
+        continue
+      }
+      const client = await LSPClient.create(match.id, handle).catch(() => {})
+      if (!client) {
+        s.skip.add(match.id)
+        continue
+      }
       s.clients.set(match.id, client)
     }
     if (waitForDiagnostics) {