refactor: use mark_dirty() helper di input.rs, DRY 22 lokasi

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