mirror of
https://github.com/nushell/nushell.git
synced 2024-12-22 23:23:12 +01:00
colored file-like completions (#11702)
<!-- if this PR closes one or more issues, you can automatically link the PR with them by using one of the [*linking keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword), e.g. - this PR should close #xxxx - fixes #xxxx you can also mention related issues, PRs or discussions! --> # Description <!-- Thank you for improving Nushell. Please, check our [contributing guide](../CONTRIBUTING.md) and talk to the core team before making major changes. Description of your pull request goes here. **Provide examples and/or screenshots** if your changes affect the user experience. --> `ls` and other file completions uses `LS_COLORS`. ![maim-2024 01 31 21 34 31](https://github.com/nushell/nushell/assets/15631555/d5c3813f-77b5-4391-aa0b-4b2125e5aca5) # User-Facing Changes <!-- List of all changes that impact the user experience here. This helps us keep track of breaking changes. --> # Tests + Formatting <!-- Don't forget to add tests that cover your changes. Make sure you've run and fixed any issues with these commands: - `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes) - `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to check that you're using the standard code style - `cargo test --workspace` to check that all tests pass (on Windows make sure to [enable developer mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging)) - `cargo run -- -c "use std testing; testing run-tests --path crates/nu-std"` to run the tests for the standard library > **Note** > from `nushell` you can also use the `toolkit` as follows > ```bash > use toolkit.nu # or use an `env_change` hook to activate it automatically > toolkit check pr > ``` --> # After Submitting <!-- If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date. --> --------- Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
This commit is contained in:
parent
bacc1f6317
commit
5042f19d1b
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2843,6 +2843,7 @@ dependencies = [
|
||||
"fuzzy-matcher",
|
||||
"is_executable",
|
||||
"log",
|
||||
"lscolors",
|
||||
"miette",
|
||||
"nu-ansi-term",
|
||||
"nu-cmd-base",
|
||||
|
@ -34,6 +34,7 @@ fuzzy-matcher = "0.3"
|
||||
is_executable = "1.0"
|
||||
log = "0.4"
|
||||
miette = { version = "7.0", features = ["fancy-no-backtrace"] }
|
||||
lscolors = { version = "0.17", default-features = false, features = ["nu-ansi-term"] }
|
||||
once_cell = "1.18"
|
||||
percent-encoding = "2"
|
||||
pathdiff = "0.2"
|
||||
|
@ -266,8 +266,10 @@ impl NuCompleter {
|
||||
|| prev_expr_str == b"overlay use"
|
||||
|| prev_expr_str == b"source-env"
|
||||
{
|
||||
let mut completer =
|
||||
DotNuCompletion::new(self.engine_state.clone());
|
||||
let mut completer = DotNuCompletion::new(
|
||||
self.engine_state.clone(),
|
||||
self.stack.clone(),
|
||||
);
|
||||
|
||||
return self.process_completion(
|
||||
&mut completer,
|
||||
@ -278,8 +280,10 @@ impl NuCompleter {
|
||||
pos,
|
||||
);
|
||||
} else if prev_expr_str == b"ls" {
|
||||
let mut completer =
|
||||
FileCompletion::new(self.engine_state.clone());
|
||||
let mut completer = FileCompletion::new(
|
||||
self.engine_state.clone(),
|
||||
self.stack.clone(),
|
||||
);
|
||||
|
||||
return self.process_completion(
|
||||
&mut completer,
|
||||
@ -313,8 +317,10 @@ impl NuCompleter {
|
||||
);
|
||||
}
|
||||
FlatShape::Directory => {
|
||||
let mut completer =
|
||||
DirectoryCompletion::new(self.engine_state.clone());
|
||||
let mut completer = DirectoryCompletion::new(
|
||||
self.engine_state.clone(),
|
||||
self.stack.clone(),
|
||||
);
|
||||
|
||||
return self.process_completion(
|
||||
&mut completer,
|
||||
@ -326,8 +332,10 @@ impl NuCompleter {
|
||||
);
|
||||
}
|
||||
FlatShape::Filepath | FlatShape::GlobPattern => {
|
||||
let mut completer =
|
||||
FileCompletion::new(self.engine_state.clone());
|
||||
let mut completer = FileCompletion::new(
|
||||
self.engine_state.clone(),
|
||||
self.stack.clone(),
|
||||
);
|
||||
|
||||
return self.process_completion(
|
||||
&mut completer,
|
||||
@ -374,8 +382,10 @@ impl NuCompleter {
|
||||
}
|
||||
|
||||
// Check for file completion
|
||||
let mut completer =
|
||||
FileCompletion::new(self.engine_state.clone());
|
||||
let mut completer = FileCompletion::new(
|
||||
self.engine_state.clone(),
|
||||
self.stack.clone(),
|
||||
);
|
||||
out = self.process_completion(
|
||||
&mut completer,
|
||||
&working_set,
|
||||
|
@ -1,6 +1,10 @@
|
||||
use crate::completions::{matches, CompletionOptions};
|
||||
use nu_ansi_term::Style;
|
||||
use nu_engine::env_to_string;
|
||||
use nu_path::home_dir;
|
||||
use nu_protocol::engine::{EngineState, Stack};
|
||||
use nu_protocol::{engine::StateWorkingSet, Span};
|
||||
use nu_utils::get_ls_colors;
|
||||
use std::path::{is_separator, Component, Path, PathBuf, MAIN_SEPARATOR as SEP};
|
||||
|
||||
fn complete_rec(
|
||||
@ -92,10 +96,21 @@ pub fn complete_item(
|
||||
partial: &str,
|
||||
cwd: &str,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<(nu_protocol::Span, String)> {
|
||||
engine_state: &EngineState,
|
||||
stack: &Stack,
|
||||
) -> Vec<(nu_protocol::Span, String, Option<Style>)> {
|
||||
let partial = surround_remove(partial);
|
||||
let isdir = partial.ends_with(is_separator);
|
||||
let cwd_pathbuf = Path::new(cwd).to_path_buf();
|
||||
let ls_colors = (engine_state.config.use_ls_colors_completions
|
||||
&& engine_state.config.use_ansi_coloring)
|
||||
.then(|| {
|
||||
let ls_colors_env_str = match stack.get_env_var(engine_state, "LS_COLORS") {
|
||||
Some(v) => env_to_string("LS_COLORS", &v, engine_state, stack).ok(),
|
||||
None => None,
|
||||
};
|
||||
get_ls_colors(ls_colors_env_str)
|
||||
});
|
||||
let mut original_cwd = OriginalCwd::None;
|
||||
let mut components = Path::new(&partial).components().peekable();
|
||||
let mut cwd = match components.peek().cloned() {
|
||||
@ -148,7 +163,18 @@ pub fn complete_item(
|
||||
|
||||
complete_rec(partial.as_slice(), &cwd, options, want_directory, isdir)
|
||||
.into_iter()
|
||||
.map(|p| (span, escape_path(original_cwd.apply(&p), want_directory)))
|
||||
.map(|p| {
|
||||
let path = original_cwd.apply(&p);
|
||||
let style = ls_colors.as_ref().map(|lsc| {
|
||||
lsc.style_for_path_with_metadata(
|
||||
&path,
|
||||
std::fs::symlink_metadata(&path).ok().as_ref(),
|
||||
)
|
||||
.map(lscolors::Style::to_nu_ansi_term_style)
|
||||
.unwrap_or_default()
|
||||
});
|
||||
(span, escape_path(path, want_directory), style)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
@ -2,8 +2,9 @@ use crate::completions::{
|
||||
completion_common::{adjust_if_intermediate, complete_item, AdjustView},
|
||||
Completer, CompletionOptions, SortBy,
|
||||
};
|
||||
use nu_ansi_term::Style;
|
||||
use nu_protocol::{
|
||||
engine::{EngineState, StateWorkingSet},
|
||||
engine::{EngineState, Stack, StateWorkingSet},
|
||||
levenshtein_distance, Span,
|
||||
};
|
||||
use reedline::Suggestion;
|
||||
@ -13,11 +14,15 @@ use std::sync::Arc;
|
||||
#[derive(Clone)]
|
||||
pub struct DirectoryCompletion {
|
||||
engine_state: Arc<EngineState>,
|
||||
stack: Stack,
|
||||
}
|
||||
|
||||
impl DirectoryCompletion {
|
||||
pub fn new(engine_state: Arc<EngineState>) -> Self {
|
||||
Self { engine_state }
|
||||
pub fn new(engine_state: Arc<EngineState>, stack: Stack) -> Self {
|
||||
Self {
|
||||
engine_state,
|
||||
stack,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,12 +44,14 @@ impl Completer for DirectoryCompletion {
|
||||
&prefix,
|
||||
&self.engine_state.current_work_dir(),
|
||||
options,
|
||||
self.engine_state.as_ref(),
|
||||
&self.stack,
|
||||
)
|
||||
.into_iter()
|
||||
.map(move |x| Suggestion {
|
||||
value: x.1,
|
||||
description: None,
|
||||
style: None,
|
||||
style: x.2,
|
||||
extra: None,
|
||||
span: reedline::Span {
|
||||
start: x.0.start - offset,
|
||||
@ -113,6 +120,8 @@ pub fn directory_completion(
|
||||
partial: &str,
|
||||
cwd: &str,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<(nu_protocol::Span, String)> {
|
||||
complete_item(true, span, partial, cwd, options)
|
||||
engine_state: &EngineState,
|
||||
stack: &Stack,
|
||||
) -> Vec<(nu_protocol::Span, String, Option<Style>)> {
|
||||
complete_item(true, span, partial, cwd, options, engine_state, stack)
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::completions::{file_path_completion, Completer, CompletionOptions, SortBy};
|
||||
use nu_protocol::{
|
||||
engine::{EngineState, StateWorkingSet},
|
||||
engine::{EngineState, Stack, StateWorkingSet},
|
||||
Span,
|
||||
};
|
||||
use reedline::Suggestion;
|
||||
@ -12,11 +12,15 @@ use std::{
|
||||
#[derive(Clone)]
|
||||
pub struct DotNuCompletion {
|
||||
engine_state: Arc<EngineState>,
|
||||
stack: Stack,
|
||||
}
|
||||
|
||||
impl DotNuCompletion {
|
||||
pub fn new(engine_state: Arc<EngineState>) -> Self {
|
||||
Self { engine_state }
|
||||
pub fn new(engine_state: Arc<EngineState>, stack: Stack) -> Self {
|
||||
Self {
|
||||
engine_state,
|
||||
stack,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,7 +96,14 @@ impl Completer for DotNuCompletion {
|
||||
let output: Vec<Suggestion> = search_dirs
|
||||
.into_iter()
|
||||
.flat_map(|search_dir| {
|
||||
let completions = file_path_completion(span, &partial, &search_dir, options);
|
||||
let completions = file_path_completion(
|
||||
span,
|
||||
&partial,
|
||||
&search_dir,
|
||||
options,
|
||||
self.engine_state.as_ref(),
|
||||
&self.stack,
|
||||
);
|
||||
completions
|
||||
.into_iter()
|
||||
.filter(move |it| {
|
||||
@ -111,7 +122,7 @@ impl Completer for DotNuCompletion {
|
||||
.map(move |x| Suggestion {
|
||||
value: x.1,
|
||||
description: None,
|
||||
style: None,
|
||||
style: x.2,
|
||||
extra: None,
|
||||
span: reedline::Span {
|
||||
start: x.0.start - offset,
|
||||
|
@ -2,8 +2,9 @@ use crate::completions::{
|
||||
completion_common::{adjust_if_intermediate, complete_item, AdjustView},
|
||||
Completer, CompletionOptions, SortBy,
|
||||
};
|
||||
use nu_ansi_term::Style;
|
||||
use nu_protocol::{
|
||||
engine::{EngineState, StateWorkingSet},
|
||||
engine::{EngineState, Stack, StateWorkingSet},
|
||||
levenshtein_distance, Span,
|
||||
};
|
||||
use nu_utils::IgnoreCaseExt;
|
||||
@ -14,11 +15,15 @@ use std::sync::Arc;
|
||||
#[derive(Clone)]
|
||||
pub struct FileCompletion {
|
||||
engine_state: Arc<EngineState>,
|
||||
stack: Stack,
|
||||
}
|
||||
|
||||
impl FileCompletion {
|
||||
pub fn new(engine_state: Arc<EngineState>) -> Self {
|
||||
Self { engine_state }
|
||||
pub fn new(engine_state: Arc<EngineState>, stack: Stack) -> Self {
|
||||
Self {
|
||||
engine_state,
|
||||
stack,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,12 +49,14 @@ impl Completer for FileCompletion {
|
||||
&prefix,
|
||||
&self.engine_state.current_work_dir(),
|
||||
options,
|
||||
self.engine_state.as_ref(),
|
||||
&self.stack,
|
||||
)
|
||||
.into_iter()
|
||||
.map(move |x| Suggestion {
|
||||
value: x.1,
|
||||
description: None,
|
||||
style: None,
|
||||
style: x.2,
|
||||
extra: None,
|
||||
span: reedline::Span {
|
||||
start: x.0.start - offset,
|
||||
@ -118,8 +125,10 @@ pub fn file_path_completion(
|
||||
partial: &str,
|
||||
cwd: &str,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<(nu_protocol::Span, String)> {
|
||||
complete_item(false, span, partial, cwd, options)
|
||||
engine_state: &EngineState,
|
||||
stack: &Stack,
|
||||
) -> Vec<(nu_protocol::Span, String, Option<Style>)> {
|
||||
complete_item(false, span, partial, cwd, options, engine_state, stack)
|
||||
}
|
||||
|
||||
pub fn matches(partial: &str, from: &str, options: &CompletionOptions) -> bool {
|
||||
|
@ -897,13 +897,10 @@ fn render_path_name(
|
||||
|
||||
let stripped_path = nu_utils::strip_ansi_unlikely(path);
|
||||
|
||||
let (style, has_metadata) = match std::fs::symlink_metadata(stripped_path.as_ref()) {
|
||||
Ok(metadata) => (
|
||||
ls_colors.style_for_path_with_metadata(stripped_path.as_ref(), Some(&metadata)),
|
||||
true,
|
||||
),
|
||||
Err(_) => (ls_colors.style_for_path(stripped_path.as_ref()), false),
|
||||
};
|
||||
let metadata = std::fs::symlink_metadata(stripped_path.as_ref());
|
||||
let has_metadata = metadata.is_ok();
|
||||
let style =
|
||||
ls_colors.style_for_path_with_metadata(stripped_path.as_ref(), metadata.ok().as_ref());
|
||||
|
||||
// clickable links don't work in remote SSH sessions
|
||||
let in_ssh_session = std::env::var("SSH_CLIENT").is_ok();
|
||||
|
@ -89,6 +89,7 @@ pub struct Config {
|
||||
pub error_style: ErrorStyle,
|
||||
pub use_kitty_protocol: bool,
|
||||
pub highlight_resolved_externals: bool,
|
||||
pub use_ls_colors_completions: bool,
|
||||
/// Configuration for plugins.
|
||||
///
|
||||
/// Users can provide configuration for a plugin through this entry. The entry name must
|
||||
@ -129,6 +130,7 @@ impl Default for Config {
|
||||
enable_external_completion: true,
|
||||
max_external_completion_results: 100,
|
||||
external_completer: None,
|
||||
use_ls_colors_completions: false,
|
||||
|
||||
filesize_metric: false,
|
||||
filesize_format: "auto".into(),
|
||||
@ -349,6 +351,9 @@ impl Value {
|
||||
*value = reconstruct_external(&config, span);
|
||||
}
|
||||
}
|
||||
"use_ls_colors" => {
|
||||
process_bool_config(value, &mut errors, &mut config.use_ls_colors_completions);
|
||||
}
|
||||
_ => {
|
||||
report_invalid_key(&[key, key2], span, &mut errors);
|
||||
return false;
|
||||
@ -366,6 +371,7 @@ impl Value {
|
||||
"algorithm" => config.completion_algorithm.reconstruct_value(span),
|
||||
"case_sensitive" => Value::bool(config.case_sensitive_completions, span),
|
||||
"external" => reconstruct_external(&config, span),
|
||||
"use_ls_colors" => Value::bool(config.use_ls_colors_completions, span),
|
||||
},
|
||||
span,
|
||||
);
|
||||
|
@ -212,6 +212,7 @@ $env.config = {
|
||||
max_results: 100 # setting it lower can improve completion performance at the cost of omitting some options
|
||||
completer: null # check 'carapace_completer' above as an example
|
||||
}
|
||||
use_ls_colors: false # set this to true to enable file/path/directory completions using LS_COLORS
|
||||
}
|
||||
|
||||
filesize: {
|
||||
|
Loading…
Reference in New Issue
Block a user