//! Module containing the internal representation of user configuration use crate as nu_protocol; use crate::FromValue; use helper::*; use prelude::*; use std::collections::HashMap; pub use completions::{ CompletionAlgorithm, CompletionConfig, CompletionSort, ExternalCompleterConfig, }; pub use datetime_format::DatetimeFormatConfig; pub use display_errors::DisplayErrors; pub use filesize::FilesizeConfig; pub use helper::extract_value; pub use history::{HistoryConfig, HistoryFileFormat}; pub use hooks::Hooks; pub use ls::LsConfig; pub use output::ErrorStyle; pub use plugin_gc::{PluginGcConfig, PluginGcConfigs}; pub use reedline::{CursorShapeConfig, EditBindings, NuCursorShape, ParsedKeybinding, ParsedMenu}; pub use rm::RmConfig; pub use shell_integration::ShellIntegrationConfig; pub use table::{FooterMode, TableConfig, TableIndexMode, TableMode, TrimStrategy}; mod completions; mod datetime_format; mod display_errors; mod error; mod filesize; mod helper; mod history; mod hooks; mod ls; mod output; mod plugin_gc; mod prelude; mod reedline; mod rm; mod shell_integration; mod table; #[derive(Clone, Debug, IntoValue, Serialize, Deserialize)] pub struct Config { pub filesize: FilesizeConfig, pub table: TableConfig, pub ls: LsConfig, pub color_config: HashMap, pub footer_mode: FooterMode, pub float_precision: i64, pub recursion_limit: i64, pub use_ansi_coloring: bool, pub completions: CompletionConfig, pub edit_mode: EditBindings, pub history: HistoryConfig, pub keybindings: Vec, pub menus: Vec, pub hooks: Hooks, pub rm: RmConfig, pub shell_integration: ShellIntegrationConfig, pub buffer_editor: Value, pub show_banner: bool, pub bracketed_paste: bool, pub render_right_prompt_on_last_line: bool, pub explore: HashMap, pub cursor_shape: CursorShapeConfig, pub datetime_format: DatetimeFormatConfig, pub error_style: ErrorStyle, pub display_errors: DisplayErrors, pub use_kitty_protocol: bool, pub highlight_resolved_externals: bool, /// Configuration for plugins. /// /// Users can provide configuration for a plugin through this entry. The entry name must /// match the registered plugin name so `plugin add nu_plugin_example` will be able to place /// its configuration under a `nu_plugin_example` column. pub plugins: HashMap, /// Configuration for plugin garbage collection. pub plugin_gc: PluginGcConfigs, } impl Default for Config { fn default() -> Config { Config { show_banner: true, table: TableConfig::default(), rm: RmConfig::default(), ls: LsConfig::default(), datetime_format: DatetimeFormatConfig::default(), explore: HashMap::new(), history: HistoryConfig::default(), completions: CompletionConfig::default(), recursion_limit: 50, filesize: FilesizeConfig::default(), cursor_shape: CursorShapeConfig::default(), color_config: HashMap::new(), footer_mode: FooterMode::RowCount(25), float_precision: 2, buffer_editor: Value::nothing(Span::unknown()), use_ansi_coloring: true, bracketed_paste: true, edit_mode: EditBindings::default(), shell_integration: ShellIntegrationConfig::default(), render_right_prompt_on_last_line: false, hooks: Hooks::new(), menus: Vec::new(), keybindings: Vec::new(), error_style: ErrorStyle::Fancy, display_errors: DisplayErrors::default(), use_kitty_protocol: false, highlight_resolved_externals: false, plugins: HashMap::new(), plugin_gc: PluginGcConfigs::default(), } } } impl UpdateFromValue for Config { fn update<'a>( &mut self, value: &'a Value, path: &mut ConfigPath<'a>, errors: &mut ConfigErrors, ) { let Value::Record { val: record, .. } = value else { errors.type_mismatch(path, Type::record(), value); return; }; for (col, val) in record.iter() { let path = &mut path.push(col); match col.as_str() { "ls" => self.ls.update(val, path, errors), "rm" => self.rm.update(val, path, errors), "history" => self.history.update(val, path, errors), "completions" => self.completions.update(val, path, errors), "cursor_shape" => self.cursor_shape.update(val, path, errors), "table" => self.table.update(val, path, errors), "filesize" => self.filesize.update(val, path, errors), "explore" => self.explore.update(val, path, errors), "color_config" => self.color_config.update(val, path, errors), "use_grid_icons" => { // TODO: delete it after 0.99 errors.deprecated_option(path, "use `grid -i`", val.span()); } "footer_mode" => self.footer_mode.update(val, path, errors), "float_precision" => self.float_precision.update(val, path, errors), "use_ansi_coloring" => self.use_ansi_coloring.update(val, path, errors), "edit_mode" => self.edit_mode.update(val, path, errors), "shell_integration" => self.shell_integration.update(val, path, errors), "buffer_editor" => match val { Value::Nothing { .. } | Value::String { .. } => { self.buffer_editor = val.clone(); } Value::List { vals, .. } if vals.iter().all(|val| matches!(val, Value::String { .. })) => { self.buffer_editor = val.clone(); } _ => errors.type_mismatch( path, Type::custom("string, list, or nothing"), val, ), }, "show_banner" => self.show_banner.update(val, path, errors), "display_errors" => self.display_errors.update(val, path, errors), "render_right_prompt_on_last_line" => self .render_right_prompt_on_last_line .update(val, path, errors), "bracketed_paste" => self.bracketed_paste.update(val, path, errors), "use_kitty_protocol" => self.use_kitty_protocol.update(val, path, errors), "highlight_resolved_externals" => { self.highlight_resolved_externals.update(val, path, errors) } "plugins" => self.plugins.update(val, path, errors), "plugin_gc" => self.plugin_gc.update(val, path, errors), "menus" => match Vec::from_value(val.clone()) { Ok(menus) => self.menus = menus, Err(err) => errors.error(err.into()), }, "keybindings" => match Vec::from_value(val.clone()) { Ok(keybindings) => self.keybindings = keybindings, Err(err) => errors.error(err.into()), }, "hooks" => self.hooks.update(val, path, errors), "datetime_format" => self.datetime_format.update(val, path, errors), "error_style" => self.error_style.update(val, path, errors), "recursion_limit" => { if let Ok(limit) = val.as_int() { if limit > 1 { self.recursion_limit = limit; } else { errors.invalid_value(path, "an int greater than 1", val); } } else { errors.type_mismatch(path, Type::Int, val); } } _ => errors.unknown_option(path, val), } } } } impl Config { pub fn update_from_value(&mut self, old: &Config, value: &Value) -> Option { // Current behaviour is that config errors are displayed, but do not prevent the rest // of the config from being updated (fields with errors are skipped/not updated). // Errors are simply collected one-by-one and wrapped into a ShellError variant at the end. let mut errors = ConfigErrors::new(old); let mut path = ConfigPath::new(); self.update(value, &mut path, &mut errors); errors.into_shell_error() } }