mirror of
https://github.com/nushell/nushell.git
synced 2025-01-25 23:58:41 +01:00
Feature/refactor completion options (#5228)
* Copy completion filter to custom completions * Remove filter function from completer This function was a no-op for FileCompletion and CommandCompletion. Flag- and VariableCompletion just filters with `starts_with` which happens in both completers anyway and should therefore also be a no-op. The remaining use case in CustomCompletion was moved into the CustomCompletion source file. Filtering should probably happen immediately while fetching completions to avoid unnecessary memory allocations. * Add get_sort_by() to Completer trait * Remove CompletionOptions from Completer::fetch() * Fix clippy lints * Apply Completer changes to DotNuCompletion
This commit is contained in:
parent
ae674bfaec
commit
0de289f6b7
@ -1,4 +1,4 @@
|
|||||||
use crate::completions::{CompletionOptions, SortBy};
|
use crate::completions::SortBy;
|
||||||
use nu_protocol::{engine::StateWorkingSet, levenshtein_distance, Span};
|
use nu_protocol::{engine::StateWorkingSet, levenshtein_distance, Span};
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
|
|
||||||
@ -12,50 +12,18 @@ pub trait Completer {
|
|||||||
span: Span,
|
span: Span,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
pos: usize,
|
pos: usize,
|
||||||
) -> (Vec<Suggestion>, CompletionOptions);
|
) -> Vec<Suggestion>;
|
||||||
|
|
||||||
// Filter results using the completion options
|
fn get_sort_by(&self) -> SortBy {
|
||||||
fn filter(
|
SortBy::Ascending
|
||||||
&self,
|
|
||||||
prefix: Vec<u8>,
|
|
||||||
items: Vec<Suggestion>,
|
|
||||||
options: CompletionOptions,
|
|
||||||
) -> Vec<Suggestion> {
|
|
||||||
items
|
|
||||||
.into_iter()
|
|
||||||
.filter(|it| {
|
|
||||||
// Minimise clones for new functionality
|
|
||||||
match (options.case_sensitive, options.positional) {
|
|
||||||
(true, true) => it.value.as_bytes().starts_with(&prefix),
|
|
||||||
(true, false) => it
|
|
||||||
.value
|
|
||||||
.contains(std::str::from_utf8(&prefix).unwrap_or("")),
|
|
||||||
(false, positional) => {
|
|
||||||
let value = it.value.to_lowercase();
|
|
||||||
let prefix = std::str::from_utf8(&prefix).unwrap_or("").to_lowercase();
|
|
||||||
if positional {
|
|
||||||
value.starts_with(&prefix)
|
|
||||||
} else {
|
|
||||||
value.contains(&prefix)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort results using the completion options
|
fn sort(&self, items: Vec<Suggestion>, prefix: Vec<u8>) -> Vec<Suggestion> {
|
||||||
fn sort(
|
|
||||||
&self,
|
|
||||||
items: Vec<Suggestion>,
|
|
||||||
prefix: Vec<u8>,
|
|
||||||
options: CompletionOptions,
|
|
||||||
) -> Vec<Suggestion> {
|
|
||||||
let prefix_str = String::from_utf8_lossy(&prefix).to_string();
|
let prefix_str = String::from_utf8_lossy(&prefix).to_string();
|
||||||
let mut filtered_items = items;
|
let mut filtered_items = items;
|
||||||
|
|
||||||
// Sort items
|
// Sort items
|
||||||
match options.sort_by {
|
match self.get_sort_by() {
|
||||||
SortBy::LevenshteinDistance => {
|
SortBy::LevenshteinDistance => {
|
||||||
filtered_items.sort_by(|a, b| {
|
filtered_items.sort_by(|a, b| {
|
||||||
let a_distance = levenshtein_distance(&prefix_str, &a.value);
|
let a_distance = levenshtein_distance(&prefix_str, &a.value);
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
use crate::completions::{
|
use crate::completions::{file_completions::file_path_completion, Completer, SortBy};
|
||||||
file_completions::file_path_completion, Completer, CompletionOptions, SortBy,
|
|
||||||
};
|
|
||||||
use nu_parser::{trim_quotes, FlatShape};
|
use nu_parser::{trim_quotes, FlatShape};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, StateWorkingSet},
|
engine::{EngineState, StateWorkingSet},
|
||||||
@ -154,7 +152,7 @@ impl Completer for CommandCompletion {
|
|||||||
span: Span,
|
span: Span,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
pos: usize,
|
pos: usize,
|
||||||
) -> (Vec<Suggestion>, CompletionOptions) {
|
) -> Vec<Suggestion> {
|
||||||
let last = self
|
let last = self
|
||||||
.flattened
|
.flattened
|
||||||
.iter()
|
.iter()
|
||||||
@ -172,9 +170,6 @@ impl Completer for CommandCompletion {
|
|||||||
})
|
})
|
||||||
.last();
|
.last();
|
||||||
|
|
||||||
// Options
|
|
||||||
let options = CompletionOptions::new(true, true, SortBy::LevenshteinDistance);
|
|
||||||
|
|
||||||
// The last item here would be the earliest shape that could possible by part of this subcommand
|
// The last item here would be the earliest shape that could possible by part of this subcommand
|
||||||
let subcommands = if let Some(last) = last {
|
let subcommands = if let Some(last) = last {
|
||||||
self.complete_commands(
|
self.complete_commands(
|
||||||
@ -191,7 +186,7 @@ impl Completer for CommandCompletion {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if !subcommands.is_empty() {
|
if !subcommands.is_empty() {
|
||||||
return (subcommands, options);
|
return subcommands;
|
||||||
}
|
}
|
||||||
|
|
||||||
let commands = if matches!(self.flat_shape, nu_parser::FlatShape::External)
|
let commands = if matches!(self.flat_shape, nu_parser::FlatShape::External)
|
||||||
@ -225,7 +220,8 @@ impl Completer for CommandCompletion {
|
|||||||
};
|
};
|
||||||
// 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(span, &prefix, &cwd)
|
|
||||||
|
file_path_completion(span, &prefix, &cwd)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(move |x| {
|
.map(move |x| {
|
||||||
if self.flat_idx == 0 {
|
if self.flat_idx == 0 {
|
||||||
@ -262,13 +258,10 @@ impl Completer for CommandCompletion {
|
|||||||
})
|
})
|
||||||
.chain(subcommands.into_iter())
|
.chain(subcommands.into_iter())
|
||||||
.chain(commands.into_iter())
|
.chain(commands.into_iter())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>()
|
||||||
|
|
||||||
(output, options)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace base filter with no filter once all the results are already based in the current path
|
fn get_sort_by(&self) -> SortBy {
|
||||||
fn filter(&self, _: Vec<u8>, items: Vec<Suggestion>, _: CompletionOptions) -> Vec<Suggestion> {
|
SortBy::LevenshteinDistance
|
||||||
items
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,14 +36,10 @@ impl NuCompleter {
|
|||||||
pos: usize,
|
pos: usize,
|
||||||
) -> Vec<Suggestion> {
|
) -> Vec<Suggestion> {
|
||||||
// Fetch
|
// Fetch
|
||||||
let (mut suggestions, options) =
|
let mut suggestions = completer.fetch(working_set, prefix.clone(), new_span, offset, pos);
|
||||||
completer.fetch(working_set, prefix.clone(), new_span, offset, pos);
|
|
||||||
|
|
||||||
// Filter
|
|
||||||
suggestions = completer.filter(prefix.clone(), suggestions, options.clone());
|
|
||||||
|
|
||||||
// Sort
|
// Sort
|
||||||
suggestions = completer.sort(suggestions, prefix, options);
|
suggestions = completer.sort(suggestions, prefix);
|
||||||
|
|
||||||
suggestions
|
suggestions
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#[derive(Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum SortBy {
|
pub enum SortBy {
|
||||||
LevenshteinDistance,
|
LevenshteinDistance,
|
||||||
Ascending,
|
Ascending,
|
||||||
@ -12,16 +12,6 @@ pub struct CompletionOptions {
|
|||||||
pub sort_by: SortBy,
|
pub sort_by: SortBy,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CompletionOptions {
|
|
||||||
pub fn new(case_sensitive: bool, positional: bool, sort_by: SortBy) -> Self {
|
|
||||||
Self {
|
|
||||||
case_sensitive,
|
|
||||||
positional,
|
|
||||||
sort_by,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for CompletionOptions {
|
impl Default for CompletionOptions {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -13,6 +13,7 @@ pub struct CustomCompletion {
|
|||||||
stack: Stack,
|
stack: Stack,
|
||||||
decl_id: usize,
|
decl_id: usize,
|
||||||
line: String,
|
line: String,
|
||||||
|
sort_by: SortBy,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CustomCompletion {
|
impl CustomCompletion {
|
||||||
@ -22,6 +23,7 @@ impl CustomCompletion {
|
|||||||
stack,
|
stack,
|
||||||
decl_id,
|
decl_id,
|
||||||
line,
|
line,
|
||||||
|
sort_by: SortBy::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,11 +57,11 @@ impl Completer for CustomCompletion {
|
|||||||
fn fetch(
|
fn fetch(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: &StateWorkingSet,
|
_: &StateWorkingSet,
|
||||||
_: Vec<u8>,
|
prefix: Vec<u8>,
|
||||||
span: Span,
|
span: Span,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
pos: usize,
|
pos: usize,
|
||||||
) -> (Vec<Suggestion>, CompletionOptions) {
|
) -> Vec<Suggestion> {
|
||||||
// Line position
|
// Line position
|
||||||
let line_pos = pos - offset;
|
let line_pos = pos - offset;
|
||||||
|
|
||||||
@ -113,6 +115,10 @@ impl Completer for CustomCompletion {
|
|||||||
.and_then(|val| val.as_bool().ok())
|
.and_then(|val| val.as_bool().ok())
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
if should_sort {
|
||||||
|
self.sort_by = SortBy::Ascending;
|
||||||
|
}
|
||||||
|
|
||||||
CompletionOptions {
|
CompletionOptions {
|
||||||
case_sensitive: options
|
case_sensitive: options
|
||||||
.get_data_by_key("case_sensitive")
|
.get_data_by_key("case_sensitive")
|
||||||
@ -144,6 +150,32 @@ impl Completer for CustomCompletion {
|
|||||||
_ => (vec![], CompletionOptions::default()),
|
_ => (vec![], CompletionOptions::default()),
|
||||||
};
|
};
|
||||||
|
|
||||||
(suggestions, options)
|
filter(&prefix, suggestions, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_sort_by(&self) -> SortBy {
|
||||||
|
self.sort_by
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn filter(prefix: &[u8], items: Vec<Suggestion>, options: CompletionOptions) -> Vec<Suggestion> {
|
||||||
|
items
|
||||||
|
.into_iter()
|
||||||
|
.filter(|it| {
|
||||||
|
// Minimise clones for new functionality
|
||||||
|
match (options.case_sensitive, options.positional) {
|
||||||
|
(true, true) => it.value.as_bytes().starts_with(prefix),
|
||||||
|
(true, false) => it.value.contains(std::str::from_utf8(prefix).unwrap_or("")),
|
||||||
|
(false, positional) => {
|
||||||
|
let value = it.value.to_lowercase();
|
||||||
|
let prefix = std::str::from_utf8(prefix).unwrap_or("").to_lowercase();
|
||||||
|
if positional {
|
||||||
|
value.starts_with(&prefix)
|
||||||
|
} else {
|
||||||
|
value.contains(&prefix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
use crate::completions::{
|
use crate::completions::{file_path_completion, partial_from, Completer, SortBy};
|
||||||
file_path_completion, partial_from, Completer, CompletionOptions, SortBy,
|
|
||||||
};
|
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, StateWorkingSet},
|
engine::{EngineState, StateWorkingSet},
|
||||||
Span,
|
Span,
|
||||||
@ -21,11 +19,6 @@ impl DotNuCompletion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Completer for DotNuCompletion {
|
impl Completer for DotNuCompletion {
|
||||||
// Replace base filter with no filter once all the results are already filtered
|
|
||||||
fn filter(&self, _: Vec<u8>, items: Vec<Suggestion>, _: CompletionOptions) -> Vec<Suggestion> {
|
|
||||||
items
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fetch(
|
fn fetch(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: &StateWorkingSet,
|
_: &StateWorkingSet,
|
||||||
@ -33,7 +26,7 @@ impl Completer for DotNuCompletion {
|
|||||||
span: Span,
|
span: Span,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
_: usize,
|
_: usize,
|
||||||
) -> (Vec<Suggestion>, CompletionOptions) {
|
) -> Vec<Suggestion> {
|
||||||
let prefix_str = String::from_utf8_lossy(&prefix).to_string();
|
let prefix_str = String::from_utf8_lossy(&prefix).to_string();
|
||||||
let mut search_dirs: Vec<String> = vec![];
|
let mut search_dirs: Vec<String> = vec![];
|
||||||
let (base_dir, mut partial) = partial_from(&prefix_str);
|
let (base_dir, mut partial) = partial_from(&prefix_str);
|
||||||
@ -118,9 +111,10 @@ impl Completer for DotNuCompletion {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Options
|
output
|
||||||
let options = CompletionOptions::new(false, true, SortBy::LevenshteinDistance);
|
}
|
||||||
|
|
||||||
(output, options)
|
fn get_sort_by(&self) -> SortBy {
|
||||||
|
SortBy::LevenshteinDistance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::completions::{Completer, CompletionOptions};
|
use crate::completions::Completer;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, StateWorkingSet},
|
engine::{EngineState, StateWorkingSet},
|
||||||
levenshtein_distance, Span,
|
levenshtein_distance, Span,
|
||||||
@ -28,7 +28,7 @@ impl Completer for FileCompletion {
|
|||||||
span: Span,
|
span: Span,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
_: usize,
|
_: usize,
|
||||||
) -> (Vec<Suggestion>, CompletionOptions) {
|
) -> Vec<Suggestion> {
|
||||||
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") {
|
||||||
match d.as_string() {
|
match d.as_string() {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
@ -51,19 +51,11 @@ impl Completer for FileCompletion {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Options
|
output
|
||||||
let options = CompletionOptions::default();
|
|
||||||
|
|
||||||
(output, options)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort results prioritizing the non hidden folders
|
// Sort results prioritizing the non hidden folders
|
||||||
fn sort(
|
fn sort(&self, items: Vec<Suggestion>, prefix: Vec<u8>) -> Vec<Suggestion> {
|
||||||
&self,
|
|
||||||
items: Vec<Suggestion>,
|
|
||||||
prefix: Vec<u8>,
|
|
||||||
_: CompletionOptions, // Ignore the given options, once it's a custom sorting
|
|
||||||
) -> Vec<Suggestion> {
|
|
||||||
let prefix_str = String::from_utf8_lossy(&prefix).to_string();
|
let prefix_str = String::from_utf8_lossy(&prefix).to_string();
|
||||||
|
|
||||||
// Sort items
|
// Sort items
|
||||||
@ -97,11 +89,6 @@ impl Completer for FileCompletion {
|
|||||||
|
|
||||||
non_hidden
|
non_hidden
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace base filter with no filter once all the results are already based in the current path
|
|
||||||
fn filter(&self, _: Vec<u8>, items: Vec<Suggestion>, _: CompletionOptions) -> Vec<Suggestion> {
|
|
||||||
items
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn partial_from(input: &str) -> (String, String) {
|
pub fn partial_from(input: &str) -> (String, String) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::completions::{Completer, CompletionOptions};
|
use crate::completions::Completer;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Expr, Expression},
|
ast::{Expr, Expression},
|
||||||
engine::StateWorkingSet,
|
engine::StateWorkingSet,
|
||||||
@ -26,7 +26,7 @@ impl Completer for FlagCompletion {
|
|||||||
span: Span,
|
span: Span,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
_: usize,
|
_: usize,
|
||||||
) -> (Vec<Suggestion>, CompletionOptions) {
|
) -> Vec<Suggestion> {
|
||||||
// Check if it's a flag
|
// Check if it's a flag
|
||||||
if let Expr::Call(call) = &self.expression.expr {
|
if let Expr::Call(call) = &self.expression.expr {
|
||||||
let decl = working_set.get_decl(call.decl_id);
|
let decl = working_set.get_decl(call.decl_id);
|
||||||
@ -73,9 +73,9 @@ impl Completer for FlagCompletion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (output, CompletionOptions::default());
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
(vec![], CompletionOptions::default())
|
vec![]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::completions::{Completer, CompletionOptions};
|
use crate::completions::Completer;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
Span, Value,
|
Span, Value,
|
||||||
@ -35,7 +35,7 @@ impl Completer for VariableCompletion {
|
|||||||
span: Span,
|
span: Span,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
_: usize,
|
_: usize,
|
||||||
) -> (Vec<Suggestion>, CompletionOptions) {
|
) -> Vec<Suggestion> {
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
let builtins = ["$nu", "$in", "$config", "$env", "$nothing"];
|
let builtins = ["$nu", "$in", "$config", "$env", "$nothing"];
|
||||||
let var_str = std::str::from_utf8(&self.var_context.0)
|
let var_str = std::str::from_utf8(&self.var_context.0)
|
||||||
@ -52,6 +52,7 @@ impl Completer for VariableCompletion {
|
|||||||
// Completion for $env.<tab>
|
// Completion for $env.<tab>
|
||||||
if var_str.as_str() == "$env" {
|
if var_str.as_str() == "$env" {
|
||||||
for env_var in self.stack.get_env_vars(&self.engine_state) {
|
for env_var in self.stack.get_env_vars(&self.engine_state) {
|
||||||
|
if env_var.0.as_bytes().starts_with(&prefix) {
|
||||||
output.push(Suggestion {
|
output.push(Suggestion {
|
||||||
value: env_var.0,
|
value: env_var.0,
|
||||||
description: None,
|
description: None,
|
||||||
@ -59,8 +60,9 @@ impl Completer for VariableCompletion {
|
|||||||
span: current_span,
|
span: current_span,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (output, CompletionOptions::default());
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Completion other variable types
|
// Completion other variable types
|
||||||
@ -96,11 +98,11 @@ impl Completer for VariableCompletion {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return (output, CompletionOptions::default());
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
return (output, CompletionOptions::default());
|
return output;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,7 +151,7 @@ impl Completer for VariableCompletion {
|
|||||||
|
|
||||||
output.dedup();
|
output.dedup();
|
||||||
|
|
||||||
(output, CompletionOptions::default())
|
output
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user