# How to Navigate Code Reviews in Tuicr: A Complete Guide to Vim-Style Shortcuts

> Master Tuicr code reviews with Vim style shortcuts. Learn to navigate files hunks and comments efficiently using single key presses. Boost your productivity today.

- Repository: [Almog Gavra/tuicr](https://github.com/agavra/tuicr)
- Tags: how-to-guide
- Published: 2026-08-01

---

**Tuicr implements Vim-style keybindings where the `Action` enum in [`src/input/keybindings.rs`](https://github.com/agavra/tuicr/blob/main/src/input/keybindings.rs) maps keystrokes to navigation methods in [`src/app/navigation.rs`](https://github.com/agavra/tuicr/blob/main/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`](https://github.com/agavra/tuicr/blob/main/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:

```rust
(KeyCode::Char('}'), _) => Action::NextFile,

```

The main event loop in [`src/main.rs`](https://github.com/agavra/tuicr/blob/main/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`](https://github.com/agavra/tuicr/blob/main/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`](https://github.com/agavra/tuicr/blob/main/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`](https://github.com/agavra/tuicr/blob/main/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`](https://github.com/agavra/tuicr/blob/main/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`](https://github.com/agavra/tuicr/blob/main/src/app/navigation.rs)):

```rust
// 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:**

```rust
// Press `}` in Normal mode
app.handle_key(KeyEvent::new(KeyCode::Char('}'), KeyModifiers::NONE));
// Dispatches to App::next_file()

```

**Moving between hunks:**

```rust
// 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:**

```rust
// 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`](https://github.com/agavra/tuicr/blob/main/src/input/keybindings.rs) maps keys to `Action` enums, which [`src/main.rs`](https://github.com/agavra/tuicr/blob/main/src/main.rs) dispatches to handler methods in [`src/app/navigation.rs`](https://github.com/agavra/tuicr/blob/main/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`](https://github.com/agavra/tuicr/blob/main/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`](https://github.com/agavra/tuicr/blob/main/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`](https://github.com/agavra/tuicr/blob/main/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.