Skip to content

fix(input): leave navigate mode on ctrl+[ - #2340

Open
qaz74107410 wants to merge 4 commits into
herdrdev:masterfrom
qaz74107410:fix/1431-ctrl-bracket-navigate
Open

fix(input): leave navigate mode on ctrl+[#2340
qaz74107410 wants to merge 4 commits into
herdrdev:masterfrom
qaz74107410:fix/1431-ctrl-bracket-navigate

Conversation

@qaz74107410

Copy link
Copy Markdown

Ctrl+[ is the terminal-level equivalent of Esc, but navigate mode only checked KeyCode::Esc, so the mode stayed open.

On a legacy terminal Ctrl+[ sends 0x1b and already arrives as KeyCode::Esc, which is why this works for some people. Under the kitty keyboard protocol, which Herdr negotiates, the modified key is reported on its own and reaches the handler as Char('[') with CONTROL, so the Esc check missed it. The report came from foot, which speaks that protocol.

Adds is_navigate_cancel_key and uses it where navigate mode tested for Esc. Ctrl+Shift+[ keeps its own meaning because the match requires CONTROL alone.

Prefix mode needs no matching change. It already falls through to leave_command_mode for any unbound key, so Ctrl+[ cancels it today, and adding the same check there would shadow a custom Ctrl+[ binding under Prefix dispatch.

No doc change looks needed: the keyboard docs do not currently mention Esc for navigate mode. Happy to add one if you want the alias documented.

Checks on 1.96.1: cargo fmt --check and cargo clippy --all-targets --locked -- -D warnings are clean. cargo nextest run --locked is 3179/3181; the two failures, live_handoff::live_server_holds_one_pty_master_fd_per_pane and terminal::state::metadata::tests::metadata_clear_only_without_ttl_does_not_extend_old_ttl, fail the same way on unmodified master in my environment. I ran this in a Linux container because zig 0.15.2 cannot build the vendored libghostty-vt on macOS 26, so just windows-lint is untested here; the change is not platform-gated.

refs #1431

@kangal-bot kangal-bot added the ai-review Trigger automated AI reviews for pull requests admitted by the PR gate label Aug 5, 2026
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Navigate-mode cancellation now supports Ctrl+[ as a fallback after navigation bindings are checked. Escape and prefix keys retain immediate cancellation. Ctrl+Shift+[ remains non-canceling. Runtime and test handlers apply the same behavior.

Changes

Navigation input handling

Layer / File(s) Summary
Cancellation predicate and handler flow
src/app/input/navigate.rs
is_ctrl_bracket_key recognizes Ctrl+[ without Shift. Runtime and test handlers apply cancellation after indexed navigation dispatch, so configured Ctrl+[ bindings take precedence. Escape and prefix-key handling remain immediate. Tests cover equivalence with Escape, binding precedence, and Ctrl+Shift+[ exclusion.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • herdrdev/herdr#2322: Both modify navigation cancellation and keybinding precedence in src/app/input/navigate.rs. This PR adds Ctrl+[ fallback handling.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary change: exiting navigate mode with Ctrl+[.
Description check ✅ Passed The description accurately explains the Ctrl+[ behavior, protocol details, scope, tests, and known environment limitations.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Aug 5, 2026

Copy link
Copy Markdown

Greptile Summary

This PR makes Ctrl+[ cancel navigate mode when no configured navigate binding consumes it, while preserving Ctrl+Shift+[ and custom Ctrl+[ bindings.

  • Adds an exact Ctrl+[ fallback after navigate-mode keybinding dispatch.
  • Applies equivalent behavior to production and test-only handlers.
  • Adds tests for cancellation, binding precedence, and modifier distinction.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
src/app/input/navigate.rs Adds post-dispatch Ctrl+[ cancellation with regression coverage while preserving existing Escape behavior and configured binding precedence.

Reviews (4): Last reviewed commit: "Merge branch 'master' into fix/1431-ctrl..." | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/app/input/navigate.rs (1)

1312-1312: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover both navigation handlers.

The new tests call App::handle_navigate_key only. Line 1312 changes the separate handle_navigate_key(&mut AppState, KeyEvent) handler. Add equivalent Ctrl+[ and Ctrl+Shift+[ assertions through that handler so the two paths cannot diverge.

Suggested additional coverage
+    #[test]
+    fn state_navigate_mode_cancel_keys_match_app_handler() {
+        let mut state = state_with_workspaces(&["one", "two"]);
+        state.mode = Mode::Navigate;
+        handle_navigate_key(
+            &mut state,
+            KeyEvent::new(KeyCode::Char('['), KeyModifiers::CONTROL),
+        );
+        assert_eq!(state.mode, Mode::Terminal);
+
+        state.mode = Mode::Navigate;
+        handle_navigate_key(
+            &mut state,
+            KeyEvent::new(
+                KeyCode::Char('['),
+                KeyModifiers::CONTROL | KeyModifiers::SHIFT,
+            ),
+        );
+        assert_eq!(state.mode, Mode::Navigate);
+    }

Also applies to: 2907-2932


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6ac4deea-63fe-4cd6-bc83-4e32e490b329

📥 Commits

Reviewing files that changed from the base of the PR and between 15442a2 and 1d55a8f.

📒 Files selected for processing (1)
  • src/app/input/navigate.rs

@ogulcancelik ogulcancelik left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

requesting changes because this currently overrides valid user configuration.

ctrl+[ is checked before navigate-mode keybinding dispatch, so any existing ctrl+[ binding is ignored while navigate mode is open. please treat it as a fallback cancel key only when no configured binding matched.

please cover both paths: unbound ctrl+[ cancels navigate mode, and a configured ctrl+[ binding still runs.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/app/input/navigate.rs (1)

1317-1337: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add tests for the state-only navigation handler.

handle_navigate_key(&mut AppState, KeyEvent) has a separate conversion and dispatch path. The new precedence test at Lines [2931-2943] covers only App::handle_navigate_key(TerminalKey). Add equivalent tests through the KeyEvent entry point for unbound Ctrl+[, a configured Ctrl+[ binding, and Ctrl+Shift+[. This prevents the two handlers from diverging.

As per coding guidelines, keep unit tests next to the Rust code in #[cfg(test)] mod tests.

Also applies to: 2931-2943

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 75966db5-3886-40ce-8eb5-694f825749b0

📥 Commits

Reviewing files that changed from the base of the PR and between 1d55a8f and 6474153.

📒 Files selected for processing (1)
  • src/app/input/navigate.rs

@qaz74107410

Copy link
Copy Markdown
Author

Good catch, ctrl+[ was shadowing the binding. Fixed in 6474153.

ctrl+[ is now checked last, after workspace up/down, reserved keys, non-indexed actions, custom commands, and indexed actions, so it only cancels when nothing else matched. Esc is back to the bare KeyCode::Esc check it had before this PR, so its behavior is unchanged. The helper is renamed to is_ctrl_bracket_key and its doc comment states the fallback-only contract.

Both paths are covered:

  • app_navigate_mode_ctrl_bracket_leaves_like_esc, unbound ctrl+[ cancels navigate mode, and Esc still does too.
  • app_navigate_mode_configured_ctrl_bracket_binding_wins_over_cancel, with navigate_workspace_down = "ctrl+[" the selection moves and navigate mode stays open.

The second test fails if the check is moved back above dispatch, so it pins the ordering rather than just passing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-review Trigger automated AI reviews for pull requests admitted by the PR gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants