zellij-org / zellij

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

Allow panes to remain open after their process exited #707

Closed stratosgear closed 1 year ago

stratosgear commented 3 years ago

In continuation of #551, where an arbitrary process could be started in each pane...

It seems that if the process ends, or I kill it, the pane closes.

My actual usecase, is to run some debug services in these panes, and as I continue developing I often do code changes and then need to restart them (CTRL+C then up arrow to rerun my latest command to bring them up again).

In the current implementation as soon as I stop the process the pane goes away, destroying my carefully layed out layout...

Can this be configurable? In tmux this is working much more intuitively.

a-kenji commented 3 years ago

Hey! Thanks for taking the time in opening this issue, this has been a pain point for quite a while. We have been thinking how to best alleviate this. An extra config option seems like a sensible addition.

Currently we would start a command as follows:

Run: 
  cmd: <path>,
  args: [ARGS],
  direction: <direction>,

I can see the value in adding something along the lines of:

Run: 
  cmd: <path>,
  args: [ARGS],
  direction: <direction>,
  pane: keep_open

With it defaulting to true. If you set it to false You can run the command Without opening an extra pane.

I am not sure about the naming, though I think that this would be very valuable.

What do you think @stratosgear ?

imsnif commented 3 years ago

IMO it can also be cool to re-run the command when focused on the pane and pressing ENTER.

stratosgear commented 2 years ago

Pressing enter, as suggested by @imsnif deviates too much from a "normal" terminal window. I would prefer if the process into that pane behaved like any other terminal window.

Since I also think that the pane window should always remain open (I am sure the error output of a failed process, might contain valuable info that you do not want lost), a config param of keep_open should default to True, and for the minority (?) of people that want to have the pane autoclose they could use a keep_open: false?

Maybe @imsnif suggestion could be implemented with an auto_reload: true that would reload on Enter? But that would have to be specifically activated, so you know that you have such a "feature" active on that pane.

And that's all I have to say about that.... :)

imsnif commented 2 years ago

@stratosgear - I totally get what you're saying. It's also important to me not to deviate too much from the way a normal terminal window behaves.

In this case however, the command has exited. Since we don't defer back to a shell in this case like a normal terminal window (since that's not something the user asked us to do), there isn't really any equivalent. The command is done and isn't accepting input any longer. I propose this feature only be available in this case, not when the command is still running. How would you feel about that?

a-kenji commented 2 years ago

Normal terminal windows usually have a keep-open flag that keeps the window open after a command finishes.

For example on alacritty that would be --hold, thats why I think the default behaviour of closing the pane when a program exits would be desirable. But I am OK if we decide differently in the end.

imsnif commented 2 years ago

I didn't know about the keep-open flag. Thanks!

I guess this would depend on the use-cases we're talking about and what we think the main one would be. For me the use-case of defaulting to keep-open and being able to re-run with ENTER is something I'd like to do with eg. cargo test (when I don't want it to run on every file change in a loop).

What use-cases do you imagine defaulting to keep-open being false, @a-kenji?

Also, @stratosgear - what use-cases would you not like the ENTER to re-run functionality to overwrite?

stratosgear commented 2 years ago

As I mentioned my use case is that these panes are running a startup script that starts my development server in debug mode.

With tmux (where each pane was a standalone, self sufficient terminal), two things would usually happen:

So I guess I need both cases, somehow active.

And to be honest, I still have troubles groking why the panes cannot be normal terminals. Maybe I am too much invested in the mentality of tmux, where each pane is a "standalone" terminal that has a very stable deeply ingrained mentality of how it works.

Maybe the processes I have to start inside the panes, have to be just bash shells? But then I need to start a bash shell, with an initial startup command, like bash start_server.sh Is this even doable?

So when my process finishes I drop back to the bash shell that started my process...? Does that make sense?

stratosgear commented 2 years ago

For what it's worth, I run kitty as my terminal.

And contrary to what @a-kenji mentions above, I have never seen a "normal" terminal window quit, once a process that is started from within, terminates. They usually drop back to the shell afterwards, no...? Maybe they can be configured that way, but that's not a default setting.

Maybe I misunderstood, and he is talking about something else?

imsnif commented 2 years ago

Some background (in a simplified manner):

Any terminal window (including a Zellij pane) is talking to another program that's running on the other side of the pty. Normally this is the shell. When you run something from within the shell, the shell itself defers to that process and forwards its stdout to the terminal and the terminal's stdin to said process. Eg. your script. When the script exits, you drop back into the shell.

Zellij and tmux panes work in the same way. They run a shell by default and when you run processes in that shell, they talk to those processes (this whole system is transparent to the multiplexer of course).

When you tell tmux to run a command in a pane, what it does (presumably by what you describe, I have not looked at the code) is forward your command to a pane running your default shell just as if you typed it in.

Zellij can do this, but IMO this is a bit hacky and we can definitely do better here. I feel it can be very powerful to give users the power to decide what they'd like to do inside a new pane. They can run a command regardless of which shell runs there, and have the pane interact directly with the command. Imagine running your dev script in a pane normally, being able to interact with it just as you did, and when it exits, you will see something like this:

    ┌ <your script name> ─────────────────────────────────────────┐
    │ Your script output which you can scroll through and resize  │
    │ as you would any other pane                                 │
    │                                                             │
    │                                                             │
    │                                                             │
    │                                                             │
    │                                                             │
    └ EXIT STATUS: 2 (error), <ENTER> to rerun ───────────────────┘

To me this sounds like it not only gives you both cases, but saves you having to press the Up arrow (you just have to press ENTER instead) and gives you a clearer visual indication that the script has terminated. But maybe I'm missing something about your use case... what do you think?

stratosgear commented 2 years ago

Hehe, I thought creating something as nice as your ASCII drawing from above shows, would be much more work than implementing something that (maybe naively) I thought would be much easier to do.

So if you think that creating something as interactive as the above, is feasible, then by all means. I would definitely agree that if you can do better (and I believe that you can) with these panes, is a much better goal!

From your description I also assume that a CTRL+C will also result in this message from above, right?

imsnif commented 2 years ago

From your description I also assume that a CTRL+C will also result in this message from above, right?

Yes, exactly (well, assuming the process responds to ctrl-c by exiting, but yeah :) ).

I'm happy you like the approach. It isn't that much work to implement, and is a direction I personally want to take Zellij anyway. While on the whole this isn't more work than creating a tmux-like solution, the incremental implementation that I'd recommend taking here might mean that there will be a gap where we'll be a little less good than tmux before we add everything else. So I hope you'll bear with us. :)

I'm going to put a Help Wanted tag on this issue if anyone wants to pick it up. Otherwise I'll try to get to it sometime in the near future. Here's a rough plan for implementing this:

  1. Add the remain_open: bool flag to the Layout. These panes should only close if the user explicitly closes them, rather than if their process exits.
  2. Collect the exit status of such panes and keep it as an Option<u8> on the TerminalPane (a different solution can be found for plugins, but we can ignore them for now).
  3. Keep the Command from the Layout on TerminalPane as an Option<String>
  4. Change the BoundariesFrame to display the exit status if the pane exited (as well as the message in my ASCII drawing above) - bonus points if you'd like to change its color depending on the exit status (red for error, normal otherwise)
  5. In the "exited" state, if the TerminalPane receives the ENTER key, it should send a message to the Pty thread to run the command in a new async task.

I'd be more than happy to provide help and guidance if anyone wants/needs it.

a-kenji commented 2 years ago

What use-cases do you imagine defaulting to keep-open being false, @a-kenji?

For example opening a command like htop, looking quickly at the output it provides and then closing the pane while closing the command itself. So you have a shortcut to launch the command, then can type q and it will close the pane again.

And contrary to what @a-kenji mentions above, I have never seen a "normal" terminal window quit, once a process that is started from within, terminates. They usually drop back to the shell afterwards, no...? Maybe they can be configured that way, but that's not a default setting.

Yes, I think you misunderstood me, even kitty has the kitty --hold flag. And yes that is exactly how every single terminal I have come across behaves, also kitty. For example the default configuration of kitty starts kitty with shell . which is equivalent to $SHELL, so basically when just running kitty it would be equivalent to running kitty -e $SHELL. And if you exit the shell the terminal usually does terminate. Try it out yourself. Run kitty -e $SHELL, then close the shell. The terminal will close. So when running kitty -e ls the child process returns immediately and the terminal closes again. When running kitty --hold -e ls it waits for you to put anything in there.

Add the remain_open: bool flag to the Layout. These panes should only close if the user explicitly closes them, rather than if their process exits.

I think that could maybe be a good way? But I think to address the original issue we would want to add a keep_open or hold flag to the cmd configuration. Those would be 2 separate contexts then.

imsnif commented 2 years ago

I think that could maybe be a good way? But I think to address the original issue we would want to add a keep_open or hold flag to the cmd configuration. Those would be 2 separate contexts then.

Hum... why do you feel it would be better to add it to the command context and not on the pane itself?

stratosgear commented 2 years ago

Sorry for the additional confusion...

Add the remain_open: bool flag to the Layout. These panes should only close if the user explicitly closes them, rather than if their process exits.

How would you know that the user explicitly closed them? Does an explicit CTRL+C differ from a failed process...? Or you mean to implement a specific zelliz command (shortcut?) that closes the pane for you, so it knows it was triggered by you...?

And thanks to @a-kenji for helping me better understand how terminals work:

Seeing the difference between kitty -e ls and kitty --hold -e ls was enlightening (hint, the first one opens a terminal with the ls output and immediately closes, the second waits for a key press before closing)

imsnif commented 2 years ago

How would you know that the user explicitly closed them? Does an explicit CTRL+C differ from a failed process...? Or you mean to implement a specific zelliz command (shortcut?) that closes the pane for you, so it knows it was triggered by you...?

Ctrl+C is sent to the process itself (either that or directly send it SIGTERM, not sure about the implementation yet) and then the process closes itself. This will not close the pane in such a case.

Closing the pane through Zellij (eg. by doing ctrl+p + x) will close the pane itself (and also kill the process) as happens now.

a-kenji commented 2 years ago

@stratosgear I am glad my description could help!

Hum... why do you feel it would be better to add it to the command context and not on the pane itself?

@imsnif, Because it is closely related to the command. Also I think these are 2 separate issues/features. That would both be good.

      - direction: Vertical
        run:
          command:
            cmd: "ls"
            hold: true

In which the pane would wait for input, after the child has already exited, in order to kill the pane. That way a simple one off command could be easily shown. Maybe even a quick grep or rg that can quickly be closed again after one has found what they were looking for.

Currently without the hold command, the pane in which ls would be executed would be closed instantly.

I think these 2 concepts are kind of separate from each other, that is why I propose a hold option for the command type in the layout apart from the rerun pane.

a-kenji commented 2 years ago

@stratosgear On second thought, is that what you were trying to do?

stratosgear commented 2 years ago

I guess so!

My original assumption was that all panes opened would remain open (like having one of your "holds" default to on) as this has been my previous experience from tmux (opening a pane meant opening a forever open terminal, unless you exited or CTRL+d)

So, you are describing a superset of what I wanted, therefore I think you are moving in the right direction... :)

alfredopalhares commented 2 years ago

For people that stumble on this issue. I have found a workaround for far:

            run:
              command:
                cmd: zsh
                args:
                  - "-c"
                  - "sleep 2 && task && zsh -i"

There currently the caveats:

har7an commented 1 year ago

Hello,

this feature is now implemented in zellij 0.32.0 and up as "command panes". Check out the blog post for the new release, which also includes a demo: https://zellij.dev/news/config-command-layouts/