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
This commit is contained in:
Johannes Altmanninger 2024-05-13 10:25:32 +02:00
parent d6e231af0d
commit a19ff4989a
2 changed files with 29 additions and 0 deletions

View file

@ -500,6 +500,7 @@ pub struct ReaderData {
/// The source of input events.
inputter: Inputter,
queued_repaint: bool,
/// The history.
history: Arc<History>,
/// 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.

View file

@ -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 >