forked from extern/nushell
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:
parent
210d25f2a0
commit
13f2048ffb
@ -9,6 +9,22 @@ use reedline::Completer;
|
|||||||
|
|
||||||
const SEP: char = std::path::MAIN_SEPARATOR;
|
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)]
|
#[derive(Clone)]
|
||||||
pub struct NuCompleter {
|
pub struct NuCompleter {
|
||||||
engine_state: EngineState,
|
engine_state: EngineState,
|
||||||
@ -192,12 +208,10 @@ impl NuCompleter {
|
|||||||
prefix.remove(pos - flat.0.start);
|
prefix.remove(pos - flat.0.start);
|
||||||
|
|
||||||
if prefix.starts_with(b"$") {
|
if prefix.starts_with(b"$") {
|
||||||
return self.complete_variables(
|
let mut output =
|
||||||
&working_set,
|
self.complete_variables(&working_set, &prefix, new_span, offset);
|
||||||
&prefix,
|
output.sort_by(|a, b| a.1.cmp(&b.1));
|
||||||
new_span,
|
return output;
|
||||||
offset,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if prefix.starts_with(b"-") {
|
if prefix.starts_with(b"-") {
|
||||||
// this might be a flag, let's see
|
// 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;
|
return output;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -279,10 +294,12 @@ impl NuCompleter {
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
let v: Vec<_> = match result {
|
fn map_completions<'a>(
|
||||||
Ok(pd) => pd
|
list: impl Iterator<Item = &'a Value>,
|
||||||
.into_iter()
|
new_span: Span,
|
||||||
.filter_map(move |x| {
|
offset: usize,
|
||||||
|
) -> Vec<(reedline::Span, String)> {
|
||||||
|
list.filter_map(move |x| {
|
||||||
let s = x.as_string();
|
let s = x.as_string();
|
||||||
|
|
||||||
match s {
|
match s {
|
||||||
@ -296,12 +313,91 @@ impl NuCompleter {
|
|||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.filter(|x| x.1.as_bytes().starts_with(&prefix))
|
.collect()
|
||||||
.collect(),
|
}
|
||||||
_ => vec![],
|
|
||||||
|
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 => {
|
FlatShape::Filepath | FlatShape::GlobPattern => {
|
||||||
let cwd = if let Some(d) = self.engine_state.env_vars.get("PWD") {
|
let cwd = if let Some(d) = self.engine_state.env_vars.get("PWD") {
|
||||||
@ -313,7 +409,8 @@ impl NuCompleter {
|
|||||||
"".to_string()
|
"".to_string()
|
||||||
};
|
};
|
||||||
let prefix = String::from_utf8_lossy(&prefix).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()
|
.into_iter()
|
||||||
.map(move |x| {
|
.map(move |x| {
|
||||||
(
|
(
|
||||||
@ -325,6 +422,8 @@ impl NuCompleter {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
output.sort_by(|a, b| a.1.cmp(&b.1));
|
||||||
|
return output;
|
||||||
}
|
}
|
||||||
flat_shape => {
|
flat_shape => {
|
||||||
let last = flattened
|
let last = flattened
|
||||||
@ -394,7 +493,7 @@ impl NuCompleter {
|
|||||||
};
|
};
|
||||||
// let prefix = working_set.get_span_contents(flat.0);
|
// let prefix = working_set.get_span_contents(flat.0);
|
||||||
let prefix = String::from_utf8_lossy(&prefix).to_string();
|
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()
|
.into_iter()
|
||||||
.map(move |x| {
|
.map(move |x| {
|
||||||
if flat_idx == 0 {
|
if flat_idx == 0 {
|
||||||
@ -437,6 +536,7 @@ impl NuCompleter {
|
|||||||
.chain(commands.into_iter())
|
.chain(commands.into_iter())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
//output.dedup_by(|a, b| a.1 == b.1);
|
//output.dedup_by(|a, b| a.1 == b.1);
|
||||||
|
output.sort_by(|a, b| a.1.cmp(&b.1));
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
@ -452,11 +552,7 @@ impl NuCompleter {
|
|||||||
|
|
||||||
impl Completer for NuCompleter {
|
impl Completer for NuCompleter {
|
||||||
fn complete(&self, line: &str, pos: usize) -> Vec<(reedline::Span, String)> {
|
fn complete(&self, line: &str, pos: usize) -> Vec<(reedline::Span, String)> {
|
||||||
let mut output = self.completion_helper(line, pos);
|
self.completion_helper(line, pos)
|
||||||
|
|
||||||
output.sort_by(|a, b| a.1.cmp(&b.1));
|
|
||||||
|
|
||||||
output
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user