elves / elvish

Powerful scripting language & versatile interactive shell
https://elv.sh/
BSD 2-Clause "Simplified" License
5.52k stars 296 forks source link

Cursor and prompt control in function to avoid prompt flick #1814

Open tmpm697 opened 1 month ago

tmpm697 commented 1 month ago

What happened, and what did you expect to happen?

set-env FZF_DEFAULT_OPTS "--height=40%"
set-env FZF_DEFAULT_COMMAND ""
fn flick-test {
  echo "1\n2\n3\n4\n5\n" | fzf --reverse
  sleep 3
  edit:redraw &full=$true
}
set edit:insert:binding[Ctrl-T] = { flick-test >$os:dev-tty 2>&1 }

https://github.com/elves/elvish/assets/48467427/79f28ce3-87d0-4fec-8a9a-091ab2002fbf

expecting: after select candidate in fzf and exit fzf cleanly, sleep 3 will not drop cursor to next line while sleep 3 is running --> should keep cursor and prompt as before trigger ctrl-t

so i think a flag that on/off prompt manually by user would solve this issue.

idk if this is bug or feature request, but it looks like a bug to me.

Output of "elvish -version"

0.20.1

Code of Conduct

krader1961 commented 1 month ago

expecting: after select candidate in fzf and exit fzf cleanly, sleep 3 will not drop cursor to next line while sleep 3 is running --> should keep cursor and prompt as before trigger ctrl-t

What you describe isn't possible. The fzf command has already written the text followed by a newline. The newline written by fzf is causing the terminal to move the cursor to the line after the selection. This isn't due to anything Elvish is doing.

It's not clear what you expect. Try doing the same thing, by adding the following to the end of you ~/.bashrc:

export FZF_DEFAULT_OPTS="--height=40%"
export FZF_DEFAULT_COMMAND=""
flick-test() {
  echo $'1\n2\n3\n4\n5' | fzf --reverse
  sleep 3
}
bind -x '"\C-t":"flick-test"'

The only difference is that bash will move the cursor to the line following the prompt in effect when you press Ctrl-T. Both Elvish and Bash will redraw the prompt, including anything you might have typed (e.g., "echo") before pressing Ctrl-T. Personally, I prefer the Elvish behavior.

tmpm697 commented 1 month ago

You can try to remove sleep 3 --> fzf will exit and return prompt super fast, you're including fzf in this case but it's minor. The issue is sleep command holding prompt.

It's ideal if user can also instruct fzf that it should keep prompt intact, just draw its list and redraw its own list below only

Holding prompt is reasonable when you're in interactive mode, in which shell waiting for user to type smth and then let the command change the prompt and wait for it to return result.

But in a function, especially when trigger keybinding to call a function, the interactive behavior causes flick issue which should be a voided.

krader1961 commented 1 month ago

I still don't understand what the problem is. There is no way to tell an external program like fzf it should keep the Elvish prompt intact. Elvish could be modified to behave like Bash but it's not clear that is preferable. On the contrary, I like the Elvish behavior of letting the external command use the line containing the current prompt. I also suspect you are using the word "flicker" in a manner different from how the rest of us who have engaged with you on this issue use the term because none of us consider what you are describing to be an example of "flicker". So like @milk on the discussion channel I'm going to bow out of this discussion because, despite trying really hard, I still don't understand what the problem is.

iandol commented 1 month ago

Thanks @krader1961 for the bash version, indeed the same thing happens (except as you say, bash moves the cursor position down so the old prompt is not affected by fzf output). In bash the partially entered text stays on screen (i.e. in bash type 3 then ctrl+t then select 5 and you'll see the 3 and 5 on the screen), very confusing! Elvish removes the partial text so this doesn't happen, I would assume this is a deliberate design choice.

@tmpm697 -- if you use a two line prompt (I use starship) in elvish then it looks more similar to bash...

krader1961 commented 1 month ago

@iandol I agree with your analysis. What is unclear to me is whether that difference in behavior is what @tmpm697 is complaining about. That's because their original problem statement is ambiguous. Specifically, this statement:

expecting: after select candidate in fzf and exit fzf cleanly, sleep 3 will not drop cursor to next line while sleep 3 is running --> should keep cursor and prompt as before trigger ctrl-t

@tmpm697 It might help if you (a) commented on how the Bash behavior differs from the Elvish behavior, and (b) provided a mockup of the Elvish behavior you prefer. Regarding point (a) does Bash behave as you prefer? Obviously you can't run Elvish to address my point (b) but it should be straightforward to create a simulated output using the markdown triple-quote mechanism to create a mockup of the output you expect to see.

iandol commented 1 month ago

What is unclear to me is whether that difference in behavior is what @tmpm697 is complaining about

I am pretty sure it is. For him the prompt is "flickering", which is visible in his first video as a single frame where the Elvish prompt disappears then reappears, causing a "flash". The behaviour is best described as prompt flicker (a fluctuation in brightness caused by the temporary removal of the prompt). He wants the Elvish prompt to remain on screen during the sleep, so that when it returns from sleep there is no flicker on that line.

Here is what Elvish is doing. FRAME C is the critical one, where line 1 prompt disappears.

FRAME A:

~/.config/test>

FRAME B:

~/.config/test>
> |
6/6------------------------------
> 1
  2

FRAME C:

1
|

FRAME D:

1
~/.config/test>

I think he would want FRAME C to be like this (line 1 keeps the prompt, it is line 2 that now holds the fzf output during the sleep):

FRAME C-ALT:

~/.config/test>
1
|

How FRAME D would then be drawn I think is not the issue, the point is the prompt disappears then reappears...

iandol commented 1 month ago

As I said above, using a two-line prompt you don't see this as most of the prompt is on the first line.

tmpm697 commented 1 month ago

@iandol thanks, it seems to be like that, when fzf is called -> move cursor where it's needed (in this case drop cursor next line and hold where user can type input)

my idea is simple, hold the cursor/prompt until a program need it or user explicitly allow it, i.e: when fzf needs cursor, gives it, but it should not need prompt until user want to update current dir or current-command which is often after fzf exit.

surprisingly fzf has option --no-clear which instructs fzf not to redraw when it exits but currently aik, if use it prompt will be swallowed by fzf fuzzy menu.

so fzf redraw a lot and it hold the prompt and current-command also while it's running which it shouldn't be, it should hold only area below prompt for its fuzzy menu.

tmpm697 commented 1 month ago

more info if someone stumble by this issue, this flick issue with fzf and terminal will be reduced if you set fzf --height 100% --> fzf take full terminal screen and display fuzzy list at top

or fzf --layout=default --height 100% --> fzf takes full terminal screen and display fuzzy list at bottom.

or you proactively drop new line for fzf echo "", fzf

or you use two lines prompt which @iandol mentioned above.

but they are not what i want :)