use crate::app::AppState;
use crate::app::InputMode;
use ratatui::style::{Modifier, Style};
impl AppState<'_> {
pub fn start_search_forward(&mut self) {
self.input_mode = InputMode::SearchForward;
self.input_buffer = String::new();
let mut text_area = tui_textarea::TextArea::default();
text_area.set_cursor_line_style(Style::default());
text_area.set_cursor_style(Style::default().add_modifier(Modifier::REVERSED));
self.text_area = text_area;
self.add_notification("Search forward mode".to_string());
self.highlight_enabled = true;
}
pub fn start_search_backward(&mut self) {
self.input_mode = InputMode::SearchBackward;
self.input_buffer = String::new();
let mut text_area = tui_textarea::TextArea::default();
text_area.set_cursor_line_style(Style::default());
text_area.set_cursor_style(Style::default().add_modifier(Modifier::REVERSED));
self.text_area = text_area;
self.add_notification("Search backward mode".to_string());
self.highlight_enabled = true;
}
pub fn execute_search(&mut self) {
let query = self.text_area.lines().join("\n");
self.input_buffer.clone_from(&query);
if query.is_empty() {
self.input_mode = InputMode::Normal;
return;
}
self.search_query.clone_from(&query);
match self.input_mode {
InputMode::SearchForward => self.search_direction = true,
InputMode::SearchBackward => self.search_direction = false,
_ => {}
}
self.search_results = self.find_all_matches(&query);
if self.search_results.is_empty() {
self.add_notification(format!("Pattern not found: {query}"));
self.current_search_idx = None;
} else {
self.jump_to_next_search_result();
self.add_notification(format!(
"{} matches found for: {}",
self.search_results.len(),
query
));
}
self.input_mode = InputMode::Normal;
self.input_buffer = String::new();
self.text_area = tui_textarea::TextArea::default();
}
pub fn find_all_matches(&self, query: &str) -> Vec<(usize, usize)> {
let sheet = self.workbook.get_current_sheet();
let query_lower = query.to_lowercase();
let mut results = Vec::with_capacity(32);
for row in 1..=sheet.max_rows {
for col in 1..=sheet.max_cols {
if row < sheet.data.len() && col < sheet.data[row].len() {
let cell_content = &sheet.data[row][col].value;
if cell_content.is_empty() {
continue;
}
if Self::case_insensitive_contains(cell_content, &query_lower) {
results.push((row, col));
}
}
}
}
results
}
fn case_insensitive_contains(haystack: &str, needle: &str) -> bool {
if needle.is_empty() {
return true;
}
if haystack.is_empty() {
return false;
}
haystack.to_lowercase().contains(needle)
}
pub fn jump_to_next_search_result(&mut self) {
if self.search_results.is_empty() {
return;
}
self.highlight_enabled = true;
let current_pos = self.selected_cell;
if self.search_direction {
let next_idx = self.search_results.iter().position(|&pos| {
pos.0 > current_pos.0 || (pos.0 == current_pos.0 && pos.1 > current_pos.1)
});
if let Some(idx) = next_idx {
self.current_search_idx = Some(idx);
self.selected_cell = self.search_results[idx];
} else {
self.current_search_idx = Some(0);
self.selected_cell = self.search_results[0];
self.add_notification("Search wrapped to top".to_string());
}
} else {
let prev_idx = self.search_results.iter().rposition(|&pos| {
pos.0 < current_pos.0 || (pos.0 == current_pos.0 && pos.1 < current_pos.1)
});
if let Some(idx) = prev_idx {
self.current_search_idx = Some(idx);
self.selected_cell = self.search_results[idx];
} else {
let last_idx = self.search_results.len() - 1;
self.current_search_idx = Some(last_idx);
self.selected_cell = self.search_results[last_idx];
self.add_notification("Search wrapped to bottom".to_string());
}
}
self.handle_scrolling();
}
pub fn jump_to_prev_search_result(&mut self) {
if self.search_results.is_empty() {
return;
}
self.search_direction = !self.search_direction;
self.jump_to_next_search_result();
self.search_direction = !self.search_direction;
}
pub fn disable_search_highlight(&mut self) {
self.highlight_enabled = false;
self.add_notification("Search highlighting disabled".to_string());
}
}