Sfoglia il codice sorgente

feat: add Java LSP server support (#2547)

Co-authored-by: rekram1-node <[email protected]>
iwauo 5 mesi fa
parent
commit
6b799b304c
1 ha cambiato i file con 88 aggiunte e 0 eliminazioni
  1. 88 0
      packages/opencode/src/lsp/server.ts

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

@@ -1,5 +1,6 @@
 import { spawn, type ChildProcessWithoutNullStreams } from "child_process"
 import path from "path"
+import os from "os"
 import { Global } from "../global"
 import { Log } from "../util/log"
 import { BunProc } from "../bun"
@@ -706,4 +707,91 @@ export namespace LSPServer {
       }
     },
   }
+
+  export const JDTLS: Info = {
+    id: "jdtls",
+    root: NearestRoot(["pom.xml", "build.gradle", "build.gradle.kts", ".project", ".classpath"]),
+    extensions: [".java"],
+    async spawn(root) {
+      const java = Bun.which("java")
+      if (!java) {
+        log.error("Java 21 or newer is required to run the JDTLS. Please install it first.")
+        return
+      }
+      const javaMajorVersion = await $`java -version`
+        .quiet()
+        .nothrow()
+        .then(({ stderr }) => {
+          const m = /"(\d+)\.\d+\.\d+"/.exec(stderr.toString())
+          return !m ? undefined : parseInt(m[1])
+        })
+      if (javaMajorVersion == null || javaMajorVersion < 21) {
+        log.error("JDTLS requires at least Java 21.")
+        return
+      }
+      const distPath = path.join(Global.Path.bin, "jdtls")
+      const launcherDir = path.join(distPath, "plugins")
+      const installed = await fs.exists(launcherDir)
+      if (!installed) {
+        if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return
+        log.info("Downloading JDTLS LSP server.")
+        await fs.mkdir(distPath, { recursive: true })
+        const releaseURL =
+          "https://www.eclipse.org/downloads/download.php?file=/jdtls/snapshots/jdt-language-server-latest.tar.gz"
+        const archivePath = path.join(distPath, "release.tar.gz")
+        await $`curl -L -o '${archivePath}' '${releaseURL}'`.quiet().nothrow()
+        await $`tar -xzf ${archivePath}`.cwd(distPath).quiet().nothrow()
+        await fs.rm(archivePath, { force: true })
+      }
+      const jarFileName = await $`ls org.eclipse.equinox.launcher_*.jar`
+        .cwd(launcherDir)
+        .quiet()
+        .nothrow()
+        .then(({ stdout }) => stdout.toString().trim())
+      const launcherJar = path.join(launcherDir, jarFileName)
+      if (!(await fs.exists(launcherJar))) {
+        log.error(`Failed to locate the JDTLS launcher module in the installed directory: ${distPath}.`)
+        return
+      }
+      const configFile = path.join(
+        distPath,
+        (() => {
+          switch (process.platform) {
+            case "darwin":
+              return "config_mac"
+            case "linux":
+              return "config_linux"
+            case "win32":
+              return "config_windows"
+            default:
+              return "config_linux"
+          }
+        })(),
+      )
+      const dataDir = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-jdtls-data"))
+      return {
+        process: spawn(
+          java,
+          [
+            "-jar",
+            launcherJar,
+            "-configuration",
+            configFile,
+            "-data",
+            dataDir,
+            "-Declipse.application=org.eclipse.jdt.ls.core.id1",
+            "-Dosgi.bundles.defaultStartLevel=4",
+            "-Declipse.product=org.eclipse.jdt.ls.core.product",
+            "-Dlog.level=ALL",
+            "--add-modules=ALL-SYSTEM",
+            "--add-opens java.base/java.util=ALL-UNNAMED",
+            "--add-opens java.base/java.lang=ALL-UNNAMED",
+          ],
+          {
+            cwd: root,
+          },
+        ),
+      }
+    },
+  }
 }