-
Notifications
You must be signed in to change notification settings - Fork 50
Expand file tree
/
Copy pathopencode_server.lua
More file actions
156 lines (136 loc) · 3.85 KB
/
opencode_server.lua
File metadata and controls
156 lines (136 loc) · 3.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
local util = require('opencode.util')
local safe_call = util.safe_call
local Promise = require('opencode.promise')
local config = require('opencode.config')
--- @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<OpencodeServer>
--- @field shutdown_promise Promise<boolean>
local OpencodeServer = {}
OpencodeServer.__index = OpencodeServer
local vim_leave_setup = false
local function ensure_vim_leave_autocmd()
if vim_leave_setup then
return
end
vim_leave_setup = true
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
pcall(function()
state.opencode_server:shutdown():wait(2000)
end)
end
end,
})
end
--- Create a new ServerJob instance
--- @return OpencodeServer
function OpencodeServer.new()
ensure_vim_leave_autocmd()
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<boolean>
function OpencodeServer:shutdown()
if self.shutdown_promise:is_resolved() then
return self.shutdown_promise
end
if self.job and self.job.pid then
local job = self.job
self.job = nil
self.url = nil
self.handle = nil
pcall(function()
job:kill(15) -- SIGTERM
end)
vim.defer_fn(function()
if job and job.pid then
pcall(function()
job:kill(9) -- SIGKILL
end)
end
end, 500)
else
self.shutdown_promise:resolve(true)
end
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<OpencodeServer>
function OpencodeServer:spawn(opts)
opts = opts or {}
self.job = vim.system({
config.opencode_executable,
'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 then
self.spawn_promise:reject(err)
safe_call(opts.on_error, err)
return
end
if data then
-- Filter out INFO/WARN/DEBUG log lines (not actual errors)
local log_level = data:match('^%s*(%u+)%s')
if log_level and (log_level == 'INFO' or log_level == 'WARN' or log_level == 'DEBUG') then
-- Ignore log lines, don't reject
return
end
-- Only reject on actual errors
self.spawn_promise:reject(data)
safe_call(opts.on_error, data)
end
end,
}, function(exit_opts)
-- Clear fields if not already cleared by shutdown()
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