mirror of
https://github.com/nushell/nushell.git
synced 2025-04-09 21:28:55 +02: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
@ -1,6 +1,6 @@
|
|||||||
use crate::completions::{
|
use crate::completions::{
|
||||||
CommandCompletion, Completer, CompletionOptions, CustomCompletion, DirectoryCompletion,
|
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_color_config::{color_record_to_nustyle, lookup_ansi_color_style};
|
||||||
use nu_engine::eval_block;
|
use nu_engine::eval_block;
|
||||||
@ -176,17 +176,32 @@ impl NuCompleter {
|
|||||||
|
|
||||||
// Variables completion
|
// Variables completion
|
||||||
if prefix.starts_with(b"$") || most_left_var.is_some() {
|
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![])));
|
VariableCompletion::new(most_left_var.unwrap_or((vec![], vec![])));
|
||||||
|
|
||||||
return self.process_completion(
|
let mut variable_completions = self.process_completion(
|
||||||
&mut completer,
|
&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,
|
&working_set,
|
||||||
prefix,
|
prefix,
|
||||||
new_span,
|
new_span,
|
||||||
fake_offset,
|
fake_offset,
|
||||||
pos,
|
pos,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
variable_completions.append(&mut variable_operations_completions);
|
||||||
|
return variable_completions;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flags completion
|
// Flags completion
|
||||||
@ -262,6 +277,26 @@ impl NuCompleter {
|
|||||||
} else if prev_expr_str == b"ls" {
|
} else if prev_expr_str == b"ls" {
|
||||||
let mut completer = FileCompletion::new();
|
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(
|
return self.process_completion(
|
||||||
&mut completer,
|
&mut completer,
|
||||||
&working_set,
|
&working_set,
|
||||||
@ -533,6 +568,11 @@ mod completer_tests {
|
|||||||
|
|
||||||
let mut completer = NuCompleter::new(engine_state.into(), Arc::new(Stack::new()));
|
let mut completer = NuCompleter::new(engine_state.into(), Arc::new(Stack::new()));
|
||||||
let dataset = [
|
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", false, "", Vec::new()),
|
||||||
("sudo l", true, "l", vec!["ls", "let", "lines", "loop"]),
|
("sudo l", true, "l", vec!["ls", "let", "lines", "loop"]),
|
||||||
(" sudo", false, "", Vec::new()),
|
(" sudo", false, "", Vec::new()),
|
||||||
|
@ -8,6 +8,7 @@ mod directory_completions;
|
|||||||
mod dotnu_completions;
|
mod dotnu_completions;
|
||||||
mod file_completions;
|
mod file_completions;
|
||||||
mod flag_completions;
|
mod flag_completions;
|
||||||
|
mod operator_completions;
|
||||||
mod variable_completions;
|
mod variable_completions;
|
||||||
|
|
||||||
pub use base::{Completer, SemanticSuggestion, SuggestionKind};
|
pub use base::{Completer, SemanticSuggestion, SuggestionKind};
|
||||||
@ -19,4 +20,5 @@ pub use directory_completions::DirectoryCompletion;
|
|||||||
pub use dotnu_completions::DotNuCompletion;
|
pub use dotnu_completions::DotNuCompletion;
|
||||||
pub use file_completions::{file_path_completion, matches, FileCompletion};
|
pub use file_completions::{file_path_completion, matches, FileCompletion};
|
||||||
pub use flag_completions::FlagCompletion;
|
pub use flag_completions::FlagCompletion;
|
||||||
|
pub use operator_completions::OperatorCompletion;
|
||||||
pub use variable_completions::VariableCompletion;
|
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 New Issue
Block a user