diff --git a/crates/nu-cli/src/completions/completer.rs b/crates/nu-cli/src/completions/completer.rs index c8272fe974..ff2a7a151f 100644 --- a/crates/nu-cli/src/completions/completer.rs +++ b/crates/nu-cli/src/completions/completer.rs @@ -8,6 +8,7 @@ use nu_protocol::{ Span, Value, }; use reedline::{Completer as ReedlineCompleter, Suggestion}; +use std::str; use std::sync::Arc; #[derive(Clone)] @@ -69,6 +70,10 @@ impl NuCompleter { for (flat_idx, flat) in flattened.iter().enumerate() { if pos >= flat.0.start && pos < flat.0.end { + // Context variables + let mut is_variable_completion = false; + let mut previous_expr: Vec = vec![]; + // Create a new span let new_span = Span { start: flat.0.start, @@ -79,9 +84,32 @@ 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 => {} + } + } + // Variables completion - if prefix.starts_with(b"$") { - let mut completer = VariableCompletion::new(self.engine_state.clone()); + if prefix.starts_with(b"$") || is_variable_completion { + let mut completer = VariableCompletion::new( + self.engine_state.clone(), + self.stack.clone(), + previous_expr, + ); return self.process_completion( &mut completer, diff --git a/crates/nu-cli/src/completions/variable_completions.rs b/crates/nu-cli/src/completions/variable_completions.rs index 71ef2dd071..a0c5858211 100644 --- a/crates/nu-cli/src/completions/variable_completions.rs +++ b/crates/nu-cli/src/completions/variable_completions.rs @@ -1,6 +1,6 @@ use crate::completions::{Completer, CompletionOptions}; use nu_protocol::{ - engine::{EngineState, StateWorkingSet}, + engine::{EngineState, Stack, StateWorkingSet}, Span, }; use reedline::Suggestion; @@ -9,11 +9,17 @@ use std::sync::Arc; #[derive(Clone)] pub struct VariableCompletion { engine_state: Arc, + stack: Stack, + previous_expr: Vec, } impl VariableCompletion { - pub fn new(engine_state: Arc) -> Self { - Self { engine_state } + pub fn new(engine_state: Arc, stack: Stack, previous_expr: Vec) -> Self { + Self { + engine_state, + stack, + previous_expr, + } } } @@ -27,10 +33,30 @@ impl Completer for VariableCompletion { _: usize, ) -> (Vec, CompletionOptions) { let mut output = vec![]; - let builtins = ["$nu", "$in", "$config", "$env", "$nothing"]; + let previous_expr_str = std::str::from_utf8(&self.previous_expr) + .unwrap_or("") + .to_lowercase(); + + // Completions for the given variable (e.g: $env. 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, + }, + }); + } + + return (output, CompletionOptions::default()); + } for builtin in builtins { + // Variable completion (e.g: $en to complete $env) if builtin.as_bytes().starts_with(&prefix) { output.push(Suggestion { value: builtin.to_string(), @@ -44,6 +70,7 @@ impl Completer for VariableCompletion { } } + // Working set scope vars for scope in &working_set.delta.scope { for v in &scope.vars { if v.0.starts_with(&prefix) { @@ -59,6 +86,8 @@ impl Completer for VariableCompletion { } } } + + // Permanent state vars for scope in &self.engine_state.scope { for v in &scope.vars { if v.0.starts_with(&prefix) { diff --git a/crates/nu-protocol/src/engine/engine_state.rs b/crates/nu-protocol/src/engine/engine_state.rs index 5622183def..1512687e65 100644 --- a/crates/nu-protocol/src/engine/engine_state.rs +++ b/crates/nu-protocol/src/engine/engine_state.rs @@ -1233,6 +1233,16 @@ impl<'a> StateWorkingSet<'a> { self.permanent_state.env_vars.get(name) } + pub fn list_env(&self) -> Vec { + let mut env_vars = vec![]; + + for env_var in self.permanent_state.env_vars.clone().into_iter() { + env_vars.push(env_var.0) + } + + env_vars + } + pub fn set_variable_type(&mut self, var_id: VarId, ty: Type) { let num_permanent_vars = self.permanent_state.num_vars(); if var_id < num_permanent_vars {