mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-04-21 08:51:14 -03:00
builtin commandline: apply commandline+cursor to first top-level reader
Historically, `fish -C "commandline echo"` was silently ignored. Make it do the expected thing. This won't affect subsequent readers because we only do it for top-level ones, and reader_pop() will clear the commandline state again. This improves consistency with the parent commit. We probably don't want to support arbitrary readline commands before the first reader is initialized, but setting the initial commandline seems useful: first, it would have helped me in the past for debugging fish. Second, it would allow one to rewrite an application launcher: foot --app-id my-foot-launcher -e fish -C ' set fish_history launcher bind escape exit bind ctrl-\[ exit - function fish_should_add_to_history - false - end - for enter in enter ctrl-j - bind $enter '\'' - history append -- "$(commandline)" - commandline "setsid $(commandline) </dev/null >/dev/null 2>&1 & disown && exit" - commandline -f execute - '\'' - end + commandline "setsid </dev/null >/dev/null 2>&1 & disown && exit" + commandline --cursor $(string length "setsid ") ' which is probably not desirable today because it will disable autosuggestions. Though that could be fixed eventually by making autosuggestions smarter. If we find a generally-useful use case, we should mention this in the changelog. Ref: https://github.com/fish-shell/fish-shell/pull/11570#discussion_r2144544053
This commit is contained in:
@@ -39,7 +39,7 @@ struct Options {
|
||||
prompt: Option<WString>,
|
||||
prompt_str: Option<WString>,
|
||||
right_prompt: WString,
|
||||
commandline: WString,
|
||||
commandline: Option<WString>,
|
||||
// If a delimiter was given. Used to distinguish between the default
|
||||
// empty string and a given empty delimiter.
|
||||
delimiter: Option<WString>,
|
||||
@@ -100,7 +100,7 @@ fn parse_cmd_opts(
|
||||
opts.array = true;
|
||||
}
|
||||
'c' => {
|
||||
opts.commandline = w.woptarg.unwrap().to_owned();
|
||||
opts.commandline = Some(w.woptarg.unwrap().to_owned());
|
||||
}
|
||||
'd' => {
|
||||
opts.delimiter = Some(w.woptarg.unwrap().to_owned());
|
||||
@@ -207,7 +207,7 @@ fn read_interactive(
|
||||
silent: bool,
|
||||
prompt: &wstr,
|
||||
right_prompt: &wstr,
|
||||
commandline: &wstr,
|
||||
commandline: &Option<WString>,
|
||||
inputfd: RawFd,
|
||||
) -> BuiltinResult {
|
||||
let mut exit_res = Ok(SUCCESS);
|
||||
@@ -238,7 +238,9 @@ fn read_interactive(
|
||||
s.readonly_commandline = false;
|
||||
})
|
||||
});
|
||||
commandline_set_buffer(parser, Some(commandline.to_owned()), None);
|
||||
if let Some(commandline) = commandline {
|
||||
commandline_set_buffer(parser, Some(commandline.clone()), None);
|
||||
}
|
||||
|
||||
let mline = {
|
||||
let _interactive = parser.push_scope(|s| s.is_interactive = true);
|
||||
|
||||
@@ -317,11 +317,12 @@ pub fn reader_push<'a>(parser: &'a Parser, history_name: &wstr, conf: ReaderConf
|
||||
assert_is_main_thread();
|
||||
let hist = History::with_name(history_name);
|
||||
hist.resolve_pending();
|
||||
let data = ReaderData::new(hist, conf);
|
||||
let is_top_level = reader_data_stack().is_empty();
|
||||
let data = ReaderData::new(hist, conf, is_top_level);
|
||||
reader_data_stack().push(data);
|
||||
let data = current_data().unwrap();
|
||||
data.command_line_changed(EditableLineTag::Commandline, AutosuggestionUpdate::Remove);
|
||||
if reader_data_stack().len() == 1 {
|
||||
if is_top_level {
|
||||
reader_interactive_init(parser);
|
||||
}
|
||||
Reader { data, parser }
|
||||
@@ -1201,12 +1202,18 @@ fn reader_received_sighup() -> bool {
|
||||
}
|
||||
|
||||
impl ReaderData {
|
||||
fn new(history: Arc<History>, conf: ReaderConfig) -> Pin<Box<Self>> {
|
||||
fn new(history: Arc<History>, conf: ReaderConfig, is_top_level: bool) -> Pin<Box<Self>> {
|
||||
let input_data = InputData::new(conf.inputfd);
|
||||
let mut command_line = EditableLine::default();
|
||||
if is_top_level {
|
||||
let state = commandline_state_snapshot();
|
||||
command_line.push_edit(Edit::new(0..0, state.text.clone()), false);
|
||||
command_line.set_position(state.cursor_pos);
|
||||
}
|
||||
Pin::new(Box::new(Self {
|
||||
canary: Rc::new(()),
|
||||
conf,
|
||||
command_line: Default::default(),
|
||||
command_line,
|
||||
command_line_transient_edit: None,
|
||||
rendered_layout: Default::default(),
|
||||
autosuggestion: Default::default(),
|
||||
|
||||
@@ -102,6 +102,18 @@ send(control("k"))
|
||||
sendline('echo "process extent is [$tmp]"')
|
||||
expect_str("process extent is [echo process # comment]")
|
||||
|
||||
sendline(
|
||||
"""$fish -C 'commandline "sq 2; exit"; commandline --cursor 1; commandline -i e'"""
|
||||
)
|
||||
expect_str("seq 2")
|
||||
send("\r")
|
||||
expect_str("1\r\n2\r\n")
|
||||
|
||||
sendline("""$fish -C 'commandline 123; read'""")
|
||||
expect_str("read> 123")
|
||||
sendline("456; exit")
|
||||
expect_str("123456")
|
||||
|
||||
# DISABLED because it keeps failing under ASAN
|
||||
# sendline(r"bind ctrl-b 'set tmp (commandline --current-process | count)'")
|
||||
# sendline(r'commandline "echo line1 \\" "# comment" "line2"')
|
||||
|
||||
Reference in New Issue
Block a user