From 0372e8c53c085d4e7d9109fda104cfa68d846e71 Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Tue, 11 Jun 2024 14:10:31 -0500 Subject: [PATCH] add `$nu.data-dir` for completions and `$nu.cache-dir` for other uses (#13122) # Description This PR is an attempt to add a standard location for people to put completions in. I saw this topic come up again recently and IIRC we decided to create a standard location. I used the dirs-next crate to dictate where these locations are. I know some people won't like that but at least this gets the ball rolling in a direction that has a standard directory. This is what the default NU_LIB_DIRS looks like now in the default_env.nu. It should also be like this when starting nushell with `nu -n` ```nushell $env.NU_LIB_DIRS = [ ($nu.default-config-dir | path join 'scripts') # add /scripts ($nu.data-dir | path join 'completions') # default home for nushell completions ] ``` I also added these default folders to the `$nu` variable so now there is `$nu.data-path` and `$nu.cache-path`. ## Data Dir Default ![image](https://github.com/nushell/nushell/assets/343840/aeeb7cd6-17b4-43e8-bb6f-986a0c7fce23) While I was in there, I also decided to add a cache dir ## Cache Dir Default ![image](https://github.com/nushell/nushell/assets/343840/87dead66-4911-4f67-bfb2-acb16f386674) ### This is what the default looks like in Ubuntu. ![image](https://github.com/nushell/nushell/assets/343840/bca8eae8-8c18-47e8-b64f-3efe34f0004f) ### This is what it looks like with XDG_CACHE_HOME and XDG_DATA_HOME overridden ```nushell XDG_DATA_HOME=/tmp/data_home XDG_CACHE_HOME=/tmp/cache_home cargo r ``` ![image](https://github.com/nushell/nushell/assets/343840/fae86d50-9821-41f1-868e-3814eca3730b) ### This is what the defaults look like in Windows (username scrubbed to protect the innocent) ![image](https://github.com/nushell/nushell/assets/343840/3ebdb5cd-0150-448c-aff5-c57053e4788a) How my NU_LIB_DIRS is set in the images above ```nushell $env.NU_LIB_DIRS = [ ($nu.default-config-dir | path join 'scripts') # add /scripts '/Users/fdncred/src/nu_scripts' ($nu.config-path | path dirname) ($nu.data-dir | path join 'completions') # default home for nushell completions ] ``` Let the debate begin. # User-Facing Changes # Tests + Formatting # After Submitting --- Cargo.toml | 1 + crates/nu-cli/tests/completions/mod.rs | 4 ++- crates/nu-path/src/helpers.rs | 26 ++++++++++++--- crates/nu-path/src/lib.rs | 2 +- crates/nu-protocol/src/eval_const.rs | 32 +++++++++++++++++++ .../nu-utils/src/sample_config/default_env.nu | 1 + src/main.rs | 19 ++++++++--- 7 files changed, 75 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f7f911a0ed..3442e96ff5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -195,6 +195,7 @@ reedline = { workspace = true, features = ["bashisms", "sqlite"] } crossterm = { workspace = true } ctrlc = { workspace = true } +dirs-next = { workspace = true } log = { workspace = true } miette = { workspace = true, features = ["fancy-no-backtrace", "fancy"] } mimalloc = { version = "0.1.42", default-features = false, optional = true } diff --git a/crates/nu-cli/tests/completions/mod.rs b/crates/nu-cli/tests/completions/mod.rs index a5b0b13aa8..107de98c80 100644 --- a/crates/nu-cli/tests/completions/mod.rs +++ b/crates/nu-cli/tests/completions/mod.rs @@ -763,11 +763,13 @@ fn variables_completions() { // Test completions for $nu let suggestions = completer.complete("$nu.", 4); - assert_eq!(15, suggestions.len()); + assert_eq!(17, suggestions.len()); let expected: Vec = vec![ + "cache-dir".into(), "config-path".into(), "current-exe".into(), + "data-dir".into(), "default-config-dir".into(), "env-path".into(), "history-enabled".into(), diff --git a/crates/nu-path/src/helpers.rs b/crates/nu-path/src/helpers.rs index aaa53eab71..5b389410e4 100644 --- a/crates/nu-path/src/helpers.rs +++ b/crates/nu-path/src/helpers.rs @@ -6,18 +6,36 @@ pub fn home_dir() -> Option { dirs_next::home_dir() } +/// Return the data directory for the current platform or XDG_DATA_HOME if specified. +pub fn data_dir() -> Option { + match std::env::var("XDG_DATA_HOME").map(PathBuf::from) { + Ok(xdg_data) if xdg_data.is_absolute() => Some(canonicalize(&xdg_data).unwrap_or(xdg_data)), + _ => get_canonicalized_path(dirs_next::data_dir()), + } +} + +/// Return the cache directory for the current platform or XDG_CACHE_HOME if specified. +pub fn cache_dir() -> Option { + match std::env::var("XDG_CACHE_HOME").map(PathBuf::from) { + Ok(xdg_cache) if xdg_cache.is_absolute() => { + Some(canonicalize(&xdg_cache).unwrap_or(xdg_cache)) + } + _ => get_canonicalized_path(dirs_next::cache_dir()), + } +} + +/// Return the config directory for the current platform or XDG_CONFIG_HOME if specified. pub fn config_dir() -> Option { match std::env::var("XDG_CONFIG_HOME").map(PathBuf::from) { Ok(xdg_config) if xdg_config.is_absolute() => { Some(canonicalize(&xdg_config).unwrap_or(xdg_config)) } - _ => config_dir_old(), + _ => get_canonicalized_path(dirs_next::config_dir()), } } -/// Get the old default config directory. Outside of Linux, this will ignore `XDG_CONFIG_HOME` -pub fn config_dir_old() -> Option { - let path = dirs_next::config_dir()?; +pub fn get_canonicalized_path(path: Option) -> Option { + let path = path?; Some(canonicalize(&path).unwrap_or(path)) } diff --git a/crates/nu-path/src/lib.rs b/crates/nu-path/src/lib.rs index 6c064d4b57..13640acd2f 100644 --- a/crates/nu-path/src/lib.rs +++ b/crates/nu-path/src/lib.rs @@ -8,6 +8,6 @@ mod trailing_slash; pub use components::components; pub use expansions::{canonicalize_with, expand_path_with, expand_to_real_path, locate_in_dirs}; -pub use helpers::{config_dir, config_dir_old, home_dir}; +pub use helpers::{cache_dir, config_dir, data_dir, get_canonicalized_path, home_dir}; pub use tilde::expand_tilde; pub use trailing_slash::{has_trailing_slash, strip_trailing_slash}; diff --git a/crates/nu-protocol/src/eval_const.rs b/crates/nu-protocol/src/eval_const.rs index 08ff6fa391..9f81a6f38b 100644 --- a/crates/nu-protocol/src/eval_const.rs +++ b/crates/nu-protocol/src/eval_const.rs @@ -149,6 +149,38 @@ pub(crate) fn create_nu_constant(engine_state: &EngineState, span: Span) -> Valu }, ); + record.push( + "data-dir", + if let Some(path) = nu_path::data_dir() { + let mut canon_data_path = canonicalize_path(engine_state, &path); + canon_data_path.push("nushell"); + Value::string(canon_data_path.to_string_lossy(), span) + } else { + Value::error( + ShellError::IOError { + msg: "Could not get data path".into(), + }, + span, + ) + }, + ); + + record.push( + "cache-dir", + if let Some(path) = nu_path::cache_dir() { + let mut canon_cache_path = canonicalize_path(engine_state, &path); + canon_cache_path.push("nushell"); + Value::string(canon_cache_path.to_string_lossy(), span) + } else { + Value::error( + ShellError::IOError { + msg: "Could not get cache path".into(), + }, + 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) diff --git a/crates/nu-utils/src/sample_config/default_env.nu b/crates/nu-utils/src/sample_config/default_env.nu index 57e255ed20..effe76c98a 100644 --- a/crates/nu-utils/src/sample_config/default_env.nu +++ b/crates/nu-utils/src/sample_config/default_env.nu @@ -77,6 +77,7 @@ $env.ENV_CONVERSIONS = { # The default for this is $nu.default-config-dir/scripts $env.NU_LIB_DIRS = [ ($nu.default-config-dir | path join 'scripts') # add /scripts + ($nu.data-dir | path join 'completions') # default home for nushell completions ] # Directories to search for plugin binaries when calling register diff --git a/src/main.rs b/src/main.rs index f9fdff3ccf..41d5534bb0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -104,7 +104,9 @@ fn main() -> Result<()> { default: nushell_config_path.display().to_string(), }, ); - } else if let Some(old_config) = nu_path::config_dir_old().map(|p| p.join("nushell")) { + } else if let Some(old_config) = + nu_path::get_canonicalized_path(dirs_next::config_dir()).map(|p| p.join("nushell")) + { let xdg_config_empty = nushell_config_path .read_dir() .map_or(true, |mut dir| dir.next().is_none()); @@ -125,13 +127,22 @@ fn main() -> Result<()> { } } + let default_nushell_completions_path = if let Some(mut path) = nu_path::data_dir() { + path.push("nushell"); + path.push("completions"); + path + } else { + std::path::PathBuf::new() + }; + let mut default_nu_lib_dirs_path = nushell_config_path.clone(); default_nu_lib_dirs_path.push("scripts"); engine_state.add_env_var( "NU_LIB_DIRS".to_string(), - Value::test_list(vec![Value::test_string( - default_nu_lib_dirs_path.to_string_lossy(), - )]), + Value::test_list(vec![ + Value::test_string(default_nu_lib_dirs_path.to_string_lossy()), + Value::test_string(default_nushell_completions_path.to_string_lossy()), + ]), ); let mut default_nu_plugin_dirs_path = nushell_config_path;