Skip to main content

nu_protocol/config/
defaults.rs

1//! Built-in defaults for `$env.config` that must be visible without loading
2//! `default_config.nu` (e.g. under `nu -n --no-std-lib`).
3//!
4//! Includes:
5//! - `color_config` theme (formerly only in `default_config.nu`)
6//! - default Reedline menus
7//! - Nushell-owned menu keybindings (not reedline's base emacs/vi maps)
8
9use super::reedline::{ParsedKeybinding, ParsedMenu};
10use crate::{Record, Span, Value, record};
11use std::collections::HashMap;
12
13fn span() -> Span {
14    Span::unknown()
15}
16
17fn str(s: &str) -> Value {
18    Value::string(s, span())
19}
20
21fn bool(b: bool) -> Value {
22    Value::bool(b, span())
23}
24
25fn int(i: i64) -> Value {
26    Value::int(i, span())
27}
28
29fn rec(r: Record) -> Value {
30    Value::record(r, span())
31}
32
33/// Default syntax/UI color theme, previously embedded in `default_config.nu`.
34pub fn default_color_config() -> HashMap<String, Value> {
35    let entries: &[(&str, Value)] = &[
36        ("separator", str("default")),
37        (
38            "leading_trailing_space_bg",
39            rec(record! { "attr" => str("n") }),
40        ),
41        ("header", str("green_bold")),
42        ("empty", str("blue")),
43        ("bool", str("light_cyan")),
44        ("int", str("default")),
45        ("filesize", str("cyan")),
46        ("duration", str("default")),
47        ("datetime", str("purple")),
48        ("range", str("default")),
49        ("float", str("default")),
50        ("string", str("default")),
51        ("nothing", str("default")),
52        ("binary", str("default")),
53        ("binary_null_char", str("grey42")),
54        ("binary_printable", str("cyan_bold")),
55        ("binary_whitespace", str("green_bold")),
56        ("binary_ascii_other", str("purple_bold")),
57        ("binary_non_ascii", str("yellow_bold")),
58        ("cell-path", str("default")),
59        ("row_index", str("green_bold")),
60        ("record", str("default")),
61        ("list", str("default")),
62        ("closure", str("green_bold")),
63        ("glob", str("cyan_bold")),
64        ("semver", str("cyan_bold")),
65        ("semver-range", str("cyan_bold")),
66        ("block", str("default")),
67        ("hints", str("dark_gray")),
68        (
69            "search_result",
70            rec(record! {
71                "bg" => str("red"),
72                "fg" => str("default"),
73            }),
74        ),
75        ("selection", rec(record! { "attr" => str("r") })),
76        // "n" (no styling) keeps the cursor cell plain inside a selection: a
77        // reversing block cursor reverses it back to the flat selection look,
78        // while thinner cursor shapes stay visible instead of drowning in it.
79        ("selection_cursor", rec(record! { "attr" => str("n") })),
80        ("shape_binary", str("purple_bold")),
81        ("shape_block", str("blue_bold")),
82        ("shape_bool", str("light_cyan")),
83        ("shape_closure", str("green_bold")),
84        ("shape_custom", str("green")),
85        ("shape_datetime", str("cyan_bold")),
86        ("shape_directory", str("cyan")),
87        ("shape_external", str("cyan")),
88        ("shape_externalarg", str("green_bold")),
89        ("shape_external_resolved", str("light_yellow_bold")),
90        ("shape_filepath", str("cyan")),
91        ("shape_flag", str("blue_bold")),
92        ("shape_float", str("purple_bold")),
93        ("shape_glob_interpolation", str("cyan_bold")),
94        ("shape_globpattern", str("cyan_bold")),
95        ("shape_int", str("purple_bold")),
96        ("shape_internalcall", str("cyan_bold")),
97        ("shape_keyword", str("cyan_bold")),
98        ("shape_list", str("cyan_bold")),
99        ("shape_literal", str("blue")),
100        ("shape_match_pattern", str("green")),
101        // String form so matching-brackets merge works with `lookup_ansi_color_style`
102        ("shape_matching_brackets", str("default_underline")),
103        ("shape_nothing", str("light_cyan")),
104        ("shape_operator", str("yellow")),
105        ("shape_pipe", str("purple_bold")),
106        ("shape_range", str("yellow_bold")),
107        ("shape_record", str("cyan_bold")),
108        ("shape_redirection", str("purple_bold")),
109        ("shape_signature", str("green_bold")),
110        ("shape_string", str("green")),
111        ("shape_string_interpolation", str("cyan_bold")),
112        ("shape_table", str("blue_bold")),
113        ("shape_variable", str("purple")),
114        ("shape_vardecl", str("purple")),
115        ("shape_raw_string", str("light_purple")),
116        (
117            "shape_garbage",
118            rec(record! {
119                "fg" => str("default"),
120                "bg" => str("red"),
121                "attr" => str("b"),
122            }),
123        ),
124    ];
125
126    entries
127        .iter()
128        .map(|(k, v)| ((*k).to_string(), v.clone()))
129        .collect()
130}
131
132fn menu_style_basic() -> Value {
133    rec(record! {
134        "text" => str("green"),
135        "selected_text" => str("green_reverse"),
136        "description_text" => str("yellow"),
137    })
138}
139
140/// Default Reedline menus applied when none are configured by the user.
141pub fn default_menus() -> Vec<ParsedMenu> {
142    vec![
143        ParsedMenu {
144            name: str("completion_menu"),
145            marker: str("| "),
146            only_buffer_difference: Some(bool(false)),
147            input_mode: None,
148            output_mode: None,
149            style: menu_style_basic(),
150            r#type: rec(record! {
151                "layout" => str("columnar"),
152                "columns" => int(4),
153                "col_width" => int(20),
154                "col_padding" => int(2),
155                "tab_traversal" => str("horizontal"),
156            }),
157            source: None,
158        },
159        ParsedMenu {
160            name: str("ide_completion_menu"),
161            marker: str("| "),
162            only_buffer_difference: Some(bool(false)),
163            input_mode: None,
164            output_mode: None,
165            style: rec(record! {
166                "text" => str("green"),
167                "selected_text" => rec(record! { "attr" => str("r") }),
168                "description_text" => str("yellow"),
169                "match_text" => rec(record! { "attr" => str("u") }),
170                "selected_match_text" => rec(record! { "attr" => str("ur") }),
171            }),
172            r#type: rec(record! {
173                "layout" => str("ide"),
174                "min_completion_width" => int(0),
175                "max_completion_width" => int(50),
176                "max_completion_height" => int(10),
177                "padding" => int(0),
178                "border" => bool(true),
179                "cursor_offset" => int(0),
180                "description_mode" => str("prefer_right"),
181                "min_description_width" => int(15),
182                "max_description_width" => int(50),
183                "max_description_height" => int(10),
184                "description_offset" => int(1),
185                "correct_cursor_pos" => bool(false),
186            }),
187            source: None,
188        },
189        ParsedMenu {
190            name: str("history_menu"),
191            marker: str("? "),
192            only_buffer_difference: Some(bool(true)),
193            input_mode: None,
194            output_mode: None,
195            style: menu_style_basic(),
196            r#type: rec(record! {
197                "layout" => str("list"),
198                "page_size" => int(10),
199            }),
200            source: None,
201        },
202        ParsedMenu {
203            name: str("help_menu"),
204            marker: str("? "),
205            only_buffer_difference: Some(bool(true)),
206            input_mode: None,
207            output_mode: None,
208            style: menu_style_basic(),
209            r#type: rec(record! {
210                "layout" => str("description"),
211                "columns" => int(4),
212                "col_width" => int(20),
213                "col_padding" => int(2),
214                "selection_rows" => int(4),
215                "description_rows" => int(15),
216            }),
217            source: None,
218        },
219    ]
220}
221
222/// Every edit mode the Nushell-owned keybindings below apply to.
223///
224/// The helix tables are named unconditionally, like `cursor_shape.helix_*`: a
225/// build without the `helix` feature has no table to bind them into and skips
226/// them when the keybindings are applied.
227fn all_modes() -> Value {
228    Value::list(
229        vec![
230            str("emacs"),
231            str("vi_normal"),
232            str("vi_insert"),
233            str("helix_normal"),
234            str("helix_select"),
235            str("helix_insert"),
236        ],
237        span(),
238    )
239}
240
241fn keybinding(name: &str, modifier: &str, keycode: &str, event: Value) -> ParsedKeybinding {
242    ParsedKeybinding {
243        name: Some(str(name)),
244        modifier: str(modifier),
245        keycode: str(keycode),
246        event,
247        mode: all_modes(),
248    }
249}
250
251fn until(events: Vec<Value>) -> Value {
252    rec(record! {
253        "until" => Value::list(events, span()),
254    })
255}
256
257fn send(name: &str) -> Value {
258    rec(record! {
259        "send" => str(name),
260    })
261}
262
263fn send_menu(menu_name: &str) -> Value {
264    rec(record! {
265        "send" => str("menu"),
266        "name" => str(menu_name),
267    })
268}
269
270fn edit(name: &str) -> Value {
271    rec(record! {
272        "edit" => str(name),
273    })
274}
275
276/// Default `$env.config.explore` values matching `ExploreConfig::default()`
277/// colors in `nu-explore` (inherit-from-terminal styles stay unset).
278pub fn default_explore() -> HashMap<String, Value> {
279    [
280        ("selected_cell", rec(record! { "bg" => str("light_blue") })),
281        (
282            "highlight",
283            rec(record! {
284                "fg" => str("black"),
285                "bg" => str("yellow"),
286            }),
287        ),
288        (
289            "status",
290            rec(record! {
291                "success" => rec(record! {
292                    "fg" => str("black"),
293                    "bg" => str("green"),
294                }),
295                "error" => rec(record! {
296                    "fg" => str("white"),
297                    "bg" => str("red"),
298                }),
299            }),
300        ),
301        (
302            "try",
303            rec(record! {
304                "reactive" => bool(false),
305            }),
306        ),
307    ]
308    .into_iter()
309    .map(|(k, v)| (k.to_string(), v))
310    .collect()
311}
312
313/// Nushell-owned menu keybindings (Tab completion, history menu, help, etc.).
314///
315/// Reedline's base emacs/vi maps are **not** included; those remain library
316/// defaults applied underneath in `create_keybindings`.
317pub fn default_keybindings() -> Vec<ParsedKeybinding> {
318    vec![
319        keybinding(
320            "completion_menu",
321            "none",
322            "tab",
323            until(vec![
324                send_menu("completion_menu"),
325                send("menunext"),
326                edit("complete"),
327            ]),
328        ),
329        keybinding(
330            "ide_completion_menu",
331            "control",
332            "space",
333            until(vec![
334                send_menu("ide_completion_menu"),
335                send("menunext"),
336                edit("complete"),
337            ]),
338        ),
339        keybinding(
340            "completion_previous",
341            "shift",
342            "backtab",
343            send("menuprevious"),
344        ),
345        keybinding(
346            "history_menu",
347            "control",
348            "char_r",
349            send_menu("history_menu"),
350        ),
351        keybinding("next_page_menu", "control", "char_x", send("menupagenext")),
352        keybinding(
353            "undo_or_previous_page_menu",
354            "control",
355            "char_z",
356            until(vec![send("menupageprevious"), edit("undo")]),
357        ),
358        keybinding("help_menu", "none", "f1", send_menu("help_menu")),
359        keybinding("search_history", "control", "char_q", send("searchhistory")),
360    ]
361}