mirror of
https://github.com/nushell/nushell.git
synced 2024-11-29 03:44:19 +01:00
For example, when running the following: crates/nu-cli/src nushell currently parses this as an external command. Before running the command, we check to see if it's a directory. If it is, we "auto cd" into that directory, otherwise we go through normal external processing. If we put a trailing slash on it though, shells typically interpret that as "user is explicitly referencing directory". So crates/nu-cli/src/ should not be interpreted as "run an external command". We intercept a trailing slash in the head position of a command in a pipeline as such, and inject a `cd` internal command.
This commit is contained in:
parent
80025ea684
commit
6f2ef05195
@ -223,7 +223,8 @@ fn create_default_starship_config() -> Option<toml::Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_default_context(
|
pub fn create_default_context(
|
||||||
syncer: &mut crate::env::environment_syncer::EnvironmentSyncer,
|
syncer: &mut crate::EnvironmentSyncer,
|
||||||
|
interactive: bool,
|
||||||
) -> Result<Context, Box<dyn Error>> {
|
) -> Result<Context, Box<dyn Error>> {
|
||||||
syncer.load_environment();
|
syncer.load_environment();
|
||||||
|
|
||||||
@ -348,7 +349,7 @@ pub fn create_default_context(
|
|||||||
whole_stream_command(FromIcs),
|
whole_stream_command(FromIcs),
|
||||||
whole_stream_command(FromVcf),
|
whole_stream_command(FromVcf),
|
||||||
// "Private" commands (not intended to be accessed directly)
|
// "Private" commands (not intended to be accessed directly)
|
||||||
whole_stream_command(RunExternalCommand),
|
whole_stream_command(RunExternalCommand { interactive }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
@ -378,7 +379,7 @@ pub async fn run_vec_of_pipelines(
|
|||||||
redirect_stdin: bool,
|
redirect_stdin: bool,
|
||||||
) -> Result<(), Box<dyn Error>> {
|
) -> Result<(), Box<dyn Error>> {
|
||||||
let mut syncer = crate::EnvironmentSyncer::new();
|
let mut syncer = crate::EnvironmentSyncer::new();
|
||||||
let mut context = crate::create_default_context(&mut syncer)?;
|
let mut context = create_default_context(&mut syncer, false)?;
|
||||||
|
|
||||||
let _ = crate::load_plugins(&mut context);
|
let _ = crate::load_plugins(&mut context);
|
||||||
|
|
||||||
@ -477,8 +478,8 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
|||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
const DEFAULT_COMPLETION_MODE: CompletionType = CompletionType::List;
|
const DEFAULT_COMPLETION_MODE: CompletionType = CompletionType::List;
|
||||||
|
|
||||||
let mut syncer = crate::env::environment_syncer::EnvironmentSyncer::new();
|
let mut syncer = crate::EnvironmentSyncer::new();
|
||||||
let mut context = create_default_context(&mut syncer)?;
|
let mut context = create_default_context(&mut syncer, true)?;
|
||||||
|
|
||||||
let _ = load_plugins(&mut context);
|
let _ = load_plugins(&mut context);
|
||||||
|
|
||||||
@ -911,7 +912,6 @@ pub fn print_err(err: ShellError, host: &dyn Host, source: &Text) {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
#[quickcheck]
|
#[quickcheck]
|
||||||
fn quickcheck_parse(data: String) -> bool {
|
fn quickcheck_parse(data: String) -> bool {
|
||||||
if let Ok(lite_block) = nu_parser::lite_parse(&data, 0) {
|
if let Ok(lite_block) = nu_parser::lite_parse(&data, 0) {
|
||||||
|
@ -1,7 +1,16 @@
|
|||||||
use crate::commands::WholeStreamCommand;
|
use crate::commands::WholeStreamCommand;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{Signature, SyntaxShape};
|
use nu_protocol::{Signature, SyntaxShape};
|
||||||
|
use nu_source::Tagged;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct CdArgs {
|
||||||
|
pub(crate) path: Option<Tagged<PathBuf>>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Cd;
|
pub struct Cd;
|
||||||
|
|
||||||
@ -27,12 +36,10 @@ impl WholeStreamCommand for Cd {
|
|||||||
args: CommandArgs,
|
args: CommandArgs,
|
||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
cd(args, registry)
|
args.process(registry, cd)?.run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cd(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
fn cd(args: CdArgs, context: RunnableContext) -> Result<OutputStream, ShellError> {
|
||||||
let shell_manager = args.shell_manager.clone();
|
context.shell_manager.cd(args, &context)
|
||||||
let args = args.evaluate_once(registry)?;
|
|
||||||
shell_manager.cd(args)
|
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,39 @@
|
|||||||
|
use crate::commands::cd::CdArgs;
|
||||||
use crate::commands::classified::external;
|
use crate::commands::classified::external;
|
||||||
use crate::commands::WholeStreamCommand;
|
use crate::commands::WholeStreamCommand;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::hir::{Expression, ExternalArgs, ExternalCommand, Literal, SpannedExpression};
|
use nu_protocol::hir::{Expression, ExternalArgs, ExternalCommand, Literal, SpannedExpression};
|
||||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape};
|
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape};
|
||||||
|
use nu_source::Tagged;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct RunExternalArgs {}
|
pub struct RunExternalArgs {}
|
||||||
|
|
||||||
#[derive(new)]
|
#[derive(new)]
|
||||||
pub struct RunExternalCommand;
|
pub struct RunExternalCommand {
|
||||||
|
/// Whether or not nushell is being used in an interactive context
|
||||||
|
pub(crate) interactive: bool,
|
||||||
|
}
|
||||||
|
|
||||||
fn spanned_expression_to_string(expr: SpannedExpression) -> String {
|
fn spanned_expression_to_string(expr: SpannedExpression) -> Result<String, ShellError> {
|
||||||
if let SpannedExpression {
|
if let SpannedExpression {
|
||||||
expr: Expression::Literal(Literal::String(s)),
|
expr: Expression::Literal(Literal::String(s)),
|
||||||
..
|
..
|
||||||
} = expr
|
} = expr
|
||||||
{
|
{
|
||||||
s
|
Ok(s)
|
||||||
} else {
|
} else {
|
||||||
"notacommand!!!".to_string()
|
Err(ShellError::labeled_error(
|
||||||
|
"Expected string for command name",
|
||||||
|
"expected string",
|
||||||
|
expr.span,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +55,7 @@ impl WholeStreamCommand for RunExternalCommand {
|
|||||||
args: CommandArgs,
|
args: CommandArgs,
|
||||||
registry: &CommandRegistry,
|
registry: &CommandRegistry,
|
||||||
) -> Result<OutputStream, ShellError> {
|
) -> Result<OutputStream, ShellError> {
|
||||||
let positionals = args.call_info.args.positional.ok_or_else(|| {
|
let positionals = args.call_info.args.positional.clone().ok_or_else(|| {
|
||||||
ShellError::untagged_runtime_error("positional arguments unexpectedly empty")
|
ShellError::untagged_runtime_error("positional arguments unexpectedly empty")
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@ -53,49 +63,89 @@ impl WholeStreamCommand for RunExternalCommand {
|
|||||||
|
|
||||||
let name = positionals
|
let name = positionals
|
||||||
.next()
|
.next()
|
||||||
.map(spanned_expression_to_string)
|
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
ShellError::untagged_runtime_error(
|
ShellError::untagged_runtime_error("run_external called with no arguments")
|
||||||
"run_external unexpectedly missing external name positional arg",
|
})
|
||||||
)
|
.and_then(spanned_expression_to_string)?;
|
||||||
})?;
|
|
||||||
|
|
||||||
let command = ExternalCommand {
|
let mut external_context = {
|
||||||
name,
|
#[cfg(windows)]
|
||||||
name_tag: args.call_info.name_tag.clone(),
|
{
|
||||||
args: ExternalArgs {
|
Context {
|
||||||
list: positionals.collect(),
|
registry: registry.clone(),
|
||||||
span: args.call_info.args.span,
|
host: args.host.clone(),
|
||||||
},
|
shell_manager: args.shell_manager.clone(),
|
||||||
|
ctrl_c: args.ctrl_c.clone(),
|
||||||
|
current_errors: Arc::new(Mutex::new(vec![])),
|
||||||
|
windows_drives_previous_cwd: Arc::new(Mutex::new(
|
||||||
|
std::collections::HashMap::new(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
{
|
||||||
|
Context {
|
||||||
|
registry: registry.clone(),
|
||||||
|
host: args.host.clone(),
|
||||||
|
shell_manager: args.shell_manager.clone(),
|
||||||
|
ctrl_c: args.ctrl_c.clone(),
|
||||||
|
current_errors: Arc::new(Mutex::new(vec![])),
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut external_context;
|
let is_interactive = self.interactive;
|
||||||
#[cfg(windows)]
|
|
||||||
{
|
|
||||||
external_context = Context {
|
|
||||||
registry: registry.clone(),
|
|
||||||
host: args.host.clone(),
|
|
||||||
shell_manager: args.shell_manager.clone(),
|
|
||||||
ctrl_c: args.ctrl_c.clone(),
|
|
||||||
current_errors: Arc::new(Mutex::new(vec![])),
|
|
||||||
windows_drives_previous_cwd: Arc::new(Mutex::new(std::collections::HashMap::new())),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
{
|
|
||||||
external_context = Context {
|
|
||||||
registry: registry.clone(),
|
|
||||||
host: args.host.clone(),
|
|
||||||
shell_manager: args.shell_manager.clone(),
|
|
||||||
ctrl_c: args.ctrl_c.clone(),
|
|
||||||
current_errors: Arc::new(Mutex::new(vec![])),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
let scope = args.call_info.scope.clone();
|
|
||||||
|
|
||||||
let is_last = args.call_info.args.is_last;
|
|
||||||
let input = args.input;
|
|
||||||
let stream = async_stream! {
|
let stream = async_stream! {
|
||||||
|
let command = ExternalCommand {
|
||||||
|
name,
|
||||||
|
name_tag: args.call_info.name_tag.clone(),
|
||||||
|
args: ExternalArgs {
|
||||||
|
list: positionals.collect(),
|
||||||
|
span: args.call_info.args.span,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// If we're in interactive mode, we will "auto cd". That is, instead of interpreting
|
||||||
|
// this as an external command, we will see it as a path and `cd` into it.
|
||||||
|
if is_interactive {
|
||||||
|
if let Some(path) = maybe_autocd_dir(&command, &mut external_context).await {
|
||||||
|
let cd_args = CdArgs {
|
||||||
|
path: Some(Tagged {
|
||||||
|
item: PathBuf::from(path),
|
||||||
|
tag: args.call_info.name_tag.clone(),
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let context = RunnableContext {
|
||||||
|
input: InputStream::empty(),
|
||||||
|
shell_manager: external_context.shell_manager.clone(),
|
||||||
|
host: external_context.host.clone(),
|
||||||
|
ctrl_c: external_context.ctrl_c.clone(),
|
||||||
|
registry: external_context.registry.clone(),
|
||||||
|
name: args.call_info.name_tag.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = external_context.shell_manager.cd(cd_args, &context);
|
||||||
|
match result {
|
||||||
|
Ok(mut stream) => {
|
||||||
|
while let Some(value) = stream.next().await {
|
||||||
|
yield value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
yield Err(e);
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let scope = args.call_info.scope.clone();
|
||||||
|
let is_last = args.call_info.args.is_last;
|
||||||
|
let input = args.input;
|
||||||
let result = external::run_external_command(
|
let result = external::run_external_command(
|
||||||
command,
|
command,
|
||||||
&mut external_context,
|
&mut external_context,
|
||||||
@ -120,3 +170,54 @@ impl WholeStreamCommand for RunExternalCommand {
|
|||||||
Ok(stream.to_output_stream())
|
Ok(stream.to_output_stream())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
async fn maybe_autocd_dir<'a>(cmd: &ExternalCommand, ctx: &mut Context) -> Option<String> {
|
||||||
|
// We will "auto cd" if
|
||||||
|
// - the command name ends in a path separator, or
|
||||||
|
// - it's not a command on the path and no arguments were given.
|
||||||
|
let name = &cmd.name;
|
||||||
|
let path_name = if name.ends_with(std::path::MAIN_SEPARATOR)
|
||||||
|
|| (cmd.args.is_empty()
|
||||||
|
&& PathBuf::from(name).is_dir()
|
||||||
|
&& dunce::canonicalize(name).is_ok()
|
||||||
|
&& ichwh::which(&name).await.unwrap_or(None).is_none())
|
||||||
|
{
|
||||||
|
Some(name)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
path_name.map(|name| {
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
if name.ends_with(':') {
|
||||||
|
// This looks like a drive shortcut. We need to a) switch drives and b) go back to the previous directory we were viewing on that drive
|
||||||
|
// But first, we need to save where we are now
|
||||||
|
let current_path = ctx.shell_manager.path();
|
||||||
|
|
||||||
|
let split_path: Vec<_> = current_path.split(':').collect();
|
||||||
|
if split_path.len() > 1 {
|
||||||
|
ctx.windows_drives_previous_cwd
|
||||||
|
.lock()
|
||||||
|
.insert(split_path[0].to_string(), current_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = name.to_uppercase();
|
||||||
|
let new_drive: Vec<_> = name.split(':').collect();
|
||||||
|
|
||||||
|
if let Some(val) = ctx.windows_drives_previous_cwd.lock().get(new_drive[0]) {
|
||||||
|
val.to_string()
|
||||||
|
} else {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
name.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
{
|
||||||
|
name.to_string()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::commands::cd::CdArgs;
|
||||||
use crate::commands::command::EvaluatedWholeStreamCommandArgs;
|
use crate::commands::command::EvaluatedWholeStreamCommandArgs;
|
||||||
use crate::commands::cp::CopyArgs;
|
use crate::commands::cp::CopyArgs;
|
||||||
use crate::commands::ls::LsArgs;
|
use crate::commands::ls::LsArgs;
|
||||||
@ -10,9 +11,7 @@ use crate::prelude::*;
|
|||||||
use crate::shell::completer::NuCompleter;
|
use crate::shell::completer::NuCompleter;
|
||||||
use crate::shell::shell::Shell;
|
use crate::shell::shell::Shell;
|
||||||
use crate::utils::FileStructure;
|
use crate::utils::FileStructure;
|
||||||
use nu_errors::ShellError;
|
|
||||||
use nu_parser::expand_ndots;
|
|
||||||
use nu_protocol::{Primitive, ReturnSuccess, UntaggedValue};
|
|
||||||
use rustyline::completion::FilenameCompleter;
|
use rustyline::completion::FilenameCompleter;
|
||||||
use rustyline::hint::{Hinter, HistoryHinter};
|
use rustyline::hint::{Hinter, HistoryHinter};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@ -21,6 +20,11 @@ use std::path::{Component, Path, PathBuf};
|
|||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
||||||
|
use nu_errors::ShellError;
|
||||||
|
use nu_parser::expand_ndots;
|
||||||
|
use nu_protocol::{Primitive, ReturnSuccess, UntaggedValue};
|
||||||
|
use nu_source::Tagged;
|
||||||
|
|
||||||
pub struct FilesystemShell {
|
pub struct FilesystemShell {
|
||||||
pub(crate) path: String,
|
pub(crate) path: String,
|
||||||
pub(crate) last_path: String,
|
pub(crate) last_path: String,
|
||||||
@ -171,21 +175,20 @@ impl Shell for FilesystemShell {
|
|||||||
Ok(stream.interruptible(ctrl_c).to_output_stream())
|
Ok(stream.interruptible(ctrl_c).to_output_stream())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result<OutputStream, ShellError> {
|
fn cd(&self, args: CdArgs, name: Tag) -> Result<OutputStream, ShellError> {
|
||||||
let path = match args.nth(0) {
|
let path = match args.path {
|
||||||
None => match dirs::home_dir() {
|
None => match dirs::home_dir() {
|
||||||
Some(o) => o,
|
Some(o) => o,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ShellError::labeled_error(
|
return Err(ShellError::labeled_error(
|
||||||
"Cannot change to home directory",
|
"Cannot change to home directory",
|
||||||
"cannot go to home",
|
"cannot go to home",
|
||||||
&args.call_info.name_tag,
|
&name,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Some(v) => {
|
Some(v) => {
|
||||||
let target = v.as_path()?;
|
let Tagged { item: target, tag } = v;
|
||||||
|
|
||||||
if target == Path::new("-") {
|
if target == Path::new("-") {
|
||||||
PathBuf::from(&self.last_path)
|
PathBuf::from(&self.last_path)
|
||||||
} else {
|
} else {
|
||||||
@ -193,7 +196,7 @@ impl Shell for FilesystemShell {
|
|||||||
ShellError::labeled_error(
|
ShellError::labeled_error(
|
||||||
"Cannot change to directory",
|
"Cannot change to directory",
|
||||||
"directory not found",
|
"directory not found",
|
||||||
&v.tag,
|
&tag,
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@ -201,7 +204,7 @@ impl Shell for FilesystemShell {
|
|||||||
return Err(ShellError::labeled_error(
|
return Err(ShellError::labeled_error(
|
||||||
"Cannot change to directory",
|
"Cannot change to directory",
|
||||||
"is not a directory",
|
"is not a directory",
|
||||||
&v.tag,
|
&tag,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,7 +219,7 @@ impl Shell for FilesystemShell {
|
|||||||
ShellError::labeled_error(
|
ShellError::labeled_error(
|
||||||
"Cannot change to directory",
|
"Cannot change to directory",
|
||||||
format!("cannot stat ({})", e),
|
format!("cannot stat ({})", e),
|
||||||
&v.tag,
|
&tag,
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@ -224,7 +227,7 @@ impl Shell for FilesystemShell {
|
|||||||
return Err(ShellError::labeled_error(
|
return Err(ShellError::labeled_error(
|
||||||
"Cannot change to directory",
|
"Cannot change to directory",
|
||||||
"permission denied",
|
"permission denied",
|
||||||
&v.tag,
|
&tag,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::commands::cd::CdArgs;
|
||||||
use crate::commands::command::EvaluatedWholeStreamCommandArgs;
|
use crate::commands::command::EvaluatedWholeStreamCommandArgs;
|
||||||
use crate::commands::cp::CopyArgs;
|
use crate::commands::cp::CopyArgs;
|
||||||
use crate::commands::ls::LsArgs;
|
use crate::commands::ls::LsArgs;
|
||||||
@ -7,12 +8,15 @@ use crate::commands::rm::RemoveArgs;
|
|||||||
use crate::data::command_dict;
|
use crate::data::command_dict;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::shell::shell::Shell;
|
use crate::shell::shell::Shell;
|
||||||
|
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Primitive, ReturnSuccess, ShellTypeName, TaggedDictBuilder, UntaggedValue, Value,
|
Primitive, ReturnSuccess, ShellTypeName, TaggedDictBuilder, UntaggedValue, Value,
|
||||||
};
|
};
|
||||||
use std::ffi::OsStr;
|
use nu_source::Tagged;
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct HelpShell {
|
pub struct HelpShell {
|
||||||
@ -149,12 +153,11 @@ impl Shell for HelpShell {
|
|||||||
Ok(output.into())
|
Ok(output.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result<OutputStream, ShellError> {
|
fn cd(&self, args: CdArgs, _name: Tag) -> Result<OutputStream, ShellError> {
|
||||||
let path = match args.nth(0) {
|
let path = match args.path {
|
||||||
None => "/".to_string(),
|
None => "/".to_string(),
|
||||||
Some(v) => {
|
Some(v) => {
|
||||||
let target = v.as_path()?;
|
let Tagged { item: target, .. } = v;
|
||||||
|
|
||||||
let mut cwd = PathBuf::from(&self.path);
|
let mut cwd = PathBuf::from(&self.path);
|
||||||
|
|
||||||
if target == PathBuf::from("..") {
|
if target == PathBuf::from("..") {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::commands::cd::CdArgs;
|
||||||
use crate::commands::command::EvaluatedWholeStreamCommandArgs;
|
use crate::commands::command::EvaluatedWholeStreamCommandArgs;
|
||||||
use crate::commands::cp::CopyArgs;
|
use crate::commands::cp::CopyArgs;
|
||||||
use crate::commands::ls::LsArgs;
|
use crate::commands::ls::LsArgs;
|
||||||
@ -18,7 +19,7 @@ pub trait Shell: std::fmt::Debug {
|
|||||||
args: LsArgs,
|
args: LsArgs,
|
||||||
context: &RunnablePerItemContext,
|
context: &RunnablePerItemContext,
|
||||||
) -> Result<OutputStream, ShellError>;
|
) -> Result<OutputStream, ShellError>;
|
||||||
fn cd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result<OutputStream, ShellError>;
|
fn cd(&self, args: CdArgs, name: Tag) -> Result<OutputStream, ShellError>;
|
||||||
fn cp(&self, args: CopyArgs, name: Tag, path: &str) -> Result<OutputStream, ShellError>;
|
fn cp(&self, args: CopyArgs, name: Tag, path: &str) -> Result<OutputStream, ShellError>;
|
||||||
fn mkdir(&self, args: MkdirArgs, name: Tag, path: &str) -> Result<OutputStream, ShellError>;
|
fn mkdir(&self, args: MkdirArgs, name: Tag, path: &str) -> Result<OutputStream, ShellError>;
|
||||||
fn mv(&self, args: MoveArgs, name: Tag, path: &str) -> Result<OutputStream, ShellError>;
|
fn mv(&self, args: MoveArgs, name: Tag, path: &str) -> Result<OutputStream, ShellError>;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::commands::cd::CdArgs;
|
||||||
use crate::commands::command::{EvaluatedWholeStreamCommandArgs, RunnablePerItemContext};
|
use crate::commands::command::{EvaluatedWholeStreamCommandArgs, RunnablePerItemContext};
|
||||||
use crate::commands::cp::CopyArgs;
|
use crate::commands::cp::CopyArgs;
|
||||||
use crate::commands::ls::LsArgs;
|
use crate::commands::ls::LsArgs;
|
||||||
@ -141,10 +142,10 @@ impl ShellManager {
|
|||||||
env[self.current_shell()].ls(args, context)
|
env[self.current_shell()].ls(args, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result<OutputStream, ShellError> {
|
pub fn cd(&self, args: CdArgs, context: &RunnableContext) -> Result<OutputStream, ShellError> {
|
||||||
let env = self.shells.lock();
|
let env = self.shells.lock();
|
||||||
|
|
||||||
env[self.current_shell()].cd(args)
|
env[self.current_shell()].cd(args, context.name.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cp(
|
pub fn cp(
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::commands::cd::CdArgs;
|
||||||
use crate::commands::command::EvaluatedWholeStreamCommandArgs;
|
use crate::commands::command::EvaluatedWholeStreamCommandArgs;
|
||||||
use crate::commands::cp::CopyArgs;
|
use crate::commands::cp::CopyArgs;
|
||||||
use crate::commands::ls::LsArgs;
|
use crate::commands::ls::LsArgs;
|
||||||
@ -7,11 +8,14 @@ use crate::commands::rm::RemoveArgs;
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::shell::shell::Shell;
|
use crate::shell::shell::Shell;
|
||||||
use crate::utils::ValueStructure;
|
use crate::utils::ValueStructure;
|
||||||
use nu_errors::ShellError;
|
|
||||||
use nu_protocol::{ReturnSuccess, ShellTypeName, UntaggedValue, Value};
|
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use nu_errors::ShellError;
|
||||||
|
use nu_protocol::{ReturnSuccess, ShellTypeName, UntaggedValue, Value};
|
||||||
|
use nu_source::Tagged;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ValueShell {
|
pub struct ValueShell {
|
||||||
pub(crate) path: String,
|
pub(crate) path: String,
|
||||||
@ -127,19 +131,18 @@ impl Shell for ValueShell {
|
|||||||
Ok(output.into())
|
Ok(output.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result<OutputStream, ShellError> {
|
fn cd(&self, args: CdArgs, name: Tag) -> Result<OutputStream, ShellError> {
|
||||||
let destination = args.nth(0);
|
let destination = args.path;
|
||||||
|
|
||||||
let path = match destination {
|
let path = match destination {
|
||||||
None => "/".to_string(),
|
None => "/".to_string(),
|
||||||
Some(v) => {
|
Some(ref v) => {
|
||||||
let target = v.as_path()?;
|
let Tagged { item: target, .. } = v;
|
||||||
|
|
||||||
let mut cwd = PathBuf::from(&self.path);
|
let mut cwd = PathBuf::from(&self.path);
|
||||||
|
|
||||||
if target == PathBuf::from("..") {
|
if target == &PathBuf::from("..") {
|
||||||
cwd.pop();
|
cwd.pop();
|
||||||
} else if target == PathBuf::from("-") {
|
} else if target == &PathBuf::from("-") {
|
||||||
cwd = PathBuf::from(&self.last_path);
|
cwd = PathBuf::from(&self.last_path);
|
||||||
} else {
|
} else {
|
||||||
match target.to_str() {
|
match target.to_str() {
|
||||||
@ -169,7 +172,7 @@ impl Shell for ValueShell {
|
|||||||
return Err(ShellError::labeled_error(
|
return Err(ShellError::labeled_error(
|
||||||
"Can not change to path inside",
|
"Can not change to path inside",
|
||||||
"No such path exists",
|
"No such path exists",
|
||||||
&args.call_info.name_tag,
|
&name,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ fn automatically_change_directory() {
|
|||||||
use nu_test_support::playground::Playground;
|
use nu_test_support::playground::Playground;
|
||||||
|
|
||||||
Playground::setup("cd_test_5_1", |dirs, sandbox| {
|
Playground::setup("cd_test_5_1", |dirs, sandbox| {
|
||||||
sandbox.within("autodir").mkdir("bar");
|
sandbox.mkdir("autodir");
|
||||||
|
|
||||||
let actual = nu!(
|
let actual = nu!(
|
||||||
cwd: dirs.test(),
|
cwd: dirs.test(),
|
||||||
@ -39,6 +39,25 @@ fn automatically_change_directory() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn automatically_change_directory_with_trailing_slash_and_same_name_as_command() {
|
||||||
|
use nu_test_support::playground::Playground;
|
||||||
|
|
||||||
|
Playground::setup("cd_test_5_1", |dirs, sandbox| {
|
||||||
|
sandbox.mkdir("cd");
|
||||||
|
|
||||||
|
let actual = nu!(
|
||||||
|
cwd: dirs.test(),
|
||||||
|
r#"
|
||||||
|
cd/
|
||||||
|
pwd | echo $it
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(actual.ends_with("cd"));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
mod it_evaluation {
|
mod it_evaluation {
|
||||||
use super::nu;
|
use super::nu;
|
||||||
use nu_test_support::fs::Stub::{EmptyFile, FileWithContent, FileWithContentToBeTrimmed};
|
use nu_test_support::fs::Stub::{EmptyFile, FileWithContent, FileWithContentToBeTrimmed};
|
||||||
|
Loading…
Reference in New Issue
Block a user