mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 13:06:08 +02:00
Allow NU_LIBS_DIR and friends to be const (#8310)
# Description Allow NU_LIBS_DIR and friends to be const they can be updated within the same parse pass. This will allow us to remove having multiple config files eventually. Small implementation detail: I've changed `call.parser_info` to a hashmap with string keys, so the information can have names rather than indices, and we don't have to worry too much about the order in which we put things into it. Closes https://github.com/nushell/nushell/issues/8422 # User-Facing Changes In a single file, users can now do stuff like ``` const NU_LIBS_DIR = ['/some/path/here'] source script.nu ``` and the source statement will use the value of NU_LIBS_DIR declared the line before. Currently, if there is no `NU_LIBS_DIR` const, then we fallback to using the value of the `NU_LIBS_DIR` env-var, so there are no breaking changes (unless someone named a const NU_LIBS_DIR for some reason).  # Tests + Formatting ~~TODO: write tests~~ Done # After Submitting ~~TODO: update docs~~ Will do when we update default_env.nu/merge default_env.nu into default_config.nu.
This commit is contained in:
@ -1,4 +1,5 @@
|
||||
use log::trace;
|
||||
use nu_engine::get_dirs_var_from_call;
|
||||
use nu_path::canonicalize_with;
|
||||
use nu_protocol::{
|
||||
ast::{
|
||||
@ -7,13 +8,14 @@ use nu_protocol::{
|
||||
},
|
||||
engine::{StateWorkingSet, DEFAULT_OVERLAY_NAME},
|
||||
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};
|
||||
|
||||
static LIB_DIRS_ENV: &str = "NU_LIB_DIRS";
|
||||
pub const LIB_DIRS_VAR: &str = "NU_LIB_DIRS";
|
||||
#[cfg(feature = "plugin")]
|
||||
static PLUGIN_DIRS_ENV: &str = "NU_PLUGIN_DIRS";
|
||||
pub const PLUGIN_DIRS_VAR: &str = "NU_PLUGIN_DIRS";
|
||||
|
||||
use crate::{
|
||||
eval::{eval_constant, value_as_string},
|
||||
@ -962,7 +964,7 @@ pub fn parse_old_alias(
|
||||
decl_id,
|
||||
redirect_stdout: true,
|
||||
redirect_stderr: false,
|
||||
parser_info: vec![],
|
||||
parser_info: HashMap::new(),
|
||||
}));
|
||||
return (
|
||||
Pipeline::from_vec(vec![Expression {
|
||||
@ -1263,7 +1265,7 @@ pub fn parse_export_in_module(
|
||||
arguments: vec![],
|
||||
redirect_stdout: true,
|
||||
redirect_stderr: false,
|
||||
parser_info: vec![],
|
||||
parser_info: HashMap::new(),
|
||||
});
|
||||
|
||||
let exportables = if let Some(kw_span) = spans.get(1) {
|
||||
@ -2087,7 +2089,7 @@ pub fn parse_module(
|
||||
],
|
||||
redirect_stdout: true,
|
||||
redirect_stderr: false,
|
||||
parser_info: vec![],
|
||||
parser_info: HashMap::new(),
|
||||
});
|
||||
|
||||
(
|
||||
@ -2229,9 +2231,12 @@ pub fn parse_use(
|
||||
unescape_unquote_string(&import_pattern.head.name, import_pattern.head.span);
|
||||
|
||||
if err.is_none() {
|
||||
if let Some(module_path) =
|
||||
find_in_dirs(&module_filename, working_set, &cwd, LIB_DIRS_ENV)
|
||||
{
|
||||
if let Some(module_path) = find_in_dirs_with_id(
|
||||
&module_filename,
|
||||
working_set,
|
||||
&cwd,
|
||||
get_dirs_var_from_call(&call),
|
||||
) {
|
||||
if let Some(i) = working_set
|
||||
.parsed_module_files
|
||||
.iter()
|
||||
@ -2444,7 +2449,7 @@ pub fn parse_use(
|
||||
};
|
||||
|
||||
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 {
|
||||
@ -2665,7 +2670,7 @@ pub fn parse_hide(
|
||||
};
|
||||
|
||||
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 {
|
||||
@ -2889,9 +2894,12 @@ pub fn parse_overlay_use(
|
||||
if let Ok(module_filename) =
|
||||
String::from_utf8(trim_quotes(overlay_name.as_bytes()).to_vec())
|
||||
{
|
||||
if let Some(module_path) =
|
||||
find_in_dirs(&module_filename, working_set, &cwd, LIB_DIRS_ENV)
|
||||
{
|
||||
if let Some(module_path) = find_in_dirs_with_id(
|
||||
&module_filename,
|
||||
working_set,
|
||||
&cwd,
|
||||
get_dirs_var_from_call(&call),
|
||||
) {
|
||||
let overlay_name = if let Some(stem) = module_path.file_stem() {
|
||||
stem.to_string_lossy().to_string()
|
||||
} else {
|
||||
@ -2982,16 +2990,19 @@ pub fn parse_overlay_use(
|
||||
|
||||
// Change the call argument to include the Overlay expression with the module ID
|
||||
let mut call = call;
|
||||
call.add_parser_info(Expression {
|
||||
expr: Expr::Overlay(if is_module_updated {
|
||||
Some(origin_module_id)
|
||||
} else {
|
||||
None
|
||||
}),
|
||||
span: overlay_name_span,
|
||||
ty: Type::Any,
|
||||
custom_completion: None,
|
||||
});
|
||||
call.set_parser_info(
|
||||
"overlay_expr".to_string(),
|
||||
Expression {
|
||||
expr: Expr::Overlay(if is_module_updated {
|
||||
Some(origin_module_id)
|
||||
} else {
|
||||
None
|
||||
}),
|
||||
span: overlay_name_span,
|
||||
ty: Type::Any,
|
||||
custom_completion: None,
|
||||
},
|
||||
);
|
||||
|
||||
let pipeline = Pipeline::from_vec(vec![Expression {
|
||||
expr: Expr::Call(call),
|
||||
@ -3160,7 +3171,7 @@ pub fn parse_let_or_const(
|
||||
],
|
||||
redirect_stdout: true,
|
||||
redirect_stderr: false,
|
||||
parser_info: vec![],
|
||||
parser_info: HashMap::new(),
|
||||
});
|
||||
|
||||
return (
|
||||
@ -3282,7 +3293,7 @@ pub fn parse_mut(
|
||||
],
|
||||
redirect_stdout: true,
|
||||
redirect_stderr: false,
|
||||
parser_info: vec![],
|
||||
parser_info: HashMap::new(),
|
||||
});
|
||||
|
||||
return (
|
||||
@ -3411,7 +3422,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) {
|
||||
// Change currently parsed directory
|
||||
let prev_currently_parsed_cwd = if let Some(parent) = path.parent() {
|
||||
@ -3457,12 +3468,15 @@ pub fn parse_source(
|
||||
|
||||
// FIXME: Adding this expression to the positional creates a syntax highlighting error
|
||||
// after writing `source example.nu`
|
||||
call_with_block.add_parser_info(Expression {
|
||||
expr: Expr::Int(block_id as i64),
|
||||
span: spans[1],
|
||||
ty: Type::Any,
|
||||
custom_completion: None,
|
||||
});
|
||||
call_with_block.set_parser_info(
|
||||
"block_id".to_string(),
|
||||
Expression {
|
||||
expr: Expr::Int(block_id as i64),
|
||||
span: spans[1],
|
||||
ty: Type::Any,
|
||||
custom_completion: None,
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
Pipeline::from_vec(vec![Expression {
|
||||
@ -3672,7 +3686,7 @@ pub fn parse_register(
|
||||
if let Some(err) = err {
|
||||
Err(err)
|
||||
} 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
|
||||
} else {
|
||||
@ -3817,12 +3831,67 @@ pub fn parse_register(
|
||||
/// a) the directory of a file currently being parsed
|
||||
/// b) current working directory (PWD)
|
||||
///
|
||||
/// Then, if the file is not found in the actual cwd, NU_LIB_DIRS is checked.
|
||||
/// If there is a relative path in NU_LIB_DIRS, it is assumed to be relative to the actual cwd
|
||||
/// Then, if the file is not found in the actual cwd, dirs_var is checked.
|
||||
/// If dirs_var is an Expr::Var, then we look for a const with that VarId,
|
||||
/// and if dirs_var is an Expr::String, then we look for an environment with that 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.
|
||||
///
|
||||
/// Always returns an absolute path
|
||||
pub fn find_in_dirs_with_id(
|
||||
filename: &str,
|
||||
working_set: &StateWorkingSet,
|
||||
cwd: &str,
|
||||
dirs_var_id: Option<VarId>,
|
||||
) -> 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(dirs_var_id?)?
|
||||
.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()
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
pub fn find_in_dirs(
|
||||
filename: &str,
|
||||
working_set: &StateWorkingSet,
|
||||
cwd: &str,
|
||||
dirs_var_name: &str,
|
||||
) -> Option<PathBuf> {
|
||||
find_dirs_var(working_set, dirs_var_name)
|
||||
.and_then(|var_id| find_in_dirs_with_id(filename, working_set, cwd, Some(var_id)))
|
||||
.or_else(|| find_in_dirs_old(filename, working_set, cwd, dirs_var_name))
|
||||
}
|
||||
|
||||
// TODO: remove (see #8310)
|
||||
pub fn find_in_dirs_old(
|
||||
filename: &str,
|
||||
working_set: &StateWorkingSet,
|
||||
cwd: &str,
|
||||
|
@ -7,6 +7,7 @@ use crate::{
|
||||
ParseError, Token, TokenContents,
|
||||
};
|
||||
|
||||
use nu_engine::DIR_VAR_PARSER_INFO;
|
||||
use nu_protocol::{
|
||||
ast::{
|
||||
Argument, Assignment, Bits, Block, Boolean, Call, CellPath, Comparison, Expr, Expression,
|
||||
@ -19,10 +20,10 @@ use nu_protocol::{
|
||||
};
|
||||
|
||||
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_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;
|
||||
@ -781,6 +782,25 @@ pub struct ParsedInternalCall {
|
||||
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(
|
||||
working_set: &mut StateWorkingSet,
|
||||
command_span: Span,
|
||||
@ -800,6 +820,10 @@ pub fn parse_internal_call(
|
||||
let signature = decl.signature();
|
||||
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
|
||||
let mut positional_idx = 0;
|
||||
|
||||
@ -5312,7 +5336,7 @@ pub fn parse_expression(
|
||||
arguments,
|
||||
redirect_stdout: true,
|
||||
redirect_stderr: false,
|
||||
parser_info: vec![],
|
||||
parser_info: HashMap::new(),
|
||||
}));
|
||||
|
||||
(
|
||||
@ -6158,7 +6182,7 @@ fn wrap_expr_with_collect(working_set: &mut StateWorkingSet, expr: &Expression)
|
||||
decl_id,
|
||||
redirect_stdout: true,
|
||||
redirect_stderr: false,
|
||||
parser_info: vec![],
|
||||
parser_info: HashMap::new(),
|
||||
})),
|
||||
span,
|
||||
ty: Type::String,
|
||||
|
Reference in New Issue
Block a user