diff --git a/crates/nu-cli/src/repl.rs b/crates/nu-cli/src/repl.rs index 38678f9f88..67e42b2bef 100644 --- a/crates/nu-cli/src/repl.rs +++ b/crates/nu-cli/src/repl.rs @@ -395,9 +395,23 @@ pub fn evaluate_repl( 0 }; + let last_shell = stack.get_env_var(engine_state, "NUSHELL_LAST_SHELL"); + let last_shell = if let Some(v) = last_shell { + v.as_integer().unwrap_or_default() as usize + } else { + 0 + }; + shells[current_shell] = Value::String { val: path, span }; stack.add_env_var("NUSHELL_SHELLS".into(), Value::List { vals: shells, span }); + stack.add_env_var( + "NUSHELL_LAST_SHELL".into(), + Value::Int { + val: last_shell as i64, + span, + }, + ); } else { trace!("eval source: {}", s); diff --git a/crates/nu-command/src/shells/enter.rs b/crates/nu-command/src/shells/enter.rs index 79dec79ef2..eb8651bc26 100644 --- a/crates/nu-command/src/shells/enter.rs +++ b/crates/nu-command/src/shells/enter.rs @@ -66,6 +66,14 @@ impl Command for Enter { let mut shells = get_shells(engine_state, stack, cwd); let mut current_shell = get_current_shell(engine_state, stack); + stack.add_env_var( + "NUSHELL_LAST_SHELL".into(), + Value::Int { + val: current_shell as i64, + span: call.head, + }, + ); + if current_shell + 1 > shells.len() { shells.push(new_path.clone()); current_shell = shells.len(); diff --git a/crates/nu-command/src/shells/exit.rs b/crates/nu-command/src/shells/exit.rs index dd429d4662..4e8cb3adf2 100644 --- a/crates/nu-command/src/shells/exit.rs +++ b/crates/nu-command/src/shells/exit.rs @@ -1,4 +1,4 @@ -use super::{get_current_shell, get_shells}; +use super::{get_current_shell, get_last_shell, get_shells}; use nu_engine::{current_dir, CallExt}; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; @@ -60,9 +60,14 @@ impl Command for Exit { let mut shells = get_shells(engine_state, stack, cwd); let mut current_shell = get_current_shell(engine_state, stack); + let mut last_shell = get_last_shell(engine_state, stack); shells.remove(current_shell); + if current_shell <= last_shell { + last_shell = 0; + } + if current_shell == shells.len() && !shells.is_empty() { current_shell -= 1; } @@ -86,6 +91,13 @@ impl Command for Exit { span: call.head, }, ); + stack.add_env_var( + "NUSHELL_LAST_SHELL".into(), + Value::Int { + val: last_shell as i64, + span: call.head, + }, + ); stack.add_env_var("PWD".into(), new_path); diff --git a/crates/nu-command/src/shells/g.rs b/crates/nu-command/src/shells/g.rs index 0b35184216..2972e07e4d 100644 --- a/crates/nu-command/src/shells/g.rs +++ b/crates/nu-command/src/shells/g.rs @@ -1,4 +1,4 @@ -use super::{get_current_shell, get_shells}; +use super::{get_current_shell, get_last_shell, get_shells}; use nu_engine::{current_dir, CallExt}; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; @@ -20,7 +20,7 @@ impl Command for GotoShell { Signature::build("g") .optional( "shell_number", - SyntaxShape::Int, + SyntaxShape::String, "shell number to change to", ) .category(Category::Shells) @@ -38,7 +38,7 @@ impl Command for GotoShell { _input: PipelineData, ) -> Result { let span = call.head; - let new_shell: Option> = call.opt(engine_state, stack, 0)?; + let new_shell: Option> = call.opt(engine_state, stack, 0)?; let cwd = current_dir(engine_state, stack)?; let cwd = Value::String { @@ -50,12 +50,22 @@ impl Command for GotoShell { match new_shell { Some(shell_span) => { - let new_path = if let Some(v) = shells.get(shell_span.item as usize) { - v.clone() + let index = if shell_span.item == "-" { + get_last_shell(engine_state, stack) } else { - return Err(ShellError::NotFound(shell_span.span)); + shell_span + .item + .parse::() + .map_err(|_| ShellError::NotFound(shell_span.span))? }; + let new_path = shells + .get(index) + .ok_or(ShellError::NotFound(shell_span.span))? + .to_owned(); + + let current_shell = get_current_shell(engine_state, stack); + stack.add_env_var( "NUSHELL_SHELLS".into(), Value::List { @@ -66,7 +76,14 @@ impl Command for GotoShell { stack.add_env_var( "NUSHELL_CURRENT_SHELL".into(), Value::Int { - val: shell_span.item, + val: index as i64, + span: call.head, + }, + ); + stack.add_env_var( + "NUSHELL_LAST_SHELL".into(), + Value::Int { + val: current_shell as i64, span: call.head, }, ); @@ -114,6 +131,11 @@ impl Command for GotoShell { example: r#"shells; g 2"#, result: None, }, + Example { + description: "Make two directories and enter new shells for them, use `g -` to jump to the last used shell", + example: r#"mkdir foo bar; enter foo; enter ../bar; g -"#, + result: None, + }, ] } } diff --git a/crates/nu-command/src/shells/mod.rs b/crates/nu-command/src/shells/mod.rs index ca4ff853cc..5d55e13321 100644 --- a/crates/nu-command/src/shells/mod.rs +++ b/crates/nu-command/src/shells/mod.rs @@ -34,3 +34,12 @@ pub fn get_current_shell(engine_state: &EngineState, stack: &mut Stack) -> usize 0 } } + +fn get_last_shell(engine_state: &EngineState, stack: &mut Stack) -> usize { + let last_shell = stack.get_env_var(engine_state, "NUSHELL_LAST_SHELL"); + if let Some(v) = last_shell { + v.as_integer().unwrap_or_default() as usize + } else { + 0 + } +} diff --git a/crates/nu-command/src/shells/n.rs b/crates/nu-command/src/shells/n.rs index 56916c306f..58b277baee 100644 --- a/crates/nu-command/src/shells/n.rs +++ b/crates/nu-command/src/shells/n.rs @@ -36,6 +36,13 @@ impl Command for NextShell { let shells = get_shells(engine_state, stack, cwd); let mut current_shell = get_current_shell(engine_state, stack); + stack.add_env_var( + "NUSHELL_LAST_SHELL".into(), + Value::Int { + val: current_shell as i64, + span: call.head, + }, + ); current_shell += 1; diff --git a/crates/nu-command/src/shells/p.rs b/crates/nu-command/src/shells/p.rs index 579507afc6..8c652eaa4b 100644 --- a/crates/nu-command/src/shells/p.rs +++ b/crates/nu-command/src/shells/p.rs @@ -36,6 +36,13 @@ impl Command for PrevShell { let shells = get_shells(engine_state, stack, cwd); let mut current_shell = get_current_shell(engine_state, stack); + stack.add_env_var( + "NUSHELL_LAST_SHELL".into(), + Value::Int { + val: current_shell as i64, + span: call.head, + }, + ); if current_shell == 0 { current_shell = shells.len() - 1; diff --git a/crates/nu-command/tests/commands/g.rs b/crates/nu-command/tests/commands/g.rs index 55596be656..aabf507ade 100644 --- a/crates/nu-command/tests/commands/g.rs +++ b/crates/nu-command/tests/commands/g.rs @@ -1,4 +1,4 @@ -use nu_test_support::{nu, pipeline}; +use nu_test_support::{nu, pipeline, playground::Playground}; #[test] fn list_shells() { @@ -29,3 +29,63 @@ fn enter_not_exist_shell() { assert!(actual.err.contains("Not found")); } + +#[test] +fn switch_to_last_used_shell_1() { + Playground::setup("switch_last_used_shell_1", |dirs, sandbox| { + sandbox.mkdir("foo").mkdir("bar"); + + let actual = nu!( + cwd: dirs.test(), + pipeline( + r#"enter foo; enter ../bar; g 0; g -;g | get active.2"# + )); + + assert_eq!(actual.out, "true"); + }) +} + +#[test] +fn switch_to_last_used_shell_2() { + Playground::setup("switch_last_used_shell_2", |dirs, sandbox| { + sandbox.mkdir("foo").mkdir("bar"); + + let actual = nu!( + cwd: dirs.test(), + pipeline( + r#"enter foo; enter ../bar; n; g -;g | get active.2"# + )); + + assert_eq!(actual.out, "true"); + }) +} + +#[test] +fn switch_to_last_used_shell_3() { + Playground::setup("switch_last_used_shell_3", |dirs, sandbox| { + sandbox.mkdir("foo").mkdir("bar"); + + let actual = nu!( + cwd: dirs.test(), + pipeline( + r#"enter foo; enter ../bar; p; g -;g | get active.2"# + )); + + assert_eq!(actual.out, "true"); + }) +} + +#[test] +fn switch_to_last_used_shell_4() { + Playground::setup("switch_last_used_shell_4", |dirs, sandbox| { + sandbox.mkdir("foo").mkdir("bar"); + + let actual = nu!( + cwd: dirs.test(), + pipeline( + r#"enter foo; enter ../bar; g 2; exit; g -;g | get active.0"# + )); + + assert_eq!(actual.out, "true"); + }) +}