use crate::editor::mode::{
ConditionalEditMode, ConditionalRule, CursorAtBegin, EditorStateRef, create_auto_match_rules,
create_bracket_delete_rules, create_skip_over_rules,
};
use crokey::KeyCombination;
use reedline::{EditCommand, EditMode, KeyCode, KeyModifiers, Keybindings, ReedlineEvent};
use std::collections::BTreeMap;
pub fn add_common_keybindings(keybindings: &mut Keybindings) {
keybindings.add_binding(
KeyModifiers::NONE,
KeyCode::Tab,
ReedlineEvent::UntilFound(vec![
ReedlineEvent::Menu("completion_menu".to_string()),
ReedlineEvent::MenuNext,
]),
);
keybindings.add_binding(
KeyModifiers::NONE,
KeyCode::Char(':'),
ReedlineEvent::Multiple(vec![
ReedlineEvent::Edit(vec![EditCommand::InsertChar(':')]),
ReedlineEvent::Menu("completion_menu".to_string()),
]),
);
keybindings.add_binding(
KeyModifiers::CONTROL,
KeyCode::Char('r'),
ReedlineEvent::Multiple(vec![
ReedlineEvent::Edit(vec![EditCommand::MoveToEnd { select: false }]),
ReedlineEvent::UntilFound(vec![
ReedlineEvent::Menu("history_menu".to_string()),
ReedlineEvent::MenuPageNext,
]),
]),
);
keybindings.add_binding(
KeyModifiers::SHIFT,
KeyCode::Enter,
ReedlineEvent::Edit(vec![EditCommand::InsertNewline]),
);
}
pub fn wrap_edit_mode_with_conditional_rules<E: EditMode + 'static>(
edit_mode: E,
state: EditorStateRef,
auto_match: bool,
completion_min_chars: Option<usize>,
) -> Box<dyn EditMode> {
let colon_rule = ConditionalRule {
match_event: Box::new(|event| {
matches!(
event,
ReedlineEvent::Multiple(events)
if events.len() == 2
&& matches!(&events[0], ReedlineEvent::Edit(cmds) if cmds.len() == 1 && matches!(&cmds[0], EditCommand::InsertChar(':')))
&& matches!(&events[1], ReedlineEvent::Menu(name) if name == "completion_menu")
)
}),
condition: Box::new(CursorAtBegin),
fallback_event: ReedlineEvent::Edit(vec![EditCommand::InsertChar(':')]),
};
let mut conditional = ConditionalEditMode::new(edit_mode, state)
.with_rule(colon_rule)
.with_completion_min_chars(completion_min_chars)
.with_tree_sitter_word_nav(true);
if auto_match {
conditional = conditional.with_rules(create_bracket_delete_rules());
conditional = conditional.with_rules(create_skip_over_rules());
conditional = conditional.with_rules(create_auto_match_rules());
}
Box::new(conditional)
}
pub fn add_auto_match_keybindings(keybindings: &mut Keybindings) {
let pairs = [
('(', ')', "()"),
('[', ']', "[]"),
('{', '}', "{}"),
('"', '"', r#""""#),
('\'', '\'', "''"),
('`', '`', "``"),
];
for (open_char, close_char, pair) in pairs {
let open_event = ReedlineEvent::Edit(vec![
EditCommand::InsertString(pair.to_string()),
EditCommand::MoveLeft { select: false },
]);
keybindings.add_binding(
KeyModifiers::NONE,
KeyCode::Char(open_char),
open_event.clone(),
);
keybindings.add_binding(KeyModifiers::SHIFT, KeyCode::Char(open_char), open_event);
if open_char != close_char {
let close_event = ReedlineEvent::Edit(vec![EditCommand::InsertChar(close_char)]);
keybindings.add_binding(
KeyModifiers::NONE,
KeyCode::Char(close_char),
close_event.clone(),
);
keybindings.add_binding(KeyModifiers::SHIFT, KeyCode::Char(close_char), close_event);
}
}
}
pub fn add_key_map_keybindings(
keybindings: &mut Keybindings,
key_map: &BTreeMap<KeyCombination, String>,
) {
use crossterm::event::KeyEvent;
for (key_combination, text) in key_map {
let key_event: KeyEvent = (*key_combination).into();
keybindings.add_binding(
key_event.modifiers,
key_event.code,
ReedlineEvent::Edit(vec![EditCommand::InsertString(text.clone())]),
);
}
}