diff --git a/crates/nu-cli/src/prompt.rs b/crates/nu-cli/src/prompt.rs index 487d2a4243..fb843dcea8 100644 --- a/crates/nu-cli/src/prompt.rs +++ b/crates/nu-cli/src/prompt.rs @@ -7,9 +7,6 @@ use { std::borrow::Cow, }; -const PROMPT_MARKER_BEFORE_PS1: &str = "\x1b]133;A\x1b\\"; // OSC 133;A ST -const PROMPT_MARKER_BEFORE_PS2: &str = "\x1b]133;A;k=s\x1b\\"; // OSC 133;A;k=s ST - /// Nushell prompt definition #[derive(Clone)] pub struct NushellPrompt { @@ -19,7 +16,6 @@ pub struct NushellPrompt { default_vi_insert_prompt_indicator: Option, default_vi_normal_prompt_indicator: Option, default_multiline_indicator: Option, - shell_integration: bool, } impl Default for NushellPrompt { @@ -37,7 +33,6 @@ impl NushellPrompt { default_vi_insert_prompt_indicator: None, default_vi_normal_prompt_indicator: None, default_multiline_indicator: None, - shell_integration: false, } } @@ -87,34 +82,20 @@ impl NushellPrompt { fn default_wrapped_custom_string(&self, str: String) -> String { format!("({})", str) } - - pub(crate) fn enable_shell_integration(&mut self) { - self.shell_integration = true - } } impl Prompt for NushellPrompt { fn render_prompt_left(&self) -> Cow { - // Just before starting to draw the PS1 prompt send the escape code (see - // https://sw.kovidgoyal.net/kitty/shell-integration/#notes-for-shell-developers) - let mut prompt = if self.shell_integration { - String::from(PROMPT_MARKER_BEFORE_PS1) + if let Some(prompt_string) = &self.left_prompt_string { + prompt_string.replace('\n', "\r\n").into() } else { - String::new() - }; - - prompt.push_str(&match &self.left_prompt_string { - Some(prompt_string) => prompt_string.replace('\n', "\r\n"), - None => { - let default = DefaultPrompt::new(); - default - .render_prompt_left() - .to_string() - .replace('\n', "\r\n") - } - }); - - prompt.into() + let default = DefaultPrompt::new(); + default + .render_prompt_left() + .to_string() + .replace('\n', "\r\n") + .into() + } } fn render_prompt_right(&self) -> Cow { @@ -155,21 +136,10 @@ impl Prompt for NushellPrompt { } fn render_prompt_multiline_indicator(&self) -> Cow { - // Just before starting to draw the PS1 prompt send the escape code (see - // https://sw.kovidgoyal.net/kitty/shell-integration/#notes-for-shell-developers) - let mut prompt = if self.shell_integration { - String::from(PROMPT_MARKER_BEFORE_PS2) - } else { - String::new() - }; - - prompt.push_str( - self.default_multiline_indicator - .as_ref() - .unwrap_or(&String::from("::: ")), - ); - - prompt.into() + match &self.default_multiline_indicator { + Some(indicator) => indicator.as_str().into(), + None => "::: ".into(), + } } fn render_prompt_history_search_indicator( diff --git a/crates/nu-cli/src/prompt_update.rs b/crates/nu-cli/src/prompt_update.rs index 1b10cffae4..9cc9ec36fa 100644 --- a/crates/nu-cli/src/prompt_update.rs +++ b/crates/nu-cli/src/prompt_update.rs @@ -147,10 +147,6 @@ pub(crate) fn update_prompt<'prompt>( (prompt_vi_insert_string, prompt_vi_normal_string), ); - if config.shell_integration { - nu_prompt.enable_shell_integration(); - } - let ret_val = nu_prompt as &dyn Prompt; if is_perf_true { info!("update_prompt {}:{}:{}", file!(), line!(), column!()); diff --git a/crates/nu-cli/src/repl.rs b/crates/nu-cli/src/repl.rs index b5f6689f26..50cbc3b2f5 100644 --- a/crates/nu-cli/src/repl.rs +++ b/crates/nu-cli/src/repl.rs @@ -19,7 +19,8 @@ use std::io::{self, Write}; use std::path::PathBuf; use std::{sync::atomic::Ordering, time::Instant}; -const PROMPT_MARKER_BEFORE_CMD: &str = "\x1b]133;C\x1b\\"; // OSC 133;C ST +const PRE_EXECUTE_MARKER: &str = "\x1b]133;A\x1b\\"; +const PRE_PROMPT_MARKER: &str = "\x1b]133;C\x1b\\"; const RESET_APPLICATION_MODE: &str = "\x1b[?1l"; pub fn evaluate_repl( @@ -206,6 +207,10 @@ pub fn evaluate_repl( } } + if config.shell_integration { + run_ansi_sequence(PRE_EXECUTE_MARKER)?; + } + let prompt = prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt, is_perf_true); @@ -221,7 +226,6 @@ pub fn evaluate_repl( } let input = line_editor.read_line(prompt); - let use_shell_integration = config.shell_integration; match input { Ok(Signal::Success(s)) => { @@ -234,6 +238,27 @@ pub fn evaluate_repl( } } + if config.shell_integration { + run_ansi_sequence(RESET_APPLICATION_MODE)?; + run_ansi_sequence(PRE_PROMPT_MARKER)?; + if let Some(cwd) = stack.get_env_var(engine_state, "PWD") { + let path = cwd.as_string()?; + // Try to abbreviate string for windows title + let maybe_abbrev_path = if let Some(p) = nu_path::home_dir() { + path.replace(&p.as_path().display().to_string(), "~") + } else { + path + }; + + // Set window title too + // https://tldp.org/HOWTO/Xterm-Title-3.html + // ESC]0;stringBEL -- Set icon name and window title to string + // ESC]1;stringBEL -- Set icon name to string + // ESC]2;stringBEL -- Set window title to string + run_ansi_sequence(&format!("\x1b]2;{}\x07", maybe_abbrev_path))?; + } + } + let start_time = Instant::now(); let tokens = lex(s.as_bytes(), 0, &[], &[], false); // Check if this is a single call to a directory, if so auto-cd @@ -321,42 +346,6 @@ pub fn evaluate_repl( let _ = std::env::set_current_dir(path); engine_state.add_env_var("PWD".into(), cwd); } - - if use_shell_integration { - // Just before running a command/program, send the escape code (see - // https://sw.kovidgoyal.net/kitty/shell-integration/#notes-for-shell-developers) - let mut ansi_escapes = String::from(RESET_APPLICATION_MODE); - ansi_escapes.push_str(PROMPT_MARKER_BEFORE_CMD); - if let Some(cwd) = stack.get_env_var(engine_state, "PWD") { - let path = cwd.as_string()?; - // Try to abbreviate string for windows title - let maybe_abbrev_path = if let Some(p) = nu_path::home_dir() { - path.replace(&p.as_path().display().to_string(), "~") - } else { - path - }; - - // Set window title too - // https://tldp.org/HOWTO/Xterm-Title-3.html - // ESC]0;stringBEL -- Set icon name and window title to string - // ESC]1;stringBEL -- Set icon name to string - // ESC]2;stringBEL -- Set window title to string - ansi_escapes.push_str(&format!("\x1b]2;{}\x07", maybe_abbrev_path)); - } - match io::stdout().write_all(ansi_escapes.as_bytes()) { - Ok(it) => it, - Err(err) => println!("error: {}", err), - }; - let _ = io::stdout().flush().map_err(|e| { - ShellError::GenericError( - "Error flushing stdio".into(), - e.to_string(), - Some(Span { start: 0, end: 0 }), - None, - Vec::new(), - ) - }); - } } Ok(Signal::CtrlC) => { // `Reedline` clears the line content. New prompt is shown @@ -378,6 +367,30 @@ pub fn evaluate_repl( Ok(()) } +fn run_ansi_sequence(seq: &str) -> Result<(), ShellError> { + match io::stdout().write_all(seq.as_bytes()) { + Ok(it) => it, + Err(err) => { + return Err(ShellError::GenericError( + "Error writing ansi sequence".into(), + err.to_string(), + Some(Span { start: 0, end: 0 }), + None, + Vec::new(), + )); + } + }; + io::stdout().flush().map_err(|e| { + ShellError::GenericError( + "Error flushing stdio".into(), + e.to_string(), + Some(Span { start: 0, end: 0 }), + None, + Vec::new(), + ) + }) +} + pub fn run_hook( engine_state: &EngineState, stack: &mut Stack,