From 1ec2ec72b5d772db8759905e03cb34b6409f5067 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Wed, 18 Mar 2020 07:13:38 +1300 Subject: [PATCH] Add automatic change directory (#1496) * Allow automatic cd in cli mode * Set correct priority for auto-cd and add test --- crates/nu-cli/src/cli.rs | 32 ++++++++++++++++++++--- tests/shell/pipeline/commands/external.rs | 19 ++++++++++++++ 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/crates/nu-cli/src/cli.rs b/crates/nu-cli/src/cli.rs index e823809b7..0a07c33e4 100644 --- a/crates/nu-cli/src/cli.rs +++ b/crates/nu-cli/src/cli.rs @@ -10,7 +10,10 @@ use crate::prelude::*; use futures_codec::FramedRead; use nu_errors::ShellError; -use nu_parser::{ClassifiedPipeline, PipelineShape, SpannedToken, TokensIterator}; +use nu_parser::{ + ClassifiedCommand, ClassifiedPipeline, ExternalCommand, PipelineShape, SpannedToken, + TokensIterator, +}; use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value}; use log::{debug, log_enabled, trace}; @@ -366,7 +369,7 @@ pub async fn run_pipeline_standalone( redirect_stdin: bool, context: &mut Context, ) -> Result<(), Box> { - let line = process_line(Ok(pipeline), context, redirect_stdin).await; + let line = process_line(Ok(pipeline), context, redirect_stdin, false).await; match line { LineResult::Success(line) => { @@ -515,7 +518,7 @@ pub async fn cli() -> Result<(), Box> { initial_command = None; } - let line = process_line(readline, &mut context, false).await; + let line = process_line(readline, &mut context, false, true).await; // Check the config to see if we need to update the path // TODO: make sure config is cached so we don't path this load every call @@ -598,6 +601,7 @@ async fn process_line( readline: Result, ctx: &mut Context, redirect_stdin: bool, + cli_mode: bool, ) -> LineResult { match &readline { Ok(line) if line.trim() == "" => LineResult::Success(line.clone()), @@ -616,12 +620,32 @@ async fn process_line( debug!("=== Parsed ==="); debug!("{:#?}", result); - let pipeline = classify_pipeline(&result, ctx, &Text::from(line)); + let pipeline = classify_pipeline(&result, &ctx, &Text::from(line)); if let Some(failure) = pipeline.failed { return LineResult::Error(line.to_string(), failure.into()); } + // There's a special case to check before we process the pipeline: + // If we're giving a path by itself + // ...and it's not a command in the path + // ...and it doesn't have any arguments + // ...and we're in the CLI + // ...then change to this directory + if cli_mode && pipeline.commands.list.len() == 1 { + if let ClassifiedCommand::External(ExternalCommand { + ref name, ref args, .. + }) = pipeline.commands.list[0] + { + if dunce::canonicalize(name).is_ok() + && which::which(name).is_err() + && args.list.is_empty() + { + ctx.shell_manager.set_path(name.to_string()); + return LineResult::Success(line.to_string()); + } + } + } let input_stream = if redirect_stdin { let file = futures::io::AllowStdIo::new(std::io::stdin()); let stream = FramedRead::new(file, MaybeTextCodec).map(|line| { diff --git a/tests/shell/pipeline/commands/external.rs b/tests/shell/pipeline/commands/external.rs index b324c55a2..0c464c079 100644 --- a/tests/shell/pipeline/commands/external.rs +++ b/tests/shell/pipeline/commands/external.rs @@ -20,6 +20,25 @@ fn shows_error_for_command_not_found() { assert!(actual.contains("Command not found")); } +#[test] +fn automatically_change_directory() { + use nu_test_support::playground::Playground; + + Playground::setup("cd_test_5_1", |dirs, sandbox| { + sandbox.within("autodir").mkdir("bar"); + + let actual = nu!( + cwd: dirs.test(), + r#" + autodir + pwd | echo $it + "# + ); + + assert!(actual.ends_with("autodir")); + }) +} + mod it_evaluation { use super::nu; use nu_test_support::fs::Stub::{EmptyFile, FileWithContent, FileWithContentToBeTrimmed};