Skip to content

Commit dfcbe2b

Browse files
authored
feat(provider): add wezterm (#86)
1 parent 0457708 commit dfcbe2b

File tree

3 files changed

+161
-1
lines changed

3 files changed

+161
-1
lines changed

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,25 @@ listen_on unix:/tmp/kitty
161161

162162
</details>
163163

164+
<details>
165+
<summary><a href="https://wezterm.org/">wezterm</a></summary>
166+
167+
```lua
168+
vim.g.opencode_opts = {
169+
provider = {
170+
enabled = "wezterm",
171+
-- these are defaults set by wezterm
172+
wezterm = {
173+
direction = "bottom", -- left/right/top/bottom
174+
top_level = false,
175+
percent = 50,
176+
}
177+
}
178+
}
179+
```
180+
181+
</details>
182+
164183
<details>
165184
<summary><a href="https://github.com/tmux/tmux">tmux</a></summary>
166185

lua/opencode/provider/init.lua

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,14 @@
4040
---Default order:
4141
--- - `"snacks"` if `snacks.terminal` is available and enabled
4242
--- - `"kitty"` if in a `kitty` session with remote control enabled
43+
--- - `"wezterm"` if in a `wezterm` window
4344
--- - `"tmux"` if in a `tmux` session
4445
--- - `false`
45-
---@field enabled? "snacks"|"kitty"|"tmux"|false
46+
---@field enabled? "snacks"|"kitty"|"wezterm"|"tmux"|false
4647
---
4748
---@field snacks? opencode.provider.snacks.Opts
4849
---@field kitty? opencode.provider.kitty.Opts
50+
---@field wezterm? opencode.provider.wezterm.Opts
4951
---@field tmux? opencode.provider.tmux.Opts
5052

5153
local M = {}
@@ -71,6 +73,7 @@ function M.list()
7173
return {
7274
require("opencode.provider.snacks"),
7375
require("opencode.provider.kitty"),
76+
require("opencode.provider.wezterm"),
7477
require("opencode.provider.tmux"),
7578
}
7679
end

lua/opencode/provider/wezterm.lua

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
---Provide `opencode` in a [`wezterm`](https://wezterm.org/index.html) pane in the current window.
2+
---@class opencode.provider.Wezterm : opencode.Provider
3+
---
4+
---@field opts opencode.provider.wezterm.Opts
5+
---@field pane_id? string The `wezterm` pane ID where `opencode` is running (internal use only).
6+
local Wezterm = {}
7+
Wezterm.__index = Wezterm
8+
Wezterm.name = "wezterm"
9+
10+
---`wezterm` options for creating the pane.
11+
---Strictly mimics the options available in the `wezterm cli split-pane --help` command
12+
---@class opencode.provider.wezterm.Opts
13+
---
14+
---Direction in which the pane that runs opencode is spawn, defaults to bottom
15+
---@field direction? "left" | "top" | "right" | "bottom"
16+
---
17+
---The number of cells that the new split should have expressed as a percentage of the available
18+
---space, default is 50%
19+
---@field percent? number
20+
---
21+
---Rather than splitting the active pane, split the entire window
22+
---@field top_level? boolean
23+
---
24+
---@param opts? opencode.provider.wezterm.Opts
25+
---@return opencode.provider.Wezterm
26+
function Wezterm.new(opts)
27+
local self = setmetatable({}, Wezterm)
28+
self.opts = opts or {}
29+
self.pane_id = nil
30+
return self
31+
end
32+
33+
local function focus_pane(pane_id)
34+
vim.fn.system(string.format("wezterm cli activate-pane --pane-id %d", pane_id))
35+
end
36+
37+
---Check if `wezterm` is running in current terminal.
38+
function Wezterm.health()
39+
if vim.fn.executable("wezterm") ~= 1 then
40+
return "`wezterm` executable not found in `$PATH`.",
41+
{
42+
"Install `wezterm` and ensure it's in your `$PATH`.",
43+
}
44+
end
45+
46+
if not vim.env.WEZTERM_PANE then
47+
return "Not running inside a `wezterm` window.", {
48+
"Launch Neovim inside a `wezterm` window.",
49+
}
50+
end
51+
52+
return true
53+
end
54+
55+
---Get the `wezterm` pane ID where `opencode` is running.
56+
---@return string|nil pane_id
57+
function Wezterm:get_pane_id()
58+
local ok = self.health()
59+
if ok ~= true then
60+
error(ok)
61+
end
62+
63+
local base_cmd = self.cmd:match("^%S+") or self.cmd
64+
local result = vim.fn.system(
65+
string.format("wezterm cli list --format json 2>&1 | jq -r '.[] | select(.title == \"%s\") | .pane_id'", base_cmd)
66+
)
67+
if result and result ~= "" and not result:match("error") then
68+
self.pane_id = result:match("^%d+")
69+
else
70+
self.pane_id = nil
71+
end
72+
73+
return self.pane_id
74+
end
75+
76+
---Create or kill the `opencode` pane.
77+
function Wezterm:toggle()
78+
local pane_id = self:get_pane_id()
79+
if pane_id then
80+
self:stop()
81+
else
82+
self:start()
83+
end
84+
end
85+
86+
---Start `opencode` in pane.
87+
function Wezterm:start()
88+
local pane_id = self:get_pane_id()
89+
if not pane_id then
90+
local cmd_parts = { "wezterm", "cli", "split-pane" }
91+
92+
if self.opts.direction then
93+
table.insert(cmd_parts, "--" .. self.opts.direction)
94+
end
95+
96+
if self.opts.percent then
97+
table.insert(cmd_parts, "--percent")
98+
table.insert(cmd_parts, tostring(self.opts.percent))
99+
end
100+
101+
if self.opts.top_level then
102+
table.insert(cmd_parts, "--top-level")
103+
end
104+
105+
table.insert(cmd_parts, "--")
106+
table.insert(cmd_parts, self.cmd)
107+
108+
local result = vim.fn.system(table.concat(cmd_parts, " "))
109+
focus_pane(vim.env.WEZTERM_PANE)
110+
111+
self.pane_id = result:match("^%d+")
112+
end
113+
end
114+
115+
---Kill the `opencode` pane.
116+
function Wezterm:stop()
117+
local pane_id = self:get_pane_id()
118+
if pane_id then
119+
vim.fn.system(string.format("wezterm cli kill-pane --pane-id %d", pane_id))
120+
self.pane_id = nil
121+
end
122+
end
123+
124+
---Focus the `opencode` pane
125+
--- INFO: This, at the moment, introduces unexpected focusing behaviors with sending `command`s
126+
--- therefore its disabled.
127+
function Wezterm:show() end
128+
-- function Wezterm:show()
129+
-- local pane_id = self:get_pane_id()
130+
-- if not pane_id then
131+
-- vim.notify("No opencode instance is currently running", vim.log.levels.WARN, { title = "opencode" })
132+
-- return
133+
-- end
134+
--
135+
-- focus_pane(pane_id)
136+
-- end
137+
138+
return Wezterm

0 commit comments

Comments
 (0)