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