Skip to main content

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