From a19ff4989a1322cde2feed7567d1bc06d26f75ee Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Mon, 13 May 2024 10:25:32 +0200 Subject: [PATCH] Prevent out-of-order execution following repaint Commit a583fe723 ("commandline -f foo" to skip queue and execute immediately, 2024-04-08) fixed the execution order of some bindings but was partially backed out in 5ba21cd29 (Send repaint requests through the input queue again, 2024-04-19) because repainting outside toplevel yields surprising results (wrong $status etc). Transient prompts wants to first repaint and then execute some more readline commands, all within a single binding. This was broken by the second commit because that one defers the repaint until after the binding has finished. Work around this problem by deferring input events again while a readline event was queued. This is closest to the historical behavior. The implementation feels hacky; we might find odd situations. For example, commandline -f repaint end-of-line set token (commandline -t) sets the wrong token. Probably not a very important case. We could throw an error or make it work by letting "commandline -t" drain the input queue. That seems too complicated, better change repaints to not use the input queue (and fake $status etc). Let's try to do that in future. Closes #10492 --- src/reader.rs | 6 ++++++ tests/checks/tmux-transient.fish | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 tests/checks/tmux-transient.fish diff --git a/src/reader.rs b/src/reader.rs index d797b7ba0..5e21b7760 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -500,6 +500,7 @@ pub struct ReaderData { /// The source of input events. inputter: Inputter, + queued_repaint: bool, /// The history. history: Arc, /// The history search. @@ -914,6 +915,9 @@ pub fn reader_execute_readline_cmd(ch: CharEvent) { | ReadlineCmd::Repaint | ReadlineCmd::ForceRepaint ) { + data.queued_repaint = true; + } + if data.queued_repaint { data.inputter.queue_char(ch); return; } @@ -1091,6 +1095,7 @@ impl ReaderData { last_flash: Default::default(), screen: Screen::new(), inputter, + queued_repaint: false, history, history_search: Default::default(), history_pager_active: Default::default(), @@ -2220,6 +2225,7 @@ impl ReaderData { } } rl::RepaintMode | rl::ForceRepaint | rl::Repaint => { + self.queued_repaint = false; self.parser().libdata_mut().pods.is_repaint = true; if c == rl::RepaintMode { // Repaint the mode-prompt only if possible. diff --git a/tests/checks/tmux-transient.fish b/tests/checks/tmux-transient.fish new file mode 100644 index 000000000..c595fbe60 --- /dev/null +++ b/tests/checks/tmux-transient.fish @@ -0,0 +1,23 @@ +#RUN: %fish %s +#REQUIRES: command -v tmux + +set -g isolated_tmux_fish_extra_args -C ' + function fish_prompt + if set -q transient + printf "> " + set --erase transient + else + printf "> full prompt > " + end + end + bind enter "set transient true; commandline -f repaint execute" +' + +isolated-tmux-start + +isolated-tmux send-keys 'echo foo' Enter +tmux-sleep +isolated-tmux capture-pane -p +# CHECK: > echo foo +# CHECK: foo +# CHECK: > full prompt >