Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 120 additions & 0 deletions sql-cli/src/ui/action_handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,16 @@ pub trait ActionHandlerContext {
// Statistics and display
fn show_column_statistics(&mut self);
fn cycle_column_packing(&mut self);

// Input and text editing
fn move_input_cursor_left(&mut self);
fn move_input_cursor_right(&mut self);
fn move_input_cursor_home(&mut self);
fn move_input_cursor_end(&mut self);
fn backspace(&mut self);
fn delete(&mut self);
fn undo(&mut self);
fn redo(&mut self);
}

/// Handler for navigation actions (Up, Down, Left, Right, PageUp, etc.)
Expand Down Expand Up @@ -527,6 +537,88 @@ impl ActionHandler for StatisticsActionHandler {
}
}

/// Handler for cursor movement actions in Command mode
pub struct InputCursorActionHandler;

impl ActionHandler for InputCursorActionHandler {
fn handle_action(
&self,
action: &Action,
context: &ActionContext,
tui: &mut dyn ActionHandlerContext,
) -> Option<Result<ActionResult>> {
// Only handle in Command mode
if context.mode != AppMode::Command {
return None;
}

match action {
Action::MoveCursorLeft => {
tui.move_input_cursor_left();
Some(Ok(ActionResult::Handled))
}
Action::MoveCursorRight => {
tui.move_input_cursor_right();
Some(Ok(ActionResult::Handled))
}
Action::MoveCursorHome => {
tui.move_input_cursor_home();
Some(Ok(ActionResult::Handled))
}
Action::MoveCursorEnd => {
tui.move_input_cursor_end();
Some(Ok(ActionResult::Handled))
}
_ => None,
}
}

fn name(&self) -> &'static str {
"InputCursor"
}
}

/// Handler for text editing actions in Command mode
pub struct TextEditActionHandler;

impl ActionHandler for TextEditActionHandler {
fn handle_action(
&self,
action: &Action,
context: &ActionContext,
tui: &mut dyn ActionHandlerContext,
) -> Option<Result<ActionResult>> {
// Only handle in Command mode
if context.mode != AppMode::Command {
return None;
}

match action {
Action::Backspace => {
tui.backspace();
Some(Ok(ActionResult::Handled))
}
Action::Delete => {
tui.delete();
Some(Ok(ActionResult::Handled))
}
Action::Undo => {
tui.undo();
Some(Ok(ActionResult::Handled))
}
Action::Redo => {
tui.redo();
Some(Ok(ActionResult::Handled))
}
_ => None,
}
}

fn name(&self) -> &'static str {
"TextEdit"
}
}

/// Main action dispatcher using visitor pattern
pub struct ActionDispatcher {
handlers: Vec<Box<dyn ActionHandler>>,
Expand All @@ -547,6 +639,8 @@ impl ActionDispatcher {
Box::new(ColumnArrangementActionHandler),
Box::new(SearchNavigationActionHandler),
Box::new(StatisticsActionHandler),
Box::new(InputCursorActionHandler),
Box::new(TextEditActionHandler),
];

Self { handlers }
Expand Down Expand Up @@ -750,6 +844,32 @@ mod tests {
fn cycle_column_packing(&mut self) {
self.last_action = "cycle_column_packing".to_string();
}

// Input and text editing
fn move_input_cursor_left(&mut self) {
self.last_action = "move_input_cursor_left".to_string();
}
fn move_input_cursor_right(&mut self) {
self.last_action = "move_input_cursor_right".to_string();
}
fn move_input_cursor_home(&mut self) {
self.last_action = "move_input_cursor_home".to_string();
}
fn move_input_cursor_end(&mut self) {
self.last_action = "move_input_cursor_end".to_string();
}
fn backspace(&mut self) {
self.last_action = "backspace".to_string();
}
fn delete(&mut self) {
self.last_action = "delete".to_string();
}
fn undo(&mut self) {
self.last_action = "undo".to_string();
}
fn redo(&mut self) {
self.last_action = "redo".to_string();
}
}

#[test]
Expand Down
177 changes: 81 additions & 96 deletions sql-cli/src/ui/enhanced_tui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -666,103 +666,23 @@ impl EnhancedTuiApp {
}

// Editing actions - only work in Command mode
MoveCursorLeft => {
if context.mode == AppMode::Command {
let buffer = self.buffer_mut();
let pos = buffer.get_input_cursor_position();
if pos > 0 {
buffer.set_input_cursor_position(pos - 1);
}
Ok(ActionResult::Handled)
} else {
Ok(ActionResult::NotHandled)
}
}
MoveCursorRight => {
if context.mode == AppMode::Command {
let buffer = self.buffer_mut();
let pos = buffer.get_input_cursor_position();
let text_len = buffer.get_input_text().chars().count();
if pos < text_len {
buffer.set_input_cursor_position(pos + 1);
}
Ok(ActionResult::Handled)
} else {
Ok(ActionResult::NotHandled)
}
}
MoveCursorHome => {
if context.mode == AppMode::Command {
self.buffer_mut().set_input_cursor_position(0);
Ok(ActionResult::Handled)
} else {
Ok(ActionResult::NotHandled)
}
}
MoveCursorEnd => {
if context.mode == AppMode::Command {
let text_len = self.buffer().get_input_text().chars().count();
self.buffer_mut().set_input_cursor_position(text_len);
Ok(ActionResult::Handled)
} else {
Ok(ActionResult::NotHandled)
}
}
Backspace => {
if context.mode == AppMode::Command {
let buffer = self.buffer_mut();
let pos = buffer.get_input_cursor_position();
if pos > 0 {
buffer.save_state_for_undo();
let mut text = buffer.get_input_text();
let mut chars: Vec<char> = text.chars().collect();
if pos <= chars.len() {
chars.remove(pos - 1);
text = chars.iter().collect();
buffer.set_input_text(text);
buffer.set_input_cursor_position(pos - 1);
}
}
Ok(ActionResult::Handled)
} else {
Ok(ActionResult::NotHandled)
}
}
Delete => {
if context.mode == AppMode::Command {
let buffer = self.buffer_mut();
let pos = buffer.get_input_cursor_position();
let mut text = buffer.get_input_text();
let chars_len = text.chars().count();
if pos < chars_len {
buffer.save_state_for_undo();
let mut chars: Vec<char> = text.chars().collect();
chars.remove(pos);
text = chars.iter().collect();
buffer.set_input_text(text);
}
Ok(ActionResult::Handled)
} else {
Ok(ActionResult::NotHandled)
}
}
// MoveCursorLeft is now handled by InputCursorActionHandler in visitor pattern
MoveCursorLeft => Ok(ActionResult::NotHandled),
// MoveCursorRight is now handled by InputCursorActionHandler in visitor pattern
MoveCursorRight => Ok(ActionResult::NotHandled),
// MoveCursorHome is now handled by InputCursorActionHandler in visitor pattern
MoveCursorHome => Ok(ActionResult::NotHandled),
// MoveCursorEnd is now handled by InputCursorActionHandler in visitor pattern
MoveCursorEnd => Ok(ActionResult::NotHandled),
// Backspace is now handled by TextEditActionHandler in visitor pattern
Backspace => Ok(ActionResult::NotHandled),
// Delete is now handled by TextEditActionHandler in visitor pattern
Delete => Ok(ActionResult::NotHandled),
// ClearLine is now handled by ClearActionHandler in visitor pattern
Undo => {
if context.mode == AppMode::Command {
self.buffer_mut().perform_undo();
Ok(ActionResult::Handled)
} else {
Ok(ActionResult::NotHandled)
}
}
Redo => {
if context.mode == AppMode::Command {
self.buffer_mut().perform_redo();
Ok(ActionResult::Handled)
} else {
Ok(ActionResult::NotHandled)
}
}
// Undo is now handled by TextEditActionHandler in visitor pattern
Undo => Ok(ActionResult::NotHandled),
// Redo is now handled by TextEditActionHandler in visitor pattern
Redo => Ok(ActionResult::NotHandled),
ExecuteQuery => {
if context.mode == AppMode::Command {
// Delegate to existing execute query logic
Expand Down Expand Up @@ -7263,6 +7183,71 @@ impl ActionHandlerContext for EnhancedTuiApp {
};
self.buffer_mut().set_status_message(message);
}

// Input and text editing methods
fn move_input_cursor_left(&mut self) {
let buffer = self.buffer_mut();
let pos = buffer.get_input_cursor_position();
if pos > 0 {
buffer.set_input_cursor_position(pos - 1);
}
}

fn move_input_cursor_right(&mut self) {
let buffer = self.buffer_mut();
let pos = buffer.get_input_cursor_position();
let text_len = buffer.get_input_text().chars().count();
if pos < text_len {
buffer.set_input_cursor_position(pos + 1);
}
}

fn move_input_cursor_home(&mut self) {
self.buffer_mut().set_input_cursor_position(0);
}

fn move_input_cursor_end(&mut self) {
let text_len = self.buffer().get_input_text().chars().count();
self.buffer_mut().set_input_cursor_position(text_len);
}

fn backspace(&mut self) {
let buffer = self.buffer_mut();
let pos = buffer.get_input_cursor_position();
if pos > 0 {
buffer.save_state_for_undo();
let mut text = buffer.get_input_text();
let mut chars: Vec<char> = text.chars().collect();
if pos <= chars.len() {
chars.remove(pos - 1);
text = chars.iter().collect();
buffer.set_input_text(text);
buffer.set_input_cursor_position(pos - 1);
}
}
}

fn delete(&mut self) {
let buffer = self.buffer_mut();
let pos = buffer.get_input_cursor_position();
let mut text = buffer.get_input_text();
let chars_len = text.chars().count();
if pos < chars_len {
buffer.save_state_for_undo();
let mut chars: Vec<char> = text.chars().collect();
chars.remove(pos);
text = chars.iter().collect();
buffer.set_input_text(text);
}
}

fn undo(&mut self) {
self.buffer_mut().perform_undo();
}

fn redo(&mut self) {
self.buffer_mut().perform_redo();
}
}

// Implement NavigationBehavior trait for EnhancedTuiApp
Expand Down
Loading