nu_protocol/config/
mod.rs

1//! Module containing the internal representation of user configuration
2
3use crate as nu_protocol;
4use crate::FromValue;
5use helper::*;
6use prelude::*;
7use std::collections::HashMap;
8
9pub use ansi_coloring::UseAnsiColoring;
10pub use completions::{
11    CompletionAlgorithm, CompletionConfig, CompletionSort, ExternalCompleterConfig,
12};
13pub use datetime_format::DatetimeFormatConfig;
14pub use display_errors::DisplayErrors;
15pub use filesize::FilesizeConfig;
16pub use helper::extract_value;
17pub use history::{HistoryConfig, HistoryFileFormat};
18pub use hooks::Hooks;
19pub use ls::LsConfig;
20pub use output::ErrorStyle;
21pub use plugin_gc::{PluginGcConfig, PluginGcConfigs};
22pub use reedline::{CursorShapeConfig, EditBindings, NuCursorShape, ParsedKeybinding, ParsedMenu};
23pub use rm::RmConfig;
24pub use shell_integration::ShellIntegrationConfig;
25pub use table::{FooterMode, TableConfig, TableIndent, TableIndexMode, TableMode, TrimStrategy};
26
27mod ansi_coloring;
28mod completions;
29mod datetime_format;
30mod display_errors;
31mod error;
32mod filesize;
33mod helper;
34mod history;
35mod hooks;
36mod ls;
37mod output;
38mod plugin_gc;
39mod prelude;
40mod reedline;
41mod rm;
42mod shell_integration;
43mod table;
44
45#[derive(Clone, Debug, IntoValue, Serialize, Deserialize)]
46pub struct Config {
47    pub filesize: FilesizeConfig,
48    pub table: TableConfig,
49    pub ls: LsConfig,
50    pub color_config: HashMap<String, Value>,
51    pub footer_mode: FooterMode,
52    pub float_precision: i64,
53    pub recursion_limit: i64,
54    pub use_ansi_coloring: UseAnsiColoring,
55    pub completions: CompletionConfig,
56    pub edit_mode: EditBindings,
57    pub history: HistoryConfig,
58    pub keybindings: Vec<ParsedKeybinding>,
59    pub menus: Vec<ParsedMenu>,
60    pub hooks: Hooks,
61    pub rm: RmConfig,
62    pub shell_integration: ShellIntegrationConfig,
63    pub buffer_editor: Value,
64    pub show_banner: Value,
65    pub bracketed_paste: bool,
66    pub render_right_prompt_on_last_line: bool,
67    pub explore: HashMap<String, Value>,
68    pub cursor_shape: CursorShapeConfig,
69    pub datetime_format: DatetimeFormatConfig,
70    pub error_style: ErrorStyle,
71    pub display_errors: DisplayErrors,
72    pub use_kitty_protocol: bool,
73    pub highlight_resolved_externals: bool,
74    /// Configuration for plugins.
75    ///
76    /// Users can provide configuration for a plugin through this entry.  The entry name must
77    /// match the registered plugin name so `plugin add nu_plugin_example` will be able to place
78    /// its configuration under a `nu_plugin_example` column.
79    pub plugins: HashMap<String, Value>,
80    /// Configuration for plugin garbage collection.
81    pub plugin_gc: PluginGcConfigs,
82}
83
84impl Default for Config {
85    fn default() -> Config {
86        Config {
87            show_banner: Value::bool(true, Span::unknown()),
88
89            table: TableConfig::default(),
90            rm: RmConfig::default(),
91            ls: LsConfig::default(),
92
93            datetime_format: DatetimeFormatConfig::default(),
94
95            explore: HashMap::new(),
96
97            history: HistoryConfig::default(),
98
99            completions: CompletionConfig::default(),
100
101            recursion_limit: 50,
102
103            filesize: FilesizeConfig::default(),
104
105            cursor_shape: CursorShapeConfig::default(),
106
107            color_config: HashMap::new(),
108            footer_mode: FooterMode::RowCount(25),
109            float_precision: 2,
110            buffer_editor: Value::nothing(Span::unknown()),
111            use_ansi_coloring: UseAnsiColoring::default(),
112            bracketed_paste: true,
113            edit_mode: EditBindings::default(),
114
115            shell_integration: ShellIntegrationConfig::default(),
116
117            render_right_prompt_on_last_line: false,
118
119            hooks: Hooks::new(),
120
121            menus: Vec::new(),
122
123            keybindings: Vec::new(),
124
125            error_style: ErrorStyle::Fancy,
126            display_errors: DisplayErrors::default(),
127
128            use_kitty_protocol: false,
129            highlight_resolved_externals: false,
130
131            plugins: HashMap::new(),
132            plugin_gc: PluginGcConfigs::default(),
133        }
134    }
135}
136
137impl UpdateFromValue for Config {
138    fn update<'a>(
139        &mut self,
140        value: &'a Value,
141        path: &mut ConfigPath<'a>,
142        errors: &mut ConfigErrors,
143    ) {
144        let Value::Record { val: record, .. } = value else {
145            errors.type_mismatch(path, Type::record(), value);
146            return;
147        };
148
149        for (col, val) in record.iter() {
150            let path = &mut path.push(col);
151            match col.as_str() {
152                "ls" => self.ls.update(val, path, errors),
153                "rm" => self.rm.update(val, path, errors),
154                "history" => self.history.update(val, path, errors),
155                "completions" => self.completions.update(val, path, errors),
156                "cursor_shape" => self.cursor_shape.update(val, path, errors),
157                "table" => self.table.update(val, path, errors),
158                "filesize" => self.filesize.update(val, path, errors),
159                "explore" => self.explore.update(val, path, errors),
160                "color_config" => self.color_config.update(val, path, errors),
161                "footer_mode" => self.footer_mode.update(val, path, errors),
162                "float_precision" => self.float_precision.update(val, path, errors),
163                "use_ansi_coloring" => self.use_ansi_coloring.update(val, path, errors),
164                "edit_mode" => self.edit_mode.update(val, path, errors),
165                "shell_integration" => self.shell_integration.update(val, path, errors),
166                "buffer_editor" => match val {
167                    Value::Nothing { .. } | Value::String { .. } => {
168                        self.buffer_editor = val.clone();
169                    }
170                    Value::List { vals, .. }
171                        if vals.iter().all(|val| matches!(val, Value::String { .. })) =>
172                    {
173                        self.buffer_editor = val.clone();
174                    }
175                    _ => errors.type_mismatch(
176                        path,
177                        Type::custom("string, list<string>, or nothing"),
178                        val,
179                    ),
180                },
181                "show_banner" => self.show_banner.update(val, path, errors),
182                "display_errors" => self.display_errors.update(val, path, errors),
183                "render_right_prompt_on_last_line" => self
184                    .render_right_prompt_on_last_line
185                    .update(val, path, errors),
186                "bracketed_paste" => self.bracketed_paste.update(val, path, errors),
187                "use_kitty_protocol" => self.use_kitty_protocol.update(val, path, errors),
188                "highlight_resolved_externals" => {
189                    self.highlight_resolved_externals.update(val, path, errors)
190                }
191                "plugins" => self.plugins.update(val, path, errors),
192                "plugin_gc" => self.plugin_gc.update(val, path, errors),
193                "menus" => match Vec::from_value(val.clone()) {
194                    Ok(menus) => self.menus = menus,
195                    Err(err) => errors.error(err.into()),
196                },
197                "keybindings" => match Vec::from_value(val.clone()) {
198                    Ok(keybindings) => self.keybindings = keybindings,
199                    Err(err) => errors.error(err.into()),
200                },
201                "hooks" => self.hooks.update(val, path, errors),
202                "datetime_format" => self.datetime_format.update(val, path, errors),
203                "error_style" => self.error_style.update(val, path, errors),
204                "recursion_limit" => {
205                    if let Ok(limit) = val.as_int() {
206                        if limit > 1 {
207                            self.recursion_limit = limit;
208                        } else {
209                            errors.invalid_value(path, "an int greater than 1", val);
210                        }
211                    } else {
212                        errors.type_mismatch(path, Type::Int, val);
213                    }
214                }
215                _ => errors.unknown_option(path, val),
216            }
217        }
218    }
219}
220
221impl Config {
222    pub fn update_from_value(&mut self, old: &Config, value: &Value) -> Option<ShellError> {
223        // Current behaviour is that config errors are displayed, but do not prevent the rest
224        // of the config from being updated (fields with errors are skipped/not updated).
225        // Errors are simply collected one-by-one and wrapped into a ShellError variant at the end.
226        let mut errors = ConfigErrors::new(old);
227        let mut path = ConfigPath::new();
228
229        self.update(value, &mut path, &mut errors);
230
231        errors.into_shell_error()
232    }
233}