1use crate::app::AppState;
2use crate::app::InputMode;
3
4impl AppState<'_> {
5 pub fn start_search_forward(&mut self) {
6 self.input_mode = InputMode::SearchForward;
7 self.input_buffer = String::new();
8 self.text_area = ratatui_textarea::TextArea::default();
9 self.add_notification("Search forward mode".to_string());
10 self.highlight_enabled = true;
11 }
12
13 pub fn start_search_backward(&mut self) {
14 self.input_mode = InputMode::SearchBackward;
15 self.input_buffer = String::new();
16 self.text_area = ratatui_textarea::TextArea::default();
17 self.add_notification("Search backward mode".to_string());
18 self.highlight_enabled = true;
19 }
20
21 pub fn execute_search(&mut self) {
22 let query = self.text_area.lines().join("\n");
23 self.input_buffer = query.clone();
24
25 if query.is_empty() {
26 self.input_mode = InputMode::Normal;
27 return;
28 }
29
30 self.search_query = query.clone();
32
33 match self.input_mode {
35 InputMode::SearchForward => self.search_direction = true,
36 InputMode::SearchBackward => self.search_direction = false,
37 _ => {}
38 }
39
40 self.search_results = self.find_all_matches(&query);
41
42 if self.search_results.is_empty() {
43 self.add_notification(format!("Pattern not found: {}", query));
44 self.current_search_idx = None;
45 } else {
46 self.jump_to_next_search_result();
48 self.add_notification(format!(
49 "{} matches found for: {}",
50 self.search_results.len(),
51 query
52 ));
53 }
54
55 self.input_mode = InputMode::Normal;
56 self.input_buffer = String::new();
57 self.text_area = ratatui_textarea::TextArea::default();
58 }
59
60 pub fn find_all_matches(&self, query: &str) -> Vec<(usize, usize)> {
61 let sheet = self.workbook.get_current_sheet();
62 let query_lower = query.to_lowercase();
63
64 let mut results = Vec::with_capacity(32);
66
67 for row in 1..=sheet.max_rows {
69 for col in 1..=sheet.max_cols {
70 if row < sheet.data.len() && col < sheet.data[row].len() {
71 let cell_content = &sheet.data[row][col].value;
72
73 if cell_content.is_empty() {
74 continue;
75 }
76
77 if self.case_insensitive_contains(cell_content, &query_lower) {
78 results.push((row, col));
79 }
80 }
81 }
82 }
83
84 results
85 }
86
87 fn case_insensitive_contains(&self, haystack: &str, needle: &str) -> bool {
88 if needle.is_empty() {
89 return true;
90 }
91 if haystack.is_empty() {
92 return false;
93 }
94
95 haystack.to_lowercase().contains(needle)
96 }
97
98 pub fn jump_to_next_search_result(&mut self) {
99 if self.search_results.is_empty() {
100 return;
101 }
102
103 self.highlight_enabled = true;
104
105 let current_pos = self.selected_cell;
106
107 if self.search_direction {
108 let next_idx = self.search_results.iter().position(|&pos| {
110 pos.0 > current_pos.0 || (pos.0 == current_pos.0 && pos.1 > current_pos.1)
111 });
112
113 match next_idx {
114 Some(idx) => {
115 self.current_search_idx = Some(idx);
116 self.selected_cell = self.search_results[idx];
117 }
118 None => {
119 self.current_search_idx = Some(0);
121 self.selected_cell = self.search_results[0];
122 self.add_notification("Search wrapped to top".to_string());
123 }
124 }
125 } else {
126 let prev_idx = self.search_results.iter().rposition(|&pos| {
128 pos.0 < current_pos.0 || (pos.0 == current_pos.0 && pos.1 < current_pos.1)
129 });
130
131 match prev_idx {
132 Some(idx) => {
133 self.current_search_idx = Some(idx);
134 self.selected_cell = self.search_results[idx];
135 }
136 None => {
137 let last_idx = self.search_results.len() - 1;
139 self.current_search_idx = Some(last_idx);
140 self.selected_cell = self.search_results[last_idx];
141 self.add_notification("Search wrapped to bottom".to_string());
142 }
143 }
144 }
145
146 self.handle_scrolling();
147 }
148
149 pub fn jump_to_prev_search_result(&mut self) {
150 if self.search_results.is_empty() {
151 return;
152 }
153
154 self.search_direction = !self.search_direction;
156 self.jump_to_next_search_result();
157 self.search_direction = !self.search_direction;
159 }
160
161 pub fn disable_search_highlight(&mut self) {
162 self.highlight_enabled = false;
163 self.add_notification("Search highlighting disabled".to_string());
164 }
165}