builtins commandline/complete: allow handling commandline before reader initialization

Commands like "commandline foo" silently fail, and "complete -C" fails with
a weird "option requires an argument" error.

I think at least the first one can be useful in edge cases, e.g. to test
code that does not separate the `commandline` input and output (#11570),
and to set fish's initial commandline, see the next commit.

I don't think there are super strong reasons to allow these, but if the
existing state is merely due to "no one has ever thought of doing this",
then we should try changing it.

For consistency, also allow "complete -C". I guess an argument for that is
that it's weird to make a command behave differently in non-interactive shells.

For now, keep the historical behavior of disabling access to the command
line in non-interactive shells. If we find a good reason for allowing that
(which seems unlikely), we can.

Ref: https://github.com/fish-shell/fish-shell/pull/11570#discussion_r2144544053

Co-authored-by: Johannes Altmanninger <aclopte@gmail.com>
This commit is contained in:
Daniel Müller
2025-06-13 07:55:09 -07:00
committed by Johannes Altmanninger
parent 4d67ca7c58
commit 32c36aa5f8
4 changed files with 29 additions and 19 deletions

View File

@@ -636,19 +636,16 @@ pub fn commandline(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr])
transient = parser.libdata().transient_commandline.clone().unwrap();
current_buffer = &transient;
current_cursor_pos = transient.len();
} else if rstate.initialized {
} else if is_interactive_session() {
current_buffer = &rstate.text;
current_cursor_pos = rstate.cursor_pos;
} else {
// There is no command line, either because we are not interactive, or because we are
// interactive and are still reading init files (in which case we silently ignore this).
if !is_interactive_session() {
streams.err.append(cmd);
streams
.err
.append(L!(": Can not set commandline in non-interactive mode\n"));
builtin_print_error_trailer(parser, streams.err, cmd);
}
// There is no command line because we are not interactive.
streams.err.append(cmd);
streams
.err
.append(L!(": Can not set commandline in non-interactive mode\n"));
builtin_print_error_trailer(parser, streams.err, cmd);
return Err(STATUS_CMD_ERROR);
}

View File

@@ -8,6 +8,7 @@
use crate::parse_constants::ParseErrorList;
use crate::parse_util::parse_util_detect_errors_in_argument_list;
use crate::parse_util::{parse_util_detect_errors, parse_util_token_extent};
use crate::proc::is_interactive_session;
use crate::reader::{commandline_get_state, completion_apply_to_command_line};
use crate::wcstringutil::string_suffixes_string;
use crate::{
@@ -465,11 +466,12 @@ pub fn complete(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) ->
None => {
// No argument given, try to use the current commandline.
let commandline_state = commandline_get_state(true);
if !commandline_state.initialized {
// This corresponds to using 'complete -C' in non-interactive mode.
// See #2361 .
builtin_missing_argument(parser, streams, cmd, L!("-C"), true);
return Err(STATUS_INVALID_ARGS);
if !is_interactive_session() {
streams.err.append(cmd);
streams
.err
.append(L!(": Can not get commandline in non-interactive mode\n"));
return Err(STATUS_CMD_ERROR);
}
commandline_state.text
}

View File

@@ -400,8 +400,6 @@ pub struct CommandlineState {
pub search_field: Option<(WString, usize)>,
/// pager is visible and search is active
pub search_mode: bool,
/// if false, the reader has not yet been entered
pub initialized: bool,
}
impl CommandlineState {
@@ -415,7 +413,6 @@ const fn new() -> Self {
pager_fully_disclosed: false,
search_field: None,
search_mode: false,
initialized: false,
}
}
}
@@ -1370,7 +1367,6 @@ fn update_commandline_state(&self) {
});
}
snapshot.search_mode = self.history_search.active();
snapshot.initialized = true;
}
/// Apply any changes from the reader snapshot. This is called after running fish script,

View File

@@ -1,5 +1,7 @@
#RUN: %fish %s
set -g fish (status fish-path)
commandline --input "echo foo | bar" --is-valid
and echo Valid
# CHECK: Valid
@@ -50,3 +52,16 @@ commandline --input "echo > {a,b}" --tokens-expanded
commandline --input "echo {arg1,arg2} <in >out" --tokens-raw
# CHECK: echo
# CHECK: {arg1,arg2}
$fish -ic '
commandline hello
commandline
commandline -i world
commandline
commandline --cursor 5
commandline -i " "
commandline
'
# CHECK: hello
# CHECK: helloworld
# CHECK: hello world