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, Default)]
57pub struct FilterState {
58 pub pattern: String,
59 pub regex: Option<Regex>,
60 pub active: bool,
61}
62
63#[derive(Default)]
64pub struct FuzzyFilterState {
65 pub pattern: String,
66 pub active: bool,
67 pub matcher: SkimMatcherV2,
68 pub filtered_indices: Vec<usize>,
69}
70
71impl std::fmt::Debug for FuzzyFilterState {
72 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73 f.debug_struct("FuzzyFilterState")
74 .field("pattern", &self.pattern)
75 .field("active", &self.active)
76 .field("matcher", &"SkimMatcherV2")
77 .field("filtered_indices", &self.filtered_indices)
78 .finish()
79 }
80}
81
82impl Clone for FuzzyFilterState {
83 fn clone(&self) -> Self {
84 Self {
85 pattern: self.pattern.clone(),
86 active: self.active,
87 matcher: SkimMatcherV2::default(), filtered_indices: self.filtered_indices.clone(),
89 }
90 }
91}
92
93#[derive(Clone, Debug, Default)]
94pub struct SearchState {
95 pub pattern: String,
96 pub current_match: Option<(usize, usize)>,
97 pub matches: Vec<(usize, usize)>,
98 pub match_index: usize,
99}
100
101#[derive(Clone, Debug, PartialEq)]
104pub enum SelectionMode {
105 Row,
106 Cell,
107 Column,
108}
109
110#[derive(Clone, Debug)]
113pub struct ViewState {
114 pub crosshair_row: usize,
116 pub crosshair_col: usize,
117 pub scroll_offset: (usize, usize),
118
119 pub selection_mode: SelectionMode,
121 pub selected_cells: Vec<(usize, usize)>,
122 pub selection_anchor: Option<(usize, usize)>,
123
124 pub viewport_lock: bool,
126 pub cursor_lock: bool,
127
128 pub navigation_history: Vec<(usize, usize)>,
130 pub history_index: usize,
131
132 pub viewport_rows: usize,
134 pub viewport_columns: usize,
135 pub total_rows: usize,
136 pub total_columns: usize,
137}
138
139impl Default for ViewState {
140 fn default() -> Self {
141 Self {
142 crosshair_row: 0,
143 crosshair_col: 0,
144 scroll_offset: (0, 0),
145 selection_mode: SelectionMode::Row,
146 selected_cells: Vec::new(),
147 selection_anchor: None,
148 viewport_lock: false,
149 cursor_lock: false,
150 navigation_history: Vec::new(),
151 history_index: 0,
152 viewport_rows: 0,
153 viewport_columns: 0,
154 total_rows: 0,
155 total_columns: 0,
156 }
157 }
158}
159
160#[derive(Clone, Debug)]
161pub enum ColumnType {
162 String,
163 Numeric,
164 Mixed,
165}
166
167#[derive(Clone)]
168pub struct ColumnStatistics {
169 pub column_name: String,
170 pub column_type: ColumnType,
171 pub total_count: usize,
173 pub null_count: usize,
174 pub unique_count: usize,
175 pub frequency_map: Option<BTreeMap<String, usize>>,
177 pub min: Option<f64>,
179 pub max: Option<f64>,
180 pub sum: Option<f64>,
181 pub mean: Option<f64>,
182 pub median: Option<f64>,
183}
184
185pub trait BufferAPI: Send + Sync {
193 fn get_id(&self) -> usize;
195 fn get_query(&self) -> String;
197 fn set_query(&mut self, query: String);
198 fn get_last_query(&self) -> String;
200 fn set_last_query(&mut self, query: String);
201
202 fn get_datatable(&self) -> Option<&DataTable>;
204 fn get_datatable_mut(&mut self) -> Option<&mut DataTable>;
205 fn has_datatable(&self) -> bool;
206 fn set_datatable(&mut self, datatable: Option<Arc<DataTable>>);
207 fn get_original_source(&self) -> Option<&DataTable>;
208 fn set_results_as_datatable(&mut self, response: Option<QueryResponse>) -> Result<(), String>;
210
211 fn get_dataview(&self) -> Option<&DataView>;
213 fn get_dataview_mut(&mut self) -> Option<&mut DataView>;
214 fn set_dataview(&mut self, dataview: Option<DataView>);
215 fn has_dataview(&self) -> bool;
216
217 fn get_mode(&self) -> AppMode;
219 fn set_mode(&mut self, mode: AppMode);
220 fn get_edit_mode(&self) -> EditMode;
221 fn set_edit_mode(&mut self, mode: EditMode);
222 fn get_status_message(&self) -> String;
223 fn set_status_message(&mut self, message: String);
224
225 fn get_selected_row(&self) -> Option<usize>;
227 fn set_selected_row(&mut self, row: Option<usize>);
228 fn get_current_column(&self) -> usize;
229 fn set_current_column(&mut self, col: usize);
230 fn get_scroll_offset(&self) -> (usize, usize);
231 fn set_scroll_offset(&mut self, offset: (usize, usize));
232 fn get_last_results_row(&self) -> Option<usize>;
233 fn set_last_results_row(&mut self, row: Option<usize>);
234 fn get_last_scroll_offset(&self) -> (usize, usize);
235 fn set_last_scroll_offset(&mut self, offset: (usize, usize));
236
237 fn get_filter_pattern(&self) -> String;
239 fn set_filter_pattern(&mut self, pattern: String);
240 fn is_filter_active(&self) -> bool;
241 fn set_filter_active(&mut self, active: bool);
242 fn get_fuzzy_filter_pattern(&self) -> String;
246 fn set_fuzzy_filter_pattern(&mut self, pattern: String);
247 fn is_fuzzy_filter_active(&self) -> bool;
248 fn set_fuzzy_filter_active(&mut self, active: bool);
249 fn get_fuzzy_filter_indices(&self) -> &Vec<usize>;
250 fn set_fuzzy_filter_indices(&mut self, indices: Vec<usize>);
251 fn clear_fuzzy_filter(&mut self);
252
253 fn get_search_pattern(&self) -> String;
255 fn set_search_pattern(&mut self, pattern: String);
256 fn get_search_matches(&self) -> Vec<(usize, usize)>;
257 fn set_search_matches(&mut self, matches: Vec<(usize, usize)>);
258 fn get_current_match(&self) -> Option<(usize, usize)>;
259 fn set_current_match(&mut self, match_pos: Option<(usize, usize)>);
260 fn get_search_match_index(&self) -> usize;
261 fn set_search_match_index(&mut self, index: usize);
262 fn clear_search_state(&mut self);
263
264 fn get_column_stats(&self) -> Option<&ColumnStatistics>;
268 fn set_column_stats(&mut self, stats: Option<ColumnStatistics>);
269
270 fn get_sort_column(&self) -> Option<usize>;
272 fn set_sort_column(&mut self, column: Option<usize>);
273 fn get_sort_order(&self) -> SortOrder;
274 fn set_sort_order(&mut self, order: SortOrder);
275
276 fn is_compact_mode(&self) -> bool;
278 fn set_compact_mode(&mut self, compact: bool);
279 fn is_show_row_numbers(&self) -> bool;
280 fn set_show_row_numbers(&mut self, show: bool);
281 fn is_viewport_lock(&self) -> bool;
282 fn set_viewport_lock(&mut self, locked: bool);
283 fn get_viewport_lock_row(&self) -> Option<usize>;
284 fn set_viewport_lock_row(&mut self, row: Option<usize>);
285 fn get_column_widths(&self) -> &Vec<u16>;
288 fn set_column_widths(&mut self, widths: Vec<u16>);
289 fn is_case_insensitive(&self) -> bool;
290 fn set_case_insensitive(&mut self, case_insensitive: bool);
291
292 fn get_name(&self) -> String;
294 fn set_name(&mut self, name: String);
295 fn get_file_path(&self) -> Option<&PathBuf>;
296 fn set_file_path(&mut self, path: Option<String>);
297 fn is_modified(&self) -> bool;
298 fn set_modified(&mut self, modified: bool);
299 fn get_last_query_source(&self) -> Option<String>;
300 fn set_last_query_source(&mut self, source: Option<String>);
301
302 fn get_input_value(&self) -> String;
307 fn set_input_value(&mut self, value: String);
308 fn get_input_cursor(&self) -> usize;
309 fn set_input_cursor(&mut self, pos: usize);
310
311 fn apply_filter(&mut self) -> Result<()>;
313 fn apply_sort(&mut self) -> Result<()>;
314 fn search(&mut self) -> Result<()>;
315 fn clear_filters(&mut self);
316 fn get_row_count(&self) -> usize;
317 fn get_column_count(&self) -> usize;
318 fn get_column_names(&self) -> Vec<String>;
319
320 fn get_undo_stack(&self) -> &Vec<(String, usize)>;
322 fn push_undo(&mut self, state: (String, usize));
323 fn pop_undo(&mut self) -> Option<(String, usize)>;
324 fn get_redo_stack(&self) -> &Vec<(String, usize)>;
325 fn push_redo(&mut self, state: (String, usize));
326 fn pop_redo(&mut self) -> Option<(String, usize)>;
327 fn clear_redo(&mut self);
328 fn get_kill_ring(&self) -> String;
329 fn set_kill_ring(&mut self, text: String);
330 fn is_kill_ring_empty(&self) -> bool;
331
332 fn perform_undo(&mut self) -> bool;
334 fn perform_redo(&mut self) -> bool;
335 fn save_state_for_undo(&mut self);
336
337 fn get_last_visible_rows(&self) -> usize;
339 fn set_last_visible_rows(&mut self, rows: usize);
340
341 fn debug_dump(&self) -> String;
343
344 fn get_input_text(&self) -> String;
346 fn set_input_text(&mut self, text: String);
347 fn handle_input_key(&mut self, event: KeyEvent) -> bool;
348 fn switch_input_mode(&mut self, multiline: bool);
349 fn get_input_cursor_position(&self) -> usize;
350 fn set_input_cursor_position(&mut self, position: usize);
351 fn is_input_multiline(&self) -> bool;
352
353 fn navigate_history_up(&mut self, history: &[String]) -> bool;
355 fn navigate_history_down(&mut self, history: &[String]) -> bool;
356 fn reset_history_navigation(&mut self);
357
358 fn clear_results(&mut self);
360}
361
362pub struct Buffer {
364 pub id: usize,
366
367 pub file_path: Option<PathBuf>,
369
370 pub name: String,
372
373 pub modified: bool,
375
376 pub datatable: Option<Arc<DataTable>>,
378 pub original_source: Option<Arc<DataTable>>,
380 pub dataview: Option<DataView>,
382
383 pub mode: AppMode,
385 pub edit_mode: EditMode,
386 pub input: Input, pub input_manager: Box<dyn InputManager>, pub table_state: TableState,
389 pub last_results_row: Option<usize>,
390 pub last_scroll_offset: (usize, usize),
391
392 pub last_query: String,
394 pub status_message: String,
395
396 pub sort_state: SortState,
398 pub filter_state: FilterState,
399 pub fuzzy_filter_state: FuzzyFilterState,
400 pub search_state: SearchState,
401
402 pub column_stats: Option<ColumnStatistics>,
403
404 pub view_state: ViewState,
406
407 pub column_widths: Vec<u16>,
409 pub compact_mode: bool,
410 pub show_row_numbers: bool,
411 pub case_insensitive: bool,
412
413 pub undo_stack: Vec<(String, usize)>,
415 pub redo_stack: Vec<(String, usize)>,
416 pub kill_ring: String,
417 pub last_visible_rows: usize,
418 pub last_query_source: Option<String>,
419
420 pub highlighted_text_cache: Option<Vec<(String, Color)>>, pub last_highlighted_text: String, pub saved_input_state: Option<(String, usize)>, }
427
428impl BufferAPI for Buffer {
430 fn get_id(&self) -> usize {
432 self.id
433 }
434
435 fn get_query(&self) -> String {
437 self.input_manager.get_text()
439 }
440
441 fn set_query(&mut self, query: String) {
442 self.input_manager.set_text(query.clone());
444 self.input = Input::new(query.clone()).with_cursor(query.len());
445 }
446
447 fn get_last_query(&self) -> String {
450 self.last_query.clone()
451 }
452
453 fn set_last_query(&mut self, query: String) {
454 self.last_query = query;
455 }
456
457 fn get_datatable(&self) -> Option<&DataTable> {
459 self.datatable.as_ref().map(std::convert::AsRef::as_ref)
460 }
461
462 fn get_datatable_mut(&mut self) -> Option<&mut DataTable> {
463 None
466 }
467
468 fn has_datatable(&self) -> bool {
469 self.datatable.is_some()
470 }
471
472 fn get_original_source(&self) -> Option<&DataTable> {
473 self.original_source
474 .as_ref()
475 .map(std::convert::AsRef::as_ref)
476 }
477
478 fn set_datatable(&mut self, datatable: Option<Arc<DataTable>>) {
479 debug!(
480 "V50: Setting DataTable with {} rows, {} columns",
481 datatable.as_ref().map_or(0, |d| d.row_count()),
482 datatable.as_ref().map_or(0, |d| d.column_count())
483 );
484
485 if let Some(ref current) = self.datatable {
487 debug!(
488 "V50: Current DataTable has {} columns: {:?}",
489 current.column_count(),
490 current.column_names()
491 );
492 }
493
494 if let Some(ref original) = self.original_source {
495 debug!(
496 "V50: Original source has {} columns: {:?}",
497 original.column_count(),
498 original.column_names()
499 );
500 }
501
502 if datatable.is_some() && self.original_source.is_none() {
505 self.original_source = datatable.clone();
506 debug!(
507 "V50: Preserving original source DataTable with {} columns",
508 datatable.as_ref().map_or(0, |d| d.column_count())
509 );
510 }
511
512 if let Some(dt) = &datatable {
515 let mut view = crate::data::data_view::DataView::new(dt.clone());
517
518 if let Some(old_view) = &self.dataview {
520 let old_all_cols = old_view.source().column_names();
522 let old_visible_cols = old_view.column_names();
523
524 for col_name in &old_all_cols {
525 if !old_visible_cols.contains(col_name) {
526 view.hide_column_by_name(col_name);
528 }
529 }
530 }
531
532 view.shrink_to_fit();
534
535 self.dataview = Some(view);
537 } else {
538 self.dataview = None;
539 }
540
541 if let Some(ref original) = self.original_source {
544 if let Some(ref new_dt) = datatable {
545 if new_dt.column_count() < original.column_count() {
546 debug!(
547 "V50: WARNING - Attempted to replace datatable with fewer columns ({} < {}). Keeping original.",
548 new_dt.column_count(),
549 original.column_count()
550 );
551 return;
553 }
554 }
555 }
556
557 self.datatable = datatable;
558 }
559
560 fn set_results_as_datatable(&mut self, response: Option<QueryResponse>) -> Result<(), String> {
561 if let Some(ref resp) = response {
562 debug!("V50: Converting QueryResponse to DataTable");
563 let table_name = resp.table.as_deref().unwrap_or("data");
564 match DataTable::from_query_response(resp, table_name) {
565 Ok(datatable) => {
566 debug!(
567 "V50: Stored DataTable with {} rows, {} columns",
568 datatable.row_count(),
569 datatable.column_count()
570 );
571 self.datatable = Some(Arc::new(datatable));
572 Ok(())
573 }
574 Err(e) => {
575 let err_msg = format!("V50: Failed to create DataTable: {e}");
576 debug!("{}", err_msg);
577 self.datatable = None;
578 Err(err_msg)
579 }
580 }
581 } else {
582 self.datatable = None;
583 Ok(())
584 }
585 }
586
587 fn get_dataview(&self) -> Option<&DataView> {
589 self.dataview.as_ref()
590 }
591 fn get_dataview_mut(&mut self) -> Option<&mut DataView> {
592 self.dataview.as_mut()
593 }
594 fn set_dataview(&mut self, dataview: Option<DataView>) {
595 debug!(
596 "V51: Setting DataView with {} rows",
597 dataview
598 .as_ref()
599 .map_or(0, super::data::data_view::DataView::row_count)
600 );
601 self.dataview = dataview;
602 }
603 fn has_dataview(&self) -> bool {
604 self.dataview.is_some()
605 }
606
607 fn get_mode(&self) -> AppMode {
609 self.mode.clone()
610 }
611
612 fn set_mode(&mut self, mode: AppMode) {
613 self.mode = mode;
614 }
615
616 fn get_edit_mode(&self) -> EditMode {
617 self.edit_mode.clone()
618 }
619
620 fn set_edit_mode(&mut self, mode: EditMode) {
621 self.edit_mode = mode;
622 }
623
624 fn get_status_message(&self) -> String {
625 self.status_message.clone()
626 }
627
628 fn set_status_message(&mut self, message: String) {
629 self.status_message = message;
630 }
631
632 fn get_selected_row(&self) -> Option<usize> {
634 self.table_state.selected()
637 }
638
639 fn set_selected_row(&mut self, row: Option<usize>) {
640 if let Some(r) = row {
641 self.view_state.crosshair_row = r;
642 self.table_state.select(Some(r));
644 } else {
645 self.view_state.crosshair_row = 0;
647 self.table_state.select(None);
648 }
649 }
650
651 fn get_current_column(&self) -> usize {
652 self.view_state.crosshair_col
653 }
654
655 fn set_current_column(&mut self, col: usize) {
656 self.view_state.crosshair_col = col;
657 }
658
659 fn get_scroll_offset(&self) -> (usize, usize) {
660 self.view_state.scroll_offset
661 }
662
663 fn set_scroll_offset(&mut self, offset: (usize, usize)) {
664 self.view_state.scroll_offset = offset;
665 }
666
667 fn get_last_results_row(&self) -> Option<usize> {
668 self.last_results_row
669 }
670
671 fn set_last_results_row(&mut self, row: Option<usize>) {
672 self.last_results_row = row;
673 }
674
675 fn get_last_scroll_offset(&self) -> (usize, usize) {
676 self.last_scroll_offset
677 }
678
679 fn set_last_scroll_offset(&mut self, offset: (usize, usize)) {
680 self.last_scroll_offset = offset;
681 }
682
683 fn get_filter_pattern(&self) -> String {
685 self.filter_state.pattern.clone()
686 }
687
688 fn set_filter_pattern(&mut self, pattern: String) {
689 self.filter_state.pattern = pattern;
690 }
691
692 fn is_filter_active(&self) -> bool {
693 self.filter_state.active
694 }
695
696 fn set_filter_active(&mut self, active: bool) {
697 self.filter_state.active = active;
698 }
699
700 fn get_fuzzy_filter_pattern(&self) -> String {
704 self.fuzzy_filter_state.pattern.clone()
705 }
706
707 fn set_fuzzy_filter_pattern(&mut self, pattern: String) {
708 self.fuzzy_filter_state.pattern = pattern;
709 }
710
711 fn is_fuzzy_filter_active(&self) -> bool {
712 self.fuzzy_filter_state.active
713 }
714
715 fn set_fuzzy_filter_active(&mut self, active: bool) {
716 self.fuzzy_filter_state.active = active;
717 }
718
719 fn get_fuzzy_filter_indices(&self) -> &Vec<usize> {
720 &self.fuzzy_filter_state.filtered_indices
721 }
722
723 fn set_fuzzy_filter_indices(&mut self, indices: Vec<usize>) {
724 self.fuzzy_filter_state.filtered_indices = indices;
725 }
726
727 fn clear_fuzzy_filter(&mut self) {
728 self.fuzzy_filter_state.pattern.clear();
729 self.fuzzy_filter_state.active = false;
730 self.fuzzy_filter_state.filtered_indices.clear();
731 }
732
733 fn get_search_pattern(&self) -> String {
735 self.search_state.pattern.clone()
736 }
737
738 fn set_search_pattern(&mut self, pattern: String) {
739 self.search_state.pattern = pattern;
740 }
741
742 fn get_search_matches(&self) -> Vec<(usize, usize)> {
743 self.search_state.matches.clone()
744 }
745
746 fn set_search_matches(&mut self, matches: Vec<(usize, usize)>) {
747 self.search_state.matches = matches;
748 }
749
750 fn get_current_match(&self) -> Option<(usize, usize)> {
751 self.search_state.current_match
752 }
753
754 fn set_current_match(&mut self, match_pos: Option<(usize, usize)>) {
755 self.search_state.current_match = match_pos;
756 }
757
758 fn get_search_match_index(&self) -> usize {
759 self.search_state.match_index
760 }
761
762 fn set_search_match_index(&mut self, index: usize) {
763 self.search_state.match_index = index;
764 }
765
766 fn clear_search_state(&mut self) {
767 self.search_state.pattern.clear();
768 self.search_state.matches.clear();
769 self.search_state.current_match = None;
770 self.search_state.match_index = 0;
771 }
772
773 fn get_column_stats(&self) -> Option<&ColumnStatistics> {
776 self.column_stats.as_ref()
777 }
778
779 fn set_column_stats(&mut self, stats: Option<ColumnStatistics>) {
780 self.column_stats = stats;
781 }
782
783 fn get_sort_column(&self) -> Option<usize> {
785 self.sort_state.column
786 }
787
788 fn set_sort_column(&mut self, column: Option<usize>) {
789 self.sort_state.column = column;
790 }
791
792 fn get_sort_order(&self) -> SortOrder {
793 self.sort_state.order
794 }
795
796 fn set_sort_order(&mut self, order: SortOrder) {
797 self.sort_state.order = order;
798 }
799
800 fn is_compact_mode(&self) -> bool {
802 self.compact_mode
803 }
804
805 fn set_compact_mode(&mut self, compact: bool) {
806 self.compact_mode = compact;
807 }
808
809 fn is_show_row_numbers(&self) -> bool {
810 self.show_row_numbers
811 }
812
813 fn set_show_row_numbers(&mut self, show: bool) {
814 self.show_row_numbers = show;
815 }
816
817 fn is_viewport_lock(&self) -> bool {
818 self.view_state.viewport_lock
819 }
820
821 fn set_viewport_lock(&mut self, locked: bool) {
822 self.view_state.viewport_lock = locked;
823 }
824
825 fn get_viewport_lock_row(&self) -> Option<usize> {
826 if self.view_state.viewport_lock {
828 Some(self.view_state.crosshair_row)
829 } else {
830 None
831 }
832 }
833
834 fn set_viewport_lock_row(&mut self, row: Option<usize>) {
835 if let Some(r) = row {
837 self.view_state.crosshair_row = r;
838 self.view_state.viewport_lock = true;
839 }
840 }
841
842 fn get_column_widths(&self) -> &Vec<u16> {
843 &self.column_widths
844 }
845
846 fn set_column_widths(&mut self, widths: Vec<u16>) {
847 self.column_widths = widths;
848 }
849
850 fn is_case_insensitive(&self) -> bool {
851 self.case_insensitive
852 }
853
854 fn set_case_insensitive(&mut self, case_insensitive: bool) {
855 self.case_insensitive = case_insensitive;
856 }
857
858 fn get_name(&self) -> String {
860 self.name.clone()
861 }
862
863 fn set_name(&mut self, name: String) {
864 self.name = name;
865 }
866
867 fn get_file_path(&self) -> Option<&PathBuf> {
868 self.file_path.as_ref()
869 }
870
871 fn set_file_path(&mut self, path: Option<String>) {
872 self.file_path = path.map(PathBuf::from);
873 }
874
875 fn is_modified(&self) -> bool {
876 self.modified
877 }
878
879 fn set_modified(&mut self, modified: bool) {
880 self.modified = modified;
881 }
882
883 fn get_last_query_source(&self) -> Option<String> {
884 self.last_query_source.clone()
885 }
886
887 fn set_last_query_source(&mut self, source: Option<String>) {
888 self.last_query_source = source;
889 }
890
891 fn get_input_value(&self) -> String {
893 self.input.value().to_string()
894 }
895
896 fn set_input_value(&mut self, value: String) {
897 let cursor = value.len();
898 self.input = Input::new(value).with_cursor(cursor);
899 }
900
901 fn get_input_cursor(&self) -> usize {
902 self.input.cursor()
903 }
904
905 fn set_input_cursor(&mut self, pos: usize) {
906 let value = self.input.value().to_string();
907 self.input = Input::new(value).with_cursor(pos);
908 }
909
910 fn apply_filter(&mut self) -> Result<()> {
912 Ok(())
914 }
915
916 fn apply_sort(&mut self) -> Result<()> {
917 Ok(())
919 }
920
921 fn search(&mut self) -> Result<()> {
922 Ok(())
924 }
925
926 fn clear_filters(&mut self) {
927 self.filter_state.active = false;
928 self.filter_state.pattern.clear();
929 self.fuzzy_filter_state.active = false;
930 self.fuzzy_filter_state.pattern.clear();
931 }
933
934 fn get_row_count(&self) -> usize {
935 if let Some(dataview) = &self.dataview {
936 dataview.row_count()
937 } else if let Some(datatable) = &self.datatable {
938 datatable.row_count()
939 } else {
940 0
941 }
942 }
943
944 fn get_column_count(&self) -> usize {
945 if let Some(datatable) = &self.datatable {
946 return datatable.column_count();
947 }
948 0
949 }
950
951 fn get_column_names(&self) -> Vec<String> {
952 if let Some(datatable) = &self.datatable {
953 return datatable.column_names();
954 }
955 Vec::new()
956 }
957
958 fn get_undo_stack(&self) -> &Vec<(String, usize)> {
960 &self.undo_stack
961 }
962
963 fn push_undo(&mut self, state: (String, usize)) {
964 self.undo_stack.push(state);
965 if self.undo_stack.len() > 100 {
966 self.undo_stack.remove(0);
967 }
968 }
969
970 fn pop_undo(&mut self) -> Option<(String, usize)> {
971 self.undo_stack.pop()
972 }
973
974 fn get_redo_stack(&self) -> &Vec<(String, usize)> {
975 &self.redo_stack
976 }
977
978 fn push_redo(&mut self, state: (String, usize)) {
979 self.redo_stack.push(state);
980 }
981
982 fn pop_redo(&mut self) -> Option<(String, usize)> {
983 self.redo_stack.pop()
984 }
985
986 fn clear_redo(&mut self) {
987 self.redo_stack.clear();
988 }
989
990 fn perform_undo(&mut self) -> bool {
991 if let Some((prev_text, prev_cursor)) = self.pop_undo() {
992 let current_state = (self.get_input_text(), self.get_input_cursor_position());
994 self.push_redo(current_state);
995
996 self.set_input_text(prev_text);
998 self.set_input_cursor_position(prev_cursor);
999 true
1000 } else {
1001 false
1002 }
1003 }
1004
1005 fn perform_redo(&mut self) -> bool {
1006 if let Some((next_text, next_cursor)) = self.pop_redo() {
1007 let current_state = (self.get_input_text(), self.get_input_cursor_position());
1009 self.push_undo(current_state);
1010
1011 self.set_input_text(next_text);
1013 self.set_input_cursor_position(next_cursor);
1014 true
1015 } else {
1016 false
1017 }
1018 }
1019
1020 fn save_state_for_undo(&mut self) {
1021 let current_state = (self.get_input_text(), self.get_input_cursor_position());
1022 self.push_undo(current_state);
1023 self.clear_redo();
1024 }
1025
1026 fn get_kill_ring(&self) -> String {
1027 self.kill_ring.clone()
1028 }
1029
1030 fn set_kill_ring(&mut self, text: String) {
1031 self.kill_ring = text;
1032 }
1033
1034 fn is_kill_ring_empty(&self) -> bool {
1035 self.kill_ring.is_empty()
1036 }
1037
1038 fn get_last_visible_rows(&self) -> usize {
1040 self.last_visible_rows
1041 }
1042
1043 fn set_last_visible_rows(&mut self, rows: usize) {
1044 self.last_visible_rows = rows;
1045 }
1046
1047 fn debug_dump(&self) -> String {
1048 let mut output = String::new();
1049 output.push_str("=== BUFFER DEBUG DUMP ===\n");
1050 output.push_str(&format!("Buffer ID: {}\n", self.id));
1051 output.push_str(&format!("Name: {}\n", self.name));
1052 output.push_str(&format!("File Path: {:?}\n", self.file_path));
1053 output.push_str(&format!("Modified: {}\n", self.modified));
1054 output.push_str("\n--- Modes ---\n");
1055 output.push_str(&format!("App Mode: {:?}\n", self.mode));
1056 output.push_str(&format!("Edit Mode: {:?}\n", self.edit_mode));
1057 output.push_str("\n--- Query State ---\n");
1058 output.push_str(&format!("Current Input: '{}'\n", self.input.value()));
1059 output.push_str(&format!("Input Cursor: {}\n", self.input.cursor()));
1060 output.push_str(&format!("Last Query: '{}'\n", self.last_query));
1061 output.push_str(&format!("Status Message: '{}'\n", self.status_message));
1062 output.push_str(&format!(
1063 "Last Query Source: {:?}\n",
1064 self.last_query_source
1065 ));
1066 output.push_str("\n--- Results ---\n");
1067 output.push_str(&format!("Has DataTable: {}\n", self.datatable.is_some()));
1068 output.push_str(&format!("Row Count: {}\n", self.get_row_count()));
1069 output.push_str(&format!("Column Count: {}\n", self.get_column_count()));
1070 output.push_str(&format!(
1071 "Selected Row: {:?}\n",
1072 self.table_state.selected()
1073 ));
1074 output.push_str(&format!(
1075 "Current Column: {}\n",
1076 self.view_state.crosshair_col
1077 ));
1078 output.push_str(&format!(
1079 "Scroll Offset: {:?}\n",
1080 self.view_state.scroll_offset
1081 ));
1082 output.push_str("\n--- Filtering ---\n");
1083 output.push_str(&format!("Filter Active: {}\n", self.filter_state.active));
1084 output.push_str(&format!(
1085 "Filter Pattern: '{}'\n",
1086 self.filter_state.pattern
1087 ));
1088 output.push_str("Filtering: Handled by DataView\n");
1089 output.push_str(&format!(
1090 "Fuzzy Filter Active: {}\n",
1091 self.fuzzy_filter_state.active
1092 ));
1093 output.push_str(&format!(
1094 "Fuzzy Pattern: '{}'\n",
1095 self.fuzzy_filter_state.pattern
1096 ));
1097 output.push_str("\n--- Search ---\n");
1098 output.push_str(&format!(
1099 "Search Pattern: '{}'\n",
1100 self.search_state.pattern
1101 ));
1102 output.push_str(&format!(
1103 "Search Matches: {} found\n",
1104 self.search_state.matches.len()
1105 ));
1106 output.push_str(&format!(
1107 "Current Match: {:?}\n",
1108 self.search_state.current_match
1109 ));
1110 output.push_str(&format!("Match Index: {}\n", self.search_state.match_index));
1111 output.push_str("\n--- Column Search ---\n");
1112 output.push_str(&format!(
1113 "Column Search Pattern: '{}'\n",
1114 "<migrated>" ));
1116 output.push_str(&format!(
1117 "Matching Columns: {:?}\n",
1118 Vec::<(usize, String)>::new() ));
1120 output.push_str("\n--- Sorting ---\n");
1121 output.push_str(&format!("Sort Column: {:?}\n", self.sort_state.column));
1122 output.push_str(&format!("Sort Order: {:?}\n", self.sort_state.order));
1123 output.push_str("\n--- Display Options ---\n");
1124 output.push_str(&format!("Compact Mode: {}\n", self.compact_mode));
1125 output.push_str(&format!("Show Row Numbers: {}\n", self.show_row_numbers));
1126 output.push_str(&format!("Case Insensitive: {}\n", self.case_insensitive));
1127 if let Some(ref dataview) = self.dataview {
1129 output.push_str(&format!(
1130 "Pinned Columns: {:?}\n",
1131 dataview.get_pinned_column_names()
1132 ));
1133 } else {
1134 output.push_str("Pinned Columns: []\n");
1135 }
1136 output.push_str(&format!("Column Widths: {:?}\n", self.column_widths));
1137 output.push_str(&format!("ViewState: {:?}\n", self.view_state));
1138 output.push_str("\n--- Data Source ---\n");
1139 output.push_str("Legacy CSV/Cache fields removed - using DataTable/DataView\n");
1140 output.push_str("\n--- Undo/Redo ---\n");
1141 output.push_str(&format!("Undo Stack Size: {}\n", self.undo_stack.len()));
1142 output.push_str(&format!("Redo Stack Size: {}\n", self.redo_stack.len()));
1143 output.push_str(&format!(
1144 "Kill Ring: '{}'\n",
1145 if self.kill_ring.len() > 50 {
1146 format!(
1147 "{}... ({} chars)",
1148 &self.kill_ring[..50],
1149 self.kill_ring.len()
1150 )
1151 } else {
1152 self.kill_ring.clone()
1153 }
1154 ));
1155 output.push_str("\n--- Stats ---\n");
1156 output.push_str(&format!(
1157 "Has Column Stats: {}\n",
1158 self.column_stats.is_some()
1159 ));
1160 output.push_str(&format!("Last Visible Rows: {}\n", self.last_visible_rows));
1161 output.push_str(&format!("Last Results Row: {:?}\n", self.last_results_row));
1162 output.push_str(&format!(
1163 "Last Scroll Offset: {:?}\n",
1164 self.last_scroll_offset
1165 ));
1166 output.push_str("\n=== END BUFFER DEBUG ===\n");
1167 output
1168 }
1169
1170 fn get_input_text(&self) -> String {
1172 self.input_manager.get_text()
1173 }
1174
1175 fn set_input_text(&mut self, text: String) {
1176 self.input_manager.set_text(text.clone());
1177 self.input = Input::new(text.clone()).with_cursor(text.len());
1179 }
1180
1181 fn handle_input_key(&mut self, event: KeyEvent) -> bool {
1182 let result = self.input_manager.handle_key_event(event);
1183 self.sync_from_input_manager();
1185 result
1186 }
1187
1188 fn switch_input_mode(&mut self, _multiline: bool) {
1189 let current_text = self.input_manager.get_text();
1190 let cursor_pos = self.input_manager.get_cursor_position();
1191
1192 self.edit_mode = EditMode::SingleLine;
1194 self.input_manager = create_single_line(current_text.clone());
1195 self.input =
1197 Input::new(current_text.clone()).with_cursor(cursor_pos.min(current_text.len()));
1198
1199 self.input_manager.set_cursor_position(cursor_pos);
1201 }
1202
1203 fn get_input_cursor_position(&self) -> usize {
1204 self.input_manager.get_cursor_position()
1205 }
1206
1207 fn set_input_cursor_position(&mut self, position: usize) {
1208 self.input_manager.set_cursor_position(position);
1209 if self.edit_mode == EditMode::SingleLine {
1211 let text = self.input.value().to_string();
1212 self.input = Input::new(text).with_cursor(position);
1213 }
1214 }
1215
1216 fn is_input_multiline(&self) -> bool {
1217 self.input_manager.is_multiline()
1218 }
1219
1220 fn navigate_history_up(&mut self, history: &[String]) -> bool {
1222 self.input_manager.set_history(history.to_vec());
1224 let navigated = self.input_manager.history_previous();
1225 if navigated {
1226 self.sync_from_input_manager();
1228 }
1229 navigated
1230 }
1231
1232 fn navigate_history_down(&mut self, history: &[String]) -> bool {
1233 self.input_manager.set_history(history.to_vec());
1235 let navigated = self.input_manager.history_next();
1236 if navigated {
1237 self.sync_from_input_manager();
1239 }
1240 navigated
1241 }
1242
1243 fn reset_history_navigation(&mut self) {
1244 self.input_manager.reset_history_position();
1245 }
1246
1247 fn clear_results(&mut self) {
1249 self.datatable = None;
1250 self.table_state.select(None);
1252 self.last_results_row = None;
1253 self.view_state.scroll_offset = (0, 0);
1254 self.last_scroll_offset = (0, 0);
1255 self.column_widths.clear();
1256 self.status_message = "Results cleared".to_string();
1257 self.filter_state.active = false;
1259 self.filter_state.pattern.clear();
1260 self.search_state.pattern.clear();
1261 self.search_state.matches.clear();
1262 self.search_state.current_match = None;
1263 }
1264}
1265
1266impl Buffer {
1267 #[must_use]
1269 pub fn new(id: usize) -> Self {
1270 Self {
1271 id,
1272 file_path: None,
1273 name: format!("[Buffer {id}]"),
1274 modified: false,
1275
1276 datatable: None,
1278 original_source: None,
1279 dataview: None,
1280
1281 mode: AppMode::Command,
1282 edit_mode: EditMode::SingleLine,
1283 input: Input::default(),
1284 input_manager: create_single_line(String::new()),
1285 table_state: TableState::default(),
1286 last_results_row: None,
1287 last_scroll_offset: (0, 0),
1288
1289 last_query: String::new(),
1290 status_message: String::new(),
1291
1292 sort_state: SortState {
1293 column: None,
1294 order: SortOrder::None,
1295 },
1296 filter_state: FilterState::default(),
1297 fuzzy_filter_state: FuzzyFilterState::default(),
1298 search_state: SearchState::default(),
1299 column_stats: None,
1301
1302 view_state: ViewState::default(),
1303 column_widths: Vec::new(),
1304 compact_mode: false,
1305 show_row_numbers: false,
1306 case_insensitive: false,
1307
1308 undo_stack: Vec::new(),
1309 redo_stack: Vec::new(),
1310 kill_ring: String::new(),
1311 last_visible_rows: 30,
1312 last_query_source: None,
1313
1314 highlighted_text_cache: None,
1315 last_highlighted_text: String::new(),
1316 saved_input_state: None,
1317 }
1318 }
1319
1320 #[must_use]
1322 pub fn from_csv(
1323 id: usize,
1324 path: PathBuf,
1325 _csv_client: CsvApiClient, _table_name: String, ) -> Self {
1328 let name = path
1329 .file_name()
1330 .and_then(|n| n.to_str())
1331 .unwrap_or("unknown.csv")
1332 .to_string();
1333
1334 let mut buffer = Self::new(id);
1335 buffer.file_path = Some(path);
1336 buffer.name = name;
1337 buffer
1340 }
1341
1342 #[must_use]
1344 pub fn from_json(
1345 id: usize,
1346 path: PathBuf,
1347 _csv_client: CsvApiClient, _table_name: String, ) -> Self {
1350 let name = path
1351 .file_name()
1352 .and_then(|n| n.to_str())
1353 .unwrap_or("unknown.json")
1354 .to_string();
1355
1356 let mut buffer = Self::new(id);
1357 buffer.file_path = Some(path);
1358 buffer.name = name;
1359 buffer
1362 }
1363
1364 pub fn display_name(&self) -> String {
1366 if self.modified {
1367 format!("{}*", self.name)
1368 } else {
1369 self.name.clone()
1370 }
1371 }
1372
1373 pub fn short_name(&self, max_len: usize) -> String {
1375 let display = self.display_name();
1376 if display.len() <= max_len {
1377 display
1378 } else {
1379 format!("{}...", &display[..max_len.saturating_sub(3)])
1380 }
1381 }
1382
1383 pub fn has_file(&self, path: &PathBuf) -> bool {
1385 self.file_path.as_ref() == Some(path)
1386 }
1387
1388 fn sync_from_input_manager(&mut self) {
1390 let text = self.input_manager.get_text();
1391 let cursor_pos = self.input_manager.get_cursor_position();
1392
1393 let text_len = text.len();
1395 self.input = Input::new(text).with_cursor(cursor_pos.min(text_len));
1396 }
1397
1398 fn sync_to_input_manager(&mut self) {
1400 let _text = self.input.value().to_string();
1402 self.input_manager = create_from_input(self.input.clone());
1403 }
1404
1405 pub fn move_cursor_word_backward(&mut self) {
1411 let text = self.input_manager.get_text();
1412 let cursor_pos = self.input_manager.get_cursor_position();
1413 let new_pos = CursorOperations::find_word_boundary_backward(&text, cursor_pos);
1414 self.input_manager.set_cursor_position(new_pos);
1415 self.sync_from_input_manager();
1416 self.status_message = format!("Moved to position {new_pos} (word boundary)");
1417 }
1418
1419 pub fn move_cursor_word_forward(&mut self) {
1421 let text = self.input_manager.get_text();
1422 let cursor_pos = self.input_manager.get_cursor_position();
1423 let new_pos = CursorOperations::find_word_boundary_forward(&text, cursor_pos);
1424 self.input_manager.set_cursor_position(new_pos);
1425 self.sync_from_input_manager();
1426 }
1427
1428 pub fn delete_word_backward(&mut self) {
1430 let text = self.input_manager.get_text();
1431 let cursor_pos = self.input_manager.get_cursor_position();
1432 let (new_text, new_cursor) = CursorOperations::delete_word_backward(&text, cursor_pos);
1433
1434 if cursor_pos > new_cursor {
1436 self.kill_ring = text[new_cursor..cursor_pos].to_string();
1437 }
1438
1439 self.input_manager.set_text(new_text);
1440 self.input_manager.set_cursor_position(new_cursor);
1441 self.sync_from_input_manager();
1442 }
1443
1444 pub fn delete_word_forward(&mut self) {
1446 let text = self.input_manager.get_text();
1447 let cursor_pos = self.input_manager.get_cursor_position();
1448 let (new_text, new_cursor) = CursorOperations::delete_word_forward(&text, cursor_pos);
1449
1450 let word_end = CursorOperations::find_word_boundary_forward(&text, cursor_pos);
1452 if word_end > cursor_pos {
1453 self.kill_ring = text[cursor_pos..word_end].to_string();
1454 }
1455
1456 self.input_manager.set_text(new_text);
1457 self.input_manager.set_cursor_position(new_cursor);
1458 self.sync_from_input_manager();
1459 }
1460
1461 pub fn kill_line(&mut self) {
1463 let text = self.input_manager.get_text();
1464 let cursor_pos = self.input_manager.get_cursor_position();
1465 let (new_text, killed) = CursorOperations::kill_line(&text, cursor_pos);
1466
1467 self.kill_ring = killed;
1468 self.input_manager.set_text(new_text);
1469 self.sync_from_input_manager();
1470 }
1471
1472 pub fn kill_line_backward(&mut self) {
1474 let text = self.input_manager.get_text();
1475 let cursor_pos = self.input_manager.get_cursor_position();
1476 let (new_text, killed, new_cursor) =
1477 CursorOperations::kill_line_backward(&text, cursor_pos);
1478
1479 self.kill_ring = killed;
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 jump_to_prev_token(&mut self) {
1487 let text = self.input_manager.get_text();
1488 let cursor_pos = self.input_manager.get_cursor_position();
1489 let new_pos = CursorOperations::jump_to_prev_token(&text, cursor_pos);
1490 self.input_manager.set_cursor_position(new_pos);
1491 self.sync_from_input_manager();
1492 }
1493
1494 pub fn jump_to_next_token(&mut self) {
1496 let text = self.input_manager.get_text();
1497 let cursor_pos = self.input_manager.get_cursor_position();
1498 let new_pos = CursorOperations::jump_to_next_token(&text, cursor_pos);
1499 self.input_manager.set_cursor_position(new_pos);
1500 self.sync_from_input_manager();
1501 }
1502
1503 pub fn yank(&mut self) {
1505 if !self.kill_ring.is_empty() {
1506 self.save_state_for_undo();
1507
1508 let text = self.input_manager.get_text();
1509 let cursor_pos = self.input_manager.get_cursor_position();
1510
1511 let before = text.chars().take(cursor_pos).collect::<String>();
1513 let after = text.chars().skip(cursor_pos).collect::<String>();
1514 let new_text = format!("{}{}{}", before, &self.kill_ring, after);
1515 let new_cursor = cursor_pos + self.kill_ring.len();
1516
1517 self.input_manager.set_text(new_text);
1518 self.input_manager.set_cursor_position(new_cursor);
1519 self.sync_from_input_manager();
1520 }
1521 }
1522
1523 pub fn expand_asterisk(&mut self, parser: &HybridParser) -> bool {
1525 let query = self.input_manager.get_text();
1526 let query_upper = query.to_uppercase();
1527
1528 if let Some(select_pos) = query_upper.find("SELECT") {
1530 if let Some(star_pos) = query_upper[select_pos..].find('*') {
1531 let star_abs_pos = select_pos + star_pos;
1532
1533 if let Some(from_rel_pos) = query_upper[star_abs_pos..].find("FROM") {
1535 let from_abs_pos = star_abs_pos + from_rel_pos;
1536
1537 let after_from = &query[from_abs_pos + 4..].trim_start();
1539 let table_name = after_from
1540 .split_whitespace()
1541 .next()
1542 .unwrap_or("")
1543 .trim_end_matches(|c: char| !c.is_alphanumeric() && c != '_');
1544
1545 if !table_name.is_empty() {
1546 let columns = parser.get_table_columns(table_name);
1548
1549 if columns.is_empty() {
1550 self.status_message =
1551 format!("No columns found for table '{table_name}'");
1552 } else {
1553 let columns_str = columns.join(", ");
1555
1556 let before_star = &query[..star_abs_pos];
1558 let after_star = &query[star_abs_pos + 1..];
1559 let new_query = format!("{before_star}{columns_str}{after_star}");
1560
1561 self.input_manager.set_text(new_query.clone());
1563 self.input_manager.set_cursor_position(new_query.len());
1564 self.sync_from_input_manager();
1565
1566 self.status_message =
1567 format!("Expanded * to {} columns", columns.len());
1568 return true;
1569 }
1570 }
1571 }
1572 }
1573 }
1574
1575 self.status_message = "No SELECT * pattern found to expand".to_string();
1576 false
1577 }
1578
1579 pub fn expand_asterisk_visible(&mut self) -> bool {
1581 let query = self.input_manager.get_text();
1582 let query_upper = query.to_uppercase();
1583
1584 if let Some(select_pos) = query_upper.find("SELECT") {
1586 if let Some(star_pos) = query_upper[select_pos..].find('*') {
1587 let star_abs_pos = select_pos + star_pos;
1588
1589 if let Some(dataview) = &self.dataview {
1591 let visible_columns = dataview.get_display_column_names();
1592
1593 if !visible_columns.is_empty() {
1594 let columns_str = visible_columns.join(", ");
1596
1597 let before_star = &query[..star_abs_pos];
1599 let after_star = &query[star_abs_pos + 1..];
1600 let new_query = format!("{before_star}{columns_str}{after_star}");
1601
1602 self.input_manager.set_text(new_query.clone());
1604 self.input_manager.set_cursor_position(new_query.len());
1605 self.sync_from_input_manager();
1606
1607 self.status_message =
1608 format!("Expanded * to {} visible columns", visible_columns.len());
1609 return true;
1610 }
1611 self.status_message = "No visible columns available".to_string();
1612 } else {
1613 self.status_message = "No data loaded to expand from".to_string();
1614 }
1615 }
1616 }
1617
1618 self.status_message = "No SELECT * pattern found to expand".to_string();
1619 false
1620 }
1621}
1622
1623impl Clone for Buffer {
1625 fn clone(&self) -> Self {
1626 let input_manager = create_from_input(self.input.clone());
1628
1629 Self {
1630 id: self.id,
1631 file_path: self.file_path.clone(),
1632 name: self.name.clone(),
1633 modified: self.modified,
1634 datatable: self.datatable.clone(),
1636 original_source: self.original_source.clone(),
1637 dataview: self.dataview.clone(),
1638 mode: self.mode.clone(),
1639 edit_mode: self.edit_mode.clone(),
1640 input: self.input.clone(),
1641 input_manager,
1642 table_state: self.table_state.clone(),
1643 last_results_row: self.last_results_row,
1644 last_scroll_offset: self.last_scroll_offset,
1645 last_query: self.last_query.clone(),
1646 status_message: self.status_message.clone(),
1647 sort_state: self.sort_state.clone(),
1648 filter_state: self.filter_state.clone(),
1649 fuzzy_filter_state: self.fuzzy_filter_state.clone(),
1650 search_state: self.search_state.clone(),
1651 column_stats: self.column_stats.clone(),
1653 view_state: self.view_state.clone(),
1654 column_widths: self.column_widths.clone(),
1655 compact_mode: self.compact_mode,
1656 show_row_numbers: self.show_row_numbers,
1657 case_insensitive: self.case_insensitive,
1658 undo_stack: self.undo_stack.clone(),
1659 redo_stack: self.redo_stack.clone(),
1660 kill_ring: self.kill_ring.clone(),
1661 last_visible_rows: self.last_visible_rows,
1662 last_query_source: self.last_query_source.clone(),
1663 highlighted_text_cache: self.highlighted_text_cache.clone(),
1664 last_highlighted_text: self.last_highlighted_text.clone(),
1665 saved_input_state: self.saved_input_state.clone(),
1666 }
1667 }
1668}
1669
1670pub struct BufferManager {
1672 buffers: Vec<Buffer>,
1673 current_buffer_index: usize,
1674 next_buffer_id: usize,
1675}
1676
1677impl Default for BufferManager {
1678 fn default() -> Self {
1679 Self::new()
1680 }
1681}
1682
1683impl BufferManager {
1684 #[must_use]
1685 pub fn new() -> Self {
1686 Self {
1687 buffers: Vec::new(),
1688 current_buffer_index: 0,
1689 next_buffer_id: 1,
1690 }
1691 }
1692
1693 pub fn add_buffer(&mut self, mut buffer: Buffer) -> usize {
1695 buffer.id = self.next_buffer_id;
1696 self.next_buffer_id += 1;
1697
1698 let index = self.buffers.len();
1699 self.buffers.push(buffer);
1700 self.current_buffer_index = index;
1701 index
1702 }
1703
1704 #[must_use]
1706 pub fn current(&self) -> Option<&Buffer> {
1707 self.buffers.get(self.current_buffer_index)
1708 }
1709
1710 pub fn current_mut(&mut self) -> Option<&mut Buffer> {
1712 self.buffers.get_mut(self.current_buffer_index)
1713 }
1714
1715 pub fn next_buffer(&mut self) {
1717 if !self.buffers.is_empty() {
1718 self.current_buffer_index = (self.current_buffer_index + 1) % self.buffers.len();
1719 }
1720 }
1721
1722 pub fn prev_buffer(&mut self) {
1724 if !self.buffers.is_empty() {
1725 if self.current_buffer_index == 0 {
1726 self.current_buffer_index = self.buffers.len() - 1;
1727 } else {
1728 self.current_buffer_index -= 1;
1729 }
1730 }
1731 }
1732
1733 pub fn switch_to(&mut self, index: usize) {
1735 if index < self.buffers.len() {
1736 self.current_buffer_index = index;
1737 }
1738 }
1739
1740 pub fn close_current(&mut self) -> bool {
1742 if self.buffers.len() <= 1 {
1743 return false; }
1745
1746 self.buffers.remove(self.current_buffer_index);
1747
1748 if self.current_buffer_index >= self.buffers.len() {
1750 self.current_buffer_index = self.buffers.len() - 1;
1751 }
1752
1753 true
1754 }
1755
1756 #[must_use]
1758 pub fn find_by_path(&self, path: &PathBuf) -> Option<usize> {
1759 self.buffers.iter().position(|b| b.has_file(path))
1760 }
1761
1762 #[must_use]
1764 pub fn all_buffers(&self) -> &[Buffer] {
1765 &self.buffers
1766 }
1767
1768 #[must_use]
1770 pub fn current_index(&self) -> usize {
1771 self.current_buffer_index
1772 }
1773
1774 #[must_use]
1776 pub fn has_multiple(&self) -> bool {
1777 self.buffers.len() > 1
1778 }
1779
1780 pub fn clear_all(&mut self) {
1782 self.buffers.clear();
1783 self.current_buffer_index = 0;
1784 }
1785}