mirror of
https://github.com/nushell/nushell.git
synced 2025-05-31 07:08:22 +02:00
# Description This PR standardizes updates to the config through a new `UpdateFromValue` trait. For now, this trait is private in case we need to make changes to it. Note that this PR adds some additional `ShellError` cases to create standard error messages for config errors. A follow-up PR will move usages of the old error cases to these new ones. This PR also uses `Type::custom` in lots of places (e.g., for string enums). Not sure if this is something we want to encourage. # User-Facing Changes Should be none.
236 lines
8.6 KiB
Rust
236 lines
8.6 KiB
Rust
//! 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<String, Value>,
|
|
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<ParsedKeybinding>,
|
|
pub menus: Vec<ParsedMenu>,
|
|
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<String, Value>,
|
|
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<String, Value>,
|
|
/// 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<string>, 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<ShellError> {
|
|
// 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()
|
|
}
|
|
}
|