local util = require('opencode.util')
local safe_call = util.safe_call
local Promise = require('opencode.promise')
--- @class OpencodeServer
--- @field job any The vim.system job handle
--- @field url string|nil The server URL once ready
--- @field handle any Compatibility property for job.stop interface
--- @field spawn_promise Promise
--- @field shutdown_promise Promise
local OpencodeServer = {}
OpencodeServer.__index = OpencodeServer
--- Create a new ServerJob instance
--- @return OpencodeServer
function OpencodeServer.new()
--- before quitting vim ensure we close opencode server
vim.api.nvim_create_autocmd('VimLeavePre', {
group = vim.api.nvim_create_augroup('OpencodeVimLeavePre', { clear = true }),
callback = function()
local state = require('opencode.state')
if state.opencode_server then
state.opencode_server:shutdown()
end
end,
})
return setmetatable({
job = nil,
url = nil,
handle = nil,
spawn_promise = Promise.new(),
shutdown_promise = Promise.new(),
}, OpencodeServer)
end
function OpencodeServer:is_running()
return self.job and self.job.pid ~= nil
end
--- Clean up this server job
--- @return Promise
function OpencodeServer:shutdown()
if self.job and self.job.pid then
pcall(function()
self.job:kill('sigterm')
end)
end
self.job = nil
self.url = nil
self.handle = nil
self.shutdown_promise:resolve(true)
return self.shutdown_promise
end
--- @class OpencodeServerSpawnOpts
--- @field cwd? string
--- @field on_ready fun(job: any, url: string)
--- @field on_error fun(err: any)
--- @field on_exit fun(exit_opts: vim.SystemCompleted )
--- Spawn the opencode server for this ServerJob instance.
--- @param opts? OpencodeServerSpawnOpts
--- @return Promise
function OpencodeServer:spawn(opts)
opts = opts or {}
self.job = vim.system({
'opencode',
'serve',
}, {
cwd = opts.cwd,
stdout = function(err, data)
if err then
safe_call(opts.on_error, err)
return
end
if data then
local url = data:match('opencode server listening on ([^%s]+)')
if url then
self.url = url
self.spawn_promise:resolve(self)
safe_call(opts.on_ready, self.job, url)
end
end
end,
stderr = function(err, data)
if err or data then
self.spawn_promise:reject(err or data)
safe_call(opts.on_error, err or data)
end
end,
}, function(exit_opts)
self.job = nil
self.url = nil
self.handle = nil
safe_call(opts.on_exit, exit_opts)
self.shutdown_promise:resolve(true)
end)
self.handle = self.job and self.job.pid
return self.spawn_promise
end
function OpencodeServer:get_shutdown_promise()
return self.shutdown_promise
end
function OpencodeServer:get_spawn_promise()
return self.spawn_promise
end
return OpencodeServer