Add completion options for custom completions (#4674)

* Add completion options for custom completions

* Make clippy happy

* Refactor options for clarity

* Make return type of filtering explicit
This commit is contained in:
Genna Wingert 2022-03-03 15:45:35 +01:00 committed by GitHub
parent 210d25f2a0
commit 13f2048ffb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -9,6 +9,22 @@ use reedline::Completer;
const SEP: char = std::path::MAIN_SEPARATOR;
pub struct CompletionOptions {
case_sensitive: bool,
positional: bool,
sort: bool,
}
impl Default for CompletionOptions {
fn default() -> Self {
Self {
case_sensitive: true,
positional: true,
sort: true,
}
}
}
#[derive(Clone)]
pub struct NuCompleter {
engine_state: EngineState,
@ -192,12 +208,10 @@ impl NuCompleter {
prefix.remove(pos - flat.0.start);
if prefix.starts_with(b"$") {
return self.complete_variables(
&working_set,
&prefix,
new_span,
offset,
);
let mut output =
self.complete_variables(&working_set, &prefix, new_span, offset);
output.sort_by(|a, b| a.1.cmp(&b.1));
return output;
}
if prefix.starts_with(b"-") {
// this might be a flag, let's see
@ -240,6 +254,7 @@ impl NuCompleter {
));
}
}
output.sort_by(|a, b| a.1.cmp(&b.1));
return output;
}
}
@ -279,10 +294,12 @@ impl NuCompleter {
true,
);
let v: Vec<_> = match result {
Ok(pd) => pd
.into_iter()
.filter_map(move |x| {
fn map_completions<'a>(
list: impl Iterator<Item = &'a Value>,
new_span: Span,
offset: usize,
) -> Vec<(reedline::Span, String)> {
list.filter_map(move |x| {
let s = x.as_string();
match s {
@ -296,12 +313,91 @@ impl NuCompleter {
Err(_) => None,
}
})
.filter(|x| x.1.as_bytes().starts_with(&prefix))
.collect(),
_ => vec![],
.collect()
}
let (completions, options) = match result {
Ok(pd) => {
let value = pd.into_value(new_span);
match &value {
Value::Record { .. } => {
let completions = value
.get_data_by_key("completions")
.and_then(|val| {
val.as_list().ok().map(|it| {
map_completions(
it.iter(),
new_span,
offset,
)
})
})
.unwrap_or_default();
let options = value.get_data_by_key("options");
let options =
if let Some(Value::Record { .. }) = &options {
let options = options.unwrap_or_default();
CompletionOptions {
case_sensitive: options
.get_data_by_key("case_sensitive")
.and_then(|val| val.as_bool().ok())
.unwrap_or(true),
positional: options
.get_data_by_key("positional")
.and_then(|val| val.as_bool().ok())
.unwrap_or(true),
sort: options
.get_data_by_key("sort")
.and_then(|val| val.as_bool().ok())
.unwrap_or(true),
}
} else {
CompletionOptions::default()
};
return v;
(completions, options)
}
Value::List { vals, .. } => {
let completions =
map_completions(vals.iter(), new_span, offset);
(completions, CompletionOptions::default())
}
_ => (vec![], CompletionOptions::default()),
}
}
_ => (vec![], CompletionOptions::default()),
};
let mut completions: Vec<(reedline::Span, String)> = completions
.into_iter()
.filter(|it| {
// Minimise clones for new functionality
match (options.case_sensitive, options.positional) {
(true, true) => it.1.as_bytes().starts_with(&prefix),
(true, false) => it.1.contains(
std::str::from_utf8(&prefix).unwrap_or(""),
),
(false, positional) => {
let value = it.1.to_lowercase();
let prefix = std::str::from_utf8(&prefix)
.unwrap_or("")
.to_lowercase();
if positional {
value.starts_with(&prefix)
} else {
value.contains(&prefix)
}
}
}
})
.collect();
if options.sort {
completions.sort_by(|a, b| a.1.cmp(&b.1));
}
return completions;
}
FlatShape::Filepath | FlatShape::GlobPattern => {
let cwd = if let Some(d) = self.engine_state.env_vars.get("PWD") {
@ -313,7 +409,8 @@ impl NuCompleter {
"".to_string()
};
let prefix = String::from_utf8_lossy(&prefix).to_string();
return file_path_completion(new_span, &prefix, &cwd)
let mut output: Vec<_> =
file_path_completion(new_span, &prefix, &cwd)
.into_iter()
.map(move |x| {
(
@ -325,6 +422,8 @@ impl NuCompleter {
)
})
.collect();
output.sort_by(|a, b| a.1.cmp(&b.1));
return output;
}
flat_shape => {
let last = flattened
@ -394,7 +493,7 @@ impl NuCompleter {
};
// let prefix = working_set.get_span_contents(flat.0);
let prefix = String::from_utf8_lossy(&prefix).to_string();
let output = file_path_completion(new_span, &prefix, &cwd)
let mut output = file_path_completion(new_span, &prefix, &cwd)
.into_iter()
.map(move |x| {
if flat_idx == 0 {
@ -437,6 +536,7 @@ impl NuCompleter {
.chain(commands.into_iter())
.collect::<Vec<_>>();
//output.dedup_by(|a, b| a.1 == b.1);
output.sort_by(|a, b| a.1.cmp(&b.1));
return output;
}
@ -452,11 +552,7 @@ impl NuCompleter {
impl Completer for NuCompleter {
fn complete(&self, line: &str, pos: usize) -> Vec<(reedline::Span, String)> {
let mut output = self.completion_helper(line, pos);
output.sort_by(|a, b| a.1.cmp(&b.1));
output
self.completion_helper(line, pos)
}
}