diff --git a/src/env_dispatch.rs b/src/env_dispatch.rs index 6481192f4..bd8ecd7e5 100644 --- a/src/env_dispatch.rs +++ b/src/env_dispatch.rs @@ -5,7 +5,7 @@ use crate::input_common::{update_wait_on_escape_ms, update_wait_on_sequence_key_ms}; use crate::reader::{ reader_change_cursor_end_mode, reader_change_cursor_selection_mode, reader_change_history, - reader_schedule_prompt_repaint, reader_set_autosuggestion_enabled, + reader_schedule_prompt_repaint, reader_set_autosuggestion_enabled, reader_set_transient_prompt, }; use crate::screen::{ screen_set_midnight_commander_hack, IS_DUMB, LAYOUT_CACHE_SHARED, ONLY_GRAYSCALE, @@ -73,6 +73,7 @@ L!("fish_autosuggestion_enabled"), handle_autosuggestion_change, ); + table.add_anon(L!("fish_transient_prompt"), handle_transient_prompt_change); table.add_anon( L!("fish_use_posix_spawn"), handle_fish_use_posix_spawn_change, @@ -284,6 +285,10 @@ fn handle_autosuggestion_change(vars: &EnvStack) { reader_set_autosuggestion_enabled(vars); } +fn handle_transient_prompt_change(vars: &EnvStack) { + reader_set_transient_prompt(vars); +} + fn handle_function_path_change(_: &EnvStack) { function::invalidate_path(); } diff --git a/src/reader.rs b/src/reader.rs index 319bf764a..e89bc396d 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -320,6 +320,9 @@ pub struct ReaderConfig { /// Whether to allow autosuggestions. pub autosuggest_ok: bool, + /// Whether to reexecute prompt function before final rendering. + pub transient_prompt: bool, + /// Whether to expand abbreviations. pub expand_abbrev_ok: bool, @@ -666,12 +669,13 @@ fn read_i(parser: &Parser) { assert_is_main_thread(); parser.assert_can_execute(); let mut conf = ReaderConfig::default(); + conf.event = L!("fish_prompt"); conf.complete_ok = true; conf.highlight_ok = true; conf.syntax_check_ok = true; - conf.autosuggest_ok = check_autosuggestion_enabled(parser.vars()); conf.expand_abbrev_ok = true; - conf.event = L!("fish_prompt"); + conf.autosuggest_ok = check_bool_var(parser.vars(), L!("fish_autosuggestion_enabled"), true); + conf.transient_prompt = check_bool_var(parser.vars(), L!("fish_transient_prompt"), false); if parser.is_breakpoint() && function::exists(DEBUG_PROMPT_FUNCTION_NAME, parser) { conf.left_prompt_cmd = DEBUG_PROMPT_FUNCTION_NAME.to_owned(); @@ -933,25 +937,32 @@ pub fn reader_change_cursor_end_mode(end_mode: CursorEndMode) { } } -fn check_autosuggestion_enabled(vars: &dyn Environment) -> bool { - vars.get(L!("fish_autosuggestion_enabled")) +fn check_bool_var(vars: &dyn Environment, name: &wstr, default: bool) -> bool { + vars.get(name) .map(|v| v.as_string()) .map(|v| v != L!("0")) - .unwrap_or(true) + .unwrap_or(default) } /// Enable or disable autosuggestions based on the associated variable. pub fn reader_set_autosuggestion_enabled(vars: &dyn Environment) { // We don't need to _change_ if we're not initialized yet. - let Some(data) = current_data() else { - return; - }; - let enable = check_autosuggestion_enabled(vars); - if data.conf.autosuggest_ok != enable { - data.conf.autosuggest_ok = enable; - data.force_exec_prompt_and_repaint = true; - data.input_data - .queue_char(CharEvent::from_readline(ReadlineCmd::Repaint)); + if let Some(data) = current_data() { + let enable = check_bool_var(vars, L!("fish_autosuggestion_enabled"), true); + if data.conf.autosuggest_ok != enable { + data.conf.autosuggest_ok = enable; + data.force_exec_prompt_and_repaint = true; + data.input_data + .queue_char(CharEvent::from_readline(ReadlineCmd::Repaint)); + } + } +} + +/// Enable or disable transient prompt based on the associated variable. +pub fn reader_set_transient_prompt(vars: &dyn Environment) { + // We don't need to _change_ if we're not initialized yet. + if let Some(data) = current_data() { + data.conf.transient_prompt = check_bool_var(vars, L!("fish_transient_prompt"), false); } } @@ -2220,7 +2231,7 @@ fn readline(&mut self, nchars: Option) -> Option { if !self.conf.event.is_empty() { event::fire_generic(self.parser, self.conf.event.to_owned(), vec![]); } - self.exec_prompt(true); + self.exec_prompt(true, false); // Start out as initially dirty. self.force_exec_prompt_and_repaint = true; @@ -2231,6 +2242,10 @@ fn readline(&mut self, nchars: Option) -> Option { } } + if self.conf.transient_prompt { + self.exec_prompt(true, true); + } + // Redraw the command line. This is what ensures the autosuggestion is hidden, etc. after the // user presses enter. if self.is_repaint_needed(None) @@ -2736,7 +2751,7 @@ fn handle_readline_command(&mut self, c: ReadlineCmd) { // This can happen e.g. if a variable triggers a repaint, // and the variable is set inside the prompt (#7324). // builtin commandline will refuse to enqueue these. - self.exec_prompt(false); + self.exec_prompt(false, false); if !self.mode_prompt_buff.is_empty() { if self.is_repaint_needed(None) { self.screen.reset_line(/*repaint_prompt=*/ true); @@ -2747,7 +2762,7 @@ fn handle_readline_command(&mut self, c: ReadlineCmd) { } // Else we repaint as normal. } - self.exec_prompt(true); + self.exec_prompt(true, false); self.screen.reset_line(/*repaint_prompt=*/ true); self.layout_and_repaint(L!("readline")); self.force_exec_prompt_and_repaint = false; @@ -3853,7 +3868,7 @@ fn clear_screen_and_repaint(&mut self) { self.screen.reset_line(/*repaint_prompt=*/ true); self.layout_and_repaint(L!("readline")); - self.exec_prompt(true); + self.exec_prompt(true, false); self.screen.reset_line(/*repaint_prompt=*/ true); self.layout_and_repaint(L!("readline")); self.force_exec_prompt_and_repaint = false; @@ -4523,14 +4538,23 @@ pub fn reader_write_title( } impl<'a> Reader<'a> { - fn exec_prompt_cmd(&self, prompt_cmd: &wstr) -> Vec { + fn exec_prompt_cmd(&self, prompt_cmd: &wstr, final_prompt: bool) -> Vec { let mut output = vec![]; - let _ = exec_subshell(prompt_cmd, self.parser, Some(&mut output), false); + if final_prompt && function::exists(prompt_cmd, self.parser) { + let _ = exec_subshell( + &(prompt_cmd.to_owned() + L!(" --final-rendering")), + self.parser, + Some(&mut output), + false, + ); + } else { + let _ = exec_subshell(prompt_cmd, self.parser, Some(&mut output), false); + }; output } /// Execute prompt commands based on the provided arguments. The output is inserted into prompt_buff. - fn exec_prompt(&mut self, full_prompt: bool) { + fn exec_prompt(&mut self, full_prompt: bool, final_prompt: bool) { // Suppress fish_trace while in the prompt. let _suppress_trace = self.parser.push_scope(|s| s.suppress_fish_trace = true); @@ -4545,7 +4569,7 @@ fn exec_prompt(&mut self, full_prompt: bool) { if function::exists(MODE_PROMPT_FUNCTION_NAME, self.parser) { // We do not support multiline mode indicators, so just concatenate all of them. self.mode_prompt_buff = - WString::from_iter(self.exec_prompt_cmd(MODE_PROMPT_FUNCTION_NAME)); + WString::from_iter(self.exec_prompt_cmd(MODE_PROMPT_FUNCTION_NAME, final_prompt)); } if full_prompt { @@ -4564,7 +4588,12 @@ fn exec_prompt(&mut self, full_prompt: bool) { DEFAULT_PROMPT }; - self.left_prompt_buff = join_strings(&self.exec_prompt_cmd(prompt_cmd), '\n'); + self.left_prompt_buff = + join_strings(&self.exec_prompt_cmd(prompt_cmd, final_prompt), '\n'); + + if final_prompt { + self.screen.reset_line(true) + } } // Don't execute the right prompt if it is undefined fish_right_prompt @@ -4573,8 +4602,9 @@ fn exec_prompt(&mut self, full_prompt: bool) { || function::exists(&self.conf.right_prompt_cmd, self.parser)) { // Right prompt does not support multiple lines, so just concatenate all of them. - self.right_prompt_buff = - WString::from_iter(self.exec_prompt_cmd(&self.conf.right_prompt_cmd)); + self.right_prompt_buff = WString::from_iter( + self.exec_prompt_cmd(&self.conf.right_prompt_cmd, final_prompt), + ); } }