sql_cli/ui/traits/
input_ops.rs

1use crate::app_state_container::AppStateContainer;
2use crate::buffer::{AppMode, BufferAPI, BufferManager, EditMode};
3use crate::cursor_manager::CursorManager;
4use crate::ui::utils::text_operations::{self, CursorMovementResult, TextOperationResult};
5// Arc import removed - no longer needed
6
7/// Trait that provides input operation behavior for TUI components
8/// This uses pure text manipulation functions and applies results to TUI state
9pub trait InputBehavior {
10    // Required methods - these provide access to TUI internals
11    fn buffer_manager(&mut self) -> &mut BufferManager;
12    fn cursor_manager(&mut self) -> &mut CursorManager;
13    fn set_input_text_with_cursor(&mut self, text: String, cursor: usize);
14    fn state_container(&self) -> &AppStateContainer;
15    fn state_container_mut(&mut self) -> &mut AppStateContainer; // Added for mutable access
16    fn buffer_mut(&mut self) -> &mut dyn BufferAPI;
17
18    // Mode switching method that needs to be implemented by EnhancedTui to handle shadow_state
19    fn set_mode_with_sync(&mut self, mode: AppMode, trigger: &str);
20
21    // Helper method to get current input state
22    fn get_current_input(&mut self) -> (String, usize) {
23        if let Some(buffer) = self.buffer_manager().current() {
24            (buffer.get_input_text(), buffer.get_input_cursor_position())
25        } else {
26            (String::new(), 0)
27        }
28    }
29
30    // Helper method to apply text operation results
31    fn apply_text_result(&mut self, result: TextOperationResult) {
32        if let Some(buffer) = self.buffer_manager().current_mut() {
33            // Update buffer text and cursor
34            buffer.set_input_text(result.new_text.clone());
35            buffer.set_input_cursor_position(result.new_cursor_position);
36
37            // Add killed text to kill ring if present
38            if let Some(killed) = result.killed_text {
39                buffer.set_kill_ring(killed);
40            }
41
42            // Sync for rendering if single-line mode
43            if buffer.get_edit_mode() == EditMode::SingleLine {
44                self.set_input_text_with_cursor(result.new_text, result.new_cursor_position);
45                self.cursor_manager()
46                    .set_position(result.new_cursor_position);
47            }
48        }
49    }
50
51    // Helper method to apply cursor movement results
52    fn apply_cursor_result(&mut self, result: CursorMovementResult) {
53        if let Some(buffer) = self.buffer_manager().current_mut() {
54            buffer.set_input_cursor_position(result.new_position);
55
56            // Sync for rendering if single-line mode
57            if buffer.get_edit_mode() == EditMode::SingleLine {
58                let text = buffer.get_input_text();
59                self.set_input_text_with_cursor(text, result.new_position);
60                self.cursor_manager().set_position(result.new_position);
61            }
62        }
63    }
64
65    // ========== Text Manipulation Operations ==========
66
67    /// Kill text from cursor to end of line (Ctrl+K)
68    fn kill_line(&mut self) {
69        if let Some(buffer) = self.buffer_manager().current_mut() {
70            buffer.save_state_for_undo();
71        }
72
73        let (text, cursor) = self.get_current_input();
74        let result = text_operations::kill_line(&text, cursor);
75        self.apply_text_result(result);
76    }
77
78    /// Kill text from beginning of line to cursor (Ctrl+U)
79    fn kill_line_backward(&mut self) {
80        if let Some(buffer) = self.buffer_manager().current_mut() {
81            buffer.save_state_for_undo();
82        }
83
84        let (text, cursor) = self.get_current_input();
85        let result = text_operations::kill_line_backward(&text, cursor);
86        self.apply_text_result(result);
87    }
88
89    /// Delete word backward from cursor (Ctrl+W)
90    fn delete_word_backward(&mut self) {
91        if let Some(buffer) = self.buffer_manager().current_mut() {
92            buffer.save_state_for_undo();
93        }
94
95        let (text, cursor) = self.get_current_input();
96        let result = text_operations::delete_word_backward(&text, cursor);
97        self.apply_text_result(result);
98    }
99
100    /// Delete word forward from cursor (Alt+D)
101    fn delete_word_forward(&mut self) {
102        if let Some(buffer) = self.buffer_manager().current_mut() {
103            buffer.save_state_for_undo();
104        }
105
106        let (text, cursor) = self.get_current_input();
107        let result = text_operations::delete_word_forward(&text, cursor);
108        self.apply_text_result(result);
109    }
110
111    // ========== Cursor Movement Operations ==========
112
113    /// Move cursor backward one word (Ctrl+Left or Alt+B)
114    fn move_cursor_word_backward(&mut self) {
115        let (text, cursor) = self.get_current_input();
116        let result = text_operations::move_word_backward(&text, cursor);
117        self.apply_cursor_result(result);
118    }
119
120    /// Move cursor forward one word (Ctrl+Right or Alt+F)
121    fn move_cursor_word_forward(&mut self) {
122        let (text, cursor) = self.get_current_input();
123        let result = text_operations::move_word_forward(&text, cursor);
124        self.apply_cursor_result(result);
125    }
126
127    /// Jump to previous SQL token (Alt+[)
128    fn jump_to_prev_token(&mut self) {
129        let (text, cursor) = self.get_current_input();
130        let result = text_operations::jump_to_prev_token(&text, cursor);
131        self.apply_cursor_result(result);
132    }
133
134    /// Jump to next SQL token (Alt+])
135    fn jump_to_next_token(&mut self) {
136        let (text, cursor) = self.get_current_input();
137        let result = text_operations::jump_to_next_token(&text, cursor);
138        self.apply_cursor_result(result);
139    }
140
141    // ========== Basic Text Operations ==========
142
143    /// Insert a character at the cursor position
144    fn insert_char(&mut self, ch: char) {
145        if let Some(buffer) = self.buffer_manager().current_mut() {
146            buffer.save_state_for_undo();
147        }
148
149        let (text, cursor) = self.get_current_input();
150        let result = text_operations::insert_char(&text, cursor, ch);
151        self.apply_text_result(result);
152    }
153
154    /// Delete character at cursor position (Delete key)
155    fn delete_char(&mut self) {
156        if let Some(buffer) = self.buffer_manager().current_mut() {
157            buffer.save_state_for_undo();
158        }
159
160        let (text, cursor) = self.get_current_input();
161        let result = text_operations::delete_char(&text, cursor);
162        self.apply_text_result(result);
163    }
164
165    /// Delete character before cursor (Backspace)
166    fn backspace(&mut self) {
167        if let Some(buffer) = self.buffer_manager().current_mut() {
168            buffer.save_state_for_undo();
169        }
170
171        let (text, cursor) = self.get_current_input();
172        let result = text_operations::backspace(&text, cursor);
173        self.apply_text_result(result);
174    }
175
176    /// Clear all input text
177    fn clear_input(&mut self) {
178        if let Some(buffer) = self.buffer_manager().current_mut() {
179            buffer.save_state_for_undo();
180        }
181
182        let result = text_operations::clear_text();
183        self.apply_text_result(result);
184    }
185
186    // ========== Jump-to-Row Input Management ==========
187
188    /// Get jump-to-row input text
189    fn get_jump_to_row_input(&self) -> String {
190        self.state_container().jump_to_row().input.clone()
191    }
192
193    /// Set jump-to-row input text
194    fn set_jump_to_row_input(&mut self, input: String) {
195        // Can directly mutate now that we have state_container_mut
196        self.state_container_mut().jump_to_row_mut().input = input;
197    }
198
199    /// Clear jump-to-row input
200    fn clear_jump_to_row_input(&mut self) {
201        // Can directly mutate now that we have state_container_mut
202        self.state_container_mut().jump_to_row_mut().input.clear();
203    }
204
205    /// Process jump-to-row input key event (handles all keys except Enter)
206    fn process_jump_to_row_key(&mut self, key: crossterm::event::KeyEvent) -> bool {
207        use crossterm::event::KeyCode;
208
209        match key.code {
210            KeyCode::Esc => {
211                // Use proper mode synchronization that updates both buffer and shadow_state
212                self.set_mode_with_sync(AppMode::Results, "escape_from_jump_to_row");
213                self.clear_jump_to_row_input();
214
215                // Clear is_active flag
216                self.state_container_mut().jump_to_row_mut().is_active = false;
217                true
218            }
219            KeyCode::Backspace => {
220                let mut input = self.get_jump_to_row_input();
221                input.pop();
222                self.set_jump_to_row_input(input);
223                true
224            }
225            KeyCode::Char(c) if c.is_ascii_digit() => {
226                let mut input = self.get_jump_to_row_input();
227                input.push(c);
228                self.set_jump_to_row_input(input);
229                true
230            }
231            _ => false,
232        }
233    }
234}