2022-01-18 08:48:28 +00:00
|
|
|
use crossterm::event::{KeyCode, KeyModifiers};
|
|
|
|
use nu_color_config::lookup_ansi_color_style;
|
|
|
|
use nu_protocol::{Config, EventType, ParsedKeybinding, ShellError};
|
|
|
|
use reedline::{
|
2022-01-18 19:32:45 +00:00
|
|
|
default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings,
|
|
|
|
ContextMenuInput, EditCommand, Keybindings, ReedlineEvent,
|
2022-01-18 08:48:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// This creates an input object for the context menu based on the dictionary
|
|
|
|
// stored in the config variable
|
|
|
|
pub(crate) fn create_menu_input(config: &Config) -> ContextMenuInput {
|
|
|
|
let mut input = ContextMenuInput::default();
|
|
|
|
|
|
|
|
input = match config
|
|
|
|
.menu_config
|
|
|
|
.get("columns")
|
|
|
|
.and_then(|value| value.as_integer().ok())
|
|
|
|
{
|
|
|
|
Some(value) => input.with_columns(value as u16),
|
|
|
|
None => input,
|
|
|
|
};
|
|
|
|
|
|
|
|
input = input.with_col_width(
|
|
|
|
config
|
|
|
|
.menu_config
|
|
|
|
.get("col_width")
|
|
|
|
.and_then(|value| value.as_integer().ok())
|
|
|
|
.map(|value| value as usize),
|
|
|
|
);
|
|
|
|
|
|
|
|
input = match config
|
|
|
|
.menu_config
|
|
|
|
.get("col_padding")
|
|
|
|
.and_then(|value| value.as_integer().ok())
|
|
|
|
{
|
|
|
|
Some(value) => input.with_col_padding(value as usize),
|
|
|
|
None => input,
|
|
|
|
};
|
|
|
|
|
|
|
|
input = match config
|
|
|
|
.menu_config
|
|
|
|
.get("text_style")
|
|
|
|
.and_then(|value| value.as_string().ok())
|
|
|
|
{
|
|
|
|
Some(value) => input.with_text_style(lookup_ansi_color_style(&value)),
|
|
|
|
None => input,
|
|
|
|
};
|
|
|
|
|
|
|
|
input = match config
|
|
|
|
.menu_config
|
|
|
|
.get("selected_text_style")
|
|
|
|
.and_then(|value| value.as_string().ok())
|
|
|
|
{
|
|
|
|
Some(value) => input.with_selected_text_style(lookup_ansi_color_style(&value)),
|
|
|
|
None => input,
|
|
|
|
};
|
|
|
|
|
|
|
|
input
|
|
|
|
}
|
|
|
|
|
2022-01-18 19:32:45 +00:00
|
|
|
pub enum KeybindingsMode {
|
|
|
|
Emacs(Keybindings),
|
|
|
|
Vi {
|
|
|
|
insert_keybindings: Keybindings,
|
|
|
|
normal_keybindings: Keybindings,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn create_keybindings(config: &Config) -> Result<KeybindingsMode, ShellError> {
|
|
|
|
let parsed_keybindings = &config.keybindings;
|
|
|
|
match config.edit_mode.as_str() {
|
|
|
|
"emacs" => {
|
|
|
|
let mut keybindings = default_emacs_keybindings();
|
|
|
|
// temporal keybinding with multiple events
|
|
|
|
keybindings.add_binding(
|
|
|
|
KeyModifiers::SHIFT,
|
|
|
|
KeyCode::BackTab,
|
|
|
|
ReedlineEvent::Multiple(vec![
|
|
|
|
ReedlineEvent::Edit(vec![EditCommand::InsertChar('p')]),
|
|
|
|
ReedlineEvent::Enter,
|
|
|
|
]),
|
|
|
|
);
|
|
|
|
|
|
|
|
for parsed_keybinding in parsed_keybindings {
|
|
|
|
if parsed_keybinding.mode.item.as_str() == "emacs" {
|
|
|
|
add_keybinding(&mut keybindings, parsed_keybinding)?
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(KeybindingsMode::Emacs(keybindings))
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
let mut insert_keybindings = default_vi_insert_keybindings();
|
|
|
|
let mut normal_keybindings = default_vi_normal_keybindings();
|
2022-01-18 08:48:28 +00:00
|
|
|
|
2022-01-18 19:32:45 +00:00
|
|
|
for parsed_keybinding in parsed_keybindings {
|
|
|
|
if parsed_keybinding.mode.item.as_str() == "vi_insert" {
|
|
|
|
add_keybinding(&mut insert_keybindings, parsed_keybinding)?
|
|
|
|
} else if parsed_keybinding.mode.item.as_str() == "vi_normal" {
|
|
|
|
add_keybinding(&mut normal_keybindings, parsed_keybinding)?
|
|
|
|
}
|
2022-01-18 08:48:28 +00:00
|
|
|
}
|
2022-01-18 19:32:45 +00:00
|
|
|
|
|
|
|
Ok(KeybindingsMode::Vi {
|
|
|
|
insert_keybindings,
|
|
|
|
normal_keybindings,
|
|
|
|
})
|
|
|
|
}
|
2022-01-18 08:48:28 +00:00
|
|
|
}
|
2022-01-18 19:32:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn add_keybinding(
|
|
|
|
keybindings: &mut Keybindings,
|
|
|
|
parsed_keybinding: &ParsedKeybinding,
|
|
|
|
) -> Result<(), ShellError> {
|
|
|
|
let modifier = match parsed_keybinding.modifier.item.as_str() {
|
|
|
|
"CONTROL" => KeyModifiers::CONTROL,
|
|
|
|
"SHIFT" => KeyModifiers::SHIFT,
|
|
|
|
"ALT" => KeyModifiers::ALT,
|
|
|
|
"NONE" => KeyModifiers::NONE,
|
|
|
|
"CONTROL | ALT" => KeyModifiers::CONTROL | KeyModifiers::ALT,
|
|
|
|
_ => {
|
|
|
|
return Err(ShellError::UnsupportedConfigValue(
|
|
|
|
"CONTROL, SHIFT, ALT or NONE".to_string(),
|
|
|
|
parsed_keybinding.modifier.item.clone(),
|
|
|
|
parsed_keybinding.modifier.span,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let keycode = match parsed_keybinding.keycode.item.as_str() {
|
|
|
|
c if c.starts_with("Char_") => {
|
|
|
|
let char = c.replace("Char_", "");
|
|
|
|
let char = char.chars().next().expect("correct");
|
|
|
|
KeyCode::Char(char)
|
|
|
|
}
|
|
|
|
"down" => KeyCode::Down,
|
|
|
|
"up" => KeyCode::Up,
|
|
|
|
"left" => KeyCode::Left,
|
|
|
|
"right" => KeyCode::Right,
|
|
|
|
"Tab" => KeyCode::Tab,
|
|
|
|
"BackTab" => KeyCode::BackTab,
|
|
|
|
_ => {
|
|
|
|
return Err(ShellError::UnsupportedConfigValue(
|
|
|
|
"crossterm KeyCode".to_string(),
|
|
|
|
parsed_keybinding.keycode.item.clone(),
|
|
|
|
parsed_keybinding.keycode.span,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let event = match &parsed_keybinding.event.item {
|
|
|
|
EventType::Single(name) => match name.as_str() {
|
|
|
|
"ActionHandler" => ReedlineEvent::ActionHandler,
|
|
|
|
"Complete" => ReedlineEvent::Complete,
|
|
|
|
"ContextMenu" => ReedlineEvent::ContextMenu,
|
|
|
|
"NextElement" => ReedlineEvent::NextElement,
|
|
|
|
"NextHistory" => ReedlineEvent::NextHistory,
|
|
|
|
"PreviousElement" => ReedlineEvent::PreviousElement,
|
|
|
|
"PreviousHistory" => ReedlineEvent::PreviousHistory,
|
|
|
|
_ => {
|
|
|
|
return Err(ShellError::UnsupportedConfigValue(
|
|
|
|
"crossterm EventType".to_string(),
|
|
|
|
name.clone(),
|
|
|
|
parsed_keybinding.event.span,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
keybindings.add_binding(modifier, keycode, event);
|
2022-01-18 08:48:28 +00:00
|
|
|
|
2022-01-18 19:32:45 +00:00
|
|
|
Ok(())
|
2022-01-18 08:48:28 +00:00
|
|
|
}
|