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