mirror of
https://github.com/nushell/nushell.git
synced 2025-02-17 02:50:56 +01: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:
parent
2b021472d6
commit
03c9eaf005
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -3509,12 +3509,14 @@ dependencies = [
|
|||||||
"indexmap",
|
"indexmap",
|
||||||
"is_executable",
|
"is_executable",
|
||||||
"nu-data",
|
"nu-data",
|
||||||
|
"nu-engine",
|
||||||
"nu-errors",
|
"nu-errors",
|
||||||
"nu-parser",
|
"nu-parser",
|
||||||
"nu-path",
|
"nu-path",
|
||||||
"nu-protocol",
|
"nu-protocol",
|
||||||
"nu-source",
|
"nu-source",
|
||||||
"nu-test-support",
|
"nu-test-support",
|
||||||
|
"parking_lot 0.11.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -27,12 +27,31 @@ impl Helper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use nu_protocol::{SignatureRegistry, VariableRegistry};
|
||||||
struct CompletionContext<'a>(&'a EvaluationContext);
|
struct CompletionContext<'a>(&'a EvaluationContext);
|
||||||
|
|
||||||
impl<'a> nu_completion::CompletionContext for CompletionContext<'a> {
|
impl<'a> nu_completion::CompletionContext for CompletionContext<'a> {
|
||||||
fn signature_registry(&self) -> &dyn nu_parser::ParserScope {
|
fn signature_registry(&self) -> &dyn SignatureRegistry {
|
||||||
&self.0.scope
|
&self.0.scope
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn scope(&self) -> &dyn nu_parser::ParserScope {
|
||||||
|
&self.0.scope
|
||||||
|
}
|
||||||
|
|
||||||
|
fn source(&self) -> &EvaluationContext {
|
||||||
|
self.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn variable_registry(&self) -> &dyn VariableRegistry {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> AsRef<EvaluationContext> for CompletionContext<'a> {
|
||||||
|
fn as_ref(&self) -> &EvaluationContext {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CompletionSuggestion(nu_completion::Suggestion);
|
pub struct CompletionSuggestion(nu_completion::Suggestion);
|
||||||
|
@ -10,6 +10,7 @@ version = "0.33.0"
|
|||||||
doctest = false
|
doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
nu-engine = { version="0.33.0", path="../nu-engine" }
|
||||||
nu-data = { version="0.33.0", path="../nu-data" }
|
nu-data = { version="0.33.0", path="../nu-data" }
|
||||||
nu-errors = { version="0.33.0", path="../nu-errors" }
|
nu-errors = { version="0.33.0", path="../nu-errors" }
|
||||||
nu-parser = { version="0.33.0", path="../nu-parser" }
|
nu-parser = { version="0.33.0", path="../nu-parser" }
|
||||||
@ -19,5 +20,8 @@ nu-source = { version="0.33.0", path="../nu-source" }
|
|||||||
nu-test-support = { version="0.33.0", path="../nu-test-support" }
|
nu-test-support = { version="0.33.0", path="../nu-test-support" }
|
||||||
|
|
||||||
dirs-next = "2.0.0"
|
dirs-next = "2.0.0"
|
||||||
indexmap = { version="1.6.1", features=["serde-1"] }
|
indexmap = { version = "1.6.1", features = ["serde-1"] }
|
||||||
is_executable = { version="1.0.1", optional=true }
|
is_executable = { version="1.0.1", optional=true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
parking_lot = "0.11.1"
|
||||||
|
@ -16,7 +16,7 @@ where
|
|||||||
{
|
{
|
||||||
fn complete(&self, ctx: &Context, partial: &str, matcher: &dyn Matcher) -> Vec<Suggestion> {
|
fn complete(&self, ctx: &Context, partial: &str, matcher: &dyn Matcher) -> Vec<Suggestion> {
|
||||||
let registry = ctx.signature_registry();
|
let registry = ctx.signature_registry();
|
||||||
let mut commands: IndexSet<String> = IndexSet::from_iter(registry.get_names());
|
let mut commands: IndexSet<String> = IndexSet::from_iter(registry.names());
|
||||||
|
|
||||||
// Command suggestions can come from three possible sets:
|
// Command suggestions can come from three possible sets:
|
||||||
// 1. internal command names,
|
// 1. internal command names,
|
||||||
|
@ -8,6 +8,7 @@ use crate::flag::FlagCompleter;
|
|||||||
use crate::matchers;
|
use crate::matchers;
|
||||||
use crate::matchers::Matcher;
|
use crate::matchers::Matcher;
|
||||||
use crate::path::{PathCompleter, PathSuggestion};
|
use crate::path::{PathCompleter, PathSuggestion};
|
||||||
|
use crate::variable::VariableCompleter;
|
||||||
use crate::{Completer, CompletionContext, Suggestion};
|
use crate::{Completer, CompletionContext, Suggestion};
|
||||||
|
|
||||||
pub struct NuCompleter {}
|
pub struct NuCompleter {}
|
||||||
@ -26,7 +27,7 @@ impl NuCompleter {
|
|||||||
let tokens = nu_parser::lex(line, 0).0;
|
let tokens = nu_parser::lex(line, 0).0;
|
||||||
|
|
||||||
let locations = Some(nu_parser::parse_block(tokens).0)
|
let locations = Some(nu_parser::parse_block(tokens).0)
|
||||||
.map(|block| nu_parser::classify_block(&block, context.signature_registry()))
|
.map(|block| nu_parser::classify_block(&block, context.scope()))
|
||||||
.map(|(block, _)| engine::completion_location(line, &block, pos))
|
.map(|(block, _)| engine::completion_location(line, &block, pos))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
@ -121,7 +122,10 @@ impl NuCompleter {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
LocationType::Variable => Vec::new(),
|
LocationType::Variable => {
|
||||||
|
let variable_completer = VariableCompleter;
|
||||||
|
variable_completer.complete(context, &partial, matcher.to_owned())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -301,10 +301,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ParserScope for VecRegistry {
|
impl ParserScope for VecRegistry {
|
||||||
fn get_names(&self) -> Vec<String> {
|
|
||||||
self.0.iter().cloned().map(|s| s.name).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn has_signature(&self, name: &str) -> bool {
|
fn has_signature(&self, name: &str) -> bool {
|
||||||
self.0.iter().any(|v| v.name == name)
|
self.0.iter().any(|v| v.name == name)
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ where
|
|||||||
Context: CompletionContext,
|
Context: CompletionContext,
|
||||||
{
|
{
|
||||||
fn complete(&self, ctx: &Context, partial: &str, matcher: &dyn Matcher) -> Vec<Suggestion> {
|
fn complete(&self, ctx: &Context, partial: &str, matcher: &dyn Matcher) -> Vec<Suggestion> {
|
||||||
if let Some(sig) = ctx.signature_registry().get_signature(&self.cmd) {
|
if let Some(sig) = ctx.signature_registry().get(&self.cmd) {
|
||||||
let mut suggestions = Vec::new();
|
let mut suggestions = Vec::new();
|
||||||
for (name, (named_type, _desc)) in sig.named.iter() {
|
for (name, (named_type, _desc)) in sig.named.iter() {
|
||||||
suggestions.push(format!("--{}", name));
|
suggestions.push(format!("--{}", name));
|
||||||
|
@ -4,6 +4,10 @@ pub(crate) mod engine;
|
|||||||
pub(crate) mod flag;
|
pub(crate) mod flag;
|
||||||
pub(crate) mod matchers;
|
pub(crate) mod matchers;
|
||||||
pub(crate) mod path;
|
pub(crate) mod path;
|
||||||
|
pub(crate) mod variable;
|
||||||
|
|
||||||
|
use nu_engine::EvaluationContext;
|
||||||
|
use nu_protocol::{SignatureRegistry, VariableRegistry};
|
||||||
|
|
||||||
use matchers::Matcher;
|
use matchers::Matcher;
|
||||||
|
|
||||||
@ -15,8 +19,20 @@ pub struct Suggestion {
|
|||||||
pub replacement: String,
|
pub replacement: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Suggestion {
|
||||||
|
fn new(display: impl Into<String>, replacement: impl Into<String>) -> Self {
|
||||||
|
Self {
|
||||||
|
display: display.into(),
|
||||||
|
replacement: replacement.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait CompletionContext {
|
pub trait CompletionContext {
|
||||||
fn signature_registry(&self) -> &dyn nu_parser::ParserScope;
|
fn signature_registry(&self) -> &dyn SignatureRegistry;
|
||||||
|
fn scope(&self) -> &dyn nu_parser::ParserScope;
|
||||||
|
fn source(&self) -> &EvaluationContext;
|
||||||
|
fn variable_registry(&self) -> &dyn VariableRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Completer<Context: CompletionContext> {
|
pub trait Completer<Context: CompletionContext> {
|
||||||
|
182
crates/nu-completion/src/variable.rs
Normal file
182
crates/nu-completion/src/variable.rs
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
use nu_engine::value_shell::ValueShell;
|
||||||
|
use nu_protocol::ColumnPath;
|
||||||
|
use nu_source::SpannedItem;
|
||||||
|
|
||||||
|
use super::matchers::Matcher;
|
||||||
|
use crate::{Completer, CompletionContext, Suggestion};
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
fn build_path(head: &str, members: &Path, entry: &str) -> String {
|
||||||
|
let mut full_path = head.to_string();
|
||||||
|
full_path.push_str(
|
||||||
|
&members
|
||||||
|
.join(entry)
|
||||||
|
.display()
|
||||||
|
.to_string()
|
||||||
|
.replace(std::path::MAIN_SEPARATOR, "."),
|
||||||
|
);
|
||||||
|
full_path
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_entries(value_fs: &ValueShell, head: &str, path: &Path) -> Vec<String> {
|
||||||
|
value_fs
|
||||||
|
.members_under(&path)
|
||||||
|
.iter()
|
||||||
|
.flat_map(|entry| {
|
||||||
|
entry
|
||||||
|
.row_entries()
|
||||||
|
.map(|(entry_name, _)| build_path(&head, &path, entry_name))
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct VariableCompleter;
|
||||||
|
|
||||||
|
impl<Context> Completer<Context> for VariableCompleter
|
||||||
|
where
|
||||||
|
Context: CompletionContext,
|
||||||
|
{
|
||||||
|
fn complete(&self, ctx: &Context, partial: &str, matcher: &dyn Matcher) -> Vec<Suggestion> {
|
||||||
|
let registry = ctx.variable_registry();
|
||||||
|
let variables_available = registry.variables();
|
||||||
|
let partial_column_path = ColumnPath::with_head(&partial.to_string().spanned_unknown());
|
||||||
|
|
||||||
|
partial_column_path
|
||||||
|
.map(|(head, members)| {
|
||||||
|
variables_available
|
||||||
|
.iter()
|
||||||
|
.filter(|candidate| matcher.matches(&head, candidate))
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|candidate| {
|
||||||
|
if !partial.ends_with('.') && members.is_empty() {
|
||||||
|
Some(vec![candidate.to_string()])
|
||||||
|
} else {
|
||||||
|
let value = registry.get_variable(&candidate[..].spanned_unknown());
|
||||||
|
let path = PathBuf::from(members.path());
|
||||||
|
|
||||||
|
value.map(|candidate| {
|
||||||
|
let fs = ValueShell::new(candidate);
|
||||||
|
|
||||||
|
fs.find(&path)
|
||||||
|
.map(|fs| collect_entries(fs, &head, &path))
|
||||||
|
.or_else(|| {
|
||||||
|
path.parent().map(|parent| {
|
||||||
|
fs.find(parent)
|
||||||
|
.map(|fs| collect_entries(fs, &head, &parent))
|
||||||
|
.unwrap_or_default()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.unwrap_or_default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.filter_map(|candidate| {
|
||||||
|
if matcher.matches(&partial, &candidate) {
|
||||||
|
Some(Suggestion::new(&candidate, &candidate))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{Completer, Suggestion as S, VariableCompleter};
|
||||||
|
use crate::matchers::case_insensitive::Matcher as CaseInsensitiveMatcher;
|
||||||
|
|
||||||
|
use indexmap::IndexMap;
|
||||||
|
use nu_engine::{
|
||||||
|
evaluation_context::EngineState, ConfigHolder, EvaluationContext, FakeHost, Host, Scope,
|
||||||
|
ShellManager,
|
||||||
|
};
|
||||||
|
use nu_protocol::{SignatureRegistry, VariableRegistry};
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
use std::ffi::OsString;
|
||||||
|
use std::sync::{atomic::AtomicBool, Arc};
|
||||||
|
|
||||||
|
struct CompletionContext<'a>(&'a EvaluationContext);
|
||||||
|
|
||||||
|
impl<'a> crate::CompletionContext for CompletionContext<'a> {
|
||||||
|
fn signature_registry(&self) -> &dyn SignatureRegistry {
|
||||||
|
&self.0.scope
|
||||||
|
}
|
||||||
|
|
||||||
|
fn source(&self) -> &nu_engine::EvaluationContext {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scope(&self) -> &dyn nu_parser::ParserScope {
|
||||||
|
&self.0.scope
|
||||||
|
}
|
||||||
|
|
||||||
|
fn variable_registry(&self) -> &dyn VariableRegistry {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_context_with_host(host: Box<dyn Host>) -> EvaluationContext {
|
||||||
|
let scope = Scope::new();
|
||||||
|
let env_vars = host.vars().iter().cloned().collect::<IndexMap<_, _>>();
|
||||||
|
scope.add_env(env_vars);
|
||||||
|
|
||||||
|
EvaluationContext {
|
||||||
|
scope,
|
||||||
|
engine_state: Arc::new(EngineState {
|
||||||
|
host: Arc::new(parking_lot::Mutex::new(host)),
|
||||||
|
current_errors: Arc::new(Mutex::new(vec![])),
|
||||||
|
ctrl_c: Arc::new(AtomicBool::new(false)),
|
||||||
|
configs: Arc::new(Mutex::new(ConfigHolder::new())),
|
||||||
|
shell_manager: ShellManager::basic(),
|
||||||
|
windows_drives_previous_cwd: Arc::new(Mutex::new(std::collections::HashMap::new())),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_envs(host: &mut FakeHost, values: Vec<(&str, &str)>) {
|
||||||
|
values.iter().for_each(|(key, value)| {
|
||||||
|
host.env_set(OsString::from(key), OsString::from(value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn structure() {
|
||||||
|
let mut host = nu_engine::FakeHost::new();
|
||||||
|
set_envs(&mut host, vec![("COMPLETER", "VARIABLE"), ("SHELL", "NU")]);
|
||||||
|
let context = create_context_with_host(Box::new(host));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
VariableCompleter {}.complete(
|
||||||
|
&CompletionContext(&context),
|
||||||
|
"$nu.env.",
|
||||||
|
&CaseInsensitiveMatcher
|
||||||
|
),
|
||||||
|
vec![
|
||||||
|
S::new("$nu.env.COMPLETER", "$nu.env.COMPLETER"),
|
||||||
|
S::new("$nu.env.SHELL", "$nu.env.SHELL")
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
VariableCompleter {}.complete(
|
||||||
|
&CompletionContext(&context),
|
||||||
|
"$nu.env.CO",
|
||||||
|
&CaseInsensitiveMatcher
|
||||||
|
),
|
||||||
|
vec![S::new("$nu.env.COMPLETER", "$nu.env.COMPLETER"),]
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
VariableCompleter {}.complete(
|
||||||
|
&CompletionContext(&context),
|
||||||
|
"$nu.en",
|
||||||
|
&CaseInsensitiveMatcher
|
||||||
|
),
|
||||||
|
vec![S::new("$nu.env", "$nu.env"),]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
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)]
|
#[allow(unused_variables)]
|
||||||
fn vars(&mut self) -> Vec<(String, String)> {
|
fn vars(&self) -> Vec<(String, String)> {
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
{
|
{
|
||||||
std::env::vars().collect::<Vec<_>>()
|
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 stderr(&mut self, out: &str);
|
||||||
fn print_err(&mut self, err: ShellError, source: &Text);
|
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_get(&mut self, key: OsString) -> Option<OsString>;
|
||||||
fn env_set(&mut self, k: OsString, v: OsString);
|
fn env_set(&mut self, k: OsString, v: OsString);
|
||||||
fn env_rm(&mut self, k: OsString);
|
fn env_rm(&mut self, k: OsString);
|
||||||
@ -41,7 +41,7 @@ impl Host for Box<dyn Host> {
|
|||||||
(**self).print_err(err, source)
|
(**self).print_err(err, source)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vars(&mut self) -> Vec<(String, String)> {
|
fn vars(&self) -> Vec<(String, String)> {
|
||||||
(**self).vars()
|
(**self).vars()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ impl Host for FakeHost {
|
|||||||
BasicHost {}.print_err(err, source);
|
BasicHost {}.print_err(err, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vars(&mut self) -> Vec<(String, String)> {
|
fn vars(&self) -> Vec<(String, String)> {
|
||||||
self.env_vars
|
self.env_vars
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(k, v)| (k.clone(), v.clone()))
|
.map(|(k, v)| (k.clone(), v.clone()))
|
||||||
|
@ -35,7 +35,7 @@ pub fn evaluate_baseline_expr(
|
|||||||
Expression::Synthetic(hir::Synthetic::String(s)) => {
|
Expression::Synthetic(hir::Synthetic::String(s)) => {
|
||||||
Ok(UntaggedValue::string(s).into_untagged_value())
|
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::Command => unimplemented!(),
|
||||||
Expression::Subexpression(block) => evaluate_subexpression(block, ctx),
|
Expression::Subexpression(block) => evaluate_subexpression(block, ctx),
|
||||||
Expression::ExternalCommand(_) => unimplemented!(),
|
Expression::ExternalCommand(_) => unimplemented!(),
|
||||||
@ -236,45 +236,64 @@ fn evaluate_literal(literal: &hir::Literal, span: Span) -> Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn evaluate_reference(
|
pub enum Variable<'a> {
|
||||||
name: &str,
|
Nu,
|
||||||
ctx: &EvaluationContext,
|
Scope,
|
||||||
span: Span,
|
True,
|
||||||
) -> Result<Value, ShellError> {
|
False,
|
||||||
match name {
|
Nothing,
|
||||||
"$nu" => crate::evaluate::variables::nu(&ctx.scope, span, ctx),
|
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_aliases(),
|
||||||
&ctx.scope.get_commands(),
|
&ctx.scope.get_commands(),
|
||||||
&ctx.scope.get_vars(),
|
&ctx.scope.get_vars(),
|
||||||
span,
|
|
||||||
),
|
),
|
||||||
|
Variable::True => Ok(UntaggedValue::boolean(true).into_untagged_value()),
|
||||||
"$true" => Ok(Value {
|
Variable::False => Ok(UntaggedValue::boolean(false).into_untagged_value()),
|
||||||
value: UntaggedValue::boolean(true),
|
Variable::Nothing => Ok(UntaggedValue::nothing().into_untagged_value()),
|
||||||
tag: span.into(),
|
Variable::Other(name) => match ctx.scope.get_var(name) {
|
||||||
}),
|
Some(v) => Ok(v),
|
||||||
|
|
||||||
"$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)
|
|
||||||
}
|
|
||||||
None => Err(ShellError::labeled_error(
|
None => Err(ShellError::labeled_error(
|
||||||
"Variable not in scope",
|
"Variable not in scope",
|
||||||
format!("unknown variable: {}", x),
|
format!("unknown variable: {}", name),
|
||||||
span,
|
tag.into(),
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
pub(crate) mod block;
|
pub(crate) mod block;
|
||||||
pub(crate) mod evaluate_args;
|
pub(crate) mod evaluate_args;
|
||||||
pub(crate) mod evaluator;
|
pub mod evaluator;
|
||||||
pub(crate) mod expr;
|
pub(crate) mod expr;
|
||||||
pub(crate) mod internal;
|
pub(crate) mod internal;
|
||||||
pub(crate) mod operator;
|
pub(crate) mod operator;
|
||||||
|
@ -2,7 +2,7 @@ use crate::whole_stream_command::{whole_stream_command, Command};
|
|||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_parser::ParserScope;
|
use nu_parser::ParserScope;
|
||||||
use nu_protocol::{hir::Block, Signature, Value};
|
use nu_protocol::{hir::Block, Signature, SignatureRegistry, Value};
|
||||||
use nu_source::Spanned;
|
use nu_source::Spanned;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@ -23,6 +23,7 @@ impl Scope {
|
|||||||
frames: Arc::new(parking_lot::Mutex::new(vec![ScopeFrame::new()])),
|
frames: Arc::new(parking_lot::Mutex::new(vec![ScopeFrame::new()])),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_command(&self, name: &str) -> Option<Command> {
|
pub fn get_command(&self, name: &str) -> Option<Command> {
|
||||||
for frame in self.frames.lock().iter().rev() {
|
for frame in self.frames.lock().iter().rev() {
|
||||||
if let Some(command) = frame.get_command(name) {
|
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()
|
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> {
|
pub fn get_vars(&self) -> IndexMap<String, Value> {
|
||||||
//FIXME: should this be an iterator?
|
//FIXME: should this be an iterator?
|
||||||
let mut output: IndexMap<String, Value> = IndexMap::new();
|
let mut output: IndexMap<String, Value> = IndexMap::new();
|
||||||
@ -327,12 +332,26 @@ impl Scope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParserScope for Scope {
|
impl SignatureRegistry for Scope {
|
||||||
fn get_names(&self) -> Vec<String> {
|
fn names(&self) -> Vec<String> {
|
||||||
self.get_command_names()
|
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())
|
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_protocol::{Dictionary, ShellTypeName, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||||
use nu_source::{Spanned, Tag};
|
use nu_source::{Spanned, Tag};
|
||||||
|
|
||||||
pub fn nu(
|
pub fn nu(scope: &Scope, ctx: &EvaluationContext) -> Result<Value, ShellError> {
|
||||||
scope: &Scope,
|
|
||||||
tag: impl Into<Tag>,
|
|
||||||
ctx: &EvaluationContext,
|
|
||||||
) -> Result<Value, ShellError> {
|
|
||||||
let env = &scope.get_env_vars();
|
let env = &scope.get_env_vars();
|
||||||
let tag = tag.into();
|
let tag = Tag::unknown();
|
||||||
|
|
||||||
let mut nu_dict = TaggedDictBuilder::new(&tag);
|
let mut nu_dict = TaggedDictBuilder::new(&tag);
|
||||||
|
|
||||||
@ -99,9 +95,8 @@ pub fn scope(
|
|||||||
aliases: &IndexMap<String, Vec<Spanned<String>>>,
|
aliases: &IndexMap<String, Vec<Spanned<String>>>,
|
||||||
commands: &IndexMap<String, Signature>,
|
commands: &IndexMap<String, Signature>,
|
||||||
variables: &IndexMap<String, Value>,
|
variables: &IndexMap<String, Value>,
|
||||||
tag: impl Into<Tag>,
|
|
||||||
) -> Result<Value, ShellError> {
|
) -> Result<Value, ShellError> {
|
||||||
let tag = tag.into();
|
let tag = Tag::unknown();
|
||||||
|
|
||||||
let mut scope_dict = TaggedDictBuilder::new(&tag);
|
let mut scope_dict = TaggedDictBuilder::new(&tag);
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::evaluate::evaluator::Variable;
|
||||||
use crate::evaluate::scope::{Scope, ScopeFrame};
|
use crate::evaluate::scope::{Scope, ScopeFrame};
|
||||||
use crate::shell::palette::ThemedPalette;
|
use crate::shell::palette::ThemedPalette;
|
||||||
use crate::shell::shell_manager::ShellManager;
|
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::{call_info::UnevaluatedCallInfo, config_holder::ConfigHolder};
|
||||||
use crate::{command_args::CommandArgs, script};
|
use crate::{command_args::CommandArgs, script};
|
||||||
use crate::{env::basic_host::BasicHost, Host};
|
use crate::{env::basic_host::BasicHost, Host};
|
||||||
use indexmap::IndexMap;
|
|
||||||
use log::trace;
|
|
||||||
use nu_data::config::{self, Conf, NuConfig};
|
use nu_data::config::{self, Conf, NuConfig};
|
||||||
use nu_errors::ShellError;
|
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_source::{Span, Tag};
|
||||||
use nu_stream::InputStream;
|
use nu_stream::InputStream;
|
||||||
use nu_test_support::NATIVE_PATH_ENV_VAR;
|
use nu_test_support::NATIVE_PATH_ENV_VAR;
|
||||||
|
|
||||||
|
use indexmap::IndexMap;
|
||||||
|
use log::trace;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
@ -33,7 +37,7 @@ pub struct EngineState {
|
|||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct EvaluationContext {
|
pub struct EvaluationContext {
|
||||||
pub scope: Scope,
|
pub scope: Scope,
|
||||||
engine_state: Arc<EngineState>,
|
pub engine_state: Arc<EngineState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EvaluationContext {
|
impl EvaluationContext {
|
||||||
@ -61,7 +65,7 @@ impl EvaluationContext {
|
|||||||
|
|
||||||
pub fn basic() -> EvaluationContext {
|
pub fn basic() -> EvaluationContext {
|
||||||
let scope = Scope::new();
|
let scope = Scope::new();
|
||||||
let mut host = BasicHost {};
|
let host = BasicHost {};
|
||||||
let env_vars = host.vars().iter().cloned().collect::<IndexMap<_, _>>();
|
let env_vars = host.vars().iter().cloned().collect::<IndexMap<_, _>>();
|
||||||
scope.add_env(env_vars);
|
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;
|
pub mod documentation;
|
||||||
mod env;
|
mod env;
|
||||||
mod evaluate;
|
mod evaluate;
|
||||||
mod evaluation_context;
|
pub mod evaluation_context;
|
||||||
mod example;
|
mod example;
|
||||||
pub mod filesystem;
|
pub mod filesystem;
|
||||||
mod from_value;
|
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::FakeHost;
|
||||||
pub use crate::env::host::Host;
|
pub use crate::env::host::Host;
|
||||||
pub use crate::evaluate::block::run_block;
|
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::scope::Scope;
|
||||||
|
pub use crate::evaluate::{evaluator, evaluator::evaluate_baseline_expr};
|
||||||
pub use crate::evaluation_context::EvaluationContext;
|
pub use crate::evaluation_context::EvaluationContext;
|
||||||
pub use crate::example::Example;
|
pub use crate::example::Example;
|
||||||
pub use crate::filesystem::dir_info::{DirBuilder, DirInfo, FileInfo};
|
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::painter::Painter;
|
||||||
pub use crate::shell::palette::{DefaultPalette, Palette};
|
pub use crate::shell::palette::{DefaultPalette, Palette};
|
||||||
pub use crate::shell::shell_manager::ShellManager;
|
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};
|
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 palette;
|
||||||
pub(crate) mod shell_args;
|
pub(crate) mod shell_args;
|
||||||
pub(crate) mod shell_manager;
|
pub(crate) mod shell_manager;
|
||||||
pub(crate) mod value_shell;
|
pub mod value_shell;
|
||||||
|
|
||||||
pub trait Shell: std::fmt::Debug {
|
pub trait Shell: std::fmt::Debug {
|
||||||
fn is_interactive(&self) -> bool;
|
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 mut shell_entries = VecDeque::new();
|
||||||
let full_path = path.to_path_buf();
|
let full_path = path.to_path_buf();
|
||||||
let mut viewed = self.value.clone();
|
let mut viewed = self.value.clone();
|
||||||
@ -72,12 +82,6 @@ impl ValueShell {
|
|||||||
|
|
||||||
shell_entries
|
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 {
|
impl Shell for ValueShell {
|
||||||
@ -106,10 +110,7 @@ impl Shell for ValueShell {
|
|||||||
full_path.push(value.as_ref());
|
full_path.push(value.as_ref());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut value_system = ValueStructure::new();
|
if self.find(&full_path).is_none() {
|
||||||
value_system.walk_decorate(&self.value)?;
|
|
||||||
|
|
||||||
if !value_system.exists(&full_path) {
|
|
||||||
if let Some(target) = &path {
|
if let Some(target) = &path {
|
||||||
return Err(ShellError::labeled_error(
|
return Err(ShellError::labeled_error(
|
||||||
"Can not list entries inside",
|
"Can not list entries inside",
|
||||||
|
@ -9,11 +9,9 @@ mod lex;
|
|||||||
mod parse;
|
mod parse;
|
||||||
mod scope;
|
mod scope;
|
||||||
mod shapes;
|
mod shapes;
|
||||||
mod signature;
|
|
||||||
|
|
||||||
pub use lex::lexer::{lex, parse_block};
|
pub use lex::lexer::{lex, parse_block};
|
||||||
pub use lex::tokens::{LiteBlock, LiteCommand, LiteGroup, LitePipeline};
|
pub use lex::tokens::{LiteBlock, LiteCommand, LiteGroup, LitePipeline};
|
||||||
pub use parse::{classify_block, garbage, parse, parse_full_column_path, parse_math_expression};
|
pub use parse::{classify_block, garbage, parse, parse_full_column_path, parse_math_expression};
|
||||||
pub use scope::ParserScope;
|
pub use scope::ParserScope;
|
||||||
pub use shapes::shapes;
|
pub use shapes::shapes;
|
||||||
pub use signature::{Signature, SignatureRegistry};
|
|
||||||
|
@ -3,8 +3,6 @@ use nu_source::Spanned;
|
|||||||
use std::{fmt::Debug, sync::Arc};
|
use std::{fmt::Debug, sync::Arc};
|
||||||
|
|
||||||
pub trait ParserScope: Debug {
|
pub trait ParserScope: Debug {
|
||||||
fn get_names(&self) -> Vec<String>;
|
|
||||||
|
|
||||||
fn get_signature(&self, name: &str) -> Option<nu_protocol::Signature>;
|
fn get_signature(&self, name: &str) -> Option<nu_protocol::Signature>;
|
||||||
|
|
||||||
fn has_signature(&self, name: &str) -> bool;
|
fn has_signature(&self, name: &str) -> bool;
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
use nu_source::{DebugDocBuilder, HasSpan, PrettyDebugWithSource, Span};
|
|
||||||
|
|
||||||
pub trait SignatureRegistry: Debug {
|
|
||||||
fn has(&self, name: &str) -> bool;
|
|
||||||
fn get(&self, name: &str) -> Option<nu_protocol::Signature>;
|
|
||||||
fn clone_box(&self) -> Box<dyn SignatureRegistry>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SignatureRegistry for Box<dyn SignatureRegistry> {
|
|
||||||
fn has(&self, name: &str) -> bool {
|
|
||||||
(&**self).has(name)
|
|
||||||
}
|
|
||||||
fn get(&self, name: &str) -> Option<nu_protocol::Signature> {
|
|
||||||
(&**self).get(name)
|
|
||||||
}
|
|
||||||
fn clone_box(&self) -> Box<dyn SignatureRegistry> {
|
|
||||||
(&**self).clone_box()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Signature {
|
|
||||||
pub(crate) unspanned: nu_protocol::Signature,
|
|
||||||
span: Span,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Signature {
|
|
||||||
pub fn new(unspanned: nu_protocol::Signature, span: impl Into<Span>) -> Signature {
|
|
||||||
Signature {
|
|
||||||
unspanned,
|
|
||||||
span: span.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HasSpan for Signature {
|
|
||||||
fn span(&self) -> Span {
|
|
||||||
self.span
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PrettyDebugWithSource for Signature {
|
|
||||||
fn pretty_debug(&self, source: &str) -> DebugDocBuilder {
|
|
||||||
self.unspanned.pretty_debug(source)
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,6 +5,7 @@ mod call_info;
|
|||||||
pub mod config_path;
|
pub mod config_path;
|
||||||
pub mod hir;
|
pub mod hir;
|
||||||
mod maybe_owned;
|
mod maybe_owned;
|
||||||
|
mod registry;
|
||||||
mod return_value;
|
mod return_value;
|
||||||
mod signature;
|
mod signature;
|
||||||
mod syntax_shape;
|
mod syntax_shape;
|
||||||
@ -18,6 +19,7 @@ pub mod dataframe;
|
|||||||
pub use crate::call_info::{CallInfo, EvaluatedArgs};
|
pub use crate::call_info::{CallInfo, EvaluatedArgs};
|
||||||
pub use crate::config_path::ConfigPath;
|
pub use crate::config_path::ConfigPath;
|
||||||
pub use crate::maybe_owned::MaybeOwned;
|
pub use crate::maybe_owned::MaybeOwned;
|
||||||
|
pub use crate::registry::{SignatureRegistry, VariableRegistry};
|
||||||
pub use crate::return_value::{CommandAction, ReturnSuccess, ReturnValue};
|
pub use crate::return_value::{CommandAction, ReturnSuccess, ReturnValue};
|
||||||
pub use crate::signature::{NamedType, PositionalType, Signature};
|
pub use crate::signature::{NamedType, PositionalType, Signature};
|
||||||
pub use crate::syntax_shape::SyntaxShape;
|
pub use crate::syntax_shape::SyntaxShape;
|
||||||
|
31
crates/nu-protocol/src/registry.rs
Normal file
31
crates/nu-protocol/src/registry.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use crate::{Signature, Value};
|
||||||
|
use nu_source::Spanned;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
pub trait VariableRegistry {
|
||||||
|
fn get_variable(&self, name: &Spanned<&str>) -> Option<Value>;
|
||||||
|
fn variables(&self) -> Vec<String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SignatureRegistry: Debug {
|
||||||
|
fn names(&self) -> Vec<String>;
|
||||||
|
fn has(&self, name: &str) -> bool;
|
||||||
|
fn get(&self, name: &str) -> Option<Signature>;
|
||||||
|
fn clone_box(&self) -> Box<dyn SignatureRegistry>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SignatureRegistry for Box<dyn SignatureRegistry> {
|
||||||
|
fn names(&self) -> Vec<String> {
|
||||||
|
(&**self).names()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has(&self, name: &str) -> bool {
|
||||||
|
(&**self).has(name)
|
||||||
|
}
|
||||||
|
fn get(&self, name: &str) -> Option<Signature> {
|
||||||
|
(&**self).get(name)
|
||||||
|
}
|
||||||
|
fn clone_box(&self) -> Box<dyn SignatureRegistry> {
|
||||||
|
(&**self).clone_box()
|
||||||
|
}
|
||||||
|
}
|
@ -61,6 +61,10 @@ impl ColumnPath {
|
|||||||
self.members.iter()
|
self.members.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.members.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the last member and a slice of the remaining members
|
/// Returns the last member and a slice of the remaining members
|
||||||
pub fn split_last(&self) -> Option<(&PathMember, &[PathMember])> {
|
pub fn split_last(&self) -> Option<(&PathMember, &[PathMember])> {
|
||||||
self.members.split_last()
|
self.members.split_last()
|
||||||
@ -71,6 +75,23 @@ impl ColumnPath {
|
|||||||
self.iter().last()
|
self.iter().last()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn path(&self) -> String {
|
||||||
|
let sep = std::path::MAIN_SEPARATOR;
|
||||||
|
let mut members = self.iter();
|
||||||
|
let mut f = String::from(sep);
|
||||||
|
|
||||||
|
if let Some(member) = members.next() {
|
||||||
|
f.push_str(&member.as_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
for member in members {
|
||||||
|
f.push(sep);
|
||||||
|
f.push_str(&member.as_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
f
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build(text: &Spanned<String>) -> ColumnPath {
|
pub fn build(text: &Spanned<String>) -> ColumnPath {
|
||||||
if let (
|
if let (
|
||||||
SpannedExpression {
|
SpannedExpression {
|
||||||
@ -87,6 +108,38 @@ impl ColumnPath {
|
|||||||
ColumnPath { members: vec![] }
|
ColumnPath { members: vec![] }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_head(text: &Spanned<String>) -> Option<(String, ColumnPath)> {
|
||||||
|
match parse_full_column_path(text) {
|
||||||
|
(
|
||||||
|
SpannedExpression {
|
||||||
|
expr: Expression::FullColumnPath(path),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
_,
|
||||||
|
) => {
|
||||||
|
if let crate::hir::FullColumnPath {
|
||||||
|
head:
|
||||||
|
SpannedExpression {
|
||||||
|
expr: Expression::Variable(name, _),
|
||||||
|
span: _,
|
||||||
|
},
|
||||||
|
tail,
|
||||||
|
} = *path
|
||||||
|
{
|
||||||
|
Some((
|
||||||
|
name,
|
||||||
|
ColumnPath {
|
||||||
|
members: tail.to_vec(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrettyDebug for ColumnPath {
|
impl PrettyDebug for ColumnPath {
|
||||||
@ -136,6 +189,102 @@ impl PathMember {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_full_column_path(
|
||||||
|
raw_column_path: &Spanned<String>,
|
||||||
|
) -> (SpannedExpression, Option<ParseError>) {
|
||||||
|
let mut inside_delimiter = vec![];
|
||||||
|
let mut output = vec![];
|
||||||
|
let mut current_part = String::new();
|
||||||
|
let mut start_index = 0;
|
||||||
|
let mut last_index = 0;
|
||||||
|
let error = None;
|
||||||
|
|
||||||
|
let mut head = None;
|
||||||
|
|
||||||
|
for (idx, c) in raw_column_path.item.char_indices() {
|
||||||
|
last_index = idx;
|
||||||
|
if c == '(' {
|
||||||
|
inside_delimiter.push(')');
|
||||||
|
} else if let Some(delimiter) = inside_delimiter.last() {
|
||||||
|
if c == *delimiter {
|
||||||
|
inside_delimiter.pop();
|
||||||
|
}
|
||||||
|
} else if c == '\'' || c == '"' {
|
||||||
|
inside_delimiter.push(c);
|
||||||
|
} else if c == '.' {
|
||||||
|
let part_span = Span::new(
|
||||||
|
raw_column_path.span.start() + start_index,
|
||||||
|
raw_column_path.span.start() + idx,
|
||||||
|
);
|
||||||
|
|
||||||
|
if head.is_none() && current_part.starts_with('$') {
|
||||||
|
// We have the variable head
|
||||||
|
head = Some(Expression::variable(current_part.clone(), part_span))
|
||||||
|
} else if let Ok(row_number) = current_part.parse::<i64>() {
|
||||||
|
output.push(UnspannedPathMember::Int(row_number).into_path_member(part_span));
|
||||||
|
} else {
|
||||||
|
let current_part = trim_quotes(¤t_part);
|
||||||
|
output.push(
|
||||||
|
UnspannedPathMember::String(current_part.clone()).into_path_member(part_span),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
current_part.clear();
|
||||||
|
// Note: I believe this is safe because of the delimiter we're using,
|
||||||
|
// but if we get fancy with Unicode we'll need to change this.
|
||||||
|
start_index = idx + '.'.len_utf8();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
current_part.push(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !current_part.is_empty() {
|
||||||
|
let part_span = Span::new(
|
||||||
|
raw_column_path.span.start() + start_index,
|
||||||
|
raw_column_path.span.start() + last_index + 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
if head.is_none() {
|
||||||
|
if current_part.starts_with('$') {
|
||||||
|
head = Some(Expression::variable(current_part, raw_column_path.span));
|
||||||
|
} else if let Ok(row_number) = current_part.parse::<i64>() {
|
||||||
|
output.push(UnspannedPathMember::Int(row_number).into_path_member(part_span));
|
||||||
|
} else {
|
||||||
|
let current_part = trim_quotes(¤t_part);
|
||||||
|
output.push(UnspannedPathMember::String(current_part).into_path_member(part_span));
|
||||||
|
}
|
||||||
|
} else if let Ok(row_number) = current_part.parse::<i64>() {
|
||||||
|
output.push(UnspannedPathMember::Int(row_number).into_path_member(part_span));
|
||||||
|
} else {
|
||||||
|
let current_part = trim_quotes(¤t_part);
|
||||||
|
output.push(UnspannedPathMember::String(current_part).into_path_member(part_span));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(head) = head {
|
||||||
|
(
|
||||||
|
SpannedExpression::new(
|
||||||
|
Expression::path(SpannedExpression::new(head, raw_column_path.span), output),
|
||||||
|
raw_column_path.span,
|
||||||
|
),
|
||||||
|
error,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
SpannedExpression::new(
|
||||||
|
Expression::path(
|
||||||
|
SpannedExpression::new(
|
||||||
|
Expression::variable("$it".into(), raw_column_path.span),
|
||||||
|
raw_column_path.span,
|
||||||
|
),
|
||||||
|
output,
|
||||||
|
),
|
||||||
|
raw_column_path.span,
|
||||||
|
),
|
||||||
|
error,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn parse(raw_column_path: &Spanned<String>) -> (SpannedExpression, Option<ParseError>) {
|
fn parse(raw_column_path: &Spanned<String>) -> (SpannedExpression, Option<ParseError>) {
|
||||||
let mut delimiter = '.';
|
let mut delimiter = '.';
|
||||||
let mut inside_delimiter = false;
|
let mut inside_delimiter = false;
|
||||||
|
@ -17,7 +17,7 @@ pub struct ValueResource {
|
|||||||
|
|
||||||
impl ValueResource {}
|
impl ValueResource {}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default, Debug)]
|
||||||
pub struct ValueStructure {
|
pub struct ValueStructure {
|
||||||
pub resources: Vec<ValueResource>,
|
pub resources: Vec<ValueResource>,
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user