dj95 / zjstatus

A configurable statusbar plugin for zellij
MIT License
355 stars 6 forks source link

vim-tpipeline support #46

Open mrdgo opened 3 months ago

mrdgo commented 3 months ago

Is your feature request related to a problem? Please describe. I am currently working on nvim-zjstatus-pipeline. So far, it seems feasible. However, I cannot create a command like

command_left_command "cat /tmp/zjstatus-$(id -u)/$ZELLIJ_SESSION_NAME-$ZELLIJ_PANE_ID-vimbridge

Because the command handle neither evaluates $() commands nor environment variables.

Describe the solution you'd like I request considering one of the following:

  1. add a file handle that listens on changes of a file and prints its contents to the status bar AND accepts at least env variables
  2. let command handles evaluate nested commands or at least env variables.

Additional context Please don't hesitate to ask anything if I didn´t express this clearly enough.

dj95 commented 3 months ago

Hi,

thanks for your feature request and your work in the tmuxline integration! Commands in zellij are not run in a shell, but directly. This results in the missing environment variables and variable substitution. However, you could simply wrap your command in a shell. E.g.

command_0_command  "bash -c \"echo $ZELLIJ_SESSION_NAME\""

will print the session name.

Screenshot 2024-03-06 at 17 00 41

Will this help you to achieve the integration?


complete config layout { pane split_direction="vertical" { pane borderless=true } pane size=2 borderless=true { plugin location="file:target/wasm32-wasi/debug/zjstatus.wasm" { format_left "{mode} #[fg=#89B4FA,bg=#181825,bold]{session} {tabs} " format_center "{command_0} {command_1} {command_git_branch} {command_3}" format_right "{tabs} {datetime}" format_space "#[bg=#181825]" // foo border_enabled "true" border_char "─" border_format "#[fg=#6C7086]{char}" border_position "top" hide_frame_for_single_pane "true" mode_normal "#[bg=blue] #[bg=yellow] " mode_tmux "#[bg=yellow] " mode_default_to_mode "tmux" tab_normal "#[fg=#6C7086,bg=#181825] {index} {name} {floating_indicator} " tab_normal_fullscreen "#[fg=#6C7086,bg=#181825] {index} {name} [] " tab_normal_sync "#[fg=#6C7086,bg=#181825] {index} {name} <> " tab_active "#[fg=#9399B2,bg=#181825,bold,italic] {index} {name} {floating_total_count}{floating_indicator}{sync_indicator}{fullscreen_indicator}" tab_separator "#[fg=#6C7086,bg=#181825] | " tab_floating_indicator "F" tab_sync_indicator "S" tab_fullscreen_indicator "FS" command_0_command "bash -c \"echo $ZELLIJ_SESSION_NAME\"" command_0_format "#[fg=blue,bg=black] {exit_code} {stdout} " command_0_interval "1" command_1_command "date" command_1_format "#[fg=blue,bg=black] {exit_code} {stdout} " command_1_interval "1" command_git_branch_command "git rev-parse --abbrev-ref HEAD" command_git_branch_format "#[fg=red,bg=black] {stdout} " command_git_branch_interval "2" command_3_command "echo -e \"#[fg=red,bg=#000000,italic,bold] foo #[bg=red,fg=#000000,italic] bar \"" command_3_format "{stdout}" command_3_interval "10" command_3_rendermode "dynamic" datetime "#[fg=#6C7086,bg=#181825,bold] {format} " datetime_format "%A, %d %b %Y %H:%M" datetime_timezone "Europe/Berlin" } } }
mrdgo commented 3 months ago

Yeah, I feel a bit dumb now. Let me test and I'll provide feedback :)

mrdgo commented 3 months ago

Yes, that works! Thank you!

I still have the issue with the update interval - it's terrible to watch the line only update every second. What do you think about a file handle? Or some dynamic command to trigger an update?

dj95 commented 3 months ago

Yeah, I feel a bit dumb now. Let me test and I'll provide feedback :)

Don't feel dumb. The other perspective and ideas help to create new features and ideas! I'm glad that the proposed solution works!

I still have the issue with the update interval - it's terrible to watch the line only update every second. What do you think about a file handle? Or some dynamic command to trigger an update?

I'm currently a bit indecisive about the file widget. On one side, it's a really specific use-case, which IMO seems to be pretty rare. The other thing that bothers me is, that it will be quite difficult to implement the update mechanism on fs events due to the nature of zellij plugins.

To elaborate a bit more on this, here are the details, on the example of how the command widget is working: zellij widgets retrieve events. On these events the plugin then can decide, whether or not the render function will be called or not. Within the render function, there will be an initial call to the command widget, which checks based on a stored/initial state, if the command needs to be re-run according to the configured interval. In case the interval has passed, it runs the command without getting the result directly! Contrarily, zjstatus will obtain a RunCommandResult from zellij, when the command is finished, with the given exit code, stdout, stderr and a context map, that was created on running the command.

With this behaviour in mind, it will be tough to trigger re-renders based on a changing file, since at least as far as I know there is no way to trigger such events. Maybe the announced pipes for the next zellij release will help us to control the behaviour and to trigger re-renders of certain widgets in zjstatus.


Maybe we could add a specific widget for your integration or some other configuration on the interval, that enables you to run the command more often, which improves the given feedback.

Another idea I have is to somehow let the vim plugin only write updates to the file. Like a queue. So the command prints and exits on an update, which triggers the RunCommandResult and updates the bar. It would offer a higher responsiveness, but introduce more complexity on your side.

Sorry for the long text :D but let's have a discussion how to solve it in a good way!

mrdgo commented 3 months ago

Thank you so much for your thorough response!

I am not sure about the rarity argument, might be true, but on the other hand it might open a lot of new ideas and implementations of interesting things.

About the implementation, well there is entr or nushell's watch that do exactly that: run a command whenever one of the specified files changes. So as far as I understand, you would have to spawn a parallel process and connect to its stdout. You mean this PR, right? Then yes, that's exactly it :) I would/will run

bash -c "echo $FILE | entr cat $FILE"

on the zjstatus side and everytime that something is printed, I "magically use zjstatus' interface" to print it to the status bar. Seems charming. However, how do you feel about the complexity on your side?


Depending on how often the signals arrive, we might not even need that. We can internally store the last-modified-time of the file read before and then call something like

ls --time-style=+%s -l $FILE | awk '{print $6}'

to get the latest time. The awk call could probably also be done in the plugin :) Then a simple if curr > last then "cat $FILE" else NOP would do the trick.


Regarding your idea, it sounds interesting. But how would a command look like that only prints on an update? Do you mean like a script that does

while true
    if not $FILE exists
        wait/nop
    endif
endwhile
cat $FILE
rm $FILE

or

while true
    if last_modified($FILE) == $(cat $FILE-last-modified)
        wait/nop
    endif
endwhile
last_modified($FILE) > $FILE-last-modified
cat $FILE

Imaginable. Then your plugin would still need a new way to call the script. We want to have exactly one instance of this script running at the same time. How much effort on your side? Though, I don't really like this idea ... yet.

dj95 commented 3 months ago

Thanks for you detailed response. I'll go through the ideas in detail when I have some time (hopefully on the weekend).


For the bash part, I thought about something like a FIFO and the cat the content, so you get only on line from it and the command exits. Basically like the following example (FIFO was created with mkfifo test).

https://github.com/dj95/zjstatus/assets/4348959/4451dde1-00e7-433e-9ff4-32676ece3d9d

"new loop call" indicates, when the cat command was finished and restarted (which should be done by zjstatus). However, when trying this in zjstatus, it skips some updates. I need to dig deeper into it, when I have some time. But maybe this would be the easiest way.

dj95 commented 3 months ago

One other thing, that comes to my mind is, that a file watcher would only be possible for files in the same directory as zellij is started. Plugins are compiled to WASM and are executed in a sandbox with very limited filesystem access. Currently the command widget is exploiting the fact that zellij runs them on the host, so you are able to reach arbitrary file path.

Therefore it will be not really useful to have this widget. Hopefully I get the bug with the FIFO fixed. And I'll try to implement a feature with the newly introduced pipes, that might help you to achieve the goal.

dj95 commented 3 months ago

About the implementation, well there is entr or nushell's watch that do exactly that: run a command whenever one of the specified files changes. So as far as I understand, you would have to spawn a parallel process and connect to its stdout. You mean this https://github.com/zellij-org/zellij/pull/3066, right? Then yes, that's exactly it :)

Thanks for mentioning these tools, such that I can take a look at their implementation. However, as I pointed out in my previous comments, it will be a really limited implementation, since zjstatus would only be able to access files within the directory of the session. This in addition with the (at least right now) missing capability to trigger the render function from everywhere in the code makes it basically impossible to implement a file watcher.

Depending on how often the signals arrive, we might not even need that. We can internally store the last-modified-time of the file read before and then call something like

Comparing timestamps as an alternative to fs notifies would be also an option. Not as elegant, but it would work, as you suggest. The main problem there is again, that I'm not able to trigger the render function from the place at which the timestamp is compared. Therefore zjstatus must wait for zellij to send the next event, which could take a few seconds, since the communication is completely event driven.

Regarding your idea, it sounds interesting. But how would a command look like that only prints on an update? Do you mean like a script that does

For this I would refer to the comment with the FIFO.


Fortunately I had some time today, which I dedicated to the pipes feature in the upcoming zellij release. There's now a pull request, which leverages the pipes to send control sequences to zjstatus - as rerunning a command. This command in combination with your file and the cat command widget will help you to speed up the responsiveness a lot - but not perfectly, since events might take some time. If you have a development version of zellij running and you are capable of compiling zjstatus, you could give it a try: https://github.com/dj95/zjstatus/pull/48

Otherwise we need to wait until the next zellij version is released.

Either way, here's a short demo. The interval for the date command is 10 seconds. Check out, how it is rerun by the pipes after the first initial 10 seconds I waited to demonstrate the configured interval.

https://github.com/dj95/zjstatus/assets/4348959/15704865-ab85-44a4-b8b8-38720f35fa21

mrdgo commented 3 months ago

Thank you SO much for your work! I was sick, please excuse the late response.

I am looking forward to test the pipes feature! Sadly I can't compile zjstatus, because of

error: linking with `rust-lld` failed: exit status: 1

However, I didn't dig too deep, yet. Similarly, I have trouble compiling zellij - but will look into that as well and update you as soon as I know more :)

dj95 commented 3 months ago

No worries :)

Have you added the wasm target to your Rust toolchain?

Otherwise feel free to ping me if you need some help

dj95 commented 2 months ago

@mrdgo With the zellij release yesterday and zjstatus 0.14.0 you can try the behaviour without building both on your own.

mrdgo commented 2 months ago

Thanks for the reminder, I will look into it as soon as time allows me to :)