REFACTOR: remove the shell commands (#8415)

Related to #8368.

# Description
as planned in #8311, the `enter`, `shells`, `g`, `n` and `p` commands
have been re-implemented in pure-`nushell` in the standard library.
this PR removes the `rust` implementations of these commands.

- all the "shells" tests have been removed from
`crates/nu-commnand/tests/commands/` in
2cc6a82da6, except for the `exit` command
- `cd` does not use the `shells` feature in its source code anymore =>
that does not change its single-shell behaviour
- all the command implementations have been removed from
`crates/nu-command/src/shells/`, except for `exit.rs` => `mod.rs` has
been modified accordingly
- the `exit` command now does not compute any "shell" related things
- the `--now` option has been removed from `exit`, as it does not serve
any purpose without sub-shells

# User-Facing Changes
users may now not use `enter`, `shells`, `g`, `n` and `p`
now they would have to use the standard library to have access to
equivalent features, thanks to the `dirs.nu` module introduced by @bobhy
in #8368

# Tests + Formatting
- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
-  `toolkit test`
-  `toolkit test stdlib`

# After Submitting
the website will have to be regenerated to reflect the removed commands
👍
This commit is contained in:
Antoine Stevan 2023-05-13 19:40:11 +02:00 committed by GitHub
parent b4a1f0f003
commit bf86cd50a5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 49 additions and 853 deletions

View file

@ -277,12 +277,7 @@ pub fn create_default_context() -> EngineState {
// Shells // Shells
bind_command! { bind_command! {
Enter,
Exit, Exit,
GotoShell,
NextShell,
PrevShell,
Shells,
}; };
// Formats // Formats

View file

@ -1,5 +1,4 @@
use crate::filesystem::cd_query::query; use crate::filesystem::cd_query::query;
use crate::{get_current_shell, get_shells};
#[cfg(unix)] #[cfg(unix)]
use libc::gid_t; use libc::gid_t;
use nu_engine::{current_dir, CallExt}; use nu_engine::{current_dir, CallExt};
@ -164,23 +163,6 @@ impl Command for Cd {
val: path.clone(), val: path.clone(),
span, span,
}; };
let cwd = Value::string(cwd.to_string_lossy(), call.head);
let mut shells = get_shells(engine_state, stack, cwd);
let current_shell = get_current_shell(engine_state, stack);
shells[current_shell] = path_value.clone();
stack.add_env_var(
"NUSHELL_SHELLS".into(),
Value::List {
vals: shells,
span: call.head,
},
);
stack.add_env_var(
"NUSHELL_CURRENT_SHELL".into(),
Value::int(current_shell as i64, call.head),
);
if let Some(oldpwd) = stack.get_env_var(engine_state, "PWD") { if let Some(oldpwd) = stack.get_env_var(engine_state, "PWD") {
stack.add_env_var("OLDPWD".into(), oldpwd) stack.add_env_var("OLDPWD".into(), oldpwd)

View file

@ -1,103 +0,0 @@
use super::{get_current_shell, get_shells};
use nu_engine::{current_dir, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
/// Source a file for environment variables.
#[derive(Clone)]
pub struct Enter;
impl Command for Enter {
fn name(&self) -> &str {
"enter"
}
fn signature(&self) -> Signature {
Signature::build("enter")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required(
"path",
SyntaxShape::Filepath,
"the path to enter as a new shell",
)
.category(Category::Shells)
}
fn usage(&self) -> &str {
"Enters a new shell at the given path."
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let new_path: Value = call.req(engine_state, stack, 0)?;
let path_span = new_path.span()?;
let new_path = new_path.as_path()?;
let cwd = current_dir(engine_state, stack)?;
let new_path = nu_path::canonicalize_with(new_path, &cwd)?;
if !new_path.exists() {
return Err(ShellError::DirectoryNotFound(path_span, None));
}
if !new_path.is_dir() {
return Err(ShellError::DirectoryNotFoundCustom(
"not a directory".to_string(),
path_span,
));
}
let new_path = Value::string(new_path.to_string_lossy(), call.head);
let cwd = Value::string(cwd.to_string_lossy(), call.head);
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(current_shell as i64, call.head),
);
if current_shell + 1 > shells.len() {
shells.push(new_path.clone());
current_shell = shells.len();
} else {
shells.insert(current_shell + 1, new_path.clone());
current_shell += 1;
}
stack.add_env_var(
"NUSHELL_SHELLS".into(),
Value::List {
vals: shells,
span: call.head,
},
);
stack.add_env_var(
"NUSHELL_CURRENT_SHELL".into(),
Value::int(current_shell as i64, call.head),
);
stack.add_env_var("PWD".into(), new_path);
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Enter a new shell at path '../dir-foo'",
example: r#"enter ../dir-foo"#,
result: None,
}]
}
}

View file

@ -1,10 +1,7 @@
use super::{get_current_shell, get_last_shell, get_shells}; use nu_engine::CallExt;
use nu_engine::{current_dir, CallExt};
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type};
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
#[derive(Clone)] #[derive(Clone)]
pub struct Exit; pub struct Exit;
@ -22,11 +19,6 @@ impl Command for Exit {
SyntaxShape::Int, SyntaxShape::Int,
"Exit code to return immediately with", "Exit code to return immediately with",
) )
.switch(
"now",
"Exit out of all shells immediately (exiting Nu)",
Some('n'),
)
.category(Category::Shells) .category(Category::Shells)
} }
@ -51,66 +43,14 @@ impl Command for Exit {
std::process::exit(exit_code as i32); std::process::exit(exit_code as i32);
} }
if call.has_flag("now") { std::process::exit(0);
std::process::exit(0);
}
let cwd = current_dir(engine_state, stack)?;
let cwd = Value::string(cwd.to_string_lossy(), call.head);
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;
}
if shells.is_empty() {
std::process::exit(0);
} else {
let new_path = shells[current_shell].clone();
stack.add_env_var(
"NUSHELL_SHELLS".into(),
Value::List {
vals: shells,
span: call.head,
},
);
stack.add_env_var(
"NUSHELL_CURRENT_SHELL".into(),
Value::int(current_shell as i64, call.head),
);
stack.add_env_var(
"NUSHELL_LAST_SHELL".into(),
Value::int(last_shell as i64, call.head),
);
stack.add_env_var("PWD".into(), new_path);
Ok(PipelineData::empty())
}
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![Example {
Example { description: "Exit the current shell",
description: "Exit the current shell", example: "exit",
example: "exit", result: None,
result: None, }]
},
Example {
description: "Exit all shells (exiting Nu)",
example: "exit --now",
result: None,
},
]
} }
} }

View file

@ -1,97 +0,0 @@
use super::{list_shells, switch_shell, SwitchTo};
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
/// Source a file for environment variables.
#[derive(Clone)]
pub struct GotoShell;
impl Command for GotoShell {
fn name(&self) -> &str {
"g"
}
fn signature(&self) -> Signature {
Signature::build("g")
.input_output_types(vec![
(Type::Nothing, Type::Nothing),
(Type::Nothing, Type::Table(vec![])),
])
.optional(
"shell_number",
SyntaxShape::OneOf(vec![SyntaxShape::Int, SyntaxShape::String]),
"shell number to change to",
)
.category(Category::Shells)
}
fn usage(&self) -> &str {
"Switch to a given shell, or list all shells if no given shell number."
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let new_shell: Option<Value> = call.opt(engine_state, stack, 0)?;
match new_shell {
Some(shell_span) => match &shell_span {
Value::String { val, span } => {
if val == "-" {
switch_shell(engine_state, stack, call, *span, SwitchTo::Last)
} else {
Err(ShellError::TypeMismatch {
err_message: "int or '-'".into(),
span: *span,
})
}
}
Value::Int { val, span } => switch_shell(
engine_state,
stack,
call,
*span,
SwitchTo::Nth(*val as usize),
),
_ => Err(ShellError::TypeMismatch {
err_message: "int or '-'".into(),
span: call.head,
}),
},
None => list_shells(engine_state, stack, call.head),
}
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Lists all open shells",
example: r#"g"#,
result: None,
},
Example {
description: "Make two directories and enter new shells for them, use `g` to jump to the specific shell",
example: r#"mkdir foo bar; enter foo; enter ../bar; g 1"#,
result: None,
},
Example {
description: "Use `shells` to show all the opened shells and run `g 2` to jump to the third one",
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,
},
]
}
}

View file

@ -1,147 +1,3 @@
mod enter;
mod exit; mod exit;
mod g;
mod n;
mod p;
mod shells_;
pub use enter::Enter;
pub use exit::Exit; pub use exit::Exit;
pub use g::GotoShell;
pub use n::NextShell;
use nu_engine::current_dir;
use nu_protocol::ast::Call;
use nu_protocol::engine::{EngineState, Stack};
use nu_protocol::{IntoInterruptiblePipelineData, PipelineData, ShellError, Span, Value};
pub use p::PrevShell;
pub use shells_::Shells;
enum SwitchTo {
Next,
Prev,
Last,
Nth(usize),
}
pub fn get_shells(engine_state: &EngineState, stack: &mut Stack, cwd: Value) -> Vec<Value> {
let shells = stack.get_env_var(engine_state, "NUSHELL_SHELLS");
let shells = if let Some(v) = shells {
v.as_list()
.map(|x| x.to_vec())
.unwrap_or_else(|_| vec![cwd])
} else {
vec![cwd]
};
shells
}
pub fn get_current_shell(engine_state: &EngineState, stack: &mut Stack) -> usize {
let current_shell = stack.get_env_var(engine_state, "NUSHELL_CURRENT_SHELL");
if let Some(v) = current_shell {
v.as_integer().unwrap_or_default() as usize
} else {
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
}
}
fn switch_shell(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
span: Span,
switch_to: SwitchTo,
) -> Result<PipelineData, ShellError> {
let cwd = current_dir(engine_state, stack)?;
let cwd = Value::string(cwd.to_string_lossy(), call.head);
let shells = get_shells(engine_state, stack, cwd);
let current_shell = get_current_shell(engine_state, stack);
let new_shell = match switch_to {
SwitchTo::Next => {
let mut new_shell = current_shell + 1;
if new_shell == shells.len() {
new_shell = 0;
}
new_shell
}
SwitchTo::Prev => {
if current_shell == 0 {
shells.len() - 1
} else {
current_shell - 1
}
}
SwitchTo::Last => get_last_shell(engine_state, stack),
SwitchTo::Nth(n) => n,
};
let new_path = shells
.get(new_shell)
.ok_or(ShellError::NotFound { span })?
.to_owned();
stack.add_env_var(
"NUSHELL_SHELLS".into(),
Value::List {
vals: shells,
span: call.head,
},
);
stack.add_env_var(
"NUSHELL_CURRENT_SHELL".into(),
Value::int(new_shell as i64, call.head),
);
stack.add_env_var(
"NUSHELL_LAST_SHELL".into(),
Value::int(current_shell as i64, call.head),
);
stack.add_env_var("PWD".into(), new_path);
Ok(PipelineData::empty())
}
fn list_shells(
engine_state: &EngineState,
stack: &mut Stack,
span: Span,
) -> Result<PipelineData, ShellError> {
let cwd = current_dir(engine_state, stack)?;
let cwd = Value::String {
val: cwd.to_string_lossy().to_string(),
span,
};
let shells = get_shells(engine_state, stack, cwd);
let current_shell = get_current_shell(engine_state, stack);
Ok(shells
.into_iter()
.enumerate()
.map(move |(idx, val)| Value::Record {
cols: vec!["active".to_string(), "path".to_string()],
vals: vec![
Value::Bool {
val: idx == current_shell,
span,
},
val,
],
span,
})
.into_pipeline_data(None))
}

View file

@ -1,49 +0,0 @@
use super::{switch_shell, SwitchTo};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Type};
/// Source a file for environment variables.
#[derive(Clone)]
pub struct NextShell;
impl Command for NextShell {
fn name(&self) -> &str {
"n"
}
fn signature(&self) -> Signature {
Signature::build("n")
.category(Category::Shells)
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
}
fn usage(&self) -> &str {
"Switch to the next shell."
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
switch_shell(engine_state, stack, call, call.head, SwitchTo::Next)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Make two directories and enter new shells for them, use `n` to jump to the next shell",
example: r#"mkdir foo bar; enter foo; enter ../bar; n"#,
result: None,
},
Example {
description: "Run `n` several times and note the changes of current directory",
example: r#"n"#,
result: None,
},
]
}
}

View file

@ -1,49 +0,0 @@
use super::{switch_shell, SwitchTo};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Type};
/// Source a file for environment variables.
#[derive(Clone)]
pub struct PrevShell;
impl Command for PrevShell {
fn name(&self) -> &str {
"p"
}
fn signature(&self) -> Signature {
Signature::build("p")
.category(Category::Shells)
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
}
fn usage(&self) -> &str {
"Switch to the previous shell."
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
switch_shell(engine_state, stack, call, call.head, SwitchTo::Prev)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Make two directories and enter new shells for them, use `p` to jump to the previous shell",
example: r#"mkdir foo bar; enter foo; enter ../bar; p"#,
result: None,
},
Example {
description: "Run `p` several times and note the changes of current directory",
example: r#"p"#,
result: None,
},
]
}
}

View file

@ -1,49 +0,0 @@
use super::list_shells;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Type};
/// Source a file for environment variables.
#[derive(Clone)]
pub struct Shells;
impl Command for Shells {
fn name(&self) -> &str {
"shells"
}
fn signature(&self) -> Signature {
Signature::build("shells")
.category(Category::Shells)
.input_output_types(vec![(Type::Nothing, Type::Table(vec![]))])
}
fn usage(&self) -> &str {
"Lists all open shells."
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
list_shells(engine_state, stack, call.head)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Enter a new shell at parent path '..' and show all opened shells",
example: r#"enter ..; shells"#,
result: None,
},
Example {
description: "Show currently active shell",
example: r#"shells | where active == true"#,
result: None,
},
]
}
}

View file

@ -1,73 +0,0 @@
use nu_test_support::fs::{files_exist_at, Stub::EmptyFile};
use nu_test_support::nu;
use nu_test_support::playground::Playground;
use std::path::Path;
#[test]
fn knows_the_filesystems_entered() {
Playground::setup("enter_test_1", |dirs, sandbox| {
sandbox
.within("red_pill")
.with_files(vec![
EmptyFile("andres.nu"),
EmptyFile("jtnu"),
EmptyFile("yehuda.nu"),
])
.within("blue_pill")
.with_files(vec![
EmptyFile("bash.nxt"),
EmptyFile("korn.nxt"),
EmptyFile("powedsh.nxt"),
])
.mkdir("expected");
let red_pill_dir = dirs.test().join("red_pill");
let blue_pill_dir = dirs.test().join("blue_pill");
let expected = dirs.test().join("expected");
let expected_recycled = expected.join("recycled");
nu!(
cwd: dirs.test(),
"
enter expected
mkdir recycled
enter ../red_pill
mv jtnu ../expected
enter ../blue_pill
cp *.nxt ../expected/recycled
p
p
mv ../red_pill/yehuda.nu .
n
mv andres.nu ../expected/andres.nu
exit
cd ..
rm red_pill --recursive
exit
n
rm blue_pill --recursive
exit
"
);
assert!(!red_pill_dir.exists());
assert!(files_exist_at(
vec![
Path::new("andres.nu"),
Path::new("jtnu"),
Path::new("yehuda.nu"),
],
expected
));
assert!(!blue_pill_dir.exists());
assert!(files_exist_at(
vec![
Path::new("bash.nxt"),
Path::new("korn.nxt"),
Path::new("powedsh.nxt"),
],
expected_recycled
));
})
}

View file

@ -1,91 +0,0 @@
use nu_test_support::{nu, pipeline, playground::Playground};
#[test]
fn list_shells() {
let actual = nu!(
cwd: ".", pipeline(
r#"g | get path | length "#
));
assert_eq!(actual.out, "1");
}
#[test]
fn enter_shell() {
let actual = nu!(
cwd: ".", pipeline(
r#"g 0"#
));
assert!(actual.err.is_empty());
}
#[test]
fn enter_not_exist_shell() {
let actual = nu!(
cwd: ".", pipeline(
r#"g 1"#
));
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");
})
}

View file

@ -18,7 +18,6 @@ mod drop;
mod each; mod each;
mod echo; mod echo;
mod empty; mod empty;
mod enter;
mod error_make; mod error_make;
mod every; mod every;
#[cfg(not(windows))] #[cfg(not(windows))]
@ -30,7 +29,6 @@ mod first;
mod flatten; mod flatten;
mod for_; mod for_;
mod format; mod format;
mod g;
mod get; mod get;
mod glob; mod glob;
mod group_by; mod group_by;
@ -54,11 +52,9 @@ mod merge;
mod mkdir; mod mkdir;
mod move_; mod move_;
mod mut_; mod mut_;
mod n;
mod network; mod network;
mod nu_check; mod nu_check;
mod open; mod open;
mod p;
mod par_each; mod par_each;
mod parse; mod parse;
mod path; mod path;
@ -84,7 +80,6 @@ mod select;
mod semicolon; mod semicolon;
mod seq; mod seq;
mod seq_char; mod seq_char;
mod shells;
mod skip; mod skip;
mod sort; mod sort;
mod sort_by; mod sort_by;

View file

@ -1,31 +0,0 @@
use nu_test_support::{nu, pipeline, playground::Playground};
#[test]
fn switch_to_next_shell_1() {
Playground::setup("switch_to_next_shell_1", |dirs, sandbox| {
sandbox.mkdir("foo").mkdir("bar");
let actual = nu!(
cwd: dirs.test(),
pipeline(
r#"enter foo; enter ../bar; n; g | get active.0"#
));
assert_eq!(actual.out, "true");
})
}
#[test]
fn switch_to_next_shell_2() {
Playground::setup("switch_to_next_shell_2", |dirs, sandbox| {
sandbox.mkdir("foo").mkdir("bar");
let actual = nu!(
cwd: dirs.test(),
pipeline(
r#"enter foo; enter ../bar; n; n; g | get active.1"#
));
assert_eq!(actual.out, "true");
})
}

View file

@ -1,31 +0,0 @@
use nu_test_support::{nu, pipeline, playground::Playground};
#[test]
fn switch_to_prev_shell_1() {
Playground::setup("switch_to_next_shell_1", |dirs, sandbox| {
sandbox.mkdir("foo").mkdir("bar");
let actual = nu!(
cwd: dirs.test(),
pipeline(
r#"enter foo; enter ../bar; p; g | get active.1"#
));
assert_eq!(actual.out, "true");
})
}
#[test]
fn switch_to_prev_shell_2() {
Playground::setup("switch_to_next_shell_2", |dirs, sandbox| {
sandbox.mkdir("foo").mkdir("bar");
let actual = nu!(
cwd: dirs.test(),
pipeline(
r#"enter foo; enter ../bar; p; p; p; g | get active.2"#
));
assert_eq!(actual.out, "true");
})
}

View file

@ -1,31 +0,0 @@
use nu_test_support::{nu, pipeline, playground::Playground};
#[test]
fn list_shells_1() {
Playground::setup("list_shells_1", |dirs, sandbox| {
sandbox.mkdir("foo").mkdir("bar");
let actual = nu!(
cwd: dirs.test(),
pipeline(
r#"enter foo; enter ../bar; g| get active.2"#
));
assert_eq!(actual.out, "true");
})
}
#[test]
fn list_shells_2() {
Playground::setup("list_shells_2", |dirs, sandbox| {
sandbox.mkdir("foo").mkdir("bar");
let actual = nu!(
cwd: dirs.test(),
pipeline(
r#"enter foo; enter ../bar; shells| get active.2"#
));
assert_eq!(actual.out, "true");
})
}

View file

@ -27,6 +27,8 @@ export def-env "dirs add" [
_fetch 0 _fetch 0
} }
export alias enter = dirs add
# Advance to the next directory in the list or wrap to beginning. # Advance to the next directory in the list or wrap to beginning.
export def-env "dirs next" [ export def-env "dirs next" [
N:int = 1 # number of positions to move. N:int = 1 # number of positions to move.
@ -34,6 +36,8 @@ export def-env "dirs next" [
_fetch $N _fetch $N
} }
export alias n = dirs next
# Back up to the previous directory or wrap to the end. # Back up to the previous directory or wrap to the end.
export def-env "dirs prev" [ export def-env "dirs prev" [
N:int = 1 # number of positions to move. N:int = 1 # number of positions to move.
@ -41,6 +45,8 @@ export def-env "dirs prev" [
_fetch (-1 * $N) _fetch (-1 * $N)
} }
export alias p = dirs prev
# Drop the current directory from the list, if it's not the only one. # Drop the current directory from the list, if it's not the only one.
# PWD becomes the next working directory # PWD becomes the next working directory
export def-env "dirs drop" [] { export def-env "dirs drop" [] {
@ -54,6 +60,8 @@ export def-env "dirs drop" [] {
_fetch 0 _fetch 0
} }
export alias dexit = dirs drop
# Display current working directories. # Display current working directories.
export def-env "dirs show" [] { export def-env "dirs show" [] {
mut out = [] mut out = []
@ -67,6 +75,31 @@ export def-env "dirs show" [] {
$out $out
} }
export alias shells = dirs show
export def-env "dirs goto" [shell?: int] {
if $shell == null {
return (dirs show)
}
if $shell < 0 or $shell >= ($env.DIRS_LIST | length) {
let span = (metadata $shell | get span)
error make {
msg: $"(ansi red_bold)invalid_shell_index(ansi reset)"
label: {
text: $"`shell` should be between 0 and (($env.DIRS_LIST | length) - 1)"
start: $span.start
end: $span.end
}
}
}
let-env DIRS_POSITION = $shell
cd ($env.DIRS_LIST | get $env.DIRS_POSITION)
}
export alias g = dirs goto
# fetch item helper # fetch item helper
def-env _fetch [ def-env _fetch [
offset: int, # signed change to position offset: int, # signed change to position

View file

@ -88,6 +88,7 @@ pub fn load_standard_library(
// Conventionally, for a command implemented as `std foo`, the name added // Conventionally, for a command implemented as `std foo`, the name added
// is either `std foo` or bare `foo`, not some arbitrary rename. // is either `std foo` or bare `foo`, not some arbitrary rename.
#[rustfmt::skip]
let prelude = vec![ let prelude = vec![
("std help", "help"), ("std help", "help"),
("std help commands", "help commands"), ("std help commands", "help commands"),
@ -95,6 +96,13 @@ pub fn load_standard_library(
("std help modules", "help modules"), ("std help modules", "help modules"),
("std help externs", "help externs"), ("std help externs", "help externs"),
("std help operators", "help operators"), ("std help operators", "help operators"),
("enter", "enter"),
("shells", "shells"),
("g", "g"),
("n", "n"),
("p", "p"),
("dexit", "dexit"),
]; ];
let mut working_set = StateWorkingSet::new(engine_state); let mut working_set = StateWorkingSet::new(engine_state);

View file

@ -179,15 +179,6 @@ fn bad_short_flag() -> TestResult {
fail_test(r#"def foo3 [-l?:int] { $l }"#, "short flag") fail_test(r#"def foo3 [-l?:int] { $l }"#, "short flag")
} }
#[test]
fn alias_with_error_doesnt_panic() -> TestResult {
fail_test(
r#"alias s = shells
s ."#,
"extra positional",
)
}
#[test] #[test]
fn quotes_with_equals() -> TestResult { fn quotes_with_equals() -> TestResult {
run_test( run_test(