Skip to content

Commit 4a10430

Browse files
authored
feat: add wezterm navigation (#18)
This PR attempts to add wezterm navigation to the plugin in addition to tmux navigation. And also introduces - a general interface to integrate different multiplexers. This interface needs two methods: `:zoomed()` and `:navigate()` - `mux` config option to automatically detects and use the correct multiplexer implementation. It defaults to `auto` which uses the default priority/fallback behavior, which is tmux, followed by wezterm. If nothing is found, it'll fallback to neovim window navigation. It can also be set to lua class which implements general interface.
1 parent 2ef04f4 commit 4a10430

File tree

7 files changed

+204
-112
lines changed

7 files changed

+204
-112
lines changed

README.md

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
<h1 align='center'>Navigator.nvim</h1>
2-
<p align="center"><sup>✨ Smoothly navigate between splits and panes ✨</sup></p>
2+
<p align="center"><sup>✨ Smoothly navigate between neovim and multiplexer ✨</sup></p>
33

44
![Navigator](https://user-images.githubusercontent.com/24727447/157040356-1f44323a-c7b6-4955-8207-5e6cade08c9e.gif "Navigating to the moon")
55

6+
This plugin provides a set of [functions](#lua-api) and [commands](#commands) that allows you to seemlessly navigate between neovim and different [terminal multiplexers](#multiplexers).
7+
68
### 🚀 Installation
79

810
#### Neovim
@@ -27,68 +29,74 @@ Plug 'numToStr/Navigator.nvim'
2729
lua require('Navigator').setup()
2830
```
2931

30-
#### Tmux
31-
32-
This plugin doesn't provides any configuration for `tmux`. You can read [here](https://github.com/christoomey/vim-tmux-navigator#tmux) to how to setup your tmux.
33-
34-
Or, you can use [tmux-tilish](https://github.com/jabirali/tmux-tilish) which is an excellent tmux plugin.
35-
3632
### ⚒️ Setup
3733

34+
#### Neovim
35+
3836
```lua
39-
-- Configuration
4037
require('Navigator').setup()
38+
```
39+
40+
- Keybindings
41+
42+
> **Note** - This plugin doesn't provide any keybindings by default, feel free to use (and modify) the following or create your own keybindings.
4143
42-
-- Keybindings
43-
vim.keymap.set('n', "<A-h>", '<CMD>NavigatorLeft<CR>')
44-
vim.keymap.set('n', "<A-l>", '<CMD>NavigatorRight<CR>')
45-
vim.keymap.set('n', "<A-k>", '<CMD>NavigatorUp<CR>')
46-
vim.keymap.set('n', "<A-j>", '<CMD>NavigatorDown<CR>')
47-
vim.keymap.set('n', "<A-p>", '<CMD>NavigatorPrevious<CR>')
44+
```lua
45+
vim.keymap.set('n', '<A-h>', '<CMD>NavigatorLeft<CR>')
46+
vim.keymap.set('n', '<A-l>', '<CMD>NavigatorRight<CR>')
47+
vim.keymap.set('n', '<A-k>', '<CMD>NavigatorUp<CR>')
48+
vim.keymap.set('n', '<A-j>', '<CMD>NavigatorDown<CR>')
49+
vim.keymap.set('n', '<A-p>', '<CMD>NavigatorPrevious<CR>')
4850
```
4951

52+
#### Multiplexer(s)
53+
54+
> **Note** - This plugin doesn't provide any configuration for multiplexers, feel free to use (and modify) the snippet for multiplexer of your choice by following the links below.
55+
56+
- [Tmux](https://github.com/numToStr/Navigator.nvim/wiki/Tmux-Integration)
57+
- [Wezterm](https://github.com/numToStr/Navigator.nvim/wiki/WezTerm-Integration)
58+
5059
#### Configuration (optional)
5160

5261
Following options can be given when calling `setup({config})`. Below is the default configuration
5362

5463
```lua
5564
{
56-
-- When you want to save the modified buffers when moving to tmux
65+
-- Save modified buffer(s) when moving to mux
5766
-- nil - Don't save (default)
5867
-- 'current' - Only save the current modified buffer
5968
-- 'all' - Save all the buffers
6069
auto_save = nil,
6170

62-
-- Disable navigation when tmux is zoomed in
71+
-- Disable navigation when the current mux pane is zoomed in
6372
disable_on_zoom = false
73+
74+
-- Multiplexer to use
75+
-- 'auto' - Chooses mux based on priority (default)
76+
-- table - Custom mux to use
77+
mux = 'auto'
6478
}
6579
```
6680

81+
> Read: [How to create and integrate custom multiplexer?](https://github.com/numToStr/Navigator.nvim/wiki/Custom-Multiplexer)
82+
6783
### 🔥 Usage
6884

6985
#### Commands
7086

7187
- `NavigatorLeft` - Go to left split/pane
72-
7388
- `NavigatorRight` - Go to right split/pane
74-
7589
- `NavigatorUp` - Go to upper split/pane
76-
7790
- `NavigatorDown` - Go to down split/pane
78-
7991
- `NavigatorPrevious` - Go to previous split/pane
8092

8193
#### Lua API
8294

8395
```lua
8496
require('Navigator').left()
85-
8697
require('Navigator').right()
87-
8898
require('Navigator').up()
89-
9099
require('Navigator').down()
91-
92100
require('Navigator').previous()
93101
```
94102

lua/Navigator/mux/tmux.lua

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---@class Tmux: Vi
2+
---@field private pane string
3+
---@field private direction table<Direction, string>
4+
---@field private execute fun(arg: string): unknown
5+
local Tmux = require('Navigator.mux.vi'):new()
6+
7+
---Creates a new Tmux navigator instance
8+
---@return Tmux
9+
function Tmux:new()
10+
local instance = os.getenv('TMUX')
11+
local pane = os.getenv('TMUX_PANE')
12+
13+
assert(instance and pane, '[Navigator] Tmux is not running!')
14+
15+
local socket = string.match(instance, '^(.-),')
16+
local cmd = instance:find('^.*tmate') and 'tmate' or 'tmux'
17+
18+
---@type Tmux
19+
local state = {
20+
pane = pane,
21+
execute = function(arg)
22+
return require('Navigator.utils').execute(string.format('%s -S %s %s', cmd, socket, arg))
23+
end,
24+
direction = { p = 'l', h = 'L', k = 'U', l = 'R', j = 'D' },
25+
}
26+
self.__index = self
27+
return setmetatable(state, self)
28+
end
29+
30+
---Checks if tmux is zoomed in
31+
---@return boolean
32+
function Tmux:zoomed()
33+
return self.execute("display-message -p '#{window_zoomed_flag}'") == '1'
34+
end
35+
36+
---Switch pane in tmux
37+
---@param direction Direction
38+
---@return Tmux
39+
function Tmux:navigate(direction)
40+
self.execute(string.format("select-pane -t '%s' -%s", self.pane, self.direction[direction]))
41+
return self
42+
end
43+
44+
return Tmux

lua/Navigator/mux/vi.lua

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---@class Vi
2+
local Vi = {}
3+
4+
---Creates new Neovim navigator instance
5+
---NOTE: This can be used as a base for other navigators
6+
---@return Vi
7+
function Vi:new()
8+
self.__index = self
9+
return setmetatable({}, self)
10+
end
11+
12+
---Checks whether neovim is maximized
13+
---NOTE: This is only useful for `tmux`, for other mux(s) return false would suffice
14+
---@return boolean
15+
function Vi.zoomed()
16+
return false
17+
end
18+
19+
---Navigate inside neovim
20+
---@param direction Direction
21+
---@return Vi
22+
function Vi:navigate(direction)
23+
vim.api.nvim_command('wincmd ' .. direction)
24+
return self
25+
end
26+
27+
return Vi

lua/Navigator/mux/wezterm.lua

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---@class WezTerm: Vi
2+
---@field private direction table<Direction, string>
3+
---@field private execute fun(arg: string): unknown
4+
local WezTerm = require('Navigator.mux.vi'):new()
5+
6+
---Creates a new WezTerm navigator instance
7+
---@return WezTerm
8+
function WezTerm:new()
9+
assert(os.getenv('TERM_PROGRAM') == 'WezTerm', '[Navigator] WezTerm is not running!')
10+
11+
local U = require('Navigator.utils')
12+
local suffix = U.suffix()
13+
14+
---@type WezTerm
15+
local state = {
16+
execute = function(arg)
17+
return U.execute(string.format('wezterm%s cli %s', suffix, arg))
18+
end,
19+
direction = { p = 'Prev', h = 'Left', k = 'Up', l = 'Right', j = 'Down' },
20+
}
21+
self.__index = self
22+
return setmetatable(state, self)
23+
end
24+
25+
---Switch pane in wezterm
26+
---@param direction Direction
27+
---@return WezTerm
28+
function WezTerm:navigate(direction)
29+
self.execute(string.format('activate-pane-direction %s', self.direction[direction]))
30+
return self
31+
end
32+
33+
return WezTerm

lua/Navigator/navigate.lua

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,57 @@
1-
local tmux = require('Navigator.tmux')
21
local A = vim.api
32
local cmd = A.nvim_command
43

4+
---Loads mux
5+
---@return Vi
6+
local function load_mux()
7+
local ok_tmux, tmux = pcall(function()
8+
return require('Navigator.mux.tmux'):new()
9+
end)
10+
if ok_tmux then
11+
return tmux
12+
end
13+
local ok_wezterm, wezterm = pcall(function()
14+
return require('Navigator.mux.wezterm'):new()
15+
end)
16+
if ok_wezterm then
17+
return wezterm
18+
end
19+
return require('Navigator.mux.vi'):new()
20+
end
21+
22+
---@alias Direction 'p'|'h'|'k'|'l'|'j'
23+
524
---@class Config
6-
---@field auto_save '"current"'|'"all"' When you want to save the modified buffers when moving to tmux
7-
---@field disable_on_zoom boolean Disable navigation when tmux is zoomed in
25+
---@field auto_save? '"current"'|'"all"' Save modified buffer(s) when moving to mux
26+
---@field disable_on_zoom boolean Disable navigation when the current mux pane is zoomed in
27+
---@field mux 'auto'|Vi Multiplexer to use
828

9-
---Just some state and defaults
29+
---State and defaults
1030
---@class Nav
1131
---@field last_pane boolean
12-
---@field config Config
32+
---@field config? Config
1333
local N = {
1434
last_pane = false,
1535
config = nil,
1636
}
1737

18-
local function wincmd(way)
19-
cmd(('wincmd %s'):format(way))
20-
end
21-
2238
---For setting up the plugin with the user provided options
2339
---@param opts Config
2440
function N.setup(opts)
25-
N.config = {
41+
local config = {
2642
disable_on_zoom = false,
2743
auto_save = nil,
44+
mux = 'auto',
2845
}
2946

3047
if opts ~= nil then
31-
N.config = vim.tbl_extend('keep', opts, N.config)
48+
N.config = vim.tbl_extend('keep', opts, config)
49+
else
50+
N.config = config
51+
end
52+
53+
if N.config.mux == 'auto' then
54+
N.config.mux = load_mux()
3255
end
3356

3457
A.nvim_create_autocmd('WinEnter', {
@@ -39,45 +62,37 @@ function N.setup(opts)
3962
})
4063
end
4164

42-
---Checks whether we need to move to the nearby tmux pane
65+
---Checks whether we need to move to the nearby mux pane
4366
---@param at_edge boolean
4467
---@return boolean
45-
function N.back_to_tmux(at_edge)
46-
if N.config.disable_on_zoom and tmux.is_zoomed() then
68+
function N.back_to_mux(at_edge)
69+
if N.config.disable_on_zoom and N.config.mux:zoomed() then
4770
return false
4871
end
49-
5072
return N.last_pane or at_edge
5173
end
5274

53-
---For smoothly navigating through neovim splits and tmux panes
75+
---For smoothly navigating through neovim splits and mux panes
5476
---@param direction string
5577
function N.navigate(direction)
56-
-- For moments when you have this plugin installed
57-
-- but for some reason you didn't bother to install tmux
58-
if not tmux.is_tmux then
59-
return wincmd(direction)
60-
end
61-
6278
-- window id before navigation
6379
local cur_win = A.nvim_get_current_win()
64-
local tmux_last_pane = direction == 'p' and N.last_pane
6580

66-
if not tmux_last_pane then
67-
wincmd(direction)
81+
local mux_last_pane = direction == 'p' and N.last_pane
82+
if not mux_last_pane then
83+
cmd('wincmd ' .. direction)
6884
end
6985

7086
-- After navigation, if the old window and new window matches
7187
local at_edge = cur_win == A.nvim_get_current_win()
7288

7389
-- then we can assume that we hit the edge
74-
-- there is tmux pane besided the edge
75-
-- So we can navigate to the tmux pane
76-
if N.back_to_tmux(at_edge) then
77-
tmux.change_pane(direction)
90+
-- there is mux pane besided the edge
91+
-- So we can navigate to the mux pane
92+
if N.back_to_mux(at_edge) then
93+
N.config.mux:navigate(direction)
7894

7995
local save = N.config.auto_save
80-
8196
if save == 'current' then
8297
cmd('update')
8398
elseif save == 'all' then

0 commit comments

Comments
 (0)