diff --git a/crates/zesdex-backend/src/controller/input.rs b/crates/zesdex-backend/src/controller/input.rs index 65602ab..2841d99 100644 --- a/crates/zesdex-backend/src/controller/input.rs +++ b/crates/zesdex-backend/src/controller/input.rs @@ -11,6 +11,12 @@ use crate::app::state::rest::AppStateRest; use crate::app::state::types::Overlay; use crate::controller::command::parse_command; +/// Mark state dirty and return an empty action list. +fn mark(state: &mut AppStateRest) -> Vec { + state.mark_dirty(); + Vec::new() +} + /// Translate a terminal `KeyEvent` into zero or more `Action` values /// based on the current application state. /// @@ -36,7 +42,7 @@ pub fn handle_key(key: KeyEvent, state: &mut AppStateRest) -> Vec { } else { state.toast_success(format!("Saved {}", ed.path)); } - state.dirty = true; + state.mark_dirty(); } return vec![]; } @@ -47,7 +53,7 @@ pub fn handle_key(key: KeyEvent, state: &mut AppStateRest) -> Vec { KeyCode::Backspace => { if let Some(ref mut ed) = state.misc.editor { ed.delete_left(); - state.dirty = true; + state.mark_dirty(); } return vec![]; } @@ -75,15 +81,13 @@ pub fn handle_key(key: KeyEvent, state: &mut AppStateRest) -> Vec { let items = crate::app::mode::learning::get_learning_items(state); let n = items.len(); state.misc.selected_index = crate::app::mode::cycle_selected_index(state.misc.selected_index, n, false); - state.dirty = true; - return vec![]; + return mark(state); } KeyCode::Down => { let items = crate::app::mode::learning::get_learning_items(state); let n = items.len(); state.misc.selected_index = crate::app::mode::cycle_selected_index(state.misc.selected_index, n, true); - state.dirty = true; - return vec![]; + return mark(state); } KeyCode::Enter | KeyCode::Char('a') => { let items = crate::app::mode::learning::get_learning_items(state); @@ -148,8 +152,7 @@ pub fn handle_key(key: KeyEvent, state: &mut AppStateRest) -> Vec { KeyCode::Enter => { if state.input.autocomplete_visible { state.input.select_autocomplete(); - state.dirty = true; - return Vec::new(); + return mark(state); } if state.misc.overlay.is_active() { return handle_overlay_enter(state); @@ -163,16 +166,14 @@ pub fn handle_key(key: KeyEvent, state: &mut AppStateRest) -> Vec { KeyCode::Backspace => { if state.input.autocomplete_visible { state.input.close_autocomplete(); - state.dirty = true; - return Vec::new(); + return mark(state); } vec![Action::DeleteChar] } KeyCode::Delete => { if state.input.autocomplete_visible { state.input.close_autocomplete(); - state.dirty = true; - return Vec::new(); + return mark(state); } vec![Action::DeleteCharRight] } @@ -185,7 +186,7 @@ pub fn handle_key(key: KeyEvent, state: &mut AppStateRest) -> Vec { KeyCode::Up => { if state.input.autocomplete_visible { state.input.cycle_autocomplete(false); - state.dirty = true; + state.mark_dirty(); Vec::new() } else if state.misc.overlay == Overlay::Effort { mode::effort::cycle_effort(state); @@ -193,12 +194,12 @@ pub fn handle_key(key: KeyEvent, state: &mut AppStateRest) -> Vec { } else if state.misc.overlay == Overlay::Rewind { let n = mode::rewind::rewind_count(state); state.misc.selected_index = mode::cycle_selected_index(state.misc.selected_index, n, false); - state.dirty = true; + state.mark_dirty(); Vec::new() } else if state.misc.overlay == Overlay::ModelSelector { let n = state.app_config.providers.len(); state.misc.selected_index = mode::cycle_selected_index(state.misc.selected_index, n, false); - state.dirty = true; + state.mark_dirty(); Vec::new() } else if key.modifiers.contains(KeyModifiers::CONTROL) { vec![Action::ScrollUp] @@ -209,7 +210,7 @@ pub fn handle_key(key: KeyEvent, state: &mut AppStateRest) -> Vec { KeyCode::Down => { if state.input.autocomplete_visible { state.input.cycle_autocomplete(true); - state.dirty = true; + state.mark_dirty(); Vec::new() } else if state.misc.overlay == Overlay::Effort { mode::effort::cycle_effort(state); @@ -217,12 +218,12 @@ pub fn handle_key(key: KeyEvent, state: &mut AppStateRest) -> Vec { } else if state.misc.overlay == Overlay::Rewind { let n = mode::rewind::rewind_count(state); state.misc.selected_index = mode::cycle_selected_index(state.misc.selected_index, n, true); - state.dirty = true; + state.mark_dirty(); Vec::new() } else if state.misc.overlay == Overlay::ModelSelector { let n = state.app_config.providers.len(); state.misc.selected_index = mode::cycle_selected_index(state.misc.selected_index, n, true); - state.dirty = true; + state.mark_dirty(); Vec::new() } else if key.modifiers.contains(KeyModifiers::CONTROL) { vec![Action::ScrollDown] @@ -241,7 +242,7 @@ pub fn handle_key(key: KeyEvent, state: &mut AppStateRest) -> Vec { vec![Action::AbortTurn] } else if state.input.autocomplete_visible { state.input.close_autocomplete(); - state.dirty = true; + state.mark_dirty(); Vec::new() } else if state.misc.overlay.is_active() { vec![Action::CloseOverlay] @@ -256,24 +257,24 @@ pub fn handle_key(key: KeyEvent, state: &mut AppStateRest) -> Vec { } else { state.input.tab_complete(); } - state.dirty = true; + state.mark_dirty(); } else if state.input.autocomplete_kind == AutocompleteKind::FileMention && state.input.autocomplete_visible { state.input.cycle_autocomplete(true); - state.dirty = true; + state.mark_dirty(); } Vec::new() } KeyCode::Char(c) => { if state.input.autocomplete_visible { state.input.close_autocomplete(); - state.dirty = true; + state.mark_dirty(); } // Insert the character inline so we can immediately check the // new buffer state for autocomplete triggers. state.input.insert(c); - state.dirty = true; + state.mark_dirty(); // Show autocomplete immediately when the buffer starts with `/`, // without requiring an extra Tab press. if state.input.buffer.starts_with('/') { @@ -304,7 +305,7 @@ fn handle_overlay_enter(state: &mut AppStateRest) -> Vec { } Overlay::Settings => { mode::settings::cycle_internet_mode(&mut state.settings); - state.dirty = true; + state.mark_dirty(); Vec::new() } Overlay::Todo => { @@ -330,7 +331,7 @@ fn handle_overlay_enter(state: &mut AppStateRest) -> Vec { state.input.cursor = 0; state.misc.overlay = Overlay::None; state.toast_success("API key saved".to_string()); - state.dirty = true; + state.mark_dirty(); Vec::new() } @@ -373,13 +374,13 @@ fn handle_overlay_enter(state: &mut AppStateRest) -> Vec { } } state.misc.overlay = Overlay::None; - state.dirty = true; + state.mark_dirty(); Vec::new() } Overlay::ClearConfirm => { state.toast_info("Transcript cleared".to_string()); state.misc.overlay = Overlay::None; - state.dirty = true; + state.mark_dirty(); Vec::new() }