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 VimSearchAdapter {
20 pub fn new() -> Self {
21 Self {
22 manager: VimSearchManager::new(),
23 is_active: false,
24 }
25 }
26
27 pub fn with_manager(manager: VimSearchManager) -> Self {
29 Self {
30 manager,
31 is_active: false,
32 }
33 }
34
35 pub fn should_handle_key(&self, state: &AppStateContainer) -> bool {
37 let should_handle = state.vim_search_should_handle_key();
39
40 debug!(
41 "VimSearchAdapter: should_handle_key? mode={:?}, pattern='{}', active={}, result={}",
42 state.get_mode(),
43 state.get_search_pattern(),
44 self.is_active,
45 should_handle
46 );
47
48 should_handle || self.is_active
49 }
50
51 pub fn should_handle_key_buffer(&self, buffer: &dyn BufferAPI) -> bool {
53 let in_search_mode = buffer.get_mode() == AppMode::Search;
55 let has_pattern = !buffer.get_search_pattern().is_empty();
56
57 debug!(
58 "VimSearchAdapter: should_handle_key_buffer? mode={:?}, pattern='{}', active={}",
59 buffer.get_mode(),
60 buffer.get_search_pattern(),
61 self.is_active
62 );
63
64 in_search_mode || (self.is_active && has_pattern)
66 }
67
68 pub fn clear(&mut self) {
70 info!("VimSearchAdapter: Clearing vim search");
71 self.manager.clear();
72 self.is_active = false;
73 }
74
75 pub fn manager(&self) -> &VimSearchManager {
77 &self.manager
78 }
79
80 pub fn manager_mut(&mut self) -> &mut VimSearchManager {
82 &mut self.manager
83 }
84
85 pub fn handle_key(&mut self, key: KeyCode, state: &mut AppStateContainer) -> bool {
88 let mode = state.get_mode();
89
90 if key == KeyCode::Esc {
92 match mode {
93 AppMode::Results if self.is_active || self.is_navigating() => {
94 info!("VimSearchAdapter: Escape detected in Results mode with active search - signaling for StateCoordinator");
97 return false;
100 }
101 AppMode::Search => {
102 info!("VimSearchAdapter: Exiting search mode to Results");
104 state.exit_vim_search();
105 self.clear();
106 return true;
107 }
108 _ => {
109 return false;
111 }
112 }
113 }
114
115 if !self.should_handle_key(state) {
117 debug!("VimSearchAdapter: Not handling key - search not active");
118 return false;
119 }
120
121 match key {
125 KeyCode::Char('n') | KeyCode::Char('N') => {
126 info!(
127 "VimSearchAdapter: Navigation key '{}' - letting TUI handle via action system",
128 if key == KeyCode::Char('n') { "n" } else { "N" }
129 );
130 false
132 }
133 KeyCode::Enter => {
134 info!("VimSearchAdapter: Confirming search - will need dataview and viewport from TUI");
135 true
137 }
138 _ => false,
139 }
140 }
141
142 pub fn handle_key_legacy(
144 &mut self,
145 key: KeyCode,
146 dataview: &DataView,
147 viewport: &mut ViewportManager,
148 buffer: &dyn BufferAPI,
149 ) -> bool {
150 if !self.should_handle_key_buffer(buffer) {
152 debug!("VimSearchAdapter: Not handling key - search not active");
153 return false;
154 }
155
156 match key {
158 KeyCode::Char('n') => {
159 info!("VimSearchAdapter: Delegating 'n' (next match) to VimSearchManager");
160 self.manager.next_match(viewport);
161 true
162 }
163 KeyCode::Char('N') => {
164 info!("VimSearchAdapter: Delegating 'N' (previous match) to VimSearchManager");
165 self.manager.previous_match(viewport);
166 true
167 }
168 KeyCode::Enter => {
169 info!("VimSearchAdapter: Delegating Enter (confirm search) to VimSearchManager");
170 self.manager.confirm_search(dataview, viewport);
171 true
172 }
173 KeyCode::Esc => {
174 info!("VimSearchAdapter: Search cancelled");
175 self.clear();
176 false }
178 _ => {
179 if self.manager.is_typing() {
181 if let KeyCode::Char(c) = key {
182 debug!("VimSearchAdapter: Character '{}' typed in search", c);
184 true
186 } else {
187 false
188 }
189 } else {
190 false
191 }
192 }
193 }
194 }
195
196 pub fn start_search(&mut self) {
198 info!("VimSearchAdapter: Starting new search");
199 self.is_active = true;
200 self.manager.start_search();
201 }
202
203 pub fn update_pattern(
205 &mut self,
206 pattern: String,
207 dataview: &DataView,
208 viewport: &mut ViewportManager,
209 ) {
210 debug!("VimSearchAdapter: Updating pattern to '{}'", pattern);
211 self.manager.update_pattern(pattern, dataview, viewport);
212 }
213
214 pub fn confirm_search(&mut self, dataview: &DataView, viewport: &mut ViewportManager) -> bool {
216 info!("VimSearchAdapter: Confirming search");
217 self.manager.confirm_search(dataview, viewport)
218 }
219
220 pub fn is_active(&self) -> bool {
222 self.is_active || self.manager.is_active()
223 }
224
225 pub fn is_navigating(&self) -> bool {
227 self.manager.is_navigating()
228 }
229
230 pub fn get_pattern(&self) -> Option<String> {
232 self.manager.get_pattern()
233 }
234
235 pub fn get_match_info(&self) -> Option<(usize, usize)> {
237 self.manager.get_match_info()
238 }
239
240 pub fn cancel_search(&mut self) {
242 info!("VimSearchAdapter: Cancelling search");
243 self.manager.cancel_search();
244 self.is_active = false;
245 }
246
247 pub fn exit_navigation(&mut self) {
249 info!("VimSearchAdapter: Exiting navigation");
250 self.manager.exit_navigation();
251 }
252
253 pub fn mark_search_complete(&mut self) {
256 info!("VimSearchAdapter: Marking search as complete, keeping matches for navigation");
257 }
261
262 pub fn next_match(
264 &mut self,
265 viewport: &mut ViewportManager,
266 ) -> Option<crate::ui::search::vim_search_manager::SearchMatch> {
267 self.manager.next_match(viewport)
268 }
269
270 pub fn previous_match(
272 &mut self,
273 viewport: &mut ViewportManager,
274 ) -> Option<crate::ui::search::vim_search_manager::SearchMatch> {
275 self.manager.previous_match(viewport)
276 }
277
278 pub fn set_search_state_from_external(
280 &mut self,
281 pattern: String,
282 matches: Vec<(usize, usize)>,
283 dataview: &DataView,
284 ) {
285 self.manager
286 .set_search_state_from_external(pattern, matches, dataview);
287 self.is_active = true; }
289
290 pub fn resume_last_search(
292 &mut self,
293 dataview: &DataView,
294 viewport: &mut ViewportManager,
295 ) -> bool {
296 let result = self.manager.resume_last_search(dataview, viewport);
297 if result {
298 self.is_active = true;
299 }
300 result
301 }
302
303 pub fn reset_to_first_match(
305 &mut self,
306 viewport: &mut ViewportManager,
307 ) -> Option<crate::ui::search::vim_search_manager::SearchMatch> {
308 self.manager.reset_to_first_match(viewport)
309 }
310}
311
312impl StateSubscriber for VimSearchAdapter {
313 fn on_state_event(&mut self, event: &StateEvent, buffer: &Buffer) {
314 match event {
315 StateEvent::SearchStarted { search_type } => {
316 if matches!(search_type, SearchType::Vim) {
317 info!("VimSearchAdapter: Activating for vim search");
318 self.is_active = true;
319 self.manager.start_search();
320 }
321 }
322
323 StateEvent::SearchEnded { search_type } => {
324 if matches!(search_type, SearchType::Vim) {
325 info!("VimSearchAdapter: Search ended, clearing");
326 self.clear();
327 }
328 }
329
330 StateEvent::ModeChanged { from: _, to } => {
331 if *to == AppMode::Results && buffer.search_state.pattern.is_empty() {
333 if self.is_active {
334 info!(
335 "VimSearchAdapter: Mode changed to Results with empty search, clearing"
336 );
337 self.clear();
338 }
339 }
340
341 if *to == AppMode::Search {
343 info!("VimSearchAdapter: Mode changed to Search, activating");
344 self.is_active = true;
345 if !self.manager.is_active() {
346 self.manager.start_search();
347 }
348 }
349 }
350
351 _ => {}
352 }
353 }
354
355 fn name(&self) -> &str {
356 "VimSearchAdapter"
357 }
358}