1use crate::api_client::QueryResponse; use crate::csv_datasource::CsvApiClient; use crate::cursor_operations::CursorOperations;
4use crate::data::data_view::DataView;
5use crate::data::datatable::DataTable;
6use crate::hybrid_parser::HybridParser;
7use crate::input_manager::{create_from_input, create_single_line, InputManager};
8use anyhow::Result;
9use crossterm::event::KeyEvent;
10use fuzzy_matcher::skim::SkimMatcherV2;
11use ratatui::style::Color;
12use ratatui::widgets::TableState;
13use regex::Regex;
14use std::collections::BTreeMap;
15use std::path::PathBuf;
16use std::sync::Arc;
17use tracing::debug;
18use tui_input::Input;
19
20#[derive(Clone, Debug, PartialEq, Eq, Hash)]
22pub enum AppMode {
23 Command,
24 Results,
25 Search,
26 Filter,
27 FuzzyFilter,
28 ColumnSearch,
29 Help,
30 History,
31 Debug,
32 PrettyQuery,
33 JumpToRow,
34 ColumnStats,
35}
36
37#[derive(Clone, PartialEq, Debug)]
38pub enum EditMode {
39 SingleLine,
40 MultiLine,
41}
42
43#[derive(Clone, PartialEq, Copy, Debug)]
44pub enum SortOrder {
45 Ascending,
46 Descending,
47 None,
48}
49
50#[derive(Clone)]
51pub struct SortState {
52 pub column: Option<usize>,
53 pub order: SortOrder,
54}
55
56#[derive(Clone, Debug)]
57pub struct FilterState {
58 pub pattern: String,
59 pub regex: Option<Regex>,
60 pub active: bool,
61}
62
63impl Default for FilterState {
64 fn default() -> Self {
65 Self {
66 pattern: String::new(),
67 regex: None,
68 active: false,
69 }
70 }
71}
72
73pub struct FuzzyFilterState {
74 pub pattern: String,
75 pub active: bool,
76 pub matcher: SkimMatcherV2,
77 pub filtered_indices: Vec<usize>,
78}
79
80impl std::fmt::Debug for FuzzyFilterState {
81 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82 f.debug_struct("FuzzyFilterState")
83 .field("pattern", &self.pattern)
84 .field("active", &self.active)
85 .field("matcher", &"SkimMatcherV2")
86 .field("filtered_indices", &self.filtered_indices)
87 .finish()
88 }
89}
90
91impl Clone for FuzzyFilterState {
92 fn clone(&self) -> Self {
93 Self {
94 pattern: self.pattern.clone(),
95 active: self.active,
96 matcher: SkimMatcherV2::default(), filtered_indices: self.filtered_indices.clone(),
98 }
99 }
100}
101
102impl Default for FuzzyFilterState {
103 fn default() -> Self {
104 Self {
105 pattern: String::new(),
106 active: false,
107 matcher: SkimMatcherV2::default(),
108 filtered_indices: Vec::new(),
109 }
110 }
111}
112
113#[derive(Clone, Debug)]
114pub struct SearchState {
115 pub pattern: String,
116 pub current_match: Option<(usize, usize)>,
117 pub matches: Vec<(usize, usize)>,
118 pub match_index: usize,
119}
120
121impl Default for SearchState {
122 fn default() -> Self {
123 Self {
124 pattern: String::new(),
125 current_match: None,
126 matches: Vec::new(),
127 match_index: 0,
128 }
129 }
130}
131
132#[derive(Clone, Debug, PartialEq)]
135pub enum SelectionMode {
136 Row,
137 Cell,
138 Column,
139}
140
141#[derive(Clone, Debug)]
144pub struct ViewState {
145 pub crosshair_row: usize,
147 pub crosshair_col: usize,
148 pub scroll_offset: (usize, usize),
149
150 pub selection_mode: SelectionMode,
152 pub selected_cells: Vec<(usize, usize)>,
153 pub selection_anchor: Option<(usize, usize)>,
154
155 pub viewport_lock: bool,
157 pub cursor_lock: bool,
158
159 pub navigation_history: Vec<(usize, usize)>,
161 pub history_index: usize,
162
163 pub viewport_rows: usize,
165 pub viewport_columns: usize,
166 pub total_rows: usize,
167 pub total_columns: usize,
168}
169
170impl Default for ViewState {
171 fn default() -> Self {
172 Self {
173 crosshair_row: 0,
174 crosshair_col: 0,
175 scroll_offset: (0, 0),
176 selection_mode: SelectionMode::Row,
177 selected_cells: Vec::new(),
178 selection_anchor: None,
179 viewport_lock: false,
180 cursor_lock: false,
181 navigation_history: Vec::new(),
182 history_index: 0,
183 viewport_rows: 0,
184 viewport_columns: 0,
185 total_rows: 0,
186 total_columns: 0,
187 }
188 }
189}
190
191#[derive(Clone, Debug)]
192pub enum ColumnType {
193 String,
194 Numeric,
195 Mixed,
196}
197
198#[derive(Clone)]
199pub struct ColumnStatistics {
200 pub column_name: String,
201 pub column_type: ColumnType,
202 pub total_count: usize,
204 pub null_count: usize,
205 pub unique_count: usize,
206 pub frequency_map: Option<BTreeMap<String, usize>>,
208 pub min: Option<f64>,
210 pub max: Option<f64>,
211 pub sum: Option<f64>,
212 pub mean: Option<f64>,
213 pub median: Option<f64>,
214}
215
216pub trait BufferAPI: Send + Sync {
224 fn get_id(&self) -> usize;
226 fn get_query(&self) -> String;
228 fn set_query(&mut self, query: String);
229 fn get_last_query(&self) -> String;
231 fn set_last_query(&mut self, query: String);
232
233 fn get_datatable(&self) -> Option<&DataTable>;
235 fn get_datatable_mut(&mut self) -> Option<&mut DataTable>;
236 fn has_datatable(&self) -> bool;
237 fn set_datatable(&mut self, datatable: Option<Arc<DataTable>>);
238 fn get_original_source(&self) -> Option<&DataTable>;
239 fn set_results_as_datatable(&mut self, response: Option<QueryResponse>) -> Result<(), String>;
241
242 fn get_dataview(&self) -> Option<&DataView>;
244 fn get_dataview_mut(&mut self) -> Option<&mut DataView>;
245 fn set_dataview(&mut self, dataview: Option<DataView>);
246 fn has_dataview(&self) -> bool;
247
248 fn get_mode(&self) -> AppMode;
250 fn set_mode(&mut self, mode: AppMode);
251 fn get_edit_mode(&self) -> EditMode;
252 fn set_edit_mode(&mut self, mode: EditMode);
253 fn get_status_message(&self) -> String;
254 fn set_status_message(&mut self, message: String);
255
256 fn get_selected_row(&self) -> Option<usize>;
258 fn set_selected_row(&mut self, row: Option<usize>);
259 fn get_current_column(&self) -> usize;
260 fn set_current_column(&mut self, col: usize);
261 fn get_scroll_offset(&self) -> (usize, usize);
262 fn set_scroll_offset(&mut self, offset: (usize, usize));
263 fn get_last_results_row(&self) -> Option<usize>;
264 fn set_last_results_row(&mut self, row: Option<usize>);
265 fn get_last_scroll_offset(&self) -> (usize, usize);
266 fn set_last_scroll_offset(&mut self, offset: (usize, usize));
267
268 fn get_filter_pattern(&self) -> String;
270 fn set_filter_pattern(&mut self, pattern: String);
271 fn is_filter_active(&self) -> bool;
272 fn set_filter_active(&mut self, active: bool);
273 fn get_fuzzy_filter_pattern(&self) -> String;
277 fn set_fuzzy_filter_pattern(&mut self, pattern: String);
278 fn is_fuzzy_filter_active(&self) -> bool;
279 fn set_fuzzy_filter_active(&mut self, active: bool);
280 fn get_fuzzy_filter_indices(&self) -> &Vec<usize>;
281 fn set_fuzzy_filter_indices(&mut self, indices: Vec<usize>);
282 fn clear_fuzzy_filter(&mut self);
283
284 fn get_search_pattern(&self) -> String;
286 fn set_search_pattern(&mut self, pattern: String);
287 fn get_search_matches(&self) -> Vec<(usize, usize)>;
288 fn set_search_matches(&mut self, matches: Vec<(usize, usize)>);
289 fn get_current_match(&self) -> Option<(usize, usize)>;
290 fn set_current_match(&mut self, match_pos: Option<(usize, usize)>);
291 fn get_search_match_index(&self) -> usize;
292 fn set_search_match_index(&mut self, index: usize);
293 fn clear_search_state(&mut self);
294
295 fn get_column_stats(&self) -> Option<&ColumnStatistics>;
299 fn set_column_stats(&mut self, stats: Option<ColumnStatistics>);
300
301 fn get_sort_column(&self) -> Option<usize>;
303 fn set_sort_column(&mut self, column: Option<usize>);
304 fn get_sort_order(&self) -> SortOrder;
305 fn set_sort_order(&mut self, order: SortOrder);
306
307 fn is_compact_mode(&self) -> bool;
309 fn set_compact_mode(&mut self, compact: bool);
310 fn is_show_row_numbers(&self) -> bool;
311 fn set_show_row_numbers(&mut self, show: bool);
312 fn is_viewport_lock(&self) -> bool;
313 fn set_viewport_lock(&mut self, locked: bool);
314 fn get_viewport_lock_row(&self) -> Option<usize>;
315 fn set_viewport_lock_row(&mut self, row: Option<usize>);
316 fn get_column_widths(&self) -> &Vec<u16>;
319 fn set_column_widths(&mut self, widths: Vec<u16>);
320 fn is_case_insensitive(&self) -> bool;
321 fn set_case_insensitive(&mut self, case_insensitive: bool);
322
323 fn get_name(&self) -> String;
325 fn set_name(&mut self, name: String);
326 fn get_file_path(&self) -> Option<&PathBuf>;
327 fn set_file_path(&mut self, path: Option<String>);
328 fn is_modified(&self) -> bool;
329 fn set_modified(&mut self, modified: bool);
330 fn get_last_query_source(&self) -> Option<String>;
331 fn set_last_query_source(&mut self, source: Option<String>);
332
333 fn get_input_value(&self) -> String;
338 fn set_input_value(&mut self, value: String);
339 fn get_input_cursor(&self) -> usize;
340 fn set_input_cursor(&mut self, pos: usize);
341
342 fn apply_filter(&mut self) -> Result<()>;
344 fn apply_sort(&mut self) -> Result<()>;
345 fn search(&mut self) -> Result<()>;
346 fn clear_filters(&mut self);
347 fn get_row_count(&self) -> usize;
348 fn get_column_count(&self) -> usize;
349 fn get_column_names(&self) -> Vec<String>;
350
351 fn get_undo_stack(&self) -> &Vec<(String, usize)>;
353 fn push_undo(&mut self, state: (String, usize));
354 fn pop_undo(&mut self) -> Option<(String, usize)>;
355 fn get_redo_stack(&self) -> &Vec<(String, usize)>;
356 fn push_redo(&mut self, state: (String, usize));
357 fn pop_redo(&mut self) -> Option<(String, usize)>;
358 fn clear_redo(&mut self);
359 fn get_kill_ring(&self) -> String;
360 fn set_kill_ring(&mut self, text: String);
361 fn is_kill_ring_empty(&self) -> bool;
362
363 fn perform_undo(&mut self) -> bool;
365 fn perform_redo(&mut self) -> bool;
366 fn save_state_for_undo(&mut self);
367
368 fn get_last_visible_rows(&self) -> usize;
370 fn set_last_visible_rows(&mut self, rows: usize);
371
372 fn debug_dump(&self) -> String;
374
375 fn get_input_text(&self) -> String;
377 fn set_input_text(&mut self, text: String);
378 fn handle_input_key(&mut self, event: KeyEvent) -> bool;
379 fn switch_input_mode(&mut self, multiline: bool);
380 fn get_input_cursor_position(&self) -> usize;
381 fn set_input_cursor_position(&mut self, position: usize);
382 fn is_input_multiline(&self) -> bool;
383
384 fn navigate_history_up(&mut self, history: &[String]) -> bool;
386 fn navigate_history_down(&mut self, history: &[String]) -> bool;
387 fn reset_history_navigation(&mut self);
388
389 fn clear_results(&mut self);
391}
392
393pub struct Buffer {
395 pub id: usize,
397
398 pub file_path: Option<PathBuf>,
400
401 pub name: String,
403
404 pub modified: bool,
406
407 pub datatable: Option<Arc<DataTable>>,
409 pub original_source: Option<Arc<DataTable>>,
411 pub dataview: Option<DataView>,
413
414 pub mode: AppMode,
416 pub edit_mode: EditMode,
417 pub input: Input, pub input_manager: Box<dyn InputManager>, pub table_state: TableState,
420 pub last_results_row: Option<usize>,
421 pub last_scroll_offset: (usize, usize),
422
423 pub last_query: String,
425 pub status_message: String,
426
427 pub sort_state: SortState,
429 pub filter_state: FilterState,
430 pub fuzzy_filter_state: FuzzyFilterState,
431 pub search_state: SearchState,
432
433 pub column_stats: Option<ColumnStatistics>,
434
435 pub view_state: ViewState,
437
438 pub column_widths: Vec<u16>,
440 pub compact_mode: bool,
441 pub show_row_numbers: bool,
442 pub case_insensitive: bool,
443
444 pub undo_stack: Vec<(String, usize)>,
446 pub redo_stack: Vec<(String, usize)>,
447 pub kill_ring: String,
448 pub last_visible_rows: usize,
449 pub last_query_source: Option<String>,
450
451 pub highlighted_text_cache: Option<Vec<(String, Color)>>, pub last_highlighted_text: String, pub saved_input_state: Option<(String, usize)>, }
458
459impl BufferAPI for Buffer {
461 fn get_id(&self) -> usize {
463 self.id
464 }
465
466 fn get_query(&self) -> String {
468 self.input_manager.get_text()
470 }
471
472 fn set_query(&mut self, query: String) {
473 self.input_manager.set_text(query.clone());
475 self.input = Input::new(query.clone()).with_cursor(query.len());
476 }
477
478 fn get_last_query(&self) -> String {
481 self.last_query.clone()
482 }
483
484 fn set_last_query(&mut self, query: String) {
485 self.last_query = query;
486 }
487
488 fn get_datatable(&self) -> Option<&DataTable> {
490 self.datatable.as_ref().map(|arc| arc.as_ref())
491 }
492
493 fn get_datatable_mut(&mut self) -> Option<&mut DataTable> {
494 None
497 }
498
499 fn has_datatable(&self) -> bool {
500 self.datatable.is_some()
501 }
502
503 fn get_original_source(&self) -> Option<&DataTable> {
504 self.original_source.as_ref().map(|arc| arc.as_ref())
505 }
506
507 fn set_datatable(&mut self, datatable: Option<Arc<DataTable>>) {
508 debug!(
509 "V50: Setting DataTable with {} rows, {} columns",
510 datatable.as_ref().map(|d| d.row_count()).unwrap_or(0),
511 datatable.as_ref().map(|d| d.column_count()).unwrap_or(0)
512 );
513
514 if let Some(ref current) = self.datatable {
516 debug!(
517 "V50: Current DataTable has {} columns: {:?}",
518 current.column_count(),
519 current.column_names()
520 );
521 }
522
523 if let Some(ref original) = self.original_source {
524 debug!(
525 "V50: Original source has {} columns: {:?}",
526 original.column_count(),
527 original.column_names()
528 );
529 }
530
531 if datatable.is_some() && self.original_source.is_none() {
534 self.original_source = datatable.clone();
535 debug!(
536 "V50: Preserving original source DataTable with {} columns",
537 datatable.as_ref().map(|d| d.column_count()).unwrap_or(0)
538 );
539 }
540
541 if let Some(dt) = &datatable {
544 let mut view = crate::data::data_view::DataView::new(dt.clone());
546
547 if let Some(old_view) = &self.dataview {
549 let old_all_cols = old_view.source().column_names();
551 let old_visible_cols = old_view.column_names();
552
553 for col_name in &old_all_cols {
554 if !old_visible_cols.contains(col_name) {
555 view.hide_column_by_name(col_name);
557 }
558 }
559 }
560
561 view.shrink_to_fit();
563
564 self.dataview = Some(view);
566 } else {
567 self.dataview = None;
568 }
569
570 if let Some(ref original) = self.original_source {
573 if let Some(ref new_dt) = datatable {
574 if new_dt.column_count() < original.column_count() {
575 debug!(
576 "V50: WARNING - Attempted to replace datatable with fewer columns ({} < {}). Keeping original.",
577 new_dt.column_count(),
578 original.column_count()
579 );
580 return;
582 }
583 }
584 }
585
586 self.datatable = datatable;
587 }
588
589 fn set_results_as_datatable(&mut self, response: Option<QueryResponse>) -> Result<(), String> {
590 if let Some(ref resp) = response {
591 debug!("V50: Converting QueryResponse to DataTable");
592 let table_name = resp.table.as_deref().unwrap_or("data");
593 match DataTable::from_query_response(resp, table_name) {
594 Ok(datatable) => {
595 debug!(
596 "V50: Stored DataTable with {} rows, {} columns",
597 datatable.row_count(),
598 datatable.column_count()
599 );
600 self.datatable = Some(Arc::new(datatable));
601 Ok(())
602 }
603 Err(e) => {
604 let err_msg = format!("V50: Failed to create DataTable: {}", e);
605 debug!("{}", err_msg);
606 self.datatable = None;
607 Err(err_msg)
608 }
609 }
610 } else {
611 self.datatable = None;
612 Ok(())
613 }
614 }
615
616 fn get_dataview(&self) -> Option<&DataView> {
618 self.dataview.as_ref()
619 }
620 fn get_dataview_mut(&mut self) -> Option<&mut DataView> {
621 self.dataview.as_mut()
622 }
623 fn set_dataview(&mut self, dataview: Option<DataView>) {
624 debug!(
625 "V51: Setting DataView with {} rows",
626 dataview.as_ref().map(|v| v.row_count()).unwrap_or(0)
627 );
628 self.dataview = dataview;
629 }
630 fn has_dataview(&self) -> bool {
631 self.dataview.is_some()
632 }
633
634 fn get_mode(&self) -> AppMode {
636 self.mode.clone()
637 }
638
639 fn set_mode(&mut self, mode: AppMode) {
640 self.mode = mode;
641 }
642
643 fn get_edit_mode(&self) -> EditMode {
644 self.edit_mode.clone()
645 }
646
647 fn set_edit_mode(&mut self, mode: EditMode) {
648 self.edit_mode = mode;
649 }
650
651 fn get_status_message(&self) -> String {
652 self.status_message.clone()
653 }
654
655 fn set_status_message(&mut self, message: String) {
656 self.status_message = message;
657 }
658
659 fn get_selected_row(&self) -> Option<usize> {
661 self.table_state.selected()
664 }
665
666 fn set_selected_row(&mut self, row: Option<usize>) {
667 if let Some(r) = row {
668 self.view_state.crosshair_row = r;
669 self.table_state.select(Some(r));
671 } else {
672 self.view_state.crosshair_row = 0;
674 self.table_state.select(None);
675 }
676 }
677
678 fn get_current_column(&self) -> usize {
679 self.view_state.crosshair_col
680 }
681
682 fn set_current_column(&mut self, col: usize) {
683 self.view_state.crosshair_col = col;
684 }
685
686 fn get_scroll_offset(&self) -> (usize, usize) {
687 self.view_state.scroll_offset
688 }
689
690 fn set_scroll_offset(&mut self, offset: (usize, usize)) {
691 self.view_state.scroll_offset = offset;
692 }
693
694 fn get_last_results_row(&self) -> Option<usize> {
695 self.last_results_row
696 }
697
698 fn set_last_results_row(&mut self, row: Option<usize>) {
699 self.last_results_row = row;
700 }
701
702 fn get_last_scroll_offset(&self) -> (usize, usize) {
703 self.last_scroll_offset
704 }
705
706 fn set_last_scroll_offset(&mut self, offset: (usize, usize)) {
707 self.last_scroll_offset = offset;
708 }
709
710 fn get_filter_pattern(&self) -> String {
712 self.filter_state.pattern.clone()
713 }
714
715 fn set_filter_pattern(&mut self, pattern: String) {
716 self.filter_state.pattern = pattern;
717 }
718
719 fn is_filter_active(&self) -> bool {
720 self.filter_state.active
721 }
722
723 fn set_filter_active(&mut self, active: bool) {
724 self.filter_state.active = active;
725 }
726
727 fn get_fuzzy_filter_pattern(&self) -> String {
731 self.fuzzy_filter_state.pattern.clone()
732 }
733
734 fn set_fuzzy_filter_pattern(&mut self, pattern: String) {
735 self.fuzzy_filter_state.pattern = pattern;
736 }
737
738 fn is_fuzzy_filter_active(&self) -> bool {
739 self.fuzzy_filter_state.active
740 }
741
742 fn set_fuzzy_filter_active(&mut self, active: bool) {
743 self.fuzzy_filter_state.active = active;
744 }
745
746 fn get_fuzzy_filter_indices(&self) -> &Vec<usize> {
747 &self.fuzzy_filter_state.filtered_indices
748 }
749
750 fn set_fuzzy_filter_indices(&mut self, indices: Vec<usize>) {
751 self.fuzzy_filter_state.filtered_indices = indices;
752 }
753
754 fn clear_fuzzy_filter(&mut self) {
755 self.fuzzy_filter_state.pattern.clear();
756 self.fuzzy_filter_state.active = false;
757 self.fuzzy_filter_state.filtered_indices.clear();
758 }
759
760 fn get_search_pattern(&self) -> String {
762 self.search_state.pattern.clone()
763 }
764
765 fn set_search_pattern(&mut self, pattern: String) {
766 self.search_state.pattern = pattern;
767 }
768
769 fn get_search_matches(&self) -> Vec<(usize, usize)> {
770 self.search_state.matches.clone()
771 }
772
773 fn set_search_matches(&mut self, matches: Vec<(usize, usize)>) {
774 self.search_state.matches = matches;
775 }
776
777 fn get_current_match(&self) -> Option<(usize, usize)> {
778 self.search_state.current_match
779 }
780
781 fn set_current_match(&mut self, match_pos: Option<(usize, usize)>) {
782 self.search_state.current_match = match_pos;
783 }
784
785 fn get_search_match_index(&self) -> usize {
786 self.search_state.match_index
787 }
788
789 fn set_search_match_index(&mut self, index: usize) {
790 self.search_state.match_index = index;
791 }
792
793 fn clear_search_state(&mut self) {
794 self.search_state.pattern.clear();
795 self.search_state.matches.clear();
796 self.search_state.current_match = None;
797 self.search_state.match_index = 0;
798 }
799
800 fn get_column_stats(&self) -> Option<&ColumnStatistics> {
803 self.column_stats.as_ref()
804 }
805
806 fn set_column_stats(&mut self, stats: Option<ColumnStatistics>) {
807 self.column_stats = stats;
808 }
809
810 fn get_sort_column(&self) -> Option<usize> {
812 self.sort_state.column
813 }
814
815 fn set_sort_column(&mut self, column: Option<usize>) {
816 self.sort_state.column = column;
817 }
818
819 fn get_sort_order(&self) -> SortOrder {
820 self.sort_state.order
821 }
822
823 fn set_sort_order(&mut self, order: SortOrder) {
824 self.sort_state.order = order;
825 }
826
827 fn is_compact_mode(&self) -> bool {
829 self.compact_mode
830 }
831
832 fn set_compact_mode(&mut self, compact: bool) {
833 self.compact_mode = compact;
834 }
835
836 fn is_show_row_numbers(&self) -> bool {
837 self.show_row_numbers
838 }
839
840 fn set_show_row_numbers(&mut self, show: bool) {
841 self.show_row_numbers = show;
842 }
843
844 fn is_viewport_lock(&self) -> bool {
845 self.view_state.viewport_lock
846 }
847
848 fn set_viewport_lock(&mut self, locked: bool) {
849 self.view_state.viewport_lock = locked;
850 }
851
852 fn get_viewport_lock_row(&self) -> Option<usize> {
853 if self.view_state.viewport_lock {
855 Some(self.view_state.crosshair_row)
856 } else {
857 None
858 }
859 }
860
861 fn set_viewport_lock_row(&mut self, row: Option<usize>) {
862 if let Some(r) = row {
864 self.view_state.crosshair_row = r;
865 self.view_state.viewport_lock = true;
866 }
867 }
868
869 fn get_column_widths(&self) -> &Vec<u16> {
870 &self.column_widths
871 }
872
873 fn set_column_widths(&mut self, widths: Vec<u16>) {
874 self.column_widths = widths;
875 }
876
877 fn is_case_insensitive(&self) -> bool {
878 self.case_insensitive
879 }
880
881 fn set_case_insensitive(&mut self, case_insensitive: bool) {
882 self.case_insensitive = case_insensitive;
883 }
884
885 fn get_name(&self) -> String {
887 self.name.clone()
888 }
889
890 fn set_name(&mut self, name: String) {
891 self.name = name;
892 }
893
894 fn get_file_path(&self) -> Option<&PathBuf> {
895 self.file_path.as_ref()
896 }
897
898 fn set_file_path(&mut self, path: Option<String>) {
899 self.file_path = path.map(PathBuf::from);
900 }
901
902 fn is_modified(&self) -> bool {
903 self.modified
904 }
905
906 fn set_modified(&mut self, modified: bool) {
907 self.modified = modified;
908 }
909
910 fn get_last_query_source(&self) -> Option<String> {
911 self.last_query_source.clone()
912 }
913
914 fn set_last_query_source(&mut self, source: Option<String>) {
915 self.last_query_source = source;
916 }
917
918 fn get_input_value(&self) -> String {
920 self.input.value().to_string()
921 }
922
923 fn set_input_value(&mut self, value: String) {
924 let cursor = value.len();
925 self.input = Input::new(value).with_cursor(cursor);
926 }
927
928 fn get_input_cursor(&self) -> usize {
929 self.input.cursor()
930 }
931
932 fn set_input_cursor(&mut self, pos: usize) {
933 let value = self.input.value().to_string();
934 self.input = Input::new(value).with_cursor(pos);
935 }
936
937 fn apply_filter(&mut self) -> Result<()> {
939 Ok(())
941 }
942
943 fn apply_sort(&mut self) -> Result<()> {
944 Ok(())
946 }
947
948 fn search(&mut self) -> Result<()> {
949 Ok(())
951 }
952
953 fn clear_filters(&mut self) {
954 self.filter_state.active = false;
955 self.filter_state.pattern.clear();
956 self.fuzzy_filter_state.active = false;
957 self.fuzzy_filter_state.pattern.clear();
958 }
960
961 fn get_row_count(&self) -> usize {
962 if let Some(dataview) = &self.dataview {
963 dataview.row_count()
964 } else if let Some(datatable) = &self.datatable {
965 datatable.row_count()
966 } else {
967 0
968 }
969 }
970
971 fn get_column_count(&self) -> usize {
972 if let Some(datatable) = &self.datatable {
973 return datatable.column_count();
974 }
975 0
976 }
977
978 fn get_column_names(&self) -> Vec<String> {
979 if let Some(datatable) = &self.datatable {
980 return datatable.column_names();
981 }
982 Vec::new()
983 }
984
985 fn get_undo_stack(&self) -> &Vec<(String, usize)> {
987 &self.undo_stack
988 }
989
990 fn push_undo(&mut self, state: (String, usize)) {
991 self.undo_stack.push(state);
992 if self.undo_stack.len() > 100 {
993 self.undo_stack.remove(0);
994 }
995 }
996
997 fn pop_undo(&mut self) -> Option<(String, usize)> {
998 self.undo_stack.pop()
999 }
1000
1001 fn get_redo_stack(&self) -> &Vec<(String, usize)> {
1002 &self.redo_stack
1003 }
1004
1005 fn push_redo(&mut self, state: (String, usize)) {
1006 self.redo_stack.push(state);
1007 }
1008
1009 fn pop_redo(&mut self) -> Option<(String, usize)> {
1010 self.redo_stack.pop()
1011 }
1012
1013 fn clear_redo(&mut self) {
1014 self.redo_stack.clear();
1015 }
1016
1017 fn perform_undo(&mut self) -> bool {
1018 if let Some((prev_text, prev_cursor)) = self.pop_undo() {
1019 let current_state = (self.get_input_text(), self.get_input_cursor_position());
1021 self.push_redo(current_state);
1022
1023 self.set_input_text(prev_text);
1025 self.set_input_cursor_position(prev_cursor);
1026 true
1027 } else {
1028 false
1029 }
1030 }
1031
1032 fn perform_redo(&mut self) -> bool {
1033 if let Some((next_text, next_cursor)) = self.pop_redo() {
1034 let current_state = (self.get_input_text(), self.get_input_cursor_position());
1036 self.push_undo(current_state);
1037
1038 self.set_input_text(next_text);
1040 self.set_input_cursor_position(next_cursor);
1041 true
1042 } else {
1043 false
1044 }
1045 }
1046
1047 fn save_state_for_undo(&mut self) {
1048 let current_state = (self.get_input_text(), self.get_input_cursor_position());
1049 self.push_undo(current_state);
1050 self.clear_redo();
1051 }
1052
1053 fn get_kill_ring(&self) -> String {
1054 self.kill_ring.clone()
1055 }
1056
1057 fn set_kill_ring(&mut self, text: String) {
1058 self.kill_ring = text;
1059 }
1060
1061 fn is_kill_ring_empty(&self) -> bool {
1062 self.kill_ring.is_empty()
1063 }
1064
1065 fn get_last_visible_rows(&self) -> usize {
1067 self.last_visible_rows
1068 }
1069
1070 fn set_last_visible_rows(&mut self, rows: usize) {
1071 self.last_visible_rows = rows;
1072 }
1073
1074 fn debug_dump(&self) -> String {
1075 let mut output = String::new();
1076 output.push_str("=== BUFFER DEBUG DUMP ===\n");
1077 output.push_str(&format!("Buffer ID: {}\n", self.id));
1078 output.push_str(&format!("Name: {}\n", self.name));
1079 output.push_str(&format!("File Path: {:?}\n", self.file_path));
1080 output.push_str(&format!("Modified: {}\n", self.modified));
1081 output.push_str("\n--- Modes ---\n");
1082 output.push_str(&format!("App Mode: {:?}\n", self.mode));
1083 output.push_str(&format!("Edit Mode: {:?}\n", self.edit_mode));
1084 output.push_str("\n--- Query State ---\n");
1085 output.push_str(&format!("Current Input: '{}'\n", self.input.value()));
1086 output.push_str(&format!("Input Cursor: {}\n", self.input.cursor()));
1087 output.push_str(&format!("Last Query: '{}'\n", self.last_query));
1088 output.push_str(&format!("Status Message: '{}'\n", self.status_message));
1089 output.push_str(&format!(
1090 "Last Query Source: {:?}\n",
1091 self.last_query_source
1092 ));
1093 output.push_str("\n--- Results ---\n");
1094 output.push_str(&format!("Has DataTable: {}\n", self.datatable.is_some()));
1095 output.push_str(&format!("Row Count: {}\n", self.get_row_count()));
1096 output.push_str(&format!("Column Count: {}\n", self.get_column_count()));
1097 output.push_str(&format!(
1098 "Selected Row: {:?}\n",
1099 self.table_state.selected()
1100 ));
1101 output.push_str(&format!(
1102 "Current Column: {}\n",
1103 self.view_state.crosshair_col
1104 ));
1105 output.push_str(&format!(
1106 "Scroll Offset: {:?}\n",
1107 self.view_state.scroll_offset
1108 ));
1109 output.push_str("\n--- Filtering ---\n");
1110 output.push_str(&format!("Filter Active: {}\n", self.filter_state.active));
1111 output.push_str(&format!(
1112 "Filter Pattern: '{}'\n",
1113 self.filter_state.pattern
1114 ));
1115 output.push_str("Filtering: Handled by DataView\n");
1116 output.push_str(&format!(
1117 "Fuzzy Filter Active: {}\n",
1118 self.fuzzy_filter_state.active
1119 ));
1120 output.push_str(&format!(
1121 "Fuzzy Pattern: '{}'\n",
1122 self.fuzzy_filter_state.pattern
1123 ));
1124 output.push_str("\n--- Search ---\n");
1125 output.push_str(&format!(
1126 "Search Pattern: '{}'\n",
1127 self.search_state.pattern
1128 ));
1129 output.push_str(&format!(
1130 "Search Matches: {} found\n",
1131 self.search_state.matches.len()
1132 ));
1133 output.push_str(&format!(
1134 "Current Match: {:?}\n",
1135 self.search_state.current_match
1136 ));
1137 output.push_str(&format!("Match Index: {}\n", self.search_state.match_index));
1138 output.push_str("\n--- Column Search ---\n");
1139 output.push_str(&format!(
1140 "Column Search Pattern: '{}'\n",
1141 "<migrated>".to_string() ));
1143 output.push_str(&format!(
1144 "Matching Columns: {:?}\n",
1145 Vec::<(usize, String)>::new() ));
1147 output.push_str("\n--- Sorting ---\n");
1148 output.push_str(&format!("Sort Column: {:?}\n", self.sort_state.column));
1149 output.push_str(&format!("Sort Order: {:?}\n", self.sort_state.order));
1150 output.push_str("\n--- Display Options ---\n");
1151 output.push_str(&format!("Compact Mode: {}\n", self.compact_mode));
1152 output.push_str(&format!("Show Row Numbers: {}\n", self.show_row_numbers));
1153 output.push_str(&format!("Case Insensitive: {}\n", self.case_insensitive));
1154 if let Some(ref dataview) = self.dataview {
1156 output.push_str(&format!(
1157 "Pinned Columns: {:?}\n",
1158 dataview.get_pinned_column_names()
1159 ));
1160 } else {
1161 output.push_str("Pinned Columns: []\n");
1162 }
1163 output.push_str(&format!("Column Widths: {:?}\n", self.column_widths));
1164 output.push_str(&format!("ViewState: {:?}\n", self.view_state));
1165 output.push_str("\n--- Data Source ---\n");
1166 output.push_str("Legacy CSV/Cache fields removed - using DataTable/DataView\n");
1167 output.push_str("\n--- Undo/Redo ---\n");
1168 output.push_str(&format!("Undo Stack Size: {}\n", self.undo_stack.len()));
1169 output.push_str(&format!("Redo Stack Size: {}\n", self.redo_stack.len()));
1170 output.push_str(&format!(
1171 "Kill Ring: '{}'\n",
1172 if self.kill_ring.len() > 50 {
1173 format!(
1174 "{}... ({} chars)",
1175 &self.kill_ring[..50],
1176 self.kill_ring.len()
1177 )
1178 } else {
1179 self.kill_ring.clone()
1180 }
1181 ));
1182 output.push_str("\n--- Stats ---\n");
1183 output.push_str(&format!(
1184 "Has Column Stats: {}\n",
1185 self.column_stats.is_some()
1186 ));
1187 output.push_str(&format!("Last Visible Rows: {}\n", self.last_visible_rows));
1188 output.push_str(&format!("Last Results Row: {:?}\n", self.last_results_row));
1189 output.push_str(&format!(
1190 "Last Scroll Offset: {:?}\n",
1191 self.last_scroll_offset
1192 ));
1193 output.push_str("\n=== END BUFFER DEBUG ===\n");
1194 output
1195 }
1196
1197 fn get_input_text(&self) -> String {
1199 self.input_manager.get_text()
1200 }
1201
1202 fn set_input_text(&mut self, text: String) {
1203 self.input_manager.set_text(text.clone());
1204 self.input = Input::new(text.clone()).with_cursor(text.len());
1206 }
1207
1208 fn handle_input_key(&mut self, event: KeyEvent) -> bool {
1209 let result = self.input_manager.handle_key_event(event);
1210 self.sync_from_input_manager();
1212 result
1213 }
1214
1215 fn switch_input_mode(&mut self, _multiline: bool) {
1216 let current_text = self.input_manager.get_text();
1217 let cursor_pos = self.input_manager.get_cursor_position();
1218
1219 self.edit_mode = EditMode::SingleLine;
1221 self.input_manager = create_single_line(current_text.clone());
1222 self.input =
1224 Input::new(current_text.clone()).with_cursor(cursor_pos.min(current_text.len()));
1225
1226 self.input_manager.set_cursor_position(cursor_pos);
1228 }
1229
1230 fn get_input_cursor_position(&self) -> usize {
1231 self.input_manager.get_cursor_position()
1232 }
1233
1234 fn set_input_cursor_position(&mut self, position: usize) {
1235 self.input_manager.set_cursor_position(position);
1236 if self.edit_mode == EditMode::SingleLine {
1238 let text = self.input.value().to_string();
1239 self.input = Input::new(text).with_cursor(position);
1240 }
1241 }
1242
1243 fn is_input_multiline(&self) -> bool {
1244 self.input_manager.is_multiline()
1245 }
1246
1247 fn navigate_history_up(&mut self, history: &[String]) -> bool {
1249 self.input_manager.set_history(history.to_vec());
1251 let navigated = self.input_manager.history_previous();
1252 if navigated {
1253 self.sync_from_input_manager();
1255 }
1256 navigated
1257 }
1258
1259 fn navigate_history_down(&mut self, history: &[String]) -> bool {
1260 self.input_manager.set_history(history.to_vec());
1262 let navigated = self.input_manager.history_next();
1263 if navigated {
1264 self.sync_from_input_manager();
1266 }
1267 navigated
1268 }
1269
1270 fn reset_history_navigation(&mut self) {
1271 self.input_manager.reset_history_position();
1272 }
1273
1274 fn clear_results(&mut self) {
1276 self.datatable = None;
1277 self.table_state.select(None);
1279 self.last_results_row = None;
1280 self.view_state.scroll_offset = (0, 0);
1281 self.last_scroll_offset = (0, 0);
1282 self.column_widths.clear();
1283 self.status_message = "Results cleared".to_string();
1284 self.filter_state.active = false;
1286 self.filter_state.pattern.clear();
1287 self.search_state.pattern.clear();
1288 self.search_state.matches.clear();
1289 self.search_state.current_match = None;
1290 }
1291}
1292
1293impl Buffer {
1294 pub fn new(id: usize) -> Self {
1296 Self {
1297 id,
1298 file_path: None,
1299 name: format!("[Buffer {}]", id),
1300 modified: false,
1301
1302 datatable: None,
1304 original_source: None,
1305 dataview: None,
1306
1307 mode: AppMode::Command,
1308 edit_mode: EditMode::SingleLine,
1309 input: Input::default(),
1310 input_manager: create_single_line(String::new()),
1311 table_state: TableState::default(),
1312 last_results_row: None,
1313 last_scroll_offset: (0, 0),
1314
1315 last_query: String::new(),
1316 status_message: String::new(),
1317
1318 sort_state: SortState {
1319 column: None,
1320 order: SortOrder::None,
1321 },
1322 filter_state: FilterState::default(),
1323 fuzzy_filter_state: FuzzyFilterState::default(),
1324 search_state: SearchState::default(),
1325 column_stats: None,
1327
1328 view_state: ViewState::default(),
1329 column_widths: Vec::new(),
1330 compact_mode: false,
1331 show_row_numbers: false,
1332 case_insensitive: false,
1333
1334 undo_stack: Vec::new(),
1335 redo_stack: Vec::new(),
1336 kill_ring: String::new(),
1337 last_visible_rows: 30,
1338 last_query_source: None,
1339
1340 highlighted_text_cache: None,
1341 last_highlighted_text: String::new(),
1342 saved_input_state: None,
1343 }
1344 }
1345
1346 pub fn from_csv(
1348 id: usize,
1349 path: PathBuf,
1350 _csv_client: CsvApiClient, _table_name: String, ) -> Self {
1353 let name = path
1354 .file_name()
1355 .and_then(|n| n.to_str())
1356 .unwrap_or("unknown.csv")
1357 .to_string();
1358
1359 let mut buffer = Self::new(id);
1360 buffer.file_path = Some(path);
1361 buffer.name = name;
1362 buffer
1365 }
1366
1367 pub fn from_json(
1369 id: usize,
1370 path: PathBuf,
1371 _csv_client: CsvApiClient, _table_name: String, ) -> Self {
1374 let name = path
1375 .file_name()
1376 .and_then(|n| n.to_str())
1377 .unwrap_or("unknown.json")
1378 .to_string();
1379
1380 let mut buffer = Self::new(id);
1381 buffer.file_path = Some(path);
1382 buffer.name = name;
1383 buffer
1386 }
1387
1388 pub fn display_name(&self) -> String {
1390 if self.modified {
1391 format!("{}*", self.name)
1392 } else {
1393 self.name.clone()
1394 }
1395 }
1396
1397 pub fn short_name(&self, max_len: usize) -> String {
1399 let display = self.display_name();
1400 if display.len() <= max_len {
1401 display
1402 } else {
1403 format!("{}...", &display[..max_len.saturating_sub(3)])
1404 }
1405 }
1406
1407 pub fn has_file(&self, path: &PathBuf) -> bool {
1409 self.file_path.as_ref() == Some(path)
1410 }
1411
1412 fn sync_from_input_manager(&mut self) {
1414 let text = self.input_manager.get_text();
1415 let cursor_pos = self.input_manager.get_cursor_position();
1416
1417 let text_len = text.len();
1419 self.input = Input::new(text).with_cursor(cursor_pos.min(text_len));
1420 }
1421
1422 fn sync_to_input_manager(&mut self) {
1424 let _text = self.input.value().to_string();
1426 self.input_manager = create_from_input(self.input.clone());
1427 }
1428
1429 pub fn move_cursor_word_backward(&mut self) {
1435 let text = self.input_manager.get_text();
1436 let cursor_pos = self.input_manager.get_cursor_position();
1437 let new_pos = CursorOperations::find_word_boundary_backward(&text, cursor_pos);
1438 self.input_manager.set_cursor_position(new_pos);
1439 self.sync_from_input_manager();
1440 self.status_message = format!("Moved to position {} (word boundary)", new_pos);
1441 }
1442
1443 pub fn move_cursor_word_forward(&mut self) {
1445 let text = self.input_manager.get_text();
1446 let cursor_pos = self.input_manager.get_cursor_position();
1447 let new_pos = CursorOperations::find_word_boundary_forward(&text, cursor_pos);
1448 self.input_manager.set_cursor_position(new_pos);
1449 self.sync_from_input_manager();
1450 }
1451
1452 pub fn delete_word_backward(&mut self) {
1454 let text = self.input_manager.get_text();
1455 let cursor_pos = self.input_manager.get_cursor_position();
1456 let (new_text, new_cursor) = CursorOperations::delete_word_backward(&text, cursor_pos);
1457
1458 if cursor_pos > new_cursor {
1460 self.kill_ring = text[new_cursor..cursor_pos].to_string();
1461 }
1462
1463 self.input_manager.set_text(new_text);
1464 self.input_manager.set_cursor_position(new_cursor);
1465 self.sync_from_input_manager();
1466 }
1467
1468 pub fn delete_word_forward(&mut self) {
1470 let text = self.input_manager.get_text();
1471 let cursor_pos = self.input_manager.get_cursor_position();
1472 let (new_text, new_cursor) = CursorOperations::delete_word_forward(&text, cursor_pos);
1473
1474 let word_end = CursorOperations::find_word_boundary_forward(&text, cursor_pos);
1476 if word_end > cursor_pos {
1477 self.kill_ring = text[cursor_pos..word_end].to_string();
1478 }
1479
1480 self.input_manager.set_text(new_text);
1481 self.input_manager.set_cursor_position(new_cursor);
1482 self.sync_from_input_manager();
1483 }
1484
1485 pub fn kill_line(&mut self) {
1487 let text = self.input_manager.get_text();
1488 let cursor_pos = self.input_manager.get_cursor_position();
1489 let (new_text, killed) = CursorOperations::kill_line(&text, cursor_pos);
1490
1491 self.kill_ring = killed;
1492 self.input_manager.set_text(new_text);
1493 self.sync_from_input_manager();
1494 }
1495
1496 pub fn kill_line_backward(&mut self) {
1498 let text = self.input_manager.get_text();
1499 let cursor_pos = self.input_manager.get_cursor_position();
1500 let (new_text, killed, new_cursor) =
1501 CursorOperations::kill_line_backward(&text, cursor_pos);
1502
1503 self.kill_ring = killed;
1504 self.input_manager.set_text(new_text);
1505 self.input_manager.set_cursor_position(new_cursor);
1506 self.sync_from_input_manager();
1507 }
1508
1509 pub fn jump_to_prev_token(&mut self) {
1511 let text = self.input_manager.get_text();
1512 let cursor_pos = self.input_manager.get_cursor_position();
1513 let new_pos = CursorOperations::jump_to_prev_token(&text, cursor_pos);
1514 self.input_manager.set_cursor_position(new_pos);
1515 self.sync_from_input_manager();
1516 }
1517
1518 pub fn jump_to_next_token(&mut self) {
1520 let text = self.input_manager.get_text();
1521 let cursor_pos = self.input_manager.get_cursor_position();
1522 let new_pos = CursorOperations::jump_to_next_token(&text, cursor_pos);
1523 self.input_manager.set_cursor_position(new_pos);
1524 self.sync_from_input_manager();
1525 }
1526
1527 pub fn yank(&mut self) {
1529 if !self.kill_ring.is_empty() {
1530 self.save_state_for_undo();
1531
1532 let text = self.input_manager.get_text();
1533 let cursor_pos = self.input_manager.get_cursor_position();
1534
1535 let before = text.chars().take(cursor_pos).collect::<String>();
1537 let after = text.chars().skip(cursor_pos).collect::<String>();
1538 let new_text = format!("{}{}{}", before, &self.kill_ring, after);
1539 let new_cursor = cursor_pos + self.kill_ring.len();
1540
1541 self.input_manager.set_text(new_text);
1542 self.input_manager.set_cursor_position(new_cursor);
1543 self.sync_from_input_manager();
1544 }
1545 }
1546
1547 pub fn expand_asterisk(&mut self, parser: &HybridParser) -> bool {
1549 let query = self.input_manager.get_text();
1550 let query_upper = query.to_uppercase();
1551
1552 if let Some(select_pos) = query_upper.find("SELECT") {
1554 if let Some(star_pos) = query_upper[select_pos..].find("*") {
1555 let star_abs_pos = select_pos + star_pos;
1556
1557 if let Some(from_rel_pos) = query_upper[star_abs_pos..].find("FROM") {
1559 let from_abs_pos = star_abs_pos + from_rel_pos;
1560
1561 let after_from = &query[from_abs_pos + 4..].trim_start();
1563 let table_name = after_from
1564 .split_whitespace()
1565 .next()
1566 .unwrap_or("")
1567 .trim_end_matches(|c: char| !c.is_alphanumeric() && c != '_');
1568
1569 if !table_name.is_empty() {
1570 let columns = parser.get_table_columns(table_name);
1572
1573 if !columns.is_empty() {
1574 let columns_str = columns.join(", ");
1576
1577 let before_star = &query[..star_abs_pos];
1579 let after_star = &query[star_abs_pos + 1..];
1580 let new_query = format!("{}{}{}", before_star, columns_str, after_star);
1581
1582 self.input_manager.set_text(new_query.clone());
1584 self.input_manager.set_cursor_position(new_query.len());
1585 self.sync_from_input_manager();
1586
1587 self.status_message =
1588 format!("Expanded * to {} columns", columns.len());
1589 return true;
1590 } else {
1591 self.status_message =
1592 format!("No columns found for table '{}'", table_name);
1593 }
1594 }
1595 }
1596 }
1597 }
1598
1599 self.status_message = "No SELECT * pattern found to expand".to_string();
1600 false
1601 }
1602
1603 pub fn expand_asterisk_visible(&mut self) -> bool {
1605 let query = self.input_manager.get_text();
1606 let query_upper = query.to_uppercase();
1607
1608 if let Some(select_pos) = query_upper.find("SELECT") {
1610 if let Some(star_pos) = query_upper[select_pos..].find("*") {
1611 let star_abs_pos = select_pos + star_pos;
1612
1613 if let Some(dataview) = &self.dataview {
1615 let visible_columns = dataview.get_display_column_names();
1616
1617 if !visible_columns.is_empty() {
1618 let columns_str = visible_columns.join(", ");
1620
1621 let before_star = &query[..star_abs_pos];
1623 let after_star = &query[star_abs_pos + 1..];
1624 let new_query = format!("{}{}{}", before_star, columns_str, after_star);
1625
1626 self.input_manager.set_text(new_query.clone());
1628 self.input_manager.set_cursor_position(new_query.len());
1629 self.sync_from_input_manager();
1630
1631 self.status_message =
1632 format!("Expanded * to {} visible columns", visible_columns.len());
1633 return true;
1634 } else {
1635 self.status_message = "No visible columns available".to_string();
1636 }
1637 } else {
1638 self.status_message = "No data loaded to expand from".to_string();
1639 }
1640 }
1641 }
1642
1643 self.status_message = "No SELECT * pattern found to expand".to_string();
1644 false
1645 }
1646}
1647
1648impl Clone for Buffer {
1650 fn clone(&self) -> Self {
1651 let input_manager = create_from_input(self.input.clone());
1653
1654 Self {
1655 id: self.id,
1656 file_path: self.file_path.clone(),
1657 name: self.name.clone(),
1658 modified: self.modified,
1659 datatable: self.datatable.clone(),
1661 original_source: self.original_source.clone(),
1662 dataview: self.dataview.clone(),
1663 mode: self.mode.clone(),
1664 edit_mode: self.edit_mode.clone(),
1665 input: self.input.clone(),
1666 input_manager,
1667 table_state: self.table_state.clone(),
1668 last_results_row: self.last_results_row,
1669 last_scroll_offset: self.last_scroll_offset,
1670 last_query: self.last_query.clone(),
1671 status_message: self.status_message.clone(),
1672 sort_state: self.sort_state.clone(),
1673 filter_state: self.filter_state.clone(),
1674 fuzzy_filter_state: self.fuzzy_filter_state.clone(),
1675 search_state: self.search_state.clone(),
1676 column_stats: self.column_stats.clone(),
1678 view_state: self.view_state.clone(),
1679 column_widths: self.column_widths.clone(),
1680 compact_mode: self.compact_mode,
1681 show_row_numbers: self.show_row_numbers,
1682 case_insensitive: self.case_insensitive,
1683 undo_stack: self.undo_stack.clone(),
1684 redo_stack: self.redo_stack.clone(),
1685 kill_ring: self.kill_ring.clone(),
1686 last_visible_rows: self.last_visible_rows,
1687 last_query_source: self.last_query_source.clone(),
1688 highlighted_text_cache: self.highlighted_text_cache.clone(),
1689 last_highlighted_text: self.last_highlighted_text.clone(),
1690 saved_input_state: self.saved_input_state.clone(),
1691 }
1692 }
1693}
1694
1695pub struct BufferManager {
1697 buffers: Vec<Buffer>,
1698 current_buffer_index: usize,
1699 next_buffer_id: usize,
1700}
1701
1702impl BufferManager {
1703 pub fn new() -> Self {
1704 Self {
1705 buffers: Vec::new(),
1706 current_buffer_index: 0,
1707 next_buffer_id: 1,
1708 }
1709 }
1710
1711 pub fn add_buffer(&mut self, mut buffer: Buffer) -> usize {
1713 buffer.id = self.next_buffer_id;
1714 self.next_buffer_id += 1;
1715
1716 let index = self.buffers.len();
1717 self.buffers.push(buffer);
1718 self.current_buffer_index = index;
1719 index
1720 }
1721
1722 pub fn current(&self) -> Option<&Buffer> {
1724 self.buffers.get(self.current_buffer_index)
1725 }
1726
1727 pub fn current_mut(&mut self) -> Option<&mut Buffer> {
1729 self.buffers.get_mut(self.current_buffer_index)
1730 }
1731
1732 pub fn next_buffer(&mut self) {
1734 if !self.buffers.is_empty() {
1735 self.current_buffer_index = (self.current_buffer_index + 1) % self.buffers.len();
1736 }
1737 }
1738
1739 pub fn prev_buffer(&mut self) {
1741 if !self.buffers.is_empty() {
1742 if self.current_buffer_index == 0 {
1743 self.current_buffer_index = self.buffers.len() - 1;
1744 } else {
1745 self.current_buffer_index -= 1;
1746 }
1747 }
1748 }
1749
1750 pub fn switch_to(&mut self, index: usize) {
1752 if index < self.buffers.len() {
1753 self.current_buffer_index = index;
1754 }
1755 }
1756
1757 pub fn close_current(&mut self) -> bool {
1759 if self.buffers.len() <= 1 {
1760 return false; }
1762
1763 self.buffers.remove(self.current_buffer_index);
1764
1765 if self.current_buffer_index >= self.buffers.len() {
1767 self.current_buffer_index = self.buffers.len() - 1;
1768 }
1769
1770 true
1771 }
1772
1773 pub fn find_by_path(&self, path: &PathBuf) -> Option<usize> {
1775 self.buffers.iter().position(|b| b.has_file(path))
1776 }
1777
1778 pub fn all_buffers(&self) -> &[Buffer] {
1780 &self.buffers
1781 }
1782
1783 pub fn current_index(&self) -> usize {
1785 self.current_buffer_index
1786 }
1787
1788 pub fn has_multiple(&self) -> bool {
1790 self.buffers.len() > 1
1791 }
1792
1793 pub fn clear_all(&mut self) {
1795 self.buffers.clear();
1796 self.current_buffer_index = 0;
1797 }
1798}