Switching Zellij and Vim panes with ease
I'm trying out Zellij after it announced non-colliding keybindings in version 0.41.0. Currently, I'm using iTerm and MacVim as two separate apps, but I'd like to try running everything in the terminal and switch to NeoVim.
I never got into tmux, but I know there are plugins to have you smoothly switch between tmux and Vim panes like they are from the same application. I want this behavior in Zellij too without having to switch Zellij modes.
As I got started with Zellij, I found a couple of different projects with different ideas on how to accomplish this.
For the default Zellij colliding keybindings, zellij-autolock looks like the way to go. It automatically switches between the "normal" and "locked" modes when entering and leaving Vim. But, as the author pointed out, it is not tested with non-colliding keybindings.
I couldn't find a solution that worked for me using the Zellij non-colliding keybindings in combination with Vim, while also allowing other applications that use the ctrl+hjkl
keybindings to work, but please let me know if I missed one!
What I ended up doing to make it work for me is the following:
Install a Neovim Zellij plugin
Add the zellij-nav.nvim plugin * to your Vim config that will send commands to Zellij when you try to press one of the ctrl+hjkl
keybindings and are on the edge of a Vim window.
*: There may be other plugins that work, too; this is the one that worked for me.
I'm using vim-plug for package management. There are other Vim package managers listed on the plugin's README.
" ~/.vimrc
call plug#begin('~/.vim/plugged')
" Other plugins here
Plug 'swaits/zellij-nav.nvim'
call plug#end()
lua require("zellij-nav").setup()
nnoremap <c-h> <cmd>ZellijNavigateLeft<cr>
nnoremap <c-j> <cmd>ZellijNavigateDown<cr>
nnoremap <c-k> <cmd>ZellijNavigateUp<cr>
nnoremap <c-l> <cmd>ZellijNavigateRight<cr>
Then quit Vim, restart it, and run the :PlugInstall
command.
Install the Zellij plugin
Next up, we'll install a Zellij plugin. I started using the vim-zellij-navigator. It works great for switching between Vim and Zellij.
However, I ran into issues with other applications like fzf, triggered by running fzf
or pressing ctrl-r
/ctrl-t
. When the fzf prompt is open, pressing ctrl-j
or ctrl-k
now does nothing. Instead, the Zellij plugin captures the key presses and switches panes or tabs, but the fzf prompt stays unchanged.
I forked the plugin and made some changes to support these scenarios. Don't worry I submitted a Pull Request to merge them upstream.
Until those are merged and released, you'll need to build the plugin manually.
- Install Rust via rustup.
Clone the repository fork:
git clone https://github.com/tombruijn/vim-zellij-navigator.git
Compile the plugin.
# Open the project directory cd vim-zellij-navigator # Install the latest Rust version (at time of writing) rustup override set 1.82 # Install Rust WASM target rustup target add wasm32-wasi # Compile the project cargo build --release
Copy the plugin to the Zellij plugin directory.
# Create the plugins directory if it does not exist yet mkdir -p ~/.config/zellij/plugins # Copy the plugin to the plugins directory cp ~/target/wasm32-wasi/release/vim-zellij-navigator.wasm ~/.config/zellij/plugins
Finally, configure Zellij to load the plugin and configure keybindings to call the plugin.
My configuration is down below, which is complete for my use case.
After configuring Zellij, restart it. The first time you press any of the keybindings you've configured to call the plugin (indicated with MessagePlugin
), you'll get a prompt from the plugin asking permission to do a couple things. Press `y' to approve the permissions.
// ~/.config/zellij/config.kdl
plugins {
vim-zellij-navigator location="file:~/.config/zellij/plugins/vim-zellij-navigator.wasm"
}
// I use the locked mode as the default mode.
// By default this is set to "normal".
default_mode "locked"
keybinds clear-defaults=true {
locked { // Change this to "normal" if your default mode is "normal"
// Switch panes with `Ctrl+hjkl`
bind "Ctrl h" {
MessagePlugin "vim-zellij-navigator" {
name "move_focus_or_tab";
payload "left";
};
}
bind "Ctrl j" {
MessagePlugin "vim-zellij-navigator" {
name "move_focus";
payload "down";
};
}
bind "Ctrl k" {
MessagePlugin "vim-zellij-navigator" {
name "move_focus";
payload "up";
};
}
bind "Ctrl l" {
MessagePlugin "vim-zellij-navigator" {
name "move_focus_or_tab";
payload "right";
};
}
// Disable the plugin when any of these keybindings are used and still press that key too
bind "Ctrl r" { // `Ctrl+r` is for forward shell history
WriteChars "\u{0012}"; // Passthrough `Ctrl+r`
MessagePlugin "vim-zellij-navigator" {
payload "disable";
};
}
bind "Ctrl s" { // `Ctrl+s` is for backward shell history
WriteChars "\u{0013}"; // Passthrough `Ctrl+s`
MessagePlugin "vim-zellij-navigator" {
payload "disable";
};
}
bind "Ctrl t" { // `Ctrl+s` is for backward shell history
WriteChars "\u{0014}"; // Passthrough `Ctrl+t`
MessagePlugin "vim-zellij-navigator" {
payload "disable";
};
}
// Reenable the plugin when any of these keybindings are used and still press that key too
bind "ESC" {
WriteChars "\u{001B}"; // Passthrough `ESC`
MessagePlugin "vim-zellij-navigator" {
payload "enable";
};
}
bind "Enter" {
WriteChars "\u{000D}"; // Passthrough `Enter`
MessagePlugin "vim-zellij-navigator" {
payload "enable";
};
}
bind "Ctrl c" {
WriteChars "\u{0003}"; // Passthrough `Ctrl+c`
MessagePlugin "vim-zellij-navigator" {
payload "enable";
};
}
// Toggle the plugin on or off manually
bind "Ctrl Alt a" {
MessagePlugin "vim-zellij-navigator" {
payload "toggle";
};
}
}
}