From abd230e12e72d5f1863fc33e91ce11733913ce92 Mon Sep 17 00:00:00 2001 From: Ian Manske Date: Thu, 5 Sep 2024 00:44:23 -0700 Subject: [PATCH] Use `IntoValue` in config code (#13751) # Description Cleans up and refactors the config code using the `IntoValue` macro. Shoutout to @cptpiepmatz for making the macro! # User-Facing Changes Should be none. # After Submitting Somehow refactor the reverse transformation. --- .../nu-cli/src/commands/history/history_.rs | 6 +- .../src/completions/command_completions.rs | 6 +- crates/nu-cli/src/completions/completer.rs | 12 +- .../src/completions/completion_common.rs | 2 +- crates/nu-cli/src/config_files.rs | 2 +- crates/nu-cli/src/eval_cmds.rs | 4 +- crates/nu-cli/src/prompt_update.rs | 6 +- crates/nu-cli/src/reedline_config.rs | 28 +- crates/nu-cli/src/repl.rs | 38 +- crates/nu-command/src/filesystem/rm.rs | 2 +- crates/nu-command/src/viewers/table.rs | 16 +- crates/nu-derive-value/src/from.rs | 4 +- crates/nu-derive-value/src/names.rs | 18 +- crates/nu-explore/src/explore.rs | 4 +- crates/nu-explore/src/nu_common/table.rs | 8 +- crates/nu-protocol/src/config/completer.rs | 81 +- .../nu-protocol/src/config/datetime_format.rs | 8 + crates/nu-protocol/src/config/filesize.rs | 17 + crates/nu-protocol/src/config/helper.rs | 12 +- crates/nu-protocol/src/config/history.rs | 41 + crates/nu-protocol/src/config/hooks.rs | 28 +- crates/nu-protocol/src/config/ls.rs | 17 + crates/nu-protocol/src/config/mod.rs | 1314 ++++++++--------- crates/nu-protocol/src/config/output.rs | 31 +- crates/nu-protocol/src/config/plugin_gc.rs | 69 +- crates/nu-protocol/src/config/prelude.rs | 3 + crates/nu-protocol/src/config/reedline.rs | 154 +- crates/nu-protocol/src/config/rm.rs | 16 + .../src/config/shell_integration.rs | 28 + crates/nu-protocol/src/config/table.rs | 185 +-- crates/nu-protocol/src/eval_const.rs | 2 +- crates/nu-protocol/src/value/filesize.rs | 4 +- crates/nu-protocol/src/value/mod.rs | 4 +- crates/nu-table/src/common.rs | 16 +- crates/nu-table/src/types/collapse.rs | 2 +- crates/nu-table/src/types/general.rs | 8 +- crates/nu-table/src/types/mod.rs | 2 +- 37 files changed, 992 insertions(+), 1206 deletions(-) create mode 100644 crates/nu-protocol/src/config/datetime_format.rs create mode 100644 crates/nu-protocol/src/config/filesize.rs create mode 100644 crates/nu-protocol/src/config/history.rs create mode 100644 crates/nu-protocol/src/config/ls.rs create mode 100644 crates/nu-protocol/src/config/prelude.rs create mode 100644 crates/nu-protocol/src/config/rm.rs create mode 100644 crates/nu-protocol/src/config/shell_integration.rs diff --git a/crates/nu-cli/src/commands/history/history_.rs b/crates/nu-cli/src/commands/history/history_.rs index cf6e0f390f..fc1ed1b516 100644 --- a/crates/nu-cli/src/commands/history/history_.rs +++ b/crates/nu-cli/src/commands/history/history_.rs @@ -55,7 +55,7 @@ impl Command for History { HistoryFileFormat::Sqlite => { history_path.push("history.sqlite3"); } - HistoryFileFormat::PlainText => { + HistoryFileFormat::Plaintext => { history_path.push("history.txt"); } } @@ -75,7 +75,7 @@ impl Command for History { .ok() } - HistoryFileFormat::PlainText => FileBackedHistory::with_file( + HistoryFileFormat::Plaintext => FileBackedHistory::with_file( history.max_size as usize, history_path.clone().into(), ) @@ -87,7 +87,7 @@ impl Command for History { }; match history.file_format { - HistoryFileFormat::PlainText => Ok(history_reader + HistoryFileFormat::Plaintext => Ok(history_reader .and_then(|h| { h.search(SearchQuery::everything(SearchDirection::Forward, None)) .ok() diff --git a/crates/nu-cli/src/completions/command_completions.rs b/crates/nu-cli/src/completions/command_completions.rs index f8107f8ed7..e5c7e8f140 100644 --- a/crates/nu-cli/src/completions/command_completions.rs +++ b/crates/nu-cli/src/completions/command_completions.rs @@ -51,7 +51,9 @@ impl CommandCompletion { if working_set .permanent_state .config - .max_external_completion_results + .completions + .external + .max_results > executables.len() as i64 && !executables.contains( &item @@ -211,7 +213,7 @@ impl Completer for CommandCompletion { working_set, span, offset, - config.enable_external_completion, + config.completions.external.enable, options.match_algorithm, ) } else { diff --git a/crates/nu-cli/src/completions/completer.rs b/crates/nu-cli/src/completions/completer.rs index 91fc1132a5..f4e3b1e817 100644 --- a/crates/nu-cli/src/completions/completer.rs +++ b/crates/nu-cli/src/completions/completer.rs @@ -46,9 +46,9 @@ impl NuCompleter { let config = self.engine_state.get_config(); let options = CompletionOptions { - case_sensitive: config.case_sensitive_completions, - match_algorithm: config.completion_algorithm.into(), - sort: config.completion_sort, + case_sensitive: config.completions.case_sensitive, + match_algorithm: config.completions.algorithm.into(), + sort: config.completions.sort, ..Default::default() }; @@ -208,7 +208,7 @@ impl NuCompleter { // We got no results for internal completion // now we can check if external completer is set and use it - if let Some(closure) = config.external_completer.as_ref() { + if let Some(closure) = config.completions.external.completer.as_ref() { if let Some(external_result) = self.external_completion(closure, &spans, fake_offset, new_span) { @@ -338,7 +338,9 @@ impl NuCompleter { } // Try to complete using an external completer (if set) - if let Some(closure) = config.external_completer.as_ref() { + if let Some(closure) = + config.completions.external.completer.as_ref() + { if let Some(external_result) = self.external_completion( closure, &spans, diff --git a/crates/nu-cli/src/completions/completion_common.rs b/crates/nu-cli/src/completions/completion_common.rs index a5d07be57e..8aae72b4f3 100644 --- a/crates/nu-cli/src/completions/completion_common.rs +++ b/crates/nu-cli/src/completions/completion_common.rs @@ -167,7 +167,7 @@ pub fn complete_item( .rfind(|c: &char| is_separator(*c)) .unwrap_or(SEP); let cwd_pathbuf = Path::new(cwd).to_path_buf(); - let ls_colors = (engine_state.config.use_ls_colors_completions + let ls_colors = (engine_state.config.completions.use_ls_colors && engine_state.config.use_ansi_coloring) .then(|| { let ls_colors_env_str = match stack.get_env_var(engine_state, "LS_COLORS") { diff --git a/crates/nu-cli/src/config_files.rs b/crates/nu-cli/src/config_files.rs index 0a1226c304..701db7ac65 100644 --- a/crates/nu-cli/src/config_files.rs +++ b/crates/nu-cli/src/config_files.rs @@ -245,7 +245,7 @@ pub(crate) fn get_history_path(storage_path: &str, mode: HistoryFileFormat) -> O nu_path::config_dir().map(|mut history_path| { history_path.push(storage_path); history_path.push(match mode { - HistoryFileFormat::PlainText => HISTORY_FILE_TXT, + HistoryFileFormat::Plaintext => HISTORY_FILE_TXT, HistoryFileFormat::Sqlite => HISTORY_FILE_SQLITE, }); history_path.into() diff --git a/crates/nu-cli/src/eval_cmds.rs b/crates/nu-cli/src/eval_cmds.rs index 1459f1ef0d..0a9136bc51 100644 --- a/crates/nu-cli/src/eval_cmds.rs +++ b/crates/nu-cli/src/eval_cmds.rs @@ -53,7 +53,7 @@ pub fn evaluate_commands( // Parse the source code let (block, delta) = { if let Some(ref t_mode) = table_mode { - Arc::make_mut(&mut engine_state.config).table_mode = + Arc::make_mut(&mut engine_state.config).table.mode = t_mode.coerce_str()?.parse().unwrap_or_default(); } @@ -88,7 +88,7 @@ pub fn evaluate_commands( } if let Some(t_mode) = table_mode { - Arc::make_mut(&mut engine_state.config).table_mode = + Arc::make_mut(&mut engine_state.config).table.mode = t_mode.coerce_str()?.parse().unwrap_or_default(); } diff --git a/crates/nu-cli/src/prompt_update.rs b/crates/nu-cli/src/prompt_update.rs index 5765755ac4..dd99b6f338 100644 --- a/crates/nu-cli/src/prompt_update.rs +++ b/crates/nu-cli/src/prompt_update.rs @@ -118,13 +118,13 @@ pub(crate) fn update_prompt( // Now that we have the prompt string lets ansify it. // <133 A><133 B><133 C> - let left_prompt_string = if config.shell_integration_osc633 { + let left_prompt_string = if config.shell_integration.osc633 { if stack.get_env_var(engine_state, "TERM_PROGRAM") == Some(Value::test_string("vscode")) { // We're in vscode and we have osc633 enabled Some(format!( "{VSCODE_PRE_PROMPT_MARKER}{configured_left_prompt_string}{VSCODE_POST_PROMPT_MARKER}" )) - } else if config.shell_integration_osc133 { + } else if config.shell_integration.osc133 { // If we're in VSCode but we don't find the env var, but we have osc133 set, then use it Some(format!( "{PRE_PROMPT_MARKER}{configured_left_prompt_string}{POST_PROMPT_MARKER}" @@ -132,7 +132,7 @@ pub(crate) fn update_prompt( } else { configured_left_prompt_string.into() } - } else if config.shell_integration_osc133 { + } else if config.shell_integration.osc133 { Some(format!( "{PRE_PROMPT_MARKER}{configured_left_prompt_string}{POST_PROMPT_MARKER}" )) diff --git a/crates/nu-cli/src/reedline_config.rs b/crates/nu-cli/src/reedline_config.rs index 63d88316df..71fd9f32e1 100644 --- a/crates/nu-cli/src/reedline_config.rs +++ b/crates/nu-cli/src/reedline_config.rs @@ -159,8 +159,8 @@ fn add_menu( stack: &Stack, config: Arc, ) -> Result { - let span = menu.menu_type.span(); - if let Value::Record { val, .. } = &menu.menu_type { + let span = menu.r#type.span(); + if let Value::Record { val, .. } = &menu.r#type { let layout = extract_value("layout", val, span)?.to_expanded_string("", &config); match layout.as_str() { @@ -170,15 +170,15 @@ fn add_menu( "description" => add_description_menu(line_editor, menu, engine_state, stack, config), _ => Err(ShellError::UnsupportedConfigValue { expected: "columnar, list, ide or description".to_string(), - value: menu.menu_type.to_abbreviated_string(&config), - span: menu.menu_type.span(), + value: menu.r#type.to_abbreviated_string(&config), + span: menu.r#type.span(), }), } } else { Err(ShellError::UnsupportedConfigValue { expected: "only record type".to_string(), - value: menu.menu_type.to_abbreviated_string(&config), - span: menu.menu_type.span(), + value: menu.r#type.to_abbreviated_string(&config), + span: menu.r#type.span(), }) } } @@ -224,11 +224,11 @@ pub(crate) fn add_columnar_menu( stack: &Stack, config: &Config, ) -> Result { - let span = menu.menu_type.span(); + let span = menu.r#type.span(); let name = menu.name.to_expanded_string("", config); let mut columnar_menu = ColumnarMenu::default().with_name(&name); - if let Value::Record { val, .. } = &menu.menu_type { + if let Value::Record { val, .. } = &menu.r#type { columnar_menu = match extract_value("columns", val, span) { Ok(columns) => { let columns = columns.as_int()?; @@ -299,8 +299,8 @@ pub(crate) fn add_list_menu( let name = menu.name.to_expanded_string("", &config); let mut list_menu = ListMenu::default().with_name(&name); - let span = menu.menu_type.span(); - if let Value::Record { val, .. } = &menu.menu_type { + let span = menu.r#type.span(); + if let Value::Record { val, .. } = &menu.r#type { list_menu = match extract_value("page_size", val, span) { Ok(page_size) => { let page_size = page_size.as_int()?; @@ -352,11 +352,11 @@ pub(crate) fn add_ide_menu( stack: &Stack, config: Arc, ) -> Result { - let span = menu.menu_type.span(); + let span = menu.r#type.span(); let name = menu.name.to_expanded_string("", &config); let mut ide_menu = IdeMenu::default().with_name(&name); - if let Value::Record { val, .. } = &menu.menu_type { + if let Value::Record { val, .. } = &menu.r#type { ide_menu = match extract_value("min_completion_width", val, span) { Ok(min_completion_width) => { let min_completion_width = min_completion_width.as_int()?; @@ -536,8 +536,8 @@ pub(crate) fn add_description_menu( let name = menu.name.to_expanded_string("", &config); let mut description_menu = DescriptionMenu::default().with_name(&name); - let span = menu.menu_type.span(); - if let Value::Record { val, .. } = &menu.menu_type { + let span = menu.r#type.span(); + if let Value::Record { val, .. } = &menu.r#type { description_menu = match extract_value("columns", val, span) { Ok(columns) => { let columns = columns.as_int()?; diff --git a/crates/nu-cli/src/repl.rs b/crates/nu-cli/src/repl.rs index 0fd23b76d0..95fb000be0 100644 --- a/crates/nu-cli/src/repl.rs +++ b/crates/nu-cli/src/repl.rs @@ -72,11 +72,11 @@ pub fn evaluate_repl( let mut entry_num = 0; // Let's grab the shell_integration configs - let shell_integration_osc2 = config.shell_integration_osc2; - let shell_integration_osc7 = config.shell_integration_osc7; - let shell_integration_osc9_9 = config.shell_integration_osc9_9; - let shell_integration_osc133 = config.shell_integration_osc133; - let shell_integration_osc633 = config.shell_integration_osc633; + let shell_integration_osc2 = config.shell_integration.osc2; + let shell_integration_osc7 = config.shell_integration.osc7; + let shell_integration_osc9_9 = config.shell_integration.osc9_9; + let shell_integration_osc133 = config.shell_integration.osc133; + let shell_integration_osc633 = config.shell_integration.osc633; let nu_prompt = NushellPrompt::new( shell_integration_osc133, @@ -322,9 +322,9 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) { start_time = std::time::Instant::now(); // Find the configured cursor shapes for each mode let cursor_config = CursorConfig { - vi_insert: map_nucursorshape_to_cursorshape(config.cursor_shape_vi_insert), - vi_normal: map_nucursorshape_to_cursorshape(config.cursor_shape_vi_normal), - emacs: map_nucursorshape_to_cursorshape(config.cursor_shape_emacs), + vi_insert: map_nucursorshape_to_cursorshape(config.cursor_shape.vi_insert), + vi_normal: map_nucursorshape_to_cursorshape(config.cursor_shape.vi_normal), + emacs: map_nucursorshape_to_cursorshape(config.cursor_shape.emacs), }; perf!("get config/cursor config", start_time, use_color); @@ -352,8 +352,8 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) { // STACK-REFERENCE 2 stack_arc.clone(), ))) - .with_quick_completions(config.quick_completions) - .with_partial_completions(config.partial_completions) + .with_quick_completions(config.completions.quick) + .with_partial_completions(config.completions.partial) .with_ansi_colors(config.use_ansi_coloring) .with_cwd(Some( engine_state @@ -457,12 +457,12 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) { .with_completer(Box::::default()); // Let's grab the shell_integration configs - let shell_integration_osc2 = config.shell_integration_osc2; - let shell_integration_osc7 = config.shell_integration_osc7; - let shell_integration_osc9_9 = config.shell_integration_osc9_9; - let shell_integration_osc133 = config.shell_integration_osc133; - let shell_integration_osc633 = config.shell_integration_osc633; - let shell_integration_reset_application_mode = config.shell_integration_reset_application_mode; + let shell_integration_osc2 = config.shell_integration.osc2; + let shell_integration_osc7 = config.shell_integration.osc7; + let shell_integration_osc9_9 = config.shell_integration.osc9_9; + let shell_integration_osc133 = config.shell_integration.osc133; + let shell_integration_osc633 = config.shell_integration.osc633; + let shell_integration_reset_application_mode = config.shell_integration.reset_application_mode; // TODO: we may clone the stack, this can lead to major performance issues // so we should avoid it or making stack cheaper to clone. @@ -1173,7 +1173,7 @@ fn update_line_editor_history( history_session_id: Option, ) -> Result { let history: Box = match history.file_format { - HistoryFileFormat::PlainText => Box::new( + HistoryFileFormat::Plaintext => Box::new( FileBackedHistory::with_file(history.max_size as usize, history_path) .into_diagnostic()?, ), @@ -1211,10 +1211,10 @@ fn confirm_stdin_is_terminal() -> Result<()> { fn map_nucursorshape_to_cursorshape(shape: NuCursorShape) -> Option { match shape { NuCursorShape::Block => Some(SetCursorStyle::SteadyBlock), - NuCursorShape::UnderScore => Some(SetCursorStyle::SteadyUnderScore), + NuCursorShape::Underscore => Some(SetCursorStyle::SteadyUnderScore), NuCursorShape::Line => Some(SetCursorStyle::SteadyBar), NuCursorShape::BlinkBlock => Some(SetCursorStyle::BlinkingBlock), - NuCursorShape::BlinkUnderScore => Some(SetCursorStyle::BlinkingUnderScore), + NuCursorShape::BlinkUnderscore => Some(SetCursorStyle::BlinkingUnderScore), NuCursorShape::BlinkLine => Some(SetCursorStyle::BlinkingBar), NuCursorShape::Inherit => None, } diff --git a/crates/nu-command/src/filesystem/rm.rs b/crates/nu-command/src/filesystem/rm.rs index 2e5b964a7e..3475c1a595 100644 --- a/crates/nu-command/src/filesystem/rm.rs +++ b/crates/nu-command/src/filesystem/rm.rs @@ -167,7 +167,7 @@ fn rm( } let span = call.head; - let rm_always_trash = stack.get_config(engine_state).rm_always_trash; + let rm_always_trash = stack.get_config(engine_state).rm.always_trash; if !TRASH_SUPPORTED { if rm_always_trash { diff --git a/crates/nu-command/src/viewers/table.rs b/crates/nu-command/src/viewers/table.rs index 7ad15497da..dc52c4e094 100644 --- a/crates/nu-command/src/viewers/table.rs +++ b/crates/nu-command/src/viewers/table.rs @@ -258,7 +258,7 @@ fn parse_table_config( let flatten_separator: Option = call.get_flag(state, stack, "flatten-separator")?; let abbrivation: Option = call .get_flag(state, stack, "abbreviated")? - .or_else(|| stack.get_config(state).table_abbreviation_threshold); + .or_else(|| stack.get_config(state).table.abbreviated_row_count); let table_view = match (expand, collapse) { (false, false) => TableView::General, (_, true) => TableView::Collapsed, @@ -269,7 +269,7 @@ fn parse_table_config( }, }; let theme = - get_theme_flag(call, state, stack)?.unwrap_or_else(|| stack.get_config(state).table_mode); + get_theme_flag(call, state, stack)?.unwrap_or_else(|| stack.get_config(state).table.mode); let index = get_index_flag(call, state, stack)?; let term_width = get_width_param(width_param); @@ -520,7 +520,7 @@ fn handle_record( } } - let indent = (config.table_indent.left, config.table_indent.right); + let indent = (config.table.padding.left, config.table.padding.right); let opts = TableOpts::new( &config, styles, @@ -821,7 +821,7 @@ impl PagingTableCreator { self.engine_state.signals(), self.head, self.cfg.term_width, - (cfg.table_indent.left, cfg.table_indent.right), + (cfg.table.padding.left, cfg.table.padding.right), self.cfg.theme, self.cfg.index.unwrap_or(0) + self.row_offset, self.cfg.index.is_none(), @@ -1010,7 +1010,7 @@ fn render_path_name( ls_colors: &LsColors, span: Span, ) -> Option { - if !config.use_ls_colors { + if !config.ls.use_ls_colors { return None; } @@ -1024,10 +1024,10 @@ fn render_path_name( // clickable links don't work in remote SSH sessions let in_ssh_session = std::env::var("SSH_CLIENT").is_ok(); //TODO: Deprecated show_clickable_links_in_ls in favor of shell_integration_osc8 - let show_clickable_links = config.show_clickable_links_in_ls + let show_clickable_links = config.ls.clickable_links && !in_ssh_session && has_metadata - && config.shell_integration_osc8; + && config.shell_integration.osc8; let ansi_style = style.map(Style::to_nu_ansi_term_style).unwrap_or_default(); @@ -1074,7 +1074,7 @@ fn create_empty_placeholder( stack: &Stack, ) -> String { let config = stack.get_config(engine_state); - if !config.table_show_empty { + if !config.table.show_empty { return String::new(); } diff --git a/crates/nu-derive-value/src/from.rs b/crates/nu-derive-value/src/from.rs index 7c929ffca3..ee5f9f823e 100644 --- a/crates/nu-derive-value/src/from.rs +++ b/crates/nu-derive-value/src/from.rs @@ -126,7 +126,7 @@ fn derive_struct_from_value( /// .remove("favorite_toy") /// .map(|v| <#ty as nu_protocol::FromValue>::from_value(v)) /// .transpose()? -/// .flatten(), +/// .flatten(), /// }) /// } /// } @@ -491,7 +491,7 @@ fn enum_from_value(data: &DataEnum, attrs: &[Attribute]) -> Result { } /// Implements `FromValue::expected_type` for enums. -/// +/// /// Since it's difficult to name the type of an enum in the current type system, we want to use the /// default implementation if `#[nu_value(type_name = "...")]` was *not* given. /// For that, a `None` value is returned, for a passed type name we return something like this: diff --git a/crates/nu-derive-value/src/names.rs b/crates/nu-derive-value/src/names.rs index 3798395b7f..3a8867121b 100644 --- a/crates/nu-derive-value/src/names.rs +++ b/crates/nu-derive-value/src/names.rs @@ -1,5 +1,6 @@ use proc_macro2::Span; use std::collections::HashMap; +use syn::ext::IdentExt; use syn::Ident; use crate::attributes::{ContainerAttributes, MemberAttributes}; @@ -33,18 +34,17 @@ impl NameResolver { /// If a duplicate identifier is detected, it returns [`DeriveError::NonUniqueName`]. pub fn resolve_ident( &mut self, - ident: &'_ Ident, - container_attrs: &'_ ContainerAttributes, - member_attrs: &'_ MemberAttributes, + ident: &Ident, + container_attrs: &ContainerAttributes, + member_attrs: &MemberAttributes, default: impl Into>, ) -> Result> { let span = ident.span(); - let rename_all = container_attrs.rename_all; - let rename = member_attrs.rename.as_ref(); - let ident = match (rename, rename_all) { - (Some(rename), _) => rename.to_string(), - (None, Some(case)) => ident.to_case(case), - (None, None) => ident.to_case(default), + let ident = if let Some(rename) = &member_attrs.rename { + rename.clone() + } else { + let case = container_attrs.rename_all.or(default.into()); + ident.unraw().to_case(case) }; if let Some(seen) = self.seen_names.get(&ident) { diff --git a/crates/nu-explore/src/explore.rs b/crates/nu-explore/src/explore.rs index 3fdd9835d8..d6a59ee083 100644 --- a/crates/nu-explore/src/explore.rs +++ b/crates/nu-explore/src/explore.rs @@ -177,8 +177,8 @@ impl ExploreConfig { pub fn from_nu_config(config: &Config) -> Self { let mut ret = Self::default(); - ret.table.column_padding_left = config.table_indent.left; - ret.table.column_padding_right = config.table_indent.right; + ret.table.column_padding_left = config.table.padding.left; + ret.table.column_padding_right = config.table.padding.right; let explore_cfg_hash_map = config.explore.clone(); let colors = get_color_map(&explore_cfg_hash_map); diff --git a/crates/nu-explore/src/nu_common/table.rs b/crates/nu-explore/src/nu_common/table.rs index 164c837bf9..79a97ed9ca 100644 --- a/crates/nu-explore/src/nu_common/table.rs +++ b/crates/nu-explore/src/nu_common/table.rs @@ -36,8 +36,8 @@ fn try_build_map( signals, Span::unknown(), usize::MAX, - (config.table_indent.left, config.table_indent.right), - config.table_mode, + (config.table.padding.left, config.table.padding.right), + config.table.mode, 0, false, ); @@ -63,8 +63,8 @@ fn try_build_list( signals, Span::unknown(), usize::MAX, - (config.table_indent.left, config.table_indent.right), - config.table_mode, + (config.table.padding.left, config.table.padding.right), + config.table.mode, 0, false, ); diff --git a/crates/nu-protocol/src/config/completer.rs b/crates/nu-protocol/src/config/completer.rs index 921057c082..1494c375a6 100644 --- a/crates/nu-protocol/src/config/completer.rs +++ b/crates/nu-protocol/src/config/completer.rs @@ -1,12 +1,8 @@ -use std::str::FromStr; +use super::prelude::*; +use crate as nu_protocol; +use crate::engine::Closure; -use serde::{Deserialize, Serialize}; - -use crate::{record, Config, Span, Value}; - -use super::helper::ReconstructVal; - -#[derive(Serialize, Deserialize, Clone, Copy, Debug, Default)] +#[derive(Clone, Copy, Debug, Default, IntoValue, PartialEq, Eq, Serialize, Deserialize)] pub enum CompletionAlgorithm { #[default] Prefix, @@ -25,17 +21,7 @@ impl FromStr for CompletionAlgorithm { } } -impl ReconstructVal for CompletionAlgorithm { - fn reconstruct_value(&self, span: Span) -> Value { - let str = match self { - CompletionAlgorithm::Prefix => "prefix", - CompletionAlgorithm::Fuzzy => "fuzzy", - }; - Value::string(str, span) - } -} - -#[derive(Serialize, Deserialize, Clone, Copy, Debug, Default, PartialEq)] +#[derive(Clone, Copy, Debug, Default, IntoValue, PartialEq, Eq, Serialize, Deserialize)] pub enum CompletionSort { #[default] Smart, @@ -54,31 +40,44 @@ impl FromStr for CompletionSort { } } -impl ReconstructVal for CompletionSort { - fn reconstruct_value(&self, span: Span) -> Value { - let str = match self { - Self::Smart => "smart", - Self::Alphabetical => "alphabetical", - }; - Value::string(str, span) +#[derive(Clone, Debug, IntoValue, Serialize, Deserialize)] +pub struct ExternalCompleterConfig { + pub enable: bool, + pub max_results: i64, + pub completer: Option, +} + +impl Default for ExternalCompleterConfig { + fn default() -> Self { + Self { + enable: true, + max_results: 100, + completer: None, + } } } -pub(super) fn reconstruct_external_completer(config: &Config, span: Span) -> Value { - if let Some(closure) = config.external_completer.as_ref() { - Value::closure(closure.clone(), span) - } else { - Value::nothing(span) - } +#[derive(Clone, Debug, IntoValue, Serialize, Deserialize)] +pub struct CompleterConfig { + pub sort: CompletionSort, + pub case_sensitive: bool, + pub quick: bool, + pub partial: bool, + pub algorithm: CompletionAlgorithm, + pub external: ExternalCompleterConfig, + pub use_ls_colors: bool, } -pub(super) fn reconstruct_external(config: &Config, span: Span) -> Value { - Value::record( - record! { - "max_results" => Value::int(config.max_external_completion_results, span), - "completer" => reconstruct_external_completer(config, span), - "enable" => Value::bool(config.enable_external_completion, span), - }, - span, - ) +impl Default for CompleterConfig { + fn default() -> Self { + Self { + sort: CompletionSort::default(), + case_sensitive: false, + quick: true, + partial: true, + algorithm: CompletionAlgorithm::default(), + external: ExternalCompleterConfig::default(), + use_ls_colors: true, + } + } } diff --git a/crates/nu-protocol/src/config/datetime_format.rs b/crates/nu-protocol/src/config/datetime_format.rs new file mode 100644 index 0000000000..1157302c8a --- /dev/null +++ b/crates/nu-protocol/src/config/datetime_format.rs @@ -0,0 +1,8 @@ +use super::prelude::*; +use crate as nu_protocol; + +#[derive(Clone, Debug, Default, IntoValue, Serialize, Deserialize)] +pub struct DatetimeFormatConfig { + pub normal: Option, + pub table: Option, +} diff --git a/crates/nu-protocol/src/config/filesize.rs b/crates/nu-protocol/src/config/filesize.rs new file mode 100644 index 0000000000..f7dc37b0f3 --- /dev/null +++ b/crates/nu-protocol/src/config/filesize.rs @@ -0,0 +1,17 @@ +use super::prelude::*; +use crate as nu_protocol; + +#[derive(Clone, Debug, IntoValue, PartialEq, Eq, Serialize, Deserialize)] +pub struct FilesizeConfig { + pub metric: bool, + pub format: String, +} + +impl Default for FilesizeConfig { + fn default() -> Self { + Self { + metric: false, + format: "auto".into(), + } + } +} diff --git a/crates/nu-protocol/src/config/helper.rs b/crates/nu-protocol/src/config/helper.rs index 7033a1ac11..aead7ea3fe 100644 --- a/crates/nu-protocol/src/config/helper.rs +++ b/crates/nu-protocol/src/config/helper.rs @@ -1,17 +1,13 @@ -use crate::{Record, ShellError, Span, Value}; +use crate::{IntoValue, Record, ShellError, Span, Value}; use std::{collections::HashMap, fmt::Display, str::FromStr}; -pub(super) trait ReconstructVal { - fn reconstruct_value(&self, span: Span) -> Value; -} - pub(super) fn process_string_enum( config_point: &mut T, config_path: &[&str], value: &mut Value, errors: &mut Vec, ) where - T: FromStr + ReconstructVal, + T: FromStr + Clone + IntoValue, E: Display, { let span = value.span(); @@ -32,7 +28,7 @@ pub(super) fn process_string_enum( inner: vec![], }); // Reconstruct - *value = config_point.reconstruct_value(span); + *value = config_point.clone().into_value(span); } } } else { @@ -44,7 +40,7 @@ pub(super) fn process_string_enum( inner: vec![], }); // Reconstruct - *value = config_point.reconstruct_value(span); + *value = config_point.clone().into_value(span); } } diff --git a/crates/nu-protocol/src/config/history.rs b/crates/nu-protocol/src/config/history.rs new file mode 100644 index 0000000000..56418ae0a1 --- /dev/null +++ b/crates/nu-protocol/src/config/history.rs @@ -0,0 +1,41 @@ +use super::prelude::*; +use crate as nu_protocol; + +#[derive(Clone, Copy, Debug, IntoValue, PartialEq, Eq, Serialize, Deserialize)] +pub enum HistoryFileFormat { + /// Store history as an SQLite database with additional context + Sqlite, + /// store history as a plain text file where every line is one command (without any context such as timestamps) + Plaintext, +} + +impl FromStr for HistoryFileFormat { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + match s.to_ascii_lowercase().as_str() { + "sqlite" => Ok(Self::Sqlite), + "plaintext" => Ok(Self::Plaintext), + _ => Err("expected either 'sqlite' or 'plaintext'"), + } + } +} + +#[derive(Clone, Copy, Debug, IntoValue, PartialEq, Eq, Serialize, Deserialize)] +pub struct HistoryConfig { + pub max_size: i64, + pub sync_on_enter: bool, + pub file_format: HistoryFileFormat, + pub isolation: bool, +} + +impl Default for HistoryConfig { + fn default() -> Self { + Self { + max_size: 100_000, + sync_on_enter: true, + file_format: HistoryFileFormat::Plaintext, + isolation: false, + } + } +} diff --git a/crates/nu-protocol/src/config/hooks.rs b/crates/nu-protocol/src/config/hooks.rs index 88f5f4abf1..c0e3aa3329 100644 --- a/crates/nu-protocol/src/config/hooks.rs +++ b/crates/nu-protocol/src/config/hooks.rs @@ -1,8 +1,9 @@ -use crate::{Config, Record, ShellError, Span, Value}; -use serde::{Deserialize, Serialize}; +use super::prelude::*; +use crate as nu_protocol; +use crate::ShellError; /// Definition of a parsed hook from the config object -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Clone, Debug, IntoValue, PartialEq, Serialize, Deserialize)] pub struct Hooks { pub pre_prompt: Option, pub pre_execution: Option, @@ -65,24 +66,3 @@ pub(super) fn create_hooks(value: &Value) -> Result { }), } } - -pub(super) fn reconstruct_hooks(config: &Config, span: Span) -> Value { - let mut hook = Record::new(); - if let Some(ref value) = config.hooks.pre_prompt { - hook.push("pre_prompt", value.clone()); - } - if let Some(ref value) = config.hooks.pre_execution { - hook.push("pre_execution", value.clone()); - } - if let Some(ref value) = config.hooks.env_change { - hook.push("env_change", value.clone()); - } - if let Some(ref value) = config.hooks.display_output { - hook.push("display_output", value.clone()); - } - if let Some(ref value) = config.hooks.command_not_found { - hook.push("command_not_found", value.clone()); - } - - Value::record(hook, span) -} diff --git a/crates/nu-protocol/src/config/ls.rs b/crates/nu-protocol/src/config/ls.rs new file mode 100644 index 0000000000..522163d9a1 --- /dev/null +++ b/crates/nu-protocol/src/config/ls.rs @@ -0,0 +1,17 @@ +use super::prelude::*; +use crate as nu_protocol; + +#[derive(Clone, Copy, Debug, IntoValue, PartialEq, Eq, Serialize, Deserialize)] +pub struct LsConfig { + pub use_ls_colors: bool, + pub clickable_links: bool, +} + +impl Default for LsConfig { + fn default() -> Self { + Self { + use_ls_colors: true, + clickable_links: true, + } + } +} diff --git a/crates/nu-protocol/src/config/mod.rs b/crates/nu-protocol/src/config/mod.rs index 59cc70fb79..ab98edf9db 100644 --- a/crates/nu-protocol/src/config/mod.rs +++ b/crates/nu-protocol/src/config/mod.rs @@ -1,108 +1,75 @@ //! Module containing the internal representation of user configuration -use self::completer::*; use self::helper::*; use self::hooks::*; -use self::output::*; -use self::reedline::*; -use self::table::*; -use crate::engine::Closure; -use crate::{record, ShellError, Span, Value}; +use crate::{IntoValue, ShellError, Span, Value}; +use reedline::create_keybindings; use serde::{Deserialize, Serialize}; use std::collections::HashMap; +use table::try_parse_trim_strategy; -pub use self::completer::{CompletionAlgorithm, CompletionSort}; +pub use self::completer::{ + CompleterConfig, CompletionAlgorithm, CompletionSort, ExternalCompleterConfig, +}; +pub use self::datetime_format::DatetimeFormatConfig; +pub use self::filesize::FilesizeConfig; pub use self::helper::extract_value; +pub use self::history::{HistoryConfig, HistoryFileFormat}; pub use self::hooks::Hooks; +pub use self::ls::LsConfig; pub use self::output::ErrorStyle; pub use self::plugin_gc::{PluginGcConfig, PluginGcConfigs}; pub use self::reedline::{ - create_menus, EditBindings, HistoryFileFormat, NuCursorShape, ParsedKeybinding, ParsedMenu, + create_menus, CursorShapeConfig, EditBindings, NuCursorShape, ParsedKeybinding, ParsedMenu, }; -pub use self::table::{FooterMode, TableIndexMode, TableMode, TrimStrategy}; +pub use self::rm::RmConfig; +pub use self::shell_integration::ShellIntegrationConfig; +pub use self::table::{FooterMode, TableConfig, TableIndexMode, TableMode, TrimStrategy}; mod completer; +mod datetime_format; +mod filesize; mod helper; +mod history; mod hooks; +mod ls; mod output; mod plugin_gc; +mod prelude; mod reedline; +mod rm; +mod shell_integration; mod table; -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] -pub struct HistoryConfig { - pub max_size: i64, - pub sync_on_enter: bool, - pub file_format: HistoryFileFormat, - pub isolation: bool, -} - -impl Default for HistoryConfig { - fn default() -> Self { - Self { - max_size: 100_000, - sync_on_enter: true, - file_format: HistoryFileFormat::PlainText, - isolation: false, - } - } -} - #[derive(Serialize, Deserialize, Clone, Debug)] pub struct Config { - pub external_completer: Option, - pub filesize_metric: bool, - pub table_mode: TableMode, - pub table_move_header: bool, - pub table_show_empty: bool, - pub table_indent: TableIndent, - pub table_abbreviation_threshold: Option, - pub use_ls_colors: bool, + pub filesize: FilesizeConfig, + pub table: TableConfig, + pub ls: LsConfig, pub color_config: HashMap, pub use_grid_icons: bool, pub footer_mode: FooterMode, pub float_precision: i64, - pub max_external_completion_results: i64, pub recursion_limit: i64, - pub filesize_format: String, pub use_ansi_coloring: bool, - pub quick_completions: bool, - pub partial_completions: bool, - pub completion_algorithm: CompletionAlgorithm, - pub completion_sort: CompletionSort, + pub completions: CompleterConfig, pub edit_mode: EditBindings, pub history: HistoryConfig, pub keybindings: Vec, pub menus: Vec, pub hooks: Hooks, - pub rm_always_trash: bool, - // Shell integration OSC meaning is described in the default_config.nu - pub shell_integration_osc2: bool, - pub shell_integration_osc7: bool, - pub shell_integration_osc8: bool, - pub shell_integration_osc9_9: bool, - pub shell_integration_osc133: bool, - pub shell_integration_osc633: bool, - pub shell_integration_reset_application_mode: bool, + pub rm: RmConfig, + pub shell_integration: ShellIntegrationConfig, pub buffer_editor: Value, - pub table_index_mode: TableIndexMode, - pub case_sensitive_completions: bool, - pub enable_external_completion: bool, - pub trim_strategy: TrimStrategy, pub show_banner: bool, pub bracketed_paste: bool, - pub show_clickable_links_in_ls: bool, pub render_right_prompt_on_last_line: bool, pub explore: HashMap, - pub cursor_shape_vi_insert: NuCursorShape, - pub cursor_shape_vi_normal: NuCursorShape, - pub cursor_shape_emacs: NuCursorShape, - pub datetime_normal_format: Option, - pub datetime_table_format: Option, + pub cursor_shape: CursorShapeConfig, + pub datetime_format: DatetimeFormatConfig, pub error_style: ErrorStyle, pub use_kitty_protocol: bool, pub highlight_resolved_externals: bool, - pub use_ls_colors_completions: bool, /// Configuration for plugins. /// /// Users can provide configuration for a plugin through this entry. The entry name must @@ -118,43 +85,23 @@ impl Default for Config { Config { show_banner: true, - use_ls_colors: true, - show_clickable_links_in_ls: true, + table: TableConfig::default(), + rm: RmConfig::default(), + ls: LsConfig::default(), - rm_always_trash: false, - - table_mode: TableMode::Rounded, - table_index_mode: TableIndexMode::Always, - table_show_empty: true, - trim_strategy: TrimStrategy::default(), - table_move_header: false, - table_indent: TableIndent { left: 1, right: 1 }, - table_abbreviation_threshold: None, - - datetime_normal_format: None, - datetime_table_format: None, + datetime_format: DatetimeFormatConfig::default(), explore: HashMap::new(), history: HistoryConfig::default(), - case_sensitive_completions: false, - quick_completions: true, - partial_completions: true, - completion_algorithm: CompletionAlgorithm::default(), - completion_sort: CompletionSort::default(), - enable_external_completion: true, - max_external_completion_results: 100, + completions: CompleterConfig::default(), + recursion_limit: 50, - external_completer: None, - use_ls_colors_completions: true, - filesize_metric: false, - filesize_format: "auto".into(), + filesize: FilesizeConfig::default(), - cursor_shape_emacs: NuCursorShape::default(), - cursor_shape_vi_insert: NuCursorShape::default(), - cursor_shape_vi_normal: NuCursorShape::default(), + cursor_shape: CursorShapeConfig::default(), color_config: HashMap::new(), use_grid_icons: true, @@ -164,14 +111,8 @@ impl Default for Config { use_ansi_coloring: true, bracketed_paste: true, edit_mode: EditBindings::default(), - // shell_integration: false, - shell_integration_osc2: false, - shell_integration_osc7: false, - shell_integration_osc8: false, - shell_integration_osc9_9: false, - shell_integration_osc133: false, - shell_integration_osc633: false, - shell_integration_reset_application_mode: false, + + shell_integration: ShellIntegrationConfig::default(), render_right_prompt_on_last_line: false, @@ -219,638 +160,7 @@ impl Value { // config setting is converted to a `Value::Boolean` and inserted in the record in place of // the `2`. - if let Value::Record { val, .. } = self { - val.to_mut().retain_mut(|key, value| { - let span = value.span(); - match key { - // Grouped options - "ls" => { - if let Value::Record { val, .. } = value { - val.to_mut().retain_mut(|key2, value| { - let span = value.span(); - match key2 { - "use_ls_colors" => { - process_bool_config(value, &mut errors, &mut config.use_ls_colors); - } - "clickable_links" => { - process_bool_config(value, &mut errors, &mut config.show_clickable_links_in_ls); - } - _ => { - report_invalid_key(&[key, key2], span, &mut errors); - return false; - } - }; true - }); - } else { - report_invalid_value("should be a record", span, &mut errors); - // Reconstruct - *value = Value::record( - record! { - "use_ls_colors" => Value::bool(config.use_ls_colors, span), - "clickable_links" => Value::bool(config.show_clickable_links_in_ls, span), - }, - span, - ); - } - } - "rm" => { - if let Value::Record { val, .. } = value { - val.to_mut().retain_mut(|key2, value| { - let span = value.span(); - match key2 { - "always_trash" => { - process_bool_config(value, &mut errors, &mut config.rm_always_trash); - } - _ => { - report_invalid_key(&[key, key2], span, &mut errors); - return false; - } - }; - true - }); - } else { - report_invalid_value("should be a record", span, &mut errors); - // Reconstruct - *value = Value::record( - record! { - "always_trash" => Value::bool(config.rm_always_trash, span), - }, - span, - ); - } - } - "history" => { - let history = &mut config.history; - if let Value::Record { val, .. } = value { - val.to_mut().retain_mut(|key2, value| { - let span = value.span(); - match key2 { - "isolation" => { - process_bool_config(value, &mut errors, &mut history.isolation); - } - "sync_on_enter" => { - process_bool_config(value, &mut errors, &mut history.sync_on_enter); - } - "max_size" => { - process_int_config(value, &mut errors, &mut history.max_size); - } - "file_format" => { - process_string_enum( - &mut history.file_format, - &[key, key2], - value, - &mut errors); - } - _ => { - report_invalid_key(&[key, key2], span, &mut errors); - return false; - } - }; - true - }); - } else { - report_invalid_value("should be a record", span, &mut errors); - // Reconstruct - *value = Value::record( - record! { - "sync_on_enter" => Value::bool(history.sync_on_enter, span), - "max_size" => Value::int(history.max_size, span), - "file_format" => history.file_format.reconstruct_value(span), - "isolation" => Value::bool(history.isolation, span), - }, - span, - ); - } - } - "completions" => { - if let Value::Record { val, .. } = value { - val.to_mut().retain_mut(|key2, value| { - let span = value.span(); - match key2 { - "quick" => { - process_bool_config(value, &mut errors, &mut config.quick_completions); - } - "partial" => { - process_bool_config(value, &mut errors, &mut config.partial_completions); - } - "algorithm" => { - process_string_enum( - &mut config.completion_algorithm, - &[key, key2], - value, - &mut errors); - } - "case_sensitive" => { - process_bool_config(value, &mut errors, &mut config.case_sensitive_completions); - } - "sort" => { - process_string_enum( - &mut config.completion_sort, - &[key, key2], - value, - &mut errors); - } - "external" => { - if let Value::Record { val, .. } = value { - val.to_mut().retain_mut(|key3, value| - { - let span = value.span(); - match key3 { - "max_results" => { - process_int_config(value, &mut errors, &mut config.max_external_completion_results); - } - "completer" => { - if let Ok(v) = value.as_closure() { - config.external_completer = Some(v.clone()) - } else { - match value { - Value::Nothing { .. } => {} - _ => { - report_invalid_value("should be a closure or null", span, &mut errors); - // Reconstruct - *value = reconstruct_external_completer(&config, - span - ); - } - } - } - } - "enable" => { - process_bool_config(value, &mut errors, &mut config.enable_external_completion); - } - _ => { - report_invalid_key(&[key, key2, key3], span, &mut errors); - return false; - } - }; - true - }); - } else { - report_invalid_value("should be a record", span, &mut errors); - // Reconstruct - *value = reconstruct_external(&config, span); - } - } - "use_ls_colors" => { - process_bool_config(value, &mut errors, &mut config.use_ls_colors_completions); - } - _ => { - report_invalid_key(&[key, key2], span, &mut errors); - return false; - } - }; - true - }); - } else { - report_invalid_value("should be a record", span, &mut errors); - // Reconstruct record - *value = Value::record( - record! { - "quick" => Value::bool(config.quick_completions, span), - "partial" => Value::bool(config.partial_completions, span), - "algorithm" => config.completion_algorithm.reconstruct_value(span), - "case_sensitive" => Value::bool(config.case_sensitive_completions, span), - "sort" => config.completion_sort.reconstruct_value(span), - "external" => reconstruct_external(&config, span), - "use_ls_colors" => Value::bool(config.use_ls_colors_completions, span), - }, - span, - ); - } - } - "cursor_shape" => { - if let Value::Record { val, .. } = value { - val.to_mut().retain_mut(|key2, value| { - let span = value.span(); - let config_point = match key2 { - "vi_insert" => &mut config.cursor_shape_vi_insert, - "vi_normal" => &mut config.cursor_shape_vi_normal, - "emacs" => &mut config.cursor_shape_emacs, - _ => { - report_invalid_key(&[key, key2], span, &mut errors); - return false; - } - }; - process_string_enum( - config_point, - &[key, key2], - value, - &mut errors); - true - }); - } else { - report_invalid_value("should be a record", span, &mut errors); - // Reconstruct - *value = Value::record( - record! { - "vi_insert" => config.cursor_shape_vi_insert.reconstruct_value(span), - "vi_normal" => config.cursor_shape_vi_normal.reconstruct_value(span), - "emacs" => config.cursor_shape_emacs.reconstruct_value(span), - }, - span, - ); - } - } - "table" => { - if let Value::Record { val, .. } = value { - val.to_mut().retain_mut(|key2, value| { - let span = value.span(); - match key2 { - "mode" => { - process_string_enum( - &mut config.table_mode, - &[key, key2], - value, - &mut errors); - } - "header_on_separator" => { - process_bool_config(value, &mut errors, &mut config.table_move_header); - } - "padding" => match value { - Value::Int { val, .. } => { - if *val < 0 { - report_invalid_value("expected a unsigned integer", span, &mut errors); - *value = reconstruct_padding(&config, span); - } else { - config.table_indent.left = *val as usize; - config.table_indent.right = *val as usize; - } - } - Value::Record { val, .. } => { - let mut invalid = false; - val.to_mut().retain(|key3, value| { - match key3 { - "left" => { - match value.as_int() { - Ok(val) if val >= 0 => { - config.table_indent.left = val as usize; - } - _ => { - report_invalid_value("expected a unsigned integer >= 0", span, &mut errors); - invalid = true; - } - } - } - "right" => { - match value.as_int() { - Ok(val) if val >= 0 => { - config.table_indent.right = val as usize; - } - _ => { - report_invalid_value("expected a unsigned integer >= 0", span, &mut errors); - invalid = true; - } - } - } - _ => { - report_invalid_key(&[key, key2, key3], span, &mut errors); - return false; - } - }; - true - }); - if invalid { - *value = reconstruct_padding(&config, span); - } - } - _ => { - report_invalid_value("expected a unsigned integer or a record", span, &mut errors); - *value = reconstruct_padding(&config, span); - } - }, - "index_mode" => { - process_string_enum( - &mut config.table_index_mode, - &[key, key2], - value, - &mut errors); - } - "trim" => { - match try_parse_trim_strategy(value, &mut errors) { - Ok(v) => config.trim_strategy = v, - Err(e) => { - // try_parse_trim_strategy() already adds its own errors - errors.push(e); - *value = - reconstruct_trim_strategy(&config, span); - } - } - } - "show_empty" => { - process_bool_config(value, &mut errors, &mut config.table_show_empty); - } - "abbreviated_row_count" => { - if let Ok(b) = value.as_int() { - if b < 0 { - report_invalid_value("should be an int unsigned", span, &mut errors); - } - - config.table_abbreviation_threshold = Some(b as usize); - } else { - report_invalid_value("should be an int", span, &mut errors); - } - } - _ => { - report_invalid_key(&[key, key2], span, &mut errors); - return false - } - }; - true - }); - } else { - report_invalid_value("should be a record", span, &mut errors); - // Reconstruct - *value = Value::record( - record! { - "mode" => config.table_mode.reconstruct_value(span), - "index_mode" => config.table_index_mode.reconstruct_value(span), - "trim" => reconstruct_trim_strategy(&config, span), - "show_empty" => Value::bool(config.table_show_empty, span), - }, - span, - ); - } - } - "filesize" => { - if let Value::Record { val, .. } = value { - val.to_mut().retain_mut(|key2, value| { - let span = value.span(); - match key2 { - "metric" => { - process_bool_config(value, &mut errors, &mut config.filesize_metric); - } - "format" => { - if let Ok(v) = value.coerce_str() { - config.filesize_format = v.to_lowercase(); - } else { - report_invalid_value("should be a string", span, &mut errors); - // Reconstruct - *value = - Value::string(config.filesize_format.clone(), span); - } - } - _ => { - report_invalid_key(&[key, key2], span, &mut errors); - return false; - } - }; - true - }) - } else { - report_invalid_value("should be a record", span, &mut errors); - // Reconstruct - *value = Value::record( - record! { - "metric" => Value::bool(config.filesize_metric, span), - "format" => Value::string(config.filesize_format.clone(), span), - }, - span, - ); - } - } - "explore" => { - if let Ok(map) = create_map(value) { - config.explore = map; - } else { - report_invalid_value("should be a record", span, &mut errors); - // Reconstruct - *value = Value::record( - config - .explore - .iter() - .map(|(k, v)| (k.clone(), v.clone())) - .collect(), - span, - ); - } - } - // Misc. options - "color_config" => { - if let Ok(map) = create_map(value) { - config.color_config = map; - } else { - report_invalid_value("should be a record", span, &mut errors); - // Reconstruct - *value = Value::record( - config - .color_config - .iter() - .map(|(k, v)| (k.clone(), v.clone())) - .collect(), - span, - ); - } - } - "use_grid_icons" => { - process_bool_config(value, &mut errors, &mut config.use_grid_icons); - } - "footer_mode" => { - process_string_enum( - &mut config.footer_mode, - &[key], - value, - &mut errors); - } - "float_precision" => { - process_int_config(value, &mut errors, &mut config.float_precision); - } - "use_ansi_coloring" => { - process_bool_config(value, &mut errors, &mut config.use_ansi_coloring); - } - "edit_mode" => { - process_string_enum( - &mut config.edit_mode, - &[key], - value, - &mut errors); - } - "shell_integration" => { - if let Value::Record { val, .. } = value { - val.to_mut().retain_mut(|key2, value| { - let span = value.span(); - match key2 { - "osc2" => { - process_bool_config(value, &mut errors, &mut config.shell_integration_osc2); - } - "osc7" => { - process_bool_config(value, &mut errors, &mut config.shell_integration_osc7); - } - "osc8" => { - process_bool_config(value, &mut errors, &mut config.shell_integration_osc8); - } - "osc9_9" => { - process_bool_config(value, &mut errors, &mut config.shell_integration_osc9_9); - } - "osc133" => { - process_bool_config(value, &mut errors, &mut config.shell_integration_osc133); - } - "osc633" => { - process_bool_config(value, &mut errors, &mut config.shell_integration_osc633); - } - "reset_application_mode" => { - process_bool_config(value, &mut errors, &mut config.shell_integration_reset_application_mode); - } - _ => { - report_invalid_key(&[key, key2], span, &mut errors); - return false; - } - }; - true - }) - } else { - report_invalid_value("boolean value is deprecated, should be a record. see `config nu --default`.", span, &mut errors); - // Reconstruct - *value = Value::record( - record! { - "osc2" => Value::bool(config.shell_integration_osc2, span), - "ocs7" => Value::bool(config.shell_integration_osc7, span), - "osc8" => Value::bool(config.shell_integration_osc8, span), - "osc9_9" => Value::bool(config.shell_integration_osc9_9, span), - "osc133" => Value::bool(config.shell_integration_osc133, span), - "osc633" => Value::bool(config.shell_integration_osc633, span), - "reset_application_mode" => Value::bool(config.shell_integration_reset_application_mode, span), - }, - span, - ); - } - } - "buffer_editor" => match value { - Value::Nothing { .. } | Value::String { .. } => { - config.buffer_editor = value.clone(); - } - Value::List { vals, .. } - if vals.iter().all(|val| matches!(val, Value::String { .. })) => - { - config.buffer_editor = value.clone(); - } - _ => { - report_invalid_value("should be a string, list, or null", span, &mut errors); - *value = config.buffer_editor.clone(); - } - }, - "show_banner" => { - process_bool_config(value, &mut errors, &mut config.show_banner); - } - "render_right_prompt_on_last_line" => { - process_bool_config(value, &mut errors, &mut config.render_right_prompt_on_last_line); - } - "bracketed_paste" => { - process_bool_config(value, &mut errors, &mut config.bracketed_paste); - } - "use_kitty_protocol" => { - process_bool_config(value, &mut errors, &mut config.use_kitty_protocol); - } - "highlight_resolved_externals" => { - process_bool_config(value, &mut errors, &mut config.highlight_resolved_externals); - } - "plugins" => { - if let Ok(map) = create_map(value) { - config.plugins = map; - } else { - report_invalid_value("should be a record", span, &mut errors); - // Reconstruct - *value = Value::record( - config - .explore - .iter() - .map(|(k, v)| (k.clone(), v.clone())) - .collect(), - span, - ); - } - } - "plugin_gc" => { - config.plugin_gc.process(&[key], value, &mut errors); - } - // Menus - "menus" => match create_menus(value) { - Ok(map) => config.menus = map, - Err(e) => { - report_invalid_value("should be a valid list of menus", span, &mut errors); - errors.push(e); - // Reconstruct - *value = reconstruct_menus(&config, span); - } - }, - // Keybindings - "keybindings" => match create_keybindings(value) { - Ok(keybindings) => config.keybindings = keybindings, - Err(e) => { - report_invalid_value("should be a valid keybindings list", span, &mut errors); - errors.push(e); - // Reconstruct - *value = reconstruct_keybindings(&config, span); - } - }, - // Hooks - "hooks" => match create_hooks(value) { - Ok(hooks) => config.hooks = hooks, - Err(e) => { - report_invalid_value("should be a valid hooks list", span, &mut errors); - errors.push(e); - *value = reconstruct_hooks(&config, span); - } - }, - "datetime_format" => { - if let Value::Record { val, .. } = value { - val.to_mut().retain_mut(|key2, value| - { - let span = value.span(); - match key2 { - "normal" => { - if let Ok(v) = value.coerce_string() { - config.datetime_normal_format = Some(v); - } else { - report_invalid_value("should be a string", span, &mut errors); - } - } - "table" => { - if let Ok(v) = value.coerce_string() { - config.datetime_table_format = Some(v); - } else { - report_invalid_value("should be a string", span, &mut errors); - } - } - _ => { - report_invalid_key(&[key, key2], span, &mut errors); - return false; - } - }; true}) - } else { - report_invalid_value("should be a record", span, &mut errors); - // Reconstruct - *value = reconstruct_datetime_format(&config, span); - } - } - "error_style" => { - process_string_enum( - &mut config.error_style, - &[key], - value, - &mut errors); - } - "recursion_limit" => { - if let Value::Int { val, internal_span } = value { - if val > &mut 1 { - config.recursion_limit = *val; - } else { - report_invalid_value("should be a integer greater than 1", span, &mut errors); - *value = Value::Int { val: 50, internal_span: *internal_span }; - } - } else { - report_invalid_value("should be a integer greater than 1", span, &mut errors); - *value = Value::Int { val: 50, internal_span: value.span() }; - } - } - // Catch all - _ => { - report_invalid_key(&[key], span, &mut errors); - return false; - } - }; - true - }); - } else { + let Value::Record { val, .. } = self else { return ( config, Some(ShellError::GenericError { @@ -861,7 +171,549 @@ impl Value { inner: vec![], }), ); - } + }; + + val.to_mut().retain_mut(|key, value| { + let span = value.span(); + match key { + "ls" => { + if let Value::Record { val, .. } = value { + val.to_mut().retain_mut(|key2, value| { + let span = value.span(); + match key2 { + "use_ls_colors" => { + process_bool_config(value, &mut errors, &mut config.ls.use_ls_colors); + } + "clickable_links" => { + process_bool_config(value, &mut errors, &mut config.ls.clickable_links); + } + _ => { + report_invalid_key(&[key, key2], span, &mut errors); + return false; + } + } + true + }); + } else { + report_invalid_value("should be a record", span, &mut errors); + *value = config.ls.into_value(span); + } + } + "rm" => { + if let Value::Record { val, .. } = value { + val.to_mut().retain_mut(|key2, value| { + let span = value.span(); + match key2 { + "always_trash" => { + process_bool_config(value, &mut errors, &mut config.rm.always_trash); + } + _ => { + report_invalid_key(&[key, key2], span, &mut errors); + return false; + } + }; + true + }); + } else { + report_invalid_value("should be a record", span, &mut errors); + *value = config.rm.into_value(span); + } + } + "history" => { + let history = &mut config.history; + if let Value::Record { val, .. } = value { + val.to_mut().retain_mut(|key2, value| { + let span = value.span(); + match key2 { + "isolation" => { + process_bool_config(value, &mut errors, &mut history.isolation); + } + "sync_on_enter" => { + process_bool_config(value, &mut errors, &mut history.sync_on_enter); + } + "max_size" => { + process_int_config(value, &mut errors, &mut history.max_size); + } + "file_format" => { + process_string_enum( + &mut history.file_format, + &[key, key2], + value, + &mut errors + ); + } + _ => { + report_invalid_key(&[key, key2], span, &mut errors); + return false; + } + }; + true + }); + } else { + report_invalid_value("should be a record", span, &mut errors); + *value = config.history.into_value(span); + } + } + "completions" => { + if let Value::Record { val, .. } = value { + val.to_mut().retain_mut(|key2, value| { + let span = value.span(); + match key2 { + "quick" => { + process_bool_config(value, &mut errors, &mut config.completions.quick); + } + "partial" => { + process_bool_config(value, &mut errors, &mut config.completions.partial); + } + "algorithm" => { + process_string_enum( + &mut config.completions.algorithm, + &[key, key2], + value, + &mut errors + ); + } + "case_sensitive" => { + process_bool_config(value, &mut errors, &mut config.completions.case_sensitive); + } + "sort" => { + process_string_enum( + &mut config.completions.sort, + &[key, key2], + value, + &mut errors + ); + } + "external" => { + if let Value::Record { val, .. } = value { + val.to_mut().retain_mut(|key3, value| { + let span = value.span(); + match key3 { + "max_results" => { + process_int_config(value, &mut errors, &mut config.completions.external.max_results); + } + "completer" => { + if let Ok(v) = value.as_closure() { + config.completions.external.completer = Some(v.clone()) + } else { + match value { + Value::Nothing { .. } => {} + _ => { + report_invalid_value("should be a closure or null", span, &mut errors); + *value = config.completions.external.completer.clone().into_value(span); + } + } + } + } + "enable" => { + process_bool_config(value, &mut errors, &mut config.completions.external.enable); + } + _ => { + report_invalid_key(&[key, key2, key3], span, &mut errors); + return false; + } + }; + true + }); + } else { + report_invalid_value("should be a record", span, &mut errors); + *value = config.completions.external.clone().into_value(span); + } + } + "use_ls_colors" => { + process_bool_config(value, &mut errors, &mut config.completions.use_ls_colors); + } + _ => { + report_invalid_key(&[key, key2], span, &mut errors); + return false; + } + }; + true + }); + } else { + report_invalid_value("should be a record", span, &mut errors); + *value = config.completions.clone().into_value(span); + } + } + "cursor_shape" => { + if let Value::Record { val, .. } = value { + val.to_mut().retain_mut(|key2, value| { + let span = value.span(); + let config_point = match key2 { + "vi_insert" => &mut config.cursor_shape.vi_insert, + "vi_normal" => &mut config.cursor_shape.vi_normal, + "emacs" => &mut config.cursor_shape.emacs, + _ => { + report_invalid_key(&[key, key2], span, &mut errors); + return false; + } + }; + process_string_enum( + config_point, + &[key, key2], + value, + &mut errors + ); + true + }); + } else { + report_invalid_value("should be a record", span, &mut errors); + *value = config.cursor_shape.into_value(span); + } + } + "table" => { + if let Value::Record { val, .. } = value { + val.to_mut().retain_mut(|key2, value| { + let span = value.span(); + match key2 { + "mode" => { + process_string_enum( + &mut config.table.mode, + &[key, key2], + value, + &mut errors + ); + } + "header_on_separator" => { + process_bool_config(value, &mut errors, &mut config.table.header_on_separator); + } + "padding" => match value { + Value::Int { val, .. } => { + if *val < 0 { + report_invalid_value("expected a unsigned integer", span, &mut errors); + *value = config.table.padding.into_value(span); + } else { + config.table.padding.left = *val as usize; + config.table.padding.right = *val as usize; + } + } + Value::Record { val, .. } => { + let mut invalid = false; + val.to_mut().retain(|key3, value| { + match key3 { + "left" => { + match value.as_int() { + Ok(val) if val >= 0 => { + config.table.padding.left = val as usize; + } + _ => { + report_invalid_value("expected a unsigned integer >= 0", span, &mut errors); + invalid = true; + } + } + } + "right" => { + match value.as_int() { + Ok(val) if val >= 0 => { + config.table.padding.right = val as usize; + } + _ => { + report_invalid_value("expected a unsigned integer >= 0", span, &mut errors); + invalid = true; + } + } + } + _ => { + report_invalid_key(&[key, key2, key3], span, &mut errors); + return false; + } + }; + true + }); + if invalid { + *value = config.table.padding.into_value(span); + } + } + _ => { + report_invalid_value("expected a unsigned integer or a record", span, &mut errors); + *value = config.table.padding.into_value(span); + } + }, + "index_mode" => { + process_string_enum( + &mut config.table.index_mode, + &[key, key2], + value, + &mut errors + ); + } + "trim" => { + match try_parse_trim_strategy(value, &mut errors) { + Ok(v) => config.table.trim = v, + Err(e) => { + // try_parse_trim_strategy() already adds its own errors + errors.push(e); + *value = config.table.trim.clone().into_value(span); + } + } + } + "show_empty" => { + process_bool_config(value, &mut errors, &mut config.table.show_empty); + } + "abbreviated_row_count" => { + match *value { + Value::Int { val, .. } => { + if val >= 0 { + config.table.abbreviated_row_count = Some(val as usize); + } else { + report_invalid_value("should be an int unsigned", span, &mut errors); + *value = config.table.abbreviated_row_count.map(|count| Value::int(count as i64, span)).unwrap_or(Value::nothing(span)); + } + } + Value::Nothing { .. } => { + config.table.abbreviated_row_count = None; + } + _ => { + report_invalid_value("should be an int", span, &mut errors); + *value = config.table.abbreviated_row_count.map(|count| Value::int(count as i64, span)).unwrap_or(Value::nothing(span)) + } + } + } + _ => { + report_invalid_key(&[key, key2], span, &mut errors); + return false; + } + }; + true + }); + } else { + report_invalid_value("should be a record", span, &mut errors); + *value = config.table.clone().into_value(span); + } + } + "filesize" => { + if let Value::Record { val, .. } = value { + val.to_mut().retain_mut(|key2, value| { + let span = value.span(); + match key2 { + "metric" => { + process_bool_config(value, &mut errors, &mut config.filesize.metric); + } + "format" => { + if let Ok(v) = value.coerce_str() { + config.filesize.format = v.to_lowercase(); + } else { + report_invalid_value("should be a string", span, &mut errors); + *value = Value::string(config.filesize.format.clone(), span); + } + } + _ => { + report_invalid_key(&[key, key2], span, &mut errors); + return false; + } + }; + true + }) + } else { + report_invalid_value("should be a record", span, &mut errors); + *value = config.filesize.clone().into_value(span); + } + } + "explore" => { + if let Ok(map) = create_map(value) { + config.explore = map; + } else { + report_invalid_value("should be a record", span, &mut errors); + *value = config.explore.clone().into_value(span); + } + } + // Misc. options + "color_config" => { + if let Ok(map) = create_map(value) { + config.color_config = map; + } else { + report_invalid_value("should be a record", span, &mut errors); + *value = config.color_config.clone().into_value(span); + } + } + "use_grid_icons" => { + process_bool_config(value, &mut errors, &mut config.use_grid_icons); + } + "footer_mode" => { + process_string_enum( + &mut config.footer_mode, + &[key], + value, + &mut errors + ); + } + "float_precision" => { + process_int_config(value, &mut errors, &mut config.float_precision); + } + "use_ansi_coloring" => { + process_bool_config(value, &mut errors, &mut config.use_ansi_coloring); + } + "edit_mode" => { + process_string_enum( + &mut config.edit_mode, + &[key], + value, + &mut errors + ); + } + "shell_integration" => { + if let Value::Record { val, .. } = value { + val.to_mut().retain_mut(|key2, value| { + let span = value.span(); + match key2 { + "osc2" => { + process_bool_config(value, &mut errors, &mut config.shell_integration.osc2); + } + "osc7" => { + process_bool_config(value, &mut errors, &mut config.shell_integration.osc7); + } + "osc8" => { + process_bool_config(value, &mut errors, &mut config.shell_integration.osc8); + } + "osc9_9" => { + process_bool_config(value, &mut errors, &mut config.shell_integration.osc9_9); + } + "osc133" => { + process_bool_config(value, &mut errors, &mut config.shell_integration.osc133); + } + "osc633" => { + process_bool_config(value, &mut errors, &mut config.shell_integration.osc633); + } + "reset_application_mode" => { + process_bool_config(value, &mut errors, &mut config.shell_integration.reset_application_mode); + } + _ => { + report_invalid_key(&[key, key2], span, &mut errors); + return false; + } + }; + true + }) + } else { + report_invalid_value("boolean value is deprecated, should be a record. see `config nu --default`.", span, &mut errors); + *value = config.shell_integration.into_value(span); + } + } + "buffer_editor" => match value { + Value::Nothing { .. } | Value::String { .. } => { + config.buffer_editor = value.clone(); + } + Value::List { vals, .. } + if vals.iter().all(|val| matches!(val, Value::String { .. })) => + { + config.buffer_editor = value.clone(); + } + _ => { + report_invalid_value("should be a string, list, or null", span, &mut errors); + *value = config.buffer_editor.clone(); + } + }, + "show_banner" => { + process_bool_config(value, &mut errors, &mut config.show_banner); + } + "render_right_prompt_on_last_line" => { + process_bool_config(value, &mut errors, &mut config.render_right_prompt_on_last_line); + } + "bracketed_paste" => { + process_bool_config(value, &mut errors, &mut config.bracketed_paste); + } + "use_kitty_protocol" => { + process_bool_config(value, &mut errors, &mut config.use_kitty_protocol); + } + "highlight_resolved_externals" => { + process_bool_config(value, &mut errors, &mut config.highlight_resolved_externals); + } + "plugins" => { + if let Ok(map) = create_map(value) { + config.plugins = map; + } else { + report_invalid_value("should be a record", span, &mut errors); + *value = config.plugins.clone().into_value(span); + } + } + "plugin_gc" => { + config.plugin_gc.process(&[key], value, &mut errors); + } + "menus" => match create_menus(value) { + Ok(map) => config.menus = map, + Err(e) => { + report_invalid_value("should be a valid list of menus", span, &mut errors); + errors.push(e); + *value = config.menus.clone().into_value(span); + } + }, + "keybindings" => match create_keybindings(value) { + Ok(keybindings) => config.keybindings = keybindings, + Err(e) => { + report_invalid_value("should be a valid keybindings list", span, &mut errors); + errors.push(e); + *value = config.keybindings.clone().into_value(span); + } + }, + "hooks" => match create_hooks(value) { + Ok(hooks) => config.hooks = hooks, + Err(e) => { + report_invalid_value("should be a valid hooks list", span, &mut errors); + errors.push(e); + *value = config.hooks.clone().into_value(span); + } + }, + "datetime_format" => { + if let Value::Record { val, .. } = value { + val.to_mut().retain_mut(|key2, value| + { + let span = value.span(); + match key2 { + "normal" => { + if let Ok(v) = value.coerce_string() { + config.datetime_format.normal = Some(v); + } else { + report_invalid_value("should be a string", span, &mut errors); + } + } + "table" => { + if let Ok(v) = value.coerce_string() { + config.datetime_format.table = Some(v); + } else { + report_invalid_value("should be a string", span, &mut errors); + } + } + _ => { + report_invalid_key(&[key, key2], span, &mut errors); + return false; + } + }; + true + }) + } else { + report_invalid_value("should be a record", span, &mut errors); + *value = config.datetime_format.clone().into_value(span); + } + } + "error_style" => { + process_string_enum( + &mut config.error_style, + &[key], + value, + &mut errors + ); + } + "recursion_limit" => { + if let Value::Int { val, internal_span } = value { + if val > &mut 1 { + config.recursion_limit = *val; + } else { + report_invalid_value("should be a integer greater than 1", span, &mut errors); + *value = Value::Int { val: 50, internal_span: *internal_span }; + } + } else { + report_invalid_value("should be a integer greater than 1", span, &mut errors); + *value = Value::Int { val: 50, internal_span: value.span() }; + } + } + // Catch all + _ => { + report_invalid_key(&[key], span, &mut errors); + return false; + } + }; + true + }); // Return the config and the vec of errors. ( diff --git a/crates/nu-protocol/src/config/output.rs b/crates/nu-protocol/src/config/output.rs index 473f9cfdff..c2340c9963 100644 --- a/crates/nu-protocol/src/config/output.rs +++ b/crates/nu-protocol/src/config/output.rs @@ -1,9 +1,7 @@ -use super::helper::ReconstructVal; -use crate::{Config, Record, Span, Value}; -use serde::{Deserialize, Serialize}; -use std::str::FromStr; +use super::prelude::*; +use crate as nu_protocol; -#[derive(Serialize, Deserialize, Clone, Debug, Copy)] +#[derive(Clone, Copy, Debug, IntoValue, PartialEq, Eq, Serialize, Deserialize)] pub enum ErrorStyle { Plain, Fancy, @@ -20,26 +18,3 @@ impl FromStr for ErrorStyle { } } } - -impl ReconstructVal for ErrorStyle { - fn reconstruct_value(&self, span: Span) -> Value { - Value::string( - match self { - ErrorStyle::Fancy => "fancy", - ErrorStyle::Plain => "plain", - }, - span, - ) - } -} - -pub(super) fn reconstruct_datetime_format(config: &Config, span: Span) -> Value { - let mut record = Record::new(); - if let Some(normal) = &config.datetime_normal_format { - record.push("normal", Value::string(normal, span)); - } - if let Some(table) = &config.datetime_table_format { - record.push("table", Value::string(table, span)); - } - Value::record(record, span) -} diff --git a/crates/nu-protocol/src/config/plugin_gc.rs b/crates/nu-protocol/src/config/plugin_gc.rs index 70beeb6ae3..240b65349a 100644 --- a/crates/nu-protocol/src/config/plugin_gc.rs +++ b/crates/nu-protocol/src/config/plugin_gc.rs @@ -1,15 +1,11 @@ +use super::helper::{process_bool_config, report_invalid_key, report_invalid_value}; +use super::prelude::*; +use crate as nu_protocol; +use crate::ShellError; use std::collections::HashMap; -use serde::{Deserialize, Serialize}; - -use crate::{record, ShellError, Span, Value}; - -use super::helper::{ - process_bool_config, report_invalid_key, report_invalid_value, ReconstructVal, -}; - /// Configures when plugins should be stopped if inactive -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] +#[derive(Clone, Debug, Default, IntoValue, PartialEq, Eq, Serialize, Deserialize)] pub struct PluginGcConfigs { /// The config to use for plugins not otherwise specified pub default: PluginGcConfig, @@ -61,23 +57,11 @@ impl PluginGcConfigs { }); } else { report_invalid_value("should be a record", value.span(), errors); - *value = self.reconstruct_value(value.span()); + *value = self.clone().into_value(value.span()); } } } -impl ReconstructVal for PluginGcConfigs { - fn reconstruct_value(&self, span: Span) -> Value { - Value::record( - record! { - "default" => self.default.reconstruct_value(span), - "plugins" => reconstruct_plugins(&self.plugins, span), - }, - span, - ) - } -} - fn process_plugins( path: &[&str], value: &mut Value, @@ -100,7 +84,7 @@ fn process_plugins( report_invalid_value("should be a record", value.span(), errors); if let Some(conf) = plugins.get(key) { // Reconstruct the value if it existed before - *value = conf.reconstruct_value(value.span()); + *value = conf.clone().into_value(value.span()); true } else { // Remove it if it didn't @@ -111,18 +95,8 @@ fn process_plugins( } } -fn reconstruct_plugins(plugins: &HashMap, span: Span) -> Value { - Value::record( - plugins - .iter() - .map(|(key, val)| (key.to_owned(), val.reconstruct_value(span))) - .collect(), - span, - ) -} - /// Configures when a plugin should be stopped if inactive -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct PluginGcConfig { /// True if the plugin should be stopped automatically pub enabled: bool, @@ -139,6 +113,16 @@ impl Default for PluginGcConfig { } } +impl IntoValue for PluginGcConfig { + fn into_value(self, span: Span) -> Value { + record! { + "enabled" => self.enabled.into_value(span), + "stop_after" => Value::duration(self.stop_after, span), + } + .into_value(span) + } +} + impl PluginGcConfig { fn process(&mut self, path: &[&str], value: &mut Value, errors: &mut Vec) { if let Value::Record { val, .. } = value { @@ -177,23 +161,11 @@ impl PluginGcConfig { }) } else { report_invalid_value("should be a record", value.span(), errors); - *value = self.reconstruct_value(value.span()); + *value = self.clone().into_value(value.span()); } } } -impl ReconstructVal for PluginGcConfig { - fn reconstruct_value(&self, span: Span) -> Value { - Value::record( - record! { - "enabled" => Value::bool(self.enabled, span), - "stop_after" => Value::duration(self.stop_after, span), - }, - span, - ) - } -} - fn join_path<'a>(a: &[&'a str], b: &[&'a str]) -> Vec<&'a str> { a.iter().copied().chain(b.iter().copied()).collect() } @@ -201,6 +173,7 @@ fn join_path<'a>(a: &[&'a str], b: &[&'a str]) -> Vec<&'a str> { #[cfg(test)] mod tests { use super::*; + use nu_protocol::{record, Span}; fn test_pair() -> (PluginGcConfigs, Value) { ( @@ -247,6 +220,6 @@ mod tests { #[test] fn reconstruct() { let (input, expected) = test_pair(); - assert_eq!(expected, input.reconstruct_value(Span::test_data())); + assert_eq!(expected, input.into_value(Span::test_data())); } } diff --git a/crates/nu-protocol/src/config/prelude.rs b/crates/nu-protocol/src/config/prelude.rs new file mode 100644 index 0000000000..8bc100cb90 --- /dev/null +++ b/crates/nu-protocol/src/config/prelude.rs @@ -0,0 +1,3 @@ +pub use crate::{record, IntoValue, Span, Value}; +pub use serde::{Deserialize, Serialize}; +pub use std::str::FromStr; diff --git a/crates/nu-protocol/src/config/reedline.rs b/crates/nu-protocol/src/config/reedline.rs index 7032e74087..65129cf176 100644 --- a/crates/nu-protocol/src/config/reedline.rs +++ b/crates/nu-protocol/src/config/reedline.rs @@ -1,11 +1,9 @@ -use std::str::FromStr; - -use super::{extract_value, helper::ReconstructVal}; -use crate::{record, Config, ShellError, Span, Value}; -use serde::{Deserialize, Serialize}; +use super::{extract_value, prelude::*}; +use crate as nu_protocol; +use crate::ShellError; /// Definition of a parsed keybinding from the config object -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Clone, Debug, IntoValue, Serialize, Deserialize)] pub struct ParsedKeybinding { pub modifier: Value, pub keycode: Value, @@ -14,23 +12,23 @@ pub struct ParsedKeybinding { } /// Definition of a parsed menu from the config object -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Clone, Debug, IntoValue, Serialize, Deserialize)] pub struct ParsedMenu { pub name: Value, pub marker: Value, pub only_buffer_difference: Value, pub style: Value, - pub menu_type: Value, + pub r#type: Value, pub source: Value, } /// Definition of a Nushell CursorShape (to be mapped to crossterm::cursor::CursorShape) -#[derive(Serialize, Deserialize, Clone, Debug, Copy, Default)] +#[derive(Clone, Copy, Debug, Default, IntoValue, PartialEq, Eq, Serialize, Deserialize)] pub enum NuCursorShape { - UnderScore, + Underscore, Line, Block, - BlinkUnderScore, + BlinkUnderscore, BlinkLine, BlinkBlock, #[default] @@ -44,66 +42,24 @@ impl FromStr for NuCursorShape { match s.to_ascii_lowercase().as_str() { "line" => Ok(NuCursorShape::Line), "block" => Ok(NuCursorShape::Block), - "underscore" => Ok(NuCursorShape::UnderScore), + "underscore" => Ok(NuCursorShape::Underscore), "blink_line" => Ok(NuCursorShape::BlinkLine), "blink_block" => Ok(NuCursorShape::BlinkBlock), - "blink_underscore" => Ok(NuCursorShape::BlinkUnderScore), + "blink_underscore" => Ok(NuCursorShape::BlinkUnderscore), "inherit" => Ok(NuCursorShape::Inherit), _ => Err("expected either 'line', 'block', 'underscore', 'blink_line', 'blink_block', 'blink_underscore' or 'inherit'"), } } } -impl ReconstructVal for NuCursorShape { - fn reconstruct_value(&self, span: Span) -> Value { - Value::string( - match self { - NuCursorShape::Line => "line", - NuCursorShape::Block => "block", - NuCursorShape::UnderScore => "underscore", - NuCursorShape::BlinkLine => "blink_line", - NuCursorShape::BlinkBlock => "blink_block", - NuCursorShape::BlinkUnderScore => "blink_underscore", - NuCursorShape::Inherit => "inherit", - }, - span, - ) - } +#[derive(Clone, Copy, Debug, Default, IntoValue, PartialEq, Eq, Serialize, Deserialize)] +pub struct CursorShapeConfig { + pub emacs: NuCursorShape, + pub vi_insert: NuCursorShape, + pub vi_normal: NuCursorShape, } -#[derive(Serialize, Deserialize, Clone, Debug, Copy)] -pub enum HistoryFileFormat { - /// Store history as an SQLite database with additional context - Sqlite, - /// store history as a plain text file where every line is one command (without any context such as timestamps) - PlainText, -} - -impl FromStr for HistoryFileFormat { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - match s.to_ascii_lowercase().as_str() { - "sqlite" => Ok(Self::Sqlite), - "plaintext" => Ok(Self::PlainText), - _ => Err("expected either 'sqlite' or 'plaintext'"), - } - } -} - -impl ReconstructVal for HistoryFileFormat { - fn reconstruct_value(&self, span: Span) -> Value { - Value::string( - match self { - HistoryFileFormat::Sqlite => "sqlite", - HistoryFileFormat::PlainText => "plaintext", - }, - span, - ) - } -} - -#[derive(Serialize, Deserialize, Clone, Debug, Default, Copy)] +#[derive(Clone, Copy, Debug, Default, IntoValue, PartialEq, Eq, Serialize, Deserialize)] pub enum EditBindings { Vi, #[default] @@ -122,18 +78,6 @@ impl FromStr for EditBindings { } } -impl ReconstructVal for EditBindings { - fn reconstruct_value(&self, span: Span) -> Value { - Value::string( - match self { - EditBindings::Vi => "vi", - EditBindings::Emacs => "emacs", - }, - span, - ) - } -} - /// Parses the config object to extract the strings that will compose a keybinding for reedline pub(super) fn create_keybindings(value: &Value) -> Result, ShellError> { let span = value.span(); @@ -172,34 +116,6 @@ pub(super) fn create_keybindings(value: &Value) -> Result, } } -pub(super) fn reconstruct_keybindings(config: &Config, span: Span) -> Value { - Value::list( - config - .keybindings - .iter() - .map( - |ParsedKeybinding { - modifier, - keycode, - mode, - event, - }| { - Value::record( - record! { - "modifier" => modifier.clone(), - "keycode" => keycode.clone(), - "mode" => mode.clone(), - "event" => event.clone(), - }, - span, - ) - }, - ) - .collect(), - span, - ) -} - /// Parses the config object to extract the strings that will compose a keybinding for reedline pub fn create_menus(value: &Value) -> Result, ShellError> { let span = value.span(); @@ -211,7 +127,7 @@ pub fn create_menus(value: &Value) -> Result, ShellError> { let only_buffer_difference = extract_value("only_buffer_difference", val, span)?.clone(); let style = extract_value("style", val, span)?.clone(); - let menu_type = extract_value("type", val, span)?.clone(); + let r#type = extract_value("type", val, span)?.clone(); // Source is an optional value let source = match extract_value("source", val, span) { @@ -224,7 +140,7 @@ pub fn create_menus(value: &Value) -> Result, ShellError> { only_buffer_difference, marker, style, - menu_type, + r#type, source, }; @@ -243,35 +159,3 @@ pub fn create_menus(value: &Value) -> Result, ShellError> { _ => Ok(Vec::new()), } } - -pub(super) fn reconstruct_menus(config: &Config, span: Span) -> Value { - Value::list( - config - .menus - .iter() - .map( - |ParsedMenu { - name, - only_buffer_difference, - marker, - style, - menu_type, // WARNING: this is not the same name as what is used in Config.nu! ("type") - source, - }| { - Value::record( - record! { - "name" => name.clone(), - "only_buffer_difference" => only_buffer_difference.clone(), - "marker" => marker.clone(), - "style" => style.clone(), - "type" => menu_type.clone(), - "source" => source.clone(), - }, - span, - ) - }, - ) - .collect(), - span, - ) -} diff --git a/crates/nu-protocol/src/config/rm.rs b/crates/nu-protocol/src/config/rm.rs new file mode 100644 index 0000000000..1d7beca04e --- /dev/null +++ b/crates/nu-protocol/src/config/rm.rs @@ -0,0 +1,16 @@ +use super::prelude::*; +use crate as nu_protocol; + +#[derive(Clone, Copy, Debug, IntoValue, PartialEq, Eq, Serialize, Deserialize)] +pub struct RmConfig { + pub always_trash: bool, +} + +#[allow(clippy::derivable_impls)] +impl Default for RmConfig { + fn default() -> Self { + Self { + always_trash: false, + } + } +} diff --git a/crates/nu-protocol/src/config/shell_integration.rs b/crates/nu-protocol/src/config/shell_integration.rs new file mode 100644 index 0000000000..f1eb6b3a53 --- /dev/null +++ b/crates/nu-protocol/src/config/shell_integration.rs @@ -0,0 +1,28 @@ +use super::prelude::*; +use crate as nu_protocol; + +#[derive(Clone, Copy, Debug, IntoValue, PartialEq, Eq, Serialize, Deserialize)] +pub struct ShellIntegrationConfig { + pub osc2: bool, + pub osc7: bool, + pub osc8: bool, + pub osc9_9: bool, + pub osc133: bool, + pub osc633: bool, + pub reset_application_mode: bool, +} + +#[allow(clippy::derivable_impls)] +impl Default for ShellIntegrationConfig { + fn default() -> Self { + Self { + osc2: false, + osc7: false, + osc8: false, + osc9_9: false, + osc133: false, + osc633: false, + reset_application_mode: false, + } + } +} diff --git a/crates/nu-protocol/src/config/table.rs b/crates/nu-protocol/src/config/table.rs index ea17bf9fec..ac752e267c 100644 --- a/crates/nu-protocol/src/config/table.rs +++ b/crates/nu-protocol/src/config/table.rs @@ -1,9 +1,8 @@ -use super::helper::ReconstructVal; -use crate::{record, Config, ShellError, Span, Value}; -use serde::{Deserialize, Serialize}; -use std::str::FromStr; +use super::prelude::*; +use crate as nu_protocol; +use crate::ShellError; -#[derive(Serialize, Deserialize, Clone, Copy, Debug, Default)] +#[derive(Clone, Copy, Debug, Default, IntoValue, PartialEq, Eq, Serialize, Deserialize)] pub enum TableMode { Basic, Thin, @@ -51,33 +50,7 @@ impl FromStr for TableMode { } } -impl ReconstructVal for TableMode { - fn reconstruct_value(&self, span: Span) -> Value { - Value::string( - match self { - TableMode::Basic => "basic", - TableMode::Thin => "thin", - TableMode::Light => "light", - TableMode::Compact => "compact", - TableMode::WithLove => "with_love", - TableMode::CompactDouble => "compact_double", - TableMode::Rounded => "rounded", - TableMode::Reinforced => "reinforced", - TableMode::Heavy => "heavy", - TableMode::None => "none", - TableMode::Psql => "psql", - TableMode::Markdown => "markdown", - TableMode::Dots => "dots", - TableMode::Restructured => "restructured", - TableMode::AsciiRounded => "ascii_rounded", - TableMode::BasicCompact => "basic_compact", - }, - span, - ) - } -} - -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum FooterMode { /// Never show the footer Never, @@ -108,21 +81,18 @@ impl FromStr for FooterMode { } } -impl ReconstructVal for FooterMode { - fn reconstruct_value(&self, span: Span) -> Value { - Value::string( - match self { - FooterMode::Always => "always".to_string(), - FooterMode::Never => "never".to_string(), - FooterMode::Auto => "auto".to_string(), - FooterMode::RowCount(c) => c.to_string(), - }, - span, - ) +impl IntoValue for FooterMode { + fn into_value(self, span: Span) -> Value { + match self { + FooterMode::Always => "always".into_value(span), + FooterMode::Never => "never".into_value(span), + FooterMode::Auto => "auto".into_value(span), + FooterMode::RowCount(c) => c.to_string().into_value(span), + } } } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Clone, Copy, Debug, IntoValue, PartialEq, Eq, Serialize, Deserialize)] pub enum TableIndexMode { /// Always show indexes Always, @@ -145,22 +115,9 @@ impl FromStr for TableIndexMode { } } -impl ReconstructVal for TableIndexMode { - fn reconstruct_value(&self, span: Span) -> Value { - Value::string( - match self { - TableIndexMode::Always => "always", - TableIndexMode::Never => "never", - TableIndexMode::Auto => "auto", - }, - span, - ) - } -} - /// A Table view configuration, for a situation where /// we need to limit cell width in order to adjust for a terminal size. -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum TrimStrategy { /// Wrapping strategy. /// @@ -196,7 +153,7 @@ impl TrimStrategy { impl Default for TrimStrategy { fn default() -> Self { - TrimStrategy::Wrap { + Self::Wrap { try_to_keep_words: true, } } @@ -289,45 +246,89 @@ fn try_parse_trim_methodology(value: &Value) -> Option { None } -pub(super) fn reconstruct_trim_strategy(config: &Config, span: Span) -> Value { - match &config.trim_strategy { - TrimStrategy::Wrap { try_to_keep_words } => Value::record( - record! { - "methodology" => Value::string("wrapping", span), - "wrapping_try_keep_words" => Value::bool(*try_to_keep_words, span), - }, - span, - ), - TrimStrategy::Truncate { suffix } => Value::record( - match suffix { - Some(s) => record! { - "methodology" => Value::string("truncating", span), - "truncating_suffix" => Value::string(s.clone(), span), - }, - None => record! { - "methodology" => Value::string("truncating", span), - "truncating_suffix" => Value::nothing(span), - }, - }, - span, - ), +impl IntoValue for TrimStrategy { + fn into_value(self, span: Span) -> Value { + match self { + TrimStrategy::Wrap { try_to_keep_words } => { + record! { + "methodology" => "wrapping".into_value(span), + "wrapping_try_keep_words" => try_to_keep_words.into_value(span), + } + } + TrimStrategy::Truncate { suffix } => { + record! { + "methodology" => "truncating".into_value(span), + "truncating_suffix" => suffix.into_value(span), + } + } + } + .into_value(span) } } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct TableIndent { pub left: usize, pub right: usize, } -pub(super) fn reconstruct_padding(config: &Config, span: Span) -> Value { - // For better completions always reconstruct the record version even though unsigned int would - // be supported, `as` conversion is sane as it came from an i64 original - Value::record( - record!( - "left" => Value::int(config.table_indent.left as i64, span), - "right" => Value::int(config.table_indent.right as i64, span), - ), - span, - ) +impl IntoValue for TableIndent { + fn into_value(self, span: Span) -> Value { + record! { + "left" => (self.left as i64).into_value(span), + "right" => (self.right as i64).into_value(span), + } + .into_value(span) + } +} + +impl Default for TableIndent { + fn default() -> Self { + Self { left: 1, right: 1 } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct TableConfig { + pub mode: TableMode, + pub index_mode: TableIndexMode, + pub show_empty: bool, + pub padding: TableIndent, + pub trim: TrimStrategy, + pub header_on_separator: bool, + pub abbreviated_row_count: Option, +} + +impl IntoValue for TableConfig { + fn into_value(self, span: Span) -> Value { + let abbv_count = self + .abbreviated_row_count + .map(|t| t as i64) + .into_value(span); + + record! { + "mode" => self.mode.into_value(span), + "index_mode" => self.index_mode.into_value(span), + "show_empty" => self.show_empty.into_value(span), + "padding" => self.padding.into_value(span), + "trim" => self.trim.into_value(span), + "header_on_separator" => self.header_on_separator.into_value(span), + "abbreviated_row_count" => abbv_count, + } + .into_value(span) + } +} + +impl Default for TableConfig { + fn default() -> Self { + Self { + mode: TableMode::Rounded, + index_mode: TableIndexMode::Always, + show_empty: true, + trim: TrimStrategy::default(), + header_on_separator: false, + padding: TableIndent::default(), + abbreviated_row_count: None, + } + } } diff --git a/crates/nu-protocol/src/eval_const.rs b/crates/nu-protocol/src/eval_const.rs index 1ee7802799..72f165d195 100644 --- a/crates/nu-protocol/src/eval_const.rs +++ b/crates/nu-protocol/src/eval_const.rs @@ -95,7 +95,7 @@ pub(crate) fn create_nu_constant(engine_state: &EngineState, span: Span) -> Valu HistoryFileFormat::Sqlite => { path.push("history.sqlite3"); } - HistoryFileFormat::PlainText => { + HistoryFileFormat::Plaintext => { path.push("history.txt"); } } diff --git a/crates/nu-protocol/src/value/filesize.rs b/crates/nu-protocol/src/value/filesize.rs index 79ecc136b9..9d58c103df 100644 --- a/crates/nu-protocol/src/value/filesize.rs +++ b/crates/nu-protocol/src/value/filesize.rs @@ -8,8 +8,8 @@ pub fn format_filesize_from_conf(num_bytes: i64, config: &Config) -> String { // and filesize_metric is false, return KiB format_filesize( num_bytes, - config.filesize_format.as_str(), - Some(config.filesize_metric), + &config.filesize.format, + Some(config.filesize.metric), ) } diff --git a/crates/nu-protocol/src/value/mod.rs b/crates/nu-protocol/src/value/mod.rs index 3620be698c..80f96707af 100644 --- a/crates/nu-protocol/src/value/mod.rs +++ b/crates/nu-protocol/src/value/mod.rs @@ -832,7 +832,7 @@ impl Value { Value::Float { val, .. } => val.to_string(), Value::Filesize { val, .. } => format_filesize_from_conf(*val, config), Value::Duration { val, .. } => format_duration(*val), - Value::Date { val, .. } => match &config.datetime_normal_format { + Value::Date { val, .. } => match &config.datetime_format.normal { Some(format) => self.format_datetime(val, format), None => { format!( @@ -886,7 +886,7 @@ impl Value { /// - "[record {n} fields]" pub fn to_abbreviated_string(&self, config: &Config) -> String { match self { - Value::Date { val, .. } => match &config.datetime_table_format { + Value::Date { val, .. } => match &config.datetime_format.table { Some(format) => self.format_datetime(val, format), None => HumanTime::from(*val).to_string(), }, diff --git a/crates/nu-table/src/common.rs b/crates/nu-table/src/common.rs index 76cd8adf52..b15874d7c8 100644 --- a/crates/nu-table/src/common.rs +++ b/crates/nu-table/src/common.rs @@ -23,8 +23,8 @@ pub fn create_nu_table_config( with_index: out.with_index, with_header: out.with_header, split_color: Some(lookup_separator_color(comp)), - trim: config.trim_strategy.clone(), - header_on_border: config.table_move_header, + trim: config.table.trim.clone(), + header_on_border: config.table.header_on_separator, expand, } } @@ -62,7 +62,8 @@ pub fn error_sign(style_computer: &StyleComputer) -> (String, TextStyle) { } pub fn wrap_text(text: &str, width: usize, config: &Config) -> String { - string_wrap(text, width, is_cfg_trim_keep_words(config)) + let keep_words = config.table.trim == TrimStrategy::wrap(true); + string_wrap(text, width, keep_words) } pub fn get_header_style(style_computer: &StyleComputer) -> TextStyle { @@ -163,15 +164,6 @@ fn convert_with_precision(val: &str, precision: usize) -> Result bool { - matches!( - config.trim_strategy, - TrimStrategy::Wrap { - try_to_keep_words: true - } - ) -} - pub fn load_theme(mode: TableMode) -> TableTheme { match mode { TableMode::Basic => TableTheme::basic(), diff --git a/crates/nu-table/src/types/collapse.rs b/crates/nu-table/src/types/collapse.rs index e25b7f1e79..0012c70a35 100644 --- a/crates/nu-table/src/types/collapse.rs +++ b/crates/nu-table/src/types/collapse.rs @@ -36,7 +36,7 @@ fn collapsed_table( return Ok(None); } - let indent = (config.table_indent.left, config.table_indent.right); + let indent = (config.table.padding.left, config.table.padding.right); let table = table.draw(style_computer, &theme, indent); Ok(Some(table)) diff --git a/crates/nu-table/src/types/general.rs b/crates/nu-table/src/types/general.rs index 66048267d7..f7bd24f2e2 100644 --- a/crates/nu-table/src/types/general.rs +++ b/crates/nu-table/src/types/general.rs @@ -26,8 +26,8 @@ impl JustTable { fn create_table(input: &[Value], opts: TableOpts<'_>) -> Result, ShellError> { match table(input, &opts)? { Some(mut out) => { - let left = opts.config.table_indent.left; - let right = opts.config.table_indent.right; + let left = opts.config.table.padding.left; + let right = opts.config.table.padding.right; out.table.set_indent(left, right); colorize_space(out.table.get_records_mut(), opts.style_computer); @@ -59,8 +59,8 @@ fn kv_table(record: &Record, opts: TableOpts<'_>) -> StringResult { let mut out = TableOutput::new(table, false, true); - let left = opts.config.table_indent.left; - let right = opts.config.table_indent.right; + let left = opts.config.table.padding.left; + let right = opts.config.table.padding.right; out.table.set_indent(left, right); let table_config = diff --git a/crates/nu-table/src/types/mod.rs b/crates/nu-table/src/types/mod.rs index 163e4b0c81..571dda26ab 100644 --- a/crates/nu-table/src/types/mod.rs +++ b/crates/nu-table/src/types/mod.rs @@ -67,7 +67,7 @@ impl<'a> TableOpts<'a> { } fn has_index(opts: &TableOpts<'_>, headers: &[String]) -> bool { - let with_index = match opts.config.table_index_mode { + let with_index = match opts.config.table.index_mode { TableIndexMode::Always => true, TableIndexMode::Never => false, TableIndexMode::Auto => headers.iter().any(|header| header == INDEX_COLUMN_NAME),