Allow NU_LIBS_DIR and friends to be const (#8538)

This commit is contained in:
StevenDoesStuffs 2023-04-05 11:56:48 -05:00 committed by GitHub
parent d18cf19a3f
commit 1134c2f16c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 279 additions and 136 deletions

View file

@ -6,7 +6,7 @@ use nu_protocol::{
PipelineData, Span, Type, Value, PipelineData, Span, Type, Value,
}; };
use reedline::Suggestion; use reedline::Suggestion;
use std::sync::Arc; use std::{collections::HashMap, sync::Arc};
use super::completer::map_value_completions; use super::completer::map_value_completions;
@ -66,7 +66,7 @@ impl Completer for CustomCompletion {
], ],
redirect_stdout: true, redirect_stdout: true,
redirect_stderr: true, redirect_stderr: true,
parser_info: vec![], parser_info: HashMap::new(),
}, },
PipelineData::empty(), PipelineData::empty(),
); );

View file

@ -1,4 +1,4 @@
use nu_engine::{eval_block, find_in_dirs_env, redirect_env, CallExt}; use nu_engine::{eval_block, find_in_dirs_env, get_dirs_var_from_call, redirect_env, CallExt};
use nu_parser::trim_quotes_str; use nu_parser::trim_quotes_str;
use nu_protocol::ast::{Call, Expr}; use nu_protocol::ast::{Call, Expr};
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
@ -66,7 +66,8 @@ impl Command for OverlayUse {
let mut name_arg: Spanned<String> = call.req(engine_state, caller_stack, 0)?; let mut name_arg: Spanned<String> = call.req(engine_state, caller_stack, 0)?;
name_arg.item = trim_quotes_str(&name_arg.item).to_string(); name_arg.item = trim_quotes_str(&name_arg.item).to_string();
let maybe_origin_module_id = if let Some(overlay_expr) = call.parser_info_nth(0) { let maybe_origin_module_id =
if let Some(overlay_expr) = call.get_parser_info("overlay_expr") {
if let Expr::Overlay(module_id) = overlay_expr.expr { if let Expr::Overlay(module_id) = overlay_expr.expr {
module_id module_id
} else { } else {
@ -113,7 +114,12 @@ impl Command for OverlayUse {
// Evaluate the export-env block (if any) and keep its environment // Evaluate the export-env block (if any) and keep its environment
if let Some(block_id) = module.env_block { if let Some(block_id) = module.env_block {
let maybe_path = find_in_dirs_env(&name_arg.item, engine_state, caller_stack)?; let maybe_path = find_in_dirs_env(
&name_arg.item,
engine_state,
caller_stack,
get_dirs_var_from_call(call),
)?;
let block = engine_state.get_block(block_id); let block = engine_state.get_block(block_id);
let mut callee_stack = caller_stack.gather_captures(&block.captures); let mut callee_stack = caller_stack.gather_captures(&block.captures);

View file

@ -1,4 +1,4 @@
use nu_engine::{eval_block, find_in_dirs_env, redirect_env}; use nu_engine::{eval_block, find_in_dirs_env, get_dirs_var_from_call, redirect_env};
use nu_protocol::ast::{Call, Expr, Expression}; use nu_protocol::ast::{Call, Expr, Expression};
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
@ -48,7 +48,7 @@ impl Command for Use {
let import_pattern = if let Some(Expression { let import_pattern = if let Some(Expression {
expr: Expr::ImportPattern(pat), expr: Expr::ImportPattern(pat),
.. ..
}) = call.parser_info_nth(0) }) = call.get_parser_info("import_pattern")
{ {
pat pat
} else { } else {
@ -72,9 +72,12 @@ impl Command for Use {
let module_arg_str = String::from_utf8_lossy( let module_arg_str = String::from_utf8_lossy(
engine_state.get_span_contents(&import_pattern.head.span), engine_state.get_span_contents(&import_pattern.head.span),
); );
let maybe_parent = if let Some(path) = let maybe_parent = if let Some(path) = find_in_dirs_env(
find_in_dirs_env(&module_arg_str, engine_state, caller_stack)? &module_arg_str,
{ engine_state,
caller_stack,
get_dirs_var_from_call(call),
)? {
path.parent().map(|p| p.to_path_buf()).or(None) path.parent().map(|p| p.to_path_buf()).or(None)
} else { } else {
None None

View file

@ -45,7 +45,7 @@ impl Command for Source {
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
// Note: this hidden positional is the block_id that corresponded to the 0th position // Note: this hidden positional is the block_id that corresponded to the 0th position
// it is put here by the parser // it is put here by the parser
let block_id: i64 = call.req_parser_info(engine_state, stack, 0)?; let block_id: i64 = call.req_parser_info(engine_state, stack, "block_id")?;
let block = engine_state.get_block(block_id as usize).clone(); let block = engine_state.get_block(block_id as usize).clone();
eval_block_with_early_return( eval_block_with_early_return(

View file

@ -1,6 +1,8 @@
use std::path::PathBuf; use std::path::PathBuf;
use nu_engine::{eval_block_with_early_return, find_in_dirs_env, redirect_env, CallExt}; use nu_engine::{
eval_block_with_early_return, find_in_dirs_env, get_dirs_var_from_call, redirect_env, 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::{
@ -42,12 +44,15 @@ impl Command for SourceEnv {
// Note: this hidden positional is the block_id that corresponded to the 0th position // Note: this hidden positional is the block_id that corresponded to the 0th position
// it is put here by the parser // it is put here by the parser
let block_id: i64 = call.req_parser_info(engine_state, caller_stack, 0)?; let block_id: i64 = call.req_parser_info(engine_state, caller_stack, "block_id")?;
// Set the currently evaluated directory (file-relative PWD) // Set the currently evaluated directory (file-relative PWD)
let mut parent = if let Some(path) = let mut parent = if let Some(path) = find_in_dirs_env(
find_in_dirs_env(&source_filename.item, engine_state, caller_stack)? &source_filename.item,
{ engine_state,
caller_stack,
get_dirs_var_from_call(call),
)? {
PathBuf::from(&path) PathBuf::from(&path)
} else { } else {
return Err(ShellError::FileNotFound(source_filename.span)); return Err(ShellError::FileNotFound(source_filename.span));

View file

@ -1,4 +1,4 @@
use nu_engine::{find_in_dirs_env, CallExt}; use nu_engine::{find_in_dirs_env, get_dirs_var_from_call, CallExt};
use nu_parser::{parse, parse_module_block, unescape_unquote_string}; use nu_parser::{parse, parse_module_block, unescape_unquote_string};
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack, StateWorkingSet}; use nu_protocol::engine::{Command, EngineState, Stack, StateWorkingSet};
@ -106,7 +106,12 @@ impl Command for NuCheck {
_ => { _ => {
if let Some(path_str) = path { if let Some(path_str) = path {
// look up the path as relative to FILE_PWD or inside NU_LIB_DIRS (same process as source-env) // look up the path as relative to FILE_PWD or inside NU_LIB_DIRS (same process as source-env)
let path = match find_in_dirs_env(&path_str.item, engine_state, stack) { let path = match find_in_dirs_env(
&path_str.item,
engine_state,
stack,
get_dirs_var_from_call(call),
) {
Ok(path) => { Ok(path) => {
if let Some(path) = path { if let Some(path) = path {
path path

View file

@ -39,7 +39,7 @@ pub trait CallExt {
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack, stack: &mut Stack,
pos: usize, name: &str,
) -> Result<T, ShellError>; ) -> Result<T, ShellError>;
} }
@ -111,9 +111,9 @@ impl CallExt for Call {
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack, stack: &mut Stack,
pos: usize, name: &str,
) -> Result<T, ShellError> { ) -> Result<T, ShellError> {
if let Some(expr) = self.parser_info_nth(pos) { if let Some(expr) = self.get_parser_info(name) {
let result = eval_expression(engine_state, stack, expr)?; let result = eval_expression(engine_state, stack, expr)?;
FromValue::from_value(&result) FromValue::from_value(&result)
} else if self.parser_info.is_empty() { } else if self.parser_info.is_empty() {

View file

@ -1,9 +1,9 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use nu_protocol::ast::PathMember; use nu_protocol::ast::{Call, Expr, PathMember};
use nu_protocol::engine::{EngineState, Stack}; use nu_protocol::engine::{EngineState, Stack};
use nu_protocol::{PipelineData, ShellError, Span, Value}; use nu_protocol::{PipelineData, ShellError, Span, Value, VarId};
use nu_path::canonicalize_with; use nu_path::canonicalize_with;
@ -18,8 +18,6 @@ const ENV_PATH_NAME: &str = "PATH";
const ENV_CONVERSIONS: &str = "ENV_CONVERSIONS"; const ENV_CONVERSIONS: &str = "ENV_CONVERSIONS";
static LIB_DIRS_ENV: &str = "NU_LIB_DIRS";
enum ConversionResult { enum ConversionResult {
Ok(Value), Ok(Value),
ConversionError(ShellError), // Failure during the conversion itself ConversionError(ShellError), // Failure during the conversion itself
@ -224,6 +222,17 @@ pub fn path_str(
env_to_string(pathname, &pathval, engine_state, stack) env_to_string(pathname, &pathval, engine_state, stack)
} }
pub const DIR_VAR_PARSER_INFO: &str = "dirs_var";
pub fn get_dirs_var_from_call(call: &Call) -> Option<VarId> {
call.get_parser_info(DIR_VAR_PARSER_INFO).and_then(|x| {
if let Expr::Var(id) = x.expr {
Some(id)
} else {
None
}
})
}
/// This helper function is used to find files during eval /// This helper function is used to find files during eval
/// ///
/// First, the actual current working directory is selected as /// First, the actual current working directory is selected as
@ -239,6 +248,7 @@ pub fn find_in_dirs_env(
filename: &str, filename: &str,
engine_state: &EngineState, engine_state: &EngineState,
stack: &Stack, stack: &Stack,
dirs_var: Option<VarId>,
) -> Result<Option<PathBuf>, ShellError> { ) -> Result<Option<PathBuf>, ShellError> {
// Choose whether to use file-relative or PWD-relative path // Choose whether to use file-relative or PWD-relative path
let cwd = if let Some(pwd) = stack.get_env_var(engine_state, "FILE_PWD") { let cwd = if let Some(pwd) = stack.get_env_var(engine_state, "FILE_PWD") {
@ -262,36 +272,35 @@ pub fn find_in_dirs_env(
current_dir_str(engine_state, stack)? current_dir_str(engine_state, stack)?
}; };
let check_dir = |lib_dirs: Option<Value>| -> Option<PathBuf> {
if let Ok(p) = canonicalize_with(filename, &cwd) { if let Ok(p) = canonicalize_with(filename, &cwd) {
Ok(Some(p)) return Some(p);
} else { }
let path = Path::new(filename); let path = Path::new(filename);
if !path.is_relative() {
if path.is_relative() { return None;
if let Some(lib_dirs) = stack.get_env_var(engine_state, LIB_DIRS_ENV) {
if let Ok(dirs) = lib_dirs.as_list() {
for lib_dir in dirs {
if let Ok(dir) = lib_dir.as_path() {
// make sure the dir is absolute path
if let Ok(dir_abs) = canonicalize_with(dir, &cwd) {
if let Ok(path) = canonicalize_with(filename, dir_abs) {
return Ok(Some(path));
}
}
}
} }
Ok(None) lib_dirs?
} else { .as_list()
Ok(None) .ok()?
} .iter()
} else { .map(|lib_dir| -> Option<PathBuf> {
Ok(None) let dir = lib_dir.as_path().ok()?;
} let dir_abs = canonicalize_with(dir, &cwd).ok()?;
} else { canonicalize_with(filename, dir_abs).ok()
Ok(None) })
} .find(Option::is_some)
} .flatten()
};
let lib_dirs = dirs_var
.and_then(|dirs_var| engine_state.find_constant(dirs_var, &[]))
.cloned();
// TODO: remove (see #8310)
let lib_dirs_fallback = stack.get_env_var(engine_state, "NU_LIB_DIRS");
Ok(check_dir(lib_dirs).or_else(|| check_dir(lib_dirs_fallback)))
} }
fn get_converted_value( fn get_converted_value(

View file

@ -859,7 +859,7 @@ pub fn eval_element_with_input(
], ],
redirect_stdout: false, redirect_stdout: false,
redirect_stderr: false, redirect_stderr: false,
parser_info: vec![], parser_info: HashMap::new(),
}, },
input, input,
) )
@ -930,7 +930,7 @@ pub fn eval_element_with_input(
], ],
redirect_stdout: false, redirect_stdout: false,
redirect_stderr: false, redirect_stderr: false,
parser_info: vec![], parser_info: HashMap::new(),
}, },
input, input,
) )

View file

@ -7,13 +7,14 @@ use nu_protocol::{
}, },
engine::{StateWorkingSet, DEFAULT_OVERLAY_NAME}, engine::{StateWorkingSet, DEFAULT_OVERLAY_NAME},
span, Alias, BlockId, Exportable, Module, PositionalArg, Span, Spanned, SyntaxShape, Type, span, Alias, BlockId, Exportable, Module, PositionalArg, Span, Spanned, SyntaxShape, Type,
VarId,
}; };
use std::collections::HashSet; use std::collections::{HashMap, HashSet};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
static LIB_DIRS_ENV: &str = "NU_LIB_DIRS"; pub const LIB_DIRS_VAR: &str = "NU_LIB_DIRS";
#[cfg(feature = "plugin")] #[cfg(feature = "plugin")]
static PLUGIN_DIRS_ENV: &str = "NU_PLUGIN_DIRS"; pub const PLUGIN_DIRS_VAR: &str = "NU_PLUGIN_DIRS";
use crate::{ use crate::{
eval::{eval_constant, value_as_string}, eval::{eval_constant, value_as_string},
@ -916,7 +917,7 @@ pub fn parse_old_alias(
decl_id, decl_id,
redirect_stdout: true, redirect_stdout: true,
redirect_stderr: false, redirect_stderr: false,
parser_info: vec![], parser_info: HashMap::new(),
})); }));
return ( return (
Pipeline::from_vec(vec![Expression { Pipeline::from_vec(vec![Expression {
@ -1217,7 +1218,7 @@ pub fn parse_export_in_module(
arguments: vec![], arguments: vec![],
redirect_stdout: true, redirect_stdout: true,
redirect_stderr: false, redirect_stderr: false,
parser_info: vec![], parser_info: HashMap::new(),
}); });
let exportables = if let Some(kw_span) = spans.get(1) { let exportables = if let Some(kw_span) = spans.get(1) {
@ -2041,7 +2042,7 @@ pub fn parse_module(
], ],
redirect_stdout: true, redirect_stdout: true,
redirect_stderr: false, redirect_stderr: false,
parser_info: vec![], parser_info: HashMap::new(),
}); });
( (
@ -2184,7 +2185,7 @@ pub fn parse_use(
if err.is_none() { if err.is_none() {
if let Some(module_path) = if let Some(module_path) =
find_in_dirs(&module_filename, working_set, &cwd, LIB_DIRS_ENV) find_in_dirs(&module_filename, working_set, &cwd, LIB_DIRS_VAR)
{ {
if let Some(i) = working_set if let Some(i) = working_set
.parsed_module_files .parsed_module_files
@ -2398,7 +2399,7 @@ pub fn parse_use(
}; };
let mut call = call; let mut call = call;
call.add_parser_info(import_pattern_expr); call.set_parser_info("import_pattern".to_string(), import_pattern_expr);
( (
Pipeline::from_vec(vec![Expression { Pipeline::from_vec(vec![Expression {
@ -2619,7 +2620,7 @@ pub fn parse_hide(
}; };
let mut call = call; let mut call = call;
call.add_parser_info(import_pattern_expr); call.set_parser_info("import_pattern".to_string(), import_pattern_expr);
( (
Pipeline::from_vec(vec![Expression { Pipeline::from_vec(vec![Expression {
@ -2844,7 +2845,7 @@ pub fn parse_overlay_use(
String::from_utf8(trim_quotes(overlay_name.as_bytes()).to_vec()) String::from_utf8(trim_quotes(overlay_name.as_bytes()).to_vec())
{ {
if let Some(module_path) = if let Some(module_path) =
find_in_dirs(&module_filename, working_set, &cwd, LIB_DIRS_ENV) find_in_dirs(&module_filename, working_set, &cwd, LIB_DIRS_VAR)
{ {
let overlay_name = if let Some(stem) = module_path.file_stem() { let overlay_name = if let Some(stem) = module_path.file_stem() {
stem.to_string_lossy().to_string() stem.to_string_lossy().to_string()
@ -2936,7 +2937,9 @@ pub fn parse_overlay_use(
// Change the call argument to include the Overlay expression with the module ID // Change the call argument to include the Overlay expression with the module ID
let mut call = call; let mut call = call;
call.add_parser_info(Expression { call.set_parser_info(
"overlay_expr".to_string(),
Expression {
expr: Expr::Overlay(if is_module_updated { expr: Expr::Overlay(if is_module_updated {
Some(origin_module_id) Some(origin_module_id)
} else { } else {
@ -2945,7 +2948,8 @@ pub fn parse_overlay_use(
span: overlay_name_span, span: overlay_name_span,
ty: Type::Any, ty: Type::Any,
custom_completion: None, custom_completion: None,
}); },
);
let pipeline = Pipeline::from_vec(vec![Expression { let pipeline = Pipeline::from_vec(vec![Expression {
expr: Expr::Call(call), expr: Expr::Call(call),
@ -3117,7 +3121,7 @@ pub fn parse_let_or_const(
], ],
redirect_stdout: true, redirect_stdout: true,
redirect_stderr: false, redirect_stderr: false,
parser_info: vec![], parser_info: HashMap::new(),
}); });
return ( return (
@ -3242,7 +3246,7 @@ pub fn parse_mut(
], ],
redirect_stdout: true, redirect_stdout: true,
redirect_stderr: false, redirect_stderr: false,
parser_info: vec![], parser_info: HashMap::new(),
}); });
return ( return (
@ -3371,7 +3375,7 @@ pub fn parse_source(
} }
}; };
if let Some(path) = find_in_dirs(&filename, working_set, &cwd, LIB_DIRS_ENV) { if let Some(path) = find_in_dirs(&filename, working_set, &cwd, LIB_DIRS_VAR) {
if let Ok(contents) = std::fs::read(&path) { if let Ok(contents) = std::fs::read(&path) {
// Change currently parsed directory // Change currently parsed directory
let prev_currently_parsed_cwd = if let Some(parent) = path.parent() { let prev_currently_parsed_cwd = if let Some(parent) = path.parent() {
@ -3417,12 +3421,15 @@ pub fn parse_source(
// FIXME: 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` // after writing `source example.nu`
call_with_block.add_parser_info(Expression { call_with_block.set_parser_info(
"block_id".to_string(),
Expression {
expr: Expr::Int(block_id as i64), expr: Expr::Int(block_id as i64),
span: spans[1], span: spans[1],
ty: Type::Any, ty: Type::Any,
custom_completion: None, custom_completion: None,
}); },
);
return ( return (
Pipeline::from_vec(vec![Expression { Pipeline::from_vec(vec![Expression {
@ -3632,7 +3639,7 @@ pub fn parse_register(
if let Some(err) = err { if let Some(err) = err {
Err(err) Err(err)
} else { } else {
let path = if let Some(p) = find_in_dirs(&name, working_set, &cwd, PLUGIN_DIRS_ENV) let path = if let Some(p) = find_in_dirs(&name, working_set, &cwd, PLUGIN_DIRS_VAR)
{ {
p p
} else { } else {
@ -3779,18 +3786,68 @@ pub fn parse_register(
) )
} }
pub fn find_dirs_var(working_set: &StateWorkingSet, var_name: &str) -> Option<VarId> {
working_set
.find_variable(format!("${}", var_name).as_bytes())
.filter(|var_id| working_set.find_constant(*var_id).is_some())
}
/// This helper function is used to find files during parsing /// This helper function is used to find files during parsing
/// ///
/// First, the actual current working directory is selected as /// First, the actual current working directory is selected as
/// a) the directory of a file currently being parsed /// a) the directory of a file currently being parsed
/// b) current working directory (PWD) /// b) current working directory (PWD)
/// ///
/// Then, if the file is not found in the actual cwd, NU_LIB_DIRS is checked. /// Then, if the file is not found in the actual cwd, dirs_var is checked.
/// If there is a relative path in NU_LIB_DIRS, it is assumed to be relative to the actual cwd /// For now, we first check for a const with the name of `dirs_var_name`,
/// and if that's not found, then we try to look for an environment variable of the same name.
/// If there is a relative path in dirs_var, it is assumed to be relative to the actual cwd
/// determined in the first step. /// determined in the first step.
/// ///
/// Always returns an absolute path /// Always returns an absolute path
pub fn find_in_dirs( pub fn find_in_dirs(
filename: &str,
working_set: &StateWorkingSet,
cwd: &str,
dirs_var_name: &str,
) -> Option<PathBuf> {
pub fn find_in_dirs_with_id(
filename: &str,
working_set: &StateWorkingSet,
cwd: &str,
dirs_var_name: &str,
) -> Option<PathBuf> {
// Choose whether to use file-relative or PWD-relative path
let actual_cwd = if let Some(currently_parsed_cwd) = &working_set.currently_parsed_cwd {
currently_parsed_cwd.as_path()
} else {
Path::new(cwd)
};
if let Ok(p) = canonicalize_with(filename, actual_cwd) {
return Some(p);
}
let path = Path::new(filename);
if !path.is_relative() {
return None;
}
working_set
.find_constant(find_dirs_var(working_set, dirs_var_name)?)?
.as_list()
.ok()?
.iter()
.map(|lib_dir| -> Option<PathBuf> {
let dir = lib_dir.as_path().ok()?;
let dir_abs = canonicalize_with(dir, actual_cwd).ok()?;
canonicalize_with(filename, dir_abs).ok()
})
.find(Option::is_some)
.flatten()
}
// TODO: remove (see #8310)
pub fn find_in_dirs_old(
filename: &str, filename: &str,
working_set: &StateWorkingSet, working_set: &StateWorkingSet,
cwd: &str, cwd: &str,
@ -3834,3 +3891,7 @@ pub fn find_in_dirs(
} }
} }
} }
find_in_dirs_with_id(filename, working_set, cwd, dirs_var_name)
.or_else(|| find_in_dirs_old(filename, working_set, cwd, dirs_var_name))
}

View file

@ -8,6 +8,7 @@ use crate::{
ParseError, Token, TokenContents, ParseError, Token, TokenContents,
}; };
use nu_engine::DIR_VAR_PARSER_INFO;
use nu_protocol::{ use nu_protocol::{
ast::{ ast::{
Argument, Assignment, Bits, Block, Boolean, Call, CellPath, Comparison, Expr, Expression, Argument, Assignment, Bits, Block, Boolean, Call, CellPath, Comparison, Expr, Expression,
@ -20,10 +21,10 @@ use nu_protocol::{
}; };
use crate::parse_keywords::{ use crate::parse_keywords::{
is_unaliasable_parser_keyword, parse_alias, parse_def, parse_def_predecl, find_dirs_var, is_unaliasable_parser_keyword, parse_alias, parse_def, parse_def_predecl,
parse_export_in_block, parse_extern, parse_for, parse_hide, parse_keyword, parse_let_or_const, parse_export_in_block, parse_extern, parse_for, parse_hide, parse_keyword, parse_let_or_const,
parse_module, parse_old_alias, parse_overlay_hide, parse_overlay_new, parse_overlay_use, parse_module, parse_old_alias, parse_overlay_hide, parse_overlay_new, parse_overlay_use,
parse_source, parse_use, parse_where, parse_where_expr, parse_source, parse_use, parse_where, parse_where_expr, LIB_DIRS_VAR,
}; };
use itertools::Itertools; use itertools::Itertools;
@ -809,6 +810,25 @@ pub struct ParsedInternalCall {
pub error: Option<ParseError>, pub error: Option<ParseError>,
} }
fn attach_parser_info_builtin(working_set: &StateWorkingSet, name: &str, call: &mut Call) {
match name {
"use" | "overlay use" | "source-env" | "nu-check" => {
if let Some(var_id) = find_dirs_var(working_set, LIB_DIRS_VAR) {
call.set_parser_info(
DIR_VAR_PARSER_INFO.to_owned(),
Expression {
expr: Expr::Var(var_id),
span: call.head,
ty: Type::Any,
custom_completion: None,
},
);
}
}
_ => {}
}
}
pub fn parse_internal_call( pub fn parse_internal_call(
working_set: &mut StateWorkingSet, working_set: &mut StateWorkingSet,
command_span: Span, command_span: Span,
@ -828,6 +848,10 @@ pub fn parse_internal_call(
let signature = decl.signature(); let signature = decl.signature();
let output = signature.output_type.clone(); let output = signature.output_type.clone();
if decl.is_builtin() {
attach_parser_info_builtin(working_set, decl.name(), &mut call);
}
// The index into the positional parameter in the definition // The index into the positional parameter in the definition
let mut positional_idx = 0; let mut positional_idx = 0;
@ -5686,7 +5710,7 @@ pub fn parse_expression(
arguments, arguments,
redirect_stdout: true, redirect_stdout: true,
redirect_stderr: false, redirect_stderr: false,
parser_info: vec![], parser_info: HashMap::new(),
})); }));
( (
@ -6577,7 +6601,7 @@ fn wrap_expr_with_collect(working_set: &mut StateWorkingSet, expr: &Expression)
decl_id, decl_id,
redirect_stdout: true, redirect_stdout: true,
redirect_stderr: false, redirect_stderr: false,
parser_info: vec![], parser_info: HashMap::new(),
})), })),
span, span,
ty: Type::String, ty: Type::String,

View file

@ -1,3 +1,5 @@
use std::collections::HashMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use super::Expression; use super::Expression;
@ -19,7 +21,7 @@ pub struct Call {
pub redirect_stdout: bool, pub redirect_stdout: bool,
pub redirect_stderr: bool, pub redirect_stderr: bool,
/// this field is used by the parser to pass additional command-specific information /// this field is used by the parser to pass additional command-specific information
pub parser_info: Vec<Expression>, pub parser_info: HashMap<String, Expression>,
} }
impl Call { impl Call {
@ -30,7 +32,7 @@ impl Call {
arguments: vec![], arguments: vec![],
redirect_stdout: true, redirect_stdout: true,
redirect_stderr: false, redirect_stderr: false,
parser_info: vec![], parser_info: HashMap::new(),
} }
} }
@ -70,10 +72,6 @@ impl Call {
self.arguments.push(Argument::Positional(positional)); self.arguments.push(Argument::Positional(positional));
} }
pub fn add_parser_info(&mut self, info: Expression) {
self.parser_info.push(info);
}
pub fn add_unknown(&mut self, unknown: Expression) { pub fn add_unknown(&mut self, unknown: Expression) {
self.arguments.push(Argument::Unknown(unknown)); self.arguments.push(Argument::Unknown(unknown));
} }
@ -106,8 +104,12 @@ impl Call {
self.positional_iter().count() self.positional_iter().count()
} }
pub fn parser_info_nth(&self, i: usize) -> Option<&Expression> { pub fn get_parser_info(&self, name: &str) -> Option<&Expression> {
self.parser_info.get(i) self.parser_info.get(name)
}
pub fn set_parser_info(&mut self, name: String, val: Expression) -> Option<Expression> {
self.parser_info.insert(name, val)
} }
pub fn has_flag(&self, flag_name: &str) -> bool { pub fn has_flag(&self, flag_name: &str) -> bool {

View file

@ -140,6 +140,34 @@ fn nu_lib_dirs_relative_repl() {
}) })
} }
// TODO: add absolute path tests after we expand const capabilities (see #8310)
#[test]
fn const_nu_lib_dirs_relative() {
Playground::setup("const_nu_lib_dirs_relative", |dirs, sandbox| {
sandbox
.mkdir("scripts")
.with_files(vec![FileWithContentToBeTrimmed(
"scripts/foo.nu",
r#"
let-env FOO = "foo"
"#,
)])
.with_files(vec![FileWithContentToBeTrimmed(
"main.nu",
r#"
const NU_LIB_DIRS = [ 'scripts' ]
source-env foo.nu
$env.FOO
"#,
)]);
let outcome = nu!(cwd: dirs.test(), "source main.nu");
assert!(outcome.err.is_empty());
assert_eq!(outcome.out, "foo");
})
}
#[test] #[test]
fn nu_lib_dirs_relative_script() { fn nu_lib_dirs_relative_script() {
Playground::setup("nu_lib_dirs_relative_script", |dirs, sandbox| { Playground::setup("nu_lib_dirs_relative_script", |dirs, sandbox| {