"commandline -f foo" to skip queue and execute immediately

Commit c3cd68dda (Process shell commands from bindings like regular char
events, 2024-03-02) mentions a "weird ordering difference".
The issue is that "commandline -f foo" goes through the input
queue while other commands are executed directly.
For example

    bind ctrl-g "commandline -f end-of-line; commandline -i x"

is executed in the wrong order. Fix that.

This doesn't yet work for "commandline -f exit" but that can be fixed easily.

It's hard to imagine anyone would rely on the existing behavior.  "commandline
-f" in bindings is mostly used for repainting the commandline.
This commit is contained in:
Johannes Altmanninger 2024-04-08 23:24:24 +02:00
parent 9d7116c12d
commit a583fe7230
4 changed files with 34 additions and 10 deletions

View file

@ -107,6 +107,8 @@ Interactive improvements
New or improved bindings
^^^^^^^^^^^^^^^^^^^^^^^^
- Bindings can now mix special input functions and shell commands, so ``bind ctrl-g expand-abbr "commandline -i \n"`` works as expected (:issue:`8186`).
- Special input functions run from bindings via ``commandline -f`` are now applied immediately instead of after the currently executing binding.
For example, ``commandline -f yank -f yank-pop`` inserts the last-but-one entry from the kill ring.
- When the cursor is on a command that resolves to an executable script, :kbd:`Alt-O` will now open that script in your editor (:issue:`10266`).
- Two improvements to the :kbd:`Alt-E` binding which edits the commandline in an external editor:
- The editor's cursor position is copied back to fish. This is currently supported for Vim and Kakoune.

View file

@ -12,7 +12,8 @@ use crate::parse_util::{
};
use crate::proc::is_interactive_session;
use crate::reader::{
commandline_get_state, commandline_set_buffer, commandline_set_search_field, reader_queue_ch,
commandline_get_state, commandline_set_buffer, commandline_set_search_field,
reader_execute_readline_cmd,
};
use crate::tokenizer::TOK_ACCEPT_UNFINISHED;
use crate::tokenizer::{TokenType, Tokenizer};
@ -298,8 +299,6 @@ pub fn commandline(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr])
let positional_args = w.argv.len() - w.woptind;
let ld = parser.libdata();
if function_mode {
// Check for invalid switch combinations.
if buffer_part.is_some()
@ -335,13 +334,13 @@ pub fn commandline(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr])
// Don't enqueue a repaint if we're currently in the middle of one,
// because that's an infinite loop.
if matches!(cmd, rl::RepaintMode | rl::ForceRepaint | rl::Repaint) {
if ld.pods.is_repaint {
if parser.libdata().pods.is_repaint {
continue;
}
}
// Inserts the readline function at the back of the queue.
reader_queue_ch(CharEvent::from_readline(cmd));
reader_execute_readline_cmd(CharEvent::from_readline(cmd));
}
return STATUS_CMD_OK;
@ -473,8 +472,13 @@ pub fn commandline(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr])
} else if let Some(override_buffer) = &override_buffer {
current_buffer = override_buffer;
current_cursor_pos = current_buffer.len();
} else if !ld.transient_commandlines.is_empty() && !cursor_mode {
transient = ld.transient_commandlines.last().unwrap().clone();
} else if !parser.libdata().transient_commandlines.is_empty() && !cursor_mode {
transient = parser
.libdata()
.transient_commandlines
.last()
.unwrap()
.clone();
current_buffer = &transient;
current_cursor_pos = transient.len();
} else if rstate.initialized {

View file

@ -904,10 +904,14 @@ pub fn reader_schedule_prompt_repaint() {
}
}
/// Enqueue an event to the back of the reader's input queue.
pub fn reader_queue_ch(ch: CharEvent) {
pub fn reader_execute_readline_cmd(ch: CharEvent) {
if let Some(data) = current_data() {
data.inputter.queue_char(ch);
if data.rls.is_none() {
data.rls = Some(ReadlineLoopState::new());
}
data.apply_commandline_state_changes();
data.handle_char_event(Some(ch));
data.update_commandline_state();
}
}

View file

@ -358,6 +358,20 @@ send('\x02\x02\x02') # ctrl-b, backward-char
sendline('\x1bu') # alt+u, upcase word
expect_prompt("fooBAR")
send("""
bind ctrl-g "
commandline --insert 'echo foo ar'
commandline -f backward-word
commandline --insert b
commandline -f backward-char
commandline -f backward-char
commandline -f delete-char
"
""")
send('\x07') # ctrl-g
send('\r')
expect_prompt("foobar")
# Check that the builtin version of `exit` works
# (for obvious reasons this MUST BE LAST)
sendline("function myexit; echo exit; exit; end; bind ctrl-z myexit")