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:

  • j or ↓ – Move down one line (Action::CursorDown(1))
  • k or ↑ – 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:

  • h or ← – Scroll left 4 columns (App::scroll_left)
  • l or → – 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.rs maps keys to Action enums, which src/main.rs dispatches to handler methods in src/app/navigation.rs.
  • Basic Motion: Use j/k for lines, Ctrl-d/Ctrl-u for half-pages, and g/G for file boundaries.
  • Structural Navigation: Bracket keys ({} and []) jump between files and hunks instantly.
  • Comments: The m/M keys cycle through review comments, while Tab switches focus to the dedicated comment navigator panel.
  • Precision: Command mode (:) supports goto <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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →