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:
Steven
2024-02-08 12:29:28 -08:00
committed by GitHub
parent bacc1f6317
commit 5042f19d1b
10 changed files with 107 additions and 36 deletions

View File

@ -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,

View File

@ -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()
}

View File

@ -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)
}

View File

@ -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,

View File

@ -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 {