1use crate::api_client::QueryResponse;
2use crate::app_state_container::ColumnSearchState;
3use crate::buffer::{
4 AppMode, BufferAPI, ColumnStatistics, EditMode, FilterState, FuzzyFilterState, SearchState,
5 SortOrder, SortState,
6};
7use crate::data::data_view::DataView;
9use crate::datatable::DataTable;
10use crate::datatable_view::{DataTableView, SortOrder as ViewSortOrder};
11use crate::input_manager::{create_single_line, InputManager};
12use anyhow::Result;
13use crossterm::event::KeyEvent;
14use ratatui::style::Color;
15use ratatui::widgets::TableState;
16use std::path::PathBuf;
17use std::sync::Arc;
18use tracing::debug;
19use tui_input::Input;
20
21pub struct DataTableBuffer {
24 id: usize,
26 file_path: Option<PathBuf>,
27 name: String,
28 modified: bool,
29
30 view: DataTableView,
32 dataview: Option<DataView>,
34
35 mode: AppMode,
37 edit_mode: EditMode,
38 input: Input, input_manager: Box<dyn InputManager>,
40 table_state: TableState,
41 last_results_row: Option<usize>,
42 last_scroll_offset: (usize, usize),
43
44 last_query: String,
46 status_message: String,
47
48 sort_state: SortState,
50 filter_state: FilterState,
51 fuzzy_filter_state: FuzzyFilterState,
52 search_state: SearchState,
53 column_search_state: ColumnSearchState,
54 column_stats: Option<ColumnStatistics>,
55 column_widths: Vec<u16>,
59 scroll_offset: (usize, usize),
60 current_column: usize,
61 compact_mode: bool,
63 viewport_lock: bool,
64 viewport_lock_row: Option<usize>,
65 show_row_numbers: bool,
66 case_insensitive: bool,
67
68 undo_stack: Vec<(String, usize)>,
70 redo_stack: Vec<(String, usize)>,
71 kill_ring: String,
72 last_visible_rows: usize,
73 last_query_source: Option<String>,
74
75 highlighted_text_cache: Option<Vec<(String, Color)>>,
77 last_highlighted_text: String,
78
79 saved_input_state: Option<(String, usize)>,
81}
82
83impl DataTableBuffer {
84 #[must_use]
86 pub fn new(id: usize, table: DataTable) -> Self {
87 let name = table.name.clone();
88 let view = DataTableView::new(table);
89
90 Self {
91 id,
93 file_path: None,
94 name,
95 modified: false,
96
97 view,
99 dataview: None,
101
102 mode: AppMode::Command,
104 edit_mode: EditMode::SingleLine,
105 input: Input::default(),
106 input_manager: create_single_line(String::new()),
107 table_state: TableState::default(),
108 last_results_row: None,
109 last_scroll_offset: (0, 0),
110
111 last_query: String::new(),
113 status_message: String::new(),
114
115 sort_state: SortState {
117 column: None,
118 order: SortOrder::None,
119 },
120 filter_state: FilterState::default(),
121 fuzzy_filter_state: FuzzyFilterState::default(),
122 search_state: SearchState::default(),
123 column_search_state: ColumnSearchState::default(),
124 column_stats: None,
125 column_widths: Vec::new(),
129 scroll_offset: (0, 0),
130 current_column: 0,
131 compact_mode: false,
132 viewport_lock: false,
133 viewport_lock_row: None,
134 show_row_numbers: false,
135 case_insensitive: false,
136
137 undo_stack: Vec::new(),
139 redo_stack: Vec::new(),
140 kill_ring: String::new(),
141 last_visible_rows: 0,
142 last_query_source: None,
143
144 highlighted_text_cache: None,
146 last_highlighted_text: String::new(),
147
148 saved_input_state: None,
150 }
151 }
152
153 pub fn from_file(id: usize, file_path: PathBuf) -> anyhow::Result<Self> {
155 let table = crate::datatable_loaders::load_json_to_datatable(&file_path, "data")?;
156 let mut buffer = Self::new(id, table);
157 buffer.file_path = Some(file_path.clone());
158 buffer.name = file_path
159 .file_stem()
160 .and_then(|s| s.to_str())
161 .unwrap_or("data")
162 .to_string();
163 Ok(buffer)
164 }
165
166 fn update_column_widths(&mut self) {
168 self.column_widths = self.calculate_column_widths();
170 }
171
172 fn calculate_column_widths(&self) -> Vec<u16> {
174 let table = self.view.table();
175 let mut widths = Vec::new();
176
177 for (col_idx, column) in table.columns.iter().enumerate() {
178 let mut max_width = column.name.len() as u16;
179
180 let sample_size = 50.min(table.row_count());
182 for row_idx in 0..sample_size {
183 if let Some(value) = table.get_value(row_idx, col_idx) {
184 let value_len = value.to_string().len() as u16;
185 max_width = max_width.max(value_len);
186 }
187 }
188
189 widths.push((max_width + 2).min(50));
191 }
192
193 widths
194 }
195
196 fn sync_sort_to_view(&mut self) {
198 if let Some(column) = self.sort_state.column {
199 let view_order = match self.sort_state.order {
200 SortOrder::Ascending => ViewSortOrder::Ascending,
201 SortOrder::Descending => ViewSortOrder::Descending,
202 SortOrder::None => return, };
204
205 self.view.apply_sort(column, view_order);
206 self.update_column_widths();
207 }
208 }
209
210 fn sync_filter_to_view(&mut self) {
212 if self.filter_state.active && !self.filter_state.pattern.is_empty() {
213 self.view.apply_filter(
214 self.filter_state.pattern.clone(),
215 None, !self.case_insensitive,
217 );
218 self.update_column_widths();
219 } else {
220 self.view.clear_filter();
221 self.update_column_widths();
222 }
223 }
224
225 pub fn table(&self) -> &DataTable {
227 self.view.table()
228 }
229
230 pub fn view(&self) -> &DataTableView {
232 &self.view
233 }
234}
235
236impl BufferAPI for DataTableBuffer {
237 fn get_id(&self) -> usize {
239 self.id
240 }
241
242 fn get_query(&self) -> String {
244 self.input_manager.get_text()
245 }
246
247 fn set_query(&mut self, query: String) {
248 self.input_manager.set_text(query.clone());
249 self.input = Input::new(query.clone()).with_cursor(query.len());
250 }
251
252 fn get_last_query(&self) -> String {
255 self.last_query.clone()
256 }
257
258 fn set_last_query(&mut self, query: String) {
259 self.last_query = query;
260 }
261
262 fn get_datatable(&self) -> Option<&DataTable> {
264 Some(self.view.get_datatable())
265 }
266
267 fn get_datatable_mut(&mut self) -> Option<&mut DataTable> {
268 Some(self.view.get_datatable_mut())
269 }
270
271 fn has_datatable(&self) -> bool {
272 true }
274
275 fn get_original_source(&self) -> Option<&DataTable> {
276 Some(self.view.table())
279 }
280
281 fn set_datatable(&mut self, datatable: Option<Arc<DataTable>>) {
282 if let Some(dt) = datatable {
285 debug!(
286 "V50: DataTableBuffer received DataTable with {} rows",
287 dt.row_count()
288 );
289 }
290 }
291
292 fn set_results_as_datatable(&mut self, _response: Option<QueryResponse>) -> Result<(), String> {
293 Ok(())
296 }
297
298 fn get_dataview(&self) -> Option<&DataView> {
300 self.dataview.as_ref()
301 }
302 fn get_dataview_mut(&mut self) -> Option<&mut DataView> {
303 self.dataview.as_mut()
304 }
305 fn set_dataview(&mut self, dataview: Option<DataView>) {
306 debug!(
307 "V51: Setting DataView with {} rows in DataTableBuffer",
308 dataview
309 .as_ref()
310 .map_or(0, super::data_view::DataView::row_count)
311 );
312 self.dataview = dataview;
313 }
314 fn has_dataview(&self) -> bool {
315 self.dataview.is_some()
316 }
317
318 fn get_mode(&self) -> AppMode {
320 self.mode.clone()
321 }
322
323 fn set_mode(&mut self, mode: AppMode) {
324 self.mode = mode;
325 }
326
327 fn get_edit_mode(&self) -> EditMode {
328 self.edit_mode.clone()
329 }
330
331 fn set_edit_mode(&mut self, mode: EditMode) {
332 self.edit_mode = mode;
333 }
334
335 fn get_status_message(&self) -> String {
336 self.status_message.clone()
337 }
338
339 fn set_status_message(&mut self, message: String) {
340 self.status_message = message;
341 }
342
343 fn get_selected_row(&self) -> Option<usize> {
345 self.last_results_row
346 }
347
348 fn set_selected_row(&mut self, row: Option<usize>) {
349 self.last_results_row = row;
350 }
351
352 fn get_current_column(&self) -> usize {
353 self.current_column
354 }
355
356 fn set_current_column(&mut self, col: usize) {
357 self.current_column = col;
358 }
359
360 fn get_scroll_offset(&self) -> (usize, usize) {
361 self.scroll_offset
362 }
363
364 fn set_scroll_offset(&mut self, offset: (usize, usize)) {
365 self.scroll_offset = offset;
366 }
367
368 fn get_last_results_row(&self) -> Option<usize> {
369 self.last_results_row
370 }
371
372 fn set_last_results_row(&mut self, row: Option<usize>) {
373 self.last_results_row = row;
374 }
375
376 fn get_last_scroll_offset(&self) -> (usize, usize) {
377 self.last_scroll_offset
378 }
379
380 fn set_last_scroll_offset(&mut self, offset: (usize, usize)) {
381 self.last_scroll_offset = offset;
382 }
383
384 fn get_filter_pattern(&self) -> String {
386 self.filter_state.pattern.clone()
387 }
388
389 fn set_filter_pattern(&mut self, pattern: String) {
390 self.filter_state.pattern = pattern;
391 self.sync_filter_to_view();
392 }
393
394 fn is_filter_active(&self) -> bool {
395 self.filter_state.active
396 }
397
398 fn set_filter_active(&mut self, active: bool) {
399 self.filter_state.active = active;
400 self.sync_filter_to_view();
401 }
402
403 fn get_fuzzy_filter_pattern(&self) -> String {
407 self.fuzzy_filter_state.pattern.clone()
408 }
409
410 fn set_fuzzy_filter_pattern(&mut self, pattern: String) {
411 self.fuzzy_filter_state.pattern = pattern;
412 }
414
415 fn is_fuzzy_filter_active(&self) -> bool {
416 self.fuzzy_filter_state.active
417 }
418
419 fn set_fuzzy_filter_active(&mut self, active: bool) {
420 self.fuzzy_filter_state.active = active;
421 }
422
423 fn get_fuzzy_filter_indices(&self) -> &Vec<usize> {
424 &self.fuzzy_filter_state.filtered_indices
425 }
426
427 fn set_fuzzy_filter_indices(&mut self, indices: Vec<usize>) {
428 self.fuzzy_filter_state.filtered_indices = indices;
429 }
430
431 fn clear_fuzzy_filter(&mut self) {
432 self.fuzzy_filter_state.active = false;
433 self.fuzzy_filter_state.pattern.clear();
434 self.fuzzy_filter_state.filtered_indices.clear();
435 }
436
437 fn get_search_pattern(&self) -> String {
439 self.search_state.pattern.clone()
440 }
441
442 fn set_search_pattern(&mut self, pattern: String) {
443 self.search_state.pattern = pattern.clone();
444 if !pattern.is_empty() {
446 self.view.start_search(pattern, !self.case_insensitive);
447 }
448 }
449
450 fn get_search_matches(&self) -> Vec<(usize, usize)> {
451 self.search_state.matches.clone()
452 }
453
454 fn set_search_matches(&mut self, matches: Vec<(usize, usize)>) {
455 self.search_state.matches = matches;
456 }
457
458 fn get_current_match(&self) -> Option<(usize, usize)> {
459 self.search_state.current_match
460 }
461
462 fn set_current_match(&mut self, match_pos: Option<(usize, usize)>) {
463 self.search_state.current_match = match_pos;
464 }
465
466 fn get_search_match_index(&self) -> usize {
467 self.search_state.match_index
468 }
469
470 fn set_search_match_index(&mut self, index: usize) {
471 self.search_state.match_index = index;
472 }
473
474 fn clear_search_state(&mut self) {
475 self.search_state.pattern.clear();
476 self.search_state.matches.clear();
477 self.search_state.current_match = None;
478 self.search_state.match_index = 0;
479 self.view.clear_search();
480 }
481
482 fn get_column_stats(&self) -> Option<&ColumnStatistics> {
487 self.column_stats.as_ref()
488 }
489
490 fn set_column_stats(&mut self, stats: Option<ColumnStatistics>) {
491 self.column_stats = stats;
492 }
493
494 fn get_sort_column(&self) -> Option<usize> {
496 self.sort_state.column
497 }
498
499 fn set_sort_column(&mut self, column: Option<usize>) {
500 self.sort_state.column = column;
501 self.sync_sort_to_view();
502 }
503
504 fn get_sort_order(&self) -> SortOrder {
505 self.sort_state.order
506 }
507
508 fn set_sort_order(&mut self, order: SortOrder) {
509 self.sort_state.order = order;
510 self.sync_sort_to_view();
511 }
512
513 fn get_name(&self) -> String {
515 self.name.clone()
516 }
517
518 fn set_name(&mut self, name: String) {
519 self.name = name;
520 }
521
522 fn get_file_path(&self) -> Option<&PathBuf> {
523 self.file_path.as_ref()
524 }
525
526 fn set_file_path(&mut self, path: Option<String>) {
527 self.file_path = path.map(PathBuf::from);
528 }
529
530 fn is_modified(&self) -> bool {
531 self.modified
532 }
533
534 fn set_modified(&mut self, modified: bool) {
535 self.modified = modified;
536 }
537
538 fn get_last_query_source(&self) -> Option<String> {
539 self.last_query_source.clone()
540 }
541
542 fn set_last_query_source(&mut self, source: Option<String>) {
543 self.last_query_source = source;
544 }
545
546 fn get_column_widths(&self) -> &Vec<u16> {
550 &self.column_widths
551 }
552
553 fn set_column_widths(&mut self, widths: Vec<u16>) {
554 self.column_widths = widths;
555 }
556
557 fn is_compact_mode(&self) -> bool {
562 self.compact_mode
563 }
564
565 fn set_compact_mode(&mut self, compact: bool) {
566 self.compact_mode = compact;
567 }
568
569 fn is_viewport_lock(&self) -> bool {
570 self.viewport_lock
571 }
572
573 fn set_viewport_lock(&mut self, locked: bool) {
574 self.viewport_lock = locked;
575 }
576
577 fn get_viewport_lock_row(&self) -> Option<usize> {
578 self.viewport_lock_row
579 }
580
581 fn set_viewport_lock_row(&mut self, row: Option<usize>) {
582 self.viewport_lock_row = row;
583 }
584
585 fn is_show_row_numbers(&self) -> bool {
586 self.show_row_numbers
587 }
588
589 fn set_show_row_numbers(&mut self, show: bool) {
590 self.show_row_numbers = show;
591 }
592
593 fn is_case_insensitive(&self) -> bool {
594 self.case_insensitive
595 }
596
597 fn set_case_insensitive(&mut self, insensitive: bool) {
598 self.case_insensitive = insensitive;
599 }
600
601 fn get_input_value(&self) -> String {
603 self.input_manager.get_text()
604 }
605
606 fn set_input_value(&mut self, value: String) {
607 self.input_manager.set_text(value.clone());
608 self.input = Input::new(value.clone()).with_cursor(value.len());
609 }
610
611 fn get_input_cursor(&self) -> usize {
612 self.input_manager.get_cursor_position()
613 }
614
615 fn set_input_cursor(&mut self, pos: usize) {
616 self.input_manager.set_cursor_position(pos);
617 }
618
619 fn get_kill_ring(&self) -> String {
621 self.kill_ring.clone()
622 }
623
624 fn set_kill_ring(&mut self, text: String) {
625 self.kill_ring = text;
626 }
627
628 fn get_last_visible_rows(&self) -> usize {
629 self.last_visible_rows
630 }
631
632 fn set_last_visible_rows(&mut self, rows: usize) {
633 self.last_visible_rows = rows;
634 }
635
636 fn apply_filter(&mut self) -> Result<()> {
638 self.sync_filter_to_view();
639 Ok(())
640 }
641
642 fn apply_sort(&mut self) -> Result<()> {
643 self.sync_sort_to_view();
644 Ok(())
645 }
646
647 fn search(&mut self) -> Result<()> {
648 Ok(())
650 }
651
652 fn clear_filters(&mut self) {
653 self.filter_state.active = false;
654 self.filter_state.pattern.clear();
655 self.view.clear_filter();
656 }
657
658 fn get_row_count(&self) -> usize {
659 self.view.table().row_count()
660 }
661
662 fn get_column_count(&self) -> usize {
663 self.view.table().column_count()
664 }
665
666 fn get_column_names(&self) -> Vec<String> {
667 self.view
668 .table()
669 .columns
670 .iter()
671 .map(|col| col.name.clone())
672 .collect()
673 }
674
675 fn get_undo_stack(&self) -> &Vec<(String, usize)> {
679 &self.undo_stack
680 }
681
682 fn push_undo(&mut self, state: (String, usize)) {
683 self.undo_stack.push(state);
684 if self.undo_stack.len() > 100 {
686 self.undo_stack.remove(0);
687 }
688 }
689
690 fn pop_undo(&mut self) -> Option<(String, usize)> {
691 self.undo_stack.pop()
692 }
693
694 fn get_redo_stack(&self) -> &Vec<(String, usize)> {
695 &self.redo_stack
696 }
697
698 fn push_redo(&mut self, state: (String, usize)) {
699 self.redo_stack.push(state);
700 if self.redo_stack.len() > 100 {
702 self.redo_stack.remove(0);
703 }
704 }
705
706 fn pop_redo(&mut self) -> Option<(String, usize)> {
707 self.redo_stack.pop()
708 }
709
710 fn clear_redo(&mut self) {
711 self.redo_stack.clear();
712 }
713
714 fn is_kill_ring_empty(&self) -> bool {
716 self.kill_ring.is_empty()
717 }
718
719 fn perform_undo(&mut self) -> bool {
720 if let Some((text, cursor)) = self.pop_undo() {
721 let current_state = (self.get_input_value(), self.get_input_cursor());
723 self.push_redo(current_state);
724
725 self.set_input_value(text);
727 self.set_input_cursor(cursor);
728 true
729 } else {
730 false
731 }
732 }
733
734 fn perform_redo(&mut self) -> bool {
735 if let Some((text, cursor)) = self.pop_redo() {
736 let current_state = (self.get_input_value(), self.get_input_cursor());
738 self.push_undo(current_state);
739
740 self.set_input_value(text);
742 self.set_input_cursor(cursor);
743 true
744 } else {
745 false
746 }
747 }
748
749 fn save_state_for_undo(&mut self) {
750 let current_state = (self.get_input_value(), self.get_input_cursor());
751 self.push_undo(current_state);
752 }
753
754 fn debug_dump(&self) -> String {
755 format!(
756 "DataTableBuffer {{ id: {}, name: \"{}\", rows: {}, cols: {} }}",
757 self.id,
758 self.name,
759 self.get_row_count(),
760 self.get_column_count()
761 )
762 }
763
764 fn get_input_text(&self) -> String {
765 self.input_manager.get_text()
766 }
767
768 fn set_input_text(&mut self, text: String) {
769 self.input_manager.set_text(text.clone());
770 self.input = Input::new(text.clone()).with_cursor(text.len());
771 }
772
773 fn handle_input_key(&mut self, event: KeyEvent) -> bool {
774 self.input_manager.handle_key_event(event)
775 }
776
777 fn switch_input_mode(&mut self, _multiline: bool) {
778 self.edit_mode = EditMode::SingleLine;
780 let current_text = self.input_manager.get_text();
781 self.input_manager = create_single_line(current_text);
782 }
783
784 fn get_input_cursor_position(&self) -> usize {
785 self.input_manager.get_cursor_position()
786 }
787
788 fn set_input_cursor_position(&mut self, position: usize) {
789 self.input_manager.set_cursor_position(position);
790 }
791
792 fn is_input_multiline(&self) -> bool {
793 matches!(self.edit_mode, EditMode::MultiLine)
794 }
795
796 fn navigate_history_up(&mut self, _history: &[String]) -> bool {
797 false
800 }
801
802 fn navigate_history_down(&mut self, _history: &[String]) -> bool {
803 false
806 }
807
808 fn reset_history_navigation(&mut self) {
809 }
811
812 fn clear_results(&mut self) {
813 }
816}