mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-05-23 04:51:16 -03:00
Query terminal only just before reading from it
Commit5e317497ef(Query terminal before reading config, 2025-05-17) disabled the kitty keyboard protocol in "fish -c read". This seems surprising, and it's not actually necessary that we query before reading config; we only need query results before we read from the TTY for the first time (which is about the time we call __fish_config_interactive). Let's do that, reverting parts of5e317497ef.
This commit is contained in:
@@ -1696,6 +1696,8 @@ Fish also provides additional information through the values of certain environm
|
||||
.. envvar:: fish_terminal
|
||||
|
||||
the name and version of the terminal fish is running inside (for example as reported via :ref:`XTVERSION <term-compat-xtversion>`).
|
||||
This is initialized just before the first interactive prompt is shown (possibly via builtin :doc:`read <cmds/read>`),
|
||||
that is, on the first ``fish_prompt`` or ``fish_read`` :ref:`event <event>`.
|
||||
|
||||
.. envvar:: history
|
||||
|
||||
|
||||
@@ -39,13 +39,11 @@
|
||||
environment::{env_init, EnvStack, Environment},
|
||||
EnvMode, Statuses,
|
||||
},
|
||||
env_dispatch::guess_emoji_width,
|
||||
eprintf,
|
||||
event::{self, Event},
|
||||
flog::{self, activate_flog_categories_by_pattern, set_flog_file_fd, FLOG, FLOGF},
|
||||
fprintf, function, future_feature_flags as features,
|
||||
history::{self, start_private_mode},
|
||||
input_common::InputEventQueuer,
|
||||
io::IoChain,
|
||||
nix::{getpid, getrusage, isatty, RUsage},
|
||||
panic::panic_handler,
|
||||
@@ -59,11 +57,10 @@
|
||||
get_login, is_interactive_session, mark_login, mark_no_exec, proc_init,
|
||||
set_interactive_session, Pid,
|
||||
},
|
||||
reader::{reader_init, reader_read, term_copy_modes, terminal_init},
|
||||
reader::{reader_init, reader_read, term_copy_modes},
|
||||
signal::{signal_clear_cancel, signal_unblock_all},
|
||||
threads::{self},
|
||||
topic_monitor,
|
||||
tty_handoff::xtversion,
|
||||
wchar::prelude::*,
|
||||
wutil::waccess,
|
||||
};
|
||||
@@ -525,44 +522,6 @@ fn throwing_main() -> i32 {
|
||||
let parser = &Parser::new(env, CancelBehavior::Clear);
|
||||
parser.set_syncs_uvars(!opts.no_config);
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
enum CommandSource {
|
||||
Arguments,
|
||||
Stdin,
|
||||
File,
|
||||
}
|
||||
let command_source = if !opts.batch_cmds.is_empty() {
|
||||
CommandSource::Arguments
|
||||
} else if my_optind == args.len() {
|
||||
CommandSource::Stdin
|
||||
} else {
|
||||
CommandSource::File
|
||||
};
|
||||
|
||||
if command_source == CommandSource::Stdin && isatty(STDIN_FILENO) {
|
||||
// Implicitly interactive mode.
|
||||
if opts.no_exec {
|
||||
FLOG!(
|
||||
error,
|
||||
"no-execute mode enabled and no script given. Exiting"
|
||||
);
|
||||
// above line should always exit
|
||||
return libc::EXIT_FAILURE;
|
||||
}
|
||||
let mut input_queue = terminal_init();
|
||||
let input_data = input_queue.get_input_data_mut();
|
||||
parser
|
||||
.pending_input
|
||||
.borrow_mut()
|
||||
.extend(std::mem::take(&mut input_data.queue));
|
||||
parser.vars().set_one(
|
||||
L!("fish_terminal"),
|
||||
EnvMode::GLOBAL,
|
||||
xtversion().unwrap().to_owned(),
|
||||
);
|
||||
guess_emoji_width(parser.vars());
|
||||
}
|
||||
|
||||
if !opts.no_exec && !opts.no_config {
|
||||
read_init(parser, config_paths.as_ref().unwrap());
|
||||
}
|
||||
@@ -604,7 +563,7 @@ enum CommandSource {
|
||||
// Clear signals in case we were interrupted (#9024).
|
||||
signal_clear_cancel();
|
||||
|
||||
if command_source == CommandSource::Arguments {
|
||||
if !opts.batch_cmds.is_empty() {
|
||||
// Run the commands specified as arguments, if any.
|
||||
if get_login() {
|
||||
// Do something nasty to support OpenSUSE assuming we're bash. This may modify cmds.
|
||||
@@ -621,8 +580,17 @@ enum CommandSource {
|
||||
);
|
||||
res = run_command_list(parser, &opts.batch_cmds);
|
||||
parser.libdata_mut().exit_current_script = false;
|
||||
} else if command_source == CommandSource::Stdin {
|
||||
res = reader_read(parser, STDIN_FILENO, &IoChain::new());
|
||||
} else if my_optind == args.len() {
|
||||
// Implicitly interactive mode.
|
||||
if opts.no_exec && isatty(libc::STDIN_FILENO) {
|
||||
FLOG!(
|
||||
error,
|
||||
"no-execute mode enabled and no script given. Exiting"
|
||||
);
|
||||
// above line should always exit
|
||||
return libc::EXIT_FAILURE;
|
||||
}
|
||||
res = reader_read(parser, libc::STDIN_FILENO, &IoChain::new());
|
||||
} else {
|
||||
let n = wcs2string(&args[my_optind]);
|
||||
let path = OsStr::from_bytes(&n);
|
||||
|
||||
@@ -313,7 +313,12 @@ fn throwing_main() -> i32 {
|
||||
return 1;
|
||||
}
|
||||
|
||||
let input_queue = terminal_init();
|
||||
let input_queue = {
|
||||
let vars = EnvStack::new();
|
||||
env_stack_set_from_env!(vars, "STY");
|
||||
env_stack_set_from_env!(vars, "TERM");
|
||||
terminal_init(&vars, STDIN_FILENO)
|
||||
};
|
||||
|
||||
setup_and_process_keys(&mut streams, continuous_mode, verbose, input_queue)
|
||||
.builtin_status_code()
|
||||
|
||||
@@ -2079,3 +2079,17 @@ fn to_cstring(self) -> CString {
|
||||
CString::new(self).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! env_stack_set_from_env {
|
||||
($vars:ident, $var_name:literal) => {{
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
if let Some(var) = std::env::var_os($var_name) {
|
||||
$vars.set_one(
|
||||
L!($var_name),
|
||||
$crate::env::EnvMode::GLOBAL,
|
||||
$crate::common::str2wcstring(var.as_bytes()),
|
||||
);
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
@@ -369,6 +369,7 @@ pub fn env_dispatch_init(vars: &EnvStack) {
|
||||
fn run_inits(vars: &EnvStack) {
|
||||
init_locale(vars);
|
||||
init_terminal(vars);
|
||||
guess_emoji_width(vars);
|
||||
update_wait_on_escape_ms(vars);
|
||||
update_wait_on_sequence_key_ms(vars);
|
||||
handle_read_limit_change(vars);
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
use std::ops::Range;
|
||||
use std::os::fd::BorrowedFd;
|
||||
use std::os::fd::{AsRawFd, RawFd};
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
#[cfg(target_has_atomic = "64")]
|
||||
@@ -66,6 +65,7 @@
|
||||
use crate::editable_line::{line_at_cursor, range_of_line_at_cursor, Edit, EditableLine};
|
||||
use crate::env::EnvStack;
|
||||
use crate::env::{EnvMode, Environment, Statuses};
|
||||
use crate::env_dispatch::guess_emoji_width;
|
||||
use crate::exec::exec_subshell;
|
||||
use crate::expand::expand_one;
|
||||
use crate::expand::{expand_string, expand_tilde, ExpandFlags, ExpandResultCode};
|
||||
@@ -148,6 +148,7 @@
|
||||
TOK_SHOW_COMMENTS,
|
||||
};
|
||||
use crate::tty_handoff::get_scroll_content_up_capability;
|
||||
use crate::tty_handoff::xtversion;
|
||||
use crate::tty_handoff::SCROLL_CONTENT_UP_TERMINFO_CODE;
|
||||
use crate::tty_handoff::{
|
||||
get_tty_protocols_active, initialize_tty_metadata, safe_deactivate_tty_protocols, TtyHandoff,
|
||||
@@ -269,26 +270,26 @@ fn querying_allowed(in_fd: RawFd) -> bool {
|
||||
&& isatty(STDOUT_FILENO)
|
||||
}
|
||||
|
||||
pub fn terminal_init() -> InputEventQueue {
|
||||
pub fn terminal_init(vars: &dyn Environment, inputfd: RawFd) -> InputEventQueue {
|
||||
reader_interactive_init();
|
||||
|
||||
let mut input_queue = InputEventQueue::new(STDIN_FILENO);
|
||||
let mut input_queue = InputEventQueue::new(inputfd);
|
||||
|
||||
let _init_tty_metadata = ScopeGuard::new((), |()| {
|
||||
initialize_tty_metadata();
|
||||
});
|
||||
|
||||
if !querying_allowed(STDIN_FILENO) {
|
||||
if !querying_allowed(inputfd) {
|
||||
return input_queue;
|
||||
}
|
||||
|
||||
set_shell_modes(STDIN_FILENO, "initial query");
|
||||
set_shell_modes(inputfd, "initial query");
|
||||
{
|
||||
let mut out = BufferedOutputter::new(Outputter::stdoutput());
|
||||
// Query for kitty keyboard protocol support.
|
||||
out.write_command(QueryKittyKeyboardProgressiveEnhancements);
|
||||
out.write_command(QueryXtversion);
|
||||
query_capabilities_via_dcs(out.by_ref());
|
||||
query_capabilities_via_dcs(out.by_ref(), vars);
|
||||
out.write_command(QueryPrimaryDeviceAttribute);
|
||||
}
|
||||
input_queue.blocking_query().replace(TerminalQuery::Initial);
|
||||
@@ -363,13 +364,20 @@ pub fn current_data() -> Option<&'static mut ReaderData> {
|
||||
/// If `history_name` is empty, then save history in-memory only; do not write it to disk.
|
||||
pub fn reader_push<'a>(parser: &'a Parser, history_name: &wstr, conf: ReaderConfig) -> Reader<'a> {
|
||||
assert_is_main_thread();
|
||||
let hist = History::with_name(history_name);
|
||||
hist.resolve_pending();
|
||||
let data = ReaderData::new(hist, conf, reader_data_stack().is_empty());
|
||||
reader_data_stack().push(data);
|
||||
let data = current_data().unwrap();
|
||||
data.command_line_changed(EditableLineTag::Commandline, AutosuggestionUpdate::Remove);
|
||||
if !parser.interactive_initialized.swap(true) {
|
||||
let mut input_queue = terminal_init(parser.vars(), conf.inputfd);
|
||||
let input_data = input_queue.get_input_data_mut();
|
||||
parser
|
||||
.pending_input
|
||||
.borrow_mut()
|
||||
.extend(std::mem::take(&mut input_data.queue));
|
||||
parser.vars().set_one(
|
||||
L!("fish_terminal"),
|
||||
EnvMode::GLOBAL,
|
||||
xtversion().unwrap().to_owned(),
|
||||
);
|
||||
guess_emoji_width(parser.vars());
|
||||
|
||||
// Provide value for `status current-command`
|
||||
parser.libdata_mut().status_vars.command = L!("fish").to_owned();
|
||||
// Also provide a value for the deprecated fish 2.0 $_ variable
|
||||
@@ -377,7 +385,15 @@ pub fn reader_push<'a>(parser: &'a Parser, history_name: &wstr, conf: ReaderConf
|
||||
.vars()
|
||||
.set_one(L!("_"), EnvMode::GLOBAL, L!("fish").to_owned());
|
||||
}
|
||||
Reader { data, parser }
|
||||
let hist = History::with_name(history_name);
|
||||
hist.resolve_pending();
|
||||
let data = ReaderData::new(hist, conf, reader_data_stack().is_empty());
|
||||
reader_data_stack().push(data);
|
||||
let data = current_data().unwrap();
|
||||
data.command_line_changed(EditableLineTag::Commandline, AutosuggestionUpdate::Remove);
|
||||
let mut reader = Reader { data, parser };
|
||||
reader.insert_front(parser.pending_input.take());
|
||||
reader
|
||||
}
|
||||
|
||||
/// Return to previous reader environment.
|
||||
@@ -2303,7 +2319,6 @@ fn readline(&mut self, nchars: Option<NonZeroUsize>) -> Option<WString> {
|
||||
// Start out as initially dirty.
|
||||
self.force_exec_prompt_and_repaint = true;
|
||||
|
||||
self.insert_front(self.parser.pending_input.take());
|
||||
while !self.rls().finished && !check_exit_loop_maybe_warning(Some(self)) {
|
||||
// Enable tty protocols while we read input.
|
||||
tty.enable_tty_protocols();
|
||||
@@ -2664,13 +2679,12 @@ fn send_xtgettcap_query(out: &mut impl Output, cap: &'static str) {
|
||||
|
||||
#[allow(renamed_and_removed_lints)]
|
||||
#[allow(clippy::blocks_in_if_conditions)] // for old clippy
|
||||
fn query_capabilities_via_dcs(out: &mut impl Output) {
|
||||
fn query_capabilities_via_dcs(out: &mut impl Output, vars: &dyn Environment) {
|
||||
if {
|
||||
use std::env::var_os;
|
||||
var_os("STY").is_some()
|
||||
|| var_os("TERM").is_some_and(|term| {
|
||||
let screens: [&[u8]; 2] = [b"screen", b"screen-256color"];
|
||||
screens.contains(&term.as_bytes())
|
||||
vars.get_unless_empty(L!("STY")).is_some()
|
||||
|| vars.get_unless_empty(L!("TERM")).is_some_and(|term| {
|
||||
let term = &term.as_list()[0];
|
||||
term == "screen" || term == "screen-256color"
|
||||
})
|
||||
} {
|
||||
return;
|
||||
|
||||
@@ -167,22 +167,11 @@ pub fn initialize_gettext() {}
|
||||
/// available. Without this, early error messages cannot be localized.
|
||||
#[cfg(feature = "localize-messages")]
|
||||
pub fn initialize_gettext() {
|
||||
use crate::common::str2wcstring;
|
||||
use crate::env::EnvMode;
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
|
||||
let locale_vars = EnvStack::new();
|
||||
macro_rules! from_env {
|
||||
($var_name:literal) => {
|
||||
if let Some(var) = std::env::var_os($var_name) {
|
||||
locale_vars.set_one(L!($var_name), EnvMode::GLOBAL, str2wcstring(var.as_bytes()));
|
||||
}
|
||||
};
|
||||
}
|
||||
from_env!("LANGUAGE");
|
||||
from_env!("LC_ALL");
|
||||
from_env!("LC_MESSAGES");
|
||||
from_env!("LANG");
|
||||
env_stack_set_from_env!(locale_vars, "LANGUAGE");
|
||||
env_stack_set_from_env!(locale_vars, "LC_ALL");
|
||||
env_stack_set_from_env!(locale_vars, "LC_MESSAGES");
|
||||
env_stack_set_from_env!(locale_vars, "LANG");
|
||||
|
||||
gettext_impl::update_locale_from_env(&locale_vars);
|
||||
}
|
||||
|
||||
@@ -27,3 +27,9 @@ sendline("true ($fish_test_helper abandon_tty)")
|
||||
expect_prompt()
|
||||
sendline("echo even cooler")
|
||||
expect_prompt("even cooler")
|
||||
|
||||
sendline("TERM=not-dumb $fish -c read")
|
||||
sp.send_primary_device_attribute()
|
||||
sendline("something")
|
||||
sp.expect_re(r"\x1b\[\?u")
|
||||
expect_prompt()
|
||||
|
||||
Reference in New Issue
Block a user