nu_protocol/config/
mod.rs

1//! Module containing the internal representation of user configuration
2
3use crate::FromValue;
4use crate::{self as nu_protocol};
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::{BannerKind, 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 show_hints: bool,
58    pub history: HistoryConfig,
59    pub keybindings: Vec<ParsedKeybinding>,
60    pub menus: Vec<ParsedMenu>,
61    pub hooks: Hooks,
62    pub rm: RmConfig,
63    pub shell_integration: ShellIntegrationConfig,
64    pub buffer_editor: Value,
65    pub show_banner: BannerKind,
66    pub bracketed_paste: bool,
67    pub render_right_prompt_on_last_line: bool,
68    pub explore: HashMap<String, Value>,
69    pub cursor_shape: CursorShapeConfig,
70    pub datetime_format: DatetimeFormatConfig,
71    pub error_style: ErrorStyle,
72    pub error_lines: i64,
73    pub display_errors: DisplayErrors,
74    pub use_kitty_protocol: bool,
75    pub highlight_resolved_externals: bool,
76    /// Configuration for plugins.
77    ///
78    /// Users can provide configuration for a plugin through this entry.  The entry name must
79    /// match the registered plugin name so `plugin add nu_plugin_example` will be able to place
80    /// its configuration under a `nu_plugin_example` column.
81    pub plugins: HashMap<String, Value>,
82    /// Configuration for plugin garbage collection.
83    pub plugin_gc: PluginGcConfigs,
84}
85
86impl Default for Config {
87    fn default() -> Config {
88        Config {
89            show_banner: BannerKind::default(),
90
91            table: TableConfig::default(),
92            rm: RmConfig::default(),
93            ls: LsConfig::default(),
94
95            datetime_format: DatetimeFormatConfig::default(),
96
97            explore: HashMap::new(),
98
99            history: HistoryConfig::default(),
100
101            completions: CompletionConfig::default(),
102
103            recursion_limit: 50,
104
105            filesize: FilesizeConfig::default(),
106
107            cursor_shape: CursorShapeConfig::default(),
108
109            color_config: HashMap::new(),
110            footer_mode: FooterMode::RowCount(25),
111            float_precision: 2,
112            buffer_editor: Value::nothing(Span::unknown()),
113            use_ansi_coloring: UseAnsiColoring::default(),
114            bracketed_paste: true,
115            edit_mode: EditBindings::default(),
116            show_hints: true,
117
118            shell_integration: ShellIntegrationConfig::default(),
119
120            render_right_prompt_on_last_line: false,
121
122            hooks: Hooks::new(),
123
124            menus: Vec::new(),
125
126            keybindings: Vec::new(),
127
128            error_style: ErrorStyle::default(),
129            error_lines: 1,
130            display_errors: DisplayErrors::default(),
131
132            use_kitty_protocol: false,
133            highlight_resolved_externals: false,
134
135            plugins: HashMap::new(),
136            plugin_gc: PluginGcConfigs::default(),
137        }
138    }
139}
140
141impl UpdateFromValue for Config {
142    fn update<'a>(
143        &mut self,
144        value: &'a Value,
145        path: &mut ConfigPath<'a>,
146        errors: &mut ConfigErrors,
147    ) {
148        let Value::Record { val: record, .. } = value else {
149            errors.type_mismatch(path, Type::record(), value);
150            return;
151        };
152
153        for (col, val) in record.iter() {
154            let path = &mut path.push(col);
155            match col.as_str() {
156                "ls" => self.ls.update(val, path, errors),
157                "rm" => self.rm.update(val, path, errors),
158                "history" => self.history.update(val, path, errors),
159                "completions" => self.completions.update(val, path, errors),
160                "cursor_shape" => self.cursor_shape.update(val, path, errors),
161                "table" => self.table.update(val, path, errors),
162                "filesize" => self.filesize.update(val, path, errors),
163                "explore" => self.explore.update(val, path, errors),
164                "color_config" => self.color_config.update(val, path, errors),
165                "footer_mode" => self.footer_mode.update(val, path, errors),
166                "float_precision" => self.float_precision.update(val, path, errors),
167                "use_ansi_coloring" => self.use_ansi_coloring.update(val, path, errors),
168                "edit_mode" => self.edit_mode.update(val, path, errors),
169                "show_hints" => self.show_hints.update(val, path, errors),
170                "shell_integration" => self.shell_integration.update(val, path, errors),
171                "buffer_editor" => match val {
172                    Value::Nothing { .. } | Value::String { .. } => {
173                        self.buffer_editor = val.clone();
174                    }
175                    Value::List { vals, .. }
176                        if vals.iter().all(|val| matches!(val, Value::String { .. })) =>
177                    {
178                        self.buffer_editor = val.clone();
179                    }
180                    _ => errors.type_mismatch(
181                        path,
182                        Type::custom("string, list<string>, or nothing"),
183                        val,
184                    ),
185                },
186                "show_banner" => self.show_banner.update(val, path, errors),
187                "display_errors" => self.display_errors.update(val, path, errors),
188                "render_right_prompt_on_last_line" => self
189                    .render_right_prompt_on_last_line
190                    .update(val, path, errors),
191                "bracketed_paste" => self.bracketed_paste.update(val, path, errors),
192                "use_kitty_protocol" => self.use_kitty_protocol.update(val, path, errors),
193                "highlight_resolved_externals" => {
194                    self.highlight_resolved_externals.update(val, path, errors)
195                }
196                "plugins" => self.plugins.update(val, path, errors),
197                "plugin_gc" => self.plugin_gc.update(val, path, errors),
198                "menus" => match Vec::from_value(val.clone()) {
199                    Ok(menus) => self.menus = menus,
200                    Err(err) => errors.error(err.into()),
201                },
202                "keybindings" => match Vec::from_value(val.clone()) {
203                    Ok(keybindings) => self.keybindings = keybindings,
204                    Err(err) => errors.error(err.into()),
205                },
206                "hooks" => self.hooks.update(val, path, errors),
207                "datetime_format" => self.datetime_format.update(val, path, errors),
208                "error_style" => self.error_style.update(val, path, errors),
209                "error_lines" => {
210                    if let Ok(lines) = val.as_int() {
211                        if lines >= 0 {
212                            self.error_lines = lines;
213                        } else {
214                            errors.invalid_value(path, "an int greater than or equal to 0", val);
215                        }
216                    } else {
217                        errors.type_mismatch(path, Type::Int, val);
218                    }
219                }
220                "recursion_limit" => {
221                    if let Ok(limit) = val.as_int() {
222                        if limit > 1 {
223                            self.recursion_limit = limit;
224                        } else {
225                            errors.invalid_value(path, "an int greater than 1", val);
226                        }
227                    } else {
228                        errors.type_mismatch(path, Type::Int, val);
229                    }
230                }
231                _ => errors.unknown_option(path, val),
232            }
233        }
234    }
235}
236
237impl Config {
238    pub fn update_from_value(
239        &mut self,
240        old: &Config,
241        value: &Value,
242    ) -> Result<Option<ShellWarning>, ShellError> {
243        // Current behaviour is that config errors are displayed, but do not prevent the rest
244        // of the config from being updated (fields with errors are skipped/not updated).
245        // Errors are simply collected one-by-one and wrapped into a ShellError variant at the end.
246        let mut errors = ConfigErrors::new(old);
247        let mut path = ConfigPath::new();
248
249        self.update(value, &mut path, &mut errors);
250
251        errors.check()
252    }
253}