diff options
| author | Bryan Lee <38807139+liby@users.noreply.github.com> | 2026-07-08 01:54:42 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-07-07 20:54:42 +0300 |
| commit | 1e1d06328cc9aefa354b7ce463f9f2d254395662 (patch) | |
| tree | 8b1d6bbeab7916a48a9db99e2671e323d1a78b04 /src/app/input | |
| parent | d0b75de0252ff70a8f43c221b506bf4a0ab3c54d (diff) | |
fix: preserve navigator search commands (#1140)
Navigator search treated Escape as either modal close or query reset,
which left no way to return to command mode while keeping the filtered
row set. `/` also rebuilt search mode by clearing the query, so a user
could not resume editing the same filter after backing out.
Make Escape leave search focus only, reserve command-mode Escape for
closing the picker, and let `/` refocus the existing query. The footer
now lists the shortcuts for the active mode so command keys are not
advertised while text input owns them.
refs #1115
Diffstat (limited to 'src/app/input')
| -rw-r--r-- | src/app/input/modal.rs | 120 |
1 files changed, 103 insertions, 17 deletions
diff --git a/src/app/input/modal.rs b/src/app/input/modal.rs index 82d393d6..84aed8da 100644 --- a/src/app/input/modal.rs +++ b/src/app/input/modal.rs @@ -165,15 +165,7 @@ pub(crate) fn handle_navigator_key( if state.navigator.search_focused { match key.code { KeyCode::Esc => { - if state.navigator.query.is_empty() { - state.navigator.search_focused = false; - leave_modal(state); - } else { - state.navigator.query.clear(); - state.navigator.state_filter = None; - state.navigator.search_focused = false; - state.clamp_navigator_selection_from(terminal_runtimes); - } + state.navigator.search_focused = false; } KeyCode::Enter => { state.accept_navigator_selection_from(terminal_runtimes); @@ -208,19 +200,12 @@ pub(crate) fn handle_navigator_key( match key.code { KeyCode::Esc => { - if state.navigator.query.is_empty() && state.navigator.state_filter.is_none() { - leave_modal(state); - } else { - state.navigator.query.clear(); - state.navigator.state_filter = None; - state.clamp_navigator_selection_from(terminal_runtimes); - } + leave_modal(state); } KeyCode::Enter => { state.accept_navigator_selection_from(terminal_runtimes); } KeyCode::Char('/') => { - state.navigator.query.clear(); state.navigator.state_filter = None; state.navigator.search_focused = true; state.clamp_navigator_selection_from(terminal_runtimes); @@ -1644,6 +1629,107 @@ mod tests { } #[test] + fn navigator_empty_search_escape_returns_to_commands() { + let mut state = state_with_workspaces(&["alpha", "beta"]); + let terminal_runtimes = crate::terminal::TerminalRuntimeRegistry::new(); + state.mode = Mode::Navigator; + state.navigator.search_focused = true; + + handle_navigator_key( + &mut state, + &terminal_runtimes, + KeyEvent::new(KeyCode::Esc, KeyModifiers::empty()), + ); + + assert_eq!(state.mode, Mode::Navigator); + assert!(!state.navigator.search_focused); + assert!(state.navigator.query.is_empty()); + + handle_navigator_key( + &mut state, + &terminal_runtimes, + KeyEvent::new(KeyCode::Char('w'), KeyModifiers::empty()), + ); + + assert_eq!( + state.navigator.state_filter, + Some(NavigatorStateFilter::Working) + ); + assert!(state.navigator.query.is_empty()); + + handle_navigator_key( + &mut state, + &terminal_runtimes, + KeyEvent::new(KeyCode::Esc, KeyModifiers::empty()), + ); + + assert_eq!(state.mode, Mode::Terminal); + } + + #[test] + fn navigator_search_escape_blurs_then_next_escape_closes() { + let mut state = state_with_workspaces(&["alpha", "beta"]); + let terminal_runtimes = crate::terminal::TerminalRuntimeRegistry::new(); + state.mode = Mode::Navigator; + state.navigator.search_focused = true; + state.navigator.query = "a".into(); + + handle_navigator_key( + &mut state, + &terminal_runtimes, + KeyEvent::new(KeyCode::Esc, KeyModifiers::empty()), + ); + + assert_eq!(state.mode, Mode::Navigator); + assert!(!state.navigator.search_focused); + assert_eq!(state.navigator.query, "a"); + + handle_navigator_key( + &mut state, + &terminal_runtimes, + KeyEvent::new(KeyCode::Char('j'), KeyModifiers::empty()), + ); + + assert_eq!(state.navigator.selected, 1); + assert_eq!(state.navigator.query, "a"); + + handle_navigator_key( + &mut state, + &terminal_runtimes, + KeyEvent::new(KeyCode::Char('/'), KeyModifiers::empty()), + ); + + assert_eq!(state.mode, Mode::Navigator); + assert!(state.navigator.search_focused); + assert_eq!(state.navigator.query, "a"); + + handle_navigator_key( + &mut state, + &terminal_runtimes, + KeyEvent::new(KeyCode::Char('l'), KeyModifiers::empty()), + ); + + assert_eq!(state.navigator.query, "al"); + + handle_navigator_key( + &mut state, + &terminal_runtimes, + KeyEvent::new(KeyCode::Esc, KeyModifiers::empty()), + ); + + assert_eq!(state.mode, Mode::Navigator); + assert!(!state.navigator.search_focused); + + handle_navigator_key( + &mut state, + &terminal_runtimes, + KeyEvent::new(KeyCode::Esc, KeyModifiers::empty()), + ); + + assert_eq!(state.mode, Mode::Terminal); + } + + #[test] fn open_rename_active_tab_can_prefill_default_new_tab_name() { let mut state = state_with_workspaces(&["test"]); state.workspaces[0].test_add_tab(None); |
