mirror of
https://github.com/nushell/nushell
synced 2025-01-13 21:55:07 +00:00
Require static path for source-env
(#6526)
This commit is contained in:
parent
1adebefc3e
commit
e76b3d61de
4 changed files with 60 additions and 118 deletions
132
crates/nu-command/src/env/source_env.rs
vendored
132
crates/nu-command/src/env/source_env.rs
vendored
|
@ -1,11 +1,10 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use nu_engine::{eval_block, find_in_dirs_env, redirect_env, CallExt};
|
||||
use nu_parser::parse;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack, StateWorkingSet};
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, CliError, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Value,
|
||||
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Value,
|
||||
};
|
||||
|
||||
/// Source a file for environment variables.
|
||||
|
@ -40,96 +39,47 @@ impl Command for SourceEnv {
|
|||
) -> Result<PipelineData, ShellError> {
|
||||
let source_filename: Spanned<String> = call.req(engine_state, caller_stack, 0)?;
|
||||
|
||||
if let Some(path) = find_in_dirs_env(&source_filename.item, engine_state, caller_stack)? {
|
||||
if let Ok(content) = std::fs::read_to_string(&path) {
|
||||
let mut parent = PathBuf::from(&path);
|
||||
parent.pop();
|
||||
// Note: this hidden positional is the block_id that corresponded to the 0th position
|
||||
// it is put here by the parser
|
||||
let block_id: i64 = call.req(engine_state, caller_stack, 1)?;
|
||||
|
||||
let mut new_engine_state = engine_state.clone();
|
||||
|
||||
let (block, delta) = {
|
||||
let mut working_set = StateWorkingSet::new(&new_engine_state);
|
||||
|
||||
// Set the currently parsed directory
|
||||
working_set.currently_parsed_cwd = Some(parent.clone());
|
||||
|
||||
let (block, err) = parse(&mut working_set, None, content.as_bytes(), true, &[]);
|
||||
|
||||
if let Some(err) = err {
|
||||
// Because the error span points at new_engine_state, we must create the error message now
|
||||
let msg = format!(
|
||||
r#"Found this parser error: {:?}"#,
|
||||
CliError(&err, &working_set)
|
||||
);
|
||||
|
||||
return Err(ShellError::GenericError(
|
||||
"Failed to parse content".to_string(),
|
||||
"cannot parse this file".to_string(),
|
||||
Some(source_filename.span),
|
||||
Some(msg),
|
||||
vec![],
|
||||
));
|
||||
} else {
|
||||
(block, working_set.render())
|
||||
}
|
||||
};
|
||||
|
||||
// Merge parser changes to a temporary engine state
|
||||
new_engine_state.merge_delta(delta)?;
|
||||
|
||||
// Set the currently evaluated directory
|
||||
let file_pwd = Value::String {
|
||||
val: parent.to_string_lossy().to_string(),
|
||||
span: call.head,
|
||||
};
|
||||
|
||||
caller_stack.add_env_var("FILE_PWD".to_string(), file_pwd);
|
||||
|
||||
// Evaluate the parsed file's block
|
||||
let mut callee_stack = caller_stack.gather_captures(&block.captures);
|
||||
|
||||
let result = eval_block(
|
||||
&new_engine_state,
|
||||
&mut callee_stack,
|
||||
&block,
|
||||
input,
|
||||
true,
|
||||
true,
|
||||
);
|
||||
|
||||
let result = if let Err(err) = result {
|
||||
// Because the error span points at new_engine_state, we must create the error message now
|
||||
let working_set = StateWorkingSet::new(&new_engine_state);
|
||||
|
||||
let msg = format!(
|
||||
r#"Found this shell error: {:?}"#,
|
||||
CliError(&err, &working_set)
|
||||
);
|
||||
|
||||
Err(ShellError::GenericError(
|
||||
"Failed to evaluate content".to_string(),
|
||||
"cannot evaluate this file".to_string(),
|
||||
Some(source_filename.span),
|
||||
Some(msg),
|
||||
vec![],
|
||||
))
|
||||
} else {
|
||||
result
|
||||
};
|
||||
|
||||
// Merge the block's environment to the current stack
|
||||
redirect_env(engine_state, caller_stack, &callee_stack);
|
||||
|
||||
// Remove the file-relative PWD
|
||||
caller_stack.remove_env_var(engine_state, "FILE_PWD");
|
||||
|
||||
result
|
||||
} else {
|
||||
Err(ShellError::FileNotFound(source_filename.span))
|
||||
}
|
||||
// Set the currently evaluated directory (file-relative PWD)
|
||||
let mut parent = if let Some(path) =
|
||||
find_in_dirs_env(&source_filename.item, engine_state, caller_stack)?
|
||||
{
|
||||
PathBuf::from(&path)
|
||||
} else {
|
||||
Err(ShellError::FileNotFound(source_filename.span))
|
||||
}
|
||||
return Err(ShellError::FileNotFound(source_filename.span));
|
||||
};
|
||||
parent.pop();
|
||||
|
||||
let file_pwd = Value::String {
|
||||
val: parent.to_string_lossy().to_string(),
|
||||
span: call.head,
|
||||
};
|
||||
|
||||
caller_stack.add_env_var("FILE_PWD".to_string(), file_pwd);
|
||||
|
||||
// Evaluate the block
|
||||
let block = engine_state.get_block(block_id as usize).clone();
|
||||
let mut callee_stack = caller_stack.gather_captures(&block.captures);
|
||||
|
||||
let result = eval_block(
|
||||
engine_state,
|
||||
&mut callee_stack,
|
||||
&block,
|
||||
input,
|
||||
call.redirect_stdout,
|
||||
call.redirect_stderr,
|
||||
);
|
||||
|
||||
// Merge the block's environment to the current stack
|
||||
redirect_env(engine_state, caller_stack, &callee_stack);
|
||||
|
||||
// Remove the file-relative PWD
|
||||
caller_stack.remove_env_var(engine_state, "FILE_PWD");
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
|
|
|
@ -143,6 +143,7 @@ fn sources_unicode_file_in_non_utf8_dir() {
|
|||
// How do I create non-UTF-8 path???
|
||||
}
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn can_source_dynamic_path() {
|
||||
Playground::setup("can_source_dynamic_path", |dirs, sandbox| {
|
||||
|
@ -269,39 +270,26 @@ fn source_env_dont_cd_overlay() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn source_env_nice_parse_error() {
|
||||
Playground::setup("source_env_nice_parse_error", |dirs, sandbox| {
|
||||
fn source_env_is_scoped() {
|
||||
Playground::setup("source_env_is_scoped", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContentToBeTrimmed(
|
||||
"spam.nu",
|
||||
r#"
|
||||
let x
|
||||
"#,
|
||||
def foo [] { 'foo' }
|
||||
alias bar = 'bar'
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let inp = &[r#"source-env spam.nu"#];
|
||||
let inp = &[r#"source-env spam.nu"#, r#"foo"#];
|
||||
|
||||
let actual = nu!(cwd: dirs.test(), pipeline(&inp.join("; ")));
|
||||
|
||||
assert!(actual.err.contains("cannot parse this file"));
|
||||
assert!(actual.err.contains("───"));
|
||||
})
|
||||
}
|
||||
assert!(actual.err.contains("did you mean"));
|
||||
|
||||
#[test]
|
||||
fn source_env_nice_shell_error() {
|
||||
Playground::setup("source_env_nice_shell_error", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![FileWithContentToBeTrimmed(
|
||||
"spam.nu",
|
||||
r#"
|
||||
let-env FILE_PWD = 'foo'
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let inp = &[r#"source-env spam.nu"#];
|
||||
let inp = &[r#"source-env spam.nu"#, r#"bar"#];
|
||||
|
||||
let actual = nu!(cwd: dirs.test(), pipeline(&inp.join("; ")));
|
||||
|
||||
assert!(actual.err.contains("cannot evaluate this file"));
|
||||
assert!(actual.err.contains("───"));
|
||||
assert!(actual.err.contains("did you mean"));
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2902,8 +2902,10 @@ pub fn parse_source(
|
|||
let mut error = None;
|
||||
let name = working_set.get_span_contents(spans[0]);
|
||||
|
||||
if name == b"source" {
|
||||
if let Some(decl_id) = working_set.find_decl(b"source", &Type::Any) {
|
||||
if name == b"source" || name == b"source-env" {
|
||||
let scoped = name == b"source-env";
|
||||
|
||||
if let Some(decl_id) = working_set.find_decl(name, &Type::Any) {
|
||||
let cwd = working_set.get_cwd();
|
||||
|
||||
// Is this the right call to be using here?
|
||||
|
@ -2958,7 +2960,7 @@ pub fn parse_source(
|
|||
working_set,
|
||||
path.file_name().and_then(|x| x.to_str()),
|
||||
&contents,
|
||||
false,
|
||||
scoped,
|
||||
expand_aliases_denylist,
|
||||
);
|
||||
|
||||
|
@ -2983,7 +2985,7 @@ pub fn parse_source(
|
|||
|
||||
let mut call_with_block = call;
|
||||
|
||||
// Adding this expression to the positional creates a syntax highlighting error
|
||||
// FIXME: Adding this expression to the positional creates a syntax highlighting error
|
||||
// after writing `source example.nu`
|
||||
call_with_block.add_positional(Expression {
|
||||
expr: Expr::Int(block_id as i64),
|
||||
|
|
|
@ -4815,7 +4815,9 @@ pub fn parse_builtin_commands(
|
|||
(pipeline, err)
|
||||
}
|
||||
b"overlay" => parse_overlay(working_set, &lite_command.parts, expand_aliases_denylist),
|
||||
b"source" => parse_source(working_set, &lite_command.parts, expand_aliases_denylist),
|
||||
b"source" | b"source-env" => {
|
||||
parse_source(working_set, &lite_command.parts, expand_aliases_denylist)
|
||||
}
|
||||
b"export" => parse_export_in_block(working_set, lite_command, expand_aliases_denylist),
|
||||
b"hide" => parse_hide(working_set, &lite_command.parts, expand_aliases_denylist),
|
||||
#[cfg(feature = "plugin")]
|
||||
|
|
Loading…
Reference in a new issue