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
18 changed files with 49 additions and 853 deletions

View File

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

View File

@ -1,5 +1,4 @@
use crate::filesystem::cd_query::query;
use crate::{get_current_shell, get_shells};
#[cfg(unix)]
use libc::gid_t;
use nu_engine::{current_dir, CallExt};
@ -164,23 +163,6 @@ impl Command for Cd {
val: path.clone(),
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") {
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::{current_dir, CallExt};
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,
};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type};
#[derive(Clone)]
pub struct Exit;
@ -22,11 +19,6 @@ impl Command for Exit {
SyntaxShape::Int,
"Exit code to return immediately with",
)
.switch(
"now",
"Exit out of all shells immediately (exiting Nu)",
Some('n'),
)
.category(Category::Shells)
}
@ -51,66 +43,14 @@ impl Command for Exit {
std::process::exit(exit_code as i32);
}
if call.has_flag("now") {
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())
}
std::process::exit(0);
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Exit the current shell",
example: "exit",
result: None,
},
Example {
description: "Exit all shells (exiting Nu)",
example: "exit --now",
result: None,
},
]
vec![Example {
description: "Exit the current shell",
example: "exit",
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 g;
mod n;
mod p;
mod shells_;
pub use enter::Enter;
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,
},
]
}
}