A few days ago I finally found the courage to update my setup, moving from a single vimscript file to a nice Lua config. I also took the opportunity to update my tmux config.
For at least 10 years, I’ve been using Vim inside tmux with the same prefix, C-w. When Vim is open, the prefix is forwarded to vim; otherwise, it goes to tmux. Life is good, everything works.
Migration fail #
The curse of updates: this behavior is broken. But only partially, meaning that with the combo tmux+neovim or ssh+tmux+neovim on my laptop, everything is fine, BUT on my desktop with tmux+neovim, it isn’t.
Am I a grumpy old man now? Is this it? Is this the feeling? The world changes and I can’t adapt? Not wanting to change my habits so they work with how the world changes?
Probably. I’m a grumpy old man then. I want Ctrl-w as the prefix for tmux and Neovim. Leave me alone with your editors of the future.
Let’s check versions first, and add a third server as a witness for fun.
| Host | distro | tmux | neovim | ssh | nushell | oh-my-posh |
|---|---|---|---|---|---|---|
| Desktop | manjaro | 3.5a | 0.11.4 | OpenSSH_10.0p2, OpenSSL 3.5.3 16 Sep 2025 | 0.109.0 | 28.1.0 |
| Laptop | arch | 3.6 | 0.11.5 | OpenSSH_10.2p1, OpenSSL 3.6.0 1 Oct 2025 | 0.109.0 | 28.1.0 |
| Server | ubuntu | 3.4 | 0.10.0-dev | OpenSSH_9.6p1 Ubuntu-3ubuntu13.14, OpenSSL 3.0.13 30 Jan 2024 | 0.79.1 | na |
Okay. We already have a small idea of what’s wrong. I’ll have to manually update tmux and nvim. Once I’ve got a working workflow, I’ll add the required versions to my config’s README.
Update on Manjaro #
On AUR, we can find tmux 3.6:
$ yay -Ss tmux
extra/tmux 3.6_a-1 (485.2 KiB 1.1 MiB) (Installed)
Terminal multiplexer
$ tmux -V
tmux 3.6a
Same for neovim:
$ yay -Ss neovim
extra/neovim 0.11.5-1 (6.8 MiB 28.8 MiB) (Installed)
Fork of Vim aiming to improve user experience, plugins, and GUIs
$ nvim --version
NVIM v0.11.5
Build type: RelWithDebInfo
LuaJIT 2.1.1765007043
Run "nvim -V1 -v" for more info
Let’s see if I restart the tmux server now
$ tmux kill-server
Nope. Still not the expected behavior.
Update on Ubuntu #
Let’s check that my config does NOT work on the server.
$ git clone git@github.com:proullon/conf.git
First, nu crashes on startup. Let’s update it.
$ git clone git@github.com:nushell/nushell.git
$ git checkout 0.109.0
$ cargo build --release
$ cp target/release/nu /usr/local/bin/nu
$ nu --version
$ curl -s https://ohmyposh.dev/install.sh | bash -s -- -d ~/.local/bin
(yes, I know, that’s not ideal.)
$ cd /tmp
$ curl -LO https://github.com/neovim/neovim/releases/download/v0.11.5/nvim-linux-arm64.tar.gz
$ tar xzf nvim-linux-arm64.tar.gz
$ sudo mv nvim-linux-arm64 /opt/neovim
$ sudo ln -sf /opt/neovim/bin/nvim /usr/local/bin/nvim
$ cd /tmp
$ curl -LO https://github.com/tmux/tmux/releases/download/3.6/tmux-3.6.tar.gz
$ tar xzf tmux-3.6.tar.gz
$ cd tmux-3.6
$ ./configure
$ make
$ sudo make install
$ tmux -V
tmux 3.6
Let’s recap now:
| Host | distro | tmux | neovim | ssh | nushell | oh-my-posh |
|---|---|---|---|---|---|---|
| Desktop | manjaro | 3.6a | 0.11.6 | OpenSSH_10.2p1, OpenSSL 3.6.0 | 0.109.0 | 28.1.0 |
| Laptop | arch | 3.6 | 0.11.5 | OpenSSH_10.2p1, OpenSSL 3.6.0 | 0.109.0 | 28.1.0 |
| Server | ubuntu | 3.6 | 0.11.5 | OpenSSH_9.6p1 Ubuntu-3ubuntu13.14, OpenSSL 3.0.13 | 0.109.0 | 28.2.2 |
tmux config debug #
So now I don’t have C-w in Neovim on both the desktop and the server. Yay.
Also, C-l doesn’t clear the shell anymore. I now have to do C-w C-l.
So, to summarize:
| Host | C-w l |
C l |
|---|---|---|
| Desktop | last_window | nothing |
| Laptop | split switch | nothing |
| Server | last_window | nothing |
To start, I’ll unbind C-w l last_window since I don’t use it.
unbind l
| Host | C-w l |
C l |
|---|---|---|
| Desktop | nothing | nothing |
| Laptop | split switch | nothing |
| Server | nothing | nothing |
OK, time to debug. Let’s check why the test to detect whether the current pane is Vim isn’t working. Let’s add this shortcut to my config:
bind D run-shell '
cmd=$(ps -o comm= -p #{pane_pid})
tmux display-message "pane_pid=#{pane_pid} cmd=$cmd"
'
Surprise. In a Vim pane -> cmd: nu. In a pane with htop -> nu. OK.
Let’s try to display the child process
bind D run-shell '
children=$(ps --ppid #{pane_pid} -o comm= | tr "\n" " ")
tmux display-message "children=$children"
'
So, I guess, a correct is_vim would rather be:
is_vim='ps --ppid #{pane_pid} -o comm= | tr "\n" " " | grep -iq nvim'
Dumb forwarding #
ANYWAY. That’s not really the problem since is_vim is never evaluated anyway. Let’s change strategy. I’m going back to what I had in my config before:
bind-key 'h' 'send-keys C-w h'
bind-key 'j' 'send-keys C-w j'
bind-key 'k' 'send-keys C-w k'
bind-key 'l' 'send-keys C-w l'
I simply make tmux forward the entire sequence when it receives C-w h/j/k/l. Still no idea why it works at all on my laptop. That’s it. I’ll deal with being smart later. I’ll look at vim-tmux-navigator later: https://github.com/christoomey/vim-tmux-navigator.
vim-tmux-navigator #
OK. Let’s try to be smart and adapt.
I’ll add vim-tmux-navigator to my tmux config:
# tmux.conf
# (automatically handle C-h/j/k/l)
set -g @plugin 'christoomey/vim-tmux-navigator'
and to my Neovim config:
--- lua/plugins/tmux.conf
return {
{
"christoomey/vim-tmux-navigator",
lazy = false,
init = function()
-- Disable default mappings if you want to control everything yourself
-- Set to 1 ONLY if you want to define your own keymaps
-- vim.g.tmux_navigator_no_mappings = 1
end,
},
}
I can now move around between nvim and tmux split seamlessly with C h/j/k/l.
It takes a month to cement a habit? Let’s see.