Skip to content

feat: Add new custom keybinding system for search TUI#3127

Open
BinaryMuse wants to merge 7 commits intomainfrom
mkt/new-keybinds
Open

feat: Add new custom keybinding system for search TUI#3127
BinaryMuse wants to merge 7 commits intomainfrom
mkt/new-keybinds

Conversation

@BinaryMuse
Copy link
Member

@BinaryMuse BinaryMuse commented Feb 3, 2026

Summary

Replaces the hardcoded ~450-line key handling block in interactive.rs with a declarative, conditional keybinding table system. Users can now fully customize TUI keyboard shortcuts via [keymap] in their config, using simple bindings or conditional rules with boolean expressions.

  • Introduces a new keybindings module with key parsing, actions, conditions, keymaps, and default definitions
  • Adds [keymap.emacs], [keymap.vim-normal], [keymap.vim-insert], [keymap.inspector], and [keymap.prefix] config sections
  • Supports conditional bindings with when clauses (e.g. { when = "cursor-at-start", action = "exit" }) and boolean expressions (&&, ||, !, parentheses)
  • Splits Accept into accept (execute immediately) and return-selection (place on command line without executing), giving users explicit control over what enter_accept previously toggled at runtime
  • Adds return-selection-N actions for numbered selection without executing
  • Full backward compatibility: existing [keys] configs continue to work; [keymap] takes precedence when present
  • 145 new tests covering key parsing, condition evaluation, boolean expression parsing, keymap resolution, executor behavior, config loading, and backward compatibility
  • Comprehensive documentation in docs/docs/configuration/advanced-key-binding.md

Motivation

The old key handling code was a single monolithic match block that was difficult to extend and impossible for users to customize beyond a few boolean flags (scroll_exits, exit_past_line_start, etc.). Feature requests for custom keybindings were a recurring source of GitHub issues.

This system lets users express any combination of key + condition + action without needing code changes. The old [keys] boolean flags become just specific instances of the more general conditional binding pattern.

Architecture

KeyEvent
  → SingleKey::from_event()          # parse terminal event
  → Keymap::resolve(key, context)    # find matching action (conditions evaluated here)
  → State::execute_action(action)    # perform side effects, return InputAction

New modules (crates/atuin/src/command/client/search/keybindings/):

Module Purpose
key.rs SingleKey, KeyInput with parsing and display
actions.rs Action enum with kebab-case serde
conditions.rs ConditionAtom, ConditionExpr with recursive descent parser
keymap.rs Keymap, KeyBinding, KeyRule with conditional resolution
defaults.rs KeymapSet with all five default keymaps + config overlay logic

Modified files:

File Changes
interactive.rs Replaced handle_search_input/handle_search_key_input with resolver+executor pipeline; added execute_action method
inspector.rs Removed input() function (absorbed into keymap system)
settings.rs Added KeymapConfig, KeyBindingConfig, KeyRuleConfig serde types; keymap field on Settings

Config format

# Simple binding
[keymap.emacs]
"ctrl-c" = "return-original"

# Conditional binding (first matching rule wins)
[keymap.emacs]
"left" = [
  { when = "cursor-at-start", action = "exit" },
  { action = "cursor-left" },
]

# Boolean condition expressions
[keymap.emacs]
"ctrl-d" = [
  { when = "cursor-at-start && input-empty", action = "return-original" },
  { action = "delete-char-after" },
]

Test plan

  • All 145 keybinding tests pass (cargo test -p atuin -- keybindings)
  • All 13 interactive tests pass (cargo test -p atuin -- interactive::tests)
  • Zero compiler warnings
  • Manual: cargo run -- search -i in emacs mode — verify default bindings work
  • Manual: cargo run -- search -i in vim mode — verify normal/insert mode switching, gg, G, hjkl, ctrl-u/d/b/f
  • Manual: Inspector tab (ctrl-o) — verify navigation and delete
  • Manual: Add a [keymap.emacs] override to config, verify it takes effect
  • Manual: Use old [keys] config with no [keymap], verify backward compat
  • Manual: Use [keys] + [keymap] together, verify [keys] is ignored with warning

Resolves #193
Resolves #2005
Resolves #2124
Resolves #2448
Resolves #2555
Resolves #2830
Resolves #2993
Resolves #2745
Resolves #2486
Resolves #2381
Resolves #2548
Resolves #2733
Probably resolves #2876

Other issues that may be affected, but likely not fixed: #1392, #1192, #2573, #3087, #2764, #2786

@BinaryMuse BinaryMuse marked this pull request as ready for review February 4, 2026 20:27
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 4, 2026

Greptile Overview

Greptile Summary

Replaces 450-line hardcoded key handling with declarative keymap system. Users can now fully customize TUI shortcuts via [keymap] config sections.

Key Changes:

  • New keybindings module with 5 submodules (key, actions, conditions, keymap, defaults)
  • Conditional bindings with boolean expressions (when = "cursor-at-start && input-empty")
  • Split Accept into accept (execute) and return-selection (no execute) for explicit control
  • Full backward compatibility: existing [keys] configs work; [keymap] takes precedence
  • 145 tests with comprehensive coverage
  • Extensive docs in advanced-key-binding.md

Architecture:

KeyEvent → resolve(key, context) → execute(action) → InputAction

All files are well-structured with clear separation of concerns. No issues found.

Important Files Changed

Filename Overview
crates/atuin/src/command/client/search/keybindings/conditions.rs Boolean expression parser with proper precedence rules; recursive descent implementation is clean
crates/atuin/src/command/client/search/keybindings/keymap.rs Core keymap resolution logic; conditional binding with first-match semantics works correctly
crates/atuin/src/command/client/search/keybindings/defaults.rs Default keymaps for all modes; backward compatibility logic properly warns when [keys] conflicts with [keymap]
crates/atuin/src/command/client/search/interactive.rs Replaced 450-line match block with resolve+execute pipeline; clean separation of concerns
crates/atuin-client/src/settings.rs Added KeymapConfig, KeyBindingConfig, KeyRuleConfig types with proper serde; comprehensive tests

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

5 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

@BinaryMuse BinaryMuse requested a review from ellie February 4, 2026 20:36
@BinaryMuse BinaryMuse added enhancement New feature or request gui labels Feb 4, 2026
@BinaryMuse
Copy link
Member Author

@ellie I'm feeling pretty happy with this

@BinaryMuse
Copy link
Member Author

Updated to include cmd/win/super key support, with strong caveats in the docs.

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