How to Navigate Code Reviews in Tuicr: A Complete Guide to Vim-Style Shortcuts
Tuicr implements Vim-style keybindings where the Action enum in src/input/keybindings.rs maps keystrokes to navigation methods in src/app/navigation.rs, enabling efficient movement through files, hunks, and comments using single-key shortcuts.
Navigating large pull requests efficiently requires keyboard-driven workflows. The agavra/tuicr repository provides a terminal-based code review tool that borrows heavily from Vim's modal editing paradigm, allowing reviewers to jump between files, hunks, and comments without lifting their hands from the keyboard.
Understanding Tuicr's Navigation Architecture
Tuicr separates input handling from state mutation through a clean four-layer architecture: Key → Action → App method → UI render. This design keeps the rendering logic decoupled from input processing, making the codebase maintainable and extensible.
From Keystroke to Action
When you press a key, map_key_to_action in src/input/keybindings.rs translates the raw crossterm::KeyEvent into a typed Action variant based on the current InputMode. For example, pressing } in Normal mode triggers:
(KeyCode::Char('}'), _) => Action::NextFile,
The main event loop in src/main.rs then dispatches this action to the appropriate handler method on the App struct.
State Management in the App Struct
Navigation methods in src/app/navigation.rs manipulate two critical fields within App::diff_state:
cursor_line– The index of the currently highlighted annotation (file headers, hunks, diff lines, or comments)scroll_offset– The top line of the current viewport
After updating these values, methods call App::ensure_cursor_visible to keep the cursor within the visible region, ensuring you never lose your place during rapid navigation.
Essential Navigation Commands
Tuicr organizes navigation into logical groups that mirror Vim's motion commands, making the learning curve shallow for experienced terminal users.
Vertical Movement and Scrolling
Basic line-by-line movement uses j and k (or arrow keys), implemented in App::cursor_down and App::cursor_up:
jor↓– Move down one line (Action::CursorDown(1))kor↑– Move up one line (Action::CursorUp(1))
For faster movement through large diffs, use the paging commands defined in src/app/navigation.rs:
Ctrl-d– Half-page down (App::scroll_down)Ctrl-u– Half-page up (App::scroll_up)Ctrl-f– Full page down (App::page_down)Ctrl-b– Full page up (App::page_up)
Jump to absolute positions with:
g– Go to top of current file (App::cursor_to_top)G– Go to bottom of current file (App::cursor_to_bottom)
File and Hunk Navigation
Move between logical sections of the diff using bracket keys:
}– Jump to next file (Action::NextFile→App::next_file){– Jump to previous file (Action::PrevFile→App::prev_file)]– Jump to next hunk (Action::NextHunk→App::next_hunk)[– Jump to previous hunk (Action::PrevHunk→App::prev_hunk)
These methods update both the selected file index and the cursor position simultaneously, ensuring the UI remains synchronized.
Comment Navigation
The Comment Navigator panel (implemented in src/ui/comment_navigator.rs) provides a searchable list of all review threads. When focused, use:
m– Next comment (Action::NextComment)M– Previous comment (Action::PrevComment)
Under the hood, App::move_cursor_to_annotation (lines 215-236 in src/app/navigation.rs) sets cursor_line to the target annotation's index and syncs the file selection, instantly scrolling the diff view to the relevant line.
Horizontal Scrolling and View Toggles
When line wrapping is disabled, navigate horizontally with:
hor←– Scroll left 4 columns (App::scroll_left)lor→– Scroll right 4 columns (App::scroll_right)
Toggle wrapping behavior with y, which triggers App::toggle_diff_wrap via Action::ToggleExpand. When wrapping is enabled, horizontal scroll commands become no-ops to maintain view consistency.
Advanced Navigation Features
Beyond basic motion, Tuicr provides context-switching and precision navigation tools for complex reviews.
Command Mode and Line Jumping
Access Command Mode by pressing : to execute explicit commands. The most powerful navigation command is goto, implemented in App::go_to_source_line (lines 401-434 in src/app/navigation.rs):
// Type `:goto 42` and press Enter
app.feed_command_input("goto 42");
// Internally: App::go_to_source_line(123, LineSide::New)
This jumps directly to line 42 in the new version of the file, regardless of where the cursor currently sits in the diff.
Search and Focus Toggling
/– Enter search mode (Action::EnterSearchMode→App::enter_search_mode)Tab– Toggle focus between the file list and diff view (Action::ToggleFocus→App::toggle_focus)
The search functionality scans through diff content and comments, while focus toggling lets you navigate the sidebar comment navigator using the same j/k keys before jumping back to the diff.
Practical Navigation Examples
Here are common navigation patterns implemented using Tuicr's API:
Jumping to the next file:
// Press `}` in Normal mode
app.handle_key(KeyEvent::new(KeyCode::Char('}'), KeyModifiers::NONE));
// Dispatches to App::next_file()
Moving between hunks:
// Next hunk
app.handle_key(KeyEvent::new(KeyCode::Char(']'), KeyModifiers::NONE));
// Previous hunk
app.handle_key(KeyEvent::new(KeyCode::Char('['), KeyModifiers::NONE));
Using the comment navigator:
// Toggle focus to comment panel
app.handle_key(KeyEvent::new(KeyCode::Tab, KeyModifiers::NONE));
// Navigate down to desired comment
app.handle_key(KeyEvent::new(KeyCode::Char('j'), KeyModifiers::NONE));
// Jump to comment location in diff
app.handle_key(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
Summary
- Architecture: Tuicr uses a strict pipeline where
src/input/keybindings.rsmaps keys toActionenums, whichsrc/main.rsdispatches to handler methods insrc/app/navigation.rs. - Basic Motion: Use
j/kfor lines,Ctrl-d/Ctrl-ufor half-pages, andg/Gfor file boundaries. - Structural Navigation: Bracket keys (
{}and[]) jump between files and hunks instantly. - Comments: The
m/Mkeys cycle through review comments, whileTabswitches focus to the dedicated comment navigator panel. - Precision: Command mode (
:) supportsgoto <line>for direct line access, and/initiates content search.
Frequently Asked Questions
How does Tuicr handle key mapping conflicts between different modes?
Tuicr resolves conflicts through the InputMode enum checked within map_key_to_action in src/input/keybindings.rs. The same physical key can trigger different Action variants depending on whether the application is in Normal mode, Search mode, or Command mode, ensuring contextual behavior without keybinding overlap.
Can I customize the Vim-style keybindings in Tuicr?
Currently, keybindings are hardcoded in the match arms of src/input/keybindings.rs (lines 58-99). To modify shortcuts, you must edit the source code and recompile. The architecture separates key translation from action handling, so adding configurable keymaps would only require modifying the mapping layer without touching src/app/navigation.rs.
Why does horizontal scrolling with h and l sometimes not work?
Horizontal scrolling via App::scroll_left and App::scroll_right only functions when wrap_lines is disabled. When line wrapping is active (toggled with y), these commands are intentionally suppressed to prevent visual confusion, as the scroll_x offset would have no effect on wrapped text display.
How does Tuicr keep the cursor visible during rapid navigation?
Every navigation method calls App::ensure_cursor_visible after updating cursor_line or scroll_offset. This function calculates the viewport boundaries and automatically adjusts the scroll offset to keep the cursor within the visible region, preventing scenarios where the cursor moves above or below the current screen view.
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 →