Просмотр исходного кода

feat: add experimental support for Ty language server (#5575)

OpeOginni 2 месяцев назад
Родитель
Сommit
ab9ac7c87a

+ 1 - 0
packages/opencode/src/flag/flag.ts

@@ -29,6 +29,7 @@ export namespace Flag {
   export const OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS = number("OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS")
   export const OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX = number("OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX")
   export const OPENCODE_EXPERIMENTAL_OXFMT = OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_OXFMT")
+  export const OPENCODE_EXPERIMENTAL_LSP_TY = truthy("OPENCODE_EXPERIMENTAL_LSP_TY")
 
   function truthy(key: string) {
     const value = process.env[key]?.toLowerCase()

+ 20 - 0
packages/opencode/src/lsp/index.ts

@@ -9,6 +9,7 @@ import z from "zod"
 import { Config } from "../config/config"
 import { spawn } from "child_process"
 import { Instance } from "../project/instance"
+import { Flag } from "@/flag/flag"
 
 export namespace LSP {
   const log = Log.create({ service: "lsp" })
@@ -60,6 +61,21 @@ export namespace LSP {
     })
   export type DocumentSymbol = z.infer<typeof DocumentSymbol>
 
+  const filterExperimentalServers = (servers: Record<string, LSPServer.Info>) => {
+    if (Flag.OPENCODE_EXPERIMENTAL_LSP_TY) {
+      // If experimental flag is enabled, disable pyright
+      if(servers["pyright"]) {
+        log.info("LSP server pyright is disabled because OPENCODE_EXPERIMENTAL_LSP_TY is enabled")
+        delete servers["pyright"]
+      }
+    } else {
+      // If experimental flag is disabled, disable ty
+      if(servers["ty"]) {
+        delete servers["ty"]
+      }
+    }
+  }
+
   const state = Instance.state(
     async () => {
       const clients: LSPClient.Info[] = []
@@ -79,6 +95,9 @@ export namespace LSP {
       for (const server of Object.values(LSPServer)) {
         servers[server.id] = server
       }
+
+      filterExperimentalServers(servers)
+
       for (const [name, item] of Object.entries(cfg.lsp ?? {})) {
         const existing = servers[name]
         if (item.disabled) {
@@ -204,6 +223,7 @@ export namespace LSP {
 
     for (const server of Object.values(s.servers)) {
       if (server.extensions.length && !server.extensions.includes(extension)) continue
+
       const root = await server.root(file)
       if (!root) continue
       if (s.broken.has(root + server.id)) continue

+ 56 - 0
packages/opencode/src/lsp/server.ts

@@ -361,6 +361,62 @@ export namespace LSPServer {
     },
   }
 
+  export const Ty: Info = {
+    id: "ty",
+    extensions: [".py", ".pyi"],
+    root: NearestRoot(["pyproject.toml", "ty.toml", "setup.py", "setup.cfg", "requirements.txt", "Pipfile", "pyrightconfig.json"]),
+    async spawn(root) {
+      if(!Flag.OPENCODE_EXPERIMENTAL_LSP_TY) {
+        return undefined
+      }
+
+      let binary = Bun.which("ty")
+
+      const initialization: Record<string, string> = {}
+
+      const potentialVenvPaths = [process.env["VIRTUAL_ENV"], path.join(root, ".venv"), path.join(root, "venv")].filter(
+        (p): p is string => p !== undefined,
+      )
+      for (const venvPath of potentialVenvPaths) {
+        const isWindows = process.platform === "win32"
+        const potentialPythonPath = isWindows
+          ? path.join(venvPath, "Scripts", "python.exe")
+          : path.join(venvPath, "bin", "python")
+        if (await Bun.file(potentialPythonPath).exists()) {
+          initialization["pythonPath"] = potentialPythonPath
+          break
+        }
+      }
+
+      if(!binary) {
+        for (const venvPath of potentialVenvPaths) {
+          const isWindows = process.platform === "win32"
+          const potentialTyPath = isWindows
+            ? path.join(venvPath, "Scripts", "ty.exe")
+            : path.join(venvPath, "bin", "ty")
+          if (await Bun.file(potentialTyPath).exists()) {
+            binary = potentialTyPath
+            break
+          }
+        }
+      }
+
+      if(!binary) {
+        log.error("ty not found, please install ty first")
+        return
+      }
+
+      const proc = spawn(binary, ["server"], {
+        cwd: root,
+      })
+      
+      return {
+        process: proc,
+        initialization,
+      }
+    },
+  }
+
   export const Pyright: Info = {
     id: "pyright",
     extensions: [".py", ".pyi"],