Skip to content

Commit d41da26

Browse files
committed
fix(snacks): track job PID to ensure process cleanup on exit
The snacks.terminal.list() is empty during VimLeavePre because snacks cleans up its terminal tracking before that event fires. However, the underlying process is not killed. This fix: - Captures the job ID and PID when the terminal starts - Uses the stored PID to kill the process directly during stop() - Falls back to snacks.terminal.list() for normal operation - Uses both SIGTERM and SIGKILL to ensure the process is terminated
1 parent a86366f commit d41da26

1 file changed

Lines changed: 30 additions & 3 deletions

File tree

lua/opencode/provider/snacks.lua

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
---@class opencode.provider.Snacks : opencode.Provider
55
---
66
---@field opts snacks.terminal.Opts
7+
---@field private _job_id number|nil
8+
---@field private _pid number|nil
79
local Snacks = {}
810
Snacks.__index = Snacks
911
Snacks.name = "snacks"
@@ -15,6 +17,8 @@ Snacks.name = "snacks"
1517
function Snacks.new(opts)
1618
local self = setmetatable({}, Snacks)
1719
self.opts = opts or {}
20+
self._job_id = nil
21+
self._pid = nil
1822
return self
1923
end
2024

@@ -49,23 +53,46 @@ end
4953
function Snacks:start()
5054
if not self:get() then
5155
require("snacks.terminal").open(self.cmd, self.opts)
56+
-- Capture the job ID and PID after terminal opens
57+
vim.defer_fn(function()
58+
local win = self:get()
59+
if win and win.buf and vim.api.nvim_buf_is_valid(win.buf) then
60+
self._job_id = vim.b[win.buf].terminal_job_id
61+
if self._job_id then
62+
self._pid = vim.fn.jobpid(self._job_id)
63+
end
64+
end
65+
end, 100)
5266
end
5367
end
5468

5569
function Snacks:stop()
56-
-- Iterate all snacks terminals to find ours, since `self:get()` uses cwd
57-
-- in the terminal ID and may not find it if cwd changed.
70+
-- Try to stop via the tracked job ID (more reliable than snacks.terminal.list()
71+
-- which may be empty during VimLeavePre)
72+
if self._job_id then
73+
local ok, _ = pcall(vim.fn.jobstop, self._job_id)
74+
-- If jobstop failed, fall back to kill
75+
if not ok and self._pid then
76+
os.execute("kill " .. tostring(self._pid) .. " 2>/dev/null")
77+
os.execute("kill -9 " .. tostring(self._pid) .. " 2>/dev/null")
78+
end
79+
end
80+
81+
-- Also try via snacks list to close the window
5882
for _, win in pairs(require("snacks.terminal").list()) do
5983
if win.cmd == self.cmd then
6084
if win.buf and vim.api.nvim_buf_is_valid(win.buf) then
6185
local job_id = vim.b[win.buf].terminal_job_id
6286
if job_id then
63-
vim.fn.jobstop(job_id)
87+
pcall(vim.fn.jobstop, job_id)
6488
end
6589
end
6690
win:close()
6791
end
6892
end
93+
94+
self._job_id = nil
95+
self._pid = nil
6996
end
7097

7198
return Snacks

0 commit comments

Comments
 (0)