mirror of
https://github.com/nushell/nushell
synced 2024-12-27 05:23:11 +00:00
nu-cli/completions: add completion for record vars (#5204)
This commit is contained in:
parent
2a3991cfdb
commit
13b371ab58
2 changed files with 159 additions and 50 deletions
|
@ -72,7 +72,8 @@ impl NuCompleter {
|
|||
if pos >= flat.0.start && pos < flat.0.end {
|
||||
// Context variables
|
||||
let mut is_variable_completion = false;
|
||||
let mut previous_expr: Vec<u8> = vec![];
|
||||
let most_left_var =
|
||||
most_left_variable(flat_idx, &working_set, flattened.clone());
|
||||
|
||||
// Create a new span
|
||||
let new_span = Span {
|
||||
|
@ -84,23 +85,9 @@ impl NuCompleter {
|
|||
let mut prefix = working_set.get_span_contents(flat.0).to_vec();
|
||||
prefix.remove(pos - flat.0.start);
|
||||
|
||||
// Try to get the previous expression
|
||||
if flat_idx > 0 {
|
||||
match flattened.get(flat_idx - 1) {
|
||||
Some(value) => {
|
||||
let previous_prefix =
|
||||
working_set.get_span_contents(value.0).to_vec();
|
||||
|
||||
// Update the previous expression
|
||||
previous_expr = previous_prefix;
|
||||
|
||||
// Check if should match variable completion
|
||||
if matches!(value.1, FlatShape::Variable) {
|
||||
is_variable_completion = true;
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
// Check if has most left variable
|
||||
if most_left_var.is_some() {
|
||||
is_variable_completion = true;
|
||||
}
|
||||
|
||||
// Variables completion
|
||||
|
@ -108,7 +95,7 @@ impl NuCompleter {
|
|||
let mut completer = VariableCompletion::new(
|
||||
self.engine_state.clone(),
|
||||
self.stack.clone(),
|
||||
previous_expr,
|
||||
most_left_var.unwrap_or((vec![], vec![])),
|
||||
);
|
||||
|
||||
return self.process_completion(
|
||||
|
@ -200,3 +187,53 @@ impl ReedlineCompleter for NuCompleter {
|
|||
self.completion_helper(line, pos)
|
||||
}
|
||||
}
|
||||
|
||||
// reads the most left variable returning it's name (e.g: $myvar)
|
||||
// and the depth (a.b.c)
|
||||
fn most_left_variable(
|
||||
idx: usize,
|
||||
working_set: &StateWorkingSet<'_>,
|
||||
flattened: Vec<(Span, FlatShape)>,
|
||||
) -> Option<(Vec<u8>, Vec<Vec<u8>>)> {
|
||||
// Reverse items to read the list backwards and truncate
|
||||
// because the only items that matters are the ones before the current index
|
||||
let mut rev = flattened;
|
||||
rev.truncate(idx);
|
||||
rev = rev.into_iter().rev().collect();
|
||||
|
||||
// Store the variables and sub levels found and reverse to correct order
|
||||
let mut variables_found: Vec<Vec<u8>> = vec![];
|
||||
let mut found_var = false;
|
||||
for item in rev.clone() {
|
||||
let result = working_set.get_span_contents(item.0).to_vec();
|
||||
|
||||
match item.1 {
|
||||
FlatShape::Variable => {
|
||||
variables_found.push(result);
|
||||
found_var = true;
|
||||
|
||||
break;
|
||||
}
|
||||
FlatShape::String => {
|
||||
variables_found.push(result);
|
||||
}
|
||||
_ => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If most left var was not found
|
||||
if !found_var {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Reverse the order back
|
||||
variables_found = variables_found.into_iter().rev().collect();
|
||||
|
||||
// Extract the variable and the sublevels
|
||||
let var = variables_found.first().unwrap_or(&vec![]).to_vec();
|
||||
let sublevels: Vec<Vec<u8>> = variables_found.into_iter().skip(1).collect();
|
||||
|
||||
Some((var, sublevels))
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::completions::{Completer, CompletionOptions};
|
||||
use nu_protocol::{
|
||||
engine::{EngineState, Stack, StateWorkingSet},
|
||||
Span,
|
||||
Span, Value,
|
||||
};
|
||||
use reedline::Suggestion;
|
||||
use std::sync::Arc;
|
||||
|
@ -10,15 +10,19 @@ use std::sync::Arc;
|
|||
pub struct VariableCompletion {
|
||||
engine_state: Arc<EngineState>,
|
||||
stack: Stack,
|
||||
previous_expr: Vec<u8>,
|
||||
var_context: (Vec<u8>, Vec<Vec<u8>>), // tuple with $var and the sublevels (.b.c.d)
|
||||
}
|
||||
|
||||
impl VariableCompletion {
|
||||
pub fn new(engine_state: Arc<EngineState>, stack: Stack, previous_expr: Vec<u8>) -> Self {
|
||||
pub fn new(
|
||||
engine_state: Arc<EngineState>,
|
||||
stack: Stack,
|
||||
var_context: (Vec<u8>, Vec<Vec<u8>>),
|
||||
) -> Self {
|
||||
Self {
|
||||
engine_state,
|
||||
stack,
|
||||
previous_expr,
|
||||
var_context,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,38 +38,83 @@ impl Completer for VariableCompletion {
|
|||
) -> (Vec<Suggestion>, CompletionOptions) {
|
||||
let mut output = vec![];
|
||||
let builtins = ["$nu", "$in", "$config", "$env", "$nothing"];
|
||||
let previous_expr_str = std::str::from_utf8(&self.previous_expr)
|
||||
let var_str = std::str::from_utf8(&self.var_context.0)
|
||||
.unwrap_or("")
|
||||
.to_lowercase();
|
||||
let var_id = working_set.find_variable(&self.var_context.0);
|
||||
let current_span = reedline::Span {
|
||||
start: span.start - offset,
|
||||
end: span.end - offset,
|
||||
};
|
||||
|
||||
// Completions for the given variable (e.g: $env.<tab> for completing $env.SOMETHING)
|
||||
if !self.previous_expr.is_empty() && previous_expr_str.as_str() == "$env" {
|
||||
for env_var in self.stack.get_env_vars(&self.engine_state) {
|
||||
output.push(Suggestion {
|
||||
value: env_var.0,
|
||||
description: None,
|
||||
extra: None,
|
||||
span: reedline::Span {
|
||||
start: span.start - offset,
|
||||
end: span.end - offset,
|
||||
},
|
||||
});
|
||||
// Completions for the given variable
|
||||
if !var_str.is_empty() {
|
||||
// Completion for $env.<tab>
|
||||
if var_str.as_str() == "$env" {
|
||||
for env_var in self.stack.get_env_vars(&self.engine_state) {
|
||||
output.push(Suggestion {
|
||||
value: env_var.0,
|
||||
description: None,
|
||||
extra: None,
|
||||
span: current_span,
|
||||
});
|
||||
}
|
||||
|
||||
return (output, CompletionOptions::default());
|
||||
}
|
||||
|
||||
return (output, CompletionOptions::default());
|
||||
// Completion other variable types
|
||||
if let Some(var_id) = var_id {
|
||||
// Extract the variable value from the stack
|
||||
let var = self.stack.get_var(
|
||||
var_id,
|
||||
Span {
|
||||
start: span.start,
|
||||
end: span.end,
|
||||
},
|
||||
);
|
||||
|
||||
// If the value exists and it's of type Record
|
||||
if let Ok(mut value) = var {
|
||||
// Find recursively the values for sublevels
|
||||
// if no sublevels are set it returns the current value
|
||||
value = recursive_value(value, self.var_context.1.clone());
|
||||
|
||||
match value {
|
||||
Value::Record {
|
||||
cols,
|
||||
vals: _,
|
||||
span: _,
|
||||
} => {
|
||||
// Add all the columns as completion
|
||||
for item in cols {
|
||||
output.push(Suggestion {
|
||||
value: item,
|
||||
description: None,
|
||||
extra: None,
|
||||
span: current_span,
|
||||
});
|
||||
}
|
||||
|
||||
return (output, CompletionOptions::default());
|
||||
}
|
||||
|
||||
_ => {
|
||||
return (output, CompletionOptions::default());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Variable completion (e.g: $en<tab> to complete $env)
|
||||
for builtin in builtins {
|
||||
// Variable completion (e.g: $en<tab> to complete $env)
|
||||
if builtin.as_bytes().starts_with(&prefix) {
|
||||
output.push(Suggestion {
|
||||
value: builtin.to_string(),
|
||||
description: None,
|
||||
extra: None,
|
||||
span: reedline::Span {
|
||||
start: span.start - offset,
|
||||
end: span.end - offset,
|
||||
},
|
||||
span: current_span,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -78,10 +127,7 @@ impl Completer for VariableCompletion {
|
|||
value: String::from_utf8_lossy(v.0).to_string(),
|
||||
description: None,
|
||||
extra: None,
|
||||
span: reedline::Span {
|
||||
start: span.start - offset,
|
||||
end: span.end - offset,
|
||||
},
|
||||
span: current_span,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -95,10 +141,7 @@ impl Completer for VariableCompletion {
|
|||
value: String::from_utf8_lossy(v.0).to_string(),
|
||||
description: None,
|
||||
extra: None,
|
||||
span: reedline::Span {
|
||||
start: span.start - offset,
|
||||
end: span.end - offset,
|
||||
},
|
||||
span: current_span,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -109,3 +152,32 @@ impl Completer for VariableCompletion {
|
|||
(output, CompletionOptions::default())
|
||||
}
|
||||
}
|
||||
|
||||
fn recursive_value(val: Value, sublevels: Vec<Vec<u8>>) -> Value {
|
||||
// Go to next sublevel
|
||||
if let Some(next_sublevel) = sublevels.clone().into_iter().next() {
|
||||
match val {
|
||||
Value::Record {
|
||||
cols,
|
||||
vals,
|
||||
span: _,
|
||||
} => {
|
||||
for item in cols.into_iter().zip(vals.into_iter()) {
|
||||
// Check if index matches with sublevel
|
||||
if item.0.as_bytes().to_vec() == next_sublevel {
|
||||
// If matches try to fetch recursively the next
|
||||
return recursive_value(item.1, sublevels.into_iter().skip(1).collect());
|
||||
}
|
||||
}
|
||||
|
||||
// Current sublevel value not found
|
||||
return Value::Nothing {
|
||||
span: Span { start: 0, end: 0 },
|
||||
};
|
||||
}
|
||||
_ => return val,
|
||||
}
|
||||
}
|
||||
|
||||
val
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue