From 533c1a89af16e9c5561d5c7680cebb5c1cd5d429 Mon Sep 17 00:00:00 2001 From: Christopher Durham Date: Fri, 15 Dec 2023 08:56:29 -0500 Subject: [PATCH] Only run $env.PROMPT_COMMAND once per prompt (#10986) # Description If `$env.TRANSIENT_PROMPT_COMMAND` is not set, use the prompt created by `$env.PROMPT_COMMAND` instead of running the command a second time. As a side effect, `$env.TRANSIENT_PROMPT_COMMAND` now runs after the hooks `pre_prompt` and `env_change`, instead of before. # User-Facing Changes - `$env.PROMPT_COMMAND` gets run only once per prompt instead of twice - `$env.TRANSIENT_PROMPT_COMMAND` now sees any environment set in a `pre_prompt` or `env_change` hook, like `$env.PROMPT_COMMAND` does --- crates/nu-cli/src/prompt.rs | 55 +++++---- crates/nu-cli/src/prompt_update.rs | 179 +++++++++++------------------ crates/nu-cli/src/repl.rs | 14 +-- 3 files changed, 102 insertions(+), 146 deletions(-) diff --git a/crates/nu-cli/src/prompt.rs b/crates/nu-cli/src/prompt.rs index 9264c1b646..081de2b7f5 100644 --- a/crates/nu-cli/src/prompt.rs +++ b/crates/nu-cli/src/prompt.rs @@ -39,35 +39,6 @@ impl NushellPrompt { } } - pub fn update_prompt_left(&mut self, prompt_string: Option) { - self.left_prompt_string = prompt_string; - } - - pub fn update_prompt_right( - &mut self, - prompt_string: Option, - render_right_prompt_on_last_line: bool, - ) { - self.right_prompt_string = prompt_string; - self.render_right_prompt_on_last_line = render_right_prompt_on_last_line; - } - - pub fn update_prompt_indicator(&mut self, prompt_indicator_string: Option) { - self.default_prompt_indicator = prompt_indicator_string; - } - - pub fn update_prompt_vi_insert(&mut self, prompt_vi_insert_string: Option) { - self.default_vi_insert_prompt_indicator = prompt_vi_insert_string; - } - - pub fn update_prompt_vi_normal(&mut self, prompt_vi_normal_string: Option) { - self.default_vi_normal_prompt_indicator = prompt_vi_normal_string; - } - - pub fn update_prompt_multiline(&mut self, prompt_multiline_indicator_string: Option) { - self.default_multiline_indicator = prompt_multiline_indicator_string; - } - pub fn update_all_prompt_strings( &mut self, left_prompt_string: Option, @@ -90,6 +61,32 @@ impl NushellPrompt { self.render_right_prompt_on_last_line = render_right_prompt_on_last_line; } + pub fn overlay_all_prompt_strings( + &mut self, + left_prompt_string: Option, + right_prompt_string: Option, + prompt_indicator_string: Option, + prompt_multiline_indicator_string: Option, + prompt_vi: (Option, Option), + render_right_prompt_on_last_line: bool, + ) { + let (prompt_vi_insert_string, prompt_vi_normal_string) = prompt_vi; + + self.left_prompt_string = left_prompt_string.or(self.left_prompt_string.take()); + self.right_prompt_string = right_prompt_string.or(self.right_prompt_string.take()); + self.default_prompt_indicator = + prompt_indicator_string.or(self.default_prompt_indicator.take()); + self.default_multiline_indicator = + prompt_multiline_indicator_string.or(self.default_multiline_indicator.take()); + + self.default_vi_insert_prompt_indicator = + prompt_vi_insert_string.or(self.default_vi_insert_prompt_indicator.take()); + self.default_vi_normal_prompt_indicator = + prompt_vi_normal_string.or(self.default_vi_normal_prompt_indicator.take()); + + self.render_right_prompt_on_last_line = render_right_prompt_on_last_line; + } + fn default_wrapped_custom_string(&self, str: String) -> String { format!("({str})") } diff --git a/crates/nu-cli/src/prompt_update.rs b/crates/nu-cli/src/prompt_update.rs index 1df120bd96..a12bb0b6af 100644 --- a/crates/nu-cli/src/prompt_update.rs +++ b/crates/nu-cli/src/prompt_update.rs @@ -7,8 +7,6 @@ use nu_protocol::{ Config, PipelineData, Value, }; use reedline::Prompt; -use std::borrow::Cow; -use std::sync::Arc; // Name of environment variable where the prompt could be stored pub(crate) const PROMPT_COMMAND: &str = "PROMPT_COMMAND"; @@ -153,118 +151,79 @@ pub(crate) fn update_prompt<'prompt>( ret_val } -struct TransientPrompt { - engine_state: Arc, - stack: Stack, -} - -/// Try getting `$env.TRANSIENT_PROMPT_`, and get `$env.PROMPT_` if that fails -fn get_transient_prompt_string( - transient_prompt: &str, - prompt: &str, +pub(crate) fn make_transient_prompt( config: &Config, engine_state: &EngineState, - stack: &mut Stack, -) -> Option { - get_prompt_string(transient_prompt, config, engine_state, stack) - .or_else(|| get_prompt_string(prompt, config, engine_state, stack)) -} + stack: &Stack, + nu_prompt: &NushellPrompt, +) -> Box { + let mut stack = stack.clone(); + let mut nu_prompt = nu_prompt.clone(); -impl Prompt for TransientPrompt { - fn render_prompt_left(&self) -> Cow { - let mut nu_prompt = NushellPrompt::new(); - let config = &self.engine_state.get_config().clone(); - let mut stack = self.stack.clone(); - nu_prompt.update_prompt_left(get_transient_prompt_string( - TRANSIENT_PROMPT_COMMAND, - PROMPT_COMMAND, - config, - &self.engine_state, - &mut stack, - )); - nu_prompt.render_prompt_left().to_string().into() - } + let left_prompt_string = + get_prompt_string(TRANSIENT_PROMPT_COMMAND, config, engine_state, &mut stack); - fn render_prompt_right(&self) -> Cow { - let mut nu_prompt = NushellPrompt::new(); - let config = &self.engine_state.get_config().clone(); - let mut stack = self.stack.clone(); - nu_prompt.update_prompt_right( - get_transient_prompt_string( - TRANSIENT_PROMPT_COMMAND_RIGHT, - PROMPT_COMMAND_RIGHT, - config, - &self.engine_state, - &mut stack, - ), - config.render_right_prompt_on_last_line, - ); - nu_prompt.render_prompt_right().to_string().into() - } + // Now that we have the prompt string lets ansify it. + // <133 A><133 B><133 C> + let left_prompt_string = if config.shell_integration { + if let Some(prompt_string) = left_prompt_string { + Some(format!( + "{PRE_PROMPT_MARKER}{prompt_string}{POST_PROMPT_MARKER}" + )) + } else { + left_prompt_string + } + } else { + left_prompt_string + }; - fn render_prompt_indicator(&self, prompt_mode: reedline::PromptEditMode) -> Cow { - let mut nu_prompt = NushellPrompt::new(); - let config = &self.engine_state.get_config().clone(); - let mut stack = self.stack.clone(); - nu_prompt.update_prompt_indicator(get_transient_prompt_string( - TRANSIENT_PROMPT_INDICATOR, - PROMPT_INDICATOR, - config, - &self.engine_state, - &mut stack, - )); - nu_prompt.update_prompt_vi_insert(get_transient_prompt_string( - TRANSIENT_PROMPT_INDICATOR_VI_INSERT, - PROMPT_INDICATOR_VI_INSERT, - config, - &self.engine_state, - &mut stack, - )); - nu_prompt.update_prompt_vi_normal(get_transient_prompt_string( - TRANSIENT_PROMPT_INDICATOR_VI_NORMAL, - PROMPT_INDICATOR_VI_NORMAL, - config, - &self.engine_state, - &mut stack, - )); - nu_prompt - .render_prompt_indicator(prompt_mode) - .to_string() - .into() - } - - fn render_prompt_multiline_indicator(&self) -> Cow { - let mut nu_prompt = NushellPrompt::new(); - let config = &self.engine_state.get_config().clone(); - let mut stack = self.stack.clone(); - nu_prompt.update_prompt_multiline(get_transient_prompt_string( - TRANSIENT_PROMPT_MULTILINE_INDICATOR, - PROMPT_MULTILINE_INDICATOR, - config, - &self.engine_state, - &mut stack, - )); - nu_prompt - .render_prompt_multiline_indicator() - .to_string() - .into() - } - - fn render_prompt_history_search_indicator( - &self, - history_search: reedline::PromptHistorySearch, - ) -> Cow { - NushellPrompt::new() - .render_prompt_history_search_indicator(history_search) - .to_string() - .into() - } -} - -/// Construct the transient prompt -pub(crate) fn transient_prompt(engine_state: Arc, stack: &Stack) -> Box { - Box::new(TransientPrompt { + let right_prompt_string = get_prompt_string( + TRANSIENT_PROMPT_COMMAND_RIGHT, + config, engine_state, - stack: stack.clone(), - }) + &mut stack, + ); + + let prompt_indicator_string = + get_prompt_string(TRANSIENT_PROMPT_INDICATOR, config, engine_state, &mut stack); + + let prompt_multiline_string = get_prompt_string( + TRANSIENT_PROMPT_MULTILINE_INDICATOR, + config, + engine_state, + &mut stack, + ); + + let prompt_vi_insert_string = get_prompt_string( + TRANSIENT_PROMPT_INDICATOR_VI_INSERT, + config, + engine_state, + &mut stack, + ); + + let prompt_vi_normal_string = get_prompt_string( + TRANSIENT_PROMPT_INDICATOR_VI_NORMAL, + config, + engine_state, + &mut stack, + ); + + // apply the other indicators + nu_prompt.overlay_all_prompt_strings( + left_prompt_string, + right_prompt_string, + prompt_indicator_string, + prompt_multiline_string, + (prompt_vi_insert_string, prompt_vi_normal_string), + config.render_right_prompt_on_last_line, + ); + + trace!( + "make_transient_prompt {}:{}:{}", + file!(), + line!(), + column!() + ); + + Box::new(nu_prompt) } diff --git a/crates/nu-cli/src/repl.rs b/crates/nu-cli/src/repl.rs index 65425d4306..09f190251b 100644 --- a/crates/nu-cli/src/repl.rs +++ b/crates/nu-cli/src/repl.rs @@ -267,11 +267,7 @@ pub fn evaluate_repl( .with_quick_completions(config.quick_completions) .with_partial_completions(config.partial_completions) .with_ansi_colors(config.use_ansi_coloring) - .with_cursor_config(cursor_config) - .with_transient_prompt(prompt_update::transient_prompt( - engine_reference.clone(), - stack, - )); + .with_cursor_config(cursor_config); perf( "reedline builder", start_time, @@ -423,8 +419,11 @@ pub fn evaluate_repl( ); start_time = std::time::Instant::now(); + // Now we compute the prompt after running the hooks let config = &engine_state.get_config().clone(); - let prompt = prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt); + prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt); + let transient_prompt = + prompt_update::make_transient_prompt(config, engine_state, stack, &nu_prompt); perf( "update_prompt", start_time, @@ -437,7 +436,8 @@ pub fn evaluate_repl( entry_num += 1; start_time = std::time::Instant::now(); - let input = line_editor.read_line(prompt); + line_editor = line_editor.with_transient_prompt(transient_prompt); + let input = line_editor.read_line(&nu_prompt); let shell_integration = config.shell_integration; match input {