1use std::cell::RefCell;
2use std::rc::Rc;
3use std::sync::Arc;
4
5use crate::app_state_container::AppStateContainer;
6use crate::buffer::{AppMode, Buffer, BufferAPI, BufferManager};
7use crate::config::config::Config;
8use crate::data::data_view::DataView;
9use crate::sql::hybrid_parser::HybridParser;
10use crate::ui::viewport_manager::ViewportManager;
11use crate::widgets::search_modes_widget::SearchMode;
12
13use tracing::{debug, error};
14
15pub struct StateCoordinator {
18 pub state_container: AppStateContainer,
20
21 pub shadow_state: Rc<RefCell<crate::ui::state::shadow_state::ShadowStateManager>>,
23
24 pub viewport_manager: Rc<RefCell<Option<ViewportManager>>>,
26
27 pub hybrid_parser: HybridParser,
29}
30
31impl StateCoordinator {
32 pub fn sync_mode_with_refs(
38 state_container: &mut AppStateContainer,
39 shadow_state: &RefCell<crate::ui::state::shadow_state::ShadowStateManager>,
40 mode: AppMode,
41 trigger: &str,
42 ) {
43 debug!(
44 "StateCoordinator::sync_mode_with_refs: Setting mode to {:?} with trigger '{}'",
45 mode, trigger
46 );
47
48 state_container.set_mode(mode.clone());
50
51 if let Some(buffer) = state_container.buffers_mut().current_mut() {
53 buffer.set_mode(mode.clone());
54 }
55
56 shadow_state.borrow_mut().observe_mode_change(mode, trigger);
58 }
59
60 pub fn update_parser_with_refs(state_container: &AppStateContainer, parser: &mut HybridParser) {
62 if let Some(dataview) = state_container.get_buffer_dataview() {
63 let table_name = dataview.source().name.clone();
64 let columns = dataview.source().column_names();
65
66 debug!(
67 "StateCoordinator: Updating parser with {} columns for table '{}'",
68 columns.len(),
69 table_name
70 );
71 parser.update_single_table(table_name, columns);
72 }
73 }
74
75 pub fn new(
78 state_container: AppStateContainer,
79 shadow_state: Rc<RefCell<crate::ui::state::shadow_state::ShadowStateManager>>,
80 viewport_manager: Rc<RefCell<Option<ViewportManager>>>,
81 hybrid_parser: HybridParser,
82 ) -> Self {
83 Self {
84 state_container,
85 shadow_state,
86 viewport_manager,
87 hybrid_parser,
88 }
89 }
90
91 pub fn sync_mode(&mut self, mode: AppMode, trigger: &str) {
96 debug!(
97 "StateCoordinator::sync_mode: Setting mode to {:?} with trigger '{}'",
98 mode, trigger
99 );
100
101 self.state_container.set_mode(mode.clone());
103
104 if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
106 buffer.set_mode(mode.clone());
107 }
108
109 self.shadow_state
111 .borrow_mut()
112 .observe_mode_change(mode, trigger);
113 }
114
115 pub fn set_mode_via_shadow_state(&mut self, mode: AppMode, trigger: &str) {
117 if let Some(buffer) = self.state_container.buffers_mut().current_mut() {
118 debug!(
119 "StateCoordinator::set_mode_via_shadow_state: Setting mode to {:?} with trigger '{}'",
120 mode, trigger
121 );
122 self.shadow_state
123 .borrow_mut()
124 .set_mode(mode, buffer, trigger);
125 } else {
126 error!(
127 "StateCoordinator::set_mode_via_shadow_state: No buffer available! Cannot set mode to {:?}",
128 mode
129 );
130 }
131 }
132
133 pub fn sync_after_buffer_switch(&mut self) {
138 self.update_parser_for_current_buffer();
141 }
142
143 pub fn update_parser_for_current_buffer(&mut self) {
145 if let Some(dataview) = self.state_container.get_buffer_dataview() {
147 let table_name = dataview.source().name.clone();
148 let columns = dataview.source().column_names();
149
150 debug!(
151 "StateCoordinator: Updating parser with {} columns for table '{}'",
152 columns.len(),
153 table_name
154 );
155 self.hybrid_parser.update_single_table(table_name, columns);
156 }
157 }
158
159 pub fn enter_search_mode(&mut self, mode: SearchMode) -> String {
163 debug!("StateCoordinator::enter_search_mode: {:?}", mode);
164
165 let trigger = match mode {
167 SearchMode::ColumnSearch => "backslash_column_search",
168 SearchMode::Search => "data_search_started",
169 SearchMode::FuzzyFilter => "fuzzy_filter_started",
170 SearchMode::Filter => "filter_started",
171 };
172
173 self.sync_mode(mode.to_app_mode(), trigger);
175
176 let search_type = match mode {
178 SearchMode::ColumnSearch => crate::ui::state::shadow_state::SearchType::Column,
179 SearchMode::Search => crate::ui::state::shadow_state::SearchType::Data,
180 SearchMode::FuzzyFilter | SearchMode::Filter => {
181 crate::ui::state::shadow_state::SearchType::Fuzzy
182 }
183 };
184 self.shadow_state
185 .borrow_mut()
186 .observe_search_start(search_type, trigger);
187
188 trigger.to_string()
189 }
190
191 pub fn cancel_search(&mut self) -> (Option<String>, Option<usize>) {
196 debug!("StateCoordinator::cancel_search: Canceling search and restoring state");
197
198 self.state_container.clear_search();
200
201 self.shadow_state
203 .borrow_mut()
204 .observe_search_end("search_cancelled");
205
206 self.sync_mode(AppMode::Results, "search_cancelled");
208
209 (None, None)
212 }
213
214 pub fn cancel_search_with_refs(
216 state_container: &mut AppStateContainer,
217 shadow_state: &RefCell<crate::ui::state::shadow_state::ShadowStateManager>,
218 vim_search_adapter: Option<
219 &RefCell<crate::ui::search::vim_search_adapter::VimSearchAdapter>,
220 >,
221 ) {
222 debug!("StateCoordinator::cancel_search_with_refs: Canceling search and clearing all search state");
223
224 if let Some(adapter) = vim_search_adapter {
226 debug!("Clearing vim search adapter state");
227 adapter.borrow_mut().clear();
228 }
229
230 state_container.set_search_pattern(String::new());
232 state_container.clear_search();
233
234 state_container.clear_column_search();
236
237 shadow_state
239 .borrow_mut()
240 .observe_search_end("search_cancelled");
241
242 Self::sync_mode_with_refs(
244 state_container,
245 shadow_state,
246 AppMode::Results,
247 "vim_search_cancelled",
248 );
249 }
250
251 pub fn should_handle_next_match(
254 state_container: &AppStateContainer,
255 vim_search_adapter: Option<
256 &RefCell<crate::ui::search::vim_search_adapter::VimSearchAdapter>,
257 >,
258 ) -> bool {
259 let has_search = !state_container.get_search_pattern().is_empty();
261 let pattern = state_container.get_search_pattern();
262
263 let vim_active = if let Some(adapter) = vim_search_adapter {
266 let adapter_ref = adapter.borrow();
267 adapter_ref.is_active() || adapter_ref.is_navigating()
268 } else {
269 false
270 };
271
272 debug!(
273 "StateCoordinator::should_handle_next_match: pattern='{}', vim_active={}, result={}",
274 pattern,
275 vim_active,
276 has_search && vim_active
277 );
278
279 has_search && vim_active
281 }
282
283 pub fn should_handle_previous_match(
286 state_container: &AppStateContainer,
287 vim_search_adapter: Option<
288 &RefCell<crate::ui::search::vim_search_adapter::VimSearchAdapter>,
289 >,
290 ) -> bool {
291 let has_search = !state_container.get_search_pattern().is_empty();
293 let pattern = state_container.get_search_pattern();
294
295 let vim_active = if let Some(adapter) = vim_search_adapter {
298 let adapter_ref = adapter.borrow();
299 adapter_ref.is_active() || adapter_ref.is_navigating()
300 } else {
301 false
302 };
303
304 debug!(
305 "StateCoordinator::should_handle_previous_match: pattern='{}', vim_active={}, result={}",
306 pattern, vim_active, has_search && vim_active
307 );
308
309 has_search && vim_active
311 }
312
313 pub fn complete_search_with_refs(
316 state_container: &mut AppStateContainer,
317 shadow_state: &RefCell<crate::ui::state::shadow_state::ShadowStateManager>,
318 vim_search_adapter: Option<
319 &RefCell<crate::ui::search::vim_search_adapter::VimSearchAdapter>,
320 >,
321 mode: AppMode,
322 trigger: &str,
323 ) {
324 debug!(
325 "StateCoordinator::complete_search_with_refs: Completing search, switching to {:?}",
326 mode
327 );
328
329 if let Some(adapter) = vim_search_adapter {
335 debug!("Marking vim search as complete but keeping matches");
336 adapter.borrow_mut().mark_search_complete();
337 }
338
339 shadow_state
341 .borrow_mut()
342 .observe_search_end("search_completed");
343
344 Self::sync_mode_with_refs(state_container, shadow_state, mode, trigger);
346 }
347
348 pub fn apply_text_filter_with_refs(
353 state_container: &mut AppStateContainer,
354 viewport_manager: &RefCell<Option<ViewportManager>>,
355 pattern: &str,
356 ) -> usize {
357 let case_insensitive = state_container.is_case_insensitive();
358
359 debug!(
360 "StateCoordinator::apply_text_filter_with_refs: Applying text filter with pattern '{}', case_sensitive: {}",
361 pattern, !case_insensitive
362 );
363
364 let rows_after = if let Some(dataview) = state_container.get_buffer_dataview_mut() {
366 let rows_before = dataview.row_count();
367 dataview.apply_text_filter(pattern, !case_insensitive);
368 let rows_after = dataview.row_count();
369 debug!(
370 "Text filter: {} rows before, {} rows after",
371 rows_before, rows_after
372 );
373 rows_after
374 } else {
375 debug!("No DataView available for text filtering");
376 0
377 };
378
379 if rows_after > 0 {
383 let col_offset = state_container.get_scroll_offset().1;
385
386 state_container.set_selected_row(Some(0));
387 state_container.set_scroll_offset((0, col_offset));
388 state_container.set_table_selected_row(Some(0));
389
390 {
391 let mut nav = state_container.navigation_mut();
392 nav.selected_row = 0;
393 nav.scroll_offset.0 = 0;
394 }
395
396 if let Ok(mut vm_borrow) = viewport_manager.try_borrow_mut() {
397 if let Some(ref mut vm) = *vm_borrow {
398 vm.set_crosshair_row(0);
399 vm.set_scroll_offset(0, col_offset);
400 debug!(
401 "StateCoordinator: Reset viewport to first match (row 0) with {} total matches",
402 rows_after
403 );
404 }
405 }
406 }
407
408 let status = if pattern.is_empty() {
410 "Filter cleared".to_string()
411 } else {
412 format!("Filter applied: '{pattern}' - {rows_after} matches")
413 };
414 state_container.set_status_message(status);
415
416 debug!(
417 "StateCoordinator: Text filter applied - {} matches for pattern '{}'",
418 rows_after, pattern
419 );
420
421 rows_after
422 }
423
424 pub fn apply_fuzzy_filter_with_refs(
427 state_container: &mut AppStateContainer,
428 viewport_manager: &RefCell<Option<ViewportManager>>,
429 ) -> (usize, Vec<usize>) {
430 let pattern = state_container.get_fuzzy_filter_pattern();
431 let case_insensitive = state_container.is_case_insensitive();
432
433 debug!(
434 "StateCoordinator::apply_fuzzy_filter_with_refs: Applying fuzzy filter with pattern '{}', case_insensitive: {}",
435 pattern, case_insensitive
436 );
437
438 let (match_count, indices) =
440 if let Some(dataview) = state_container.get_buffer_dataview_mut() {
441 dataview.apply_fuzzy_filter(&pattern, case_insensitive);
442 let match_count = dataview.row_count();
443 let indices = dataview.get_fuzzy_filter_indices();
444 (match_count, indices)
445 } else {
446 (0, Vec::new())
447 };
448
449 if pattern.is_empty() {
451 state_container.set_fuzzy_filter_active(false);
452 state_container.set_status_message("Fuzzy filter cleared".to_string());
453 } else {
454 state_container.set_fuzzy_filter_active(true);
455 state_container.set_status_message(format!("Fuzzy filter: {match_count} matches"));
456
457 if match_count > 0 {
459 let col_offset = state_container.get_scroll_offset().1;
461
462 state_container.set_selected_row(Some(0));
464 state_container.set_scroll_offset((0, col_offset));
465 state_container.set_table_selected_row(Some(0));
466
467 let mut nav = state_container.navigation_mut();
469 nav.selected_row = 0;
470 nav.scroll_offset.0 = 0;
471
472 if let Ok(mut vm_borrow) = viewport_manager.try_borrow_mut() {
474 if let Some(ref mut vm) = *vm_borrow {
475 vm.set_crosshair_row(0);
476 vm.set_scroll_offset(0, col_offset);
477 debug!(
478 "StateCoordinator: Reset viewport to first match (row 0) with {} total matches",
479 match_count
480 );
481 }
482 }
483 }
484 }
485
486 debug!(
487 "StateCoordinator: Fuzzy filter applied - {} matches, pattern: '{}'",
488 match_count, pattern
489 );
490
491 (match_count, indices)
492 }
493
494 pub fn reset_table_state_with_refs(
499 state_container: &mut AppStateContainer,
500 viewport_manager: &RefCell<Option<ViewportManager>>,
501 ) {
502 debug!("StateCoordinator::reset_table_state_with_refs: Resetting all table state");
503
504 state_container.navigation_mut().reset();
506 state_container.set_table_selected_row(Some(0));
507 state_container.reset_navigation_state();
508
509 if let Ok(mut vm_borrow) = viewport_manager.try_borrow_mut() {
511 if let Some(ref mut vm) = *vm_borrow {
512 vm.reset_crosshair();
513 debug!("StateCoordinator: Reset ViewportManager crosshair position");
514 }
515 }
516
517 state_container.filter_mut().clear();
519
520 {
522 let mut search = state_container.search_mut();
523 search.pattern.clear();
524 search.current_match = 0;
525 search.matches.clear();
526 search.is_active = false;
527 }
528
529 state_container.clear_fuzzy_filter_state();
531
532 state_container.clear_column_search();
534
535 debug!("StateCoordinator: Table state reset complete");
536 }
537
538 pub fn add_dataview_with_refs(
543 state_container: &mut AppStateContainer,
544 viewport_manager: &RefCell<Option<ViewportManager>>,
545 dataview: DataView,
546 source_name: &str,
547 config: &Config,
548 ) -> Result<(), anyhow::Error> {
549 debug!(
550 "StateCoordinator::add_dataview_with_refs: Adding DataView for '{}'",
551 source_name
552 );
553
554 let buffer_id = state_container.buffers().all_buffers().len() + 1;
556 let mut buffer = crate::buffer::Buffer::new(buffer_id);
557
558 buffer.set_dataview(Some(dataview.clone()));
560
561 let buffer_name = std::path::Path::new(source_name)
563 .file_name()
564 .and_then(|s| s.to_str())
565 .unwrap_or(source_name)
566 .to_string();
567 buffer.set_name(buffer_name.clone());
568
569 buffer.set_case_insensitive(config.behavior.case_insensitive_default);
571 buffer.set_compact_mode(config.display.compact_mode);
572 buffer.set_show_row_numbers(config.display.show_row_numbers);
573
574 debug!(
575 "StateCoordinator: Created buffer '{}' with {} rows, {} columns",
576 buffer_name,
577 dataview.row_count(),
578 dataview.column_count()
579 );
580
581 state_container.buffers_mut().add_buffer(buffer);
583 let new_index = state_container.buffers().all_buffers().len() - 1;
584 state_container.buffers_mut().switch_to(new_index);
585
586 state_container.set_dataview(Some(dataview.clone()));
588
589 *viewport_manager.borrow_mut() = Some(ViewportManager::new(Arc::new(dataview.clone())));
592
593 debug!("StateCoordinator: Created new ViewportManager for DataView");
594
595 let row_count = dataview.row_count();
597 let column_count = dataview.column_count();
598 state_container.update_data_size(row_count, column_count);
599
600 debug!(
601 "StateCoordinator: DataView '{}' successfully added and all state synchronized",
602 buffer_name
603 );
604
605 Ok(())
606 }
607
608 pub fn set_sql_query_with_refs(
613 state_container: &mut AppStateContainer,
614 shadow_state: &RefCell<crate::ui::state::shadow_state::ShadowStateManager>,
615 parser: &mut HybridParser,
616 table_name: &str,
617 raw_table_name: &str,
618 config: &Config,
619 ) -> String {
620 debug!(
621 "StateCoordinator::set_sql_query_with_refs: Setting query for table '{}'",
622 table_name
623 );
624
625 let auto_query = format!("SELECT * FROM {table_name}");
627
628 if let Some(dataview) = state_container
630 .buffers()
631 .current()
632 .and_then(|b| b.get_dataview())
633 {
634 let columns = dataview.column_names();
635 parser.update_single_table(table_name.to_string(), columns);
636
637 let display_msg = if raw_table_name == table_name {
639 format!(
640 "Loaded table '{}' with {} columns. Query pre-populated.",
641 table_name,
642 dataview.column_count()
643 )
644 } else {
645 format!(
646 "Loaded '{}' as table '{}' with {} columns. Query pre-populated.",
647 raw_table_name,
648 table_name,
649 dataview.column_count()
650 )
651 };
652 state_container.set_status_message(display_msg);
653 }
654
655 let initial_mode = match config.behavior.start_mode.to_lowercase().as_str() {
657 "results" => AppMode::Results,
658 "command" => AppMode::Command,
659 _ => AppMode::Results, };
661
662 Self::sync_mode_with_refs(
664 state_container,
665 shadow_state,
666 initial_mode.clone(),
667 "initial_load_from_config",
668 );
669
670 debug!(
671 "StateCoordinator: SQL query set to '{}', mode set to {:?}",
672 auto_query, initial_mode
673 );
674
675 auto_query
676 }
677
678 pub fn handle_execute_query_with_refs(
681 state_container: &mut AppStateContainer,
682 shadow_state: &RefCell<crate::ui::state::shadow_state::ShadowStateManager>,
683 query: &str,
684 ) -> Result<bool, anyhow::Error> {
685 debug!(
686 "StateCoordinator::handle_execute_query_with_refs: Processing query '{}'",
687 query
688 );
689
690 let trimmed = query.trim();
691
692 if trimmed.is_empty() {
693 state_container
694 .set_status_message("Empty query - please enter a SQL command".to_string());
695 return Ok(false);
696 }
697
698 if trimmed == ":help" {
700 state_container.set_help_visible(true);
701 Self::sync_mode_with_refs(
702 state_container,
703 shadow_state,
704 AppMode::Help,
705 "help_requested",
706 );
707 state_container.set_status_message("Help Mode - Press ESC to return".to_string());
708 Ok(false)
709 } else if trimmed == ":exit" || trimmed == ":quit" || trimmed == ":q" {
710 Ok(true) } else if trimmed == ":tui" {
712 state_container.set_status_message("Already in TUI mode".to_string());
713 Ok(false)
714 } else {
715 state_container.set_status_message(format!("Processing query: '{trimmed}'"));
717 Ok(false)
718 }
719 }
720
721 pub fn switch_to_results_after_query(&mut self) {
725 self.sync_mode(AppMode::Results, "execute_query_success");
726 }
727
728 pub fn switch_to_results_after_query_with_refs(
730 state_container: &mut AppStateContainer,
731 shadow_state: &RefCell<crate::ui::state::shadow_state::ShadowStateManager>,
732 ) {
733 Self::sync_mode_with_refs(
734 state_container,
735 shadow_state,
736 AppMode::Results,
737 "execute_query_success",
738 );
739 }
740
741 pub fn apply_filter_search_with_refs(
745 state_container: &mut AppStateContainer,
746 shadow_state: &RefCell<crate::ui::state::shadow_state::ShadowStateManager>,
747 pattern: &str,
748 ) {
749 debug!(
750 "StateCoordinator::apply_filter_search_with_refs: Applying filter with pattern '{}'",
751 pattern
752 );
753
754 state_container.set_filter_pattern(pattern.to_string());
756 state_container
757 .filter_mut()
758 .set_pattern(pattern.to_string());
759
760 let before_count = state_container
762 .get_buffer_dataview()
763 .map_or(0, |v| v.source().row_count());
764
765 debug!(
766 "StateCoordinator: Filter search - case_insensitive={}, rows_before={}",
767 state_container.is_case_insensitive(),
768 before_count
769 );
770
771 debug!(
775 "StateCoordinator: Filter pattern set to '{}', mode={:?}",
776 pattern,
777 shadow_state.borrow().get_mode()
778 );
779 }
780
781 pub fn apply_fuzzy_filter_search_with_refs(
783 state_container: &mut AppStateContainer,
784 shadow_state: &RefCell<crate::ui::state::shadow_state::ShadowStateManager>,
785 pattern: &str,
786 ) {
787 debug!(
788 "StateCoordinator::apply_fuzzy_filter_search_with_refs: Applying fuzzy filter with pattern '{}'",
789 pattern
790 );
791
792 let before_count = state_container
793 .get_buffer_dataview()
794 .map_or(0, |v| v.source().row_count());
795
796 state_container.set_fuzzy_filter_pattern(pattern.to_string());
798
799 debug!(
800 "StateCoordinator: Fuzzy filter - rows_before={}, pattern='{}'",
801 before_count, pattern
802 );
803
804 debug!(
808 "StateCoordinator: Fuzzy filter pattern set, mode={:?}",
809 shadow_state.borrow().get_mode()
810 );
811 }
812
813 pub fn apply_column_search_with_refs(
815 state_container: &mut AppStateContainer,
816 shadow_state: &RefCell<crate::ui::state::shadow_state::ShadowStateManager>,
817 pattern: &str,
818 ) {
819 debug!(
820 "StateCoordinator::apply_column_search_with_refs: Starting column search with pattern '{}'",
821 pattern
822 );
823
824 state_container.start_column_search(pattern.to_string());
826
827 let current_mode = shadow_state.borrow().get_mode();
829 if current_mode != AppMode::ColumnSearch {
830 debug!(
831 "StateCoordinator: WARNING - Mode was {:?}, restoring to ColumnSearch",
832 current_mode
833 );
834 Self::sync_mode_with_refs(
835 state_container,
836 shadow_state,
837 AppMode::ColumnSearch,
838 "column_search_mode_restore",
839 );
840 }
841
842 debug!(
843 "StateCoordinator: Column search started with pattern '{}'",
844 pattern
845 );
846 }
847
848 pub fn start_history_search_with_refs(
852 state_container: &mut AppStateContainer,
853 shadow_state: &RefCell<crate::ui::state::shadow_state::ShadowStateManager>,
854 current_input: String,
855 ) -> (String, usize) {
856 debug!("StateCoordinator::start_history_search_with_refs: Starting history search");
857
858 let mut input_to_use = current_input;
859
860 if shadow_state.borrow().is_in_results_mode() {
862 let last_query = state_container.get_last_query();
863 if !last_query.is_empty() {
864 input_to_use = last_query.clone();
865 debug!(
866 "StateCoordinator: Using last query for history search: '{}'",
867 last_query
868 );
869 }
870
871 state_container.set_mode(AppMode::Command);
873 shadow_state
874 .borrow_mut()
875 .observe_mode_change(AppMode::Command, "history_search_from_results");
876 state_container.set_table_selected_row(None);
877 }
878
879 state_container.start_history_search(input_to_use.clone());
881
882 let match_count = state_container.history_search().matches.len();
887 state_container.set_status_message(format!("History search: {match_count} matches"));
888
889 state_container.set_mode(AppMode::History);
891 shadow_state
892 .borrow_mut()
893 .observe_mode_change(AppMode::History, "history_search_started");
894
895 debug!(
896 "StateCoordinator: History search started with {} matches, mode=History",
897 match_count
898 );
899
900 (input_to_use, match_count)
901 }
902
903 pub fn goto_first_row_with_refs(
907 state_container: &mut AppStateContainer,
908 vim_search_adapter: Option<
909 &RefCell<crate::ui::search::vim_search_adapter::VimSearchAdapter>,
910 >,
911 viewport_manager: Option<&RefCell<Option<ViewportManager>>>,
912 ) {
913 debug!("StateCoordinator::goto_first_row_with_refs: Going to first row");
914
915 state_container.set_table_selected_row(Some(0));
917 state_container.set_scroll_offset((0, 0));
918
919 if let Some(adapter) = vim_search_adapter {
921 let is_navigating = adapter.borrow().is_navigating();
922
923 if is_navigating {
924 if let Some(viewport_ref) = viewport_manager {
925 let mut vim_search_mut = adapter.borrow_mut();
926 let mut viewport_borrow = viewport_ref.borrow_mut();
927 if let Some(ref mut viewport) = *viewport_borrow {
928 if let Some(first_match) = vim_search_mut.reset_to_first_match(viewport) {
929 debug!(
930 "StateCoordinator: Reset vim search to first match at ({}, {})",
931 first_match.row, first_match.col
932 );
933 }
934 }
935 }
936 }
937 }
938 }
939
940 pub fn goto_last_row_with_refs(state_container: &mut AppStateContainer) {
942 debug!("StateCoordinator::goto_last_row_with_refs: Going to last row");
943
944 if let Some(dataview) = state_container.get_buffer_dataview() {
946 let last_row = dataview.row_count().saturating_sub(1);
947 state_container.set_table_selected_row(Some(last_row));
948
949 let scroll_row = last_row.saturating_sub(20); state_container.set_scroll_offset((scroll_row, 0));
953 }
954 }
955
956 pub fn goto_row_with_refs(state_container: &mut AppStateContainer, row: usize) {
958 debug!("StateCoordinator::goto_row_with_refs: Going to row {}", row);
959
960 if let Some(dataview) = state_container.get_buffer_dataview() {
962 let max_row = dataview.row_count().saturating_sub(1);
963 let target_row = row.min(max_row);
964
965 state_container.set_table_selected_row(Some(target_row));
966
967 let current_scroll = state_container.get_scroll_offset().0;
969 if target_row < current_scroll || target_row > current_scroll + 20 {
970 let new_scroll = target_row.saturating_sub(10);
972 state_container.set_scroll_offset((new_scroll, 0));
973 }
974 }
975 }
976
977 pub fn state_container(&self) -> &AppStateContainer {
981 &self.state_container
982 }
983
984 pub fn state_container_mut(&mut self) -> &mut AppStateContainer {
986 &mut self.state_container
987 }
988
989 pub fn current_buffer(&self) -> Option<&Buffer> {
991 self.state_container.buffers().current()
992 }
993
994 pub fn current_buffer_mut(&mut self) -> Option<&mut Buffer> {
996 self.state_container.buffers_mut().current_mut()
997 }
998
999 pub fn buffers(&self) -> &BufferManager {
1001 self.state_container.buffers()
1002 }
1003
1004 pub fn buffers_mut(&mut self) -> &mut BufferManager {
1006 self.state_container.buffers_mut()
1007 }
1008
1009 pub fn parser(&self) -> &HybridParser {
1011 &self.hybrid_parser
1012 }
1013
1014 pub fn parser_mut(&mut self) -> &mut HybridParser {
1016 &mut self.hybrid_parser
1017 }
1018}