-
Notifications
You must be signed in to change notification settings - Fork 123
Expand file tree
/
Copy pathinit.lua
More file actions
222 lines (195 loc) · 5.92 KB
/
init.lua
File metadata and controls
222 lines (195 loc) · 5.92 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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
local Config = require("sidekick.config")
local Util = require("sidekick.util")
local M = {}
M.backends = {} ---@type table<string,sidekick.cli.Session>
M.did_setup = false
M._attached = {} ---@type table<string,sidekick.cli.Session>
---@class sidekick.cli.session.State
---@field id string unique id of the running tool (typically pid of tool)
---@field cwd string
---@field tool sidekick.cli.Tool|string
---@field pids? integer[] list of pids associated with this session
---@field backend? string
---@field started? boolean
---@field external? boolean external sessions won't be opened in a terminal
---@field parent? sidekick.cli.Session
---@field mux_session? string
---@field mux_backend? string
---@alias sidekick.cli.session.Opts sidekick.cli.session.State|{cwd?:string,id?:string}
---@class sidekick.cli.Session: sidekick.cli.session.State
---@field sid string unique id based on tool and cwd
---@field tool sidekick.cli.Tool
---@field backend string
---@field dump? fun(self:sidekick.cli.Session):string?
local B = {}
B.__index = B
B.priority = 0
--- Send text to the session
---@param text string
function B:send(text)
error("Backend:send() not implemented")
end
--- Initialize the session backend (optional hook)
function B:init() end
--- Submit the current input to the session
function B:submit()
error("Backend:submit() not implemented")
end
--- Attach to an existing session
--- If the backend returns a Cmd, a new terminal session will be spawned
---@return sidekick.cli.terminal.Cmd?
function B:attach() end
--- Detach from an existing session
function B:detach() end
--- Focus the session
function B:focus() end
--- Whether the session currently has user focus
---@return boolean
function B:is_focused()
return false
end
--- Move focus away from the session
function B:blur() end
--- Start a new session
--- If the backend returns a Cmd, a new terminal session will be spawned
---@return sidekick.cli.terminal.Cmd?
function B:start()
error("Backend:start() not implemented")
end
--- Check if the session is still running
--- @return boolean
function B:is_running()
error("Backend:is_running() not implemented")
end
function B:is_attached()
return M._attached[self.id] ~= nil
end
--- List all active sessions for this backend
---@return sidekick.cli.session.State[]
function B.sessions()
error("Backend:sessions() not implemented")
end
---@param state sidekick.cli.session.Opts
function M.new(state)
local tool = state.tool
tool = type(tool) == "string" and Config.get_tool(tool) or tool --[[@as sidekick.cli.Tool]]
local backend = state.backend or (Config.cli.mux.enabled and Config.cli.mux.backend or "terminal")
local super = assert(M.backends[backend], "unknown backend: " .. backend)
local meta = getmetatable(state)
local self = setmetatable(state, super) --[[@as sidekick.cli.Session]]
self.tool = tool
self.cwd = M.cwd(state)
-- self.cmd = state.cmd or { cmd = tool.cmd, env = tool.env }
self.backend = backend
self.sid = M.sid({ tool = tool.name, cwd = self.cwd })
self.id = self.id or self.sid
if meta ~= super and self.init then
self:init()
end
return self
end
---@param opts? {cwd?:string}
function M.cwd(opts)
return vim.fs.normalize(vim.fn.fnamemodify(opts and opts.cwd or vim.fn.getcwd(0), ":p"))
end
---@param opts {tool:string, cwd?:string}
function M.sid(opts)
local tool = assert(opts and opts.tool, "missing tool")
local cwd = M.cwd(opts)
return ("%s %s"):format(tool, vim.fn.sha256(cwd):sub(1, 16 - #tool))
end
---@param name string
---@param backend sidekick.cli.Session
function M.register(name, backend)
setmetatable(backend, B)
backend.backend = name
M.backends[name] = backend
end
function M.setup()
if M.did_setup then
return
end
M.did_setup = true
Config.tools() -- load tools, since they may register session backends
local session_backends = { tmux = "sidekick.cli.session.tmux", zellij = "sidekick.cli.session.zellij" }
for name, mod in pairs(session_backends) do
if vim.fn.executable(name) == 1 then
M.register(name, require(mod))
end
end
M.register("terminal", require("sidekick.cli.terminal"))
end
function M.sessions()
M.setup()
local ret = {} ---@type sidekick.cli.Session[]
local ids = {} ---@type table<string,boolean>
for name, backend in pairs(M.backends) do
for _, s in pairs(backend:sessions()) do
s.backend = name
s.started = true
ret[#ret + 1] = M.new(s)
assert(not ids[s.id], "duplicate session id: " .. s.id)
ids[s.id] = true
if M._attached[s.id] then
M._attached[s.id] = ret[#ret] -- update to latest session instance
end
end
end
for id in pairs(M._attached) do
if not ids[id] then -- session is no longer running
M.detach(M._attached[id])
end
end
return ret
end
---@param session sidekick.cli.Session
function M.detach(session)
if M._attached[session.id] then
M._attached[session.id] = nil
session:detach()
vim.schedule(function()
Util.emit("SidekickCliDetach", { id = session.id })
end)
end
return session
end
---@param session sidekick.cli.Session
function M.attach(session)
if M._attached[session.id] then
return session
end
---@type sidekick.cli.terminal.Cmd?
local cmd
if session.started then
cmd = session:attach()
else
cmd = session:start()
end
if cmd then
session = M.new({
tool = session.tool:clone({ cmd = cmd.cmd, env = cmd.env }),
cwd = session.cwd,
id = "terminal: " .. session.sid,
backend = "terminal",
mux_backend = session.backend,
mux_session = session.mux_session,
parent = session,
})
session:start()
end
M._attached[session.id] = session
Util.emit("SidekickCliAttach", { id = session.id })
return session
end
function M.attached()
local ret = {} ---@type table<string,sidekick.cli.Session>
for id, s in pairs(M._attached) do
if s:is_running() then
ret[id] = s
else
M.detach(s)
end
end
return ret
end
return M