sql_cli/ui/search/
vim_search_adapter.rs1use crate::app_state_container::AppStateContainer;
4use crate::buffer::{AppMode, Buffer, BufferAPI};
5use crate::data::data_view::DataView;
6use crate::state::{StateEvent, StateSubscriber};
7use crate::ui::search::vim_search_manager::VimSearchManager;
8use crate::ui::state::shadow_state::SearchType;
9use crate::ui::viewport_manager::ViewportManager;
10use crossterm::event::KeyCode;
11use tracing::{debug, info};
12
13pub struct VimSearchAdapter {
15 manager: VimSearchManager,
16 is_active: bool,
17}
18
19impl Default for VimSearchAdapter {
20 fn default() -> Self {
21 Self::new()
22 }
23}
24
25impl VimSearchAdapter {
26 #[must_use]
27 pub fn new() -> Self {
28 Self {
29 manager: VimSearchManager::new(),
30 is_active: false,
31 }
32 }
33
34 #[must_use]
36 pub fn with_manager(manager: VimSearchManager) -> Self {
37 Self {
38 manager,
39 is_active: false,
40 }
41 }
42
43 pub fn should_handle_key(&self, state: &AppStateContainer) -> bool {
45 let should_handle = state.vim_search_should_handle_key();
47
48 debug!(
49 "VimSearchAdapter: should_handle_key? mode={:?}, pattern='{}', active={}, result={}",
50 state.get_mode(),
51 state.get_search_pattern(),
52 self.is_active,
53 should_handle
54 );
55
56 should_handle || self.is_active
57 }
58
59 pub fn should_handle_key_buffer(&self, buffer: &dyn BufferAPI) -> bool {
61 let in_search_mode = buffer.get_mode() == AppMode::Search;
63 let has_pattern = !buffer.get_search_pattern().is_empty();
64
65 debug!(
66 "VimSearchAdapter: should_handle_key_buffer? mode={:?}, pattern='{}', active={}",
67 buffer.get_mode(),
68 buffer.get_search_pattern(),
69 self.is_active
70 );
71
72 in_search_mode || (self.is_active && has_pattern)
74 }
75
76 pub fn clear(&mut self) {
78 info!("VimSearchAdapter: Clearing vim search");
79 self.manager.clear();
80 self.is_active = false;
81 }
82
83 #[must_use]
85 pub fn manager(&self) -> &VimSearchManager {
86 &self.manager
87 }
88
89 pub fn manager_mut(&mut self) -> &mut VimSearchManager {
91 &mut self.manager
92 }
93
94 pub fn handle_key(&mut self, key: KeyCode, state: &mut AppStateContainer) -> bool {
97 let mode = state.get_mode();
98
99 if key == KeyCode::Esc {
101 match mode {
102 AppMode::Results if self.is_active || self.is_navigating() => {
103 info!("VimSearchAdapter: Escape detected in Results mode with active search - signaling for StateCoordinator");
106 return false;
109 }
110 AppMode::Search => {
111 info!("VimSearchAdapter: Exiting search mode to Results");
113 state.exit_vim_search();
114 self.clear();
115 return true;
116 }
117 _ => {
118 return false;
120 }
121 }
122 }
123
124 if !self.should_handle_key(state) {
126 debug!("VimSearchAdapter: Not handling key - search not active");
127 return false;
128 }
129
130 match key {
134 KeyCode::Char('n' | 'N') => {
135 info!(
136 "VimSearchAdapter: Navigation key '{}' - letting TUI handle via action system",
137 if key == KeyCode::Char('n') { "n" } else { "N" }
138 );
139 false
141 }
142 KeyCode::Enter => {
143 info!("VimSearchAdapter: Confirming search - will need dataview and viewport from TUI");
144 true
146 }
147 _ => false,
148 }
149 }
150
151 pub fn handle_key_legacy(
153 &mut self,
154 key: KeyCode,
155 dataview: &DataView,
156 viewport: &mut ViewportManager,
157 buffer: &dyn BufferAPI,
158 ) -> bool {
159 if !self.should_handle_key_buffer(buffer) {
161 debug!("VimSearchAdapter: Not handling key - search not active");
162 return false;
163 }
164
165 match key {
167 KeyCode::Char('n') => {
168 info!("VimSearchAdapter: Delegating 'n' (next match) to VimSearchManager");
169 self.manager.next_match(viewport);
170 true
171 }
172 KeyCode::Char('N') => {
173 info!("VimSearchAdapter: Delegating 'N' (previous match) to VimSearchManager");
174 self.manager.previous_match(viewport);
175 true
176 }
177 KeyCode::Enter => {
178 info!("VimSearchAdapter: Delegating Enter (confirm search) to VimSearchManager");
179 self.manager.confirm_search(dataview, viewport);
180 true
181 }
182 KeyCode::Esc => {
183 info!("VimSearchAdapter: Search cancelled");
184 self.clear();
185 false }
187 _ => {
188 if self.manager.is_typing() {
190 if let KeyCode::Char(c) = key {
191 debug!("VimSearchAdapter: Character '{}' typed in search", c);
193 true
195 } else {
196 false
197 }
198 } else {
199 false
200 }
201 }
202 }
203 }
204
205 pub fn start_search(&mut self) {
207 info!("VimSearchAdapter: Starting new search");
208 self.is_active = true;
209 self.manager.start_search();
210 }
211
212 pub fn update_pattern(
214 &mut self,
215 pattern: String,
216 dataview: &DataView,
217 viewport: &mut ViewportManager,
218 ) {
219 debug!("VimSearchAdapter: Updating pattern to '{}'", pattern);
220 self.manager.update_pattern(pattern, dataview, viewport);
221 }
222
223 pub fn confirm_search(&mut self, dataview: &DataView, viewport: &mut ViewportManager) -> bool {
225 info!("VimSearchAdapter: Confirming search");
226 self.manager.confirm_search(dataview, viewport)
227 }
228
229 #[must_use]
231 pub fn is_active(&self) -> bool {
232 self.is_active || self.manager.is_active()
233 }
234
235 #[must_use]
237 pub fn is_navigating(&self) -> bool {
238 self.manager.is_navigating()
239 }
240
241 #[must_use]
243 pub fn get_pattern(&self) -> Option<String> {
244 self.manager.get_pattern()
245 }
246
247 #[must_use]
249 pub fn get_match_info(&self) -> Option<(usize, usize)> {
250 self.manager.get_match_info()
251 }
252
253 pub fn cancel_search(&mut self) {
255 info!("VimSearchAdapter: Cancelling search");
256 self.manager.cancel_search();
257 self.is_active = false;
258 }
259
260 pub fn exit_navigation(&mut self) {
262 info!("VimSearchAdapter: Exiting navigation");
263 self.manager.exit_navigation();
264 }
265
266 pub fn mark_search_complete(&mut self) {
269 info!("VimSearchAdapter: Marking search as complete, keeping matches for navigation");
270 }
274
275 pub fn next_match(
277 &mut self,
278 viewport: &mut ViewportManager,
279 ) -> Option<crate::ui::search::vim_search_manager::SearchMatch> {
280 self.manager.next_match(viewport)
281 }
282
283 pub fn previous_match(
285 &mut self,
286 viewport: &mut ViewportManager,
287 ) -> Option<crate::ui::search::vim_search_manager::SearchMatch> {
288 self.manager.previous_match(viewport)
289 }
290
291 pub fn set_search_state_from_external(
293 &mut self,
294 pattern: String,
295 matches: Vec<(usize, usize)>,
296 dataview: &DataView,
297 ) {
298 self.manager
299 .set_search_state_from_external(pattern, matches, dataview);
300 self.is_active = true; }
302
303 pub fn resume_last_search(
305 &mut self,
306 dataview: &DataView,
307 viewport: &mut ViewportManager,
308 ) -> bool {
309 let result = self.manager.resume_last_search(dataview, viewport);
310 if result {
311 self.is_active = true;
312 }
313 result
314 }
315
316 pub fn reset_to_first_match(
318 &mut self,
319 viewport: &mut ViewportManager,
320 ) -> Option<crate::ui::search::vim_search_manager::SearchMatch> {
321 self.manager.reset_to_first_match(viewport)
322 }
323}
324
325impl StateSubscriber for VimSearchAdapter {
326 fn on_state_event(&mut self, event: &StateEvent, buffer: &Buffer) {
327 match event {
328 StateEvent::SearchStarted { search_type } => {
329 if matches!(search_type, SearchType::Vim) {
330 info!("VimSearchAdapter: Activating for vim search");
331 self.is_active = true;
332 self.manager.start_search();
333 }
334 }
335
336 StateEvent::SearchEnded { search_type } => {
337 if matches!(search_type, SearchType::Vim) {
338 info!("VimSearchAdapter: Search ended, clearing");
339 self.clear();
340 }
341 }
342
343 StateEvent::ModeChanged { from: _, to } => {
344 if *to == AppMode::Results
346 && buffer.search_state.pattern.is_empty()
347 && self.is_active
348 {
349 info!("VimSearchAdapter: Mode changed to Results with empty search, clearing");
350 self.clear();
351 }
352
353 if *to == AppMode::Search {
355 info!("VimSearchAdapter: Mode changed to Search, activating");
356 self.is_active = true;
357 if !self.manager.is_active() {
358 self.manager.start_search();
359 }
360 }
361 }
362
363 _ => {}
364 }
365 }
366
367 fn name(&self) -> &'static str {
368 "VimSearchAdapter"
369 }
370}