mirror of
https://github.com/nushell/nushell.git
synced 2024-11-07 17:14:23 +01:00
Add CLI flag to disable history (#11550)
# Description Adds a CLI flag for nushell that disables reading and writing to the history file. This will be useful for future testing and possibly our users as well. To borrow `fish` shell's terminology, this allows users to start nushell in "private" mode. # User-Facing Changes Breaking API change for `nu-protocol` (changed `Config`).
This commit is contained in:
parent
a4199ea312
commit
55bf4d847f
@ -44,6 +44,10 @@ impl Command for History {
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
|
||||
let Some(history) = engine_state.history_config() else {
|
||||
return Ok(PipelineData::empty());
|
||||
};
|
||||
|
||||
// todo for sqlite history this command should be an alias to `open ~/.config/nushell/history.sqlite3 | get history`
|
||||
if let Some(config_path) = nu_path::config_dir() {
|
||||
let clear = call.has_flag(engine_state, stack, "clear")?;
|
||||
@ -52,7 +56,7 @@ impl Command for History {
|
||||
|
||||
let mut history_path = config_path;
|
||||
history_path.push("nushell");
|
||||
match engine_state.config.history_file_format {
|
||||
match history.file_format {
|
||||
HistoryFileFormat::Sqlite => {
|
||||
history_path.push("history.sqlite3");
|
||||
}
|
||||
@ -66,8 +70,7 @@ impl Command for History {
|
||||
// TODO: FIXME also clear the auxiliary files when using sqlite
|
||||
Ok(PipelineData::empty())
|
||||
} else {
|
||||
let history_reader: Option<Box<dyn ReedlineHistory>> =
|
||||
match engine_state.config.history_file_format {
|
||||
let history_reader: Option<Box<dyn ReedlineHistory>> = match history.file_format {
|
||||
HistoryFileFormat::Sqlite => {
|
||||
SqliteBackedHistory::with_file(history_path, None, None)
|
||||
.map(|inner| {
|
||||
@ -77,18 +80,17 @@ impl Command for History {
|
||||
.ok()
|
||||
}
|
||||
|
||||
HistoryFileFormat::PlainText => FileBackedHistory::with_file(
|
||||
engine_state.config.max_history_size as usize,
|
||||
history_path,
|
||||
)
|
||||
HistoryFileFormat::PlainText => {
|
||||
FileBackedHistory::with_file(history.max_size as usize, history_path)
|
||||
.map(|inner| {
|
||||
let boxed: Box<dyn ReedlineHistory> = Box::new(inner);
|
||||
boxed
|
||||
})
|
||||
.ok(),
|
||||
.ok()
|
||||
}
|
||||
};
|
||||
|
||||
match engine_state.config.history_file_format {
|
||||
match history.file_format {
|
||||
HistoryFileFormat::PlainText => Ok(history_reader
|
||||
.and_then(|h| {
|
||||
h.search(SearchQuery::everything(SearchDirection::Forward, None))
|
||||
|
@ -17,8 +17,8 @@ use nu_protocol::{
|
||||
config::NuCursorShape,
|
||||
engine::{EngineState, Stack, StateWorkingSet},
|
||||
eval_const::create_nu_constant,
|
||||
report_error, report_error_new, HistoryFileFormat, PipelineData, ShellError, Span, Spanned,
|
||||
Value, NU_VARIABLE_ID,
|
||||
report_error, report_error_new, HistoryConfig, HistoryFileFormat, PipelineData, ShellError,
|
||||
Span, Spanned, Value, NU_VARIABLE_ID,
|
||||
};
|
||||
use nu_utils::utils::perf;
|
||||
use reedline::{
|
||||
@ -28,7 +28,7 @@ use reedline::{
|
||||
use std::{
|
||||
env::temp_dir,
|
||||
io::{self, IsTerminal, Write},
|
||||
path::Path,
|
||||
path::PathBuf,
|
||||
sync::atomic::Ordering,
|
||||
time::Instant,
|
||||
};
|
||||
@ -109,23 +109,27 @@ pub fn evaluate_repl(
|
||||
use_color,
|
||||
);
|
||||
|
||||
if let Some(history) = engine_state.history_config() {
|
||||
start_time = std::time::Instant::now();
|
||||
|
||||
// Setup history_isolation aka "history per session"
|
||||
let history_isolation = engine_state.get_config().history_isolation;
|
||||
let history_session_id = if history_isolation {
|
||||
let history_session_id = if history.isolation {
|
||||
Reedline::create_history_session_id()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
start_time = std::time::Instant::now();
|
||||
let history_path = crate::config_files::get_history_path(
|
||||
nushell_path,
|
||||
engine_state.config.history_file_format,
|
||||
);
|
||||
if let Some(history_path) = history_path.as_deref() {
|
||||
line_editor =
|
||||
update_line_editor_history(engine_state, history_path, line_editor, history_session_id)?
|
||||
if let Some(path) = crate::config_files::get_history_path(nushell_path, history.file_format)
|
||||
{
|
||||
line_editor = update_line_editor_history(
|
||||
engine_state,
|
||||
path,
|
||||
history,
|
||||
line_editor,
|
||||
history_session_id,
|
||||
)?
|
||||
};
|
||||
|
||||
perf(
|
||||
"setup history",
|
||||
start_time,
|
||||
@ -134,6 +138,7 @@ pub fn evaluate_repl(
|
||||
column!(),
|
||||
use_color,
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(s) = prerun_command {
|
||||
eval_source(
|
||||
@ -313,8 +318,9 @@ pub fn evaluate_repl(
|
||||
use_color,
|
||||
);
|
||||
|
||||
if let Some(history) = engine_state.history_config() {
|
||||
start_time = std::time::Instant::now();
|
||||
if config.sync_history_on_enter {
|
||||
if history.sync_on_enter {
|
||||
if let Err(e) = line_editor.sync_history() {
|
||||
warn!("Failed to sync history: {}", e);
|
||||
}
|
||||
@ -327,6 +333,7 @@ pub fn evaluate_repl(
|
||||
column!(),
|
||||
use_color,
|
||||
);
|
||||
}
|
||||
|
||||
start_time = std::time::Instant::now();
|
||||
// Changing the line editor based on the found keybindings
|
||||
@ -418,8 +425,10 @@ pub fn evaluate_repl(
|
||||
match input {
|
||||
Ok(Signal::Success(s)) => {
|
||||
let hostname = System::host_name();
|
||||
let history_supports_meta =
|
||||
matches!(config.history_file_format, HistoryFileFormat::Sqlite);
|
||||
let history_supports_meta = matches!(
|
||||
engine_state.history_config().map(|h| h.file_format),
|
||||
Some(HistoryFileFormat::Sqlite)
|
||||
);
|
||||
if history_supports_meta && !s.is_empty() && line_editor.has_last_command_context()
|
||||
{
|
||||
line_editor
|
||||
@ -715,17 +724,14 @@ fn store_history_id_in_engine(engine_state: &mut EngineState, line_editor: &Reed
|
||||
|
||||
fn update_line_editor_history(
|
||||
engine_state: &mut EngineState,
|
||||
history_path: &Path,
|
||||
history_path: PathBuf,
|
||||
history: HistoryConfig,
|
||||
line_editor: Reedline,
|
||||
history_session_id: Option<HistorySessionId>,
|
||||
) -> Result<Reedline, ErrReport> {
|
||||
let config = engine_state.get_config();
|
||||
let history: Box<dyn reedline::History> = match engine_state.config.history_file_format {
|
||||
let history: Box<dyn reedline::History> = match history.file_format {
|
||||
HistoryFileFormat::PlainText => Box::new(
|
||||
FileBackedHistory::with_file(
|
||||
config.max_history_size as usize,
|
||||
history_path.to_path_buf(),
|
||||
)
|
||||
FileBackedHistory::with_file(history.max_size as usize, history_path)
|
||||
.into_diagnostic()?,
|
||||
),
|
||||
HistoryFileFormat::Sqlite => Box::new(
|
||||
@ -834,14 +840,18 @@ fn trailing_slash_looks_like_path() {
|
||||
#[test]
|
||||
fn are_session_ids_in_sync() {
|
||||
let engine_state = &mut EngineState::new();
|
||||
let history_path_o =
|
||||
crate::config_files::get_history_path("nushell", engine_state.config.history_file_format);
|
||||
assert!(history_path_o.is_some());
|
||||
let history_path = history_path_o.as_deref().unwrap();
|
||||
let history = engine_state.history_config().unwrap();
|
||||
let history_path =
|
||||
crate::config_files::get_history_path("nushell", history.file_format).unwrap();
|
||||
let line_editor = reedline::Reedline::create();
|
||||
let history_session_id = reedline::Reedline::create_history_session_id();
|
||||
let line_editor =
|
||||
update_line_editor_history(engine_state, history_path, line_editor, history_session_id);
|
||||
let line_editor = update_line_editor_history(
|
||||
engine_state,
|
||||
history_path,
|
||||
history,
|
||||
line_editor,
|
||||
history_session_id,
|
||||
);
|
||||
assert_eq!(
|
||||
i64::from(line_editor.unwrap().get_history_session_id().unwrap()),
|
||||
engine_state.history_session_id
|
||||
|
@ -684,13 +684,14 @@ fn variables_completions() {
|
||||
// Test completions for $nu
|
||||
let suggestions = completer.complete("$nu.", 4);
|
||||
|
||||
assert_eq!(14, suggestions.len());
|
||||
assert_eq!(15, suggestions.len());
|
||||
|
||||
let expected: Vec<String> = vec![
|
||||
"config-path".into(),
|
||||
"current-exe".into(),
|
||||
"default-config-dir".into(),
|
||||
"env-path".into(),
|
||||
"history-enabled".into(),
|
||||
"history-path".into(),
|
||||
"home-path".into(),
|
||||
"is-interactive".into(),
|
||||
@ -709,9 +710,13 @@ fn variables_completions() {
|
||||
// Test completions for $nu.h (filter)
|
||||
let suggestions = completer.complete("$nu.h", 5);
|
||||
|
||||
assert_eq!(2, suggestions.len());
|
||||
assert_eq!(3, suggestions.len());
|
||||
|
||||
let expected: Vec<String> = vec!["history-path".into(), "home-path".into()];
|
||||
let expected: Vec<String> = vec![
|
||||
"history-enabled".into(),
|
||||
"history-path".into(),
|
||||
"home-path".into(),
|
||||
];
|
||||
|
||||
// Match results
|
||||
match_suggestions(expected, suggestions);
|
||||
|
@ -25,6 +25,25 @@ mod output;
|
||||
mod reedline;
|
||||
mod table;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
pub struct HistoryConfig {
|
||||
pub max_size: i64,
|
||||
pub sync_on_enter: bool,
|
||||
pub file_format: HistoryFileFormat,
|
||||
pub isolation: bool,
|
||||
}
|
||||
|
||||
impl Default for HistoryConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
max_size: 100_000,
|
||||
sync_on_enter: true,
|
||||
file_format: HistoryFileFormat::PlainText,
|
||||
isolation: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct Config {
|
||||
pub external_completer: Option<usize>,
|
||||
@ -46,10 +65,7 @@ pub struct Config {
|
||||
pub partial_completions: bool,
|
||||
pub completion_algorithm: CompletionAlgorithm,
|
||||
pub edit_mode: EditBindings,
|
||||
pub max_history_size: i64,
|
||||
pub sync_history_on_enter: bool,
|
||||
pub history_file_format: HistoryFileFormat,
|
||||
pub history_isolation: bool,
|
||||
pub history: HistoryConfig,
|
||||
pub keybindings: Vec<ParsedKeybinding>,
|
||||
pub menus: Vec<ParsedMenu>,
|
||||
pub hooks: Hooks,
|
||||
@ -104,10 +120,7 @@ impl Default for Config {
|
||||
|
||||
explore: HashMap::new(),
|
||||
|
||||
max_history_size: 100_000,
|
||||
sync_history_on_enter: true,
|
||||
history_file_format: HistoryFileFormat::PlainText,
|
||||
history_isolation: false,
|
||||
history: HistoryConfig::default(),
|
||||
|
||||
case_sensitive_completions: false,
|
||||
quick_completions: true,
|
||||
@ -172,7 +185,7 @@ impl Value {
|
||||
// the `2`.
|
||||
|
||||
if let Value::Record { val, .. } = self {
|
||||
val.retain_mut( |key, value| {
|
||||
val.retain_mut(|key, value| {
|
||||
let span = value.span();
|
||||
match key {
|
||||
// Grouped options
|
||||
@ -232,22 +245,23 @@ impl Value {
|
||||
}
|
||||
}
|
||||
"history" => {
|
||||
let history = &mut config.history;
|
||||
if let Value::Record { val, .. } = value {
|
||||
val.retain_mut(|key2, value| {
|
||||
let span = value.span();
|
||||
match key2 {
|
||||
"isolation" => {
|
||||
process_bool_config(value, &mut errors, &mut config.history_isolation);
|
||||
process_bool_config(value, &mut errors, &mut history.isolation);
|
||||
}
|
||||
"sync_on_enter" => {
|
||||
process_bool_config(value, &mut errors, &mut config.sync_history_on_enter);
|
||||
process_bool_config(value, &mut errors, &mut history.sync_on_enter);
|
||||
}
|
||||
"max_size" => {
|
||||
process_int_config(value, &mut errors, &mut config.max_history_size);
|
||||
process_int_config(value, &mut errors, &mut history.max_size);
|
||||
}
|
||||
"file_format" => {
|
||||
process_string_enum(
|
||||
&mut config.history_file_format,
|
||||
&mut history.file_format,
|
||||
&[key, key2],
|
||||
value,
|
||||
&mut errors);
|
||||
@ -264,10 +278,10 @@ impl Value {
|
||||
// Reconstruct
|
||||
*value = Value::record(
|
||||
record! {
|
||||
"sync_on_enter" => Value::bool(config.sync_history_on_enter, span),
|
||||
"max_size" => Value::int(config.max_history_size, span),
|
||||
"file_format" => config.history_file_format.reconstruct_value(span),
|
||||
"isolation" => Value::bool(config.history_isolation, span),
|
||||
"sync_on_enter" => Value::bool(history.sync_on_enter, span),
|
||||
"max_size" => Value::int(history.max_size, span),
|
||||
"file_format" => history.file_format.reconstruct_value(span),
|
||||
"isolation" => Value::bool(history.isolation, span),
|
||||
},
|
||||
span,
|
||||
);
|
||||
|
@ -5,8 +5,8 @@ use super::{usage::build_usage, usage::Usage, StateDelta};
|
||||
use super::{Command, EnvVars, OverlayFrame, ScopeFrame, Stack, Visibility, DEFAULT_OVERLAY_NAME};
|
||||
use crate::ast::Block;
|
||||
use crate::{
|
||||
BlockId, Config, DeclId, Example, FileId, Module, ModuleId, OverlayId, ShellError, Signature,
|
||||
Span, Type, VarId, Variable, VirtualPathId,
|
||||
BlockId, Config, DeclId, Example, FileId, HistoryConfig, Module, ModuleId, OverlayId,
|
||||
ShellError, Signature, Span, Type, VarId, Variable, VirtualPathId,
|
||||
};
|
||||
use crate::{Category, Value};
|
||||
use std::borrow::Borrow;
|
||||
@ -96,6 +96,7 @@ pub struct EngineState {
|
||||
#[cfg(feature = "plugin")]
|
||||
pub plugin_signatures: Option<PathBuf>,
|
||||
config_path: HashMap<String, PathBuf>,
|
||||
pub history_enabled: bool,
|
||||
pub history_session_id: i64,
|
||||
// If Nushell was started, e.g., with `nu spam.nu`, the file's parent is stored here
|
||||
pub(super) currently_parsed_cwd: Option<PathBuf>,
|
||||
@ -151,6 +152,7 @@ impl EngineState {
|
||||
#[cfg(feature = "plugin")]
|
||||
plugin_signatures: None,
|
||||
config_path: HashMap::new(),
|
||||
history_enabled: true,
|
||||
history_session_id: 0,
|
||||
currently_parsed_cwd: None,
|
||||
regex_cache: Arc::new(Mutex::new(LruCache::new(
|
||||
@ -720,6 +722,15 @@ impl EngineState {
|
||||
self.config.plugins.get(plugin)
|
||||
}
|
||||
|
||||
/// Returns the configuration settings for command history or `None` if history is disabled
|
||||
pub fn history_config(&self) -> Option<HistoryConfig> {
|
||||
if self.history_enabled {
|
||||
Some(self.config.history)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_var(&self, var_id: VarId) -> &Variable {
|
||||
self.vars
|
||||
.get(var_id)
|
||||
|
@ -80,7 +80,7 @@ pub fn create_nu_constant(engine_state: &EngineState, span: Span) -> Result<Valu
|
||||
"history-path",
|
||||
if let Some(mut path) = nu_path::config_dir() {
|
||||
path.push("nushell");
|
||||
match engine_state.config.history_file_format {
|
||||
match engine_state.config.history.file_format {
|
||||
HistoryFileFormat::Sqlite => {
|
||||
path.push("history.sqlite3");
|
||||
}
|
||||
@ -187,6 +187,11 @@ pub fn create_nu_constant(engine_state: &EngineState, span: Span) -> Result<Valu
|
||||
|
||||
record.push("is-login", Value::bool(engine_state.is_login, span));
|
||||
|
||||
record.push(
|
||||
"history-enabled",
|
||||
Value::bool(engine_state.history_enabled, span),
|
||||
);
|
||||
|
||||
record.push(
|
||||
"current-exe",
|
||||
if let Ok(current_exe) = std::env::current_exe() {
|
||||
|
@ -98,6 +98,7 @@ pub(crate) fn parse_commandline_args(
|
||||
#[cfg(feature = "plugin")]
|
||||
let plugin_file = call.get_flag_expr("plugin-config");
|
||||
let no_config_file = call.get_named_arg("no-config-file");
|
||||
let no_history = call.get_named_arg("no-history");
|
||||
let no_std_lib = call.get_named_arg("no-std-lib");
|
||||
let config_file = call.get_flag_expr("config");
|
||||
let env_file = call.get_flag_expr("env-config");
|
||||
@ -184,6 +185,7 @@ pub(crate) fn parse_commandline_args(
|
||||
#[cfg(feature = "plugin")]
|
||||
plugin_file,
|
||||
no_config_file,
|
||||
no_history,
|
||||
no_std_lib,
|
||||
config_file,
|
||||
env_file,
|
||||
@ -223,6 +225,7 @@ pub(crate) struct NushellCliArgs {
|
||||
#[cfg(feature = "plugin")]
|
||||
pub(crate) plugin_file: Option<Spanned<String>>,
|
||||
pub(crate) no_config_file: Option<Spanned<String>>,
|
||||
pub(crate) no_history: Option<Spanned<String>>,
|
||||
pub(crate) no_std_lib: Option<Spanned<String>>,
|
||||
pub(crate) config_file: Option<Spanned<String>>,
|
||||
pub(crate) env_file: Option<Spanned<String>>,
|
||||
@ -281,6 +284,11 @@ impl Command for Nu {
|
||||
"start with no config file and no env file",
|
||||
Some('n'),
|
||||
)
|
||||
.switch(
|
||||
"no-history",
|
||||
"disable reading and writing to command history",
|
||||
None,
|
||||
)
|
||||
.switch("no-std-lib", "start with no standard library", None)
|
||||
.named(
|
||||
"threads",
|
||||
|
@ -128,6 +128,8 @@ fn main() -> Result<()> {
|
||||
|
||||
engine_state.is_login = parsed_nu_cli_args.login_shell.is_some();
|
||||
|
||||
engine_state.history_enabled = parsed_nu_cli_args.no_history.is_none();
|
||||
|
||||
let use_color = engine_state.get_config().use_ansi_coloring;
|
||||
if let Some(level) = parsed_nu_cli_args
|
||||
.log_level
|
||||
|
Loading…
Reference in New Issue
Block a user