diff --git a/crates/nu-cli/src/cli.rs b/crates/nu-cli/src/cli.rs index 9b4b9f992f..c634f3ed8c 100644 --- a/crates/nu-cli/src/cli.rs +++ b/crates/nu-cli/src/cli.rs @@ -11,7 +11,7 @@ use futures_codec::FramedRead; use nu_errors::ShellError; use nu_protocol::hir::{ClassifiedCommand, ExternalCommand}; -use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value}; +use nu_protocol::{Primitive, ReturnSuccess, Scope, Signature, UntaggedValue, Value}; use log::{debug, trace}; use rustyline::error::ReadlineError; @@ -258,6 +258,7 @@ pub fn create_default_context( whole_stream_command(What), whole_stream_command(Which), whole_stream_command(Debug), + per_item_command(Alias), // Statistics whole_stream_command(Size), whole_stream_command(Count), @@ -442,6 +443,29 @@ pub async fn cli() -> Result<(), Box> { }) .expect("Error setting Ctrl-C handler"); let mut ctrlcbreak = false; + + // before we start up, let's run our startup commands + if let Ok(config) = crate::data::config::config(Tag::unknown()) { + if let Some(commands) = config.get("startup") { + match commands { + Value { + value: UntaggedValue::Table(pipelines), + .. + } => { + for pipeline in pipelines { + if let Ok(pipeline_string) = pipeline.as_string() { + let _ = + run_pipeline_standalone(pipeline_string, false, &mut context).await; + } + } + } + _ => { + println!("warning: expected a table of pipeline strings as startup commands"); + } + } + } + } + loop { if context.ctrl_c.load(Ordering::SeqCst) { context.ctrl_c.store(false, Ordering::SeqCst); @@ -712,7 +736,7 @@ async fn process_line( None }; - match run_pipeline(pipeline, ctx, input_stream).await { + match run_pipeline(pipeline, ctx, input_stream, &Scope::empty()).await { Ok(Some(input)) => { // Running a pipeline gives us back a stream that we can then // work through. At the top level, we just want to pull on the diff --git a/crates/nu-cli/src/commands.rs b/crates/nu-cli/src/commands.rs index 88e392399f..fa9324ac54 100644 --- a/crates/nu-cli/src/commands.rs +++ b/crates/nu-cli/src/commands.rs @@ -4,6 +4,7 @@ pub(crate) mod macros; mod from_delimited_data; mod to_delimited_data; +pub(crate) mod alias; pub(crate) mod append; pub(crate) mod args; pub(crate) mod autoview; @@ -75,6 +76,7 @@ pub(crate) mod reject; pub(crate) mod rename; pub(crate) mod reverse; pub(crate) mod rm; +pub(crate) mod run_alias; pub(crate) mod save; pub(crate) mod shells; pub(crate) mod shuffle; @@ -115,6 +117,7 @@ pub(crate) use command::{ WholeStreamCommand, }; +pub(crate) use alias::Alias; pub(crate) use append::Append; pub(crate) use calc::Calc; pub(crate) use compact::Compact; diff --git a/crates/nu-cli/src/commands/alias.rs b/crates/nu-cli/src/commands/alias.rs new file mode 100644 index 0000000000..bfaa1467b0 --- /dev/null +++ b/crates/nu-cli/src/commands/alias.rs @@ -0,0 +1,65 @@ +use crate::commands::PerItemCommand; +use crate::context::CommandRegistry; +use crate::prelude::*; +use nu_errors::ShellError; +use nu_protocol::{ + CallInfo, CommandAction, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value, +}; + +pub struct Alias; + +impl PerItemCommand for Alias { + fn name(&self) -> &str { + "alias" + } + + fn signature(&self) -> Signature { + Signature::build("alias") + .required("name", SyntaxShape::String, "the name of the alias") + .required("args", SyntaxShape::Table, "the arguments to the alias") + .required("block", SyntaxShape::Block, "the block to run on each row") + } + + fn usage(&self) -> &str { + "Run a block on each row of the table." + } + + fn run( + &self, + call_info: &CallInfo, + _registry: &CommandRegistry, + _raw_args: &RawCommandArgs, + _input: Value, + ) -> Result { + let call_info = call_info.clone(); + let stream = async_stream! { + match (call_info.args.expect_nth(0)?, call_info.args.expect_nth(1)?, call_info.args.expect_nth(2)?) { + (Value {value: UntaggedValue::Primitive(Primitive::String(name)), .. }, + Value { value: UntaggedValue::Table(list), .. }, + Value { + value: UntaggedValue::Block(block), + tag + }) => { + let mut args: Vec = vec![]; + for item in list.iter() { + if let Ok(string) = item.as_string() { + args.push(format!("${}", string)); + } else { + yield Err(ShellError::labeled_error("Expected a string", "expected a string", item.tag())); + } + } + yield ReturnSuccess::action(CommandAction::AddAlias(name.to_string(), args, block.clone())) + } + _ => { + yield Err(ShellError::labeled_error( + "Expected `name [args] {block}", + "needs a name, args, and a block", + call_info.name_tag, + )) + } + }; + }; + + Ok(stream.to_output_stream()) + } +} diff --git a/crates/nu-cli/src/commands/autoview.rs b/crates/nu-cli/src/commands/autoview.rs index 06d5f7e6fd..47f0882c2b 100644 --- a/crates/nu-cli/src/commands/autoview.rs +++ b/crates/nu-cli/src/commands/autoview.rs @@ -3,7 +3,7 @@ use crate::commands::WholeStreamCommand; use crate::prelude::*; use nu_errors::ShellError; use nu_protocol::{hir, hir::Expression, hir::Literal, hir::SpannedExpression}; -use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value}; +use nu_protocol::{Primitive, ReturnSuccess, Scope, Signature, UntaggedValue, Value}; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; @@ -287,6 +287,7 @@ fn create_default_command_args(context: &RunnableContextWithoutInput) -> RawComm span, }, name_tag: context.name.clone(), + scope: Scope::empty(), }, } } diff --git a/crates/nu-cli/src/commands/classified/expr.rs b/crates/nu-cli/src/commands/classified/expr.rs index b64d1a2da9..c8ef362406 100644 --- a/crates/nu-cli/src/commands/classified/expr.rs +++ b/crates/nu-cli/src/commands/classified/expr.rs @@ -11,7 +11,9 @@ pub(crate) fn run_expression_block( expr: SpannedExpression, context: &mut Context, input: Option, + scope: &Scope, ) -> Result, ShellError> { + let scope = scope.clone(); if log_enabled!(log::Level::Trace) { trace!(target: "nu::run::expr", "->"); trace!(target: "nu::run::expr", "{:?}", expr); @@ -25,10 +27,11 @@ pub(crate) fn run_expression_block( pin_mut!(values); while let Some(row) = values.next().await { - yield evaluate_baseline_expr(&expr, ®istry, &Scope::new(row)); + let scope = scope.clone().set_it(row); + yield evaluate_baseline_expr(&expr, ®istry, &scope); } } else { - yield evaluate_baseline_expr(&expr, ®istry, &Scope::empty()); + yield evaluate_baseline_expr(&expr, ®istry, &scope); } }; diff --git a/crates/nu-cli/src/commands/classified/external.rs b/crates/nu-cli/src/commands/classified/external.rs index 9984f85c0a..6343de4fac 100644 --- a/crates/nu-cli/src/commands/classified/external.rs +++ b/crates/nu-cli/src/commands/classified/external.rs @@ -7,7 +7,7 @@ use futures_codec::FramedRead; use log::trace; use nu_errors::ShellError; use nu_protocol::hir::{ExternalArg, ExternalCommand}; -use nu_protocol::{ColumnPath, Primitive, ShellTypeName, UntaggedValue, Value}; +use nu_protocol::{ColumnPath, Primitive, Scope, ShellTypeName, UntaggedValue, Value}; use nu_source::{Tag, Tagged}; use nu_value_ext::as_column_path; use std::io::Write; @@ -97,6 +97,7 @@ pub(crate) async fn run_external_command( command: ExternalCommand, context: &mut Context, input: Option, + _scope: &Scope, is_last: bool, ) -> Result, ShellError> { trace!(target: "nu::run::external", "-> {}", command.name); @@ -718,6 +719,7 @@ mod tests { }; use futures::executor::block_on; use nu_errors::ShellError; + use nu_protocol::Scope; use nu_test_support::commands::ExternalBuilder; // async fn read(mut stream: OutputStream) -> Option { @@ -738,9 +740,11 @@ mod tests { let mut ctx = Context::basic().expect("There was a problem creating a basic context."); - assert!(run_external_command(cmd, &mut ctx, None, false) - .await - .is_err()); + assert!( + run_external_command(cmd, &mut ctx, None, &Scope::empty(), false) + .await + .is_err() + ); Ok(()) } diff --git a/crates/nu-cli/src/commands/classified/internal.rs b/crates/nu-cli/src/commands/classified/internal.rs index f14fa29268..2a76e0f406 100644 --- a/crates/nu-cli/src/commands/classified/internal.rs +++ b/crates/nu-cli/src/commands/classified/internal.rs @@ -1,14 +1,17 @@ +use crate::commands::command::per_item_command; +use crate::commands::run_alias::AliasCommand; use crate::commands::UnevaluatedCallInfo; use crate::prelude::*; use log::{log_enabled, trace}; use nu_errors::ShellError; use nu_protocol::hir::InternalCommand; -use nu_protocol::{CommandAction, Primitive, ReturnSuccess, UntaggedValue, Value}; +use nu_protocol::{CommandAction, Primitive, ReturnSuccess, Scope, UntaggedValue, Value}; pub(crate) fn run_internal_command( command: InternalCommand, context: &mut Context, input: Option, + scope: &Scope, ) -> Result, ShellError> { if log_enabled!(log::Level::Trace) { trace!(target: "nu::run::internal", "->"); @@ -28,6 +31,7 @@ pub(crate) fn run_internal_command( internal_command?, Tag::unknown_anchor(command.name_span), command.args.clone(), + scope, objects, ) }; @@ -68,6 +72,7 @@ pub(crate) fn run_internal_command( span: Span::unknown() }, name_tag: Tag::unknown_anchor(command.name_span), + scope: Scope::empty(), } }; let mut result = converter.run(new_args.with_input(vec![tagged_contents]), &context.registry); @@ -120,6 +125,15 @@ pub(crate) fn run_internal_command( FilesystemShell::with_location(location, context.registry().clone()), )); } + CommandAction::AddAlias(name, args, commands) => { + context.add_commands(vec![ + per_item_command(AliasCommand::new( + name, + args, + commands, + )) + ]); + } CommandAction::PreviousShell => { context.shell_manager.prev(); } diff --git a/crates/nu-cli/src/commands/classified/pipeline.rs b/crates/nu-cli/src/commands/classified/pipeline.rs index 5d94b6c616..505e584dcb 100644 --- a/crates/nu-cli/src/commands/classified/pipeline.rs +++ b/crates/nu-cli/src/commands/classified/pipeline.rs @@ -5,11 +5,13 @@ use crate::context::Context; use crate::stream::InputStream; use nu_errors::ShellError; use nu_protocol::hir::{ClassifiedCommand, ClassifiedPipeline}; +use nu_protocol::Scope; pub(crate) async fn run_pipeline( pipeline: ClassifiedPipeline, ctx: &mut Context, mut input: Option, + scope: &Scope, ) -> Result, ShellError> { let mut iter = pipeline.commands.list.into_iter().peekable(); @@ -22,18 +24,22 @@ pub(crate) async fn run_pipeline( return Err(ShellError::unimplemented("Dynamic commands")) } - (Some(ClassifiedCommand::Expr(expr)), _) => run_expression_block(*expr, ctx, input)?, + (Some(ClassifiedCommand::Expr(expr)), _) => { + run_expression_block(*expr, ctx, input, scope)? + } (Some(ClassifiedCommand::Error(err)), _) => return Err(err.into()), (_, Some(ClassifiedCommand::Error(err))) => return Err(err.clone().into()), - (Some(ClassifiedCommand::Internal(left)), _) => run_internal_command(left, ctx, input)?, + (Some(ClassifiedCommand::Internal(left)), _) => { + run_internal_command(left, ctx, input, scope)? + } (Some(ClassifiedCommand::External(left)), None) => { - run_external_command(left, ctx, input, true).await? + run_external_command(left, ctx, input, scope, true).await? } (Some(ClassifiedCommand::External(left)), _) => { - run_external_command(left, ctx, input, false).await? + run_external_command(left, ctx, input, scope, false).await? } (None, _) => break, diff --git a/crates/nu-cli/src/commands/command.rs b/crates/nu-cli/src/commands/command.rs index 4995e9178e..1b743e5201 100644 --- a/crates/nu-cli/src/commands/command.rs +++ b/crates/nu-cli/src/commands/command.rs @@ -16,15 +16,27 @@ use std::sync::atomic::AtomicBool; pub struct UnevaluatedCallInfo { pub args: hir::Call, pub name_tag: Tag, + pub scope: Scope, } impl UnevaluatedCallInfo { - pub fn evaluate( + pub fn evaluate(self, registry: &CommandRegistry) -> Result { + let args = evaluate_args(&self.args, registry, &self.scope)?; + + Ok(CallInfo { + args, + name_tag: self.name_tag, + }) + } + + pub fn evaluate_with_new_it( self, registry: &CommandRegistry, - scope: &Scope, + it: &Value, ) -> Result { - let args = evaluate_args(&self.args, registry, scope)?; + let mut scope = self.scope.clone(); + scope = scope.set_it(it.clone()); + let args = evaluate_args(&self.args, registry, &scope)?; Ok(CallInfo { args, @@ -113,7 +125,7 @@ impl CommandArgs { let ctrl_c = self.ctrl_c.clone(); let shell_manager = self.shell_manager.clone(); let input = self.input; - let call_info = self.call_info.evaluate(registry, &Scope::empty())?; + let call_info = self.call_info.evaluate(registry)?; Ok(EvaluatedWholeStreamCommandArgs::new( host, @@ -133,7 +145,12 @@ impl CommandArgs { let ctrl_c = self.ctrl_c.clone(); let shell_manager = self.shell_manager.clone(); let input = self.input; - let call_info = self.call_info.evaluate(registry, scope)?; + let call_info = UnevaluatedCallInfo { + name_tag: self.call_info.name_tag, + args: self.call_info.args, + scope: scope.clone(), + }; + let call_info = call_info.evaluate(registry)?; Ok(EvaluatedWholeStreamCommandArgs::new( host, @@ -515,10 +532,16 @@ impl Command { .input .values .map(move |x| { - let call_info = raw_args - .clone() - .call_info - .evaluate(®istry, &Scope::it_value(x.clone())); + let call_info = UnevaluatedCallInfo { + args: raw_args.call_info.args.clone(), + name_tag: raw_args.call_info.name_tag.clone(), + scope: raw_args.call_info.scope.clone().set_it(x.clone()), + } + .evaluate(®istry); + // let call_info = raw_args + // .clone() + // .call_info + // .evaluate(®istry, &Scope::it_value(x.clone())); match call_info { Ok(call_info) => match command.run(&call_info, ®istry, &raw_args, x) { @@ -576,7 +599,7 @@ impl WholeStreamCommand for FnFilterCommand { let result = input.values.map(move |it| { let registry = registry.clone(); - let call_info = match call_info.clone().evaluate(®istry, &Scope::it_value(it)) { + let call_info = match call_info.clone().evaluate_with_new_it(®istry, &it) { Err(err) => return OutputStream::from(vec![Err(err)]).values, Ok(args) => args, }; diff --git a/crates/nu-cli/src/commands/each.rs b/crates/nu-cli/src/commands/each.rs index cb8f0669be..61d2b2de04 100644 --- a/crates/nu-cli/src/commands/each.rs +++ b/crates/nu-cli/src/commands/each.rs @@ -5,7 +5,8 @@ use crate::context::CommandRegistry; use crate::prelude::*; use nu_errors::ShellError; use nu_protocol::{ - hir::ClassifiedPipeline, CallInfo, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value, + hir::ClassifiedPipeline, CallInfo, ReturnSuccess, Scope, Signature, SyntaxShape, UntaggedValue, + Value, }; pub struct Each; @@ -52,6 +53,7 @@ impl PerItemCommand for Each { ClassifiedPipeline::new(block.clone(), None), &mut context, Some(input_stream), + &Scope::empty(), ).await; match result { diff --git a/crates/nu-cli/src/commands/enter.rs b/crates/nu-cli/src/commands/enter.rs index 4b056bfaaa..e68d12d2de 100644 --- a/crates/nu-cli/src/commands/enter.rs +++ b/crates/nu-cli/src/commands/enter.rs @@ -104,6 +104,7 @@ impl PerItemCommand for Enter { span: Span::unknown() }, name_tag: raw_args.call_info.name_tag, + scope: raw_args.call_info.scope.clone() }, }; let mut result = converter.run( diff --git a/crates/nu-cli/src/commands/plugin.rs b/crates/nu-cli/src/commands/plugin.rs index 6f5fb19e62..ab5284dc50 100644 --- a/crates/nu-cli/src/commands/plugin.rs +++ b/crates/nu-cli/src/commands/plugin.rs @@ -3,7 +3,7 @@ use crate::prelude::*; use derive_new::new; use log::trace; use nu_errors::ShellError; -use nu_protocol::{Primitive, ReturnSuccess, ReturnValue, Scope, Signature, UntaggedValue, Value}; +use nu_protocol::{Primitive, ReturnSuccess, ReturnValue, Signature, UntaggedValue, Value}; use serde::{self, Deserialize, Serialize}; use std::io::prelude::*; use std::io::BufReader; @@ -71,10 +71,13 @@ pub fn filter_plugin( ) -> Result { trace!("filter_plugin :: {}", path); - let args = args.evaluate_once_with_scope( - registry, - &Scope::it_value(UntaggedValue::string("$it").into_untagged_value()), - )?; + let scope = &args + .call_info + .scope + .clone() + .set_it(UntaggedValue::string("$it").into_untagged_value()); + + let args = args.evaluate_once_with_scope(registry, &scope)?; let mut child = std::process::Command::new(path) .stdin(std::process::Stdio::piped()) diff --git a/crates/nu-cli/src/commands/run_alias.rs b/crates/nu-cli/src/commands/run_alias.rs new file mode 100644 index 0000000000..14be5d635e --- /dev/null +++ b/crates/nu-cli/src/commands/run_alias.rs @@ -0,0 +1,93 @@ +use crate::commands::classified::pipeline::run_pipeline; +use crate::prelude::*; + +use derive_new::new; +use nu_errors::ShellError; +use nu_protocol::{ + hir::ClassifiedPipeline, hir::Commands, CallInfo, ReturnSuccess, Scope, Signature, SyntaxShape, + Value, +}; + +#[derive(new)] +pub struct AliasCommand { + name: String, + args: Vec, + block: Commands, +} + +impl PerItemCommand for AliasCommand { + fn name(&self) -> &str { + &self.name + } + + fn signature(&self) -> Signature { + let mut alias = Signature::build(&self.name); + + for arg in &self.args { + alias = alias.required(arg, SyntaxShape::Any, ""); + } + + alias + } + + fn usage(&self) -> &str { + "" + } + + fn run( + &self, + call_info: &CallInfo, + registry: &CommandRegistry, + raw_args: &RawCommandArgs, + input: Value, + ) -> Result { + let tag = call_info.name_tag.clone(); + let call_info = call_info.clone(); + let registry = registry.clone(); + let raw_args = raw_args.clone(); + let block = self.block.clone(); + + let mut scope = Scope::empty(); + if let Some(positional) = &call_info.args.positional { + for (pos, arg) in positional.iter().enumerate() { + scope = scope.set_var(self.args[pos].to_string(), arg.clone()); + } + } + + let stream = async_stream! { + let mut context = Context::from_raw(&raw_args, ®istry); + let input_stream = async_stream! { + yield Ok(input.clone()) + }.to_input_stream(); + + let result = run_pipeline( + ClassifiedPipeline::new(block.clone(), None), + &mut context, + Some(input_stream), + &scope + ).await; + + match result { + Ok(Some(v)) => { + let results: Vec = v.collect().await; + + for result in results { + yield Ok(ReturnSuccess::Value(result)); + } + } + Ok(None) => { + yield Err(ShellError::labeled_error( + "Expected a block", + "each needs a block", + tag, + )); + } + Err(e) => { + yield Err(e); + } + } + }; + + Ok(stream.to_output_stream()) + } +} diff --git a/crates/nu-cli/src/commands/save.rs b/crates/nu-cli/src/commands/save.rs index 895344e1ab..b3d6a67b6e 100644 --- a/crates/nu-cli/src/commands/save.rs +++ b/crates/nu-cli/src/commands/save.rs @@ -1,7 +1,7 @@ use crate::commands::{UnevaluatedCallInfo, WholeStreamCommand}; use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; +use nu_protocol::{Primitive, ReturnSuccess, Scope, Signature, SyntaxShape, UntaggedValue, Value}; use nu_source::Tagged; use std::path::{Path, PathBuf}; @@ -235,6 +235,7 @@ fn save( span: Span::unknown() }, name_tag: raw_args.call_info.name_tag, + scope: Scope::empty(), // FIXME? } }; let mut result = converter.run(new_args.with_input(input), ®istry); diff --git a/crates/nu-cli/src/commands/where_.rs b/crates/nu-cli/src/commands/where_.rs index 14567831ac..d518d642b2 100644 --- a/crates/nu-cli/src/commands/where_.rs +++ b/crates/nu-cli/src/commands/where_.rs @@ -68,6 +68,7 @@ impl PerItemCommand for Where { } }; + //FIXME: should we use the scope that's brought in as well? let condition = evaluate_baseline_expr(&condition, registry, &Scope::new(input.clone()))?; let stream = match condition.as_bool() { diff --git a/crates/nu-cli/src/context.rs b/crates/nu-cli/src/context.rs index f73ec98b2d..ffae364eeb 100644 --- a/crates/nu-cli/src/context.rs +++ b/crates/nu-cli/src/context.rs @@ -7,7 +7,7 @@ use crate::stream::{InputStream, OutputStream}; use indexmap::IndexMap; use nu_errors::ShellError; use nu_parser::SignatureRegistry; -use nu_protocol::{hir, Signature}; +use nu_protocol::{hir, Scope, Signature}; use nu_source::{Tag, Text}; use parking_lot::Mutex; use std::error::Error; @@ -196,22 +196,33 @@ impl Context { command: Arc, name_tag: Tag, args: hir::Call, + scope: &Scope, input: InputStream, ) -> OutputStream { - let command_args = self.command_args(args, input, name_tag); + let command_args = self.command_args(args, input, name_tag, scope); command.run(command_args, self.registry()) } - fn call_info(&self, args: hir::Call, name_tag: Tag) -> UnevaluatedCallInfo { - UnevaluatedCallInfo { args, name_tag } + fn call_info(&self, args: hir::Call, name_tag: Tag, scope: &Scope) -> UnevaluatedCallInfo { + UnevaluatedCallInfo { + args, + name_tag, + scope: scope.clone(), + } } - fn command_args(&self, args: hir::Call, input: InputStream, name_tag: Tag) -> CommandArgs { + fn command_args( + &self, + args: hir::Call, + input: InputStream, + name_tag: Tag, + scope: &Scope, + ) -> CommandArgs { CommandArgs { host: self.host.clone(), ctrl_c: self.ctrl_c.clone(), shell_manager: self.shell_manager.clone(), - call_info: self.call_info(args, name_tag), + call_info: self.call_info(args, name_tag, scope), input, } } diff --git a/crates/nu-cli/tests/commands/alias.rs b/crates/nu-cli/tests/commands/alias.rs new file mode 100644 index 0000000000..170c8aac18 --- /dev/null +++ b/crates/nu-cli/tests/commands/alias.rs @@ -0,0 +1,17 @@ +use nu_test_support::nu; +use nu_test_support::playground::Playground; + +#[test] +fn alias_args_work() { + Playground::setup("append_test_1", |dirs, _| { + let actual = nu!( + cwd: dirs.root(), + r#" + alias double_echo [a b] {echo $a $b} + double_echo 1 2 | to-json + "# + ); + + assert_eq!(actual, "[1,2]"); + }) +} diff --git a/crates/nu-cli/tests/commands/mod.rs b/crates/nu-cli/tests/commands/mod.rs index ec78cf622c..a1821b4b07 100644 --- a/crates/nu-cli/tests/commands/mod.rs +++ b/crates/nu-cli/tests/commands/mod.rs @@ -1,3 +1,4 @@ +mod alias; mod append; mod calc; mod cd; diff --git a/crates/nu-protocol/src/return_value.rs b/crates/nu-protocol/src/return_value.rs index 7d22c42096..a3aecbcf5c 100644 --- a/crates/nu-protocol/src/return_value.rs +++ b/crates/nu-protocol/src/return_value.rs @@ -1,3 +1,4 @@ +use crate::hir::Commands; use crate::value::Value; use nu_errors::ShellError; use nu_source::{b, DebugDocBuilder, PrettyDebug}; @@ -20,6 +21,8 @@ pub enum CommandAction { EnterValueShell(Value), /// Enter the help shell, which allows exploring the help system EnterHelpShell(Value), + /// Enter the help shell, which allows exploring the help system + AddAlias(String, Vec, Commands), /// Go to the previous shell in the shell ring buffer PreviousShell, /// Go to the next shell in the shell ring buffer @@ -41,6 +44,7 @@ impl PrettyDebug for CommandAction { CommandAction::EnterShell(s) => b::typed("enter shell", b::description(s)), CommandAction::EnterValueShell(v) => b::typed("enter value shell", v.pretty()), CommandAction::EnterHelpShell(v) => b::typed("enter help shell", v.pretty()), + CommandAction::AddAlias(..) => b::description("add alias"), CommandAction::PreviousShell => b::description("previous shell"), CommandAction::NextShell => b::description("next shell"), CommandAction::LeaveShell => b::description("leave shell"), diff --git a/crates/nu-protocol/src/value/evaluate.rs b/crates/nu-protocol/src/value/evaluate.rs index 2bb3abb660..04a458880b 100644 --- a/crates/nu-protocol/src/value/evaluate.rs +++ b/crates/nu-protocol/src/value/evaluate.rs @@ -1,11 +1,12 @@ use crate::value::{Primitive, UntaggedValue, Value}; use indexmap::IndexMap; +use serde::{Deserialize, Serialize}; use std::fmt::Debug; /// An evaluation scope. Scopes map variable names to Values and aid in evaluating blocks and expressions. /// Additionally, holds the value for the special $it variable, a variable used to refer to the value passing /// through the pipeline at that moment -#[derive(Debug)] +#[derive(Deserialize, Serialize, Debug, Clone)] pub struct Scope { pub it: Value, pub vars: IndexMap, @@ -37,4 +38,20 @@ impl Scope { vars: IndexMap::new(), } } + + pub fn set_it(self, value: Value) -> Scope { + Scope { + it: value, + vars: self.vars, + } + } + + pub fn set_var(self, name: String, value: Value) -> Scope { + let mut new_vars = self.vars.clone(); + new_vars.insert(name, value); + Scope { + it: self.it, + vars: new_vars, + } + } }