files and directory completions now use ascending ordering rather than Levenshtein. #8023 (#8085)

# Description

This change sorts completions for files and directories by the ascending
ordering method, related to issue:
[#8023](https://github.com/nushell/nushell/issues/8023)

Currently the Suggestions are being sorted twice, so it's now following
the convention from `completion/base.rs` to match on the
`self.get_sort_by()` result.

# User-Facing Changes

Previously the suggestions were being sorted by the Levenshtein method:
```
/home/rdevenney/projects/open_source/nushell| cd
src/                wix/                docs/               tests/
assets/             crates/             docker/             images/
target/             benches/            pkg_mgrs/           .git/
.cargo/             .github/
```

Now when you tab for autocompletions, they show up in ascending
alphabetical order as shown below (with hidden files/folders at the
end).
```
/home/rdevenney/projects/open_source/nushell| cd
assets/             benches/            crates/             docker/
docs/               images/             pkg_mgrs/           src/
target/             tests/              wix/                .cargo/
.git/               .github/
```

And when you've already typed a bit of the path:
```
/home/rdevenney/projects/open_source/nushell| cd crates/nu
crates/nu-cli/                   crates/nu-color-config/          crates/nu-command/
crates/nu-engine/                crates/nu-explore/               crates/nu-glob/
crates/nu-json/                  crates/nu-parser/                crates/nu-path/
crates/nu-plugin/                crates/nu-pretty-hex/            crates/nu-protocol/
crates/nu-system/                crates/nu-table/                 crates/nu-term-grid/
crates/nu-test-support/          crates/nu-utils/                 crates/nu_plugin_custom_values/
crates/nu_plugin_example/        crates/nu_plugin_formats/        crates/nu_plugin_gstat/
crates/nu_plugin_inc/            crates/nu_plugin_python/         crates/nu_plugin_query/
```

And another for when there are files and directories present:
```
/home/rdevenney/projects/open_source/nushell/crates/nu-cli/src| nvim                              02/16/2023 08:22:16 AM
commands.rs          completions/         config_files.rs      eval_file.rs
lib.rs               menus/               nu_highlight.rs      print.rs
prompt.rs            prompt_update.rs     reedline_config.rs   repl.rs
syntax_highlight.rs  util.rs              validation.rs
```

# 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 -A
clippy::needless_collect` to check that you're using the standard code
style
[*] `cargo test --workspace` to check that all tests pass

# 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.
This commit is contained in:
Ryan Devenney 2023-02-22 08:03:48 -05:00 committed by GitHub
parent 150b0b6b86
commit d34a2c353f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 70 additions and 52 deletions

View File

@ -8,7 +8,7 @@ use std::fs;
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use super::{partial_from, prepend_base_dir}; use super::{partial_from, prepend_base_dir, SortBy};
const SEP: char = std::path::MAIN_SEPARATOR; const SEP: char = std::path::MAIN_SEPARATOR;
@ -60,12 +60,20 @@ impl Completer for DirectoryCompletion {
// Sort items // Sort items
let mut sorted_items = items; let mut sorted_items = items;
match self.get_sort_by() {
SortBy::Ascending => {
sorted_items.sort_by(|a, b| a.value.cmp(&b.value)); sorted_items.sort_by(|a, b| a.value.cmp(&b.value));
}
SortBy::LevenshteinDistance => {
sorted_items.sort_by(|a, b| { sorted_items.sort_by(|a, b| {
let a_distance = levenshtein_distance(&prefix_str, &a.value); let a_distance = levenshtein_distance(&prefix_str, &a.value);
let b_distance = levenshtein_distance(&prefix_str, &b.value); let b_distance = levenshtein_distance(&prefix_str, &b.value);
a_distance.cmp(&b_distance) a_distance.cmp(&b_distance)
}); });
}
_ => (),
}
// Separate the results between hidden and non hidden // Separate the results between hidden and non hidden
let mut hidden: Vec<Suggestion> = vec![]; let mut hidden: Vec<Suggestion> = vec![];

View File

@ -7,6 +7,8 @@ use reedline::Suggestion;
use std::path::{is_separator, Path}; use std::path::{is_separator, Path};
use std::sync::Arc; use std::sync::Arc;
use super::SortBy;
const SEP: char = std::path::MAIN_SEPARATOR; const SEP: char = std::path::MAIN_SEPARATOR;
#[derive(Clone)] #[derive(Clone)]
@ -55,12 +57,20 @@ impl Completer for FileCompletion {
// Sort items // Sort items
let mut sorted_items = items; let mut sorted_items = items;
match self.get_sort_by() {
SortBy::Ascending => {
sorted_items.sort_by(|a, b| a.value.cmp(&b.value)); sorted_items.sort_by(|a, b| a.value.cmp(&b.value));
}
SortBy::LevenshteinDistance => {
sorted_items.sort_by(|a, b| { sorted_items.sort_by(|a, b| {
let a_distance = levenshtein_distance(&prefix_str, &a.value); let a_distance = levenshtein_distance(&prefix_str, &a.value);
let b_distance = levenshtein_distance(&prefix_str, &b.value); let b_distance = levenshtein_distance(&prefix_str, &b.value);
a_distance.cmp(&b_distance) a_distance.cmp(&b_distance)
}); });
}
_ => (),
}
// Separate the results between hidden and non hidden // Separate the results between hidden and non hidden
let mut hidden: Vec<Suggestion> = vec![]; let mut hidden: Vec<Suggestion> = vec![];

View File

@ -178,11 +178,11 @@ fn file_completions() {
// Create the expected values // Create the expected values
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
folder(dir.join("another")),
file(dir.join("custom_completion.nu")),
file(dir.join("nushell")), file(dir.join("nushell")),
folder(dir.join("test_a")), folder(dir.join("test_a")),
folder(dir.join("test_b")), folder(dir.join("test_b")),
folder(dir.join("another")),
file(dir.join("custom_completion.nu")),
file(dir.join(".hidden_file")), file(dir.join(".hidden_file")),
folder(dir.join(".hidden_folder")), folder(dir.join(".hidden_folder")),
]; ];
@ -212,21 +212,21 @@ fn command_ls_with_filecompletion() {
#[cfg(windows)] #[cfg(windows)]
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a\\".to_string(), "test_a\\".to_string(),
"test_b\\".to_string(), "test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(), ".hidden_file".to_string(),
".hidden_folder\\".to_string(), ".hidden_folder\\".to_string(),
]; ];
#[cfg(not(windows))] #[cfg(not(windows))]
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a/".to_string(), "test_a/".to_string(),
"test_b/".to_string(), "test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(), ".hidden_file".to_string(),
".hidden_folder/".to_string(), ".hidden_folder/".to_string(),
]; ];
@ -244,21 +244,21 @@ fn command_open_with_filecompletion() {
#[cfg(windows)] #[cfg(windows)]
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a\\".to_string(), "test_a\\".to_string(),
"test_b\\".to_string(), "test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(), ".hidden_file".to_string(),
".hidden_folder\\".to_string(), ".hidden_folder\\".to_string(),
]; ];
#[cfg(not(windows))] #[cfg(not(windows))]
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a/".to_string(), "test_a/".to_string(),
"test_b/".to_string(), "test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(), ".hidden_file".to_string(),
".hidden_folder/".to_string(), ".hidden_folder/".to_string(),
]; ];
@ -277,21 +277,21 @@ fn command_rm_with_globcompletion() {
#[cfg(windows)] #[cfg(windows)]
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a\\".to_string(), "test_a\\".to_string(),
"test_b\\".to_string(), "test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(), ".hidden_file".to_string(),
".hidden_folder\\".to_string(), ".hidden_folder\\".to_string(),
]; ];
#[cfg(not(windows))] #[cfg(not(windows))]
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a/".to_string(), "test_a/".to_string(),
"test_b/".to_string(), "test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(), ".hidden_file".to_string(),
".hidden_folder/".to_string(), ".hidden_folder/".to_string(),
]; ];
@ -310,21 +310,21 @@ fn command_cp_with_globcompletion() {
#[cfg(windows)] #[cfg(windows)]
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a\\".to_string(), "test_a\\".to_string(),
"test_b\\".to_string(), "test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(), ".hidden_file".to_string(),
".hidden_folder\\".to_string(), ".hidden_folder\\".to_string(),
]; ];
#[cfg(not(windows))] #[cfg(not(windows))]
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a/".to_string(), "test_a/".to_string(),
"test_b/".to_string(), "test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(), ".hidden_file".to_string(),
".hidden_folder/".to_string(), ".hidden_folder/".to_string(),
]; ];
@ -343,21 +343,21 @@ fn command_save_with_filecompletion() {
#[cfg(windows)] #[cfg(windows)]
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a\\".to_string(), "test_a\\".to_string(),
"test_b\\".to_string(), "test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(), ".hidden_file".to_string(),
".hidden_folder\\".to_string(), ".hidden_folder\\".to_string(),
]; ];
#[cfg(not(windows))] #[cfg(not(windows))]
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a/".to_string(), "test_a/".to_string(),
"test_b/".to_string(), "test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(), ".hidden_file".to_string(),
".hidden_folder/".to_string(), ".hidden_folder/".to_string(),
]; ];
@ -376,21 +376,21 @@ fn command_touch_with_filecompletion() {
#[cfg(windows)] #[cfg(windows)]
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a\\".to_string(), "test_a\\".to_string(),
"test_b\\".to_string(), "test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(), ".hidden_file".to_string(),
".hidden_folder\\".to_string(), ".hidden_folder\\".to_string(),
]; ];
#[cfg(not(windows))] #[cfg(not(windows))]
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a/".to_string(), "test_a/".to_string(),
"test_b/".to_string(), "test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(), ".hidden_file".to_string(),
".hidden_folder/".to_string(), ".hidden_folder/".to_string(),
]; ];
@ -409,21 +409,21 @@ fn command_watch_with_filecompletion() {
#[cfg(windows)] #[cfg(windows)]
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a\\".to_string(), "test_a\\".to_string(),
"test_b\\".to_string(), "test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(), ".hidden_file".to_string(),
".hidden_folder\\".to_string(), ".hidden_folder\\".to_string(),
]; ];
#[cfg(not(windows))] #[cfg(not(windows))]
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a/".to_string(), "test_a/".to_string(),
"test_b/".to_string(), "test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(), ".hidden_file".to_string(),
".hidden_folder/".to_string(), ".hidden_folder/".to_string(),
]; ];
@ -499,9 +499,9 @@ fn folder_with_directorycompletions() {
// Create the expected values // Create the expected values
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
folder(dir.join("another")),
folder(dir.join("test_a")), folder(dir.join("test_a")),
folder(dir.join("test_b")), folder(dir.join("test_b")),
folder(dir.join("another")),
folder(dir.join(".hidden_folder")), folder(dir.join(".hidden_folder")),
]; ];
@ -695,21 +695,21 @@ fn unknown_command_completion() {
#[cfg(windows)] #[cfg(windows)]
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a\\".to_string(), "test_a\\".to_string(),
"test_b\\".to_string(), "test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(), ".hidden_file".to_string(),
".hidden_folder\\".to_string(), ".hidden_folder\\".to_string(),
]; ];
#[cfg(not(windows))] #[cfg(not(windows))]
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a/".to_string(), "test_a/".to_string(),
"test_b/".to_string(), "test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(), ".hidden_file".to_string(),
".hidden_folder/".to_string(), ".hidden_folder/".to_string(),
]; ];
@ -755,21 +755,21 @@ fn filecompletions_triggers_after_cursor() {
#[cfg(windows)] #[cfg(windows)]
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a\\".to_string(), "test_a\\".to_string(),
"test_b\\".to_string(), "test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(), ".hidden_file".to_string(),
".hidden_folder\\".to_string(), ".hidden_folder\\".to_string(),
]; ];
#[cfg(not(windows))] #[cfg(not(windows))]
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"nushell".to_string(), "nushell".to_string(),
"test_a/".to_string(), "test_a/".to_string(),
"test_b/".to_string(), "test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(), ".hidden_file".to_string(),
".hidden_folder/".to_string(), ".hidden_folder/".to_string(),
]; ];