From d77f1753c2714d97396286e0e94e2d996e75b343 Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Sat, 25 Nov 2023 09:42:05 -0600 Subject: [PATCH] add shape `ExternalResolved` to show found externals via syntax highlighting in the repl (#11135) # Description This PR enables a new feature that shows which externals are found in your path via the syntax highlighter as you type. ![external_resolved](https://github.com/nushell/nushell/assets/343840/e5fa91f0-6fac-485c-8afc-5711fc0ed9bc) This idea could use some improvement where it caches the items in your path and on some trigger, expires that cache and creates a new on. Right now, all it does is call the `which` crate on every character you type. This could be problematic if you have hundreds of paths in your PATH or if some of your paths in your Path point to extraordinarily slow file systems. WSL pointing to Windows comes to mind. Either way, I've thrown it up here for people to try and provide feedback. I think the novelty of showing what is valid and what isn't is pretty cool. I believe fish-shell also does this, IIRC. # User-Facing Changes # Tests + Formatting # After Submitting --- Cargo.lock | 1 + crates/nu-cli/Cargo.toml | 1 + crates/nu-cli/src/syntax_highlight.rs | 20 ++++++++++++++++++- crates/nu-color-config/src/shape_color.rs | 1 + crates/nu-parser/src/flatten.rs | 2 ++ crates/nu-protocol/src/config/mod.rs | 5 +++++ .../src/sample_config/default_config.nu | 5 ++++- 7 files changed, 33 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 364d2d927..8cd33b2ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2723,6 +2723,7 @@ dependencies = [ "sysinfo", "unicode-segmentation", "uuid", + "which 5.0.0", ] [[package]] diff --git a/crates/nu-cli/Cargo.toml b/crates/nu-cli/Cargo.toml index 6afa3678f..fd800c856 100644 --- a/crates/nu-cli/Cargo.toml +++ b/crates/nu-cli/Cargo.toml @@ -40,6 +40,7 @@ pathdiff = "0.2" sysinfo = "0.29" unicode-segmentation = "1.10" uuid = { version = "1.6.0", features = ["v4"] } +which = "5.0.0" [features] plugin = [] diff --git a/crates/nu-cli/src/syntax_highlight.rs b/crates/nu-cli/src/syntax_highlight.rs index 8f1265499..babe1635d 100644 --- a/crates/nu-cli/src/syntax_highlight.rs +++ b/crates/nu-cli/src/syntax_highlight.rs @@ -17,10 +17,27 @@ impl Highlighter for NuHighlighter { fn highlight(&self, line: &str, _cursor: usize) -> StyledText { trace!("highlighting: {}", line); + let highlight_resolved_externals = + self.engine_state.get_config().highlight_resolved_externals; let mut working_set = StateWorkingSet::new(&self.engine_state); let block = parse(&mut working_set, None, line.as_bytes(), false); let (shapes, global_span_offset) = { - let shapes = flatten_block(&working_set, &block); + let mut shapes = flatten_block(&working_set, &block); + // Highlighting externals has a config point because of concerns that using which to resolve + // externals may slow down things too much. + if highlight_resolved_externals { + for (span, shape) in shapes.iter_mut() { + if *shape == FlatShape::External { + let str_contents = + working_set.get_span_contents(Span::new(span.start, span.end)); + + let str_word = String::from_utf8_lossy(str_contents).to_string(); + if which::which(str_word).ok().is_some() { + *shape = FlatShape::ExternalResolved; + } + } + } + } (shapes, self.engine_state.next_span_start()) }; @@ -91,6 +108,7 @@ impl Highlighter for NuHighlighter { FlatShape::InternalCall(_) => add_colored_token(&shape.1, next_token), FlatShape::External => add_colored_token(&shape.1, next_token), FlatShape::ExternalArg => add_colored_token(&shape.1, next_token), + FlatShape::ExternalResolved => add_colored_token(&shape.1, next_token), FlatShape::Keyword => add_colored_token(&shape.1, next_token), FlatShape::Literal => add_colored_token(&shape.1, next_token), FlatShape::Operator => add_colored_token(&shape.1, next_token), diff --git a/crates/nu-color-config/src/shape_color.rs b/crates/nu-color-config/src/shape_color.rs index a3db8a377..188abda99 100644 --- a/crates/nu-color-config/src/shape_color.rs +++ b/crates/nu-color-config/src/shape_color.rs @@ -15,6 +15,7 @@ pub fn default_shape_color(shape: String) -> Style { "shape_directory" => Style::new().fg(Color::Cyan), "shape_external" => Style::new().fg(Color::Cyan), "shape_externalarg" => Style::new().fg(Color::Green).bold(), + "shape_external_resolved" => Style::new().fg(Color::LightYellow).bold(), "shape_filepath" => Style::new().fg(Color::Cyan), "shape_flag" => Style::new().fg(Color::Blue).bold(), "shape_float" => Style::new().fg(Color::Purple).bold(), diff --git a/crates/nu-parser/src/flatten.rs b/crates/nu-parser/src/flatten.rs index 6f364951a..b2bf26f46 100644 --- a/crates/nu-parser/src/flatten.rs +++ b/crates/nu-parser/src/flatten.rs @@ -18,6 +18,7 @@ pub enum FlatShape { Directory, External, ExternalArg, + ExternalResolved, Filepath, Flag, Float, @@ -57,6 +58,7 @@ impl Display for FlatShape { FlatShape::Directory => write!(f, "shape_directory"), FlatShape::External => write!(f, "shape_external"), FlatShape::ExternalArg => write!(f, "shape_externalarg"), + FlatShape::ExternalResolved => write!(f, "shape_external_resolved"), FlatShape::Filepath => write!(f, "shape_filepath"), FlatShape::Flag => write!(f, "shape_flag"), FlatShape::Float => write!(f, "shape_float"), diff --git a/crates/nu-protocol/src/config/mod.rs b/crates/nu-protocol/src/config/mod.rs index e414773e6..dc2566aa6 100644 --- a/crates/nu-protocol/src/config/mod.rs +++ b/crates/nu-protocol/src/config/mod.rs @@ -72,6 +72,7 @@ pub struct Config { pub datetime_table_format: Option, pub error_style: ErrorStyle, pub use_kitty_protocol: bool, + pub highlight_resolved_externals: bool, } impl Default for Config { @@ -137,6 +138,7 @@ impl Default for Config { error_style: ErrorStyle::Fancy, use_kitty_protocol: false, + highlight_resolved_externals: false, } } } @@ -622,6 +624,9 @@ impl Value { "use_kitty_protocol" => { process_bool_config(value, &mut errors, &mut config.use_kitty_protocol); } + "highlight_resolved_externals" => { + process_bool_config(value, &mut errors, &mut config.highlight_resolved_externals); + } // Menus "menus" => match create_menus(value) { Ok(map) => config.menus = map, diff --git a/crates/nu-utils/src/sample_config/default_config.nu b/crates/nu-utils/src/sample_config/default_config.nu index d01687e08..f07764ead 100644 --- a/crates/nu-utils/src/sample_config/default_config.nu +++ b/crates/nu-utils/src/sample_config/default_config.nu @@ -42,6 +42,7 @@ let dark_theme = { shape_directory: cyan shape_external: cyan shape_externalarg: green_bold + shape_external_resolved: light_yellow_bold shape_filepath: cyan shape_flag: blue_bold shape_float: purple_bold @@ -106,6 +107,7 @@ let light_theme = { shape_directory: cyan shape_external: cyan shape_externalarg: green_bold + shape_external_resolved: light_purple_bold shape_filepath: cyan shape_flag: blue_bold shape_float: purple_bold @@ -233,7 +235,8 @@ $env.config = { edit_mode: emacs # emacs, vi shell_integration: false # enables terminal shell integration. Off by default, as some terminals have issues with this. render_right_prompt_on_last_line: false # true or false to enable or disable right prompt to be rendered on last line of the prompt. - use_kitty_protocol: false # enables keyboard enhancement protocol implemented by kitty console, only if your terminal support this + use_kitty_protocol: false # enables keyboard enhancement protocol implemented by kitty console, only if your terminal support this. + highlight_resolved_externals: false # true enables highlighting of external commands in the repl resolved by which. hooks: { pre_prompt: [{ null }] # run before the prompt is shown