2022-08-23 16:24:24 +02:00
|
|
|
pub mod support;
|
|
|
|
|
2025-02-17 18:52:07 +01:00
|
|
|
use std::{
|
|
|
|
fs::{read_dir, FileType, ReadDir},
|
|
|
|
path::{PathBuf, MAIN_SEPARATOR},
|
|
|
|
sync::Arc,
|
|
|
|
};
|
|
|
|
|
2022-08-23 16:24:24 +02:00
|
|
|
use nu_cli::NuCompleter;
|
2024-04-21 07:03:33 +02:00
|
|
|
use nu_engine::eval_block;
|
2022-08-23 16:24:24 +02:00
|
|
|
use nu_parser::parse;
|
2025-02-17 18:52:07 +01:00
|
|
|
use nu_path::expand_tilde;
|
2024-04-21 07:03:33 +02:00
|
|
|
use nu_protocol::{debugger::WithoutDebug, engine::StateWorkingSet, PipelineData};
|
2025-03-07 08:38:42 +01:00
|
|
|
use nu_std::load_standard_library;
|
2022-08-23 16:24:24 +02:00
|
|
|
use reedline::{Completer, Suggestion};
|
|
|
|
use rstest::{fixture, rstest};
|
2023-10-02 19:44:51 +02:00
|
|
|
use support::{
|
2025-02-23 19:47:49 +01:00
|
|
|
completions_helpers::{
|
|
|
|
new_dotnu_engine, new_external_engine, new_partial_engine, new_quote_engine,
|
|
|
|
},
|
2025-02-28 19:39:59 +01:00
|
|
|
file, folder, match_suggestions, match_suggestions_by_string, new_engine,
|
2023-10-02 19:44:51 +02:00
|
|
|
};
|
2022-08-23 16:24:24 +02:00
|
|
|
|
2025-02-17 18:52:07 +01:00
|
|
|
// Match a list of suggestions with the content of a directory.
|
|
|
|
// This helper is for DotNutCompletion, so actually it only retrieves
|
|
|
|
// *.nu files and subdirectories.
|
|
|
|
pub fn match_dir_content_for_dotnu(dir: ReadDir, suggestions: &[Suggestion]) {
|
|
|
|
let actual_dir_entries: Vec<_> = dir.filter_map(|c| c.ok()).collect();
|
|
|
|
let type_name_pairs: Vec<(FileType, String)> = actual_dir_entries
|
|
|
|
.into_iter()
|
|
|
|
.filter_map(|t| t.file_type().ok().zip(t.file_name().into_string().ok()))
|
|
|
|
.collect();
|
|
|
|
let mut simple_dir_entries: Vec<&str> = type_name_pairs
|
|
|
|
.iter()
|
|
|
|
.filter_map(|(t, n)| {
|
|
|
|
if t.is_dir() || n.ends_with(".nu") {
|
|
|
|
Some(n.as_str())
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
simple_dir_entries.sort();
|
|
|
|
let mut pure_suggestions: Vec<&str> = suggestions
|
|
|
|
.iter()
|
|
|
|
.map(|s| {
|
|
|
|
// The file names in suggestions contain some extra characters,
|
|
|
|
// we clean them to compare more exactly with read_dir result.
|
|
|
|
s.value
|
|
|
|
.as_str()
|
|
|
|
.trim_end_matches('`')
|
|
|
|
.trim_end_matches('/')
|
|
|
|
.trim_end_matches('\\')
|
|
|
|
.trim_start_matches('`')
|
|
|
|
.trim_start_matches("~/")
|
|
|
|
.trim_start_matches("~\\")
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
pure_suggestions.sort();
|
|
|
|
assert_eq!(simple_dir_entries, pure_suggestions);
|
|
|
|
}
|
|
|
|
|
2022-08-23 16:24:24 +02:00
|
|
|
#[fixture]
|
|
|
|
fn completer() -> NuCompleter {
|
|
|
|
// Create a new engine
|
2024-09-25 20:04:26 +02:00
|
|
|
let (_, _, mut engine, mut stack) = new_engine();
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
// Add record value as example
|
|
|
|
let record = "def tst [--mod -s] {}";
|
2024-09-25 20:04:26 +02:00
|
|
|
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack).is_ok());
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
// Instantiate a new completer
|
2024-05-09 07:38:24 +02:00
|
|
|
NuCompleter::new(Arc::new(engine), Arc::new(stack))
|
2022-08-23 16:24:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[fixture]
|
|
|
|
fn completer_strings() -> NuCompleter {
|
|
|
|
// Create a new engine
|
2024-09-25 20:04:26 +02:00
|
|
|
let (_, _, mut engine, mut stack) = new_engine();
|
|
|
|
|
2022-08-23 16:24:24 +02:00
|
|
|
// Add record value as example
|
|
|
|
let record = r#"def animals [] { ["cat", "dog", "eel" ] }
|
|
|
|
def my-command [animal: string@animals] { print $animal }"#;
|
2024-09-25 20:04:26 +02:00
|
|
|
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack).is_ok());
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
// Instantiate a new completer
|
2024-05-09 07:38:24 +02:00
|
|
|
NuCompleter::new(Arc::new(engine), Arc::new(stack))
|
2022-08-23 16:24:24 +02:00
|
|
|
}
|
|
|
|
|
2022-12-21 23:33:26 +01:00
|
|
|
#[fixture]
|
|
|
|
fn extern_completer() -> NuCompleter {
|
|
|
|
// Create a new engine
|
2024-09-25 20:04:26 +02:00
|
|
|
let (_, _, mut engine, mut stack) = new_engine();
|
2022-12-21 23:33:26 +01:00
|
|
|
|
|
|
|
// Add record value as example
|
|
|
|
let record = r#"
|
|
|
|
def animals [] { [ "cat", "dog", "eel" ] }
|
|
|
|
extern spam [
|
|
|
|
animal: string@animals
|
|
|
|
--foo (-f): string@animals
|
|
|
|
-b: string@animals
|
|
|
|
]
|
|
|
|
"#;
|
2024-09-25 20:04:26 +02:00
|
|
|
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack).is_ok());
|
2022-12-21 23:33:26 +01:00
|
|
|
|
|
|
|
// Instantiate a new completer
|
2024-05-09 07:38:24 +02:00
|
|
|
NuCompleter::new(Arc::new(engine), Arc::new(stack))
|
2022-12-21 23:33:26 +01:00
|
|
|
}
|
|
|
|
|
Custom completions: Inherit case_sensitive option from $env.config (#14738)
# Description
Currently, if a custom completer returns a record containing an
`options` field, but these options don't specify `case_sensitive`,
`case_sensitive` will be true. This PR instead makes the default value
whatever the user specified in `$env.config.completions.case_sensitive`.
The match algorithm option already does this. `positional` is also
inherited from the global config, although user's can't actually specify
that one themselves in `$env.config` (I'm planning on getting rid of
`positional` in a separate PR).
# User-Facing Changes
For those making custom completions, if they need matching to be done
case-sensitively and:
- their completer returns a record rather than a list,
- and the record contains an `options` field,
- and the `options` field is a record,
- and the record doesn't contain a `case_sensitive` option,
then they will need to specify `case_sensitive: true` in their custom
completer's options. Otherwise, if the user sets
`$env.config.completions.case_sensitive = false`, their custom completer
will also use case-insensitive matching.
Others shouldn't have to make any changes.
# Tests + Formatting
Updated tests to check if `case_sensitive`. Basically rewrote them,
actually. I figured it'd be better to make a single helper function that
takes completer options and completion suggestions and generates a
completer from that rather than having multiple fixtures providing
different completers.
# After Submitting
Probably needs to be noted in the release notes, but I don't think the
[docs](https://www.nushell.sh/book/custom_completions.html#options-for-custom-completions)
need to be updated.
2025-01-07 18:52:31 +01:00
|
|
|
fn custom_completer_with_options(
|
|
|
|
global_opts: &str,
|
|
|
|
completer_opts: &str,
|
|
|
|
completions: &[&str],
|
|
|
|
) -> NuCompleter {
|
fix: Respect sort in custom completions (#14424)
<!--
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
This PR makes it so that when a custom completer sets `options.sort` to
true, completions aren't sorted. Previously, in #13311, I'd made it so
that setting `sort` to true would sort in alphabetical order, while
omitting it or setting it to false would sort it in the default order
for the chosen match algorithm (alphabetical for prefix matching, fuzzy
match score for fuzzy matching). I'd assumed that you'd always want to
sort completions and the important thing was choosing alphabetical
sorting vs the default sort order for your match algorithm. However,
this assumption was incorrect (see #13696 and [this
thread](https://discord.com/channels/601130461678272522/1302332259227144294)
in Discord).
An alternative would be to make `sort` accept `"alphabetical"`,
`"smart"`, and `"none"`/`null` rather than keeping it a boolean. But
that would be a breaking change and require more discussion, and I
wanted to keep this PR simple/small so that we can go back to the
sensible behavior as soon as possible.
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
Here are the different scenarios:
- If your custom completer returns a record with an `options` field
that's a record:
- If `options` contains `sort: true`, completions **will be sorted
according to the order set in the user's config**. Previously, they
would have been sorted in alphabetical order. This does mean that
**custom completers cannot explicitly choose to sort in alphabetical
order** anymore. I think that's an acceptable trade-off, though.
- If `options` contains `sort: false`, completions will not be sorted.
#13311 broke things so they would be sorted in the default order for the
match algorithm used. Before that PR, completions would not have been
sorted.
- If there's no `sort` option, that **will be treated as `sort: true`**.
Previously, this would have been treated as `sort: false`.
- Otherwise, nothing changes. Completions will still be sorted.
# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
Added 1 test to make sure that completions aren't sorted with `sort:
false` explicitly set.
# 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.
-->
2024-11-30 02:47:57 +01:00
|
|
|
let (_, _, mut engine, mut stack) = new_engine();
|
Custom completions: Inherit case_sensitive option from $env.config (#14738)
# Description
Currently, if a custom completer returns a record containing an
`options` field, but these options don't specify `case_sensitive`,
`case_sensitive` will be true. This PR instead makes the default value
whatever the user specified in `$env.config.completions.case_sensitive`.
The match algorithm option already does this. `positional` is also
inherited from the global config, although user's can't actually specify
that one themselves in `$env.config` (I'm planning on getting rid of
`positional` in a separate PR).
# User-Facing Changes
For those making custom completions, if they need matching to be done
case-sensitively and:
- their completer returns a record rather than a list,
- and the record contains an `options` field,
- and the `options` field is a record,
- and the record doesn't contain a `case_sensitive` option,
then they will need to specify `case_sensitive: true` in their custom
completer's options. Otherwise, if the user sets
`$env.config.completions.case_sensitive = false`, their custom completer
will also use case-insensitive matching.
Others shouldn't have to make any changes.
# Tests + Formatting
Updated tests to check if `case_sensitive`. Basically rewrote them,
actually. I figured it'd be better to make a single helper function that
takes completer options and completion suggestions and generates a
completer from that rather than having multiple fixtures providing
different completers.
# After Submitting
Probably needs to be noted in the release notes, but I don't think the
[docs](https://www.nushell.sh/book/custom_completions.html#options-for-custom-completions)
need to be updated.
2025-01-07 18:52:31 +01:00
|
|
|
let command = format!(
|
|
|
|
r#"
|
|
|
|
{}
|
|
|
|
def comp [] {{
|
|
|
|
{{ completions: [{}], options: {{ {} }} }}
|
|
|
|
}}
|
|
|
|
def my-command [arg: string@comp] {{}}"#,
|
|
|
|
global_opts,
|
|
|
|
completions
|
|
|
|
.iter()
|
|
|
|
.map(|comp| format!("'{}'", comp))
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join(", "),
|
|
|
|
completer_opts,
|
|
|
|
);
|
fix: Respect sort in custom completions (#14424)
<!--
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
This PR makes it so that when a custom completer sets `options.sort` to
true, completions aren't sorted. Previously, in #13311, I'd made it so
that setting `sort` to true would sort in alphabetical order, while
omitting it or setting it to false would sort it in the default order
for the chosen match algorithm (alphabetical for prefix matching, fuzzy
match score for fuzzy matching). I'd assumed that you'd always want to
sort completions and the important thing was choosing alphabetical
sorting vs the default sort order for your match algorithm. However,
this assumption was incorrect (see #13696 and [this
thread](https://discord.com/channels/601130461678272522/1302332259227144294)
in Discord).
An alternative would be to make `sort` accept `"alphabetical"`,
`"smart"`, and `"none"`/`null` rather than keeping it a boolean. But
that would be a breaking change and require more discussion, and I
wanted to keep this PR simple/small so that we can go back to the
sensible behavior as soon as possible.
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
Here are the different scenarios:
- If your custom completer returns a record with an `options` field
that's a record:
- If `options` contains `sort: true`, completions **will be sorted
according to the order set in the user's config**. Previously, they
would have been sorted in alphabetical order. This does mean that
**custom completers cannot explicitly choose to sort in alphabetical
order** anymore. I think that's an acceptable trade-off, though.
- If `options` contains `sort: false`, completions will not be sorted.
#13311 broke things so they would be sorted in the default order for the
match algorithm used. Before that PR, completions would not have been
sorted.
- If there's no `sort` option, that **will be treated as `sort: true`**.
Previously, this would have been treated as `sort: false`.
- Otherwise, nothing changes. Completions will still be sorted.
# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
Added 1 test to make sure that completions aren't sorted with `sort:
false` explicitly set.
# 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.
-->
2024-11-30 02:47:57 +01:00
|
|
|
assert!(support::merge_input(command.as_bytes(), &mut engine, &mut stack).is_ok());
|
|
|
|
|
|
|
|
NuCompleter::new(Arc::new(engine), Arc::new(stack))
|
|
|
|
}
|
|
|
|
|
2023-11-29 23:17:06 +01:00
|
|
|
#[fixture]
|
|
|
|
fn custom_completer() -> NuCompleter {
|
|
|
|
// Create a new engine
|
2024-09-25 20:04:26 +02:00
|
|
|
let (_, _, mut engine, mut stack) = new_engine();
|
2023-11-29 23:17:06 +01:00
|
|
|
|
|
|
|
// Add record value as example
|
|
|
|
let record = r#"
|
2024-01-17 16:40:59 +01:00
|
|
|
let external_completer = {|spans|
|
2023-11-29 23:17:06 +01:00
|
|
|
$spans
|
|
|
|
}
|
|
|
|
|
|
|
|
$env.config.completions.external = {
|
|
|
|
enable: true
|
|
|
|
max_results: 100
|
|
|
|
completer: $external_completer
|
|
|
|
}
|
|
|
|
"#;
|
2024-09-25 20:04:26 +02:00
|
|
|
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack).is_ok());
|
2023-11-29 23:17:06 +01:00
|
|
|
|
|
|
|
// Instantiate a new completer
|
2024-05-09 07:38:24 +02:00
|
|
|
NuCompleter::new(Arc::new(engine), Arc::new(stack))
|
2023-11-29 23:17:06 +01:00
|
|
|
}
|
|
|
|
|
2024-08-06 02:30:10 +02:00
|
|
|
/// Use fuzzy completions but sort in alphabetical order
|
|
|
|
#[fixture]
|
|
|
|
fn fuzzy_alpha_sort_completer() -> NuCompleter {
|
|
|
|
// Create a new engine
|
2024-09-25 20:04:26 +02:00
|
|
|
let (_, _, mut engine, mut stack) = new_engine();
|
2024-08-06 02:30:10 +02:00
|
|
|
|
|
|
|
let config = r#"
|
|
|
|
$env.config.completions.algorithm = "fuzzy"
|
|
|
|
$env.config.completions.sort = "alphabetical"
|
|
|
|
"#;
|
2024-09-25 20:04:26 +02:00
|
|
|
assert!(support::merge_input(config.as_bytes(), &mut engine, &mut stack).is_ok());
|
2024-08-06 02:30:10 +02:00
|
|
|
|
|
|
|
// Instantiate a new completer
|
|
|
|
NuCompleter::new(Arc::new(engine), Arc::new(stack))
|
|
|
|
}
|
|
|
|
|
2022-09-22 20:50:16 +02:00
|
|
|
#[test]
|
2024-07-06 00:58:35 +02:00
|
|
|
fn variables_dollar_sign_with_variablecompletion() {
|
2022-09-22 20:50:16 +02:00
|
|
|
let (_, _, engine, stack) = new_engine();
|
|
|
|
|
2024-05-09 07:38:24 +02:00
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
2022-09-22 20:50:16 +02:00
|
|
|
|
|
|
|
let target_dir = "$ ";
|
|
|
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
|
|
|
|
2024-12-27 12:45:52 +01:00
|
|
|
assert_eq!(9, suggestions.len());
|
2022-09-22 20:50:16 +02:00
|
|
|
}
|
|
|
|
|
2022-08-23 16:24:24 +02:00
|
|
|
#[rstest]
|
2022-09-22 20:50:16 +02:00
|
|
|
fn variables_double_dash_argument_with_flagcompletion(mut completer: NuCompleter) {
|
2022-08-23 16:24:24 +02:00
|
|
|
let suggestions = completer.complete("tst --", 6);
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["--help", "--mod"];
|
2022-08-23 16:24:24 +02:00
|
|
|
// dbg!(&expected, &suggestions);
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2022-08-23 16:24:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[rstest]
|
2022-09-22 20:50:16 +02:00
|
|
|
fn variables_single_dash_argument_with_flagcompletion(mut completer: NuCompleter) {
|
2022-08-23 16:24:24 +02:00
|
|
|
let suggestions = completer.complete("tst -", 5);
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["--help", "--mod", "-h", "-s"];
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2022-08-23 16:24:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[rstest]
|
2022-09-22 20:50:16 +02:00
|
|
|
fn variables_command_with_commandcompletion(mut completer_strings: NuCompleter) {
|
2022-09-29 10:43:58 +02:00
|
|
|
let suggestions = completer_strings.complete("my-c ", 4);
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["my-command"];
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2022-08-23 16:24:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[rstest]
|
2022-09-22 20:50:16 +02:00
|
|
|
fn variables_subcommands_with_customcompletion(mut completer_strings: NuCompleter) {
|
2022-08-23 16:24:24 +02:00
|
|
|
let suggestions = completer_strings.complete("my-command ", 11);
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["cat", "dog", "eel"];
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2022-08-23 16:24:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[rstest]
|
2022-09-22 20:50:16 +02:00
|
|
|
fn variables_customcompletion_subcommands_with_customcompletion_2(
|
|
|
|
mut completer_strings: NuCompleter,
|
|
|
|
) {
|
2022-08-23 16:24:24 +02:00
|
|
|
let suggestions = completer_strings.complete("my-command ", 11);
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["cat", "dog", "eel"];
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2022-08-23 16:24:24 +02:00
|
|
|
}
|
|
|
|
|
Custom completions: Inherit case_sensitive option from $env.config (#14738)
# Description
Currently, if a custom completer returns a record containing an
`options` field, but these options don't specify `case_sensitive`,
`case_sensitive` will be true. This PR instead makes the default value
whatever the user specified in `$env.config.completions.case_sensitive`.
The match algorithm option already does this. `positional` is also
inherited from the global config, although user's can't actually specify
that one themselves in `$env.config` (I'm planning on getting rid of
`positional` in a separate PR).
# User-Facing Changes
For those making custom completions, if they need matching to be done
case-sensitively and:
- their completer returns a record rather than a list,
- and the record contains an `options` field,
- and the `options` field is a record,
- and the record doesn't contain a `case_sensitive` option,
then they will need to specify `case_sensitive: true` in their custom
completer's options. Otherwise, if the user sets
`$env.config.completions.case_sensitive = false`, their custom completer
will also use case-insensitive matching.
Others shouldn't have to make any changes.
# Tests + Formatting
Updated tests to check if `case_sensitive`. Basically rewrote them,
actually. I figured it'd be better to make a single helper function that
takes completer options and completion suggestions and generates a
completer from that rather than having multiple fixtures providing
different completers.
# After Submitting
Probably needs to be noted in the release notes, but I don't think the
[docs](https://www.nushell.sh/book/custom_completions.html#options-for-custom-completions)
need to be updated.
2025-01-07 18:52:31 +01:00
|
|
|
/// $env.config should be overridden by the custom completer's options
|
|
|
|
#[test]
|
|
|
|
fn customcompletions_override_options() {
|
|
|
|
let mut completer = custom_completer_with_options(
|
|
|
|
r#"$env.config.completions.algorithm = "fuzzy"
|
|
|
|
$env.config.completions.case_sensitive = false"#,
|
|
|
|
r#"completion_algorithm: "prefix",
|
|
|
|
positional: false,
|
|
|
|
case_sensitive: true,
|
|
|
|
sort: true"#,
|
|
|
|
&["Foo Abcdef", "Abcdef", "Acd Bar"],
|
|
|
|
);
|
|
|
|
|
|
|
|
// positional: false should make it do substring matching
|
|
|
|
// sort: true should force sorting
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["Abcdef", "Foo Abcdef"];
|
Custom completions: Inherit case_sensitive option from $env.config (#14738)
# Description
Currently, if a custom completer returns a record containing an
`options` field, but these options don't specify `case_sensitive`,
`case_sensitive` will be true. This PR instead makes the default value
whatever the user specified in `$env.config.completions.case_sensitive`.
The match algorithm option already does this. `positional` is also
inherited from the global config, although user's can't actually specify
that one themselves in `$env.config` (I'm planning on getting rid of
`positional` in a separate PR).
# User-Facing Changes
For those making custom completions, if they need matching to be done
case-sensitively and:
- their completer returns a record rather than a list,
- and the record contains an `options` field,
- and the `options` field is a record,
- and the record doesn't contain a `case_sensitive` option,
then they will need to specify `case_sensitive: true` in their custom
completer's options. Otherwise, if the user sets
`$env.config.completions.case_sensitive = false`, their custom completer
will also use case-insensitive matching.
Others shouldn't have to make any changes.
# Tests + Formatting
Updated tests to check if `case_sensitive`. Basically rewrote them,
actually. I figured it'd be better to make a single helper function that
takes completer options and completion suggestions and generates a
completer from that rather than having multiple fixtures providing
different completers.
# After Submitting
Probably needs to be noted in the release notes, but I don't think the
[docs](https://www.nushell.sh/book/custom_completions.html#options-for-custom-completions)
need to be updated.
2025-01-07 18:52:31 +01:00
|
|
|
let suggestions = completer.complete("my-command Abcd", 15);
|
Use right options in custom completions (#13698)
<!--
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.
-->
This issue was reported by kira in
[Discord](https://discord.com/channels/601130461678272522/1276981416307069019).
In https://github.com/nushell/nushell/pull/13311, I accidentally made it
so that custom completions are filtered according to the user's
configured completion options (`$env.config.completions`) rather than
the completion options provided as part of the custom completions. This
PR is a quick fix for that.
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
It should once again be possible to override match algorithm, case
sensitivity, and substring matching (`positional`) in custom
completions.
# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
Added a couple tests.
# 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.
-->
fdncred and I discussed this in Discord a bit and we thought it might be
better to not allow custom completions to override the user's config.
However, `positional` can't currently be set inside one's config, so you
can only do strict prefix matching, no substring matching. Another PR
could do one of the following:
- Document the fact that you can provide completion options inside
custom completions
- Remove the ability to provide completion options with custom
completions and add a `$env.config.completions.positional: true` option
- Remove the ability to provide completion options with custom
completions and add a new match algorithm `substring` (this is the one I
like most, since `positional` only applies to prefix matching anyway)
Separately from these options, we could also allow completers to specify
that they don't Nushell to do any filtering and sorting on the provided
custom completions.
2024-08-26 19:14:57 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
Custom completions: Inherit case_sensitive option from $env.config (#14738)
# Description
Currently, if a custom completer returns a record containing an
`options` field, but these options don't specify `case_sensitive`,
`case_sensitive` will be true. This PR instead makes the default value
whatever the user specified in `$env.config.completions.case_sensitive`.
The match algorithm option already does this. `positional` is also
inherited from the global config, although user's can't actually specify
that one themselves in `$env.config` (I'm planning on getting rid of
`positional` in a separate PR).
# User-Facing Changes
For those making custom completions, if they need matching to be done
case-sensitively and:
- their completer returns a record rather than a list,
- and the record contains an `options` field,
- and the `options` field is a record,
- and the record doesn't contain a `case_sensitive` option,
then they will need to specify `case_sensitive: true` in their custom
completer's options. Otherwise, if the user sets
`$env.config.completions.case_sensitive = false`, their custom completer
will also use case-insensitive matching.
Others shouldn't have to make any changes.
# Tests + Formatting
Updated tests to check if `case_sensitive`. Basically rewrote them,
actually. I figured it'd be better to make a single helper function that
takes completer options and completion suggestions and generates a
completer from that rather than having multiple fixtures providing
different completers.
# After Submitting
Probably needs to be noted in the release notes, but I don't think the
[docs](https://www.nushell.sh/book/custom_completions.html#options-for-custom-completions)
need to be updated.
2025-01-07 18:52:31 +01:00
|
|
|
|
|
|
|
// Custom options should make case-sensitive
|
|
|
|
let suggestions = completer.complete("my-command aBcD", 15);
|
|
|
|
assert!(suggestions.is_empty());
|
Use right options in custom completions (#13698)
<!--
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.
-->
This issue was reported by kira in
[Discord](https://discord.com/channels/601130461678272522/1276981416307069019).
In https://github.com/nushell/nushell/pull/13311, I accidentally made it
so that custom completions are filtered according to the user's
configured completion options (`$env.config.completions`) rather than
the completion options provided as part of the custom completions. This
PR is a quick fix for that.
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
It should once again be possible to override match algorithm, case
sensitivity, and substring matching (`positional`) in custom
completions.
# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
Added a couple tests.
# 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.
-->
fdncred and I discussed this in Discord a bit and we thought it might be
better to not allow custom completions to override the user's config.
However, `positional` can't currently be set inside one's config, so you
can only do strict prefix matching, no substring matching. Another PR
could do one of the following:
- Document the fact that you can provide completion options inside
custom completions
- Remove the ability to provide completion options with custom
completions and add a `$env.config.completions.positional: true` option
- Remove the ability to provide completion options with custom
completions and add a new match algorithm `substring` (this is the one I
like most, since `positional` only applies to prefix matching anyway)
Separately from these options, we could also allow completers to specify
that they don't Nushell to do any filtering and sorting on the provided
custom completions.
2024-08-26 19:14:57 +02:00
|
|
|
}
|
|
|
|
|
Custom completions: Inherit case_sensitive option from $env.config (#14738)
# Description
Currently, if a custom completer returns a record containing an
`options` field, but these options don't specify `case_sensitive`,
`case_sensitive` will be true. This PR instead makes the default value
whatever the user specified in `$env.config.completions.case_sensitive`.
The match algorithm option already does this. `positional` is also
inherited from the global config, although user's can't actually specify
that one themselves in `$env.config` (I'm planning on getting rid of
`positional` in a separate PR).
# User-Facing Changes
For those making custom completions, if they need matching to be done
case-sensitively and:
- their completer returns a record rather than a list,
- and the record contains an `options` field,
- and the `options` field is a record,
- and the record doesn't contain a `case_sensitive` option,
then they will need to specify `case_sensitive: true` in their custom
completer's options. Otherwise, if the user sets
`$env.config.completions.case_sensitive = false`, their custom completer
will also use case-insensitive matching.
Others shouldn't have to make any changes.
# Tests + Formatting
Updated tests to check if `case_sensitive`. Basically rewrote them,
actually. I figured it'd be better to make a single helper function that
takes completer options and completion suggestions and generates a
completer from that rather than having multiple fixtures providing
different completers.
# After Submitting
Probably needs to be noted in the release notes, but I don't think the
[docs](https://www.nushell.sh/book/custom_completions.html#options-for-custom-completions)
need to be updated.
2025-01-07 18:52:31 +01:00
|
|
|
/// $env.config should be inherited by the custom completer's options
|
|
|
|
#[test]
|
|
|
|
fn customcompletions_inherit_options() {
|
|
|
|
let mut completer = custom_completer_with_options(
|
|
|
|
r#"$env.config.completions.algorithm = "fuzzy"
|
|
|
|
$env.config.completions.case_sensitive = false"#,
|
|
|
|
"",
|
|
|
|
&["Foo Abcdef", "Abcdef", "Acd Bar"],
|
|
|
|
);
|
|
|
|
|
|
|
|
// Make sure matching is fuzzy
|
|
|
|
let suggestions = completer.complete("my-command Acd", 14);
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["Acd Bar", "Abcdef", "Foo Abcdef"];
|
Custom completions: Inherit case_sensitive option from $env.config (#14738)
# Description
Currently, if a custom completer returns a record containing an
`options` field, but these options don't specify `case_sensitive`,
`case_sensitive` will be true. This PR instead makes the default value
whatever the user specified in `$env.config.completions.case_sensitive`.
The match algorithm option already does this. `positional` is also
inherited from the global config, although user's can't actually specify
that one themselves in `$env.config` (I'm planning on getting rid of
`positional` in a separate PR).
# User-Facing Changes
For those making custom completions, if they need matching to be done
case-sensitively and:
- their completer returns a record rather than a list,
- and the record contains an `options` field,
- and the `options` field is a record,
- and the record doesn't contain a `case_sensitive` option,
then they will need to specify `case_sensitive: true` in their custom
completer's options. Otherwise, if the user sets
`$env.config.completions.case_sensitive = false`, their custom completer
will also use case-insensitive matching.
Others shouldn't have to make any changes.
# Tests + Formatting
Updated tests to check if `case_sensitive`. Basically rewrote them,
actually. I figured it'd be better to make a single helper function that
takes completer options and completion suggestions and generates a
completer from that rather than having multiple fixtures providing
different completers.
# After Submitting
Probably needs to be noted in the release notes, but I don't think the
[docs](https://www.nushell.sh/book/custom_completions.html#options-for-custom-completions)
need to be updated.
2025-01-07 18:52:31 +01:00
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
|
|
|
|
// Custom options should make matching case insensitive
|
|
|
|
let suggestions = completer.complete("my-command acd", 14);
|
Use right options in custom completions (#13698)
<!--
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.
-->
This issue was reported by kira in
[Discord](https://discord.com/channels/601130461678272522/1276981416307069019).
In https://github.com/nushell/nushell/pull/13311, I accidentally made it
so that custom completions are filtered according to the user's
configured completion options (`$env.config.completions`) rather than
the completion options provided as part of the custom completions. This
PR is a quick fix for that.
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
It should once again be possible to override match algorithm, case
sensitivity, and substring matching (`positional`) in custom
completions.
# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
Added a couple tests.
# 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.
-->
fdncred and I discussed this in Discord a bit and we thought it might be
better to not allow custom completions to override the user's config.
However, `positional` can't currently be set inside one's config, so you
can only do strict prefix matching, no substring matching. Another PR
could do one of the following:
- Document the fact that you can provide completion options inside
custom completions
- Remove the ability to provide completion options with custom
completions and add a `$env.config.completions.positional: true` option
- Remove the ability to provide completion options with custom
completions and add a new match algorithm `substring` (this is the one I
like most, since `positional` only applies to prefix matching anyway)
Separately from these options, we could also allow completers to specify
that they don't Nushell to do any filtering and sorting on the provided
custom completions.
2024-08-26 19:14:57 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
}
|
|
|
|
|
Custom completions: Inherit case_sensitive option from $env.config (#14738)
# Description
Currently, if a custom completer returns a record containing an
`options` field, but these options don't specify `case_sensitive`,
`case_sensitive` will be true. This PR instead makes the default value
whatever the user specified in `$env.config.completions.case_sensitive`.
The match algorithm option already does this. `positional` is also
inherited from the global config, although user's can't actually specify
that one themselves in `$env.config` (I'm planning on getting rid of
`positional` in a separate PR).
# User-Facing Changes
For those making custom completions, if they need matching to be done
case-sensitively and:
- their completer returns a record rather than a list,
- and the record contains an `options` field,
- and the `options` field is a record,
- and the record doesn't contain a `case_sensitive` option,
then they will need to specify `case_sensitive: true` in their custom
completer's options. Otherwise, if the user sets
`$env.config.completions.case_sensitive = false`, their custom completer
will also use case-insensitive matching.
Others shouldn't have to make any changes.
# Tests + Formatting
Updated tests to check if `case_sensitive`. Basically rewrote them,
actually. I figured it'd be better to make a single helper function that
takes completer options and completion suggestions and generates a
completer from that rather than having multiple fixtures providing
different completers.
# After Submitting
Probably needs to be noted in the release notes, but I don't think the
[docs](https://www.nushell.sh/book/custom_completions.html#options-for-custom-completions)
need to be updated.
2025-01-07 18:52:31 +01:00
|
|
|
#[test]
|
|
|
|
fn customcompletions_no_sort() {
|
|
|
|
let mut completer = custom_completer_with_options(
|
|
|
|
"",
|
|
|
|
r#"completion_algorithm: "fuzzy",
|
|
|
|
sort: false"#,
|
|
|
|
&["zzzfoo", "foo", "not matched", "abcfoo"],
|
|
|
|
);
|
|
|
|
let suggestions = completer.complete("my-command foo", 14);
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["zzzfoo", "foo", "abcfoo"];
|
fix: Respect sort in custom completions (#14424)
<!--
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
This PR makes it so that when a custom completer sets `options.sort` to
true, completions aren't sorted. Previously, in #13311, I'd made it so
that setting `sort` to true would sort in alphabetical order, while
omitting it or setting it to false would sort it in the default order
for the chosen match algorithm (alphabetical for prefix matching, fuzzy
match score for fuzzy matching). I'd assumed that you'd always want to
sort completions and the important thing was choosing alphabetical
sorting vs the default sort order for your match algorithm. However,
this assumption was incorrect (see #13696 and [this
thread](https://discord.com/channels/601130461678272522/1302332259227144294)
in Discord).
An alternative would be to make `sort` accept `"alphabetical"`,
`"smart"`, and `"none"`/`null` rather than keeping it a boolean. But
that would be a breaking change and require more discussion, and I
wanted to keep this PR simple/small so that we can go back to the
sensible behavior as soon as possible.
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
Here are the different scenarios:
- If your custom completer returns a record with an `options` field
that's a record:
- If `options` contains `sort: true`, completions **will be sorted
according to the order set in the user's config**. Previously, they
would have been sorted in alphabetical order. This does mean that
**custom completers cannot explicitly choose to sort in alphabetical
order** anymore. I think that's an acceptable trade-off, though.
- If `options` contains `sort: false`, completions will not be sorted.
#13311 broke things so they would be sorted in the default order for the
match algorithm used. Before that PR, completions would not have been
sorted.
- If there's no `sort` option, that **will be treated as `sort: true`**.
Previously, this would have been treated as `sort: false`.
- Otherwise, nothing changes. Completions will still be sorted.
# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
Added 1 test to make sure that completions aren't sorted with `sort:
false` explicitly set.
# 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.
-->
2024-11-30 02:47:57 +01:00
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
}
|
|
|
|
|
Fallback to file completer in custom/external completer (#14781)
# Description
Closes #14595. This modifies the behavior of both custom and external
completers so that if the custom/external completer returns an invalid
value, completions are suppressed and an error is logged. However, if
the completer returns `null` (which this PR treats as a special value),
we fall back to file completions.
Previously, custom completers and external completers had different
behavior. Any time an external completer returned an invalid value
(including `null`), we would fall back to file completions. Any time a
custom completer returned an invalid value (including `null`), we would
suppress completions.
I'm not too happy about the implementation, but it's the least intrusive
way I could think of to do it. I added a `fallback` field to
`CustomCompletions` that's checked after calling its `fetch()` method.
If `fallback` is true, then we use file completions afterwards.
An alternative would be to make `CustomCompletions` no longer implement
the `Completer` trait, and instead have its `fetch()` method return an
`Option<Vec<Suggestion>>`. But that resulted in a teeny bit of code
duplication.
# User-Facing Changes
For those using an external completer, if they want to fall back to file
completions on invalid values, their completer will have to explicitly
return `null`. Returning `"foo"` or something will no longer make
Nushell use file completions instead.
For those making custom completers, they now have the option to fall
back to file completions.
# Tests + Formatting
Added some tests and manually tested that if the completer returns an
invalid value or the completer throws an error, that gets logged and
completions are suppressed.
# After Submitting
The documentation for custom completions and external completers will
have to be updated after this.
2025-01-26 06:44:01 +01:00
|
|
|
/// Fallback to file completions if custom completer returns null
|
|
|
|
#[test]
|
|
|
|
fn customcompletions_fallback() {
|
|
|
|
let (_, _, mut engine, mut stack) = new_engine();
|
|
|
|
let command = r#"
|
|
|
|
def comp [] { null }
|
|
|
|
def my-command [arg: string@comp] {}"#;
|
|
|
|
assert!(support::merge_input(command.as_bytes(), &mut engine, &mut stack).is_ok());
|
|
|
|
|
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
|
|
|
let completion_str = "my-command test";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected = [folder("test_a"), file("test_a_symlink"), folder("test_b")];
|
|
|
|
match_suggestions_by_string(&expected, &suggestions);
|
Fallback to file completer in custom/external completer (#14781)
# Description
Closes #14595. This modifies the behavior of both custom and external
completers so that if the custom/external completer returns an invalid
value, completions are suppressed and an error is logged. However, if
the completer returns `null` (which this PR treats as a special value),
we fall back to file completions.
Previously, custom completers and external completers had different
behavior. Any time an external completer returned an invalid value
(including `null`), we would fall back to file completions. Any time a
custom completer returned an invalid value (including `null`), we would
suppress completions.
I'm not too happy about the implementation, but it's the least intrusive
way I could think of to do it. I added a `fallback` field to
`CustomCompletions` that's checked after calling its `fetch()` method.
If `fallback` is true, then we use file completions afterwards.
An alternative would be to make `CustomCompletions` no longer implement
the `Completer` trait, and instead have its `fetch()` method return an
`Option<Vec<Suggestion>>`. But that resulted in a teeny bit of code
duplication.
# User-Facing Changes
For those using an external completer, if they want to fall back to file
completions on invalid values, their completer will have to explicitly
return `null`. Returning `"foo"` or something will no longer make
Nushell use file completions instead.
For those making custom completers, they now have the option to fall
back to file completions.
# Tests + Formatting
Added some tests and manually tested that if the completer returns an
invalid value or the completer throws an error, that gets logged and
completions are suppressed.
# After Submitting
The documentation for custom completions and external completers will
have to be updated after this.
2025-01-26 06:44:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-23 19:47:49 +01:00
|
|
|
/// Custom function arguments mixed with subcommands
|
|
|
|
#[test]
|
|
|
|
fn custom_arguments_and_subcommands() {
|
|
|
|
let (_, _, mut engine, mut stack) = new_engine();
|
|
|
|
let command = r#"
|
|
|
|
def foo [i: directory] {}
|
|
|
|
def "foo test bar" [] {}"#;
|
|
|
|
assert!(support::merge_input(command.as_bytes(), &mut engine, &mut stack).is_ok());
|
|
|
|
|
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
|
|
|
let completion_str = "foo test";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
// including both subcommand and directory completions
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected = ["foo test bar".into(), folder("test_a"), folder("test_b")];
|
|
|
|
match_suggestions_by_string(&expected, &suggestions);
|
2025-02-23 19:47:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Custom function flags mixed with subcommands
|
|
|
|
#[test]
|
|
|
|
fn custom_flags_and_subcommands() {
|
|
|
|
let (_, _, mut engine, mut stack) = new_engine();
|
|
|
|
let command = r#"
|
|
|
|
def foo [--test: directory] {}
|
|
|
|
def "foo --test bar" [] {}"#;
|
|
|
|
assert!(support::merge_input(command.as_bytes(), &mut engine, &mut stack).is_ok());
|
|
|
|
|
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
|
|
|
let completion_str = "foo --test";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
// including both flag and directory completions
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["foo --test bar", "--test"];
|
2025-02-23 19:47:49 +01:00
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// If argument type is something like int/string, complete only subcommands
|
|
|
|
#[test]
|
|
|
|
fn custom_arguments_vs_subcommands() {
|
|
|
|
let (_, _, mut engine, mut stack) = new_engine();
|
|
|
|
let command = r#"
|
|
|
|
def foo [i: string] {}
|
|
|
|
def "foo test bar" [] {}"#;
|
|
|
|
assert!(support::merge_input(command.as_bytes(), &mut engine, &mut stack).is_ok());
|
|
|
|
|
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
|
|
|
let completion_str = "foo test";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
// including only subcommand completions
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["foo test bar"];
|
2025-02-23 19:47:49 +01:00
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// External command only if starts with `^`
|
|
|
|
#[test]
|
|
|
|
fn external_commands_only() {
|
|
|
|
let engine = new_external_engine();
|
|
|
|
let mut completer = NuCompleter::new(
|
|
|
|
Arc::new(engine),
|
|
|
|
Arc::new(nu_protocol::engine::Stack::new()),
|
|
|
|
);
|
2025-02-25 11:47:10 +01:00
|
|
|
let completion_str = "ls; ^sleep";
|
2025-02-23 19:47:49 +01:00
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
#[cfg(windows)]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["sleep.exe"];
|
2025-02-23 19:47:49 +01:00
|
|
|
#[cfg(not(windows))]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["sleep"];
|
2025-02-23 19:47:49 +01:00
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
|
|
|
|
let completion_str = "sleep";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
#[cfg(windows)]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["sleep", "sleep.exe"];
|
2025-02-23 19:47:49 +01:00
|
|
|
#[cfg(not(windows))]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["sleep", "^sleep"];
|
2025-02-23 19:47:49 +01:00
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Which completes both internals and externals
|
|
|
|
#[test]
|
|
|
|
fn which_command_completions() {
|
|
|
|
let engine = new_external_engine();
|
|
|
|
let mut completer = NuCompleter::new(
|
|
|
|
Arc::new(engine),
|
|
|
|
Arc::new(nu_protocol::engine::Stack::new()),
|
|
|
|
);
|
|
|
|
// flags
|
|
|
|
let completion_str = "which --all";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["--all"];
|
2025-02-23 19:47:49 +01:00
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
// commands
|
|
|
|
let completion_str = "which sleep";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
#[cfg(windows)]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["sleep", "sleep.exe"];
|
2025-02-23 19:47:49 +01:00
|
|
|
#[cfg(not(windows))]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["sleep", "^sleep"];
|
2025-02-23 19:47:49 +01:00
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
}
|
|
|
|
|
Fallback to file completer in custom/external completer (#14781)
# Description
Closes #14595. This modifies the behavior of both custom and external
completers so that if the custom/external completer returns an invalid
value, completions are suppressed and an error is logged. However, if
the completer returns `null` (which this PR treats as a special value),
we fall back to file completions.
Previously, custom completers and external completers had different
behavior. Any time an external completer returned an invalid value
(including `null`), we would fall back to file completions. Any time a
custom completer returned an invalid value (including `null`), we would
suppress completions.
I'm not too happy about the implementation, but it's the least intrusive
way I could think of to do it. I added a `fallback` field to
`CustomCompletions` that's checked after calling its `fetch()` method.
If `fallback` is true, then we use file completions afterwards.
An alternative would be to make `CustomCompletions` no longer implement
the `Completer` trait, and instead have its `fetch()` method return an
`Option<Vec<Suggestion>>`. But that resulted in a teeny bit of code
duplication.
# User-Facing Changes
For those using an external completer, if they want to fall back to file
completions on invalid values, their completer will have to explicitly
return `null`. Returning `"foo"` or something will no longer make
Nushell use file completions instead.
For those making custom completers, they now have the option to fall
back to file completions.
# Tests + Formatting
Added some tests and manually tested that if the completer returns an
invalid value or the completer throws an error, that gets logged and
completions are suppressed.
# After Submitting
The documentation for custom completions and external completers will
have to be updated after this.
2025-01-26 06:44:01 +01:00
|
|
|
/// Suppress completions for invalid values
|
|
|
|
#[test]
|
|
|
|
fn customcompletions_invalid() {
|
|
|
|
let (_, _, mut engine, mut stack) = new_engine();
|
|
|
|
let command = r#"
|
|
|
|
def comp [] { 123 }
|
|
|
|
def my-command [arg: string@comp] {}"#;
|
|
|
|
assert!(support::merge_input(command.as_bytes(), &mut engine, &mut stack).is_ok());
|
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
|
|
|
|
|
|
|
let completion_str = "my-command foo";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
assert!(suggestions.is_empty());
|
|
|
|
}
|
|
|
|
|
2025-02-23 19:47:49 +01:00
|
|
|
#[test]
|
|
|
|
fn dont_use_dotnu_completions() {
|
|
|
|
// Create a new engine
|
|
|
|
let (_, _, engine, stack) = new_dotnu_engine();
|
|
|
|
// Instantiate a new completer
|
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
|
|
|
// Test nested nu script
|
2025-02-28 19:39:59 +01:00
|
|
|
let completion_str = "go work use `./dir_module/";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
2025-02-23 19:47:49 +01:00
|
|
|
|
|
|
|
// including a plaintext file
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec![
|
|
|
|
"./dir_module/mod.nu",
|
|
|
|
"./dir_module/plain.txt",
|
|
|
|
"`./dir_module/sub module/`",
|
2025-02-23 19:47:49 +01:00
|
|
|
];
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
}
|
|
|
|
|
2022-08-23 16:24:24 +02:00
|
|
|
#[test]
|
|
|
|
fn dotnu_completions() {
|
|
|
|
// Create a new engine
|
Force completers to sort in fetch() (#13242)
<!--
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.
-->
This PR fixes the problem pointed out in
https://github.com/nushell/nushell/issues/13204, where the Fish-like
completions aren't sorted properly (this PR doesn't close that issue
because the author there wants more than just fixed sort order).
The cause is all of the file/directory completions being fetched first
and then sorted all together while being treated as strings. Instead,
this PR sorts completions within each individual directory, avoiding
treating `/` as part of the path.
To do this, I removed the `sort` method from the completer trait (as
well as `get_sort_by`) and made all completers sort within the `fetch`
method itself. A generic `sort_completions` helper has been added to
sort lists of completions, and a more specific `sort_suggestions` helper
has been added to sort `Vec<Suggestion>`s.
As for the actual change that fixes the sort order for file/directory
completions, the `complete_rec` helper now sorts the children of each
directory before visiting their children. The file and directory
completers don't bother sorting at the end (except to move hidden files
down).
To reviewers: don't let the 29 changed files scare you, most of those
are just the test fixtures :)
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
This is the current behavior with prefix matching:

And with fuzzy matching:

Notice how `partial/hello.txt` is the last suggestion, even though it
should come before `partial-a`. This is because the ASCII code for `/`
is greater than that of `-`, so `partial-` is put before `partial/`.
This is this PR's behavior with prefix matching (`partial/hello.txt` is
at the start):

And with fuzzy matching:

# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
- Modified the partial completions test fixture to test whether this PR
even fixed anything
- Modified fixture to test sort order of .nu completions (a previous
version of my changes didn't sort all the completions at the end but
there were no tests catching that)
- Added a test for making sure subcommand completions are sorted by
Levenshtein distance (a previous version of my changes sorted in
alphabetical order but there were no tests catching that)
# 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.
-->
2024-07-03 13:48:06 +02:00
|
|
|
let (_, _, engine, stack) = new_dotnu_engine();
|
2022-08-23 16:24:24 +02:00
|
|
|
|
Fix typos by codespell (#7600)
# Description
Found via `codespell -S target -L
crate,ser,numer,falsy,ro,te,nd,bu,ndoes,statics,ons,fo,rouge,pard`
# User-Facing Changes
None.
# Tests + Formatting
None and done.
# After Submitting
None.
2022-12-26 08:31:26 +01:00
|
|
|
// Instantiate a new completer
|
2024-05-09 07:38:24 +02:00
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
2022-08-23 16:24:24 +02:00
|
|
|
|
2025-02-23 19:47:49 +01:00
|
|
|
// Flags should still be working
|
2025-02-28 19:39:59 +01:00
|
|
|
let completion_str = "overlay use --";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
2025-02-23 19:47:49 +01:00
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions(&vec!["--help", "--prefix", "--reload"], &suggestions);
|
2025-02-23 19:47:49 +01:00
|
|
|
|
2025-02-01 14:15:58 +01:00
|
|
|
// Test nested nu script
|
|
|
|
#[cfg(windows)]
|
2025-02-28 19:39:59 +01:00
|
|
|
let completion_str = "use `.\\dir_module\\";
|
2025-02-01 14:15:58 +01:00
|
|
|
#[cfg(not(windows))]
|
2025-02-28 19:39:59 +01:00
|
|
|
let completion_str = "use `./dir_module/";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
2025-02-01 14:15:58 +01:00
|
|
|
|
2025-02-02 14:54:09 +01:00
|
|
|
match_suggestions(
|
|
|
|
&vec![
|
2025-02-28 19:39:59 +01:00
|
|
|
"mod.nu",
|
2025-02-02 14:54:09 +01:00
|
|
|
#[cfg(windows)]
|
2025-02-28 19:39:59 +01:00
|
|
|
"sub module\\`",
|
2025-02-02 14:54:09 +01:00
|
|
|
#[cfg(not(windows))]
|
2025-02-28 19:39:59 +01:00
|
|
|
"sub module/`",
|
2025-02-02 14:54:09 +01:00
|
|
|
],
|
|
|
|
&suggestions,
|
|
|
|
);
|
|
|
|
|
|
|
|
// Test nested nu script, with ending '`'
|
|
|
|
#[cfg(windows)]
|
2025-02-28 19:39:59 +01:00
|
|
|
let completion_str = "use `.\\dir_module\\sub module\\`";
|
2025-02-02 14:54:09 +01:00
|
|
|
#[cfg(not(windows))]
|
2025-02-28 19:39:59 +01:00
|
|
|
let completion_str = "use `./dir_module/sub module/`";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
2025-02-02 14:54:09 +01:00
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions(&vec!["sub.nu`"], &suggestions);
|
2025-02-01 14:15:58 +01:00
|
|
|
|
2025-03-07 08:38:42 +01:00
|
|
|
let mut expected = vec,
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.
-->
This PR fixes the problem pointed out in
https://github.com/nushell/nushell/issues/13204, where the Fish-like
completions aren't sorted properly (this PR doesn't close that issue
because the author there wants more than just fixed sort order).
The cause is all of the file/directory completions being fetched first
and then sorted all together while being treated as strings. Instead,
this PR sorts completions within each individual directory, avoiding
treating `/` as part of the path.
To do this, I removed the `sort` method from the completer trait (as
well as `get_sort_by`) and made all completers sort within the `fetch`
method itself. A generic `sort_completions` helper has been added to
sort lists of completions, and a more specific `sort_suggestions` helper
has been added to sort `Vec<Suggestion>`s.
As for the actual change that fixes the sort order for file/directory
completions, the `complete_rec` helper now sorts the children of each
directory before visiting their children. The file and directory
completers don't bother sorting at the end (except to move hidden files
down).
To reviewers: don't let the 29 changed files scare you, most of those
are just the test fixtures :)
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
This is the current behavior with prefix matching:

And with fuzzy matching:

Notice how `partial/hello.txt` is the last suggestion, even though it
should come before `partial-a`. This is because the ASCII code for `/`
is greater than that of `-`, so `partial-` is put before `partial/`.
This is this PR's behavior with prefix matching (`partial/hello.txt` is
at the start):

And with fuzzy matching:

# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
- Modified the partial completions test fixture to test whether this PR
even fixed anything
- Modified fixture to test sort order of .nu completions (a previous
version of my changes didn't sort all the completions at the end but
there were no tests catching that)
- Added a test for making sure subcommand completions are sorted by
Levenshtein distance (a previous version of my changes sorted in
alphabetical order but there were no tests catching that)
# 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.
-->
2024-07-03 13:48:06 +02:00
|
|
|
#[cfg(windows)]
|
2025-02-28 19:39:59 +01:00
|
|
|
"dir_module\\",
|
Force completers to sort in fetch() (#13242)
<!--
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.
-->
This PR fixes the problem pointed out in
https://github.com/nushell/nushell/issues/13204, where the Fish-like
completions aren't sorted properly (this PR doesn't close that issue
because the author there wants more than just fixed sort order).
The cause is all of the file/directory completions being fetched first
and then sorted all together while being treated as strings. Instead,
this PR sorts completions within each individual directory, avoiding
treating `/` as part of the path.
To do this, I removed the `sort` method from the completer trait (as
well as `get_sort_by`) and made all completers sort within the `fetch`
method itself. A generic `sort_completions` helper has been added to
sort lists of completions, and a more specific `sort_suggestions` helper
has been added to sort `Vec<Suggestion>`s.
As for the actual change that fixes the sort order for file/directory
completions, the `complete_rec` helper now sorts the children of each
directory before visiting their children. The file and directory
completers don't bother sorting at the end (except to move hidden files
down).
To reviewers: don't let the 29 changed files scare you, most of those
are just the test fixtures :)
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
This is the current behavior with prefix matching:

And with fuzzy matching:

Notice how `partial/hello.txt` is the last suggestion, even though it
should come before `partial-a`. This is because the ASCII code for `/`
is greater than that of `-`, so `partial-` is put before `partial/`.
This is this PR's behavior with prefix matching (`partial/hello.txt` is
at the start):

And with fuzzy matching:

# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
- Modified the partial completions test fixture to test whether this PR
even fixed anything
- Modified fixture to test sort order of .nu completions (a previous
version of my changes didn't sort all the completions at the end but
there were no tests catching that)
- Added a test for making sure subcommand completions are sorted by
Levenshtein distance (a previous version of my changes sorted in
alphabetical order but there were no tests catching that)
# 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.
-->
2024-07-03 13:48:06 +02:00
|
|
|
#[cfg(not(windows))]
|
2025-02-28 19:39:59 +01:00
|
|
|
"dir_module/",
|
|
|
|
"foo.nu",
|
2025-02-01 14:15:58 +01:00
|
|
|
#[cfg(windows)]
|
2025-02-28 19:39:59 +01:00
|
|
|
"lib-dir1\\",
|
2025-02-01 14:15:58 +01:00
|
|
|
#[cfg(not(windows))]
|
2025-02-28 19:39:59 +01:00
|
|
|
"lib-dir1/",
|
2025-02-01 14:15:58 +01:00
|
|
|
#[cfg(windows)]
|
2025-02-28 19:39:59 +01:00
|
|
|
"lib-dir2\\",
|
2025-02-01 14:15:58 +01:00
|
|
|
#[cfg(not(windows))]
|
2025-02-28 19:39:59 +01:00
|
|
|
"lib-dir2/",
|
2025-02-01 14:15:58 +01:00
|
|
|
#[cfg(windows)]
|
2025-02-28 19:39:59 +01:00
|
|
|
"lib-dir3\\",
|
2025-02-01 14:15:58 +01:00
|
|
|
#[cfg(not(windows))]
|
2025-02-28 19:39:59 +01:00
|
|
|
"lib-dir3/",
|
|
|
|
"spam.nu",
|
|
|
|
"xyzzy.nu",
|
Force completers to sort in fetch() (#13242)
<!--
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.
-->
This PR fixes the problem pointed out in
https://github.com/nushell/nushell/issues/13204, where the Fish-like
completions aren't sorted properly (this PR doesn't close that issue
because the author there wants more than just fixed sort order).
The cause is all of the file/directory completions being fetched first
and then sorted all together while being treated as strings. Instead,
this PR sorts completions within each individual directory, avoiding
treating `/` as part of the path.
To do this, I removed the `sort` method from the completer trait (as
well as `get_sort_by`) and made all completers sort within the `fetch`
method itself. A generic `sort_completions` helper has been added to
sort lists of completions, and a more specific `sort_suggestions` helper
has been added to sort `Vec<Suggestion>`s.
As for the actual change that fixes the sort order for file/directory
completions, the `complete_rec` helper now sorts the children of each
directory before visiting their children. The file and directory
completers don't bother sorting at the end (except to move hidden files
down).
To reviewers: don't let the 29 changed files scare you, most of those
are just the test fixtures :)
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
This is the current behavior with prefix matching:

And with fuzzy matching:

Notice how `partial/hello.txt` is the last suggestion, even though it
should come before `partial-a`. This is because the ASCII code for `/`
is greater than that of `-`, so `partial-` is put before `partial/`.
This is this PR's behavior with prefix matching (`partial/hello.txt` is
at the start):

And with fuzzy matching:

# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
- Modified the partial completions test fixture to test whether this PR
even fixed anything
- Modified fixture to test sort order of .nu completions (a previous
version of my changes didn't sort all the completions at the end but
there were no tests catching that)
- Added a test for making sure subcommand completions are sorted by
Levenshtein distance (a previous version of my changes sorted in
alphabetical order but there were no tests catching that)
# 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.
-->
2024-07-03 13:48:06 +02:00
|
|
|
];
|
|
|
|
|
2022-08-23 16:24:24 +02:00
|
|
|
// Test source completion
|
2025-02-28 19:39:59 +01:00
|
|
|
let completion_str = "source-env ";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
2022-08-23 16:24:24 +02:00
|
|
|
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
// Test use completion
|
2025-03-07 08:38:42 +01:00
|
|
|
expected.push("std");
|
|
|
|
expected.push("std-rfc");
|
2025-02-28 19:39:59 +01:00
|
|
|
let completion_str = "use ";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
2022-08-23 16:24:24 +02:00
|
|
|
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2023-12-19 10:14:34 +01:00
|
|
|
|
|
|
|
// Test overlay use completion
|
2025-02-28 19:39:59 +01:00
|
|
|
let completion_str = "overlay use ";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
2023-12-19 10:14:34 +01:00
|
|
|
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2025-02-17 18:52:07 +01:00
|
|
|
|
|
|
|
// Test special paths
|
|
|
|
#[cfg(windows)]
|
|
|
|
{
|
2025-02-28 19:39:59 +01:00
|
|
|
let completion_str = "use \\";
|
2025-02-17 18:52:07 +01:00
|
|
|
let dir_content = read_dir("\\").unwrap();
|
2025-02-28 19:39:59 +01:00
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
2025-02-17 18:52:07 +01:00
|
|
|
match_dir_content_for_dotnu(dir_content, &suggestions);
|
|
|
|
}
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let completion_str = "use /";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
2025-02-17 18:52:07 +01:00
|
|
|
let dir_content = read_dir("/").unwrap();
|
|
|
|
match_dir_content_for_dotnu(dir_content, &suggestions);
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let completion_str = "use ~";
|
2025-02-17 18:52:07 +01:00
|
|
|
let dir_content = read_dir(expand_tilde("~")).unwrap();
|
2025-02-28 19:39:59 +01:00
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
2025-02-17 18:52:07 +01:00
|
|
|
match_dir_content_for_dotnu(dir_content, &suggestions);
|
|
|
|
}
|
|
|
|
|
2025-03-07 08:38:42 +01:00
|
|
|
#[test]
|
|
|
|
fn dotnu_stdlib_completions() {
|
|
|
|
let (_, _, mut engine, stack) = new_dotnu_engine();
|
|
|
|
assert!(load_standard_library(&mut engine).is_ok());
|
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
|
|
|
|
|
|
|
let completion_str = "export use std/ass";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
match_suggestions(&vec!["assert"], &suggestions);
|
|
|
|
|
|
|
|
let completion_str = "use `std-rfc/cli";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
match_suggestions(&vec!["clip"], &suggestions);
|
|
|
|
|
|
|
|
let completion_str = "use \"std";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
match_suggestions(&vec!["\"std", "\"std-rfc"], &suggestions);
|
|
|
|
|
|
|
|
let completion_str = "overlay use \'std-rfc/cli";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
match_suggestions(&vec!["clip"], &suggestions);
|
|
|
|
}
|
|
|
|
|
2025-03-08 12:28:51 +01:00
|
|
|
#[test]
|
|
|
|
fn exportable_completions() {
|
2025-03-08 12:58:00 +01:00
|
|
|
let (_, _, mut engine, mut stack) = new_dotnu_engine();
|
|
|
|
let code = r#"export module "🤔🐘" {
|
|
|
|
export const foo = "🤔🐘";
|
|
|
|
}"#;
|
|
|
|
assert!(support::merge_input(code.as_bytes(), &mut engine, &mut stack).is_ok());
|
2025-03-08 12:28:51 +01:00
|
|
|
assert!(load_standard_library(&mut engine).is_ok());
|
2025-03-08 12:58:00 +01:00
|
|
|
|
2025-03-08 12:28:51 +01:00
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
|
|
|
|
|
|
|
let completion_str = "use std null";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
match_suggestions(&vec!["null-device", "null_device"], &suggestions);
|
|
|
|
|
|
|
|
let completion_str = "export use std/assert eq";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
match_suggestions(&vec!["equal"], &suggestions);
|
|
|
|
|
|
|
|
let completion_str = "use std/assert \"not eq";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
match_suggestions(&vec!["'not equal'"], &suggestions);
|
|
|
|
|
|
|
|
let completion_str = "use std-rfc/clip ['prefi";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
match_suggestions(&vec!["prefix"], &suggestions);
|
|
|
|
|
|
|
|
let completion_str = "use std/math [E, `TAU";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
match_suggestions(&vec!["TAU"], &suggestions);
|
2025-03-08 12:58:00 +01:00
|
|
|
|
2025-03-08 14:46:36 +01:00
|
|
|
let completion_str = "use 🤔🐘 'foo";
|
2025-03-08 12:58:00 +01:00
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
match_suggestions(&vec!["foo"], &suggestions);
|
2025-03-08 12:28:51 +01:00
|
|
|
}
|
|
|
|
|
2025-02-17 18:52:07 +01:00
|
|
|
#[test]
|
|
|
|
fn dotnu_completions_const_nu_lib_dirs() {
|
|
|
|
let (_, _, engine, stack) = new_dotnu_engine();
|
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
|
|
|
|
|
|
|
// file in `lib-dir1/`, set by `const NU_LIB_DIRS`
|
2025-02-28 19:39:59 +01:00
|
|
|
let completion_str = "use xyzz";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
match_suggestions(&vec!["xyzzy.nu"], &suggestions);
|
2025-02-17 18:52:07 +01:00
|
|
|
|
|
|
|
// file in `lib-dir2/`, set by `$env.NU_LIB_DIRS`
|
2025-02-28 19:39:59 +01:00
|
|
|
let completion_str = "use asdf";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
match_suggestions(&vec!["asdf.nu"], &suggestions);
|
2025-02-17 18:52:07 +01:00
|
|
|
|
|
|
|
// file in `lib-dir3/`, set by both, should not replicate
|
2025-02-28 19:39:59 +01:00
|
|
|
let completion_str = "use spam";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
match_suggestions(&vec!["spam.nu"], &suggestions);
|
2025-02-17 18:52:07 +01:00
|
|
|
|
|
|
|
// if `./` specified by user, file in `lib-dir*` should be ignored
|
|
|
|
#[cfg(windows)]
|
|
|
|
{
|
2025-02-28 19:39:59 +01:00
|
|
|
let completion_str = "use .\\asdf";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
2025-02-17 18:52:07 +01:00
|
|
|
assert!(suggestions.is_empty());
|
|
|
|
}
|
2025-02-28 19:39:59 +01:00
|
|
|
let completion_str = "use ./asdf";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
2025-02-17 18:52:07 +01:00
|
|
|
assert!(suggestions.is_empty());
|
2022-08-23 16:24:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[ignore]
|
|
|
|
fn external_completer_trailing_space() {
|
|
|
|
// https://github.com/nushell/nushell/issues/6378
|
2024-04-21 07:03:33 +02:00
|
|
|
let block = "{|spans| $spans}";
|
2025-02-28 19:39:59 +01:00
|
|
|
let input = "gh alias ";
|
2022-08-23 16:24:24 +02:00
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let suggestions = run_external_completion(block, input);
|
2022-08-23 16:24:24 +02:00
|
|
|
assert_eq!(3, suggestions.len());
|
2023-11-17 16:15:55 +01:00
|
|
|
assert_eq!("gh", suggestions.first().unwrap().value);
|
2022-08-23 16:24:24 +02:00
|
|
|
assert_eq!("alias", suggestions.get(1).unwrap().value);
|
|
|
|
assert_eq!("", suggestions.get(2).unwrap().value);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn external_completer_no_trailing_space() {
|
2023-07-03 07:45:10 +02:00
|
|
|
let block = "{|spans| $spans}";
|
2025-02-28 19:39:59 +01:00
|
|
|
let input = "gh alias";
|
2022-08-23 16:24:24 +02:00
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let suggestions = run_external_completion(block, input);
|
2022-08-23 16:24:24 +02:00
|
|
|
assert_eq!(2, suggestions.len());
|
2023-11-17 16:15:55 +01:00
|
|
|
assert_eq!("gh", suggestions.first().unwrap().value);
|
2022-08-23 16:24:24 +02:00
|
|
|
assert_eq!("alias", suggestions.get(1).unwrap().value);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn external_completer_pass_flags() {
|
2023-07-03 07:45:10 +02:00
|
|
|
let block = "{|spans| $spans}";
|
2025-02-28 19:39:59 +01:00
|
|
|
let input = "gh api --";
|
2022-08-23 16:24:24 +02:00
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let suggestions = run_external_completion(block, input);
|
2022-08-23 16:24:24 +02:00
|
|
|
assert_eq!(3, suggestions.len());
|
2023-11-17 16:15:55 +01:00
|
|
|
assert_eq!("gh", suggestions.first().unwrap().value);
|
2022-08-23 16:24:24 +02:00
|
|
|
assert_eq!("api", suggestions.get(1).unwrap().value);
|
|
|
|
assert_eq!("--", suggestions.get(2).unwrap().value);
|
|
|
|
}
|
|
|
|
|
Fallback to file completer in custom/external completer (#14781)
# Description
Closes #14595. This modifies the behavior of both custom and external
completers so that if the custom/external completer returns an invalid
value, completions are suppressed and an error is logged. However, if
the completer returns `null` (which this PR treats as a special value),
we fall back to file completions.
Previously, custom completers and external completers had different
behavior. Any time an external completer returned an invalid value
(including `null`), we would fall back to file completions. Any time a
custom completer returned an invalid value (including `null`), we would
suppress completions.
I'm not too happy about the implementation, but it's the least intrusive
way I could think of to do it. I added a `fallback` field to
`CustomCompletions` that's checked after calling its `fetch()` method.
If `fallback` is true, then we use file completions afterwards.
An alternative would be to make `CustomCompletions` no longer implement
the `Completer` trait, and instead have its `fetch()` method return an
`Option<Vec<Suggestion>>`. But that resulted in a teeny bit of code
duplication.
# User-Facing Changes
For those using an external completer, if they want to fall back to file
completions on invalid values, their completer will have to explicitly
return `null`. Returning `"foo"` or something will no longer make
Nushell use file completions instead.
For those making custom completers, they now have the option to fall
back to file completions.
# Tests + Formatting
Added some tests and manually tested that if the completer returns an
invalid value or the completer throws an error, that gets logged and
completions are suppressed.
# After Submitting
The documentation for custom completions and external completers will
have to be updated after this.
2025-01-26 06:44:01 +01:00
|
|
|
/// Fallback to file completions when external completer returns null
|
|
|
|
#[test]
|
|
|
|
fn external_completer_fallback() {
|
|
|
|
let block = "{|spans| null}";
|
2025-02-28 19:39:59 +01:00
|
|
|
let input = "foo test";
|
Fallback to file completer in custom/external completer (#14781)
# Description
Closes #14595. This modifies the behavior of both custom and external
completers so that if the custom/external completer returns an invalid
value, completions are suppressed and an error is logged. However, if
the completer returns `null` (which this PR treats as a special value),
we fall back to file completions.
Previously, custom completers and external completers had different
behavior. Any time an external completer returned an invalid value
(including `null`), we would fall back to file completions. Any time a
custom completer returned an invalid value (including `null`), we would
suppress completions.
I'm not too happy about the implementation, but it's the least intrusive
way I could think of to do it. I added a `fallback` field to
`CustomCompletions` that's checked after calling its `fetch()` method.
If `fallback` is true, then we use file completions afterwards.
An alternative would be to make `CustomCompletions` no longer implement
the `Completer` trait, and instead have its `fetch()` method return an
`Option<Vec<Suggestion>>`. But that resulted in a teeny bit of code
duplication.
# User-Facing Changes
For those using an external completer, if they want to fall back to file
completions on invalid values, their completer will have to explicitly
return `null`. Returning `"foo"` or something will no longer make
Nushell use file completions instead.
For those making custom completers, they now have the option to fall
back to file completions.
# Tests + Formatting
Added some tests and manually tested that if the completer returns an
invalid value or the completer throws an error, that gets logged and
completions are suppressed.
# After Submitting
The documentation for custom completions and external completers will
have to be updated after this.
2025-01-26 06:44:01 +01:00
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected = [folder("test_a"), file("test_a_symlink"), folder("test_b")];
|
|
|
|
let suggestions = run_external_completion(block, input);
|
|
|
|
match_suggestions_by_string(&expected, &suggestions);
|
Fallback to file completer in custom/external completer (#14781)
# Description
Closes #14595. This modifies the behavior of both custom and external
completers so that if the custom/external completer returns an invalid
value, completions are suppressed and an error is logged. However, if
the completer returns `null` (which this PR treats as a special value),
we fall back to file completions.
Previously, custom completers and external completers had different
behavior. Any time an external completer returned an invalid value
(including `null`), we would fall back to file completions. Any time a
custom completer returned an invalid value (including `null`), we would
suppress completions.
I'm not too happy about the implementation, but it's the least intrusive
way I could think of to do it. I added a `fallback` field to
`CustomCompletions` that's checked after calling its `fetch()` method.
If `fallback` is true, then we use file completions afterwards.
An alternative would be to make `CustomCompletions` no longer implement
the `Completer` trait, and instead have its `fetch()` method return an
`Option<Vec<Suggestion>>`. But that resulted in a teeny bit of code
duplication.
# User-Facing Changes
For those using an external completer, if they want to fall back to file
completions on invalid values, their completer will have to explicitly
return `null`. Returning `"foo"` or something will no longer make
Nushell use file completions instead.
For those making custom completers, they now have the option to fall
back to file completions.
# Tests + Formatting
Added some tests and manually tested that if the completer returns an
invalid value or the completer throws an error, that gets logged and
completions are suppressed.
# After Submitting
The documentation for custom completions and external completers will
have to be updated after this.
2025-01-26 06:44:01 +01:00
|
|
|
}
|
|
|
|
|
2025-02-23 19:47:49 +01:00
|
|
|
/// Fallback to external completions for flags of `sudo`
|
|
|
|
#[test]
|
|
|
|
fn external_completer_sudo() {
|
|
|
|
let block = "{|spans| ['--background']}";
|
2025-02-28 19:39:59 +01:00
|
|
|
let input = "sudo --back";
|
2025-02-23 19:47:49 +01:00
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected = vec!["--background"];
|
|
|
|
let suggestions = run_external_completion(block, input);
|
2025-02-23 19:47:49 +01:00
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
}
|
|
|
|
|
Fallback to file completer in custom/external completer (#14781)
# Description
Closes #14595. This modifies the behavior of both custom and external
completers so that if the custom/external completer returns an invalid
value, completions are suppressed and an error is logged. However, if
the completer returns `null` (which this PR treats as a special value),
we fall back to file completions.
Previously, custom completers and external completers had different
behavior. Any time an external completer returned an invalid value
(including `null`), we would fall back to file completions. Any time a
custom completer returned an invalid value (including `null`), we would
suppress completions.
I'm not too happy about the implementation, but it's the least intrusive
way I could think of to do it. I added a `fallback` field to
`CustomCompletions` that's checked after calling its `fetch()` method.
If `fallback` is true, then we use file completions afterwards.
An alternative would be to make `CustomCompletions` no longer implement
the `Completer` trait, and instead have its `fetch()` method return an
`Option<Vec<Suggestion>>`. But that resulted in a teeny bit of code
duplication.
# User-Facing Changes
For those using an external completer, if they want to fall back to file
completions on invalid values, their completer will have to explicitly
return `null`. Returning `"foo"` or something will no longer make
Nushell use file completions instead.
For those making custom completers, they now have the option to fall
back to file completions.
# Tests + Formatting
Added some tests and manually tested that if the completer returns an
invalid value or the completer throws an error, that gets logged and
completions are suppressed.
# After Submitting
The documentation for custom completions and external completers will
have to be updated after this.
2025-01-26 06:44:01 +01:00
|
|
|
/// Suppress completions when external completer returns invalid value
|
|
|
|
#[test]
|
|
|
|
fn external_completer_invalid() {
|
|
|
|
let block = "{|spans| 123}";
|
2025-02-28 19:39:59 +01:00
|
|
|
let input = "foo ";
|
Fallback to file completer in custom/external completer (#14781)
# Description
Closes #14595. This modifies the behavior of both custom and external
completers so that if the custom/external completer returns an invalid
value, completions are suppressed and an error is logged. However, if
the completer returns `null` (which this PR treats as a special value),
we fall back to file completions.
Previously, custom completers and external completers had different
behavior. Any time an external completer returned an invalid value
(including `null`), we would fall back to file completions. Any time a
custom completer returned an invalid value (including `null`), we would
suppress completions.
I'm not too happy about the implementation, but it's the least intrusive
way I could think of to do it. I added a `fallback` field to
`CustomCompletions` that's checked after calling its `fetch()` method.
If `fallback` is true, then we use file completions afterwards.
An alternative would be to make `CustomCompletions` no longer implement
the `Completer` trait, and instead have its `fetch()` method return an
`Option<Vec<Suggestion>>`. But that resulted in a teeny bit of code
duplication.
# User-Facing Changes
For those using an external completer, if they want to fall back to file
completions on invalid values, their completer will have to explicitly
return `null`. Returning `"foo"` or something will no longer make
Nushell use file completions instead.
For those making custom completers, they now have the option to fall
back to file completions.
# Tests + Formatting
Added some tests and manually tested that if the completer returns an
invalid value or the completer throws an error, that gets logged and
completions are suppressed.
# After Submitting
The documentation for custom completions and external completers will
have to be updated after this.
2025-01-26 06:44:01 +01:00
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let suggestions = run_external_completion(block, input);
|
Fallback to file completer in custom/external completer (#14781)
# Description
Closes #14595. This modifies the behavior of both custom and external
completers so that if the custom/external completer returns an invalid
value, completions are suppressed and an error is logged. However, if
the completer returns `null` (which this PR treats as a special value),
we fall back to file completions.
Previously, custom completers and external completers had different
behavior. Any time an external completer returned an invalid value
(including `null`), we would fall back to file completions. Any time a
custom completer returned an invalid value (including `null`), we would
suppress completions.
I'm not too happy about the implementation, but it's the least intrusive
way I could think of to do it. I added a `fallback` field to
`CustomCompletions` that's checked after calling its `fetch()` method.
If `fallback` is true, then we use file completions afterwards.
An alternative would be to make `CustomCompletions` no longer implement
the `Completer` trait, and instead have its `fetch()` method return an
`Option<Vec<Suggestion>>`. But that resulted in a teeny bit of code
duplication.
# User-Facing Changes
For those using an external completer, if they want to fall back to file
completions on invalid values, their completer will have to explicitly
return `null`. Returning `"foo"` or something will no longer make
Nushell use file completions instead.
For those making custom completers, they now have the option to fall
back to file completions.
# Tests + Formatting
Added some tests and manually tested that if the completer returns an
invalid value or the completer throws an error, that gets logged and
completions are suppressed.
# After Submitting
The documentation for custom completions and external completers will
have to be updated after this.
2025-01-26 06:44:01 +01:00
|
|
|
assert!(suggestions.is_empty());
|
|
|
|
}
|
|
|
|
|
2022-08-23 16:24:24 +02:00
|
|
|
#[test]
|
|
|
|
fn file_completions() {
|
|
|
|
// Create a new engine
|
|
|
|
let (dir, dir_str, engine, stack) = new_engine();
|
|
|
|
|
Fix typos by codespell (#7600)
# Description
Found via `codespell -S target -L
crate,ser,numer,falsy,ro,te,nd,bu,ndoes,statics,ons,fo,rouge,pard`
# User-Facing Changes
None.
# Tests + Formatting
None and done.
# After Submitting
None.
2022-12-26 08:31:26 +01:00
|
|
|
// Instantiate a new completer
|
2024-05-09 07:38:24 +02:00
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
// Test completions for the current folder
|
Migrate to a new PWD API (#12603)
This is the first PR towards migrating to a new `$env.PWD` API that
returns potentially un-canonicalized paths. Refer to PR #12515 for
motivations.
## New API: `EngineState::cwd()`
The goal of the new API is to cover both parse-time and runtime use
case, and avoid unintentional misuse. It takes an `Option<Stack>` as
argument, which if supplied, will search for `$env.PWD` on the stack in
additional to the engine state. I think with this design, there's less
confusion over parse-time and runtime environments. If you have access
to a stack, just supply it; otherwise supply `None`.
## Deprecation of other PWD-related APIs
Other APIs are re-implemented using `EngineState::cwd()` and properly
documented. They're marked deprecated, but their behavior is unchanged.
Unused APIs are deleted, and code that accesses `$env.PWD` directly
without using an API is rewritten.
Deprecated APIs:
* `EngineState::current_work_dir()`
* `StateWorkingSet::get_cwd()`
* `env::current_dir()`
* `env::current_dir_str()`
* `env::current_dir_const()`
* `env::current_dir_str_const()`
Other changes:
* `EngineState::get_cwd()` (deleted)
* `StateWorkingSet::list_env()` (deleted)
* `repl::do_run_cmd()` (rewritten with `env::current_dir_str()`)
## `cd` and `pwd` now use logical paths by default
This pulls the changes from PR #12515. It's currently somewhat broken
because using non-canonicalized paths exposed a bug in our path
normalization logic (Issue #12602). Once that is fixed, this should
work.
## Future plans
This PR needs some tests. Which test helpers should I use, and where
should I put those tests?
I noticed that unquoted paths are expanded within `eval_filepath()` and
`eval_directory()` before they even reach the `cd` command. This means
every paths is expanded twice. Is this intended?
Once this PR lands, the plan is to review all usages of the deprecated
APIs and migrate them to `EngineState::cwd()`. In the meantime, these
usages are annotated with `#[allow(deprecated)]` to avoid breaking CI.
---------
Co-authored-by: Jakub Žádník <kubouch@gmail.com>
2024-05-03 13:33:09 +02:00
|
|
|
let target_dir = format!("cp {dir_str}{MAIN_SEPARATOR}");
|
2022-08-23 16:24:24 +02:00
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
|
|
|
// Create the expected values
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths = [
|
2023-02-22 14:03:48 +01:00
|
|
|
folder(dir.join("another")),
|
|
|
|
file(dir.join("custom_completion.nu")),
|
2023-12-19 10:14:34 +01:00
|
|
|
folder(dir.join("directory_completion")),
|
2022-08-23 16:24:24 +02:00
|
|
|
file(dir.join("nushell")),
|
|
|
|
folder(dir.join("test_a")),
|
2024-12-27 12:45:52 +01:00
|
|
|
file(dir.join("test_a_symlink")),
|
2022-08-23 16:24:24 +02:00
|
|
|
folder(dir.join("test_b")),
|
|
|
|
file(dir.join(".hidden_file")),
|
|
|
|
folder(dir.join(".hidden_folder")),
|
|
|
|
];
|
|
|
|
|
2024-07-30 15:28:41 +02:00
|
|
|
#[cfg(windows)]
|
|
|
|
{
|
|
|
|
let separator = '/';
|
|
|
|
let target_dir = format!("cp {dir_str}{separator}");
|
|
|
|
let slash_suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_slash_paths: Vec<_> = expected_paths
|
2024-07-30 15:28:41 +02:00
|
|
|
.iter()
|
|
|
|
.map(|s| s.replace('\\', "/"))
|
|
|
|
.collect();
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_slash_paths, &slash_suggestions);
|
2024-07-30 15:28:41 +02:00
|
|
|
}
|
|
|
|
|
2022-08-23 16:24:24 +02:00
|
|
|
// Match the results
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_paths, &suggestions);
|
2022-08-23 16:24:24 +02:00
|
|
|
|
2024-12-04 03:39:11 +01:00
|
|
|
// Test completions for the current folder even with parts before the autocomplet
|
|
|
|
let target_dir = format!("cp somefile.txt {dir_str}{MAIN_SEPARATOR}");
|
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
|
|
|
// Create the expected values
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths = [
|
2024-12-04 03:39:11 +01:00
|
|
|
folder(dir.join("another")),
|
|
|
|
file(dir.join("custom_completion.nu")),
|
|
|
|
folder(dir.join("directory_completion")),
|
|
|
|
file(dir.join("nushell")),
|
|
|
|
folder(dir.join("test_a")),
|
2024-12-27 12:45:52 +01:00
|
|
|
file(dir.join("test_a_symlink")),
|
2024-12-04 03:39:11 +01:00
|
|
|
folder(dir.join("test_b")),
|
|
|
|
file(dir.join(".hidden_file")),
|
|
|
|
folder(dir.join(".hidden_folder")),
|
|
|
|
];
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
{
|
|
|
|
let separator = '/';
|
|
|
|
let target_dir = format!("cp somefile.txt {dir_str}{separator}");
|
|
|
|
let slash_suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_slash_paths: Vec<_> = expected_paths
|
2024-12-04 03:39:11 +01:00
|
|
|
.iter()
|
|
|
|
.map(|s| s.replace('\\', "/"))
|
|
|
|
.collect();
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_slash_paths, &slash_suggestions);
|
2024-12-04 03:39:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Match the results
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_paths, &suggestions);
|
2024-12-04 03:39:11 +01:00
|
|
|
|
2022-08-23 16:24:24 +02:00
|
|
|
// Test completions for a file
|
|
|
|
let target_dir = format!("cp {}", folder(dir.join("another")));
|
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
|
|
|
// Create the expected values
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths = [file(dir.join("another").join("newfile"))];
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
// Match the results
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_paths, &suggestions);
|
2024-02-26 19:14:19 +01:00
|
|
|
|
|
|
|
// Test completions for hidden files
|
2024-09-09 20:39:18 +02:00
|
|
|
let target_dir = format!("ls {}", file(dir.join(".hidden_folder").join(".")));
|
2024-02-26 19:14:19 +01:00
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths = [file(dir.join(".hidden_folder").join(".hidden_subfile"))];
|
2024-02-26 19:14:19 +01:00
|
|
|
|
2024-07-30 15:28:41 +02:00
|
|
|
#[cfg(windows)]
|
|
|
|
{
|
|
|
|
let target_dir = format!("ls {}/.", folder(dir.join(".hidden_folder")));
|
|
|
|
let slash_suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_slash: Vec<_> = expected_paths
|
2024-07-30 15:28:41 +02:00
|
|
|
.iter()
|
|
|
|
.map(|s| s.replace('\\', "/"))
|
|
|
|
.collect();
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_slash, &slash_suggestions);
|
2024-07-30 15:28:41 +02:00
|
|
|
}
|
|
|
|
|
2024-02-26 19:14:19 +01:00
|
|
|
// Match the results
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_paths, &suggestions);
|
2024-07-30 15:28:41 +02:00
|
|
|
}
|
|
|
|
|
2024-12-04 03:39:11 +01:00
|
|
|
#[test]
|
|
|
|
fn custom_command_rest_any_args_file_completions() {
|
|
|
|
// Create a new engine
|
|
|
|
let (dir, dir_str, mut engine, mut stack) = new_engine();
|
|
|
|
let command = r#"def list [ ...args: any ] {}"#;
|
|
|
|
assert!(support::merge_input(command.as_bytes(), &mut engine, &mut stack).is_ok());
|
|
|
|
|
|
|
|
// Instantiate a new completer
|
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
|
|
|
|
|
|
|
// Test completions for the current folder
|
|
|
|
let target_dir = format!("list {dir_str}{MAIN_SEPARATOR}");
|
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
|
|
|
// Create the expected values
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths = [
|
2024-12-04 03:39:11 +01:00
|
|
|
folder(dir.join("another")),
|
|
|
|
file(dir.join("custom_completion.nu")),
|
|
|
|
folder(dir.join("directory_completion")),
|
|
|
|
file(dir.join("nushell")),
|
|
|
|
folder(dir.join("test_a")),
|
2024-12-27 12:45:52 +01:00
|
|
|
file(dir.join("test_a_symlink")),
|
2024-12-04 03:39:11 +01:00
|
|
|
folder(dir.join("test_b")),
|
|
|
|
file(dir.join(".hidden_file")),
|
|
|
|
folder(dir.join(".hidden_folder")),
|
|
|
|
];
|
|
|
|
|
|
|
|
// Match the results
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_paths, &suggestions);
|
2024-12-04 03:39:11 +01:00
|
|
|
|
|
|
|
// Test completions for the current folder even with parts before the autocomplet
|
|
|
|
let target_dir = format!("list somefile.txt {dir_str}{MAIN_SEPARATOR}");
|
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
|
|
|
// Create the expected values
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths = [
|
2024-12-04 03:39:11 +01:00
|
|
|
folder(dir.join("another")),
|
|
|
|
file(dir.join("custom_completion.nu")),
|
|
|
|
folder(dir.join("directory_completion")),
|
|
|
|
file(dir.join("nushell")),
|
|
|
|
folder(dir.join("test_a")),
|
2024-12-27 12:45:52 +01:00
|
|
|
file(dir.join("test_a_symlink")),
|
2024-12-04 03:39:11 +01:00
|
|
|
folder(dir.join("test_b")),
|
|
|
|
file(dir.join(".hidden_file")),
|
|
|
|
folder(dir.join(".hidden_folder")),
|
|
|
|
];
|
|
|
|
|
|
|
|
// Match the results
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_paths, &suggestions);
|
2024-12-04 03:39:11 +01:00
|
|
|
|
|
|
|
// Test completions for a file
|
|
|
|
let target_dir = format!("list {}", folder(dir.join("another")));
|
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
|
|
|
// Create the expected values
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths = [file(dir.join("another").join("newfile"))];
|
2024-12-04 03:39:11 +01:00
|
|
|
|
|
|
|
// Match the results
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_paths, &suggestions);
|
2024-12-04 03:39:11 +01:00
|
|
|
|
|
|
|
// Test completions for hidden files
|
|
|
|
let target_dir = format!("list {}", file(dir.join(".hidden_folder").join(".")));
|
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths = [file(dir.join(".hidden_folder").join(".hidden_subfile"))];
|
2024-12-04 03:39:11 +01:00
|
|
|
|
|
|
|
// Match the results
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_paths, &suggestions);
|
2024-12-04 03:39:11 +01:00
|
|
|
}
|
|
|
|
|
2024-07-30 15:28:41 +02:00
|
|
|
#[cfg(windows)]
|
|
|
|
#[test]
|
|
|
|
fn file_completions_with_mixed_separators() {
|
|
|
|
// Create a new engine
|
|
|
|
let (dir, dir_str, engine, stack) = new_dotnu_engine();
|
|
|
|
|
|
|
|
// Instantiate a new completer
|
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
|
|
|
|
|
|
|
// Create Expected values
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec![
|
2024-07-30 15:28:41 +02:00
|
|
|
file(dir.join("lib-dir1").join("bar.nu")),
|
|
|
|
file(dir.join("lib-dir1").join("baz.nu")),
|
|
|
|
file(dir.join("lib-dir1").join("xyzzy.nu")),
|
|
|
|
];
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_slash_paths: Vec<_> = expected_paths
|
2024-07-30 15:28:41 +02:00
|
|
|
.iter()
|
|
|
|
.map(|s| s.replace(MAIN_SEPARATOR, "/"))
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let target_dir = format!("ls {dir_str}/lib-dir1/");
|
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_slash_paths, &suggestions);
|
2024-07-30 15:28:41 +02:00
|
|
|
|
|
|
|
let target_dir = format!("cp {dir_str}\\lib-dir1/");
|
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_slash_paths, &suggestions);
|
2024-07-30 15:28:41 +02:00
|
|
|
|
|
|
|
let target_dir = format!("ls {dir_str}/lib-dir1\\/");
|
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_slash_paths, &suggestions);
|
2024-07-30 15:28:41 +02:00
|
|
|
|
|
|
|
let target_dir = format!("ls {dir_str}\\lib-dir1\\/");
|
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_slash_paths, &suggestions);
|
2024-07-30 15:28:41 +02:00
|
|
|
|
|
|
|
let target_dir = format!("ls {dir_str}\\lib-dir1\\");
|
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_paths, &suggestions);
|
2024-07-30 15:28:41 +02:00
|
|
|
|
|
|
|
let target_dir = format!("ls {dir_str}/lib-dir1\\");
|
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_paths, &suggestions);
|
2024-07-30 15:28:41 +02:00
|
|
|
|
|
|
|
let target_dir = format!("ls {dir_str}/lib-dir1/\\");
|
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_paths, &suggestions);
|
2024-07-30 15:28:41 +02:00
|
|
|
|
|
|
|
let target_dir = format!("ls {dir_str}\\lib-dir1/\\");
|
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_paths, &suggestions);
|
2022-08-23 16:24:24 +02:00
|
|
|
}
|
|
|
|
|
2023-10-02 19:44:51 +02:00
|
|
|
#[test]
|
|
|
|
fn partial_completions() {
|
|
|
|
// Create a new engine
|
|
|
|
let (dir, _, engine, stack) = new_partial_engine();
|
|
|
|
|
|
|
|
// Instantiate a new completer
|
2024-05-09 07:38:24 +02:00
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
2023-10-02 19:44:51 +02:00
|
|
|
|
|
|
|
// Test completions for a folder's name
|
|
|
|
let target_dir = format!("cd {}", file(dir.join("pa")));
|
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
|
|
|
// Create the expected values
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths = [
|
Force completers to sort in fetch() (#13242)
<!--
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.
-->
This PR fixes the problem pointed out in
https://github.com/nushell/nushell/issues/13204, where the Fish-like
completions aren't sorted properly (this PR doesn't close that issue
because the author there wants more than just fixed sort order).
The cause is all of the file/directory completions being fetched first
and then sorted all together while being treated as strings. Instead,
this PR sorts completions within each individual directory, avoiding
treating `/` as part of the path.
To do this, I removed the `sort` method from the completer trait (as
well as `get_sort_by`) and made all completers sort within the `fetch`
method itself. A generic `sort_completions` helper has been added to
sort lists of completions, and a more specific `sort_suggestions` helper
has been added to sort `Vec<Suggestion>`s.
As for the actual change that fixes the sort order for file/directory
completions, the `complete_rec` helper now sorts the children of each
directory before visiting their children. The file and directory
completers don't bother sorting at the end (except to move hidden files
down).
To reviewers: don't let the 29 changed files scare you, most of those
are just the test fixtures :)
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
This is the current behavior with prefix matching:

And with fuzzy matching:

Notice how `partial/hello.txt` is the last suggestion, even though it
should come before `partial-a`. This is because the ASCII code for `/`
is greater than that of `-`, so `partial-` is put before `partial/`.
This is this PR's behavior with prefix matching (`partial/hello.txt` is
at the start):

And with fuzzy matching:

# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
- Modified the partial completions test fixture to test whether this PR
even fixed anything
- Modified fixture to test sort order of .nu completions (a previous
version of my changes didn't sort all the completions at the end but
there were no tests catching that)
- Added a test for making sure subcommand completions are sorted by
Levenshtein distance (a previous version of my changes sorted in
alphabetical order but there were no tests catching that)
# 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.
-->
2024-07-03 13:48:06 +02:00
|
|
|
folder(dir.join("partial")),
|
|
|
|
folder(dir.join("partial-a")),
|
|
|
|
folder(dir.join("partial-b")),
|
|
|
|
folder(dir.join("partial-c")),
|
2023-10-02 19:44:51 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
// Match the results
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_paths, &suggestions);
|
2023-10-02 19:44:51 +02:00
|
|
|
|
|
|
|
// Test completions for the files whose name begin with "h"
|
|
|
|
// and are present under directories whose names begin with "pa"
|
|
|
|
let dir_str = file(dir.join("pa").join("h"));
|
|
|
|
let target_dir = format!("cp {dir_str}");
|
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
|
|
|
// Create the expected values
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths = [
|
Force completers to sort in fetch() (#13242)
<!--
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.
-->
This PR fixes the problem pointed out in
https://github.com/nushell/nushell/issues/13204, where the Fish-like
completions aren't sorted properly (this PR doesn't close that issue
because the author there wants more than just fixed sort order).
The cause is all of the file/directory completions being fetched first
and then sorted all together while being treated as strings. Instead,
this PR sorts completions within each individual directory, avoiding
treating `/` as part of the path.
To do this, I removed the `sort` method from the completer trait (as
well as `get_sort_by`) and made all completers sort within the `fetch`
method itself. A generic `sort_completions` helper has been added to
sort lists of completions, and a more specific `sort_suggestions` helper
has been added to sort `Vec<Suggestion>`s.
As for the actual change that fixes the sort order for file/directory
completions, the `complete_rec` helper now sorts the children of each
directory before visiting their children. The file and directory
completers don't bother sorting at the end (except to move hidden files
down).
To reviewers: don't let the 29 changed files scare you, most of those
are just the test fixtures :)
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
This is the current behavior with prefix matching:

And with fuzzy matching:

Notice how `partial/hello.txt` is the last suggestion, even though it
should come before `partial-a`. This is because the ASCII code for `/`
is greater than that of `-`, so `partial-` is put before `partial/`.
This is this PR's behavior with prefix matching (`partial/hello.txt` is
at the start):

And with fuzzy matching:

# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
- Modified the partial completions test fixture to test whether this PR
even fixed anything
- Modified fixture to test sort order of .nu completions (a previous
version of my changes didn't sort all the completions at the end but
there were no tests catching that)
- Added a test for making sure subcommand completions are sorted by
Levenshtein distance (a previous version of my changes sorted in
alphabetical order but there were no tests catching that)
# 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.
-->
2024-07-03 13:48:06 +02:00
|
|
|
file(dir.join("partial").join("hello.txt")),
|
|
|
|
file(dir.join("partial-a").join("have_ext.exe")),
|
|
|
|
file(dir.join("partial-a").join("have_ext.txt")),
|
|
|
|
file(dir.join("partial-a").join("hello")),
|
|
|
|
file(dir.join("partial-a").join("hola")),
|
|
|
|
file(dir.join("partial-b").join("hello_b")),
|
|
|
|
file(dir.join("partial-b").join("hi_b")),
|
|
|
|
file(dir.join("partial-c").join("hello_c")),
|
2023-10-02 19:44:51 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
// Match the results
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_paths, &suggestions);
|
2023-10-02 19:44:51 +02:00
|
|
|
|
|
|
|
// Test completion for all files under directories whose names begin with "pa"
|
|
|
|
let dir_str = folder(dir.join("pa"));
|
|
|
|
let target_dir = format!("ls {dir_str}");
|
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
|
|
|
// Create the expected values
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths = [
|
Force completers to sort in fetch() (#13242)
<!--
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.
-->
This PR fixes the problem pointed out in
https://github.com/nushell/nushell/issues/13204, where the Fish-like
completions aren't sorted properly (this PR doesn't close that issue
because the author there wants more than just fixed sort order).
The cause is all of the file/directory completions being fetched first
and then sorted all together while being treated as strings. Instead,
this PR sorts completions within each individual directory, avoiding
treating `/` as part of the path.
To do this, I removed the `sort` method from the completer trait (as
well as `get_sort_by`) and made all completers sort within the `fetch`
method itself. A generic `sort_completions` helper has been added to
sort lists of completions, and a more specific `sort_suggestions` helper
has been added to sort `Vec<Suggestion>`s.
As for the actual change that fixes the sort order for file/directory
completions, the `complete_rec` helper now sorts the children of each
directory before visiting their children. The file and directory
completers don't bother sorting at the end (except to move hidden files
down).
To reviewers: don't let the 29 changed files scare you, most of those
are just the test fixtures :)
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
This is the current behavior with prefix matching:

And with fuzzy matching:

Notice how `partial/hello.txt` is the last suggestion, even though it
should come before `partial-a`. This is because the ASCII code for `/`
is greater than that of `-`, so `partial-` is put before `partial/`.
This is this PR's behavior with prefix matching (`partial/hello.txt` is
at the start):

And with fuzzy matching:

# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
- Modified the partial completions test fixture to test whether this PR
even fixed anything
- Modified fixture to test sort order of .nu completions (a previous
version of my changes didn't sort all the completions at the end but
there were no tests catching that)
- Added a test for making sure subcommand completions are sorted by
Levenshtein distance (a previous version of my changes sorted in
alphabetical order but there were no tests catching that)
# 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.
-->
2024-07-03 13:48:06 +02:00
|
|
|
file(dir.join("partial").join("hello.txt")),
|
|
|
|
file(dir.join("partial-a").join("anotherfile")),
|
|
|
|
file(dir.join("partial-a").join("have_ext.exe")),
|
|
|
|
file(dir.join("partial-a").join("have_ext.txt")),
|
|
|
|
file(dir.join("partial-a").join("hello")),
|
|
|
|
file(dir.join("partial-a").join("hola")),
|
|
|
|
file(dir.join("partial-b").join("hello_b")),
|
|
|
|
file(dir.join("partial-b").join("hi_b")),
|
|
|
|
file(dir.join("partial-c").join("hello_c")),
|
2023-10-02 19:44:51 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
// Match the results
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_paths, &suggestions);
|
2023-10-02 19:44:51 +02:00
|
|
|
|
|
|
|
// Test completion for a single file
|
|
|
|
let dir_str = file(dir.join("fi").join("so"));
|
|
|
|
let target_dir = format!("rm {dir_str}");
|
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
|
|
|
// Create the expected values
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths = [file(dir.join("final_partial").join("somefile"))];
|
2023-10-02 19:44:51 +02:00
|
|
|
|
|
|
|
// Match the results
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_paths, &suggestions);
|
2023-10-02 19:44:51 +02:00
|
|
|
|
|
|
|
// Test completion where there is a sneaky `..` in the path
|
|
|
|
let dir_str = file(dir.join("par").join("..").join("fi").join("so"));
|
|
|
|
let target_dir = format!("rm {dir_str}");
|
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
|
|
|
// Create the expected values
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths = [
|
2024-05-04 03:17:50 +02:00
|
|
|
file(
|
Force completers to sort in fetch() (#13242)
<!--
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.
-->
This PR fixes the problem pointed out in
https://github.com/nushell/nushell/issues/13204, where the Fish-like
completions aren't sorted properly (this PR doesn't close that issue
because the author there wants more than just fixed sort order).
The cause is all of the file/directory completions being fetched first
and then sorted all together while being treated as strings. Instead,
this PR sorts completions within each individual directory, avoiding
treating `/` as part of the path.
To do this, I removed the `sort` method from the completer trait (as
well as `get_sort_by`) and made all completers sort within the `fetch`
method itself. A generic `sort_completions` helper has been added to
sort lists of completions, and a more specific `sort_suggestions` helper
has been added to sort `Vec<Suggestion>`s.
As for the actual change that fixes the sort order for file/directory
completions, the `complete_rec` helper now sorts the children of each
directory before visiting their children. The file and directory
completers don't bother sorting at the end (except to move hidden files
down).
To reviewers: don't let the 29 changed files scare you, most of those
are just the test fixtures :)
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
This is the current behavior with prefix matching:

And with fuzzy matching:

Notice how `partial/hello.txt` is the last suggestion, even though it
should come before `partial-a`. This is because the ASCII code for `/`
is greater than that of `-`, so `partial-` is put before `partial/`.
This is this PR's behavior with prefix matching (`partial/hello.txt` is
at the start):

And with fuzzy matching:

# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
- Modified the partial completions test fixture to test whether this PR
even fixed anything
- Modified fixture to test sort order of .nu completions (a previous
version of my changes didn't sort all the completions at the end but
there were no tests catching that)
- Added a test for making sure subcommand completions are sorted by
Levenshtein distance (a previous version of my changes sorted in
alphabetical order but there were no tests catching that)
# 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.
-->
2024-07-03 13:48:06 +02:00
|
|
|
dir.join("partial")
|
|
|
|
.join("..")
|
|
|
|
.join("final_partial")
|
|
|
|
.join("somefile"),
|
|
|
|
),
|
|
|
|
file(
|
|
|
|
dir.join("partial-a")
|
2024-05-04 03:17:50 +02:00
|
|
|
.join("..")
|
|
|
|
.join("final_partial")
|
|
|
|
.join("somefile"),
|
|
|
|
),
|
|
|
|
file(
|
Force completers to sort in fetch() (#13242)
<!--
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.
-->
This PR fixes the problem pointed out in
https://github.com/nushell/nushell/issues/13204, where the Fish-like
completions aren't sorted properly (this PR doesn't close that issue
because the author there wants more than just fixed sort order).
The cause is all of the file/directory completions being fetched first
and then sorted all together while being treated as strings. Instead,
this PR sorts completions within each individual directory, avoiding
treating `/` as part of the path.
To do this, I removed the `sort` method from the completer trait (as
well as `get_sort_by`) and made all completers sort within the `fetch`
method itself. A generic `sort_completions` helper has been added to
sort lists of completions, and a more specific `sort_suggestions` helper
has been added to sort `Vec<Suggestion>`s.
As for the actual change that fixes the sort order for file/directory
completions, the `complete_rec` helper now sorts the children of each
directory before visiting their children. The file and directory
completers don't bother sorting at the end (except to move hidden files
down).
To reviewers: don't let the 29 changed files scare you, most of those
are just the test fixtures :)
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
This is the current behavior with prefix matching:

And with fuzzy matching:

Notice how `partial/hello.txt` is the last suggestion, even though it
should come before `partial-a`. This is because the ASCII code for `/`
is greater than that of `-`, so `partial-` is put before `partial/`.
This is this PR's behavior with prefix matching (`partial/hello.txt` is
at the start):

And with fuzzy matching:

# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
- Modified the partial completions test fixture to test whether this PR
even fixed anything
- Modified fixture to test sort order of .nu completions (a previous
version of my changes didn't sort all the completions at the end but
there were no tests catching that)
- Added a test for making sure subcommand completions are sorted by
Levenshtein distance (a previous version of my changes sorted in
alphabetical order but there were no tests catching that)
# 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.
-->
2024-07-03 13:48:06 +02:00
|
|
|
dir.join("partial-b")
|
2024-05-04 03:17:50 +02:00
|
|
|
.join("..")
|
|
|
|
.join("final_partial")
|
|
|
|
.join("somefile"),
|
|
|
|
),
|
|
|
|
file(
|
Force completers to sort in fetch() (#13242)
<!--
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.
-->
This PR fixes the problem pointed out in
https://github.com/nushell/nushell/issues/13204, where the Fish-like
completions aren't sorted properly (this PR doesn't close that issue
because the author there wants more than just fixed sort order).
The cause is all of the file/directory completions being fetched first
and then sorted all together while being treated as strings. Instead,
this PR sorts completions within each individual directory, avoiding
treating `/` as part of the path.
To do this, I removed the `sort` method from the completer trait (as
well as `get_sort_by`) and made all completers sort within the `fetch`
method itself. A generic `sort_completions` helper has been added to
sort lists of completions, and a more specific `sort_suggestions` helper
has been added to sort `Vec<Suggestion>`s.
As for the actual change that fixes the sort order for file/directory
completions, the `complete_rec` helper now sorts the children of each
directory before visiting their children. The file and directory
completers don't bother sorting at the end (except to move hidden files
down).
To reviewers: don't let the 29 changed files scare you, most of those
are just the test fixtures :)
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
This is the current behavior with prefix matching:

And with fuzzy matching:

Notice how `partial/hello.txt` is the last suggestion, even though it
should come before `partial-a`. This is because the ASCII code for `/`
is greater than that of `-`, so `partial-` is put before `partial/`.
This is this PR's behavior with prefix matching (`partial/hello.txt` is
at the start):

And with fuzzy matching:

# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
- Modified the partial completions test fixture to test whether this PR
even fixed anything
- Modified fixture to test sort order of .nu completions (a previous
version of my changes didn't sort all the completions at the end but
there were no tests catching that)
- Added a test for making sure subcommand completions are sorted by
Levenshtein distance (a previous version of my changes sorted in
alphabetical order but there were no tests catching that)
# 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.
-->
2024-07-03 13:48:06 +02:00
|
|
|
dir.join("partial-c")
|
2024-05-04 03:17:50 +02:00
|
|
|
.join("..")
|
|
|
|
.join("final_partial")
|
|
|
|
.join("somefile"),
|
|
|
|
),
|
|
|
|
];
|
2023-10-02 19:44:51 +02:00
|
|
|
|
|
|
|
// Match the results
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_paths, &suggestions);
|
2024-05-23 04:47:06 +02:00
|
|
|
|
|
|
|
// Test completion for all files under directories whose names begin with "pa"
|
Force completers to sort in fetch() (#13242)
<!--
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.
-->
This PR fixes the problem pointed out in
https://github.com/nushell/nushell/issues/13204, where the Fish-like
completions aren't sorted properly (this PR doesn't close that issue
because the author there wants more than just fixed sort order).
The cause is all of the file/directory completions being fetched first
and then sorted all together while being treated as strings. Instead,
this PR sorts completions within each individual directory, avoiding
treating `/` as part of the path.
To do this, I removed the `sort` method from the completer trait (as
well as `get_sort_by`) and made all completers sort within the `fetch`
method itself. A generic `sort_completions` helper has been added to
sort lists of completions, and a more specific `sort_suggestions` helper
has been added to sort `Vec<Suggestion>`s.
As for the actual change that fixes the sort order for file/directory
completions, the `complete_rec` helper now sorts the children of each
directory before visiting their children. The file and directory
completers don't bother sorting at the end (except to move hidden files
down).
To reviewers: don't let the 29 changed files scare you, most of those
are just the test fixtures :)
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
This is the current behavior with prefix matching:

And with fuzzy matching:

Notice how `partial/hello.txt` is the last suggestion, even though it
should come before `partial-a`. This is because the ASCII code for `/`
is greater than that of `-`, so `partial-` is put before `partial/`.
This is this PR's behavior with prefix matching (`partial/hello.txt` is
at the start):

And with fuzzy matching:

# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
- Modified the partial completions test fixture to test whether this PR
even fixed anything
- Modified fixture to test sort order of .nu completions (a previous
version of my changes didn't sort all the completions at the end but
there were no tests catching that)
- Added a test for making sure subcommand completions are sorted by
Levenshtein distance (a previous version of my changes sorted in
alphabetical order but there were no tests catching that)
# 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.
-->
2024-07-03 13:48:06 +02:00
|
|
|
let file_str = file(dir.join("partial-a").join("have"));
|
2024-05-23 04:47:06 +02:00
|
|
|
let target_file = format!("rm {file_str}");
|
|
|
|
let suggestions = completer.complete(&target_file, target_file.len());
|
|
|
|
|
|
|
|
// Create the expected values
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths = [
|
Force completers to sort in fetch() (#13242)
<!--
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.
-->
This PR fixes the problem pointed out in
https://github.com/nushell/nushell/issues/13204, where the Fish-like
completions aren't sorted properly (this PR doesn't close that issue
because the author there wants more than just fixed sort order).
The cause is all of the file/directory completions being fetched first
and then sorted all together while being treated as strings. Instead,
this PR sorts completions within each individual directory, avoiding
treating `/` as part of the path.
To do this, I removed the `sort` method from the completer trait (as
well as `get_sort_by`) and made all completers sort within the `fetch`
method itself. A generic `sort_completions` helper has been added to
sort lists of completions, and a more specific `sort_suggestions` helper
has been added to sort `Vec<Suggestion>`s.
As for the actual change that fixes the sort order for file/directory
completions, the `complete_rec` helper now sorts the children of each
directory before visiting their children. The file and directory
completers don't bother sorting at the end (except to move hidden files
down).
To reviewers: don't let the 29 changed files scare you, most of those
are just the test fixtures :)
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
This is the current behavior with prefix matching:

And with fuzzy matching:

Notice how `partial/hello.txt` is the last suggestion, even though it
should come before `partial-a`. This is because the ASCII code for `/`
is greater than that of `-`, so `partial-` is put before `partial/`.
This is this PR's behavior with prefix matching (`partial/hello.txt` is
at the start):

And with fuzzy matching:

# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
- Modified the partial completions test fixture to test whether this PR
even fixed anything
- Modified fixture to test sort order of .nu completions (a previous
version of my changes didn't sort all the completions at the end but
there were no tests catching that)
- Added a test for making sure subcommand completions are sorted by
Levenshtein distance (a previous version of my changes sorted in
alphabetical order but there were no tests catching that)
# 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.
-->
2024-07-03 13:48:06 +02:00
|
|
|
file(dir.join("partial-a").join("have_ext.exe")),
|
|
|
|
file(dir.join("partial-a").join("have_ext.txt")),
|
2024-05-23 04:47:06 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
// Match the results
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_paths, &suggestions);
|
2024-05-23 04:47:06 +02:00
|
|
|
|
|
|
|
// Test completion for all files under directories whose names begin with "pa"
|
Force completers to sort in fetch() (#13242)
<!--
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.
-->
This PR fixes the problem pointed out in
https://github.com/nushell/nushell/issues/13204, where the Fish-like
completions aren't sorted properly (this PR doesn't close that issue
because the author there wants more than just fixed sort order).
The cause is all of the file/directory completions being fetched first
and then sorted all together while being treated as strings. Instead,
this PR sorts completions within each individual directory, avoiding
treating `/` as part of the path.
To do this, I removed the `sort` method from the completer trait (as
well as `get_sort_by`) and made all completers sort within the `fetch`
method itself. A generic `sort_completions` helper has been added to
sort lists of completions, and a more specific `sort_suggestions` helper
has been added to sort `Vec<Suggestion>`s.
As for the actual change that fixes the sort order for file/directory
completions, the `complete_rec` helper now sorts the children of each
directory before visiting their children. The file and directory
completers don't bother sorting at the end (except to move hidden files
down).
To reviewers: don't let the 29 changed files scare you, most of those
are just the test fixtures :)
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
This is the current behavior with prefix matching:

And with fuzzy matching:

Notice how `partial/hello.txt` is the last suggestion, even though it
should come before `partial-a`. This is because the ASCII code for `/`
is greater than that of `-`, so `partial-` is put before `partial/`.
This is this PR's behavior with prefix matching (`partial/hello.txt` is
at the start):

And with fuzzy matching:

# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
- Modified the partial completions test fixture to test whether this PR
even fixed anything
- Modified fixture to test sort order of .nu completions (a previous
version of my changes didn't sort all the completions at the end but
there were no tests catching that)
- Added a test for making sure subcommand completions are sorted by
Levenshtein distance (a previous version of my changes sorted in
alphabetical order but there were no tests catching that)
# 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.
-->
2024-07-03 13:48:06 +02:00
|
|
|
let file_str = file(dir.join("partial-a").join("have_ext."));
|
2024-05-23 04:47:06 +02:00
|
|
|
let file_dir = format!("rm {file_str}");
|
|
|
|
let suggestions = completer.complete(&file_dir, file_dir.len());
|
|
|
|
|
|
|
|
// Create the expected values
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths = [
|
Force completers to sort in fetch() (#13242)
<!--
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.
-->
This PR fixes the problem pointed out in
https://github.com/nushell/nushell/issues/13204, where the Fish-like
completions aren't sorted properly (this PR doesn't close that issue
because the author there wants more than just fixed sort order).
The cause is all of the file/directory completions being fetched first
and then sorted all together while being treated as strings. Instead,
this PR sorts completions within each individual directory, avoiding
treating `/` as part of the path.
To do this, I removed the `sort` method from the completer trait (as
well as `get_sort_by`) and made all completers sort within the `fetch`
method itself. A generic `sort_completions` helper has been added to
sort lists of completions, and a more specific `sort_suggestions` helper
has been added to sort `Vec<Suggestion>`s.
As for the actual change that fixes the sort order for file/directory
completions, the `complete_rec` helper now sorts the children of each
directory before visiting their children. The file and directory
completers don't bother sorting at the end (except to move hidden files
down).
To reviewers: don't let the 29 changed files scare you, most of those
are just the test fixtures :)
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
This is the current behavior with prefix matching:

And with fuzzy matching:

Notice how `partial/hello.txt` is the last suggestion, even though it
should come before `partial-a`. This is because the ASCII code for `/`
is greater than that of `-`, so `partial-` is put before `partial/`.
This is this PR's behavior with prefix matching (`partial/hello.txt` is
at the start):

And with fuzzy matching:

# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
- Modified the partial completions test fixture to test whether this PR
even fixed anything
- Modified fixture to test sort order of .nu completions (a previous
version of my changes didn't sort all the completions at the end but
there were no tests catching that)
- Added a test for making sure subcommand completions are sorted by
Levenshtein distance (a previous version of my changes sorted in
alphabetical order but there were no tests catching that)
# 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.
-->
2024-07-03 13:48:06 +02:00
|
|
|
file(dir.join("partial-a").join("have_ext.exe")),
|
|
|
|
file(dir.join("partial-a").join("have_ext.txt")),
|
2024-05-23 04:47:06 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
// Match the results
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_paths, &suggestions);
|
2023-10-02 19:44:51 +02:00
|
|
|
}
|
|
|
|
|
2024-09-09 20:39:18 +02:00
|
|
|
#[test]
|
|
|
|
fn partial_completion_with_dot_expansions() {
|
|
|
|
let (dir, _, engine, stack) = new_partial_engine();
|
|
|
|
|
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
|
|
|
|
|
|
|
let dir_str = file(
|
|
|
|
dir.join("par")
|
|
|
|
.join("...")
|
|
|
|
.join("par")
|
|
|
|
.join("fi")
|
|
|
|
.join("so"),
|
|
|
|
);
|
|
|
|
let target_dir = format!("rm {dir_str}");
|
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
|
|
|
// Create the expected values
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths = [
|
2024-09-09 20:39:18 +02:00
|
|
|
file(
|
|
|
|
dir.join("partial")
|
|
|
|
.join("...")
|
|
|
|
.join("partial_completions")
|
|
|
|
.join("final_partial")
|
|
|
|
.join("somefile"),
|
|
|
|
),
|
|
|
|
file(
|
|
|
|
dir.join("partial-a")
|
|
|
|
.join("...")
|
|
|
|
.join("partial_completions")
|
|
|
|
.join("final_partial")
|
|
|
|
.join("somefile"),
|
|
|
|
),
|
|
|
|
file(
|
|
|
|
dir.join("partial-b")
|
|
|
|
.join("...")
|
|
|
|
.join("partial_completions")
|
|
|
|
.join("final_partial")
|
|
|
|
.join("somefile"),
|
|
|
|
),
|
|
|
|
file(
|
|
|
|
dir.join("partial-c")
|
|
|
|
.join("...")
|
|
|
|
.join("partial_completions")
|
|
|
|
.join("final_partial")
|
|
|
|
.join("somefile"),
|
|
|
|
),
|
|
|
|
];
|
|
|
|
|
|
|
|
// Match the results
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_paths, &suggestions);
|
2024-09-09 20:39:18 +02:00
|
|
|
}
|
|
|
|
|
2022-08-23 16:24:24 +02:00
|
|
|
#[test]
|
2022-09-22 20:50:16 +02:00
|
|
|
fn command_ls_with_filecompletion() {
|
2022-08-23 16:24:24 +02:00
|
|
|
let (_, _, engine, stack) = new_engine();
|
|
|
|
|
2024-05-09 07:38:24 +02:00
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
let target_dir = "ls ";
|
|
|
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec![
|
|
|
|
"another\\",
|
|
|
|
"custom_completion.nu",
|
|
|
|
"directory_completion\\",
|
|
|
|
"nushell",
|
|
|
|
"test_a\\",
|
|
|
|
"test_a_symlink",
|
|
|
|
"test_b\\",
|
|
|
|
".hidden_file",
|
|
|
|
".hidden_folder\\",
|
2022-08-23 16:24:24 +02:00
|
|
|
];
|
|
|
|
#[cfg(not(windows))]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec![
|
|
|
|
"another/",
|
|
|
|
"custom_completion.nu",
|
|
|
|
"directory_completion/",
|
|
|
|
"nushell",
|
|
|
|
"test_a/",
|
|
|
|
"test_a_symlink",
|
|
|
|
"test_b/",
|
|
|
|
".hidden_file",
|
|
|
|
".hidden_folder/",
|
2022-08-23 16:24:24 +02:00
|
|
|
];
|
|
|
|
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected_paths, &suggestions);
|
2024-05-23 04:47:06 +02:00
|
|
|
|
|
|
|
let target_dir = "ls custom_completion.";
|
|
|
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec!["custom_completion.nu"];
|
2024-05-23 04:47:06 +02:00
|
|
|
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected_paths, &suggestions);
|
2022-08-23 16:24:24 +02:00
|
|
|
}
|
2024-07-30 15:28:41 +02:00
|
|
|
|
2022-08-23 16:24:24 +02:00
|
|
|
#[test]
|
2022-09-22 20:50:16 +02:00
|
|
|
fn command_open_with_filecompletion() {
|
2022-08-23 16:24:24 +02:00
|
|
|
let (_, _, engine, stack) = new_engine();
|
|
|
|
|
2024-05-09 07:38:24 +02:00
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
let target_dir = "open ";
|
|
|
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec![
|
|
|
|
"another\\",
|
|
|
|
"custom_completion.nu",
|
|
|
|
"directory_completion\\",
|
|
|
|
"nushell",
|
|
|
|
"test_a\\",
|
|
|
|
"test_a_symlink",
|
|
|
|
"test_b\\",
|
|
|
|
".hidden_file",
|
|
|
|
".hidden_folder\\",
|
2022-08-23 16:24:24 +02:00
|
|
|
];
|
|
|
|
#[cfg(not(windows))]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec![
|
|
|
|
"another/",
|
|
|
|
"custom_completion.nu",
|
|
|
|
"directory_completion/",
|
|
|
|
"nushell",
|
|
|
|
"test_a/",
|
|
|
|
"test_a_symlink",
|
|
|
|
"test_b/",
|
|
|
|
".hidden_file",
|
|
|
|
".hidden_folder/",
|
2022-08-23 16:24:24 +02:00
|
|
|
];
|
|
|
|
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected_paths, &suggestions);
|
2024-05-23 04:47:06 +02:00
|
|
|
|
|
|
|
let target_dir = "open custom_completion.";
|
|
|
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec!["custom_completion.nu"];
|
2024-05-23 04:47:06 +02:00
|
|
|
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected_paths, &suggestions);
|
2022-08-23 16:24:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-09-22 20:50:16 +02:00
|
|
|
fn command_rm_with_globcompletion() {
|
2022-08-23 16:24:24 +02:00
|
|
|
let (_, _, engine, stack) = new_engine();
|
|
|
|
|
2024-05-09 07:38:24 +02:00
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
let target_dir = "rm ";
|
|
|
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec![
|
|
|
|
"another\\",
|
|
|
|
"custom_completion.nu",
|
|
|
|
"directory_completion\\",
|
|
|
|
"nushell",
|
|
|
|
"test_a\\",
|
|
|
|
"test_a_symlink",
|
|
|
|
"test_b\\",
|
|
|
|
".hidden_file",
|
|
|
|
".hidden_folder\\",
|
2022-08-23 16:24:24 +02:00
|
|
|
];
|
|
|
|
#[cfg(not(windows))]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec![
|
|
|
|
"another/",
|
|
|
|
"custom_completion.nu",
|
|
|
|
"directory_completion/",
|
|
|
|
"nushell",
|
|
|
|
"test_a/",
|
|
|
|
"test_a_symlink",
|
|
|
|
"test_b/",
|
|
|
|
".hidden_file",
|
|
|
|
".hidden_folder/",
|
2022-08-23 16:24:24 +02:00
|
|
|
];
|
|
|
|
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected_paths, &suggestions)
|
2022-08-23 16:24:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-09-22 20:50:16 +02:00
|
|
|
fn command_cp_with_globcompletion() {
|
2022-08-23 16:24:24 +02:00
|
|
|
let (_, _, engine, stack) = new_engine();
|
|
|
|
|
2024-05-09 07:38:24 +02:00
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
let target_dir = "cp ";
|
|
|
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec![
|
|
|
|
"another\\",
|
|
|
|
"custom_completion.nu",
|
|
|
|
"directory_completion\\",
|
|
|
|
"nushell",
|
|
|
|
"test_a\\",
|
|
|
|
"test_a_symlink",
|
|
|
|
"test_b\\",
|
|
|
|
".hidden_file",
|
|
|
|
".hidden_folder\\",
|
2022-08-23 16:24:24 +02:00
|
|
|
];
|
|
|
|
#[cfg(not(windows))]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec![
|
|
|
|
"another/",
|
|
|
|
"custom_completion.nu",
|
|
|
|
"directory_completion/",
|
|
|
|
"nushell",
|
|
|
|
"test_a/",
|
|
|
|
"test_a_symlink",
|
|
|
|
"test_b/",
|
|
|
|
".hidden_file",
|
|
|
|
".hidden_folder/",
|
2022-08-23 16:24:24 +02:00
|
|
|
];
|
|
|
|
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected_paths, &suggestions)
|
2022-08-23 16:24:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-09-22 20:50:16 +02:00
|
|
|
fn command_save_with_filecompletion() {
|
2022-08-23 16:24:24 +02:00
|
|
|
let (_, _, engine, stack) = new_engine();
|
|
|
|
|
2024-05-09 07:38:24 +02:00
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
let target_dir = "save ";
|
|
|
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec![
|
|
|
|
"another\\",
|
|
|
|
"custom_completion.nu",
|
|
|
|
"directory_completion\\",
|
|
|
|
"nushell",
|
|
|
|
"test_a\\",
|
|
|
|
"test_a_symlink",
|
|
|
|
"test_b\\",
|
|
|
|
".hidden_file",
|
|
|
|
".hidden_folder\\",
|
2022-08-23 16:24:24 +02:00
|
|
|
];
|
|
|
|
#[cfg(not(windows))]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec![
|
|
|
|
"another/",
|
|
|
|
"custom_completion.nu",
|
|
|
|
"directory_completion/",
|
|
|
|
"nushell",
|
|
|
|
"test_a/",
|
|
|
|
"test_a_symlink",
|
|
|
|
"test_b/",
|
|
|
|
".hidden_file",
|
|
|
|
".hidden_folder/",
|
2022-08-23 16:24:24 +02:00
|
|
|
];
|
|
|
|
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected_paths, &suggestions)
|
2022-08-23 16:24:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-09-22 20:50:16 +02:00
|
|
|
fn command_touch_with_filecompletion() {
|
2022-08-23 16:24:24 +02:00
|
|
|
let (_, _, engine, stack) = new_engine();
|
|
|
|
|
2024-05-09 07:38:24 +02:00
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
let target_dir = "touch ";
|
|
|
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec![
|
|
|
|
"another\\",
|
|
|
|
"custom_completion.nu",
|
|
|
|
"directory_completion\\",
|
|
|
|
"nushell",
|
|
|
|
"test_a\\",
|
|
|
|
"test_a_symlink",
|
|
|
|
"test_b\\",
|
|
|
|
".hidden_file",
|
|
|
|
".hidden_folder\\",
|
2022-08-23 16:24:24 +02:00
|
|
|
];
|
|
|
|
#[cfg(not(windows))]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec![
|
|
|
|
"another/",
|
|
|
|
"custom_completion.nu",
|
|
|
|
"directory_completion/",
|
|
|
|
"nushell",
|
|
|
|
"test_a/",
|
|
|
|
"test_a_symlink",
|
|
|
|
"test_b/",
|
|
|
|
".hidden_file",
|
|
|
|
".hidden_folder/",
|
2022-08-23 16:24:24 +02:00
|
|
|
];
|
|
|
|
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected_paths, &suggestions)
|
2022-08-23 16:24:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-09-22 20:50:16 +02:00
|
|
|
fn command_watch_with_filecompletion() {
|
2022-08-23 16:24:24 +02:00
|
|
|
let (_, _, engine, stack) = new_engine();
|
|
|
|
|
2024-05-09 07:38:24 +02:00
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
let target_dir = "watch ";
|
|
|
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec![
|
|
|
|
"another\\",
|
|
|
|
"custom_completion.nu",
|
|
|
|
"directory_completion\\",
|
|
|
|
"nushell",
|
|
|
|
"test_a\\",
|
|
|
|
"test_a_symlink",
|
|
|
|
"test_b\\",
|
|
|
|
".hidden_file",
|
|
|
|
".hidden_folder\\",
|
2022-08-23 16:24:24 +02:00
|
|
|
];
|
|
|
|
#[cfg(not(windows))]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec![
|
|
|
|
"another/",
|
|
|
|
"custom_completion.nu",
|
|
|
|
"directory_completion/",
|
|
|
|
"nushell",
|
|
|
|
"test_a/",
|
|
|
|
"test_a_symlink",
|
|
|
|
"test_b/",
|
|
|
|
".hidden_file",
|
|
|
|
".hidden_folder/",
|
2022-08-23 16:24:24 +02:00
|
|
|
];
|
|
|
|
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected_paths, &suggestions)
|
2022-08-23 16:24:24 +02:00
|
|
|
}
|
|
|
|
|
Force completers to sort in fetch() (#13242)
<!--
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.
-->
This PR fixes the problem pointed out in
https://github.com/nushell/nushell/issues/13204, where the Fish-like
completions aren't sorted properly (this PR doesn't close that issue
because the author there wants more than just fixed sort order).
The cause is all of the file/directory completions being fetched first
and then sorted all together while being treated as strings. Instead,
this PR sorts completions within each individual directory, avoiding
treating `/` as part of the path.
To do this, I removed the `sort` method from the completer trait (as
well as `get_sort_by`) and made all completers sort within the `fetch`
method itself. A generic `sort_completions` helper has been added to
sort lists of completions, and a more specific `sort_suggestions` helper
has been added to sort `Vec<Suggestion>`s.
As for the actual change that fixes the sort order for file/directory
completions, the `complete_rec` helper now sorts the children of each
directory before visiting their children. The file and directory
completers don't bother sorting at the end (except to move hidden files
down).
To reviewers: don't let the 29 changed files scare you, most of those
are just the test fixtures :)
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
This is the current behavior with prefix matching:

And with fuzzy matching:

Notice how `partial/hello.txt` is the last suggestion, even though it
should come before `partial-a`. This is because the ASCII code for `/`
is greater than that of `-`, so `partial-` is put before `partial/`.
This is this PR's behavior with prefix matching (`partial/hello.txt` is
at the start):

And with fuzzy matching:

# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
- Modified the partial completions test fixture to test whether this PR
even fixed anything
- Modified fixture to test sort order of .nu completions (a previous
version of my changes didn't sort all the completions at the end but
there were no tests catching that)
- Added a test for making sure subcommand completions are sorted by
Levenshtein distance (a previous version of my changes sorted in
alphabetical order but there were no tests catching that)
# 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.
-->
2024-07-03 13:48:06 +02:00
|
|
|
#[rstest]
|
Use nucleo instead of skim for completions (#14846)
# Description
This PR replaces `SkimMatcherV2` from the
[fuzzy-matcher](https://docs.rs/fuzzy-matcher/latest/fuzzy_matcher/)
crate with the
[nucleo-matcher](https://docs.rs/nucleo-matcher/latest/nucleo_matcher/)
crate for doing fuzzy matching. This touches both our completion code in
`nu-cli` and symbol filtering in `nu-lsp`.
Nucleo should give us better performance than Skim. In the event that we
decide to use the Nucleo frontend ([crate
docs](https://docs.rs/nucleo/latest/nucleo/)) too, it also works on
Windows, unlike [Skim](https://github.com/skim-rs/skim), which appears
to only support Linux and MacOS.
Unfortunately, we still have an indirect dependency on `fuzzy-matcher`,
because the [`dialoguer`](https://github.com/console-rs/dialoguer) crate
uses it.
# User-Facing Changes
No breaking changes. Suggestions will be sorted differently, because
Nucleo uses a different algorithm from Skim for matching/scoring.
Hopefully, the new sorting will generally make more sense.
# Tests + Formatting
In `nu-cli`, modified an existing test, but didn't test performance. I
haven't tested `nu-lsp` manually, but existing tests pass.
I did manually do `ls /nix/store/<TAB>`, `ls /nix/store/d<TAB>`, etc.,
but didn't notice Nucleo being faster (my `/nix/store` folder has 34136
items at the time of writing).
2025-01-17 13:24:00 +01:00
|
|
|
fn subcommand_completions() {
|
|
|
|
let (_, _, mut engine, mut stack) = new_engine();
|
|
|
|
let commands = r#"
|
|
|
|
$env.config.completions.algorithm = "fuzzy"
|
|
|
|
def foo-test-command [] {}
|
|
|
|
def "foo-test-command bar" [] {}
|
|
|
|
def "foo-test-command aagap bcr" [] {}
|
|
|
|
def "food bar" [] {}
|
|
|
|
"#;
|
|
|
|
assert!(support::merge_input(commands.as_bytes(), &mut engine, &mut stack).is_ok());
|
|
|
|
let mut subcommand_completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
Force completers to sort in fetch() (#13242)
<!--
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.
-->
This PR fixes the problem pointed out in
https://github.com/nushell/nushell/issues/13204, where the Fish-like
completions aren't sorted properly (this PR doesn't close that issue
because the author there wants more than just fixed sort order).
The cause is all of the file/directory completions being fetched first
and then sorted all together while being treated as strings. Instead,
this PR sorts completions within each individual directory, avoiding
treating `/` as part of the path.
To do this, I removed the `sort` method from the completer trait (as
well as `get_sort_by`) and made all completers sort within the `fetch`
method itself. A generic `sort_completions` helper has been added to
sort lists of completions, and a more specific `sort_suggestions` helper
has been added to sort `Vec<Suggestion>`s.
As for the actual change that fixes the sort order for file/directory
completions, the `complete_rec` helper now sorts the children of each
directory before visiting their children. The file and directory
completers don't bother sorting at the end (except to move hidden files
down).
To reviewers: don't let the 29 changed files scare you, most of those
are just the test fixtures :)
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
This is the current behavior with prefix matching:

And with fuzzy matching:

Notice how `partial/hello.txt` is the last suggestion, even though it
should come before `partial-a`. This is because the ASCII code for `/`
is greater than that of `-`, so `partial-` is put before `partial/`.
This is this PR's behavior with prefix matching (`partial/hello.txt` is
at the start):

And with fuzzy matching:

# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
- Modified the partial completions test fixture to test whether this PR
even fixed anything
- Modified fixture to test sort order of .nu completions (a previous
version of my changes didn't sort all the completions at the end but
there were no tests catching that)
- Added a test for making sure subcommand completions are sorted by
Levenshtein distance (a previous version of my changes sorted in
alphabetical order but there were no tests catching that)
# 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.
-->
2024-07-03 13:48:06 +02:00
|
|
|
|
Use nucleo instead of skim for completions (#14846)
# Description
This PR replaces `SkimMatcherV2` from the
[fuzzy-matcher](https://docs.rs/fuzzy-matcher/latest/fuzzy_matcher/)
crate with the
[nucleo-matcher](https://docs.rs/nucleo-matcher/latest/nucleo_matcher/)
crate for doing fuzzy matching. This touches both our completion code in
`nu-cli` and symbol filtering in `nu-lsp`.
Nucleo should give us better performance than Skim. In the event that we
decide to use the Nucleo frontend ([crate
docs](https://docs.rs/nucleo/latest/nucleo/)) too, it also works on
Windows, unlike [Skim](https://github.com/skim-rs/skim), which appears
to only support Linux and MacOS.
Unfortunately, we still have an indirect dependency on `fuzzy-matcher`,
because the [`dialoguer`](https://github.com/console-rs/dialoguer) crate
uses it.
# User-Facing Changes
No breaking changes. Suggestions will be sorted differently, because
Nucleo uses a different algorithm from Skim for matching/scoring.
Hopefully, the new sorting will generally make more sense.
# Tests + Formatting
In `nu-cli`, modified an existing test, but didn't test performance. I
haven't tested `nu-lsp` manually, but existing tests pass.
I did manually do `ls /nix/store/<TAB>`, `ls /nix/store/d<TAB>`, etc.,
but didn't notice Nucleo being faster (my `/nix/store` folder has 34136
items at the time of writing).
2025-01-17 13:24:00 +01:00
|
|
|
let prefix = "fod br";
|
Force completers to sort in fetch() (#13242)
<!--
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.
-->
This PR fixes the problem pointed out in
https://github.com/nushell/nushell/issues/13204, where the Fish-like
completions aren't sorted properly (this PR doesn't close that issue
because the author there wants more than just fixed sort order).
The cause is all of the file/directory completions being fetched first
and then sorted all together while being treated as strings. Instead,
this PR sorts completions within each individual directory, avoiding
treating `/` as part of the path.
To do this, I removed the `sort` method from the completer trait (as
well as `get_sort_by`) and made all completers sort within the `fetch`
method itself. A generic `sort_completions` helper has been added to
sort lists of completions, and a more specific `sort_suggestions` helper
has been added to sort `Vec<Suggestion>`s.
As for the actual change that fixes the sort order for file/directory
completions, the `complete_rec` helper now sorts the children of each
directory before visiting their children. The file and directory
completers don't bother sorting at the end (except to move hidden files
down).
To reviewers: don't let the 29 changed files scare you, most of those
are just the test fixtures :)
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
This is the current behavior with prefix matching:

And with fuzzy matching:

Notice how `partial/hello.txt` is the last suggestion, even though it
should come before `partial-a`. This is because the ASCII code for `/`
is greater than that of `-`, so `partial-` is put before `partial/`.
This is this PR's behavior with prefix matching (`partial/hello.txt` is
at the start):

And with fuzzy matching:

# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
- Modified the partial completions test fixture to test whether this PR
even fixed anything
- Modified fixture to test sort order of .nu completions (a previous
version of my changes didn't sort all the completions at the end but
there were no tests catching that)
- Added a test for making sure subcommand completions are sorted by
Levenshtein distance (a previous version of my changes sorted in
alphabetical order but there were no tests catching that)
# 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.
-->
2024-07-03 13:48:06 +02:00
|
|
|
let suggestions = subcommand_completer.complete(prefix, prefix.len());
|
|
|
|
match_suggestions(
|
2024-07-30 15:28:41 +02:00
|
|
|
&vec,
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.
-->
This PR fixes the problem pointed out in
https://github.com/nushell/nushell/issues/13204, where the Fish-like
completions aren't sorted properly (this PR doesn't close that issue
because the author there wants more than just fixed sort order).
The cause is all of the file/directory completions being fetched first
and then sorted all together while being treated as strings. Instead,
this PR sorts completions within each individual directory, avoiding
treating `/` as part of the path.
To do this, I removed the `sort` method from the completer trait (as
well as `get_sort_by`) and made all completers sort within the `fetch`
method itself. A generic `sort_completions` helper has been added to
sort lists of completions, and a more specific `sort_suggestions` helper
has been added to sort `Vec<Suggestion>`s.
As for the actual change that fixes the sort order for file/directory
completions, the `complete_rec` helper now sorts the children of each
directory before visiting their children. The file and directory
completers don't bother sorting at the end (except to move hidden files
down).
To reviewers: don't let the 29 changed files scare you, most of those
are just the test fixtures :)
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
This is the current behavior with prefix matching:

And with fuzzy matching:

Notice how `partial/hello.txt` is the last suggestion, even though it
should come before `partial-a`. This is because the ASCII code for `/`
is greater than that of `-`, so `partial-` is put before `partial/`.
This is this PR's behavior with prefix matching (`partial/hello.txt` is
at the start):

And with fuzzy matching:

# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
- Modified the partial completions test fixture to test whether this PR
even fixed anything
- Modified fixture to test sort order of .nu completions (a previous
version of my changes didn't sort all the completions at the end but
there were no tests catching that)
- Added a test for making sure subcommand completions are sorted by
Levenshtein distance (a previous version of my changes sorted in
alphabetical order but there were no tests catching that)
# 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.
-->
2024-07-03 13:48:06 +02:00
|
|
|
],
|
2024-07-30 15:28:41 +02:00
|
|
|
&suggestions,
|
Force completers to sort in fetch() (#13242)
<!--
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.
-->
This PR fixes the problem pointed out in
https://github.com/nushell/nushell/issues/13204, where the Fish-like
completions aren't sorted properly (this PR doesn't close that issue
because the author there wants more than just fixed sort order).
The cause is all of the file/directory completions being fetched first
and then sorted all together while being treated as strings. Instead,
this PR sorts completions within each individual directory, avoiding
treating `/` as part of the path.
To do this, I removed the `sort` method from the completer trait (as
well as `get_sort_by`) and made all completers sort within the `fetch`
method itself. A generic `sort_completions` helper has been added to
sort lists of completions, and a more specific `sort_suggestions` helper
has been added to sort `Vec<Suggestion>`s.
As for the actual change that fixes the sort order for file/directory
completions, the `complete_rec` helper now sorts the children of each
directory before visiting their children. The file and directory
completers don't bother sorting at the end (except to move hidden files
down).
To reviewers: don't let the 29 changed files scare you, most of those
are just the test fixtures :)
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
This is the current behavior with prefix matching:

And with fuzzy matching:

Notice how `partial/hello.txt` is the last suggestion, even though it
should come before `partial-a`. This is because the ASCII code for `/`
is greater than that of `-`, so `partial-` is put before `partial/`.
This is this PR's behavior with prefix matching (`partial/hello.txt` is
at the start):

And with fuzzy matching:

# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
- Modified the partial completions test fixture to test whether this PR
even fixed anything
- Modified fixture to test sort order of .nu completions (a previous
version of my changes didn't sort all the completions at the end but
there were no tests catching that)
- Added a test for making sure subcommand completions are sorted by
Levenshtein distance (a previous version of my changes sorted in
alphabetical order but there were no tests catching that)
# 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.
-->
2024-07-03 13:48:06 +02:00
|
|
|
);
|
Use nucleo instead of skim for completions (#14846)
# Description
This PR replaces `SkimMatcherV2` from the
[fuzzy-matcher](https://docs.rs/fuzzy-matcher/latest/fuzzy_matcher/)
crate with the
[nucleo-matcher](https://docs.rs/nucleo-matcher/latest/nucleo_matcher/)
crate for doing fuzzy matching. This touches both our completion code in
`nu-cli` and symbol filtering in `nu-lsp`.
Nucleo should give us better performance than Skim. In the event that we
decide to use the Nucleo frontend ([crate
docs](https://docs.rs/nucleo/latest/nucleo/)) too, it also works on
Windows, unlike [Skim](https://github.com/skim-rs/skim), which appears
to only support Linux and MacOS.
Unfortunately, we still have an indirect dependency on `fuzzy-matcher`,
because the [`dialoguer`](https://github.com/console-rs/dialoguer) crate
uses it.
# User-Facing Changes
No breaking changes. Suggestions will be sorted differently, because
Nucleo uses a different algorithm from Skim for matching/scoring.
Hopefully, the new sorting will generally make more sense.
# Tests + Formatting
In `nu-cli`, modified an existing test, but didn't test performance. I
haven't tested `nu-lsp` manually, but existing tests pass.
I did manually do `ls /nix/store/<TAB>`, `ls /nix/store/d<TAB>`, etc.,
but didn't notice Nucleo being faster (my `/nix/store` folder has 34136
items at the time of writing).
2025-01-17 13:24:00 +01:00
|
|
|
|
|
|
|
let prefix = "foot bar";
|
|
|
|
let suggestions = subcommand_completer.complete(prefix, prefix.len());
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions(&vec!["foo-test-command bar"], &suggestions);
|
Force completers to sort in fetch() (#13242)
<!--
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.
-->
This PR fixes the problem pointed out in
https://github.com/nushell/nushell/issues/13204, where the Fish-like
completions aren't sorted properly (this PR doesn't close that issue
because the author there wants more than just fixed sort order).
The cause is all of the file/directory completions being fetched first
and then sorted all together while being treated as strings. Instead,
this PR sorts completions within each individual directory, avoiding
treating `/` as part of the path.
To do this, I removed the `sort` method from the completer trait (as
well as `get_sort_by`) and made all completers sort within the `fetch`
method itself. A generic `sort_completions` helper has been added to
sort lists of completions, and a more specific `sort_suggestions` helper
has been added to sort `Vec<Suggestion>`s.
As for the actual change that fixes the sort order for file/directory
completions, the `complete_rec` helper now sorts the children of each
directory before visiting their children. The file and directory
completers don't bother sorting at the end (except to move hidden files
down).
To reviewers: don't let the 29 changed files scare you, most of those
are just the test fixtures :)
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
This is the current behavior with prefix matching:

And with fuzzy matching:

Notice how `partial/hello.txt` is the last suggestion, even though it
should come before `partial-a`. This is because the ASCII code for `/`
is greater than that of `-`, so `partial-` is put before `partial/`.
This is this PR's behavior with prefix matching (`partial/hello.txt` is
at the start):

And with fuzzy matching:

# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
- Modified the partial completions test fixture to test whether this PR
even fixed anything
- Modified fixture to test sort order of .nu completions (a previous
version of my changes didn't sort all the completions at the end but
there were no tests catching that)
- Added a test for making sure subcommand completions are sorted by
Levenshtein distance (a previous version of my changes sorted in
alphabetical order but there were no tests catching that)
# 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.
-->
2024-07-03 13:48:06 +02:00
|
|
|
}
|
|
|
|
|
2022-12-08 21:37:10 +01:00
|
|
|
#[test]
|
|
|
|
fn file_completion_quoted() {
|
|
|
|
let (_, _, engine, stack) = new_quote_engine();
|
|
|
|
|
2024-05-09 07:38:24 +02:00
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
2022-12-08 21:37:10 +01:00
|
|
|
|
|
|
|
let target_dir = "open ";
|
|
|
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let test_dir_folder = format!("`{}`", folder("test dir"));
|
|
|
|
let expected_paths: Vec<_> = vec![
|
|
|
|
"`--help`",
|
|
|
|
"`-42`",
|
|
|
|
"`-inf`",
|
|
|
|
"`4.2`",
|
|
|
|
"\'[a] bc.txt\'",
|
|
|
|
"`te st.txt`",
|
|
|
|
"`te#st.txt`",
|
|
|
|
"`te'st.txt`",
|
|
|
|
"`te(st).txt`",
|
|
|
|
test_dir_folder.as_str(),
|
2023-10-06 18:45:30 +02:00
|
|
|
];
|
|
|
|
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected_paths, &suggestions);
|
2023-10-06 18:45:30 +02:00
|
|
|
|
|
|
|
let dir: PathBuf = "test dir".into();
|
|
|
|
let target_dir = format!("open '{}'", folder(dir.clone()));
|
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths = [
|
2023-10-06 18:45:30 +02:00
|
|
|
format!("`{}`", file(dir.join("double quote"))),
|
|
|
|
format!("`{}`", file(dir.join("single quote"))),
|
2022-12-08 21:37:10 +01:00
|
|
|
];
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_paths, &suggestions)
|
2022-12-08 21:37:10 +01:00
|
|
|
}
|
|
|
|
|
2022-08-23 16:24:24 +02:00
|
|
|
#[test]
|
|
|
|
fn flag_completions() {
|
|
|
|
// Create a new engine
|
|
|
|
let (_, _, engine, stack) = new_engine();
|
|
|
|
|
Fix typos by codespell (#7600)
# Description
Found via `codespell -S target -L
crate,ser,numer,falsy,ro,te,nd,bu,ndoes,statics,ons,fo,rouge,pard`
# User-Facing Changes
None.
# Tests + Formatting
None and done.
# After Submitting
None.
2022-12-26 08:31:26 +01:00
|
|
|
// Instantiate a new completer
|
2024-05-09 07:38:24 +02:00
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
2022-08-23 16:24:24 +02:00
|
|
|
// Test completions for the 'ls' flags
|
|
|
|
let suggestions = completer.complete("ls -", 4);
|
|
|
|
|
2024-09-24 15:40:48 +02:00
|
|
|
assert_eq!(18, suggestions.len());
|
2022-08-23 16:24:24 +02:00
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec![
|
|
|
|
"--all",
|
|
|
|
"--directory",
|
|
|
|
"--du",
|
|
|
|
"--full-paths",
|
|
|
|
"--help",
|
|
|
|
"--long",
|
|
|
|
"--mime-type",
|
|
|
|
"--short-names",
|
|
|
|
"--threads",
|
|
|
|
"-a",
|
|
|
|
"-D",
|
|
|
|
"-d",
|
|
|
|
"-f",
|
|
|
|
"-h",
|
|
|
|
"-l",
|
|
|
|
"-m",
|
|
|
|
"-s",
|
|
|
|
"-t",
|
2022-08-23 16:24:24 +02:00
|
|
|
];
|
|
|
|
|
2025-02-11 13:34:51 +01:00
|
|
|
// Match results
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn attribute_completions() {
|
|
|
|
// Create a new engine
|
|
|
|
let (_, _, engine, stack) = new_engine();
|
|
|
|
|
|
|
|
// Instantiate a new completer
|
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
|
|
|
// Test completions for the 'ls' flags
|
|
|
|
let suggestions = completer.complete("@", 1);
|
|
|
|
|
|
|
|
// Only checking for the builtins and not the std attributes
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["category", "example", "search-terms"];
|
2025-02-11 13:34:51 +01:00
|
|
|
|
|
|
|
// Match results
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn attributable_completions() {
|
|
|
|
// Create a new engine
|
|
|
|
let (_, _, engine, stack) = new_engine();
|
|
|
|
|
|
|
|
// Instantiate a new completer
|
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
|
|
|
// Test completions for the 'ls' flags
|
|
|
|
let suggestions = completer.complete("@example; ", 10);
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["def", "export def", "export extern", "extern"];
|
2025-02-11 13:34:51 +01:00
|
|
|
|
2022-08-23 16:24:24 +02:00
|
|
|
// Match results
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2022-08-23 16:24:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-09-22 20:50:16 +02:00
|
|
|
fn folder_with_directorycompletions() {
|
2022-08-23 16:24:24 +02:00
|
|
|
// Create a new engine
|
|
|
|
let (dir, dir_str, engine, stack) = new_engine();
|
|
|
|
|
Fix typos by codespell (#7600)
# Description
Found via `codespell -S target -L
crate,ser,numer,falsy,ro,te,nd,bu,ndoes,statics,ons,fo,rouge,pard`
# User-Facing Changes
None.
# Tests + Formatting
None and done.
# After Submitting
None.
2022-12-26 08:31:26 +01:00
|
|
|
// Instantiate a new completer
|
2024-05-09 07:38:24 +02:00
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
// Test completions for the current folder
|
Migrate to a new PWD API (#12603)
This is the first PR towards migrating to a new `$env.PWD` API that
returns potentially un-canonicalized paths. Refer to PR #12515 for
motivations.
## New API: `EngineState::cwd()`
The goal of the new API is to cover both parse-time and runtime use
case, and avoid unintentional misuse. It takes an `Option<Stack>` as
argument, which if supplied, will search for `$env.PWD` on the stack in
additional to the engine state. I think with this design, there's less
confusion over parse-time and runtime environments. If you have access
to a stack, just supply it; otherwise supply `None`.
## Deprecation of other PWD-related APIs
Other APIs are re-implemented using `EngineState::cwd()` and properly
documented. They're marked deprecated, but their behavior is unchanged.
Unused APIs are deleted, and code that accesses `$env.PWD` directly
without using an API is rewritten.
Deprecated APIs:
* `EngineState::current_work_dir()`
* `StateWorkingSet::get_cwd()`
* `env::current_dir()`
* `env::current_dir_str()`
* `env::current_dir_const()`
* `env::current_dir_str_const()`
Other changes:
* `EngineState::get_cwd()` (deleted)
* `StateWorkingSet::list_env()` (deleted)
* `repl::do_run_cmd()` (rewritten with `env::current_dir_str()`)
## `cd` and `pwd` now use logical paths by default
This pulls the changes from PR #12515. It's currently somewhat broken
because using non-canonicalized paths exposed a bug in our path
normalization logic (Issue #12602). Once that is fixed, this should
work.
## Future plans
This PR needs some tests. Which test helpers should I use, and where
should I put those tests?
I noticed that unquoted paths are expanded within `eval_filepath()` and
`eval_directory()` before they even reach the `cd` command. This means
every paths is expanded twice. Is this intended?
Once this PR lands, the plan is to review all usages of the deprecated
APIs and migrate them to `EngineState::cwd()`. In the meantime, these
usages are annotated with `#[allow(deprecated)]` to avoid breaking CI.
---------
Co-authored-by: Jakub Žádník <kubouch@gmail.com>
2024-05-03 13:33:09 +02:00
|
|
|
let target_dir = format!("cd {dir_str}{MAIN_SEPARATOR}");
|
2022-08-23 16:24:24 +02:00
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
|
|
|
// Create the expected values
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths = [
|
2023-02-22 14:03:48 +01:00
|
|
|
folder(dir.join("another")),
|
2023-12-19 10:14:34 +01:00
|
|
|
folder(dir.join("directory_completion")),
|
2022-08-23 16:24:24 +02:00
|
|
|
folder(dir.join("test_a")),
|
|
|
|
folder(dir.join("test_b")),
|
|
|
|
folder(dir.join(".hidden_folder")),
|
|
|
|
];
|
|
|
|
|
2024-07-30 15:28:41 +02:00
|
|
|
#[cfg(windows)]
|
|
|
|
{
|
|
|
|
let target_dir = format!("cd {dir_str}/");
|
|
|
|
let slash_suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_slash_paths: Vec<_> = expected_paths
|
2024-07-30 15:28:41 +02:00
|
|
|
.iter()
|
|
|
|
.map(|s| s.replace('\\', "/"))
|
|
|
|
.collect();
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_slash_paths, &slash_suggestions);
|
2024-07-30 15:28:41 +02:00
|
|
|
}
|
|
|
|
|
2022-08-23 16:24:24 +02:00
|
|
|
// Match the results
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_paths, &suggestions);
|
2022-08-23 16:24:24 +02:00
|
|
|
}
|
|
|
|
|
2024-09-09 20:39:18 +02:00
|
|
|
#[test]
|
|
|
|
fn folder_with_directorycompletions_with_dots() {
|
|
|
|
// Create a new engine
|
|
|
|
let (dir, _, engine, stack) = new_engine();
|
|
|
|
let dir_str = dir
|
|
|
|
.join("directory_completion")
|
|
|
|
.join("folder_inside_folder")
|
|
|
|
.into_os_string()
|
|
|
|
.into_string()
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// Instantiate a new completer
|
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
|
|
|
|
|
|
|
// Test completions for the current folder
|
|
|
|
let target_dir = format!("cd {dir_str}{MAIN_SEPARATOR}..{MAIN_SEPARATOR}");
|
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
|
|
|
// Create the expected values
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths = [folder(
|
2024-09-09 20:39:18 +02:00
|
|
|
dir.join("directory_completion")
|
|
|
|
.join("folder_inside_folder")
|
|
|
|
.join("..")
|
|
|
|
.join("folder_inside_folder"),
|
|
|
|
)];
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
{
|
|
|
|
let target_dir = format!("cd {dir_str}/../");
|
|
|
|
let slash_suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_slash_paths: Vec<_> = expected_paths
|
2024-09-09 20:39:18 +02:00
|
|
|
.iter()
|
|
|
|
.map(|s| s.replace('\\', "/"))
|
|
|
|
.collect();
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_slash_paths, &slash_suggestions);
|
2024-09-09 20:39:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Match the results
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_paths, &suggestions);
|
2024-09-09 20:39:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn folder_with_directorycompletions_with_three_trailing_dots() {
|
|
|
|
// Create a new engine
|
|
|
|
let (dir, _, engine, stack) = new_engine();
|
|
|
|
let dir_str = dir
|
|
|
|
.join("directory_completion")
|
|
|
|
.join("folder_inside_folder")
|
|
|
|
.into_os_string()
|
|
|
|
.into_string()
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// Instantiate a new completer
|
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
|
|
|
|
|
|
|
// Test completions for the current folder
|
|
|
|
let target_dir = format!("cd {dir_str}{MAIN_SEPARATOR}...{MAIN_SEPARATOR}");
|
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
|
|
|
// Create the expected values
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths = [
|
2024-09-09 20:39:18 +02:00
|
|
|
folder(
|
|
|
|
dir.join("directory_completion")
|
|
|
|
.join("folder_inside_folder")
|
|
|
|
.join("...")
|
|
|
|
.join("another"),
|
|
|
|
),
|
|
|
|
folder(
|
|
|
|
dir.join("directory_completion")
|
|
|
|
.join("folder_inside_folder")
|
|
|
|
.join("...")
|
|
|
|
.join("directory_completion"),
|
|
|
|
),
|
|
|
|
folder(
|
|
|
|
dir.join("directory_completion")
|
|
|
|
.join("folder_inside_folder")
|
|
|
|
.join("...")
|
|
|
|
.join("test_a"),
|
|
|
|
),
|
|
|
|
folder(
|
|
|
|
dir.join("directory_completion")
|
|
|
|
.join("folder_inside_folder")
|
|
|
|
.join("...")
|
|
|
|
.join("test_b"),
|
|
|
|
),
|
|
|
|
folder(
|
|
|
|
dir.join("directory_completion")
|
|
|
|
.join("folder_inside_folder")
|
|
|
|
.join("...")
|
|
|
|
.join(".hidden_folder"),
|
|
|
|
),
|
|
|
|
];
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
{
|
|
|
|
let target_dir = format!("cd {dir_str}/.../");
|
|
|
|
let slash_suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_slash_paths: Vec<_> = expected_paths
|
2024-09-09 20:39:18 +02:00
|
|
|
.iter()
|
|
|
|
.map(|s| s.replace('\\', "/"))
|
|
|
|
.collect();
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_slash_paths, &slash_suggestions);
|
2024-09-09 20:39:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Match the results
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_paths, &suggestions);
|
2024-09-09 20:39:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn folder_with_directorycompletions_do_not_collapse_dots() {
|
|
|
|
// Create a new engine
|
|
|
|
let (dir, _, engine, stack) = new_engine();
|
|
|
|
let dir_str = dir
|
|
|
|
.join("directory_completion")
|
|
|
|
.join("folder_inside_folder")
|
|
|
|
.into_os_string()
|
|
|
|
.into_string()
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// Instantiate a new completer
|
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
|
|
|
|
|
|
|
// Test completions for the current folder
|
|
|
|
let target_dir = format!("cd {dir_str}{MAIN_SEPARATOR}..{MAIN_SEPARATOR}..{MAIN_SEPARATOR}");
|
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
|
|
|
// Create the expected values
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec![
|
2024-09-09 20:39:18 +02:00
|
|
|
folder(
|
|
|
|
dir.join("directory_completion")
|
|
|
|
.join("folder_inside_folder")
|
|
|
|
.join("..")
|
|
|
|
.join("..")
|
|
|
|
.join("another"),
|
|
|
|
),
|
|
|
|
folder(
|
|
|
|
dir.join("directory_completion")
|
|
|
|
.join("folder_inside_folder")
|
|
|
|
.join("..")
|
|
|
|
.join("..")
|
|
|
|
.join("directory_completion"),
|
|
|
|
),
|
|
|
|
folder(
|
|
|
|
dir.join("directory_completion")
|
|
|
|
.join("folder_inside_folder")
|
|
|
|
.join("..")
|
|
|
|
.join("..")
|
|
|
|
.join("test_a"),
|
|
|
|
),
|
|
|
|
folder(
|
|
|
|
dir.join("directory_completion")
|
|
|
|
.join("folder_inside_folder")
|
|
|
|
.join("..")
|
|
|
|
.join("..")
|
|
|
|
.join("test_b"),
|
|
|
|
),
|
|
|
|
folder(
|
|
|
|
dir.join("directory_completion")
|
|
|
|
.join("folder_inside_folder")
|
|
|
|
.join("..")
|
|
|
|
.join("..")
|
|
|
|
.join(".hidden_folder"),
|
|
|
|
),
|
|
|
|
];
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
{
|
|
|
|
let target_dir = format!("cd {dir_str}/../../");
|
|
|
|
let slash_suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_slash_paths: Vec<_> = expected_paths
|
2024-09-09 20:39:18 +02:00
|
|
|
.iter()
|
|
|
|
.map(|s| s.replace('\\', "/"))
|
|
|
|
.collect();
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_slash_paths, &slash_suggestions);
|
2024-09-09 20:39:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Match the results
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions_by_string(&expected_paths, &suggestions);
|
2024-09-09 20:39:18 +02:00
|
|
|
}
|
|
|
|
|
2022-08-23 16:24:24 +02:00
|
|
|
#[test]
|
|
|
|
fn variables_completions() {
|
|
|
|
// Create a new engine
|
2024-09-25 20:04:26 +02:00
|
|
|
let (_, _, mut engine, mut stack) = new_engine();
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
// Add record value as example
|
|
|
|
let record = "let actor = { name: 'Tom Hardy', age: 44 }";
|
2024-09-25 20:04:26 +02:00
|
|
|
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack).is_ok());
|
2022-08-23 16:24:24 +02:00
|
|
|
|
Fix typos by codespell (#7600)
# Description
Found via `codespell -S target -L
crate,ser,numer,falsy,ro,te,nd,bu,ndoes,statics,ons,fo,rouge,pard`
# User-Facing Changes
None.
# Tests + Formatting
None and done.
# After Submitting
None.
2022-12-26 08:31:26 +01:00
|
|
|
// Instantiate a new completer
|
2024-05-09 07:38:24 +02:00
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
// Test completions for $nu
|
|
|
|
let suggestions = completer.complete("$nu.", 4);
|
|
|
|
|
2025-01-02 23:10:05 +01:00
|
|
|
assert_eq!(19, suggestions.len());
|
2022-08-23 16:24:24 +02:00
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec![
|
|
|
|
"cache-dir",
|
|
|
|
"config-path",
|
|
|
|
"current-exe",
|
|
|
|
"data-dir",
|
|
|
|
"default-config-dir",
|
|
|
|
"env-path",
|
|
|
|
"history-enabled",
|
|
|
|
"history-path",
|
|
|
|
"home-path",
|
|
|
|
"is-interactive",
|
|
|
|
"is-login",
|
|
|
|
"loginshell-path",
|
|
|
|
"os-info",
|
|
|
|
"pid",
|
|
|
|
"plugin-path",
|
|
|
|
"startup-time",
|
|
|
|
"temp-path",
|
|
|
|
"user-autoload-dirs",
|
|
|
|
"vendor-autoload-dirs",
|
2022-08-23 16:24:24 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
// Match results
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
// Test completions for $nu.h (filter)
|
|
|
|
let suggestions = completer.complete("$nu.h", 5);
|
|
|
|
|
2024-01-17 16:40:59 +01:00
|
|
|
assert_eq!(3, suggestions.len());
|
2022-08-23 16:24:24 +02:00
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["history-enabled", "history-path", "home-path"];
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
// Match results
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2022-08-23 16:24:24 +02:00
|
|
|
|
2023-04-26 16:15:27 +02:00
|
|
|
// Test completions for $nu.os-info
|
|
|
|
let suggestions = completer.complete("$nu.os-info.", 12);
|
|
|
|
assert_eq!(4, suggestions.len());
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["arch", "family", "kernel_version", "name"];
|
2023-04-26 16:15:27 +02:00
|
|
|
// Match results
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2023-04-26 16:15:27 +02:00
|
|
|
|
2022-08-23 16:24:24 +02:00
|
|
|
// Test completions for custom var
|
|
|
|
let suggestions = completer.complete("$actor.", 7);
|
|
|
|
|
|
|
|
assert_eq!(2, suggestions.len());
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["age", "name"];
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
// Match results
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
// Test completions for custom var (filtering)
|
|
|
|
let suggestions = completer.complete("$actor.n", 8);
|
|
|
|
|
|
|
|
assert_eq!(1, suggestions.len());
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["name"];
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
// Match results
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
// Test completions for $env
|
|
|
|
let suggestions = completer.complete("$env.", 5);
|
|
|
|
|
2023-02-09 03:53:46 +01:00
|
|
|
assert_eq!(3, suggestions.len());
|
2022-08-23 16:24:24 +02:00
|
|
|
|
2023-02-09 03:53:46 +01:00
|
|
|
#[cfg(windows)]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["Path", "PWD", "TEST"];
|
2023-02-09 03:53:46 +01:00
|
|
|
#[cfg(not(windows))]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["PATH", "PWD", "TEST"];
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
// Match results
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
// Test completions for $env
|
|
|
|
let suggestions = completer.complete("$env.T", 6);
|
|
|
|
|
|
|
|
assert_eq!(1, suggestions.len());
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["TEST"];
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
// Match results
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2024-07-06 00:58:35 +02:00
|
|
|
|
|
|
|
let suggestions = completer.complete("$", 1);
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["$actor", "$env", "$in", "$nu"];
|
2024-07-06 00:58:35 +02:00
|
|
|
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2022-08-23 16:24:24 +02:00
|
|
|
}
|
|
|
|
|
2025-02-10 04:26:41 +01:00
|
|
|
#[test]
|
|
|
|
fn record_cell_path_completions() {
|
|
|
|
let (_, _, mut engine, mut stack) = new_engine();
|
|
|
|
let command = r#"let foo = {a: [1 {a: 2}]}; const bar = {a: [1 {a: 2}]}"#;
|
|
|
|
assert!(support::merge_input(command.as_bytes(), &mut engine, &mut stack).is_ok());
|
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["a"];
|
2025-02-10 04:26:41 +01:00
|
|
|
let completion_str = "$foo.";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
|
|
|
|
let completion_str = "$foo.a.1.";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
|
|
|
|
let completion_str = "$bar.";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
|
|
|
|
let completion_str = "$bar.a.1.";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
|
|
|
|
let completion_str = "{a: [1 {a: 2}]}.a.1.";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn table_cell_path_completions() {
|
|
|
|
let (_, _, mut engine, mut stack) = new_engine();
|
|
|
|
let command = r#"let foo = [{a:{b:1}}, {a:{b:2}}]; const bar = [[a b]; [1 2]]"#;
|
|
|
|
assert!(support::merge_input(command.as_bytes(), &mut engine, &mut stack).is_ok());
|
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["a"];
|
2025-02-10 04:26:41 +01:00
|
|
|
let completion_str = "$foo.";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["b"];
|
2025-02-10 04:26:41 +01:00
|
|
|
let completion_str = "$foo.a.";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["a", "b"];
|
2025-02-10 04:26:41 +01:00
|
|
|
let completion_str = "$bar.";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
}
|
|
|
|
|
2022-08-23 16:24:24 +02:00
|
|
|
#[test]
|
|
|
|
fn alias_of_command_and_flags() {
|
2024-09-25 20:04:26 +02:00
|
|
|
let (_, _, mut engine, mut stack) = new_engine();
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
// Create an alias
|
|
|
|
let alias = r#"alias ll = ls -l"#;
|
2024-09-25 20:04:26 +02:00
|
|
|
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack).is_ok());
|
2022-08-23 16:24:24 +02:00
|
|
|
|
2024-05-09 07:38:24 +02:00
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
let suggestions = completer.complete("ll t", 4);
|
|
|
|
#[cfg(windows)]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec!["test_a\\", "test_a_symlink", "test_b\\"];
|
2022-08-23 16:24:24 +02:00
|
|
|
#[cfg(not(windows))]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec!["test_a/", "test_a_symlink", "test_b/"];
|
2022-08-23 16:24:24 +02:00
|
|
|
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected_paths, &suggestions)
|
2022-08-23 16:24:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn alias_of_basic_command() {
|
2024-09-25 20:04:26 +02:00
|
|
|
let (_, _, mut engine, mut stack) = new_engine();
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
// Create an alias
|
|
|
|
let alias = r#"alias ll = ls "#;
|
2024-09-25 20:04:26 +02:00
|
|
|
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack).is_ok());
|
2022-08-23 16:24:24 +02:00
|
|
|
|
2024-05-09 07:38:24 +02:00
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
let suggestions = completer.complete("ll t", 4);
|
|
|
|
#[cfg(windows)]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec!["test_a\\", "test_a_symlink", "test_b\\"];
|
2022-08-23 16:24:24 +02:00
|
|
|
#[cfg(not(windows))]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec!["test_a/", "test_a_symlink", "test_b/"];
|
2022-08-23 16:24:24 +02:00
|
|
|
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected_paths, &suggestions)
|
2022-08-23 16:24:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn alias_of_another_alias() {
|
2024-09-25 20:04:26 +02:00
|
|
|
let (_, _, mut engine, mut stack) = new_engine();
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
// Create an alias
|
|
|
|
let alias = r#"alias ll = ls -la"#;
|
2024-09-25 20:04:26 +02:00
|
|
|
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack).is_ok());
|
2022-08-23 16:24:24 +02:00
|
|
|
// Create the second alias
|
|
|
|
let alias = r#"alias lf = ll -f"#;
|
2024-09-25 20:04:26 +02:00
|
|
|
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack).is_ok());
|
2022-08-23 16:24:24 +02:00
|
|
|
|
2024-05-09 07:38:24 +02:00
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
let suggestions = completer.complete("lf t", 4);
|
|
|
|
#[cfg(windows)]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec!["test_a\\", "test_a_symlink", "test_b\\"];
|
2022-08-23 16:24:24 +02:00
|
|
|
#[cfg(not(windows))]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec!["test_a/", "test_a_symlink", "test_b/"];
|
2022-08-23 16:24:24 +02:00
|
|
|
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected_paths, &suggestions)
|
2022-08-23 16:24:24 +02:00
|
|
|
}
|
|
|
|
|
2024-04-21 07:03:33 +02:00
|
|
|
fn run_external_completion(completer: &str, input: &str) -> Vec<Suggestion> {
|
|
|
|
let completer = format!("$env.config.completions.external.completer = {completer}");
|
|
|
|
|
2022-08-23 16:24:24 +02:00
|
|
|
// Create a new engine
|
2024-09-25 20:04:26 +02:00
|
|
|
let (_, _, mut engine_state, mut stack) = new_engine();
|
2024-04-21 07:03:33 +02:00
|
|
|
let (block, delta) = {
|
2022-08-23 16:24:24 +02:00
|
|
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
2024-04-21 07:03:33 +02:00
|
|
|
let block = parse(&mut working_set, None, completer.as_bytes(), false);
|
2023-04-07 02:35:45 +02:00
|
|
|
assert!(working_set.parse_errors.is_empty());
|
2022-08-23 16:24:24 +02:00
|
|
|
|
|
|
|
(block, working_set.render())
|
|
|
|
};
|
|
|
|
|
|
|
|
assert!(engine_state.merge_delta(delta).is_ok());
|
|
|
|
|
2024-04-21 07:03:33 +02:00
|
|
|
assert!(
|
|
|
|
eval_block::<WithoutDebug>(&engine_state, &mut stack, &block, PipelineData::Empty).is_ok()
|
|
|
|
);
|
|
|
|
|
2022-08-23 16:24:24 +02:00
|
|
|
// Merge environment into the permanent state
|
2024-09-25 20:04:26 +02:00
|
|
|
assert!(engine_state.merge_env(&mut stack).is_ok());
|
2022-08-23 16:24:24 +02:00
|
|
|
|
Fix typos by codespell (#7600)
# Description
Found via `codespell -S target -L
crate,ser,numer,falsy,ro,te,nd,bu,ndoes,statics,ons,fo,rouge,pard`
# User-Facing Changes
None.
# Tests + Formatting
None and done.
# After Submitting
None.
2022-12-26 08:31:26 +01:00
|
|
|
// Instantiate a new completer
|
2024-05-09 07:38:24 +02:00
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine_state), Arc::new(stack));
|
2022-08-23 16:24:24 +02:00
|
|
|
|
2022-09-25 22:06:13 +02:00
|
|
|
completer.complete(input, input.len())
|
2022-08-23 16:24:24 +02:00
|
|
|
}
|
2022-08-24 21:46:00 +02:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn unknown_command_completion() {
|
|
|
|
let (_, _, engine, stack) = new_engine();
|
|
|
|
|
2024-05-09 07:38:24 +02:00
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
2022-08-24 21:46:00 +02:00
|
|
|
|
|
|
|
let target_dir = "thiscommanddoesnotexist ";
|
|
|
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec![
|
|
|
|
"another\\",
|
|
|
|
"custom_completion.nu",
|
|
|
|
"directory_completion\\",
|
|
|
|
"nushell",
|
|
|
|
"test_a\\",
|
|
|
|
"test_a_symlink",
|
|
|
|
"test_b\\",
|
|
|
|
".hidden_file",
|
|
|
|
".hidden_folder\\",
|
2022-08-24 21:46:00 +02:00
|
|
|
];
|
|
|
|
#[cfg(not(windows))]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec![
|
|
|
|
"another/",
|
|
|
|
"custom_completion.nu",
|
|
|
|
"directory_completion/",
|
|
|
|
"nushell",
|
|
|
|
"test_a/",
|
|
|
|
"test_a_symlink",
|
|
|
|
"test_b/",
|
|
|
|
".hidden_file",
|
|
|
|
".hidden_folder/",
|
2022-08-24 21:46:00 +02:00
|
|
|
];
|
|
|
|
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected_paths, &suggestions)
|
2022-08-24 21:46:00 +02:00
|
|
|
}
|
2022-09-29 10:43:58 +02:00
|
|
|
|
|
|
|
#[rstest]
|
|
|
|
fn flagcompletion_triggers_after_cursor(mut completer: NuCompleter) {
|
|
|
|
let suggestions = completer.complete("tst -h", 5);
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["--help", "--mod", "-h", "-s"];
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2022-09-29 10:43:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[rstest]
|
|
|
|
fn customcompletion_triggers_after_cursor(mut completer_strings: NuCompleter) {
|
|
|
|
let suggestions = completer_strings.complete("my-command c", 11);
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["cat", "dog", "eel"];
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2022-09-29 10:43:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[rstest]
|
|
|
|
fn customcompletion_triggers_after_cursor_piped(mut completer_strings: NuCompleter) {
|
|
|
|
let suggestions = completer_strings.complete("my-command c | ls", 11);
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["cat", "dog", "eel"];
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2022-09-29 10:43:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[rstest]
|
|
|
|
fn flagcompletion_triggers_after_cursor_piped(mut completer: NuCompleter) {
|
|
|
|
let suggestions = completer.complete("tst -h | ls", 5);
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["--help", "--mod", "-h", "-s"];
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2022-09-29 10:43:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn filecompletions_triggers_after_cursor() {
|
|
|
|
let (_, _, engine, stack) = new_engine();
|
|
|
|
|
2024-05-09 07:38:24 +02:00
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
2022-09-29 10:43:58 +02:00
|
|
|
|
|
|
|
let suggestions = completer.complete("cp test_c", 3);
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec![
|
|
|
|
"another\\",
|
|
|
|
"custom_completion.nu",
|
|
|
|
"directory_completion\\",
|
|
|
|
"nushell",
|
|
|
|
"test_a\\",
|
|
|
|
"test_a_symlink",
|
|
|
|
"test_b\\",
|
|
|
|
".hidden_file",
|
|
|
|
".hidden_folder\\",
|
2022-09-29 10:43:58 +02:00
|
|
|
];
|
|
|
|
#[cfg(not(windows))]
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected_paths: Vec<_> = vec![
|
|
|
|
"another/",
|
|
|
|
"custom_completion.nu",
|
|
|
|
"directory_completion/",
|
|
|
|
"nushell",
|
|
|
|
"test_a/",
|
|
|
|
"test_a_symlink",
|
|
|
|
"test_b/",
|
|
|
|
".hidden_file",
|
|
|
|
".hidden_folder/",
|
2022-09-29 10:43:58 +02:00
|
|
|
];
|
|
|
|
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected_paths, &suggestions);
|
2022-09-29 10:43:58 +02:00
|
|
|
}
|
2022-12-21 23:33:26 +01:00
|
|
|
|
|
|
|
#[rstest]
|
|
|
|
fn extern_custom_completion_positional(mut extern_completer: NuCompleter) {
|
|
|
|
let suggestions = extern_completer.complete("spam ", 5);
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["cat", "dog", "eel"];
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2022-12-21 23:33:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[rstest]
|
|
|
|
fn extern_custom_completion_long_flag_1(mut extern_completer: NuCompleter) {
|
|
|
|
let suggestions = extern_completer.complete("spam --foo=", 11);
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["cat", "dog", "eel"];
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2022-12-21 23:33:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[rstest]
|
|
|
|
fn extern_custom_completion_long_flag_2(mut extern_completer: NuCompleter) {
|
|
|
|
let suggestions = extern_completer.complete("spam --foo ", 11);
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["cat", "dog", "eel"];
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2022-12-21 23:33:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[rstest]
|
|
|
|
fn extern_custom_completion_long_flag_short(mut extern_completer: NuCompleter) {
|
|
|
|
let suggestions = extern_completer.complete("spam -f ", 8);
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["cat", "dog", "eel"];
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2022-12-21 23:33:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[rstest]
|
|
|
|
fn extern_custom_completion_short_flag(mut extern_completer: NuCompleter) {
|
|
|
|
let suggestions = extern_completer.complete("spam -b ", 8);
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["cat", "dog", "eel"];
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2022-12-21 23:33:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[rstest]
|
|
|
|
fn extern_complete_flags(mut extern_completer: NuCompleter) {
|
|
|
|
let suggestions = extern_completer.complete("spam -", 6);
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["--foo", "-b", "-f"];
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2022-12-21 23:33:26 +01:00
|
|
|
}
|
2023-01-17 07:30:00 +01:00
|
|
|
|
2023-11-29 23:17:06 +01:00
|
|
|
#[rstest]
|
|
|
|
fn custom_completer_triggers_cursor_before_word(mut custom_completer: NuCompleter) {
|
|
|
|
let suggestions = custom_completer.complete("cmd foo bar", 8);
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["cmd", "foo", ""];
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2023-11-29 23:17:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[rstest]
|
|
|
|
fn custom_completer_triggers_cursor_on_word_left_boundary(mut custom_completer: NuCompleter) {
|
|
|
|
let suggestions = custom_completer.complete("cmd foo bar", 8);
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["cmd", "foo", ""];
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2023-11-29 23:17:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[rstest]
|
|
|
|
fn custom_completer_triggers_cursor_next_to_word(mut custom_completer: NuCompleter) {
|
|
|
|
let suggestions = custom_completer.complete("cmd foo bar", 11);
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["cmd", "foo", "bar"];
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2023-11-29 23:17:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[rstest]
|
|
|
|
fn custom_completer_triggers_cursor_after_word(mut custom_completer: NuCompleter) {
|
|
|
|
let suggestions = custom_completer.complete("cmd foo bar ", 12);
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["cmd", "foo", "bar", ""];
|
2024-07-30 15:28:41 +02:00
|
|
|
match_suggestions(&expected, &suggestions);
|
2023-11-29 23:17:06 +01:00
|
|
|
}
|
|
|
|
|
2024-08-06 02:30:10 +02:00
|
|
|
#[rstest]
|
|
|
|
fn sort_fuzzy_completions_in_alphabetical_order(mut fuzzy_alpha_sort_completer: NuCompleter) {
|
|
|
|
let suggestions = fuzzy_alpha_sort_completer.complete("ls nu", 5);
|
|
|
|
// Even though "nushell" is a better match, it should come second because
|
|
|
|
// the completions should be sorted in alphabetical order
|
2025-02-28 19:39:59 +01:00
|
|
|
match_suggestions(&vec!["custom_completion.nu", "nushell"], &suggestions);
|
2024-08-06 02:30:10 +02:00
|
|
|
}
|
|
|
|
|
Avoid recomputing fuzzy match scores (#13700)
<!--
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.
-->
This PR makes it so that when using fuzzy matching, the score isn't
recomputed when sorting. Instead, filtering and sorting suggestions is
handled by a new `NuMatcher` struct. This struct accepts suggestions
and, if they match the user's typed text, stores those suggestions
(along with their scores and values). At the end, it returns a sorted
list of suggestions.
This probably won't have a noticeable impact on performance, but it
might be helpful if we start using Nucleo in the future.
Minor change: Makes `find_commands_by_predicate` in `StateWorkingSet`
and `EngineState` take `FnMut` rather than `Fn` for the predicate.
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
When using case-insensitive matching, if you have two matches `FOO` and
`abc`, `abc` will be shown before `FOO` rather than the other way
around. I think this way makes more sense than the current behavior.
When I brought this up on Discord, WindSoilder did say it would make
sense to show uppercase matches first if the user typed, say, `F`.
However, that would be a lot more complicated to implement.
# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
Added a test for the changes in
https://github.com/nushell/nushell/pull/13302.
# 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.
-->
2024-11-22 13:29:00 +01:00
|
|
|
#[test]
|
|
|
|
fn exact_match() {
|
|
|
|
let (dir, _, engine, stack) = new_partial_engine();
|
|
|
|
|
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
|
|
|
|
|
|
|
let target_dir = format!("open {}", folder(dir.join("pArTiAL")));
|
|
|
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
|
|
|
|
|
|
|
// Since it's an exact match, only 'partial' should be suggested, not
|
|
|
|
// 'partial-a' and stuff. Implemented in #13302
|
|
|
|
match_suggestions(
|
2025-02-28 19:39:59 +01:00
|
|
|
&vec![file(dir.join("partial").join("hello.txt")).as_str()],
|
Avoid recomputing fuzzy match scores (#13700)
<!--
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.
-->
This PR makes it so that when using fuzzy matching, the score isn't
recomputed when sorting. Instead, filtering and sorting suggestions is
handled by a new `NuMatcher` struct. This struct accepts suggestions
and, if they match the user's typed text, stores those suggestions
(along with their scores and values). At the end, it returns a sorted
list of suggestions.
This probably won't have a noticeable impact on performance, but it
might be helpful if we start using Nucleo in the future.
Minor change: Makes `find_commands_by_predicate` in `StateWorkingSet`
and `EngineState` take `FnMut` rather than `Fn` for the predicate.
# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->
When using case-insensitive matching, if you have two matches `FOO` and
`abc`, `abc` will be shown before `FOO` rather than the other way
around. I think this way makes more sense than the current behavior.
When I brought this up on Discord, WindSoilder did say it would make
sense to show uppercase matches first if the user typed, say, `F`.
However, that would be a lot more complicated to implement.
# 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 toolkit.nu; toolkit test stdlib"` 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
> ```
-->
Added a test for the changes in
https://github.com/nushell/nushell/pull/13302.
# 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.
-->
2024-11-22 13:29:00 +01:00
|
|
|
&suggestions,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-02-18 18:38:29 +01:00
|
|
|
#[ignore = "was reverted, still needs fixing"]
|
2023-01-17 07:30:00 +01:00
|
|
|
#[rstest]
|
2023-02-18 18:38:29 +01:00
|
|
|
fn alias_offset_bug_7648() {
|
2024-09-25 20:04:26 +02:00
|
|
|
let (_, _, mut engine, mut stack) = new_engine();
|
2023-01-17 07:30:00 +01:00
|
|
|
|
|
|
|
// Create an alias
|
|
|
|
let alias = r#"alias ea = ^$env.EDITOR /tmp/test.s"#;
|
2024-09-25 20:04:26 +02:00
|
|
|
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack).is_ok());
|
2023-01-17 07:30:00 +01:00
|
|
|
|
2024-05-09 07:38:24 +02:00
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
2023-01-17 07:30:00 +01:00
|
|
|
|
2023-02-18 18:38:29 +01:00
|
|
|
// Issue #7648
|
2023-01-17 07:30:00 +01:00
|
|
|
// Nushell crashes when an alias name is shorter than the alias command
|
|
|
|
// and the alias command is a external command
|
|
|
|
// This happens because of offset is not correct.
|
|
|
|
// This crashes before PR #7779
|
|
|
|
let _suggestions = completer.complete("e", 1);
|
|
|
|
}
|
2023-01-19 11:19:27 +01:00
|
|
|
|
2023-02-18 18:38:29 +01:00
|
|
|
#[ignore = "was reverted, still needs fixing"]
|
2023-01-19 11:19:27 +01:00
|
|
|
#[rstest]
|
|
|
|
fn alias_offset_bug_7754() {
|
2024-09-25 20:04:26 +02:00
|
|
|
let (_, _, mut engine, mut stack) = new_engine();
|
2023-01-19 11:19:27 +01:00
|
|
|
|
|
|
|
// Create an alias
|
|
|
|
let alias = r#"alias ll = ls -l"#;
|
2024-09-25 20:04:26 +02:00
|
|
|
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack).is_ok());
|
2023-01-19 11:19:27 +01:00
|
|
|
|
2024-05-09 07:38:24 +02:00
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
2023-01-19 11:19:27 +01:00
|
|
|
|
|
|
|
// Issue #7754
|
|
|
|
// Nushell crashes when an alias name is shorter than the alias command
|
|
|
|
// and the alias command contains pipes.
|
|
|
|
// This crashes before PR #7756
|
|
|
|
let _suggestions = completer.complete("ll -a | c", 9);
|
|
|
|
}
|
2025-01-30 07:15:38 +01:00
|
|
|
|
|
|
|
#[rstest]
|
|
|
|
fn nested_block(mut completer: NuCompleter) {
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["--help", "--mod", "-h", "-s"];
|
2025-01-30 07:15:38 +01:00
|
|
|
|
|
|
|
let suggestions = completer.complete("somecmd | lines | each { tst - }", 30);
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
|
|
|
|
let suggestions = completer.complete("somecmd | lines | each { tst -}", 30);
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[rstest]
|
|
|
|
fn incomplete_nested_block(mut completer: NuCompleter) {
|
|
|
|
let suggestions = completer.complete("somecmd | lines | each { tst -", 30);
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["--help", "--mod", "-h", "-s"];
|
2025-01-30 07:15:38 +01:00
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[rstest]
|
|
|
|
fn deeply_nested_block(mut completer: NuCompleter) {
|
|
|
|
let suggestions = completer.complete(
|
|
|
|
"somecmd | lines | each { print ([each (print) (tst -)]) }",
|
|
|
|
52,
|
|
|
|
);
|
2025-02-28 19:39:59 +01:00
|
|
|
let expected: Vec<_> = vec!["--help", "--mod", "-h", "-s"];
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[rstest]
|
|
|
|
fn operator_completions(mut custom_completer: NuCompleter) {
|
|
|
|
let suggestions = custom_completer.complete("1 ", 2);
|
|
|
|
// == != > < >= <= in not-in
|
|
|
|
// + - * / // mod **
|
|
|
|
// 5 bit-xxx
|
|
|
|
assert_eq!(20, suggestions.len());
|
|
|
|
let suggestions = custom_completer.complete("1 bit-s", 7);
|
|
|
|
let expected: Vec<_> = vec!["bit-shl", "bit-shr"];
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
|
|
|
|
let suggestions = custom_completer.complete("'str' ", 6);
|
|
|
|
// == != > < >= <= in not-in
|
|
|
|
// has not-has starts-with ends-with
|
|
|
|
// =~ !~ like not-like ++
|
|
|
|
assert_eq!(17, suggestions.len());
|
|
|
|
let suggestions = custom_completer.complete("'str' +", 7);
|
|
|
|
let expected: Vec<_> = vec!["++"];
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
|
|
|
|
let suggestions = custom_completer.complete("1ms ", 4);
|
|
|
|
// == != > < >= <= in not-in
|
|
|
|
// + - * / // mod
|
|
|
|
assert_eq!(14, suggestions.len());
|
|
|
|
let suggestions = custom_completer.complete("1ms /", 5);
|
|
|
|
let expected: Vec<_> = vec!["/", "//"];
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
|
|
|
|
let suggestions = custom_completer.complete("..2 ", 4);
|
|
|
|
// == != in not-in has not-has
|
|
|
|
assert_eq!(6, suggestions.len());
|
|
|
|
let suggestions = custom_completer.complete("..2 h", 5);
|
|
|
|
let expected: Vec<_> = vec!["has"];
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
|
|
|
|
let suggestions = custom_completer.complete("[[];[]] ", 8);
|
|
|
|
// == != in not-in has not-has ++
|
|
|
|
assert_eq!(7, suggestions.len());
|
|
|
|
let suggestions = custom_completer.complete("[[];[]] h", 9);
|
|
|
|
let expected: Vec<_> = vec!["has"];
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
|
|
|
|
let suggestions = custom_completer.complete("(date now) ", 11);
|
|
|
|
// == != > < >= <= in not-in
|
|
|
|
assert_eq!(8, suggestions.len());
|
|
|
|
let suggestions = custom_completer.complete("(date now) <", 12);
|
|
|
|
let expected: Vec<_> = vec!["<", "<="];
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
|
|
|
|
// default operators for all types
|
|
|
|
let expected: Vec<_> = vec!["!=", "==", "in", "not-in"];
|
|
|
|
let suggestions = custom_completer.complete("{1} ", 4);
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
let suggestions = custom_completer.complete("null ", 5);
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[rstest]
|
|
|
|
fn cell_path_operator_completions(mut custom_completer: NuCompleter) {
|
|
|
|
let suggestions = custom_completer.complete("[1].0 ", 6);
|
|
|
|
// == != > < >= <= in not-in
|
|
|
|
// + - * / // mod **
|
|
|
|
// 5 bit-xxx
|
|
|
|
assert_eq!(20, suggestions.len());
|
|
|
|
let suggestions = custom_completer.complete("[1].0 bit-s", 11);
|
|
|
|
let expected: Vec<_> = vec!["bit-shl", "bit-shr"];
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
|
|
|
|
let suggestions = custom_completer.complete("{'foo': [1, 1kb]}.foo.1 ", 24);
|
|
|
|
// == != > < >= <= in not-in
|
|
|
|
// + - * / // mod
|
|
|
|
assert_eq!(14, suggestions.len());
|
|
|
|
let suggestions = custom_completer.complete("{'foo': [1, 1kb]}.foo.1 mo", 26);
|
|
|
|
let expected: Vec<_> = vec!["mod"];
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
|
|
|
|
let suggestions = custom_completer.complete("const f = {'foo': [1, '1']}; $f.foo.1 ", 38);
|
|
|
|
// == != > < >= <= in not-in
|
|
|
|
// has not-has starts-with ends-with
|
|
|
|
// =~ !~ like not-like ++
|
|
|
|
assert_eq!(17, suggestions.len());
|
|
|
|
let suggestions = custom_completer.complete("const f = {'foo': [1, '1']}; $f.foo.1 ++", 40);
|
|
|
|
let expected: Vec<_> = vec!["++"];
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[rstest]
|
|
|
|
fn assignment_operator_completions(mut custom_completer: NuCompleter) {
|
|
|
|
let suggestions = custom_completer.complete("mut foo = ''; $foo ", 19);
|
|
|
|
// == != > < >= <= in not-in
|
|
|
|
// has not-has starts-with ends-with
|
|
|
|
// =~ !~ like not-like ++
|
|
|
|
// = ++=
|
|
|
|
assert_eq!(19, suggestions.len());
|
|
|
|
let suggestions = custom_completer.complete("mut foo = ''; $foo ++", 21);
|
|
|
|
let expected: Vec<_> = vec!["++", "++="];
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
|
|
|
|
// == != > < >= <= in not-in
|
|
|
|
// =
|
|
|
|
let suggestions = custom_completer.complete("mut foo = date now; $foo ", 25);
|
|
|
|
assert_eq!(9, suggestions.len());
|
|
|
|
let suggestions = custom_completer.complete("mut foo = date now; $foo =", 26);
|
|
|
|
let expected: Vec<_> = vec!["=", "=="];
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
|
|
|
|
let suggestions = custom_completer.complete("mut foo = date now; $foo ", 25);
|
|
|
|
// == != > < >= <= in not-in
|
|
|
|
// =
|
|
|
|
assert_eq!(9, suggestions.len());
|
|
|
|
let suggestions = custom_completer.complete("mut foo = date now; $foo =", 26);
|
|
|
|
let expected: Vec<_> = vec!["=", "=="];
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
|
|
|
|
let suggestions = custom_completer.complete("mut foo = 1ms; $foo ", 20);
|
|
|
|
// == != > < >= <= in not-in
|
|
|
|
// + - * / // mod
|
|
|
|
// = += -= *= /=
|
|
|
|
assert_eq!(19, suggestions.len());
|
|
|
|
let suggestions = custom_completer.complete("mut foo = 1ms; $foo +", 21);
|
|
|
|
let expected: Vec<_> = vec!["+", "+="];
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
|
|
|
|
// default operators for all mutables
|
|
|
|
let expected: Vec<_> = vec!["!=", "=", "==", "in", "not-in"];
|
|
|
|
let suggestions = custom_completer.complete("mut foo = null; $foo ", 21);
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
|
|
|
|
// $env should be considered mutable
|
|
|
|
let suggestions = custom_completer.complete("$env.config.keybindings ", 24);
|
|
|
|
// == != in not-in
|
|
|
|
// has not-has ++=
|
|
|
|
// = ++=
|
|
|
|
assert_eq!(9, suggestions.len());
|
|
|
|
let expected: Vec<_> = vec!["++", "++="];
|
|
|
|
let suggestions = custom_completer.complete("$env.config.keybindings +", 25);
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn cellpath_assignment_operator_completions() {
|
|
|
|
let (_, _, mut engine, mut stack) = new_engine();
|
|
|
|
let command = r#"mut foo = {'foo': [1, '1']}"#;
|
|
|
|
assert!(support::merge_input(command.as_bytes(), &mut engine, &mut stack).is_ok());
|
|
|
|
|
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
|
|
|
let completion_str = "$foo.foo.1 ";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
// == != > < >= <= in not-in
|
|
|
|
// has not-has starts-with ends-with
|
|
|
|
// =~ !~ like not-like ++
|
|
|
|
// = ++=
|
|
|
|
assert_eq!(19, suggestions.len());
|
|
|
|
let completion_str = "$foo.foo.1 ++";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
let expected: Vec<_> = vec!["++", "++="];
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
|
|
|
|
let (_, _, mut engine, mut stack) = new_engine();
|
|
|
|
let command = r#"mut foo = {'foo': [1, (date now)]}"#;
|
|
|
|
assert!(support::merge_input(command.as_bytes(), &mut engine, &mut stack).is_ok());
|
|
|
|
|
|
|
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
|
|
|
let completion_str = "$foo.foo.1 ";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
// == != > < >= <= in not-in
|
|
|
|
// =
|
|
|
|
assert_eq!(9, suggestions.len());
|
|
|
|
let completion_str = "$foo.foo.1 =";
|
|
|
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
|
|
|
let expected: Vec<_> = vec!["=", "=="];
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: type inference
|
|
|
|
#[ignore]
|
|
|
|
#[rstest]
|
|
|
|
fn type_inferenced_operator_completions(mut custom_completer: NuCompleter) {
|
|
|
|
let suggestions = custom_completer.complete("let f = {'foo': [1, '1']}; $f.foo.1 ", 36);
|
|
|
|
// == != > < >= <= in not-in
|
|
|
|
// has not-has starts-with ends-with
|
|
|
|
// =~ !~ like not-like ++
|
|
|
|
assert_eq!(17, suggestions.len());
|
|
|
|
let suggestions = custom_completer.complete("const f = {'foo': [1, '1']}; $f.foo.1 ++", 38);
|
|
|
|
let expected: Vec<_> = vec!["++"];
|
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
|
|
|
|
let suggestions = custom_completer.complete("mut foo = [(date now)]; $foo.0 ", 31);
|
|
|
|
// == != > < >= <= in not-in
|
|
|
|
// =
|
|
|
|
assert_eq!(9, suggestions.len());
|
|
|
|
let suggestions = custom_completer.complete("mut foo = [(date now)]; $foo.0 =", 32);
|
|
|
|
let expected: Vec<_> = vec!["=", "=="];
|
2025-01-30 07:15:38 +01:00
|
|
|
match_suggestions(&expected, &suggestions);
|
|
|
|
}
|