mirror of
https://github.com/nushell/nushell.git
synced 2025-01-24 23:29:52 +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.
This commit is contained in:
parent
dad956b2ee
commit
787f292ca7
@ -66,7 +66,7 @@ impl Completer for CustomCompletion {
|
||||
PipelineData::empty(),
|
||||
);
|
||||
|
||||
let mut custom_completion_options = None;
|
||||
let mut completion_options = completion_options.clone();
|
||||
let mut should_sort = true;
|
||||
|
||||
// Parse result
|
||||
@ -89,25 +89,24 @@ impl Completer for CustomCompletion {
|
||||
should_sort = sort;
|
||||
}
|
||||
|
||||
custom_completion_options = Some(CompletionOptions {
|
||||
case_sensitive: options
|
||||
.get("case_sensitive")
|
||||
.and_then(|val| val.as_bool().ok())
|
||||
.unwrap_or(true),
|
||||
positional: options
|
||||
.get("positional")
|
||||
.and_then(|val| val.as_bool().ok())
|
||||
.unwrap_or(completion_options.positional),
|
||||
match_algorithm: match options.get("completion_algorithm") {
|
||||
Some(option) => option
|
||||
.coerce_string()
|
||||
.ok()
|
||||
.and_then(|option| option.try_into().ok())
|
||||
.unwrap_or(completion_options.match_algorithm),
|
||||
None => completion_options.match_algorithm,
|
||||
},
|
||||
sort: completion_options.sort,
|
||||
});
|
||||
if let Some(case_sensitive) = options
|
||||
.get("case_sensitive")
|
||||
.and_then(|val| val.as_bool().ok())
|
||||
{
|
||||
completion_options.case_sensitive = case_sensitive;
|
||||
}
|
||||
if let Some(positional) =
|
||||
options.get("positional").and_then(|val| val.as_bool().ok())
|
||||
{
|
||||
completion_options.positional = positional;
|
||||
}
|
||||
if let Some(algorithm) = options
|
||||
.get("completion_algorithm")
|
||||
.and_then(|option| option.coerce_string().ok())
|
||||
.and_then(|option| option.try_into().ok())
|
||||
{
|
||||
completion_options.match_algorithm = algorithm;
|
||||
}
|
||||
}
|
||||
|
||||
completions
|
||||
@ -117,8 +116,7 @@ impl Completer for CustomCompletion {
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let options = custom_completion_options.unwrap_or(completion_options.clone());
|
||||
let mut matcher = NuMatcher::new(String::from_utf8_lossy(prefix), options);
|
||||
let mut matcher = NuMatcher::new(String::from_utf8_lossy(prefix), completion_options);
|
||||
|
||||
if should_sort {
|
||||
for sugg in suggestions {
|
||||
|
@ -62,50 +62,29 @@ fn extern_completer() -> NuCompleter {
|
||||
NuCompleter::new(Arc::new(engine), Arc::new(stack))
|
||||
}
|
||||
|
||||
#[fixture]
|
||||
fn completer_strings_with_options() -> NuCompleter {
|
||||
// Create a new engine
|
||||
fn custom_completer_with_options(
|
||||
global_opts: &str,
|
||||
completer_opts: &str,
|
||||
completions: &[&str],
|
||||
) -> NuCompleter {
|
||||
let (_, _, mut engine, mut stack) = new_engine();
|
||||
// Add record value as example
|
||||
let record = r#"
|
||||
# To test that the config setting has no effect on the custom completions
|
||||
$env.config.completions.algorithm = "fuzzy"
|
||||
def animals [] {
|
||||
{
|
||||
# Very rare and totally real animals
|
||||
completions: ["Abcdef", "Foo Abcdef", "Acd Bar" ],
|
||||
options: {
|
||||
completion_algorithm: "prefix",
|
||||
positional: false,
|
||||
case_sensitive: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
def my-command [animal: string@animals] { print $animal }"#;
|
||||
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack).is_ok());
|
||||
|
||||
// Instantiate a new completer
|
||||
NuCompleter::new(Arc::new(engine), Arc::new(stack))
|
||||
}
|
||||
|
||||
#[fixture]
|
||||
fn completer_strings_no_sort() -> NuCompleter {
|
||||
// Create a new engine
|
||||
let (_, _, mut engine, mut stack) = new_engine();
|
||||
let command = r#"
|
||||
def animals [] {
|
||||
{
|
||||
completions: ["zzzfoo", "foo", "not matched", "abcfoo" ],
|
||||
options: {
|
||||
completion_algorithm: "fuzzy",
|
||||
sort: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
def my-command [animal: string@animals] { print $animal }"#;
|
||||
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,
|
||||
);
|
||||
assert!(support::merge_input(command.as_bytes(), &mut engine, &mut stack).is_ok());
|
||||
|
||||
// Instantiate a new completer
|
||||
NuCompleter::new(Arc::new(engine), Arc::new(stack))
|
||||
}
|
||||
|
||||
@ -217,23 +196,59 @@ fn variables_customcompletion_subcommands_with_customcompletion_2(
|
||||
match_suggestions(&expected, &suggestions);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn customcompletions_substring_matching(mut completer_strings_with_options: NuCompleter) {
|
||||
let suggestions = completer_strings_with_options.complete("my-command Abcd", 15);
|
||||
/// $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
|
||||
let expected: Vec<String> = vec!["Abcdef".into(), "Foo Abcdef".into()];
|
||||
let suggestions = completer.complete("my-command Abcd", 15);
|
||||
match_suggestions(&expected, &suggestions);
|
||||
|
||||
// Custom options should make case-sensitive
|
||||
let suggestions = completer.complete("my-command aBcD", 15);
|
||||
assert!(suggestions.is_empty());
|
||||
}
|
||||
|
||||
/// $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);
|
||||
let expected: Vec<String> = vec!["Acd Bar".into(), "Abcdef".into(), "Foo Abcdef".into()];
|
||||
match_suggestions(&expected, &suggestions);
|
||||
|
||||
// Custom options should make matching case insensitive
|
||||
let suggestions = completer.complete("my-command acd", 14);
|
||||
match_suggestions(&expected, &suggestions);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn customcompletions_case_insensitive(mut completer_strings_with_options: NuCompleter) {
|
||||
let suggestions = completer_strings_with_options.complete("my-command foo", 14);
|
||||
let expected: Vec<String> = vec!["Foo Abcdef".into()];
|
||||
match_suggestions(&expected, &suggestions);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn customcompletions_no_sort(mut completer_strings_no_sort: NuCompleter) {
|
||||
let suggestions = completer_strings_no_sort.complete("my-command foo", 14);
|
||||
#[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);
|
||||
let expected: Vec<String> = vec!["zzzfoo".into(), "foo".into(), "abcfoo".into()];
|
||||
match_suggestions(&expected, &suggestions);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user