Skip to content

Commit

Permalink
Add user autoload directory (#14669)
Browse files Browse the repository at this point in the history
# Description

Adds a user-level (non-vendor) autoload directory:

```
($nu.default-config-dir)/autoload
```

Currently this is the only directory. We can consider adding others if
needed.

Related: As a separate PR, I'm going to try to restore the ability to
set `$env.NU_AUTOLOAD_DIRS` during startup.

# User-Facing Changes

Files in `$nu.default-config-dir/autoload` will be autoload at startup.
These files will be loaded after any vendor autoloads, so that a user
can override the vendor settings.

# Tests + Formatting

- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
- 🟢 `toolkit test`
- 🟢 `toolkit test stdlib`

# After Submitting

TODO; add a `$nu.user-autoload-dirs` constant.

Doc updates
  • Loading branch information
NotTheDr01ds authored Jan 2, 2025
1 parent df3892f commit 461eb43
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 28 deletions.
3 changes: 2 additions & 1 deletion crates/nu-cli/tests/completions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1357,7 +1357,7 @@ fn variables_completions() {
// Test completions for $nu
let suggestions = completer.complete("$nu.", 4);

assert_eq!(18, suggestions.len());
assert_eq!(19, suggestions.len());

let expected: Vec<String> = vec![
"cache-dir".into(),
Expand All @@ -1377,6 +1377,7 @@ fn variables_completions() {
"plugin-path".into(),
"startup-time".into(),
"temp-path".into(),
"user-autoload-dirs".into(),
"vendor-autoload-dirs".into(),
];

Expand Down
56 changes: 44 additions & 12 deletions crates/nu-protocol/src/eval_const.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,17 @@ pub(crate) fn create_nu_constant(engine_state: &EngineState, span: Span) -> Valu
),
);

record.push(
"user-autoload-dirs",
Value::list(
get_user_autoload_dirs(engine_state)
.iter()
.map(|path| Value::string(path.to_string_lossy(), span))
.collect(),
span,
),
);

record.push("temp-path", {
let canon_temp_path = canonicalize_path(engine_state, &std::env::temp_dir());
Value::string(canon_temp_path.to_string_lossy(), span)
Expand Down Expand Up @@ -306,10 +317,9 @@ pub fn get_vendor_autoload_dirs(_engine_state: &EngineState) -> Vec<PathBuf> {
.map(into_autoload_path_fn)
.for_each(&mut append_fn);

option_env!("NU_VENDOR_AUTOLOAD_DIR")
.into_iter()
.map(PathBuf::from)
.for_each(&mut append_fn);
if let Some(path) = option_env!("NU_VENDOR_AUTOLOAD_DIR") {
append_fn(PathBuf::from(path));
}

#[cfg(target_os = "macos")]
std::env::var("XDG_DATA_HOME")
Expand All @@ -326,15 +336,37 @@ pub fn get_vendor_autoload_dirs(_engine_state: &EngineState) -> Vec<PathBuf> {
.into_iter()
.for_each(&mut append_fn);

dirs::data_dir()
.into_iter()
.map(into_autoload_path_fn)
.for_each(&mut append_fn);
if let Some(data_dir) = dirs::data_dir() {
append_fn(into_autoload_path_fn(data_dir));
}

std::env::var_os("NU_VENDOR_AUTOLOAD_DIR")
.into_iter()
.map(PathBuf::from)
.for_each(&mut append_fn);
if let Some(path) = std::env::var_os("NU_VENDOR_AUTOLOAD_DIR") {
append_fn(PathBuf::from(path));
}

dirs
}

pub fn get_user_autoload_dirs(_engine_state: &EngineState) -> Vec<PathBuf> {
// User autoload directories - Currently just `autoload` in the default
// configuration directory
let into_autoload_path_fn = |mut path: PathBuf| {
path.push("nushell");
path.push("autoload");
path
};

let mut dirs = Vec::new();

let mut append_fn = |path: PathBuf| {
if !dirs.contains(&path) {
dirs.push(path)
}
};

if let Some(config_dir) = dirs::config_dir() {
append_fn(into_autoload_path_fn(config_dir));
}

dirs
}
Expand Down
36 changes: 21 additions & 15 deletions src/config_files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use nu_engine::convert_env_values;
use nu_path::canonicalize_with;
use nu_protocol::{
engine::{EngineState, Stack, StateWorkingSet},
eval_const::{get_user_autoload_dirs, get_vendor_autoload_dirs},
report_parse_error, report_shell_error, Config, ParseError, PipelineData, Spanned,
};
use nu_utils::{get_default_config, get_default_env, get_scaffold_config, get_scaffold_env, perf};
Expand Down Expand Up @@ -197,24 +198,29 @@ pub(crate) fn read_vendor_autoload_files(engine_state: &mut EngineState, stack:

// The evaluation order is first determined by the semantics of `get_vendor_autoload_dirs`
// to determine the order of directories to evaluate
for autoload_dir in nu_protocol::eval_const::get_vendor_autoload_dirs(engine_state) {
warn!("read_vendor_autoload_files: {}", autoload_dir.display());

if autoload_dir.exists() {
// on a second levels files are lexicographically sorted by the string of the filename
let entries = read_and_sort_directory(&autoload_dir);
if let Ok(entries) = entries {
for entry in entries {
if !entry.ends_with(".nu") {
continue;
get_vendor_autoload_dirs(engine_state)
.iter()
// User autoload directories are evaluated after vendor, which means that
// the user can override vendor autoload files
.chain(get_user_autoload_dirs(engine_state).iter())
.for_each(|autoload_dir| {
warn!("read_vendor_autoload_files: {}", autoload_dir.display());

if autoload_dir.exists() {
// on a second levels files are lexicographically sorted by the string of the filename
let entries = read_and_sort_directory(autoload_dir);
if let Ok(entries) = entries {
for entry in entries {
if !entry.ends_with(".nu") {
continue;
}
let path = autoload_dir.join(entry);
warn!("AutoLoading: {:?}", path);
eval_config_contents(path, engine_state, stack);
}
let path = autoload_dir.join(entry);
warn!("AutoLoading: {:?}", path);
eval_config_contents(path, engine_state, stack);
}
}
}
}
});
}

fn eval_default_config(
Expand Down

0 comments on commit 461eb43

Please sign in to comment.