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 pub fn new(id: usize, table: DataTable) -> Self {
86 let name = table.name.clone();
87 let view = DataTableView::new(table);
88
89 Self {
90 id,
92 file_path: None,
93 name,
94 modified: false,
95
96 view,
98 dataview: None,
100
101 mode: AppMode::Command,
103 edit_mode: EditMode::SingleLine,
104 input: Input::default(),
105 input_manager: create_single_line(String::new()),
106 table_state: TableState::default(),
107 last_results_row: None,
108 last_scroll_offset: (0, 0),
109
110 last_query: String::new(),
112 status_message: String::new(),
113
114 sort_state: SortState {
116 column: None,
117 order: SortOrder::None,
118 },
119 filter_state: FilterState::default(),
120 fuzzy_filter_state: FuzzyFilterState::default(),
121 search_state: SearchState::default(),
122 column_search_state: ColumnSearchState::default(),
123 column_stats: None,
124 column_widths: Vec::new(),
128 scroll_offset: (0, 0),
129 current_column: 0,
130 compact_mode: false,
131 viewport_lock: false,
132 viewport_lock_row: None,
133 show_row_numbers: false,
134 case_insensitive: false,
135
136 undo_stack: Vec::new(),
138 redo_stack: Vec::new(),
139 kill_ring: String::new(),
140 last_visible_rows: 0,
141 last_query_source: None,
142
143 highlighted_text_cache: None,
145 last_highlighted_text: String::new(),
146
147 saved_input_state: None,
149 }
150 }
151
152 pub fn from_file(id: usize, file_path: PathBuf) -> anyhow::Result<Self> {
154 let table = crate::datatable_loaders::load_json_to_datatable(&file_path, "data")?;
155 let mut buffer = Self::new(id, table);
156 buffer.file_path = Some(file_path.clone());
157 buffer.name = file_path
158 .file_stem()
159 .and_then(|s| s.to_str())
160 .unwrap_or("data")
161 .to_string();
162 Ok(buffer)
163 }
164
165 fn update_column_widths(&mut self) {
167 self.column_widths = self.calculate_column_widths();
169 }
170
171 fn calculate_column_widths(&self) -> Vec<u16> {
173 let table = self.view.table();
174 let mut widths = Vec::new();
175
176 for (col_idx, column) in table.columns.iter().enumerate() {
177 let mut max_width = column.name.len() as u16;
178
179 let sample_size = 50.min(table.row_count());
181 for row_idx in 0..sample_size {
182 if let Some(value) = table.get_value(row_idx, col_idx) {
183 let value_len = value.to_string().len() as u16;
184 max_width = max_width.max(value_len);
185 }
186 }
187
188 widths.push((max_width + 2).min(50));
190 }
191
192 widths
193 }
194
195 fn sync_sort_to_view(&mut self) {
197 if let Some(column) = self.sort_state.column {
198 let view_order = match self.sort_state.order {
199 SortOrder::Ascending => ViewSortOrder::Ascending,
200 SortOrder::Descending => ViewSortOrder::Descending,
201 SortOrder::None => return, };
203
204 self.view.apply_sort(column, view_order);
205 self.update_column_widths();
206 }
207 }
208
209 fn sync_filter_to_view(&mut self) {
211 if self.filter_state.active && !self.filter_state.pattern.is_empty() {
212 self.view.apply_filter(
213 self.filter_state.pattern.clone(),
214 None, !self.case_insensitive,
216 );
217 self.update_column_widths();
218 } else {
219 self.view.clear_filter();
220 self.update_column_widths();
221 }
222 }
223
224 pub fn table(&self) -> &DataTable {
226 self.view.table()
227 }
228
229 pub fn view(&self) -> &DataTableView {
231 &self.view
232 }
233}
234
235impl BufferAPI for DataTableBuffer {
236 fn get_id(&self) -> usize {
238 self.id
239 }
240
241 fn get_query(&self) -> String {
243 self.input_manager.get_text()
244 }
245
246 fn set_query(&mut self, query: String) {
247 self.input_manager.set_text(query.clone());
248 self.input = Input::new(query.clone()).with_cursor(query.len());
249 }
250
251 fn get_last_query(&self) -> String {
254 self.last_query.clone()
255 }
256
257 fn set_last_query(&mut self, query: String) {
258 self.last_query = query;
259 }
260
261 fn get_datatable(&self) -> Option<&DataTable> {
263 Some(self.view.get_datatable())
264 }
265
266 fn get_datatable_mut(&mut self) -> Option<&mut DataTable> {
267 Some(self.view.get_datatable_mut())
268 }
269
270 fn has_datatable(&self) -> bool {
271 true }
273
274 fn get_original_source(&self) -> Option<&DataTable> {
275 Some(self.view.table())
278 }
279
280 fn set_datatable(&mut self, datatable: Option<Arc<DataTable>>) {
281 if let Some(dt) = datatable {
284 debug!(
285 "V50: DataTableBuffer received DataTable with {} rows",
286 dt.row_count()
287 );
288 }
289 }
290
291 fn set_results_as_datatable(&mut self, _response: Option<QueryResponse>) -> Result<(), String> {
292 Ok(())
295 }
296
297 fn get_dataview(&self) -> Option<&DataView> {
299 self.dataview.as_ref()
300 }
301 fn get_dataview_mut(&mut self) -> Option<&mut DataView> {
302 self.dataview.as_mut()
303 }
304 fn set_dataview(&mut self, dataview: Option<DataView>) {
305 debug!(
306 "V51: Setting DataView with {} rows in DataTableBuffer",
307 dataview.as_ref().map(|v| v.row_count()).unwrap_or(0)
308 );
309 self.dataview = dataview;
310 }
311 fn has_dataview(&self) -> bool {
312 self.dataview.is_some()
313 }
314
315 fn get_mode(&self) -> AppMode {
317 self.mode.clone()
318 }
319
320 fn set_mode(&mut self, mode: AppMode) {
321 self.mode = mode;
322 }
323
324 fn get_edit_mode(&self) -> EditMode {
325 self.edit_mode.clone()
326 }
327
328 fn set_edit_mode(&mut self, mode: EditMode) {
329 self.edit_mode = mode;
330 }
331
332 fn get_status_message(&self) -> String {
333 self.status_message.clone()
334 }
335
336 fn set_status_message(&mut self, message: String) {
337 self.status_message = message;
338 }
339
340 fn get_selected_row(&self) -> Option<usize> {
342 self.last_results_row
343 }
344
345 fn set_selected_row(&mut self, row: Option<usize>) {
346 self.last_results_row = row;
347 }
348
349 fn get_current_column(&self) -> usize {
350 self.current_column
351 }
352
353 fn set_current_column(&mut self, col: usize) {
354 self.current_column = col;
355 }
356
357 fn get_scroll_offset(&self) -> (usize, usize) {
358 self.scroll_offset
359 }
360
361 fn set_scroll_offset(&mut self, offset: (usize, usize)) {
362 self.scroll_offset = offset;
363 }
364
365 fn get_last_results_row(&self) -> Option<usize> {
366 self.last_results_row
367 }
368
369 fn set_last_results_row(&mut self, row: Option<usize>) {
370 self.last_results_row = row;
371 }
372
373 fn get_last_scroll_offset(&self) -> (usize, usize) {
374 self.last_scroll_offset
375 }
376
377 fn set_last_scroll_offset(&mut self, offset: (usize, usize)) {
378 self.last_scroll_offset = offset;
379 }
380
381 fn get_filter_pattern(&self) -> String {
383 self.filter_state.pattern.clone()
384 }
385
386 fn set_filter_pattern(&mut self, pattern: String) {
387 self.filter_state.pattern = pattern;
388 self.sync_filter_to_view();
389 }
390
391 fn is_filter_active(&self) -> bool {
392 self.filter_state.active
393 }
394
395 fn set_filter_active(&mut self, active: bool) {
396 self.filter_state.active = active;
397 self.sync_filter_to_view();
398 }
399
400 fn get_fuzzy_filter_pattern(&self) -> String {
404 self.fuzzy_filter_state.pattern.clone()
405 }
406
407 fn set_fuzzy_filter_pattern(&mut self, pattern: String) {
408 self.fuzzy_filter_state.pattern = pattern;
409 }
411
412 fn is_fuzzy_filter_active(&self) -> bool {
413 self.fuzzy_filter_state.active
414 }
415
416 fn set_fuzzy_filter_active(&mut self, active: bool) {
417 self.fuzzy_filter_state.active = active;
418 }
419
420 fn get_fuzzy_filter_indices(&self) -> &Vec<usize> {
421 &self.fuzzy_filter_state.filtered_indices
422 }
423
424 fn set_fuzzy_filter_indices(&mut self, indices: Vec<usize>) {
425 self.fuzzy_filter_state.filtered_indices = indices;
426 }
427
428 fn clear_fuzzy_filter(&mut self) {
429 self.fuzzy_filter_state.active = false;
430 self.fuzzy_filter_state.pattern.clear();
431 self.fuzzy_filter_state.filtered_indices.clear();
432 }
433
434 fn get_search_pattern(&self) -> String {
436 self.search_state.pattern.clone()
437 }
438
439 fn set_search_pattern(&mut self, pattern: String) {
440 self.search_state.pattern = pattern.clone();
441 if !pattern.is_empty() {
443 self.view.start_search(pattern, !self.case_insensitive);
444 }
445 }
446
447 fn get_search_matches(&self) -> Vec<(usize, usize)> {
448 self.search_state.matches.clone()
449 }
450
451 fn set_search_matches(&mut self, matches: Vec<(usize, usize)>) {
452 self.search_state.matches = matches;
453 }
454
455 fn get_current_match(&self) -> Option<(usize, usize)> {
456 self.search_state.current_match
457 }
458
459 fn set_current_match(&mut self, match_pos: Option<(usize, usize)>) {
460 self.search_state.current_match = match_pos;
461 }
462
463 fn get_search_match_index(&self) -> usize {
464 self.search_state.match_index
465 }
466
467 fn set_search_match_index(&mut self, index: usize) {
468 self.search_state.match_index = index;
469 }
470
471 fn clear_search_state(&mut self) {
472 self.search_state.pattern.clear();
473 self.search_state.matches.clear();
474 self.search_state.current_match = None;
475 self.search_state.match_index = 0;
476 self.view.clear_search();
477 }
478
479 fn get_column_stats(&self) -> Option<&ColumnStatistics> {
484 self.column_stats.as_ref()
485 }
486
487 fn set_column_stats(&mut self, stats: Option<ColumnStatistics>) {
488 self.column_stats = stats;
489 }
490
491 fn get_sort_column(&self) -> Option<usize> {
493 self.sort_state.column
494 }
495
496 fn set_sort_column(&mut self, column: Option<usize>) {
497 self.sort_state.column = column;
498 self.sync_sort_to_view();
499 }
500
501 fn get_sort_order(&self) -> SortOrder {
502 self.sort_state.order
503 }
504
505 fn set_sort_order(&mut self, order: SortOrder) {
506 self.sort_state.order = order;
507 self.sync_sort_to_view();
508 }
509
510 fn get_name(&self) -> String {
512 self.name.clone()
513 }
514
515 fn set_name(&mut self, name: String) {
516 self.name = name;
517 }
518
519 fn get_file_path(&self) -> Option<&PathBuf> {
520 self.file_path.as_ref()
521 }
522
523 fn set_file_path(&mut self, path: Option<String>) {
524 self.file_path = path.map(PathBuf::from);
525 }
526
527 fn is_modified(&self) -> bool {
528 self.modified
529 }
530
531 fn set_modified(&mut self, modified: bool) {
532 self.modified = modified;
533 }
534
535 fn get_last_query_source(&self) -> Option<String> {
536 self.last_query_source.clone()
537 }
538
539 fn set_last_query_source(&mut self, source: Option<String>) {
540 self.last_query_source = source;
541 }
542
543 fn get_column_widths(&self) -> &Vec<u16> {
547 &self.column_widths
548 }
549
550 fn set_column_widths(&mut self, widths: Vec<u16>) {
551 self.column_widths = widths;
552 }
553
554 fn is_compact_mode(&self) -> bool {
559 self.compact_mode
560 }
561
562 fn set_compact_mode(&mut self, compact: bool) {
563 self.compact_mode = compact;
564 }
565
566 fn is_viewport_lock(&self) -> bool {
567 self.viewport_lock
568 }
569
570 fn set_viewport_lock(&mut self, locked: bool) {
571 self.viewport_lock = locked;
572 }
573
574 fn get_viewport_lock_row(&self) -> Option<usize> {
575 self.viewport_lock_row
576 }
577
578 fn set_viewport_lock_row(&mut self, row: Option<usize>) {
579 self.viewport_lock_row = row;
580 }
581
582 fn is_show_row_numbers(&self) -> bool {
583 self.show_row_numbers
584 }
585
586 fn set_show_row_numbers(&mut self, show: bool) {
587 self.show_row_numbers = show;
588 }
589
590 fn is_case_insensitive(&self) -> bool {
591 self.case_insensitive
592 }
593
594 fn set_case_insensitive(&mut self, insensitive: bool) {
595 self.case_insensitive = insensitive;
596 }
597
598 fn get_input_value(&self) -> String {
600 self.input_manager.get_text()
601 }
602
603 fn set_input_value(&mut self, value: String) {
604 self.input_manager.set_text(value.clone());
605 self.input = Input::new(value.clone()).with_cursor(value.len());
606 }
607
608 fn get_input_cursor(&self) -> usize {
609 self.input_manager.get_cursor_position()
610 }
611
612 fn set_input_cursor(&mut self, pos: usize) {
613 self.input_manager.set_cursor_position(pos);
614 }
615
616 fn get_kill_ring(&self) -> String {
618 self.kill_ring.clone()
619 }
620
621 fn set_kill_ring(&mut self, text: String) {
622 self.kill_ring = text;
623 }
624
625 fn get_last_visible_rows(&self) -> usize {
626 self.last_visible_rows
627 }
628
629 fn set_last_visible_rows(&mut self, rows: usize) {
630 self.last_visible_rows = rows;
631 }
632
633 fn apply_filter(&mut self) -> Result<()> {
635 self.sync_filter_to_view();
636 Ok(())
637 }
638
639 fn apply_sort(&mut self) -> Result<()> {
640 self.sync_sort_to_view();
641 Ok(())
642 }
643
644 fn search(&mut self) -> Result<()> {
645 Ok(())
647 }
648
649 fn clear_filters(&mut self) {
650 self.filter_state.active = false;
651 self.filter_state.pattern.clear();
652 self.view.clear_filter();
653 }
654
655 fn get_row_count(&self) -> usize {
656 self.view.table().row_count()
657 }
658
659 fn get_column_count(&self) -> usize {
660 self.view.table().column_count()
661 }
662
663 fn get_column_names(&self) -> Vec<String> {
664 self.view
665 .table()
666 .columns
667 .iter()
668 .map(|col| col.name.clone())
669 .collect()
670 }
671
672 fn get_undo_stack(&self) -> &Vec<(String, usize)> {
676 &self.undo_stack
677 }
678
679 fn push_undo(&mut self, state: (String, usize)) {
680 self.undo_stack.push(state);
681 if self.undo_stack.len() > 100 {
683 self.undo_stack.remove(0);
684 }
685 }
686
687 fn pop_undo(&mut self) -> Option<(String, usize)> {
688 self.undo_stack.pop()
689 }
690
691 fn get_redo_stack(&self) -> &Vec<(String, usize)> {
692 &self.redo_stack
693 }
694
695 fn push_redo(&mut self, state: (String, usize)) {
696 self.redo_stack.push(state);
697 if self.redo_stack.len() > 100 {
699 self.redo_stack.remove(0);
700 }
701 }
702
703 fn pop_redo(&mut self) -> Option<(String, usize)> {
704 self.redo_stack.pop()
705 }
706
707 fn clear_redo(&mut self) {
708 self.redo_stack.clear();
709 }
710
711 fn is_kill_ring_empty(&self) -> bool {
713 self.kill_ring.is_empty()
714 }
715
716 fn perform_undo(&mut self) -> bool {
717 if let Some((text, cursor)) = self.pop_undo() {
718 let current_state = (self.get_input_value(), self.get_input_cursor());
720 self.push_redo(current_state);
721
722 self.set_input_value(text);
724 self.set_input_cursor(cursor);
725 true
726 } else {
727 false
728 }
729 }
730
731 fn perform_redo(&mut self) -> bool {
732 if let Some((text, cursor)) = self.pop_redo() {
733 let current_state = (self.get_input_value(), self.get_input_cursor());
735 self.push_undo(current_state);
736
737 self.set_input_value(text);
739 self.set_input_cursor(cursor);
740 true
741 } else {
742 false
743 }
744 }
745
746 fn save_state_for_undo(&mut self) {
747 let current_state = (self.get_input_value(), self.get_input_cursor());
748 self.push_undo(current_state);
749 }
750
751 fn debug_dump(&self) -> String {
752 format!(
753 "DataTableBuffer {{ id: {}, name: \"{}\", rows: {}, cols: {} }}",
754 self.id,
755 self.name,
756 self.get_row_count(),
757 self.get_column_count()
758 )
759 }
760
761 fn get_input_text(&self) -> String {
762 self.input_manager.get_text()
763 }
764
765 fn set_input_text(&mut self, text: String) {
766 self.input_manager.set_text(text.clone());
767 self.input = Input::new(text.clone()).with_cursor(text.len());
768 }
769
770 fn handle_input_key(&mut self, event: KeyEvent) -> bool {
771 self.input_manager.handle_key_event(event)
772 }
773
774 fn switch_input_mode(&mut self, _multiline: bool) {
775 self.edit_mode = EditMode::SingleLine;
777 let current_text = self.input_manager.get_text();
778 self.input_manager = create_single_line(current_text);
779 }
780
781 fn get_input_cursor_position(&self) -> usize {
782 self.input_manager.get_cursor_position()
783 }
784
785 fn set_input_cursor_position(&mut self, position: usize) {
786 self.input_manager.set_cursor_position(position);
787 }
788
789 fn is_input_multiline(&self) -> bool {
790 matches!(self.edit_mode, EditMode::MultiLine)
791 }
792
793 fn navigate_history_up(&mut self, _history: &[String]) -> bool {
794 false
797 }
798
799 fn navigate_history_down(&mut self, _history: &[String]) -> bool {
800 false
803 }
804
805 fn reset_history_navigation(&mut self) {
806 }
808
809 fn clear_results(&mut self) {
810 }
813}