mirror of
https://github.com/nushell/nushell
synced 2024-12-26 13:03:07 +00:00
Add operator completions (#13818)
# Description This PR addresses #13676 . It adds completions for the operators listed on https://www.nushell.sh/lang-guide/chapters/operators.html#operators based on the type of the value before the cursor. Currently, values created as the output of other operations will not have completions. For example `(1 + 3)` will not have completions. When operators are added/removed/updated the completions will have to be adjusted manually. # User-Facing Changes - Tab completions for operators # Tests Added unit tests to the new completion struct OperationCompletion for int completions, float completions, and str completions
This commit is contained in:
parent
199aa2ad3a
commit
baadaee016
3 changed files with 202 additions and 4 deletions
|
@ -1,6 +1,6 @@
|
|||
use crate::completions::{
|
||||
CommandCompletion, Completer, CompletionOptions, CustomCompletion, DirectoryCompletion,
|
||||
DotNuCompletion, FileCompletion, FlagCompletion, VariableCompletion,
|
||||
DotNuCompletion, FileCompletion, FlagCompletion, OperatorCompletion, VariableCompletion,
|
||||
};
|
||||
use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style};
|
||||
use nu_engine::eval_block;
|
||||
|
@ -176,17 +176,32 @@ impl NuCompleter {
|
|||
|
||||
// Variables completion
|
||||
if prefix.starts_with(b"$") || most_left_var.is_some() {
|
||||
let mut completer =
|
||||
let mut variable_names_completer =
|
||||
VariableCompletion::new(most_left_var.unwrap_or((vec![], vec![])));
|
||||
|
||||
return self.process_completion(
|
||||
&mut completer,
|
||||
let mut variable_completions = self.process_completion(
|
||||
&mut variable_names_completer,
|
||||
&working_set,
|
||||
prefix.clone(),
|
||||
new_span,
|
||||
fake_offset,
|
||||
pos,
|
||||
);
|
||||
|
||||
let mut variable_operations_completer =
|
||||
OperatorCompletion::new(pipeline_element.expr.clone());
|
||||
|
||||
let mut variable_operations_completions = self.process_completion(
|
||||
&mut variable_operations_completer,
|
||||
&working_set,
|
||||
prefix,
|
||||
new_span,
|
||||
fake_offset,
|
||||
pos,
|
||||
);
|
||||
|
||||
variable_completions.append(&mut variable_operations_completions);
|
||||
return variable_completions;
|
||||
}
|
||||
|
||||
// Flags completion
|
||||
|
@ -262,6 +277,26 @@ impl NuCompleter {
|
|||
} else if prev_expr_str == b"ls" {
|
||||
let mut completer = FileCompletion::new();
|
||||
|
||||
return self.process_completion(
|
||||
&mut completer,
|
||||
&working_set,
|
||||
prefix,
|
||||
new_span,
|
||||
fake_offset,
|
||||
pos,
|
||||
);
|
||||
} else if matches!(
|
||||
previous_expr.1,
|
||||
FlatShape::Float
|
||||
| FlatShape::Int
|
||||
| FlatShape::String
|
||||
| FlatShape::List
|
||||
| FlatShape::Bool
|
||||
| FlatShape::Variable(_)
|
||||
) {
|
||||
let mut completer =
|
||||
OperatorCompletion::new(pipeline_element.expr.clone());
|
||||
|
||||
return self.process_completion(
|
||||
&mut completer,
|
||||
&working_set,
|
||||
|
@ -533,6 +568,11 @@ mod completer_tests {
|
|||
|
||||
let mut completer = NuCompleter::new(engine_state.into(), Arc::new(Stack::new()));
|
||||
let dataset = [
|
||||
("1 bit-sh", true, "b", vec!["bit-shl", "bit-shr"]),
|
||||
("1.0 bit-sh", false, "b", vec![]),
|
||||
("1 m", true, "m", vec!["mod"]),
|
||||
("1.0 m", true, "m", vec!["mod"]),
|
||||
("\"a\" s", true, "s", vec!["starts-with"]),
|
||||
("sudo", false, "", Vec::new()),
|
||||
("sudo l", true, "l", vec!["ls", "let", "lines", "loop"]),
|
||||
(" sudo", false, "", Vec::new()),
|
||||
|
|
|
@ -8,6 +8,7 @@ mod directory_completions;
|
|||
mod dotnu_completions;
|
||||
mod file_completions;
|
||||
mod flag_completions;
|
||||
mod operator_completions;
|
||||
mod variable_completions;
|
||||
|
||||
pub use base::{Completer, SemanticSuggestion, SuggestionKind};
|
||||
|
@ -19,4 +20,5 @@ pub use directory_completions::DirectoryCompletion;
|
|||
pub use dotnu_completions::DotNuCompletion;
|
||||
pub use file_completions::{file_path_completion, matches, FileCompletion};
|
||||
pub use flag_completions::FlagCompletion;
|
||||
pub use operator_completions::OperatorCompletion;
|
||||
pub use variable_completions::VariableCompletion;
|
||||
|
|
156
crates/nu-cli/src/completions/operator_completions.rs
Normal file
156
crates/nu-cli/src/completions/operator_completions.rs
Normal file
|
@ -0,0 +1,156 @@
|
|||
use crate::completions::{
|
||||
Completer, CompletionOptions, MatchAlgorithm, SemanticSuggestion, SuggestionKind,
|
||||
};
|
||||
use nu_protocol::{
|
||||
ast::{Expr, Expression},
|
||||
engine::{Stack, StateWorkingSet},
|
||||
Span, Type,
|
||||
};
|
||||
use reedline::Suggestion;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OperatorCompletion {
|
||||
previous_expr: Expression,
|
||||
}
|
||||
|
||||
impl OperatorCompletion {
|
||||
pub fn new(previous_expr: Expression) -> Self {
|
||||
OperatorCompletion { previous_expr }
|
||||
}
|
||||
}
|
||||
|
||||
impl Completer for OperatorCompletion {
|
||||
fn fetch(
|
||||
&mut self,
|
||||
working_set: &StateWorkingSet,
|
||||
_stack: &Stack,
|
||||
_prefix: Vec<u8>,
|
||||
span: Span,
|
||||
offset: usize,
|
||||
_pos: usize,
|
||||
_options: &CompletionOptions,
|
||||
) -> Vec<SemanticSuggestion> {
|
||||
//Check if int, float, or string
|
||||
let partial = std::str::from_utf8(working_set.get_span_contents(span)).unwrap_or("");
|
||||
let op = match &self.previous_expr.expr {
|
||||
Expr::BinaryOp(x, _, _) => &x.expr,
|
||||
_ => {
|
||||
return vec![];
|
||||
}
|
||||
};
|
||||
let possible_operations = match op {
|
||||
Expr::Int(_) => vec![
|
||||
("+", "Plus / Addition"),
|
||||
("-", "Minus / Subtraction"),
|
||||
("*", "Multiply"),
|
||||
("/", "Divide"),
|
||||
("==", "Equal"),
|
||||
("!=", "Not Equal"),
|
||||
("//", "Floor Division"),
|
||||
("<", "Less Than"),
|
||||
(">", "Greater Than"),
|
||||
("<=", "Less Than or Equal to"),
|
||||
(">=", "Greater Than or Equal to"),
|
||||
("mod", "Modulo"),
|
||||
("**", "Pow"),
|
||||
("bit-or", "bitwise or"),
|
||||
("bit-xor", "bitwise exclusive or"),
|
||||
("bit-and", "bitwise and"),
|
||||
("bit-shl", "bitwise shift left"),
|
||||
("bit-shr", "bitwise shift right"),
|
||||
],
|
||||
Expr::String(_) => vec![
|
||||
("=~", "Regex Match / Contains"),
|
||||
("!~", "Not Regex Match / Not Contains"),
|
||||
("in", "In / Contains (doesn't use regex)"),
|
||||
(
|
||||
"++",
|
||||
"Appends two lists, a list and a value, two strings, or two binary values",
|
||||
),
|
||||
("not-in", "Not In / Not Contains (doesn't use regex"),
|
||||
("starts-with", "Starts With"),
|
||||
("ends-with", "Ends With"),
|
||||
],
|
||||
Expr::Float(_) => vec![
|
||||
("+", "Plus / Addition"),
|
||||
("-", "Minus / Subtraction"),
|
||||
("*", "Multiply"),
|
||||
("/", "Divide"),
|
||||
("==", "Equal"),
|
||||
("!=", "Not Equal"),
|
||||
("//", "Floor Division"),
|
||||
("<", "Less Than"),
|
||||
(">", "Greater Than"),
|
||||
("<=", "Less Than or Equal to"),
|
||||
(">=", "Greater Than or Equal to"),
|
||||
("mod", "Modulo"),
|
||||
("**", "Pow"),
|
||||
],
|
||||
Expr::Bool(_) => vec![
|
||||
("and", "Checks if both values are true."),
|
||||
("or", "Checks if either value is true."),
|
||||
("xor", "Checks if one value is true and the other is false."),
|
||||
("not", "Negates a value or expression."),
|
||||
],
|
||||
Expr::FullCellPath(path) => match path.head.expr {
|
||||
Expr::List(_) => vec![(
|
||||
"++",
|
||||
"Appends two lists, a list and a value, two strings, or two binary values",
|
||||
)],
|
||||
Expr::Var(id) => get_variable_completions(id, working_set),
|
||||
_ => vec![],
|
||||
},
|
||||
_ => vec![],
|
||||
};
|
||||
|
||||
let match_algorithm = MatchAlgorithm::Prefix;
|
||||
let input_fuzzy_search =
|
||||
|(operator, _): &(&str, &str)| match_algorithm.matches_str(operator, partial);
|
||||
|
||||
possible_operations
|
||||
.into_iter()
|
||||
.filter(input_fuzzy_search)
|
||||
.map(move |x| SemanticSuggestion {
|
||||
suggestion: Suggestion {
|
||||
value: x.0.to_string(),
|
||||
description: Some(x.1.to_string()),
|
||||
span: reedline::Span::new(span.start - offset, span.end - offset),
|
||||
append_whitespace: true,
|
||||
..Suggestion::default()
|
||||
},
|
||||
kind: Some(SuggestionKind::Command(
|
||||
nu_protocol::engine::CommandType::Builtin,
|
||||
)),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_variable_completions<'a>(
|
||||
id: nu_protocol::Id<nu_protocol::marker::Var>,
|
||||
working_set: &StateWorkingSet,
|
||||
) -> Vec<(&'a str, &'a str)> {
|
||||
let var = working_set.get_variable(id);
|
||||
if !var.mutable {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
match var.ty {
|
||||
Type::List(_) | Type::String | Type::Binary => vec![
|
||||
(
|
||||
"++=",
|
||||
"Appends a list, a value, a string, or a binary value to a variable.",
|
||||
),
|
||||
("=", "Assigns a value to a variable."),
|
||||
],
|
||||
|
||||
Type::Int | Type::Float => vec![
|
||||
("=", "Assigns a value to a variable."),
|
||||
("+=", "Adds a value to a variable."),
|
||||
("-=", "Subtracts a value from a variable."),
|
||||
("*=", "Multiplies a variable by a value"),
|
||||
("/=", "Divides a variable by a value."),
|
||||
],
|
||||
_ => vec![],
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue