use crate::{menus::NuMenuCompleter, NuHelpCompleter}; use crossterm::event::{KeyCode, KeyModifiers}; use log::trace; use nu_ansi_term::Style; use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style}; use nu_engine::eval_block; use nu_parser::parse; use nu_protocol::{ create_menus, debugger::WithoutDebug, engine::{EngineState, Stack, StateWorkingSet}, extract_value, Config, EditBindings, ParsedKeybinding, ParsedMenu, PipelineData, Record, ShellError, Span, Value, }; use reedline::{ default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings, ColumnarMenu, DescriptionMenu, DescriptionMode, EditCommand, IdeMenu, Keybindings, ListMenu, MenuBuilder, Reedline, ReedlineEvent, ReedlineMenu, }; use std::sync::Arc; const DEFAULT_COMPLETION_MENU: &str = r#" { name: completion_menu only_buffer_difference: false marker: "| " type: { layout: columnar columns: 4 col_width: 20 col_padding: 2 } style: { text: green, selected_text: green_reverse description_text: yellow } }"#; const DEFAULT_HISTORY_MENU: &str = r#" { name: history_menu only_buffer_difference: true marker: "? " type: { layout: list page_size: 10 } style: { text: green, selected_text: green_reverse description_text: yellow } }"#; const DEFAULT_HELP_MENU: &str = r#" { name: help_menu only_buffer_difference: true marker: "? " type: { layout: description columns: 4 col_width: 20 col_padding: 2 selection_rows: 4 description_rows: 10 } style: { text: green, selected_text: green_reverse description_text: yellow } }"#; // Adds all menus to line editor pub(crate) fn add_menus( mut line_editor: Reedline, engine_state: Arc, stack: &Stack, config: &Config, ) -> Result { trace!("add_menus: config: {:#?}", &config); line_editor = line_editor.clear_menus(); for menu in &config.menus { line_editor = add_menu(line_editor, menu, engine_state.clone(), stack, config)? } // Checking if the default menus have been added from the config file let default_menus = [ ("completion_menu", DEFAULT_COMPLETION_MENU), ("history_menu", DEFAULT_HISTORY_MENU), ("help_menu", DEFAULT_HELP_MENU), ]; for (name, definition) in default_menus { if !config .menus .iter() .any(|menu| menu.name.to_expanded_string("", config) == name) { let (block, _) = { let mut working_set = StateWorkingSet::new(&engine_state); let output = parse( &mut working_set, Some(name), // format!("entry #{}", entry_num) definition.as_bytes(), true, ); (output, working_set.render()) }; let mut temp_stack = Stack::new().capture(); let input = PipelineData::Empty; let res = eval_block::(&engine_state, &mut temp_stack, &block, input)?; if let PipelineData::Value(value, None) = res { for menu in create_menus(&value)? { line_editor = add_menu(line_editor, &menu, engine_state.clone(), stack, config)?; } } } } Ok(line_editor) } fn add_menu( line_editor: Reedline, menu: &ParsedMenu, engine_state: Arc, stack: &Stack, config: &Config, ) -> Result { let span = menu.menu_type.span(); if let Value::Record { val, .. } = &menu.menu_type { let layout = extract_value("layout", val, span)?.to_expanded_string("", config); match layout.as_str() { "columnar" => add_columnar_menu(line_editor, menu, engine_state, stack, config), "list" => add_list_menu(line_editor, menu, engine_state, stack, config), "ide" => add_ide_menu(line_editor, menu, engine_state, stack, config), "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(), }), } } else { Err(ShellError::UnsupportedConfigValue { expected: "only record type".to_string(), value: menu.menu_type.to_abbreviated_string(config), span: menu.menu_type.span(), }) } } fn get_style(record: &Record, name: &str, span: Span) -> Option