use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers, MouseEvent, MouseEventKind};
#[cfg(feature = "search")]
use crate::minus_core::search::SearchMode;
use crate::{LineNumbers, PagerState};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[allow(clippy::module_name_repetitions)]
pub enum InputEvent {
Exit,
UpdateTermArea(usize, usize),
UpdateUpperMark(usize),
UpdateLineNumber(LineNumbers),
Number(char),
RestorePrompt,
#[cfg(feature = "search")]
Search(SearchMode),
#[cfg(feature = "search")]
NextMatch,
#[cfg(feature = "search")]
PrevMatch,
#[cfg(feature = "search")]
MoveToNextMatch(usize),
#[cfg(feature = "search")]
MoveToPrevMatch(usize),
}
#[cfg_attr(feature = "search", doc = "use minus::SearchMode;")]
#[allow(clippy::module_name_repetitions)]
pub trait InputClassifier {
fn classify_input(&self, ev: Event, ps: &PagerState) -> Option<InputEvent>;
}
pub struct DefaultInputClassifier;
impl InputClassifier for DefaultInputClassifier {
#[allow(clippy::too_many_lines)]
fn classify_input(&self, ev: Event, ps: &PagerState) -> Option<InputEvent> {
#[allow(clippy::unnested_or_patterns)]
match ev {
Event::Key(KeyEvent {
code,
modifiers: KeyModifiers::NONE,
}) if code == KeyCode::Up || code == KeyCode::Char('k') => {
let position = ps.prefix_num.parse::<usize>().unwrap_or(1);
Some(InputEvent::UpdateUpperMark(
ps.upper_mark.saturating_sub(position),
))
}
Event::Key(KeyEvent {
code,
modifiers: KeyModifiers::NONE,
}) if code == KeyCode::Down || code == KeyCode::Char('j') => {
let position = ps.prefix_num.parse::<usize>().unwrap_or(1);
Some(InputEvent::UpdateUpperMark(
ps.upper_mark.saturating_add(position),
))
}
Event::Key(KeyEvent {
code: KeyCode::Char(c),
modifiers: KeyModifiers::NONE,
}) if c.is_ascii_digit() => Some(InputEvent::Number(c)),
Event::Key(KeyEvent {
code: KeyCode::Enter,
modifiers: KeyModifiers::NONE,
}) => {
if ps.message.is_some() {
Some(InputEvent::RestorePrompt)
} else {
let position = ps.prefix_num.parse::<usize>().unwrap_or(1);
Some(InputEvent::UpdateUpperMark(
ps.upper_mark.saturating_add(position),
))
}
}
Event::Key(KeyEvent {
code: KeyCode::Char('u'),
modifiers,
}) if modifiers == KeyModifiers::CONTROL || modifiers == KeyModifiers::NONE => {
let half_screen = (ps.rows / 2) as usize;
Some(InputEvent::UpdateUpperMark(
ps.upper_mark.saturating_sub(half_screen),
))
}
Event::Key(KeyEvent {
code: KeyCode::Char('d'),
modifiers,
}) if modifiers == KeyModifiers::CONTROL || modifiers == KeyModifiers::NONE => {
let half_screen = (ps.rows / 2) as usize;
Some(InputEvent::UpdateUpperMark(
ps.upper_mark.saturating_add(half_screen),
))
}
Event::Mouse(MouseEvent {
kind: MouseEventKind::ScrollUp,
..
}) => Some(InputEvent::UpdateUpperMark(ps.upper_mark.saturating_sub(5))),
Event::Mouse(MouseEvent {
kind: MouseEventKind::ScrollDown,
..
}) => Some(InputEvent::UpdateUpperMark(ps.upper_mark.saturating_add(5))),
Event::Key(KeyEvent {
code: KeyCode::Char('g'),
modifiers: KeyModifiers::NONE,
}) => Some(InputEvent::UpdateUpperMark(0)),
Event::Key(KeyEvent {
code: KeyCode::Char('g'),
modifiers: KeyModifiers::SHIFT,
})
| Event::Key(KeyEvent {
code: KeyCode::Char('G'),
modifiers: KeyModifiers::SHIFT,
})
| Event::Key(KeyEvent {
code: KeyCode::Char('G'),
modifiers: KeyModifiers::NONE,
}) => {
let mut position = ps
.prefix_num
.parse::<usize>()
.unwrap_or(usize::MAX)
.saturating_sub(1);
if position == 0 {
position = usize::MAX;
}
Some(InputEvent::UpdateUpperMark(position))
}
Event::Key(KeyEvent {
code: KeyCode::PageUp,
modifiers: KeyModifiers::NONE,
}) => Some(InputEvent::UpdateUpperMark(
ps.upper_mark.saturating_sub(ps.rows - 1),
)),
Event::Key(KeyEvent {
code: c,
modifiers: KeyModifiers::NONE,
}) if c == KeyCode::PageDown || c == KeyCode::Char(' ') => Some(
InputEvent::UpdateUpperMark(ps.upper_mark.saturating_add(ps.rows - 1)),
),
Event::Resize(cols, rows) => {
Some(InputEvent::UpdateTermArea(cols as usize, rows as usize))
}
Event::Key(KeyEvent {
code: KeyCode::Char('l'),
modifiers: KeyModifiers::CONTROL,
}) => Some(InputEvent::UpdateLineNumber(!ps.line_numbers)),
Event::Key(KeyEvent {
code: KeyCode::Char('q'),
modifiers: KeyModifiers::NONE,
})
| Event::Key(KeyEvent {
code: KeyCode::Char('c'),
modifiers: KeyModifiers::CONTROL,
}) => Some(InputEvent::Exit),
#[cfg(feature = "search")]
Event::Key(KeyEvent {
code: KeyCode::Char('/'),
modifiers: KeyModifiers::NONE,
}) => Some(InputEvent::Search(SearchMode::Forward)),
#[cfg(feature = "search")]
Event::Key(KeyEvent {
code: KeyCode::Char('?'),
modifiers: KeyModifiers::NONE,
}) => Some(InputEvent::Search(SearchMode::Reverse)),
#[cfg(feature = "search")]
Event::Key(KeyEvent {
code: KeyCode::Char('n'),
modifiers: KeyModifiers::NONE,
}) => {
let position = ps.prefix_num.parse::<usize>().unwrap_or(1);
if ps.search_mode == SearchMode::Reverse {
Some(InputEvent::MoveToPrevMatch(position))
} else {
Some(InputEvent::MoveToNextMatch(position))
}
}
#[cfg(feature = "search")]
Event::Key(KeyEvent {
code: KeyCode::Char('p'),
modifiers: KeyModifiers::NONE,
}) => {
let position = ps.prefix_num.parse::<usize>().unwrap_or(1);
if ps.search_mode == SearchMode::Reverse {
Some(InputEvent::MoveToNextMatch(position))
} else {
Some(InputEvent::MoveToPrevMatch(position))
}
}
_ => None,
}
}
}
#[cfg(test)]
mod tests;