Skip to content

Commit 8dcf8e7

Browse files
authored
Merge pull request #307 from mrjones2014/mrj/111/zellij
feat(mux): Experimental Zellij support
2 parents f301508 + 84bbd8b commit 8dcf8e7

File tree

4 files changed

+177
-1
lines changed

4 files changed

+177
-1
lines changed

README.md

+81
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ multiplexer split panes. See [Multiplexer Integrations](#multiplexer-integration
1919
- [Usage](#usage)
2020
- [Multiplexer Integrations](#multiplexer-integrations)
2121
- [Tmux](#tmux)
22+
- [Zellij](#zellij)
2223
- [Wezterm](#wezterm)
2324
- [Kitty](#kitty)
2425
- [Credits](#credits)
@@ -389,6 +390,86 @@ window:
389390
option_as_alt: "Both" # or "OnlyLeft" or "OnlyRight" if you prefer
390391
```
391392
393+
#### Zellij
394+
395+
> [!NOTE]
396+
> Zellij support is currently experimental. Please try it out and report any issues! \
397+
> Resizing by a specific amount from Neovim and presetting new split size is unsupported.
398+
399+
Add the following keymap config to your Zellij KDL config, adjusting the keys you wish to use as necessary.
400+
No configuration should be needed on the Neovim side.
401+
402+
```kdl
403+
keybinds {
404+
shared_except "locked" {
405+
bind "Ctrl h" {
406+
MessagePlugin "https://github.com/hiasr/vim-zellij-navigator/releases/download/0.2.1/vim-zellij-navigator.wasm" {
407+
name "move_focus_or_tab";
408+
payload "left";
409+
move_mod "ctrl";
410+
resize_mod "alt";
411+
};
412+
}
413+
bind "Ctrl j" {
414+
MessagePlugin "https://github.com/hiasr/vim-zellij-navigator/releases/download/0.2.1/vim-zellij-navigator.wasm" {
415+
name "move_focus";
416+
payload "down";
417+
move_mod "ctrl";
418+
resize_mod "alt";
419+
};
420+
}
421+
bind "Ctrl k" {
422+
MessagePlugin "https://github.com/hiasr/vim-zellij-navigator/releases/download/0.2.1/vim-zellij-navigator.wasm" {
423+
name "move_focus";
424+
payload "up";
425+
move_mod "ctrl";
426+
resize_mod "alt";
427+
};
428+
}
429+
bind "Ctrl l" {
430+
MessagePlugin "https://github.com/hiasr/vim-zellij-navigator/releases/download/0.2.1/vim-zellij-navigator.wasm" {
431+
name "move_focus_or_tab";
432+
payload "right";
433+
move_mod "ctrl";
434+
resize_mod "alt";
435+
};
436+
}
437+
bind "Alt h" {
438+
MessagePlugin "https://github.com/hiasr/vim-zellij-navigator/releases/download/0.2.1/vim-zellij-navigator.wasm" {
439+
name "resize";
440+
payload "left";
441+
move_mod "ctrl";
442+
resize_mod "alt";
443+
};
444+
}
445+
bind "Alt j" {
446+
MessagePlugin "https://github.com/hiasr/vim-zellij-navigator/releases/download/0.2.1/vim-zellij-navigator.wasm" {
447+
name "resize";
448+
payload "down";
449+
move_mod "ctrl";
450+
resize_mod "alt";
451+
};
452+
}
453+
bind "Alt k" {
454+
MessagePlugin "https://github.com/hiasr/vim-zellij-navigator/releases/download/0.2.1/vim-zellij-navigator.wasm" {
455+
name "resize";
456+
payload "up";
457+
move_mod "ctrl";
458+
resize_mod "alt";
459+
};
460+
}
461+
bind "Alt l" {
462+
MessagePlugin "https://github.com/hiasr/vim-zellij-navigator/releases/download/0.2.1/vim-zellij-navigator.wasm" {
463+
name "resize";
464+
payload "right";
465+
move_mod "ctrl";
466+
resize_mod "alt";
467+
};
468+
}
469+
}
470+
}
471+
```
472+
392473
#### Wezterm
393474
394475
> [!NOTE]

lua/smart-splits/config.lua

+2
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ function M.set_default_multiplexer()
113113
config.multiplexer_integration = Multiplexer.tmux
114114
elseif term == 'wezterm' then
115115
config.multiplexer_integration = Multiplexer.wezterm
116+
elseif vim.env.ZELLIJ ~= nil then
117+
config.multiplexer_integration = 'zellij'
116118
elseif vim.env.KITTY_LISTEN_ON ~= nil then
117119
-- Kitty doesn't use $TERM_PROGRAM, and also requires remote control enabled anyway
118120
config.multiplexer_integration = Multiplexer.kitty

lua/smart-splits/mux/zellij.lua

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
local Direction = require('smart-splits.types').Direction
2+
local log = require('smart-splits.log')
3+
4+
local function zellij_exec(cmd)
5+
local command = vim.deepcopy(cmd)
6+
table.insert(command, 1, 'zellij')
7+
local result = vim.fn.systemlist(command)
8+
return result
9+
end
10+
11+
---@type SmartSplitsMultiplexer
12+
local M = {} ---@diagnostic disable-line: missing-fields
13+
14+
M.type = 'zellij'
15+
16+
function M.current_pane_id()
17+
local output = zellij_exec({ 'action', 'list-clients' })
18+
if not output[2] then
19+
return nil
20+
end
21+
local pane_id = string.match(output[2], '%S+%s+%w+_(%d+)')
22+
return pane_id
23+
end
24+
25+
function M.current_pane_at_edge()
26+
local pane_id = M.current_pane_id()
27+
if pane_id == nil then
28+
log.warn('could not get zeillij pane id')
29+
return false
30+
end
31+
zellij_exec({ 'action', 'move-focus', Direction.left })
32+
local new_pane_id = M.current_pane_id()
33+
34+
if new_pane_id == nil then
35+
log.warn('could not get zeillij pane id')
36+
return false
37+
end
38+
39+
return pane_id == new_pane_id
40+
end
41+
42+
-- amount is not supported on zellij
43+
function M.resize_pane(direction, _amount) ---@diagnostic disable-line: unused-local
44+
if not M.is_in_session() then
45+
return false
46+
end
47+
48+
local ok, _ = pcall(zellij_exec, { 'action', 'resize', 'increase', direction })
49+
return ok
50+
end
51+
52+
function M.is_in_session()
53+
return M.current_pane_id() ~= nil
54+
end
55+
56+
function M.current_pane_is_zoomed()
57+
return false
58+
end
59+
60+
function M.next_pane(direction)
61+
if not M.is_in_session() then
62+
return false
63+
end
64+
local ok, _ = pcall(zellij_exec, { 'action', 'move-focus', direction })
65+
return ok
66+
end
67+
68+
-- size is not supported on zellij
69+
function M.split_pane(direction, _size) ---@diagnostic disable-line: unused-local
70+
-- zellij only splits right and down; for the others,
71+
-- we must split right and down then swap the panes
72+
local args = { 'action', 'new-pane' }
73+
local need_swap
74+
if direction == Direction.left then
75+
table.insert(args, 'right')
76+
need_swap = 'right'
77+
elseif direction == Direction.up then
78+
table.insert(args, 'down')
79+
need_swap = 'down'
80+
else
81+
table.insert(args, direction)
82+
end
83+
local split_ok, _ = pcall(zellij_exec, args)
84+
if need_swap ~= nil then
85+
local swap_ok = pcall(zellij_exec, { 'action', 'move-pane', need_swap })
86+
return split_ok and swap_ok
87+
end
88+
return split_ok
89+
end
90+
91+
return M

lua/smart-splits/types.lua

+3-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
---
1717
---@alias SmartSplitsFloatWinBehavior 'previous'|'mux'
1818

19-
---@alias SmartSplitsMultiplexerType 'tmux'|'wezterm'|'kitty'
19+
---@alias SmartSplitsMultiplexerType 'tmux'|'wezterm'|'kitty'|'zellij'
2020

2121
---@class SmartSplitsContext
2222
---@field mux SmartSplitsMultiplexer|nil Multiplexer API, if one is currently in use
@@ -55,6 +55,8 @@ local M = {
5555
wezterm = 'wezterm',
5656
---@type SmartSplitsMultiplexerType
5757
kitty = 'kitty',
58+
---@type SmartSplitsMultiplexerType
59+
zellij = 'zellij',
5860
},
5961
}
6062

0 commit comments

Comments
 (0)