diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index 0a4075c532..a4288c685c 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -106,6 +106,7 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState { bind_command! { Path, PathBasename, + PathSelf, PathDirname, PathExists, PathExpand, diff --git a/crates/nu-command/src/path/mod.rs b/crates/nu-command/src/path/mod.rs index 8a7b9a6f2c..4b6405c8d8 100644 --- a/crates/nu-command/src/path/mod.rs +++ b/crates/nu-command/src/path/mod.rs @@ -6,6 +6,7 @@ mod join; mod parse; pub mod path_; mod relative_to; +mod self_; mod split; mod r#type; @@ -18,6 +19,7 @@ pub use parse::SubCommand as PathParse; pub use path_::PathCommand as Path; pub use r#type::SubCommand as PathType; pub use relative_to::SubCommand as PathRelativeTo; +pub use self_::SubCommand as PathSelf; pub use split::SubCommand as PathSplit; use nu_protocol::{ShellError, Span, Value}; diff --git a/crates/nu-command/src/path/self_.rs b/crates/nu-command/src/path/self_.rs new file mode 100644 index 0000000000..242fbea7a8 --- /dev/null +++ b/crates/nu-command/src/path/self_.rs @@ -0,0 +1,129 @@ +use nu_engine::command_prelude::*; +use nu_path::expand_path_with; +use nu_protocol::engine::StateWorkingSet; + +#[derive(Clone)] +pub struct SubCommand; + +impl Command for SubCommand { + fn name(&self) -> &str { + "path self" + } + + fn signature(&self) -> Signature { + Signature::build("path self") + .input_output_type(Type::Nothing, Type::String) + .allow_variants_without_examples(true) + .optional( + "path", + SyntaxShape::Filepath, + "Path to get instead of the current file.", + ) + .category(Category::Path) + } + + fn description(&self) -> &str { + "Get the absolute path of the script or module containing this command at parse time." + } + + fn is_const(&self) -> bool { + true + } + + fn run( + &self, + _engine_state: &EngineState, + _stack: &mut Stack, + call: &Call, + _input: PipelineData, + ) -> Result { + Err(ShellError::GenericError { + error: "this command can only run during parse-time".into(), + msg: "can't run after parse-time".into(), + span: Some(call.head), + help: Some("try assigning this command's output to a const variable".into()), + inner: vec![], + }) + } + + fn run_const( + &self, + working_set: &StateWorkingSet, + call: &Call, + _input: PipelineData, + ) -> Result { + let path: Option = call.opt_const(working_set, 0)?; + let cwd = working_set.permanent_state.cwd(None)?; + let current_file = + working_set + .files + .top() + .ok_or_else(|| ShellError::FileNotFoundCustom { + msg: "Couldn't find current file".into(), + span: call.head, + })?; + + let out = if let Some(path) = path { + let dir = expand_path_with( + current_file + .parent() + .ok_or_else(|| ShellError::FileNotFoundCustom { + msg: "Couldn't find current file's parent.".into(), + span: call.head, + })?, + &cwd, + true, + ); + Value::string( + expand_path_with(path, dir, false).to_string_lossy(), + call.head, + ) + } else { + Value::string( + expand_path_with(current_file, &cwd, true).to_string_lossy(), + call.head, + ) + }; + + Ok(out.into_pipeline_data()) + } + + fn examples(&self) -> Vec { + vec![ + Example { + description: "Get the path of the current file", + example: r#"const current_file = path self"#, + result: None, + }, + Example { + description: "Get the path of the directory containing the current file", + example: r#"const current_file = path self ."#, + result: None, + }, + #[cfg(windows)] + Example { + description: "Get the absolute form of a path relative to the current file", + example: r#"const current_file = path self ..\foo"#, + result: None, + }, + #[cfg(not(windows))] + Example { + description: "Get the absolute form of a path relative to the current file", + example: r#"const current_file = path self ../foo"#, + result: None, + }, + ] + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_examples() { + use crate::test_examples; + + test_examples(SubCommand {}) + } +}