pub mod keymap;
pub mod standard;
pub mod vim;
use ratatui::crossterm::event::KeyEvent;
use crate::config::Config;
use crate::edit_op::EditOp;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EditingMode {
None,
Normal,
Insert,
Replace,
Visual,
VisualLine,
VisualBlock,
}
impl EditingMode {
pub fn label(self) -> Option<&'static str> {
match self {
EditingMode::None => None,
EditingMode::Normal => Some("NORMAL"),
EditingMode::Insert => Some("INSERT"),
EditingMode::Replace => Some("REPLACE"),
EditingMode::Visual => Some("VISUAL"),
EditingMode::VisualLine => Some("V-LINE"),
EditingMode::VisualBlock => Some("V-BLOCK"),
}
}
pub fn tooltip_label(self) -> &'static str {
match self {
EditingMode::Insert => "green = INSERT",
EditingMode::Replace => "orange = REPLACE",
EditingMode::Visual => "purple = VISUAL",
EditingMode::VisualLine => "purple = V-LINE",
EditingMode::VisualBlock => "purple = V-BLOCK",
EditingMode::Normal => "red = NORMAL",
EditingMode::None => "green = EDIT (cyan = read-only)",
}
}
pub fn is_visual(self) -> bool {
matches!(
self,
EditingMode::Visual | EditingMode::VisualLine | EditingMode::VisualBlock
)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AppCommand {
Save,
ExCommand(String),
RunCommand(String),
DotRepeat(u32),
SetMark(char),
JumpToMarkLine(char),
JumpToMarkExact(char),
MacroRecordInto(char),
MacroReplayFrom {
reg: char,
count: u32,
},
BlockInsertStart {
append: bool,
},
BlockChangeStart,
BlockReplaceWith {
ch: char,
},
FilterLinesFromCursor {
count: u32,
},
FilterParagraphFromCursor {
around: bool,
},
RepeatInsertStart {
count: u32,
above: bool,
},
OperatorLinewiseTo {
op: char,
target: Option<u32>,
},
CmdlineTabComplete,
CmdlinePopupMove(i8),
CmdlineEnter(String),
CmdlineInsertCursorWord(bool),
CmdlinePasteFromClipboard,
FlashStart(char, char),
}
pub enum InputResult {
Ops(Vec<EditOp>),
Consumed,
Ignored,
App(AppCommand),
}
#[derive(Debug, Clone, Copy)]
pub struct EditCtx {
pub cursor: usize,
pub line_len: usize,
pub line_idx: usize,
pub line_count: usize,
pub at_line_start: bool,
pub at_line_end: bool,
pub has_selection: bool,
pub line_first_nonws_col: usize,
pub cursor_col: usize,
pub next_find_match: Option<(usize, usize)>,
pub prev_find_match: Option<(usize, usize)>,
pub wrap_width: Option<usize>,
}
pub trait InputHandler: Send {
fn handle_key(&mut self, key: KeyEvent, ctx: &EditCtx) -> InputResult;
fn mode(&self) -> EditingMode;
fn pending_display(&self) -> Option<String> {
None
}
fn is_cmdline_open(&self) -> bool {
false
}
fn is_op_pending(&self) -> bool {
false
}
fn name(&self) -> &'static str;
fn on_blur(&mut self) {}
fn set_ex_history(&mut self, _entries: Vec<String>) {}
fn ex_history(&self) -> Vec<String> {
Vec::new()
}
fn operator_menu_hint(&self) -> Option<(String, Vec<(char, &'static str, bool)>)> {
None
}
fn request_insert_mode(&mut self) {}
fn request_visual_mode(&mut self) {}
fn cmdline_get(&self) -> Option<String> {
None
}
fn cmdline_set(&mut self, _text: Option<String>) {}
fn cmdline_caret(&self) -> Option<usize> {
None
}
fn set_cmdline_caret(&mut self, _byte_offset: usize) {}
}
pub fn make_handler_for(style: &str, cfg: &Config) -> Box<dyn InputHandler> {
match style {
"vim" => Box::new(vim::VimInputHandler::new(cfg)),
_ => Box::new(standard::StandardInputHandler::new(cfg)),
}
}
pub fn make_handler(cfg: &Config) -> Box<dyn InputHandler> {
make_handler_for(&cfg.editor.input_style, cfg)
}
pub fn is_vim_style(cfg: &Config) -> bool {
cfg.editor.input_style == "vim"
}