Add completions.sort option (#13311)

This commit is contained in:
Yash Thakur
2024-08-05 20:30:10 -04:00
committed by GitHub
parent 2f44801414
commit 6d36941e55
15 changed files with 139 additions and 85 deletions

View File

@ -1,5 +1,5 @@
use crate::{
completions::{Completer, CompletionOptions, MatchAlgorithm, SortBy},
completions::{Completer, CompletionOptions, MatchAlgorithm},
SuggestionKind,
};
use nu_parser::FlatShape;
@ -193,11 +193,7 @@ impl Completer for CommandCompletion {
};
if !subcommands.is_empty() {
return sort_suggestions(
&String::from_utf8_lossy(&prefix),
subcommands,
SortBy::LevenshteinDistance,
);
return sort_suggestions(&String::from_utf8_lossy(&prefix), subcommands, options);
}
let config = working_set.get_config();
@ -222,11 +218,7 @@ impl Completer for CommandCompletion {
vec![]
};
sort_suggestions(
&String::from_utf8_lossy(&prefix),
commands,
SortBy::LevenshteinDistance,
)
sort_suggestions(&String::from_utf8_lossy(&prefix), commands, options)
}
}

View File

@ -48,6 +48,7 @@ impl NuCompleter {
let options = CompletionOptions {
case_sensitive: config.case_sensitive_completions,
match_algorithm: config.completion_algorithm.into(),
sort: config.completion_sort,
..Default::default()
};

View File

@ -2,17 +2,18 @@ use crate::{
completions::{matches, CompletionOptions},
SemanticSuggestion,
};
use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
use nu_ansi_term::Style;
use nu_engine::env_to_string;
use nu_path::{expand_to_real_path, home_dir};
use nu_protocol::{
engine::{EngineState, Stack, StateWorkingSet},
levenshtein_distance, Span,
CompletionSort, Span,
};
use nu_utils::get_ls_colors;
use std::path::{is_separator, Component, Path, PathBuf, MAIN_SEPARATOR as SEP};
use super::{MatchAlgorithm, SortBy};
use super::MatchAlgorithm;
#[derive(Clone, Default)]
pub struct PathBuiltFromString {
@ -71,7 +72,7 @@ pub fn complete_rec(
}
let prefix = partial.first().unwrap_or(&"");
let sorted_entries = sort_completions(prefix, entries, SortBy::Ascending, |(entry, _)| entry);
let sorted_entries = sort_completions(prefix, entries, options, |(entry, _)| entry);
for (entry_name, built) in sorted_entries {
match partial.split_first() {
@ -305,33 +306,37 @@ pub fn adjust_if_intermediate(
pub fn sort_suggestions(
prefix: &str,
items: Vec<SemanticSuggestion>,
sort_by: SortBy,
options: &CompletionOptions,
) -> Vec<SemanticSuggestion> {
sort_completions(prefix, items, sort_by, |it| &it.suggestion.value)
sort_completions(prefix, items, options, |it| &it.suggestion.value)
}
/// # Arguments
/// * `prefix` - What the user's typed, for sorting by Levenshtein distance
/// * `prefix` - What the user's typed, for sorting by fuzzy matcher score
pub fn sort_completions<T>(
prefix: &str,
mut items: Vec<T>,
sort_by: SortBy,
options: &CompletionOptions,
get_value: fn(&T) -> &str,
) -> Vec<T> {
// Sort items
match sort_by {
SortBy::LevenshteinDistance => {
items.sort_by(|a, b| {
let a_distance = levenshtein_distance(prefix, get_value(a));
let b_distance = levenshtein_distance(prefix, get_value(b));
a_distance.cmp(&b_distance)
});
}
SortBy::Ascending => {
items.sort_by(|a, b| get_value(a).cmp(get_value(b)));
}
SortBy::None => {}
};
if options.sort == CompletionSort::Smart && options.match_algorithm == MatchAlgorithm::Fuzzy {
let mut matcher = SkimMatcherV2::default();
if options.case_sensitive {
matcher = matcher.respect_case();
} else {
matcher = matcher.ignore_case();
};
items.sort_by(|a, b| {
let a_str = get_value(a);
let b_str = get_value(b);
let a_score = matcher.fuzzy_match(a_str, prefix).unwrap_or_default();
let b_score = matcher.fuzzy_match(b_str, prefix).unwrap_or_default();
b_score.cmp(&a_score).then(a_str.cmp(b_str))
});
} else {
items.sort_by(|a, b| get_value(a).cmp(get_value(b)));
}
items
}

View File

@ -1,17 +1,10 @@
use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
use nu_parser::trim_quotes_str;
use nu_protocol::CompletionAlgorithm;
use nu_protocol::{CompletionAlgorithm, CompletionSort};
use std::fmt::Display;
#[derive(Copy, Clone)]
pub enum SortBy {
LevenshteinDistance,
Ascending,
None,
}
/// Describes how suggestions should be matched.
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum MatchAlgorithm {
/// Only show suggestions which begin with the given input
///
@ -96,6 +89,7 @@ pub struct CompletionOptions {
pub case_sensitive: bool,
pub positional: bool,
pub match_algorithm: MatchAlgorithm,
pub sort: CompletionSort,
}
impl Default for CompletionOptions {
@ -104,6 +98,7 @@ impl Default for CompletionOptions {
case_sensitive: true,
positional: true,
match_algorithm: MatchAlgorithm::Prefix,
sort: Default::default(),
}
}
}

View File

@ -1,13 +1,13 @@
use crate::completions::{
completer::map_value_completions, Completer, CompletionOptions, MatchAlgorithm,
SemanticSuggestion, SortBy,
SemanticSuggestion,
};
use nu_engine::eval_call;
use nu_protocol::{
ast::{Argument, Call, Expr, Expression},
debugger::WithoutDebug,
engine::{Stack, StateWorkingSet},
PipelineData, Span, Type, Value,
CompletionSort, PipelineData, Span, Type, Value,
};
use nu_utils::IgnoreCaseExt;
use std::collections::HashMap;
@ -18,7 +18,6 @@ pub struct CustomCompletion {
stack: Stack,
decl_id: usize,
line: String,
sort_by: SortBy,
}
impl CustomCompletion {
@ -27,7 +26,6 @@ impl CustomCompletion {
stack,
decl_id,
line,
sort_by: SortBy::None,
}
}
}
@ -93,10 +91,6 @@ impl Completer for CustomCompletion {
.and_then(|val| val.as_bool().ok())
.unwrap_or(false);
if should_sort {
self.sort_by = SortBy::Ascending;
}
custom_completion_options = Some(CompletionOptions {
case_sensitive: options
.get("case_sensitive")
@ -114,6 +108,11 @@ impl Completer for CustomCompletion {
.unwrap_or(MatchAlgorithm::Prefix),
None => completion_options.match_algorithm,
},
sort: if should_sort {
CompletionSort::Alphabetical
} else {
CompletionSort::Smart
},
});
}
@ -124,12 +123,11 @@ impl Completer for CustomCompletion {
})
.unwrap_or_default();
let suggestions = if let Some(custom_completion_options) = custom_completion_options {
filter(&prefix, suggestions, &custom_completion_options)
} else {
filter(&prefix, suggestions, completion_options)
};
sort_suggestions(&String::from_utf8_lossy(&prefix), suggestions, self.sort_by)
let options = custom_completion_options
.as_ref()
.unwrap_or(completion_options);
let suggestions = filter(&prefix, suggestions, completion_options);
sort_suggestions(&String::from_utf8_lossy(&prefix), suggestions, options)
}
}

View File

@ -6,7 +6,7 @@ use nu_protocol::{
use reedline::Suggestion;
use std::path::{is_separator, Path, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR};
use super::{completion_common::sort_suggestions, SemanticSuggestion, SortBy};
use super::{completion_common::sort_suggestions, SemanticSuggestion};
#[derive(Clone, Default)]
pub struct DotNuCompletion {}
@ -130,6 +130,6 @@ impl Completer for DotNuCompletion {
})
.collect();
sort_suggestions(&prefix_str, output, SortBy::Ascending)
sort_suggestions(&prefix_str, output, options)
}
}

View File

@ -1,6 +1,4 @@
use crate::completions::{
completion_common::sort_suggestions, Completer, CompletionOptions, SortBy,
};
use crate::completions::{completion_common::sort_suggestions, Completer, CompletionOptions};
use nu_protocol::{
ast::{Expr, Expression},
engine::{Stack, StateWorkingSet},
@ -90,7 +88,7 @@ impl Completer for FlagCompletion {
}
}
return sort_suggestions(&String::from_utf8_lossy(&prefix), output, SortBy::Ascending);
return sort_suggestions(&String::from_utf8_lossy(&prefix), output, options);
}
vec![]

View File

@ -13,7 +13,7 @@ mod variable_completions;
pub use base::{Completer, SemanticSuggestion, SuggestionKind};
pub use command_completions::CommandCompletion;
pub use completer::NuCompleter;
pub use completion_options::{CompletionOptions, MatchAlgorithm, SortBy};
pub use completion_options::{CompletionOptions, MatchAlgorithm};
pub use custom_completions::CustomCompletion;
pub use directory_completions::DirectoryCompletion;
pub use dotnu_completions::DotNuCompletion;

View File

@ -9,7 +9,7 @@ use nu_protocol::{
use reedline::Suggestion;
use std::str;
use super::{completion_common::sort_suggestions, SortBy};
use super::completion_common::sort_suggestions;
#[derive(Clone)]
pub struct VariableCompletion {
@ -72,7 +72,7 @@ impl Completer for VariableCompletion {
}
}
return sort_suggestions(&prefix_str, output, SortBy::Ascending);
return sort_suggestions(&prefix_str, output, options);
}
} else {
// No nesting provided, return all env vars
@ -93,7 +93,7 @@ impl Completer for VariableCompletion {
}
}
return sort_suggestions(&prefix_str, output, SortBy::Ascending);
return sort_suggestions(&prefix_str, output, options);
}
}
@ -117,7 +117,7 @@ impl Completer for VariableCompletion {
}
}
return sort_suggestions(&prefix_str, output, SortBy::Ascending);
return sort_suggestions(&prefix_str, output, options);
}
}
@ -139,7 +139,7 @@ impl Completer for VariableCompletion {
}
}
return sort_suggestions(&prefix_str, output, SortBy::Ascending);
return sort_suggestions(&prefix_str, output, options);
}
}
}
@ -217,7 +217,7 @@ impl Completer for VariableCompletion {
}
}
output = sort_suggestions(&prefix_str, output, SortBy::Ascending);
output = sort_suggestions(&prefix_str, output, options);
output.dedup(); // TODO: Removes only consecutive duplicates, is it intended?