sql_cli/
buffer.rs

1use crate::api_client::QueryResponse; // V50: Still needed for conversion from API responses
2use crate::csv_datasource::CsvApiClient; // Kept for API compatibility in from_csv/from_json
3use 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// Re-define the types we need (these should eventually be moved to a common module)
21#[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(), // Create new matcher
97            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// ColumnSearchState: MIGRATED to AppStateContainer
133
134#[derive(Clone, Debug, PartialEq)]
135pub enum SelectionMode {
136    Row,
137    Cell,
138    Column,
139}
140
141/// ViewState consolidates all view-related state for a buffer
142/// This is the single source of truth for navigation, selection, and viewport state
143#[derive(Clone, Debug)]
144pub struct ViewState {
145    // Position
146    pub crosshair_row: usize,
147    pub crosshair_col: usize,
148    pub scroll_offset: (usize, usize),
149
150    // Selection
151    pub selection_mode: SelectionMode,
152    pub selected_cells: Vec<(usize, usize)>,
153    pub selection_anchor: Option<(usize, usize)>,
154
155    // Viewport config
156    pub viewport_lock: bool,
157    pub cursor_lock: bool,
158
159    // Navigation history
160    pub navigation_history: Vec<(usize, usize)>,
161    pub history_index: usize,
162
163    // Viewport dimensions (cached from last render)
164    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    // For all columns
203    pub total_count: usize,
204    pub null_count: usize,
205    pub unique_count: usize,
206    // For categorical/string columns
207    pub frequency_map: Option<BTreeMap<String, usize>>,
208    // For numeric columns
209    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
216// ColumnSearchState Default impl: MIGRATED to AppStateContainer
217
218// pub type ColumnStatistics = std::collections::BTreeMap<String, String>; // Replaced with struct
219
220/// BufferAPI trait - defines the interface for interacting with buffer state
221/// This abstraction allows the TUI to work with buffer state without knowing
222/// the implementation details, enabling gradual migration and testing
223pub trait BufferAPI: Send + Sync {
224    // --- Identity ---
225    fn get_id(&self) -> usize;
226    // --- Query ---
227    fn get_query(&self) -> String;
228    fn set_query(&mut self, query: String);
229    // V50: Removed get_results/set_results - use DataTable methods instead
230    fn get_last_query(&self) -> String;
231    fn set_last_query(&mut self, query: String);
232
233    // --- V50: DataTable is primary storage ---
234    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    /// V50: Helper to convert QueryResponse to DataTable and store it
240    fn set_results_as_datatable(&mut self, response: Option<QueryResponse>) -> Result<(), String>;
241
242    // --- V51: DataView support (direct query results) ---
243    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    // --- Mode and Status ---
249    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    // --- Table Navigation ---
257    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    // --- Filtering ---
269    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    // REMOVED: get_filtered_data/set_filtered_data - DataView handles filtering
274
275    // --- Fuzzy Filter ---
276    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    // --- Search ---
285    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    // --- Column Search ---
296
297    // --- Column Statistics ---
298    fn get_column_stats(&self) -> Option<&ColumnStatistics>;
299    fn set_column_stats(&mut self, stats: Option<ColumnStatistics>);
300
301    // --- Sorting ---
302    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    // --- Display Options ---
308    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    // REMOVED: pinned_columns methods - DataView handles pinned columns
317    // REMOVED: hidden_columns methods - DataView handles column visibility
318    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    // --- Buffer Metadata ---
324    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    // --- CSV/Data Source ---
334    // REMOVED: CSV/Cache methods - legacy data access patterns
335
336    // --- Input State ---
337    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    // --- Advanced Operations ---
343    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    // --- Edit State ---
352    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    // High-level undo/redo operations
364    fn perform_undo(&mut self) -> bool;
365    fn perform_redo(&mut self) -> bool;
366    fn save_state_for_undo(&mut self);
367
368    // --- Viewport State ---
369    fn get_last_visible_rows(&self) -> usize;
370    fn set_last_visible_rows(&mut self, rows: usize);
371
372    // --- Debug ---
373    fn debug_dump(&self) -> String;
374
375    // --- Input Management ---
376    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    // --- History Navigation ---
385    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    // --- Results Management ---
390    fn clear_results(&mut self);
391}
392
393/// Represents a single buffer/tab with its own independent state
394pub struct Buffer {
395    /// Unique identifier for this buffer
396    pub id: usize,
397
398    /// File path if loaded from file
399    pub file_path: Option<PathBuf>,
400
401    /// Display name (filename or "untitled")
402    pub name: String,
403
404    /// Whether this buffer has unsaved changes
405    pub modified: bool,
406
407    // --- Data State ---
408    pub datatable: Option<Arc<DataTable>>,
409    /// Original unmodified DataTable (preserved for query operations)
410    pub original_source: Option<Arc<DataTable>>,
411    /// DataView for applying filters like hidden columns without modifying the DataTable
412    pub dataview: Option<DataView>,
413
414    // --- UI State ---
415    pub mode: AppMode,
416    pub edit_mode: EditMode,
417    pub input: Input, // Legacy - kept for compatibility during migration
418    pub input_manager: Box<dyn InputManager>, // New unified input management
419    pub table_state: TableState,
420    pub last_results_row: Option<usize>,
421    pub last_scroll_offset: (usize, usize),
422
423    // --- Query State ---
424    pub last_query: String,
425    pub status_message: String,
426
427    // --- Filter/Search State ---
428    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    // --- View State (Consolidated) ---
436    pub view_state: ViewState,
437
438    // --- Display Options (not navigation-related) ---
439    pub column_widths: Vec<u16>,
440    pub compact_mode: bool,
441    pub show_row_numbers: bool,
442    pub case_insensitive: bool,
443
444    // --- Misc State ---
445    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    // --- Syntax Highlighting ---
452    pub highlighted_text_cache: Option<Vec<(String, Color)>>, // Cache of highlighted tokens
453    pub last_highlighted_text: String, // Track what text was highlighted to detect changes
454
455    // --- Input State Stack (for search/filter modes) ---
456    pub saved_input_state: Option<(String, usize)>, // Save input when entering search/filter
457}
458
459// Implement BufferAPI for Buffer
460impl BufferAPI for Buffer {
461    // --- Identity ---
462    fn get_id(&self) -> usize {
463        self.id
464    }
465
466    // --- Query and Results ---
467    fn get_query(&self) -> String {
468        // Use InputManager if available, fallback to legacy input
469        self.input_manager.get_text()
470    }
471
472    fn set_query(&mut self, query: String) {
473        // Update both InputManager and legacy field for compatibility
474        self.input_manager.set_text(query.clone());
475        self.input = Input::new(query.clone()).with_cursor(query.len());
476    }
477
478    // V50: Removed get_results/set_results - use get_datatable/set_datatable instead
479
480    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    // --- V50: DataTable is primary storage ---
489    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        // Can't mutate through Arc - need to make a new copy if mutation is needed
495        // For now, return None as we shouldn't be mutating the DataTable directly
496        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        // Log current state
515        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        // Preserve the original source if this is the first data load
532        // Only update original_source if we don't have one yet
533        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        // When setting a DataTable, also create a DataView for it
542        // This ensures we always have a DataView as the source of truth for column visibility
543        if let Some(dt) = &datatable {
544            // Use the existing Arc, don't clone the DataTable!
545            let mut view = crate::data::data_view::DataView::new(dt.clone());
546
547            // Apply any existing hidden columns from the previous DataView
548            if let Some(old_view) = &self.dataview {
549                // Get list of hidden column names from old view
550                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                        // This column was hidden in the old view
556                        view.hide_column_by_name(col_name);
557                    }
558                }
559            }
560
561            // Optimize memory after setting up the view
562            view.shrink_to_fit();
563
564            // DataView now handles column visibility directly
565            self.dataview = Some(view);
566        } else {
567            self.dataview = None;
568        }
569
570        // IMPORTANT: Never replace the datatable if we have an original source
571        // and the new table has fewer columns (indicating it's a query result)
572        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                    // Don't replace the datatable with a reduced one
581                    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    // --- V51: DataView support (direct query results) ---
617    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    // --- Mode and Status ---
635    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    // --- Table Navigation ---
660    fn get_selected_row(&self) -> Option<usize> {
661        // For backward compatibility, check if table_state has a selection
662        // This maintains the old API behavior where None means no selection
663        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            // Also update table_state for compatibility during migration
670            self.table_state.select(Some(r));
671        } else {
672            // When setting to None, reset crosshair to 0 but clear table selection
673            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    // --- Filtering ---
711    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    // REMOVED: get_filtered_data/set_filtered_data implementations
728
729    // --- Fuzzy Filter ---
730    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    // --- Search ---
761    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    // --- Column Search ---
801
802    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    // --- Sorting ---
811    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    // --- Display Options ---
828    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        // Return current crosshair row when viewport is locked
854        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        // When setting viewport lock row, update the crosshair position
863        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    // --- Buffer Metadata ---
886    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    // --- Input State ---
919    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    // --- Advanced Operations ---
938    fn apply_filter(&mut self) -> Result<()> {
939        // TODO: Implement actual filtering logic
940        Ok(())
941    }
942
943    fn apply_sort(&mut self) -> Result<()> {
944        // TODO: Implement actual sorting logic
945        Ok(())
946    }
947
948    fn search(&mut self) -> Result<()> {
949        // TODO: Implement actual search logic
950        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        // DataView handles the actual filtering
959    }
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    // --- Edit State ---
986    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            // Save current state to redo stack
1020            let current_state = (self.get_input_text(), self.get_input_cursor_position());
1021            self.push_redo(current_state);
1022
1023            // Restore previous state
1024            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            // Save current state to undo stack
1035            let current_state = (self.get_input_text(), self.get_input_cursor_position());
1036            self.push_undo(current_state);
1037
1038            // Restore next state
1039            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    // --- Viewport State ---
1066    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() // Column search migrated to AppStateContainer
1142        ));
1143        output.push_str(&format!(
1144            "Matching Columns: {:?}\n",
1145            Vec::<(usize, String)>::new() // Column search migrated to AppStateContainer
1146        ));
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        // Pinned columns now handled by DataView
1155        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    // --- Input Management ---
1198    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        // Sync with legacy fields for compatibility
1205        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        // Sync with legacy fields after key handling
1211        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        // Always use single-line mode
1220        self.edit_mode = EditMode::SingleLine;
1221        self.input_manager = create_single_line(current_text.clone());
1222        // Update legacy input
1223        self.input =
1224            Input::new(current_text.clone()).with_cursor(cursor_pos.min(current_text.len()));
1225
1226        // Try to restore cursor position
1227        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        // Sync with legacy fields
1237        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    // --- History Navigation ---
1248    fn navigate_history_up(&mut self, history: &[String]) -> bool {
1249        // Set history if not already set
1250        self.input_manager.set_history(history.to_vec());
1251        let navigated = self.input_manager.history_previous();
1252        if navigated {
1253            // Sync to legacy fields
1254            self.sync_from_input_manager();
1255        }
1256        navigated
1257    }
1258
1259    fn navigate_history_down(&mut self, history: &[String]) -> bool {
1260        // Set history if not already set
1261        self.input_manager.set_history(history.to_vec());
1262        let navigated = self.input_manager.history_next();
1263        if navigated {
1264            // Sync to legacy fields
1265            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    // --- Results Management ---
1275    fn clear_results(&mut self) {
1276        self.datatable = None;
1277        // DataView handles filtering
1278        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        // Reset search/filter states
1285        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    /// Create a new empty buffer
1295    pub fn new(id: usize) -> Self {
1296        Self {
1297            id,
1298            file_path: None,
1299            name: format!("[Buffer {}]", id),
1300            modified: false,
1301
1302            // Legacy CSV/Cache fields removed
1303            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_search_state: MIGRATED to AppStateContainer
1326            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    /// Create a buffer from a CSV file
1347    pub fn from_csv(
1348        id: usize,
1349        path: PathBuf,
1350        _csv_client: CsvApiClient, // Kept for API compatibility but unused
1351        _table_name: String,       // Kept for API compatibility but unused
1352    ) -> 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        // Legacy CSV fields removed - DataTable/DataView handles data
1363
1364        buffer
1365    }
1366
1367    /// Create a buffer from a JSON file
1368    pub fn from_json(
1369        id: usize,
1370        path: PathBuf,
1371        _csv_client: CsvApiClient, // Kept for API compatibility but unused
1372        _table_name: String,       // Kept for API compatibility but unused
1373    ) -> 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        // Legacy CSV fields removed - DataTable/DataView handles data
1384
1385        buffer
1386    }
1387
1388    /// Get display name for tab bar
1389    pub fn display_name(&self) -> String {
1390        if self.modified {
1391            format!("{}*", self.name)
1392        } else {
1393            self.name.clone()
1394        }
1395    }
1396
1397    /// Get short name for tab bar (truncated if needed)
1398    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    /// Check if buffer has a specific file open
1408    pub fn has_file(&self, path: &PathBuf) -> bool {
1409        self.file_path.as_ref() == Some(path)
1410    }
1411
1412    /// Sync from InputManager to legacy fields (for compatibility during migration)
1413    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        // Always sync to single-line input
1418        let text_len = text.len();
1419        self.input = Input::new(text).with_cursor(cursor_pos.min(text_len));
1420    }
1421
1422    /// Sync from legacy fields to InputManager (for compatibility during migration)
1423    fn sync_to_input_manager(&mut self) {
1424        // Always sync from single-line input
1425        let _text = self.input.value().to_string();
1426        self.input_manager = create_from_input(self.input.clone());
1427    }
1428
1429    // --- Cursor Movement Operations ---
1430    // These use the CursorOperations helper to provide intelligent
1431    // SQL-aware cursor movement and text manipulation
1432
1433    /// Move cursor to previous word boundary
1434    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    /// Move cursor to next word boundary
1444    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    /// Delete word backward from cursor
1453    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        // Store deleted text in kill ring
1459        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    /// Delete word forward from cursor
1469    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        // Store deleted text in kill ring
1475        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    /// Kill line from cursor to end
1486    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    /// Kill line from start to cursor
1497    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    /// Jump to previous SQL token
1510    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    /// Jump to next SQL token
1519    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    /// Yank (paste) from kill ring
1528    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            // Insert kill ring content at cursor position
1536            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    /// Expand SELECT * to column names using schema information
1548    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        // Find SELECT * pattern
1553        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                // Find FROM clause after the *
1558                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                    // Extract table name after FROM
1562                    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                        // Get columns from the schema
1571                        let columns = parser.get_table_columns(table_name);
1572
1573                        if !columns.is_empty() {
1574                            // Build the replacement with all columns
1575                            let columns_str = columns.join(", ");
1576
1577                            // Replace * with the column list
1578                            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                            // Update the input
1583                            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    /// Expand SELECT * to only visible column names
1604    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        // Find SELECT * pattern
1609        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                // Get visible columns from the DataView
1614                if let Some(dataview) = &self.dataview {
1615                    let visible_columns = dataview.get_display_column_names();
1616
1617                    if !visible_columns.is_empty() {
1618                        // Build the replacement with visible columns only
1619                        let columns_str = visible_columns.join(", ");
1620
1621                        // Replace * with the column list
1622                        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                        // Update the input
1627                        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
1648// Manual Clone implementation for Buffer due to Box<dyn InputManager>
1649impl Clone for Buffer {
1650    fn clone(&self) -> Self {
1651        // Always clone as single-line mode
1652        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            // Legacy CSV/Cache fields removed
1660            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_search_state: MIGRATED to AppStateContainer
1677            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
1695/// Manages multiple buffers and switching between them
1696pub 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    /// Add a new buffer and make it current
1712    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    /// Get current buffer
1723    pub fn current(&self) -> Option<&Buffer> {
1724        self.buffers.get(self.current_buffer_index)
1725    }
1726
1727    /// Get current buffer mutably
1728    pub fn current_mut(&mut self) -> Option<&mut Buffer> {
1729        self.buffers.get_mut(self.current_buffer_index)
1730    }
1731
1732    /// Switch to next buffer
1733    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    /// Switch to previous buffer
1740    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    /// Switch to buffer by index
1751    pub fn switch_to(&mut self, index: usize) {
1752        if index < self.buffers.len() {
1753            self.current_buffer_index = index;
1754        }
1755    }
1756
1757    /// Close current buffer
1758    pub fn close_current(&mut self) -> bool {
1759        if self.buffers.len() <= 1 {
1760            return false; // Don't close last buffer
1761        }
1762
1763        self.buffers.remove(self.current_buffer_index);
1764
1765        // Adjust current index if needed
1766        if self.current_buffer_index >= self.buffers.len() {
1767            self.current_buffer_index = self.buffers.len() - 1;
1768        }
1769
1770        true
1771    }
1772
1773    /// Find buffer by file path
1774    pub fn find_by_path(&self, path: &PathBuf) -> Option<usize> {
1775        self.buffers.iter().position(|b| b.has_file(path))
1776    }
1777
1778    /// Get all buffers for display
1779    pub fn all_buffers(&self) -> &[Buffer] {
1780        &self.buffers
1781    }
1782
1783    /// Get current buffer index
1784    pub fn current_index(&self) -> usize {
1785        self.current_buffer_index
1786    }
1787
1788    /// Check if we have multiple buffers
1789    pub fn has_multiple(&self) -> bool {
1790        self.buffers.len() > 1
1791    }
1792
1793    /// Clear all buffers (used when loading a new file)
1794    pub fn clear_all(&mut self) {
1795        self.buffers.clear();
1796        self.current_buffer_index = 0;
1797    }
1798}