sql_cli/
buffer_handler.rs

1use crate::buffer::{Buffer, BufferAPI, BufferManager};
2use crate::config::config::Config;
3use tracing::{debug, info};
4
5/// Handles all buffer-related operations
6pub struct BufferHandler {
7    /// Stack for tracking buffer history (for quick switching)
8    buffer_history: Vec<usize>,
9    /// Maximum number of buffers to keep in history
10    max_history: usize,
11}
12
13impl Default for BufferHandler {
14    fn default() -> Self {
15        Self::new()
16    }
17}
18
19impl BufferHandler {
20    #[must_use]
21    pub fn new() -> Self {
22        Self {
23            buffer_history: vec![0], // Start with buffer 0
24            max_history: 10,
25        }
26    }
27
28    /// Switch to next buffer
29    pub fn next_buffer(&mut self, manager: &mut BufferManager) -> String {
30        let prev_index = manager.current_index();
31        manager.next_buffer();
32        let index = manager.current_index();
33        let total = manager.all_buffers().len();
34
35        self.update_history(index);
36        debug!(target: "buffer", "Switched from buffer {} to {} (total: {})", prev_index + 1, index + 1, total);
37
38        format!("Switched to buffer {}/{}", index + 1, total)
39    }
40
41    /// Switch to previous buffer
42    pub fn previous_buffer(&mut self, manager: &mut BufferManager) -> String {
43        let prev_index = manager.current_index();
44        manager.prev_buffer();
45        let index = manager.current_index();
46        let total = manager.all_buffers().len();
47
48        self.update_history(index);
49        debug!(target: "buffer", "Switched from buffer {} to {} (total: {})", prev_index + 1, index + 1, total);
50
51        format!("Switched to buffer {}/{}", index + 1, total)
52    }
53
54    /// Quick switch between last two buffers (like vim's Ctrl+6)
55    pub fn quick_switch(&mut self, manager: &mut BufferManager) -> String {
56        if self.buffer_history.len() >= 2 {
57            // Get the previous buffer from history
58            let prev_buffer = self.buffer_history[self.buffer_history.len() - 2];
59            self.switch_to_buffer(manager, prev_buffer)
60        } else {
61            // If no history, just switch to next
62            self.next_buffer(manager)
63        }
64    }
65
66    /// Switch to specific buffer by index (0-based)
67    pub fn switch_to_buffer(&mut self, manager: &mut BufferManager, index: usize) -> String {
68        let total = manager.all_buffers().len();
69        if index >= total {
70            return format!(
71                "Buffer {} does not exist (have {} buffers)",
72                index + 1,
73                total
74            );
75        }
76
77        let prev_index = manager.current_index();
78        if prev_index == index {
79            return format!("Already on buffer {}/{}", index + 1, total);
80        }
81
82        // Set the buffer directly
83        if index == 0 {
84            while manager.current_index() != 0 {
85                manager.prev_buffer();
86            }
87        } else {
88            while manager.current_index() != index {
89                if manager.current_index() < index {
90                    manager.next_buffer();
91                } else {
92                    manager.prev_buffer();
93                }
94            }
95        }
96
97        self.update_history(index);
98        debug!(target: "buffer", "Switched from buffer {} to {} (total: {})", prev_index + 1, index + 1, total);
99
100        format!("Switched to buffer {}/{}", index + 1, total)
101    }
102
103    /// Create a new buffer
104    pub fn new_buffer(&mut self, manager: &mut BufferManager, config: &Config) -> String {
105        let buffer_id = manager.all_buffers().len() + 1;
106        let mut new_buffer = Buffer::new(buffer_id);
107
108        // Apply config settings to the new buffer
109        new_buffer.set_compact_mode(config.display.compact_mode);
110        new_buffer.set_case_insensitive(config.behavior.case_insensitive_default);
111        new_buffer.set_show_row_numbers(config.display.show_row_numbers);
112
113        info!(target: "buffer", "Creating new buffer with config: compact_mode={}, case_insensitive={}, show_row_numbers={}",
114              config.display.compact_mode,
115              config.behavior.case_insensitive_default,
116              config.display.show_row_numbers);
117
118        manager.add_buffer(new_buffer);
119        let index = manager.current_index();
120        let total = manager.all_buffers().len();
121
122        self.update_history(index);
123
124        format!("Created new buffer {}/{}", index + 1, total)
125    }
126
127    /// Close current buffer
128    pub fn close_buffer(&mut self, manager: &mut BufferManager) -> (bool, String) {
129        if manager.all_buffers().len() == 1 {
130            return (false, "Cannot close the last buffer".to_string());
131        }
132
133        if manager.close_current() {
134            let index = manager.current_index();
135            let total = manager.all_buffers().len();
136
137            // Remove closed buffer from history
138            self.buffer_history.retain(|&idx| idx < total);
139
140            // Update indices in history (shift down if needed)
141            for idx in &mut self.buffer_history {
142                if *idx > index {
143                    *idx -= 1;
144                }
145            }
146
147            self.update_history(index);
148
149            (
150                true,
151                format!("Buffer closed. Now at buffer {}/{}", index + 1, total),
152            )
153        } else {
154            (false, "Failed to close buffer".to_string())
155        }
156    }
157
158    /// List all buffers with their status
159    #[must_use]
160    pub fn list_buffers(&self, manager: &BufferManager) -> Vec<String> {
161        let current_index = manager.current_index();
162        let mut buffer_list = Vec::new();
163
164        for (i, buffer) in manager.all_buffers().iter().enumerate() {
165            let marker = if i == current_index { "▶" } else { " " };
166
167            // Get buffer info
168            let has_results = buffer.has_datatable();
169            let query = buffer.get_query();
170            let query_preview = if query.is_empty() {
171                "Empty".to_string()
172            } else if query.len() > 30 {
173                format!("{}...", &query[..27])
174            } else {
175                query.clone()
176            };
177
178            let status = if has_results { "●" } else { "○" };
179
180            buffer_list.push(format!(
181                "{} [{}] Buffer {}: {} {}",
182                marker,
183                status,
184                i + 1,
185                query_preview,
186                if i < 9 {
187                    format!("(Alt+{})", i + 1)
188                } else {
189                    String::new()
190                }
191            ));
192        }
193
194        // Add history at the bottom
195        if !self.buffer_history.is_empty() {
196            buffer_list.push(format!(
197                "  History: {}",
198                self.buffer_history
199                    .iter()
200                    .rev()
201                    .take(5)
202                    .map(|idx| format!("{}", idx + 1))
203                    .collect::<Vec<_>>()
204                    .join(" → ")
205            ));
206        }
207
208        buffer_list
209    }
210
211    /// Update buffer history
212    fn update_history(&mut self, index: usize) {
213        // Remove if already in history
214        self.buffer_history.retain(|&idx| idx != index);
215
216        // Add to end
217        self.buffer_history.push(index);
218
219        // Trim to max size
220        if self.buffer_history.len() > self.max_history {
221            self.buffer_history.remove(0);
222        }
223    }
224
225    /// Get buffer history
226    #[must_use]
227    pub fn get_history(&self) -> &[usize] {
228        &self.buffer_history
229    }
230
231    /// Clear buffer history
232    pub fn clear_history(&mut self) {
233        self.buffer_history.clear();
234        self.buffer_history.push(0);
235    }
236}