zellij-org / zellij

A terminal workspace with batteries included
https://zellij.dev
MIT License
21.95k stars 666 forks source link

Seamlessly navigate through tmux/vim #967

Open DanCardin opened 2 years ago

DanCardin commented 2 years ago

(originally commented about this on reddit and was directed to suggest it here.

The relevant tmux plugin is christoomey/vim-tmux-navigator and the corresponding vim plugin (which i use, there are a few) is aserowy/tmux.nvim.

At a high level, the idea is to bind the same tmux and vim keybinds and for both navigation and pane resize to work contextually to whether vim is focused or not (ideally interacting with vim splits seamlessly also.

The comment on reddit suggested that zellij might be able to get away without a vim plugin. Because of vim split navigation, im skeptical this can be done (fully) without a vim plugin, but I'm personally happy to fork the plugin, so long as the hooks necessary for the vim plugin to function correctly exist (relevant portion of the vim plugin i use)

But in order to be fully specified, i'll lay out the expected behavior in the event it can all be done in zellij, but being aware that if that's not possible, some of the behaviors laid out will be handled by the vim plugin

Given some layout

+-----------------------------------+----------------+
| ................................. |                |
| ⋮     vim     ⋮       vim       ⋮  |       pane 2  |
| ⋮     split 1 ⋮       split 2   ⋮  |               |
| ................................. |                |
+-----------------------------------+----------------+

Suppose keybind of C-h, C-j, C-k, C-l for navigation and M-h, M-j, M-k, M-l resizing: navigation

resizing

With a similar table for vertical naviation.

I notice that zoom is not currently implemented today. but the above plugins (really just the vim one) have the option to only navigate vim splits when zommed. I.e. disable pane navigation if zoomed. Personally, that's the behavior i have enabled (right now), but just to be aware that this (and i suppose normal pane navigation keybinds) interact with a theoretical zoom feature

jaeheonji commented 2 years ago

Hi! I also like to use neovim with Zellij. but, currently, the Zellij does not provide that feature. I personally think this feature should be processed in vim/neovim (using plugin). Of course, we can add the feature for vim/nvim, but i don't know if that's the right direction.

DanCardin commented 2 years ago

There does need to be some level of zellij (or zellij plugin) specific behavior, since it needs to only conditionally forward the original binding to vim in the even the pane's running process is vim

Re the vim side, I'm willing to fork the (n)vim plugin i reference for myself for that side, what's unclear to me is how such a plugin would work. i.e. whether zellij supports the external control required for support it.

jaeheonji commented 2 years ago

@DanCardin Thanks for the explanation!

First, I looked at how vim-tmux-navigator works. As you already know, the core of this plugin is the if-shell conditions statement in tmux.conf. Unfortunately, Zellij does not yet provide such an extensible config. So I think you are curious about plugins.

So, I thought about whether the above function can be implemented using the current Zellij plugin system. The conclusion seems to be impossible at present.

Because by default the Zellij plugin has an isolated environment using WASM. In plugin, there are some convenience function that can communicate with the Host (Zellij Server). For examples, hook the keyboard event.

But, the problem is, there is still no function to trigger a specific event of Zellij inside the plugin. For example, the navigation of the pane, etc.

Also, it seems difficult to confirm that vim/nvim is currently running. Because the inside of the plugin is a WASM VM, it is isolated from the host's environment variables.

Therefore, further development related to WASM is required to implement the seamless navigation function through the Zellij Plugin.

You can check which functions the plugin can use through the zellij-server/src/wasm_vm.rs file.

DanCardin commented 2 years ago

I'm not super tuned into how the plugin interface works, but it seems like a PaneProgramChanged (or somesuch name) event could be emitted? my brief lookings into how this is done in tmux basically that they pipe the pane's tty to pgrep -t. Perhaps there's a better, more cross platform way of doing that.

being able to subscribe to an event like this seems like it would be useful elsewhere. For example, I dont know if this is default, but my tmux's tabs automatically display "nvim" or "zsh" as the tab name depending on which is focused in the currently active tab.

jgsch commented 2 years ago

I am also very interested for something like christoomey/vim-tmux-navigator. I think this might be the last thing that keeps me from going completely to zellij.

Lilja commented 2 years ago

I've spent some time looking into this issue to try to create a neovim plugin to mirror the same features as above mentioned plugin.

Tmux

Reading Chris' code, it effectively uses the following cli command to navigate to another frame: tmux shell-pane -t $TMUX_PANE -<direction>. Where <direction> is either LUDR(left, up, down, right). It take the command and runs it in vim's system-function. Which spawns a non-interactive shell and runs the command.

:echo system("tmux select-pane $TMUX_PANE -R")

It works!

Zellij

Luckily for us, zellij has as an action argument where you can navigate using zellij action move-focus-or-tab <direction>. So let's try to use it. I'm using neovim, but it's mostly the same for vim.

:echo system("zellij action move-focus-or-tab right")
ENODEV: No such device 

What gives?! An error! I've opened up #1861 which tries to debug the issue. However, due to how zellij tries to access tty information in a non-interactive terminal, it panics and fails.

So, unless this bug is fixed which I believe will need some internal refactors of zellij and how it handles communication between client and server, this is blocked.

jaeheonji commented 2 years ago

@Lilja Thank you for your interest in this feature.

I am also constantly interested in this feature, and did some testing after a new version was released. I don't know if this could be a fundamental solution, but you can use lua from the nvim side to run a zellij action like this:

local pipe = io.popen("zellij action move-focus down")
pipe:close() -- necessary

I hope this will help you with your work.

Lilja commented 2 years ago

Thanks @jaeheonji! That worked.

I created a neovim plugin to solve this problem, Lilja/zellij.nvim which has vim-tmux-navigator compatible keybindings. Feel free to try it out.

jaeheonji commented 2 years ago

Thank you for making a awesome plugin Lilja/zellij.nvim!

But, for natural navigation like tmux-nvigator, there seems to be more to be implemented in zellij side. Therefore, I will keep this issue as it is now.

casonadams commented 1 year ago

Here is something that is getting close. I just need some help sending ctrl + w h etc.

        bind "h" {
            SwitchToMode "Normal";
            Run "bash" "-c" "[ ! -z $VIMRUNTIME ] && zellij action close-pane & zellij action move-focus left || zellij action close-pane & zellij action write 7f 68";
        }

It would be nice to be able to send-keys that way I could so something like

        bind "h" {
            SwitchToMode "Normal";
            Run "bash" "-c" "[ ! -z $VIMRUNTIME ] && zellij action close-pane & zellij action move-focus left || zellij action close-pane & zellij action send-keys ctrl+w h";
        }
Lilja commented 1 year ago

From @casonadams find above. I was able to make it work, with a small adjustment. But it's not working well.

If you disregard sending ctrl+w h and instead using my zellij.nvim neovim plugin, you can actually send :ZellijNavigate(Right|Left|Down|Up)<Cr> like this:

Run "bash" "-c" "[ -z $VIMRUNTIME ] && zellij action move-focus left || zellij action write-chars \":ZellijNavigateLeft \"";

☝️ note the newline. It's important as we need something to press after the command has been completed.

This doesn't work fully as the run command will open a pane, because it's expected to be used for running certain long running commands like tail or some sort of compile-watcher tool.

So we probably need a flag for Run to run inside of a subprocess and not display the contents like it does right now.

hnorkowski commented 1 year ago

It would be awesome if we could implement this not only for vim but also for helix (or other programs)

Dzordzu commented 1 year ago

Idea

In order to achieve this zellij can follow tmux like approach: create a custom "vim locked mode", then use plugin provided by https://github.com/zellij-org/zellij/issues/967#issuecomment-1295806553.

How it would work

Depends on:

mpasa commented 10 months ago

@Dzordzu I believe your last point might be already addressed (in nvim at least) by this the zellij-nav.nvim plugin.

from-nibly commented 9 months ago

It might be obvious to some, but maybe not. If such a partial lock mode existed, it should probably be named something more generic. Maybe "Partial Lock" or "Subprocess Lock"

But maybe those two features should be separated?

  1. Be able to trigger actions based on the subprocess of the focused pane a. Might need to make the trigger mechanism respond to inverted queries. e.g. "process is not vim" or "process is vim"
  2. Be able to enter a Partial Lock mode where a list of keymaps are disabled.

If the features get separated then "Partial Lock" would make sense

If they are joined as a single feature then "Subprocess Lock" might make more sense.

imsnif commented 6 months ago

The recent patch version (0.40.1) adds the ability to:

  1. Bind Ctrl j
  2. List connected clients to the current session with zellij action list-clients Sample output:
    
    $ zellij action list-clients

CLIENT_ID ZELLIJ_PANE_ID RUNNING_COMMAND 1 plugin_2 zellij:session-manager 2 terminal_3 vim /tmp/my-file.txt



I think this should give anyone wishing to implement this sort of navigation all the tools they need (more info here: https://github.com/zellij-org/zellij/issues/2434#issuecomment-2089950065)
hiasr commented 6 months ago

I have created a plugin which solves this issue! See vim-zellij-navigator as the Zellij version of vim-tmux-navigator! Currently, it relies on an additional plugin in Neovim to know whether Neovim is running, I will change this to use list-clients once that command becomes available to plugins.

imsnif commented 6 months ago

@hiasr - if you want, until this command is directly available to plugins, you can invoke zellij action list-clients from plugins using the run_command API call and parsing its output.

imsnif commented 6 months ago

Also @hiasr - if I may offer another suggestion: could be cool if you make the plugin generic: a sort of discrete piece of logic that you can bind to a key and does: "move <direction> unless I am focused on <config_app_string> in which case send the key to the focused pane". I think this will also help out @mrjones2014 in implementing support in their plugin.

hiasr commented 6 months ago

Good idea! I am currently working on migrating towards zellij action list-clients such that the additional neovim plugin is not necessary and multiple clients work as expected. Once that is finished I will look into configurability, maybe a regex you can add as an argument would cover most of the potential usecases.

imsnif commented 6 months ago

And also, while I'm at it: another piece of advice - I'd recommend not using latest in the keybinding https url but using the version number explicitly. The reason for this is that Zellij caches the plugins on successful load by their URL - and so when someone would want to upgrade, if using latest they'd have to explicitly clear the cache.

fresh2dev commented 6 months ago

Idea

In order to achieve this zellij can follow tmux like approach: create a custom "vim locked mode", then use plugin provided by #967 (comment).

How it would work

* During vim startup zellij enters "vim locked mode" (command send by vim)

* Zellij plugin detects that pane is using vim

* Zellij triggers "vim locked mode"

* Some of the keybindings are disabled

* When VIM plugin detects edge of the vim, the disable mode command is sent to zellij. Afterwards, the command changing the pane/tab is sent

I think this sort of approach is the solution to this problem. I referenced vim-zellij-navigator and built a plugin to support this approach, in pursuit of seamless navigation for Vim and other programs that benefit from Ctrl+h/j/k/l nav, like Helix, FZF, Zoxide, Atuin, etc.

The result: fresh2dev/zellij-autolock

fresh2dev commented 2 weeks ago

I just published v0.2 of zellij-autolock which makes use of Zellij v0.41's API enhancements. The result is a much more responsive and reliable auto-locking mechanism. Thanks @imsnif!

rafaelpirolla commented 1 week ago

Any way of the autolock work with the new colliding keybinding solution? For now I've managed to configure the vim-zellij-navigator – with a bit of trouble.

fresh2dev commented 1 week ago

Any way of the autolock work with the new colliding keybinding solution?

For now I've managed to configure the vim-zellij-navigator – with a bit of trouble.

If you have a specific issue, please file an issue in the repo with more details.