Herdr Copy Mode Navigation Bindings: Complete Guide to Vim-Style Scrollback Control
Herdr's copy mode uses vim-style key bindings (h/j/k/l) combined with traditional terminal shortcuts to navigate scrollback history, select text, and copy to the system clipboard via handle_copy_mode_key in src/app/input/copy_mode.rs.
Herdr is a Rust-based terminal multiplexer that provides efficient scrollback navigation through its copy mode feature. The application implements a comprehensive set of herdr copy mode navigation bindings that combine familiar vim motions with standard terminal controls. When activated, Herdr switches to Mode::Copy and routes all keyboard input through the centralized AppState::handle_copy_mode_key handler to manage cursor movement and text selection.
Entering Copy Mode
To access the scrollback buffer, press the default prefix (Ctrl+b) followed by [. This binding is defined in src/config/model.rs and triggers the state transition to Mode::Copy. Once active, the application captures all subsequent keystrokes to navigate the buffer rather than sending them to the underlying process.
// Enter copy mode (default prefix = Ctrl-b)
app.handle_key(TerminalKey::new(KeyCode::Char('['), KeyModifiers::empty()));
Basic Cursor Movement
Herdr supports dual input schemes for cursor navigation, accepting both arrow keys and their vim-style equivalents. All movement commands in this category invoke move_copy_cursor with delta parameters to update the virtual cursor position.
Arrow Keys and Vim Motions
The following bindings control basic directional movement within the scrollback buffer:
- Move cursor left:
←orh— Decrements column viamove_copy_cursor(..., 0, -1) - Move cursor down:
↓orj— Increments row viamove_copy_cursor(..., 1, 0) - Move cursor up:
↑ork— Decrements row viamove_copy_cursor(..., -1, 0) - Move cursor right:
→orl— Increments column viamove_copy_cursor(..., 0, 1)
Scrolling Commands
For navigating large scrollback histories, Herdr provides page-based and absolute positioning commands handled by scroll_copy_mode_page and the history jump functions.
Page and Half-Page Navigation
These bindings utilize scroll_copy_mode_page with directional integers and a boolean flag indicating half-screen movements:
- Page up:
PgUp— Scrolls up one full screen (scroll_copy_mode_page(..., -1, false)) - Page down:
PgDn— Scrolls down one full screen (scroll_copy_mode_page(..., 1, false)) - Half-page up:
Ctrl+u— Scrolls up half a screen (scroll_copy_mode_page(..., -1, true)) - Half-page down:
Ctrl+d— Scrolls down half a screen (scroll_copy_mode_page(..., 1, true))
Jump to Buffer Edges
Absolute positioning commands move the cursor to the beginning or end of the scrollback history:
- Scroll to top of history:
g— Jumps to the oldest line viacopy_mode_history_top - Scroll to bottom (live view):
G— Jumps to the newest line viacopy_mode_history_bottom
Line Navigation
Horizontal movement within a single line supports both standard terminal keys and vim-style line-position commands, implemented through copy_mode_line_edge and copy_mode_first_non_blank.
- Go to beginning of line:
Homeor0— Sets column to0(copy_mode_line_edge(..., false)) - Go to end of line:
Endor$— Sets column to last visible column (copy_mode_line_edge(..., true)) - First non-blank on line:
^— Moves to the first non-space character (copy_mode_first_non_blank)
Word and Paragraph Navigation
Advanced text navigation follows vim conventions for word-based movement and paragraph jumping. These commands utilize the WordMotion enum variants (NextStart, PreviousStart, NextEnd) via copy_mode_word_motion, and copy_mode_paragraph for block-based navigation.
- Next word start:
w— Moves to start of next word (WordMotion::NextStart) - Previous word start:
b— Moves to start of previous word (WordMotion::PreviousStart) - Next word end:
e— Moves to end of next word (WordMotion::NextEnd) - Paragraph up:
{— Moves upward until blank line (copy_mode_paragraph(..., -1)) - Paragraph down:
}— Moves downward until blank line (copy_mode_paragraph(..., 1))
Implementation Details
The complete navigation system resides in src/app/input/copy_mode.rs within the handle_copy_mode_key function (lines 86-155). This match block processes input in priority order: first checking for literal arrow keys and special keys (Esc, Enter, Home, End), then control-page combinations, and finally falling back to character-based commands through copy_mode_command_char.
The function signature and routing logic demonstrate how Herdr distinguishes between immediate scrolling actions and command-style movements:
// Example cursor movement down one line using both input methods
app.handle_key(TerminalKey::new(KeyCode::Down, KeyModifiers::empty()));
app.handle_key(TerminalKey::new(KeyCode::Char('j'), KeyModifiers::empty()));
// Jump to top of buffer history
app.handle_key(TerminalKey::new(KeyCode::Char('g'), KeyModifiers::empty()));
Configuration and Default Bindings
The default keymap configuration is defined in src/config/model.rs, which establishes the prefix+[ binding to activate copy mode. Visual feedback and help text are generated by src/ui/keybind_help.rs, while src/ui/navigator.rs provides the cursor position updates during selection changes.
Summary
- Herdr copy mode navigation bindings combine vim motions (h/j/k/l, w/b/e) with terminal standards (arrows, PgUp/PgDn) for intuitive scrollback control.
- All key handling routes through
handle_copy_mode_keyinsrc/app/input/copy_mode.rswhen the application state isMode::Copy. - Navigation functions include
move_copy_cursorfor basic movement,scroll_copy_mode_pagefor paging, and specialized functions for word/paragraph jumps. - Default activation requires pressing
prefix+[(typicallyCtrl+bfollowed by[). - The implementation supports both line-based and history-based absolute positioning via
g/Gand0/$/^commands.
Frequently Asked Questions
How do I enter copy mode in Herdr?
Press the default prefix key (Ctrl+b) followed by the left square bracket [. This binding is configured in src/config/model.rs and transitions the application from normal mode to Mode::Copy, enabling all navigation bindings.
What is the difference between g and G in Herdr copy mode?
The lowercase g command invokes copy_mode_history_top and jumps to the oldest line in the scrollback buffer, while uppercase G calls copy_mode_history_bottom to return to the newest line and live view. These correspond to "go to top" and "go to bottom" respectively.
Can I use arrow keys instead of vim bindings?
Yes. Herdr accepts both arrow keys (↑, ↓, ←, →) and their vim equivalents (h, j, k, l) interchangeably. The handle_copy_mode_key function processes both input types through the same underlying move_copy_cursor logic, so you can mix navigation styles based on preference.
Where is the copy mode logic implemented?
The core implementation resides in src/app/input/copy_mode.rs, specifically within the handle_copy_mode_key function (lines 72-155). This file contains the state machine for Mode::Copy and defines all navigation functions including move_copy_cursor, scroll_copy_mode_page, and copy_mode_word_motion.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →