server.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. import { spawn, type ChildProcessWithoutNullStreams } from "child_process"
  2. import type { App } from "../app/app"
  3. import path from "path"
  4. import { Global } from "../global"
  5. import { Log } from "../util/log"
  6. import { BunProc } from "../bun"
  7. export namespace LSPServer {
  8. const log = Log.create({ service: "lsp.server" })
  9. export interface Handle {
  10. process: ChildProcessWithoutNullStreams
  11. initialization?: Record<string, any>
  12. }
  13. export interface Info {
  14. id: string
  15. extensions: string[]
  16. spawn(app: App.Info): Promise<Handle | undefined>
  17. }
  18. export const Typescript: Info = {
  19. id: "typescript",
  20. extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"],
  21. async spawn(app) {
  22. const tsserver = await Bun.resolve(
  23. "typescript/lib/tsserver.js",
  24. app.path.cwd,
  25. ).catch(() => {})
  26. if (!tsserver) return
  27. const proc = spawn(
  28. BunProc.which(),
  29. ["x", "typescript-language-server", "--stdio"],
  30. {
  31. env: {
  32. ...process.env,
  33. BUN_BE_BUN: "1",
  34. },
  35. },
  36. )
  37. return {
  38. process: proc,
  39. initialization: {
  40. tsserver: {
  41. path: tsserver,
  42. },
  43. },
  44. }
  45. },
  46. }
  47. export const Gopls: Info = {
  48. id: "golang",
  49. extensions: [".go"],
  50. async spawn() {
  51. let bin = Bun.which("gopls", {
  52. PATH: process.env["PATH"] + ":" + Global.Path.bin,
  53. })
  54. if (!bin) {
  55. log.info("installing gopls")
  56. const proc = Bun.spawn({
  57. cmd: ["go", "install", "golang.org/x/tools/gopls@latest"],
  58. env: { ...process.env, GOBIN: Global.Path.bin },
  59. stdout: "pipe",
  60. stderr: "pipe",
  61. stdin: "pipe",
  62. })
  63. const exit = await proc.exited
  64. if (exit !== 0) {
  65. log.error("Failed to install gopls")
  66. return
  67. }
  68. bin = path.join(
  69. Global.Path.bin,
  70. "gopls" + (process.platform === "win32" ? ".exe" : ""),
  71. )
  72. log.info(`installed gopls`, {
  73. bin,
  74. })
  75. }
  76. return {
  77. process: spawn(bin!),
  78. }
  79. },
  80. }
  81. export const RubyLsp: Info = {
  82. id: "ruby-lsp",
  83. extensions: [".rb", ".rake", ".gemspec", ".ru"],
  84. async spawn() {
  85. let bin = Bun.which("ruby-lsp", {
  86. PATH: process.env["PATH"] + ":" + Global.Path.bin,
  87. })
  88. if (!bin) {
  89. const ruby = Bun.which("ruby")
  90. const gem = Bun.which("gem")
  91. if (!ruby || !gem) {
  92. log.info("Ruby not found, please install Ruby first")
  93. return
  94. }
  95. log.info("installing ruby-lsp")
  96. const proc = Bun.spawn({
  97. cmd: ["gem", "install", "ruby-lsp", "--bindir", Global.Path.bin],
  98. stdout: "pipe",
  99. stderr: "pipe",
  100. stdin: "pipe",
  101. })
  102. const exit = await proc.exited
  103. if (exit !== 0) {
  104. log.error("Failed to install ruby-lsp")
  105. return
  106. }
  107. bin = path.join(
  108. Global.Path.bin,
  109. "ruby-lsp" + (process.platform === "win32" ? ".exe" : ""),
  110. )
  111. log.info(`installed ruby-lsp`, {
  112. bin,
  113. })
  114. }
  115. return {
  116. process: spawn(bin!, ["--stdio"]),
  117. }
  118. },
  119. }
  120. }