mirror of
https://github.com/nushell/nushell.git
synced 2024-12-22 15:13:01 +01:00
Use $XDG_DATA_DIR as default history path
Fixes #10100 Consensus was that the history file was not config and so should not live in the config directory by default. Also includes an automated "migration" that moves the old history file to the new path: only if the old file exists and there is no file in the new path. Notes: * Changes `nu_path::data_dir()` to `nu_path::nu_data_dir()` so that it returns the data dir with `/nushell` appended. This was already being doing by callers of `nu_path::data_dir()` so I just refactored it. * `history_import.rs` tests now set `XDG_CONFIG_HOME` _and_ `XDG_DATA_HOME`, there may be other tests that will come to need both. But currently all tests pass.
This commit is contained in:
parent
4d3283e235
commit
975ebba33f
@ -46,7 +46,7 @@ impl Command for History {
|
|||||||
};
|
};
|
||||||
// todo for sqlite history this command should be an alias to `open ~/.config/nushell/history.sqlite3 | get history`
|
// todo for sqlite history this command should be an alias to `open ~/.config/nushell/history.sqlite3 | get history`
|
||||||
let Some(history_path) = history.file_path() else {
|
let Some(history_path) = history.file_path() else {
|
||||||
return Err(ShellError::ConfigDirNotFound { span: Some(head) });
|
return Err(ShellError::DataDirNotFound { span: Some(head) });
|
||||||
};
|
};
|
||||||
|
|
||||||
if call.has_flag(engine_state, stack, "clear")? {
|
if call.has_flag(engine_state, stack, "clear")? {
|
||||||
|
@ -74,7 +74,7 @@ Note that history item IDs are ignored when importing from file."#
|
|||||||
return ok;
|
return ok;
|
||||||
};
|
};
|
||||||
let Some(current_history_path) = history.file_path() else {
|
let Some(current_history_path) = history.file_path() else {
|
||||||
return Err(ShellError::ConfigDirNotFound {
|
return Err(ShellError::DataDirNotFound {
|
||||||
span: Some(call.head),
|
span: Some(call.head),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -8,38 +8,39 @@ use rstest::rstest;
|
|||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
struct Test {
|
struct Test {
|
||||||
cfg_dir: TempDir,
|
cfg_and_data_dir: TempDir,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Test {
|
impl Test {
|
||||||
fn new(history_format: &'static str) -> Self {
|
fn new(history_format: &'static str) -> Self {
|
||||||
let cfg_dir = tempfile::Builder::new()
|
let cfg_and_data_dir = tempfile::Builder::new()
|
||||||
.prefix("history_import_test")
|
.prefix("history_import_test")
|
||||||
.tempdir()
|
.tempdir()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
// Assigning to $env.config.history.file_format seems to work only in startup
|
// Assigning to $env.config.history.file_format seems to work only in startup
|
||||||
// configuration.
|
// configuration.
|
||||||
std::fs::write(
|
std::fs::write(
|
||||||
cfg_dir.path().join("env.nu"),
|
cfg_and_data_dir.path().join("env.nu"),
|
||||||
format!("$env.config.history.file_format = {history_format:?}"),
|
format!("$env.config.history.file_format = {history_format:?}"),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
Self { cfg_dir }
|
Self { cfg_and_data_dir }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn nu(&self, cmd: impl AsRef<str>) -> Outcome {
|
fn nu(&self, cmd: impl AsRef<str>) -> Outcome {
|
||||||
let env = [(
|
let config_and_data_dir = self.cfg_and_data_dir.path().to_str().unwrap().to_string();
|
||||||
"XDG_CONFIG_HOME".to_string(),
|
let env = [
|
||||||
self.cfg_dir.path().to_str().unwrap().to_string(),
|
("XDG_CONFIG_HOME".to_string(), config_and_data_dir.clone()),
|
||||||
)];
|
("XDG_DATA_HOME".to_string(), config_and_data_dir),
|
||||||
let env_config = self.cfg_dir.path().join("env.nu");
|
];
|
||||||
|
let env_config = self.cfg_and_data_dir.path().join("env.nu");
|
||||||
nu!(envs: env, env_config: env_config, cmd.as_ref())
|
nu!(envs: env, env_config: env_config, cmd.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_plaintext(&self) -> Result<FileBackedHistory, ReedlineError> {
|
fn open_plaintext(&self) -> Result<FileBackedHistory, ReedlineError> {
|
||||||
FileBackedHistory::with_file(
|
FileBackedHistory::with_file(
|
||||||
100,
|
100,
|
||||||
self.cfg_dir
|
self.cfg_and_data_dir
|
||||||
.path()
|
.path()
|
||||||
.join("nushell")
|
.join("nushell")
|
||||||
.join(HistoryFileFormat::Plaintext.default_file_name()),
|
.join(HistoryFileFormat::Plaintext.default_file_name()),
|
||||||
@ -48,7 +49,7 @@ impl Test {
|
|||||||
|
|
||||||
fn open_sqlite(&self) -> Result<SqliteBackedHistory, ReedlineError> {
|
fn open_sqlite(&self) -> Result<SqliteBackedHistory, ReedlineError> {
|
||||||
SqliteBackedHistory::with_file(
|
SqliteBackedHistory::with_file(
|
||||||
self.cfg_dir
|
self.cfg_and_data_dir
|
||||||
.path()
|
.path()
|
||||||
.join("nushell")
|
.join("nushell")
|
||||||
.join(HistoryFileFormat::Sqlite.default_file_name()),
|
.join(HistoryFileFormat::Sqlite.default_file_name()),
|
||||||
|
@ -6,9 +6,12 @@ pub fn home_dir() -> Option<AbsolutePathBuf> {
|
|||||||
dirs::home_dir().and_then(|home| AbsolutePathBuf::try_from(home).ok())
|
dirs::home_dir().and_then(|home| AbsolutePathBuf::try_from(home).ok())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the data directory for the current platform or XDG_DATA_HOME if specified.
|
/// Return the nushell data directory.
|
||||||
pub fn data_dir() -> Option<AbsolutePathBuf> {
|
pub fn nu_data_dir() -> Option<AbsolutePathBuf> {
|
||||||
configurable_dir_path("XDG_DATA_HOME", dirs::data_dir)
|
configurable_dir_path("XDG_DATA_HOME", dirs::data_dir).map(|mut path| {
|
||||||
|
path.push("nushell");
|
||||||
|
path
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the cache directory for the current platform or XDG_CACHE_HOME if specified.
|
/// Return the cache directory for the current platform or XDG_CACHE_HOME if specified.
|
||||||
@ -18,9 +21,9 @@ pub fn cache_dir() -> Option<AbsolutePathBuf> {
|
|||||||
|
|
||||||
/// Return the nushell config directory.
|
/// Return the nushell config directory.
|
||||||
pub fn nu_config_dir() -> Option<AbsolutePathBuf> {
|
pub fn nu_config_dir() -> Option<AbsolutePathBuf> {
|
||||||
configurable_dir_path("XDG_CONFIG_HOME", dirs::config_dir).map(|mut p| {
|
configurable_dir_path("XDG_CONFIG_HOME", dirs::config_dir).map(|mut path| {
|
||||||
p.push("nushell");
|
path.push("nushell");
|
||||||
p
|
path
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ mod trailing_slash;
|
|||||||
|
|
||||||
pub use components::components;
|
pub use components::components;
|
||||||
pub use expansions::{canonicalize_with, expand_path_with, expand_to_real_path, locate_in_dirs};
|
pub use expansions::{canonicalize_with, expand_path_with, expand_to_real_path, locate_in_dirs};
|
||||||
pub use helpers::{cache_dir, data_dir, home_dir, nu_config_dir};
|
pub use helpers::{cache_dir, home_dir, nu_config_dir, nu_data_dir};
|
||||||
pub use path::*;
|
pub use path::*;
|
||||||
pub use tilde::expand_tilde;
|
pub use tilde::expand_tilde;
|
||||||
pub use trailing_slash::{has_trailing_slash, strip_trailing_slash};
|
pub use trailing_slash::{has_trailing_slash, strip_trailing_slash};
|
||||||
|
@ -47,10 +47,46 @@ pub struct HistoryConfig {
|
|||||||
|
|
||||||
impl HistoryConfig {
|
impl HistoryConfig {
|
||||||
pub fn file_path(&self) -> Option<std::path::PathBuf> {
|
pub fn file_path(&self) -> Option<std::path::PathBuf> {
|
||||||
nu_path::nu_config_dir().map(|mut history_path| {
|
let history_path: std::path::PathBuf = nu_path::nu_data_dir().map(|mut history_path| {
|
||||||
history_path.push(self.file_format.default_file_name());
|
history_path.push(self.file_format.default_file_name());
|
||||||
history_path.into()
|
history_path.into()
|
||||||
})
|
})?;
|
||||||
|
|
||||||
|
self.maybe_migrate_history_file_path(history_path.clone());
|
||||||
|
|
||||||
|
Some(history_path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn maybe_migrate_history_file_path(&self, modern_history_path: std::path::PathBuf) {
|
||||||
|
let maybe_pre_0_99_1_history_path: Option<std::path::PathBuf> = nu_path::nu_config_dir()
|
||||||
|
.map(|mut path| {
|
||||||
|
path.push(self.file_format.default_file_name());
|
||||||
|
path.into()
|
||||||
|
});
|
||||||
|
|
||||||
|
let Some(pre_0_99_1_history_path) = maybe_pre_0_99_1_history_path else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if modern_history_path == pre_0_99_1_history_path {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pre_0_99_1_history_path.exists() || modern_history_path.exists() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Create the base directory? `std::fs::create_dir(modern_history_path.parent())`
|
||||||
|
log::info!("Moving {pre_0_99_1_history_path:?} to {modern_history_path:?}");
|
||||||
|
let result = std::fs::rename(pre_0_99_1_history_path.clone(), modern_history_path.clone());
|
||||||
|
|
||||||
|
if result.is_err() {
|
||||||
|
// TODO: Report an error.
|
||||||
|
// It seems a shame to create a whole new error for something that isn't going to
|
||||||
|
// be relevant for the lifetime of Nushell. But panicking seems a bit overkill for an
|
||||||
|
// innocent migration.
|
||||||
|
log::warn!("Couldn't migrate {pre_0_99_1_history_path:?} to {modern_history_path:?}. Error: {result:?}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1434,6 +1434,21 @@ On Windows, this would be %USERPROFILE%\AppData\Roaming"#
|
|||||||
span: Option<Span>,
|
span: Option<Span>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// The data directory could not be found
|
||||||
|
#[error("The data directory could not be found")]
|
||||||
|
#[diagnostic(
|
||||||
|
code(nu::shell::data_dir_not_found),
|
||||||
|
help(
|
||||||
|
r#"On Linux, this would be $XDG_DATA_HOME or $HOME/.local/share.
|
||||||
|
On MacOS, this would be `$HOME/Library/Application Support`
|
||||||
|
On Windows, this would be {{FOLDERID_RoamingAppData}}"#
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
DataDirNotFound {
|
||||||
|
#[label = "Could not find state directory"]
|
||||||
|
span: Option<Span>,
|
||||||
|
},
|
||||||
|
|
||||||
/// XDG_CONFIG_HOME was set to an invalid path
|
/// XDG_CONFIG_HOME was set to an invalid path
|
||||||
#[error("$env.XDG_CONFIG_HOME ({xdg}) is invalid, using default config directory instead: {default}")]
|
#[error("$env.XDG_CONFIG_HOME ({xdg}) is invalid, using default config directory instead: {default}")]
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
|
@ -153,9 +153,8 @@ pub(crate) fn create_nu_constant(engine_state: &EngineState, span: Span) -> Valu
|
|||||||
|
|
||||||
record.push(
|
record.push(
|
||||||
"data-dir",
|
"data-dir",
|
||||||
if let Some(path) = nu_path::data_dir() {
|
if let Some(path) = nu_path::nu_data_dir() {
|
||||||
let mut canon_data_path = canonicalize_path(engine_state, path.as_ref());
|
let canon_data_path = canonicalize_path(engine_state, path.as_ref());
|
||||||
canon_data_path.push("nushell");
|
|
||||||
Value::string(canon_data_path.to_string_lossy(), span)
|
Value::string(canon_data_path.to_string_lossy(), span)
|
||||||
} else {
|
} else {
|
||||||
Value::error(
|
Value::error(
|
||||||
|
@ -137,8 +137,7 @@ fn main() -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let default_nushell_completions_path = if let Some(mut path) = nu_path::data_dir() {
|
let default_nushell_completions_path = if let Some(mut path) = nu_path::nu_data_dir() {
|
||||||
path.push("nushell");
|
|
||||||
path.push("completions");
|
path.push("completions");
|
||||||
path.into()
|
path.into()
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user