Browse Source

optimistically boot lsp servers

Dax Raad 7 months ago
parent
commit
9ad1687f04

+ 2 - 0
packages/opencode/src/cli/bootstrap.ts

@@ -1,6 +1,7 @@
 import { App } from "../app/app"
 import { ConfigHooks } from "../config/hooks"
 import { Format } from "../format"
+import { LSP } from "../lsp"
 import { Share } from "../share/share"
 
 export async function bootstrap<T>(
@@ -11,6 +12,7 @@ export async function bootstrap<T>(
     Share.init()
     Format.init()
     ConfigHooks.init()
+    LSP.init()
 
     return cb(app)
   })

+ 53 - 3
packages/opencode/src/cli/cmd/debug.ts

@@ -1,12 +1,18 @@
 import { App } from "../../app/app"
 import { Ripgrep } from "../../file/ripgrep"
 import { LSP } from "../../lsp"
+import { bootstrap } from "../bootstrap"
 import { cmd } from "./cmd"
 
 export const DebugCommand = cmd({
   command: "debug",
   builder: (yargs) =>
-    yargs.command(DiagnosticsCommand).command(TreeCommand).demandCommand(),
+    yargs
+      .command(DiagnosticsCommand)
+      .command(TreeCommand)
+      .command(SymbolsCommand)
+      .command(FilesCommand)
+      .demandCommand(),
   async handler() {},
 })
 
@@ -15,7 +21,7 @@ const DiagnosticsCommand = cmd({
   builder: (yargs) =>
     yargs.positional("file", { type: "string", demandOption: true }),
   async handler(args) {
-    await App.provide({ cwd: process.cwd() }, async () => {
+    await bootstrap({ cwd: process.cwd() }, async () => {
       await LSP.touchFile(args.file, true)
       console.log(await LSP.diagnostics())
     })
@@ -29,9 +35,53 @@ const TreeCommand = cmd({
       type: "number",
     }),
   async handler(args) {
-    await App.provide({ cwd: process.cwd() }, async () => {
+    await bootstrap({ cwd: process.cwd() }, async () => {
       const app = App.info()
       console.log(await Ripgrep.tree({ cwd: app.path.cwd, limit: args.limit }))
     })
   },
 })
+
+const SymbolsCommand = cmd({
+  command: "symbols <query>",
+  builder: (yargs) =>
+    yargs.positional("query", { type: "string", demandOption: true }),
+  async handler(args) {
+    await bootstrap({ cwd: process.cwd() }, async () => {
+      await LSP.touchFile("./src/index.ts", true)
+      await new Promise((resolve) => setTimeout(resolve, 3000))
+      const results = await LSP.workspaceSymbol(args.query)
+      console.log(JSON.stringify(results, null, 2))
+    })
+  },
+})
+
+const FilesCommand = cmd({
+  command: "files",
+  builder: (yargs) =>
+    yargs
+      .option("query", {
+        type: "string",
+        description: "Filter files by query",
+      })
+      .option("glob", {
+        type: "string",
+        description: "Glob pattern to match files",
+      })
+      .option("limit", {
+        type: "number",
+        description: "Limit number of results",
+      }),
+  async handler(args) {
+    await bootstrap({ cwd: process.cwd() }, async () => {
+      const app = App.info()
+      const files = await Ripgrep.files({
+        cwd: app.path.cwd,
+        query: args.query,
+        glob: args.glob,
+        limit: args.limit,
+      })
+      console.log(files.join("\n"))
+    })
+  },
+})

+ 1 - 1
packages/opencode/src/lsp/client.ts

@@ -185,7 +185,7 @@ export namespace LSPClient {
         log.info("shutting down")
         connection.end()
         connection.dispose()
-        server.process.kill("SIGKILL")
+        server.process.kill("SIGTERM")
       },
     }
 

+ 29 - 5
packages/opencode/src/lsp/index.ts

@@ -3,6 +3,7 @@ import { Log } from "../util/log"
 import { LSPClient } from "./client"
 import path from "path"
 import { LSPServer } from "./server"
+import { Ripgrep } from "../file/ripgrep"
 
 export namespace LSP {
   const log = Log.create({ service: "lsp" })
@@ -25,6 +26,23 @@ export namespace LSP {
     },
   )
 
+  export async function init() {
+    log.info("init")
+    const app = App.info()
+    const result = Object.values(LSPServer).map(async (x) => {
+      for (const extension of x.extensions) {
+        const [file] = await Ripgrep.files({
+          cwd: app.path.cwd,
+          glob: "*" + extension,
+        })
+        if (!file) continue
+        await LSP.touchFile(file, true)
+        break
+      }
+    })
+    return Promise.all(result)
+  }
+
   export async function touchFile(input: string, waitForDiagnostics?: boolean) {
     const extension = path.parse(input).ext
     const s = await state()
@@ -32,14 +50,12 @@ 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
+      if (s.skip.has(match.id)) continue
+      s.skip.add(match.id)
       const handle = await match.spawn(App.info())
-      if (!handle) {
-        s.skip.add(match.id)
-        continue
-      }
+      if (!handle) continue
       const client = await LSPClient.create(match.id, handle).catch(() => {})
       if (!client) {
         s.skip.add(match.id)
@@ -86,6 +102,14 @@ export namespace LSP {
     })
   }
 
+  export async function workspaceSymbol(query: string) {
+    return run((client) =>
+      client.connection.sendRequest("workspace/symbol", {
+        query,
+      }),
+    )
+  }
+
   async function run<T>(
     input: (client: LSPClient.Info) => Promise<T>,
   ): Promise<T[]> {