use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DiffSide {
Left,
Right,
}
pub type SearchOrigin = usize;
#[derive(Debug, Clone)]
pub enum SearchMatch {
ListEntry(usize),
DiffLine {
row: usize,
col_start: usize,
col_end: usize,
side: DiffSide,
},
}
#[derive(Debug, Clone)]
pub struct SearchState {
pub active: bool,
pub input: String,
pub query: Option<String>,
pub origin: SearchOrigin,
pub matches: Vec<SearchMatch>,
pub current_match_idx: Option<usize>,
pub last_query: Option<String>,
pub history: Vec<String>,
pub(crate) history_idx: Option<usize>,
saved_input: String,
}
impl SearchState {
pub fn new() -> Self {
Self {
active: false,
input: String::new(),
query: None,
origin: 0,
matches: Vec::new(),
current_match_idx: None,
last_query: None,
history: Vec::new(),
history_idx: None,
saved_input: String::new(),
}
}
pub fn start(&mut self, origin: SearchOrigin) {
self.active = true;
self.input.clear();
self.query = None;
self.origin = origin;
self.matches.clear();
self.current_match_idx = None;
self.history_idx = None;
self.saved_input.clear();
}
pub fn reset_matches(&mut self) {
self.matches.clear();
self.current_match_idx = None;
}
pub fn clear(&mut self) {
self.active = false;
self.input.clear();
if self.query.is_some() {
self.last_query = self.query.take();
}
self.matches.clear();
self.current_match_idx = None;
}
pub fn handle_input_key(&mut self, key: KeyEvent) -> bool {
match key.code {
KeyCode::Enter => {
let query = self.input.clone();
if query.is_empty() {
self.active = false;
return false;
}
self.push_history(&query);
self.active = false;
self.query = Some(query);
true
}
KeyCode::Esc => {
self.active = false;
self.input.clear();
false
}
KeyCode::Backspace => {
self.input.pop();
self.history_idx = None;
false
}
KeyCode::Up | KeyCode::Char('p')
if key.code == KeyCode::Up || key.modifiers.contains(KeyModifiers::CONTROL) =>
{
self.history_prev();
false
}
KeyCode::Down | KeyCode::Char('n')
if key.code == KeyCode::Down || key.modifiers.contains(KeyModifiers::CONTROL) =>
{
self.history_next();
false
}
KeyCode::Char(c) => {
self.input.push(c);
self.history_idx = None;
false
}
_ => false,
}
}
pub fn history_prev(&mut self) {
if self.history.is_empty() {
return;
}
match self.history_idx {
None => {
self.saved_input = self.input.clone();
let idx = self.history.len() - 1;
self.history_idx = Some(idx);
self.input = self.history[idx].clone();
}
Some(idx) if idx > 0 => {
let new_idx = idx - 1;
self.history_idx = Some(new_idx);
self.input = self.history[new_idx].clone();
}
_ => {}
}
}
pub fn history_next(&mut self) {
if let Some(idx) = self.history_idx {
if idx + 1 < self.history.len() {
let new_idx = idx + 1;
self.history_idx = Some(new_idx);
self.input = self.history[new_idx].clone();
} else {
self.history_idx = None;
self.input = self.saved_input.clone();
}
}
}
pub fn push_history(&mut self, query: &str) {
if query.is_empty() {
return;
}
if self.history.last().map(|s| s.as_str()) != Some(query) {
self.history.push(query.to_string());
}
}
}