mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 01:05:01 +02:00
Variable completions. (#3666)
In Nu we have variables (E.g. $var-name) and these contain `Value` types. This means we can bind to variables any structured data and column path syntax (E.g. `$variable.path.to`) allows flexibility for "querying" said structures. Here we offer completions for these. For example, in a Nushell session the variable `$nu` contains environment values among other things. If we wanted to see in the screen some environment variable (say the var `SHELL`) we do: ``` > echo $nu.env.SHELL ``` with completions we can now do: `echo $nu.env.S[\TAB]` and we get suggestions that start at the column path `$nu.env` with vars starting with the letter `S` in this case `SHELL` appears in the suggestions.
This commit is contained in:
committed by
GitHub
parent
2b021472d6
commit
03c9eaf005
2
crates/nu-engine/src/env/basic_host.rs
vendored
2
crates/nu-engine/src/env/basic_host.rs
vendored
@ -37,7 +37,7 @@ impl Host for BasicHost {
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn vars(&mut self) -> Vec<(String, String)> {
|
||||
fn vars(&self) -> Vec<(String, String)> {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
std::env::vars().collect::<Vec<_>>()
|
||||
|
6
crates/nu-engine/src/env/host.rs
vendored
6
crates/nu-engine/src/env/host.rs
vendored
@ -11,7 +11,7 @@ pub trait Host: Debug + Send {
|
||||
fn stderr(&mut self, out: &str);
|
||||
fn print_err(&mut self, err: ShellError, source: &Text);
|
||||
|
||||
fn vars(&mut self) -> Vec<(String, String)>;
|
||||
fn vars(&self) -> Vec<(String, String)>;
|
||||
fn env_get(&mut self, key: OsString) -> Option<OsString>;
|
||||
fn env_set(&mut self, k: OsString, v: OsString);
|
||||
fn env_rm(&mut self, k: OsString);
|
||||
@ -41,7 +41,7 @@ impl Host for Box<dyn Host> {
|
||||
(**self).print_err(err, source)
|
||||
}
|
||||
|
||||
fn vars(&mut self) -> Vec<(String, String)> {
|
||||
fn vars(&self) -> Vec<(String, String)> {
|
||||
(**self).vars()
|
||||
}
|
||||
|
||||
@ -104,7 +104,7 @@ impl Host for FakeHost {
|
||||
BasicHost {}.print_err(err, source);
|
||||
}
|
||||
|
||||
fn vars(&mut self) -> Vec<(String, String)> {
|
||||
fn vars(&self) -> Vec<(String, String)> {
|
||||
self.env_vars
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), v.clone()))
|
||||
|
@ -35,7 +35,7 @@ pub fn evaluate_baseline_expr(
|
||||
Expression::Synthetic(hir::Synthetic::String(s)) => {
|
||||
Ok(UntaggedValue::string(s).into_untagged_value())
|
||||
}
|
||||
Expression::Variable(var, s) => evaluate_reference(var, ctx, *s),
|
||||
expr @ Expression::Variable(_, _) => evaluate_reference(&Variable::from(expr), ctx, span),
|
||||
Expression::Command => unimplemented!(),
|
||||
Expression::Subexpression(block) => evaluate_subexpression(block, ctx),
|
||||
Expression::ExternalCommand(_) => unimplemented!(),
|
||||
@ -236,45 +236,64 @@ fn evaluate_literal(literal: &hir::Literal, span: Span) -> Value {
|
||||
}
|
||||
}
|
||||
|
||||
fn evaluate_reference(
|
||||
name: &str,
|
||||
ctx: &EvaluationContext,
|
||||
span: Span,
|
||||
) -> Result<Value, ShellError> {
|
||||
match name {
|
||||
"$nu" => crate::evaluate::variables::nu(&ctx.scope, span, ctx),
|
||||
pub enum Variable<'a> {
|
||||
Nu,
|
||||
Scope,
|
||||
True,
|
||||
False,
|
||||
Nothing,
|
||||
Other(&'a str),
|
||||
}
|
||||
|
||||
"$scope" => crate::evaluate::variables::scope(
|
||||
impl<'a> Variable<'a> {
|
||||
pub fn list() -> Vec<String> {
|
||||
vec![
|
||||
String::from("$nu"),
|
||||
String::from("$scope"),
|
||||
String::from("$true"),
|
||||
String::from("$false"),
|
||||
String::from("$nothing"),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Expression> for Variable<'a> {
|
||||
fn from(expr: &'a Expression) -> Self {
|
||||
match &expr {
|
||||
Expression::Variable(name, _) => match name.as_str() {
|
||||
"$nu" => Self::Nu,
|
||||
"$scope" => Self::Scope,
|
||||
"$true" => Self::True,
|
||||
"$false" => Self::False,
|
||||
"$nothing" => Self::Nothing,
|
||||
_ => Self::Other(&name),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn evaluate_reference(
|
||||
variable: &Variable,
|
||||
ctx: &EvaluationContext,
|
||||
tag: impl Into<Tag>,
|
||||
) -> Result<Value, ShellError> {
|
||||
match variable {
|
||||
Variable::Nu => crate::evaluate::variables::nu(&ctx.scope, ctx),
|
||||
Variable::Scope => crate::evaluate::variables::scope(
|
||||
&ctx.scope.get_aliases(),
|
||||
&ctx.scope.get_commands(),
|
||||
&ctx.scope.get_vars(),
|
||||
span,
|
||||
),
|
||||
|
||||
"$true" => Ok(Value {
|
||||
value: UntaggedValue::boolean(true),
|
||||
tag: span.into(),
|
||||
}),
|
||||
|
||||
"$false" => Ok(Value {
|
||||
value: UntaggedValue::boolean(false),
|
||||
tag: span.into(),
|
||||
}),
|
||||
|
||||
"$nothing" => Ok(Value {
|
||||
value: UntaggedValue::nothing(),
|
||||
tag: span.into(),
|
||||
}),
|
||||
|
||||
x => match ctx.scope.get_var(x) {
|
||||
Some(mut v) => {
|
||||
v.tag.span = span;
|
||||
Ok(v)
|
||||
}
|
||||
Variable::True => Ok(UntaggedValue::boolean(true).into_untagged_value()),
|
||||
Variable::False => Ok(UntaggedValue::boolean(false).into_untagged_value()),
|
||||
Variable::Nothing => Ok(UntaggedValue::nothing().into_untagged_value()),
|
||||
Variable::Other(name) => match ctx.scope.get_var(name) {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(ShellError::labeled_error(
|
||||
"Variable not in scope",
|
||||
format!("unknown variable: {}", x),
|
||||
span,
|
||||
format!("unknown variable: {}", name),
|
||||
tag.into(),
|
||||
)),
|
||||
},
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
pub(crate) mod block;
|
||||
pub(crate) mod evaluate_args;
|
||||
pub(crate) mod evaluator;
|
||||
pub mod evaluator;
|
||||
pub(crate) mod expr;
|
||||
pub(crate) mod internal;
|
||||
pub(crate) mod operator;
|
||||
|
@ -2,7 +2,7 @@ use crate::whole_stream_command::{whole_stream_command, Command};
|
||||
use indexmap::IndexMap;
|
||||
use nu_errors::ShellError;
|
||||
use nu_parser::ParserScope;
|
||||
use nu_protocol::{hir::Block, Signature, Value};
|
||||
use nu_protocol::{hir::Block, Signature, SignatureRegistry, Value};
|
||||
use nu_source::Spanned;
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -23,6 +23,7 @@ impl Scope {
|
||||
frames: Arc::new(parking_lot::Mutex::new(vec![ScopeFrame::new()])),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_command(&self, name: &str) -> Option<Command> {
|
||||
for frame in self.frames.lock().iter().rev() {
|
||||
if let Some(command) = frame.get_command(name) {
|
||||
@ -64,6 +65,10 @@ impl Scope {
|
||||
output.sorted_by(|k1, _v1, k2, _v2| k1.cmp(k2)).collect()
|
||||
}
|
||||
|
||||
pub fn get_variable_names(&self) -> Vec<String> {
|
||||
self.get_vars().iter().map(|(k, _)| k.to_string()).collect()
|
||||
}
|
||||
|
||||
pub fn get_vars(&self) -> IndexMap<String, Value> {
|
||||
//FIXME: should this be an iterator?
|
||||
let mut output: IndexMap<String, Value> = IndexMap::new();
|
||||
@ -327,12 +332,26 @@ impl Scope {
|
||||
}
|
||||
}
|
||||
|
||||
impl ParserScope for Scope {
|
||||
fn get_names(&self) -> Vec<String> {
|
||||
impl SignatureRegistry for Scope {
|
||||
fn names(&self) -> Vec<String> {
|
||||
self.get_command_names()
|
||||
}
|
||||
|
||||
fn get_signature(&self, name: &str) -> Option<nu_protocol::Signature> {
|
||||
fn has(&self, name: &str) -> bool {
|
||||
self.get_signature(name).is_some()
|
||||
}
|
||||
|
||||
fn get(&self, name: &str) -> Option<Signature> {
|
||||
self.get_signature(name)
|
||||
}
|
||||
|
||||
fn clone_box(&self) -> Box<dyn SignatureRegistry> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl ParserScope for Scope {
|
||||
fn get_signature(&self, name: &str) -> Option<Signature> {
|
||||
self.get_command(name).map(|x| x.signature())
|
||||
}
|
||||
|
||||
|
@ -5,13 +5,9 @@ use nu_errors::ShellError;
|
||||
use nu_protocol::{Dictionary, ShellTypeName, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use nu_source::{Spanned, Tag};
|
||||
|
||||
pub fn nu(
|
||||
scope: &Scope,
|
||||
tag: impl Into<Tag>,
|
||||
ctx: &EvaluationContext,
|
||||
) -> Result<Value, ShellError> {
|
||||
pub fn nu(scope: &Scope, ctx: &EvaluationContext) -> Result<Value, ShellError> {
|
||||
let env = &scope.get_env_vars();
|
||||
let tag = tag.into();
|
||||
let tag = Tag::unknown();
|
||||
|
||||
let mut nu_dict = TaggedDictBuilder::new(&tag);
|
||||
|
||||
@ -99,9 +95,8 @@ pub fn scope(
|
||||
aliases: &IndexMap<String, Vec<Spanned<String>>>,
|
||||
commands: &IndexMap<String, Signature>,
|
||||
variables: &IndexMap<String, Value>,
|
||||
tag: impl Into<Tag>,
|
||||
) -> Result<Value, ShellError> {
|
||||
let tag = tag.into();
|
||||
let tag = Tag::unknown();
|
||||
|
||||
let mut scope_dict = TaggedDictBuilder::new(&tag);
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::evaluate::evaluator::Variable;
|
||||
use crate::evaluate::scope::{Scope, ScopeFrame};
|
||||
use crate::shell::palette::ThemedPalette;
|
||||
use crate::shell::shell_manager::ShellManager;
|
||||
@ -5,14 +6,17 @@ use crate::whole_stream_command::Command;
|
||||
use crate::{call_info::UnevaluatedCallInfo, config_holder::ConfigHolder};
|
||||
use crate::{command_args::CommandArgs, script};
|
||||
use crate::{env::basic_host::BasicHost, Host};
|
||||
use indexmap::IndexMap;
|
||||
use log::trace;
|
||||
|
||||
use nu_data::config::{self, Conf, NuConfig};
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{hir, ConfigPath};
|
||||
use nu_protocol::{hir, ConfigPath, VariableRegistry};
|
||||
use nu_source::Spanned;
|
||||
use nu_source::{Span, Tag};
|
||||
use nu_stream::InputStream;
|
||||
use nu_test_support::NATIVE_PATH_ENV_VAR;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use log::trace;
|
||||
use parking_lot::Mutex;
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
@ -33,7 +37,7 @@ pub struct EngineState {
|
||||
#[derive(Clone, Default)]
|
||||
pub struct EvaluationContext {
|
||||
pub scope: Scope,
|
||||
engine_state: Arc<EngineState>,
|
||||
pub engine_state: Arc<EngineState>,
|
||||
}
|
||||
|
||||
impl EvaluationContext {
|
||||
@ -61,7 +65,7 @@ impl EvaluationContext {
|
||||
|
||||
pub fn basic() -> EvaluationContext {
|
||||
let scope = Scope::new();
|
||||
let mut host = BasicHost {};
|
||||
let host = BasicHost {};
|
||||
let env_vars = host.vars().iter().cloned().collect::<IndexMap<_, _>>();
|
||||
scope.add_env(env_vars);
|
||||
|
||||
@ -395,3 +399,24 @@ impl EvaluationContext {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
impl VariableRegistry for EvaluationContext {
|
||||
fn get_variable(&self, name: &Spanned<&str>) -> Option<nu_protocol::Value> {
|
||||
let span = name.span;
|
||||
let name = nu_protocol::hir::Expression::variable(name.item.to_string(), name.span);
|
||||
|
||||
let var = Variable::from(&name);
|
||||
|
||||
crate::evaluate::evaluator::evaluate_reference(&var, self, span).ok()
|
||||
}
|
||||
|
||||
fn variables(&self) -> Vec<String> {
|
||||
Variable::list()
|
||||
.into_iter()
|
||||
.chain(self.scope.get_variable_names().into_iter())
|
||||
.unique()
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ mod config_holder;
|
||||
pub mod documentation;
|
||||
mod env;
|
||||
mod evaluate;
|
||||
mod evaluation_context;
|
||||
pub mod evaluation_context;
|
||||
mod example;
|
||||
pub mod filesystem;
|
||||
mod from_value;
|
||||
@ -23,8 +23,8 @@ pub use crate::documentation::{generate_docs, get_brief_help, get_documentation,
|
||||
pub use crate::env::host::FakeHost;
|
||||
pub use crate::env::host::Host;
|
||||
pub use crate::evaluate::block::run_block;
|
||||
pub use crate::evaluate::evaluator::evaluate_baseline_expr;
|
||||
pub use crate::evaluate::scope::Scope;
|
||||
pub use crate::evaluate::{evaluator, evaluator::evaluate_baseline_expr};
|
||||
pub use crate::evaluation_context::EvaluationContext;
|
||||
pub use crate::example::Example;
|
||||
pub use crate::filesystem::dir_info::{DirBuilder, DirInfo, FileInfo};
|
||||
@ -35,5 +35,5 @@ pub use crate::print::maybe_print_errors;
|
||||
pub use crate::shell::painter::Painter;
|
||||
pub use crate::shell::palette::{DefaultPalette, Palette};
|
||||
pub use crate::shell::shell_manager::ShellManager;
|
||||
pub use crate::shell::value_shell::ValueShell;
|
||||
pub use crate::shell::value_shell;
|
||||
pub use crate::whole_stream_command::{whole_stream_command, Command, WholeStreamCommand};
|
||||
|
@ -14,7 +14,7 @@ pub(crate) mod painter;
|
||||
pub(crate) mod palette;
|
||||
pub(crate) mod shell_args;
|
||||
pub(crate) mod shell_manager;
|
||||
pub(crate) mod value_shell;
|
||||
pub mod value_shell;
|
||||
|
||||
pub trait Shell: std::fmt::Debug {
|
||||
fn is_interactive(&self) -> bool;
|
||||
|
@ -38,7 +38,17 @@ impl ValueShell {
|
||||
}
|
||||
}
|
||||
|
||||
fn members_under(&self, path: &Path) -> VecDeque<Value> {
|
||||
pub fn find(&self, path: &Path) -> Option<&Self> {
|
||||
let mut value_system = ValueStructure::new();
|
||||
|
||||
if value_system.walk_decorate(&self.value).is_ok() {
|
||||
value_system.exists(&path).then(|| self)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn members_under(&self, path: &Path) -> VecDeque<Value> {
|
||||
let mut shell_entries = VecDeque::new();
|
||||
let full_path = path.to_path_buf();
|
||||
let mut viewed = self.value.clone();
|
||||
@ -72,12 +82,6 @@ impl ValueShell {
|
||||
|
||||
shell_entries
|
||||
}
|
||||
|
||||
// TODO make use of this in the new completion engine
|
||||
#[allow(dead_code)]
|
||||
fn members(&self) -> VecDeque<Value> {
|
||||
self.members_under(Path::new("."))
|
||||
}
|
||||
}
|
||||
|
||||
impl Shell for ValueShell {
|
||||
@ -106,10 +110,7 @@ impl Shell for ValueShell {
|
||||
full_path.push(value.as_ref());
|
||||
}
|
||||
|
||||
let mut value_system = ValueStructure::new();
|
||||
value_system.walk_decorate(&self.value)?;
|
||||
|
||||
if !value_system.exists(&full_path) {
|
||||
if self.find(&full_path).is_none() {
|
||||
if let Some(target) = &path {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Can not list entries inside",
|
||||
|
Reference in New Issue
Block a user