# srev — source reviewer

**日本語** | [English](README.md)

コードを「読む」ことに特化したターミナル TUI ビューワーです。
差分の確認とフルファイルの閲覧に集中できるよう、操作をシンプルにまとめています。編集機能はありません。

---

## 特長

- **作業ツリー vs HEAD の差分**を `d` で表示（未コミットの変更をレビュー）
- **差分 ⇄ フルコードのトグル**（`d`）。見ている行を保持したまま相互にジャンプ
- **vim ライクなカーソル**でコードを読み、カーソル下の語から**定義へジャンプ**（`gd`）
- **参照（呼び出し元）検索**（`gr`）。カーソル下のシンボルの呼び出し箇所をプロジェクト全体から一覧表示してジャンプ（名前ベースの近似）
- **ジャンプ履歴**：`gd`・アウトライン・プロジェクト検索・`gg`/`G`・ファイル内検索などのジャンプで「飛ぶ前の位置」を記憶し、`(` / `)` で戻る/進む（ファイルをまたいでも可）。**右側のジャンプ履歴ペイン**に経路を表示し、現在地を強調（`J` で表示切替）
- **visual mode**（`v`/`V`）で範囲選択し、`y` でクリップボードへコピー
- **左下アウトラインペイン**にシンボル一覧を表示して定義へ移動できる
- **ファイル内検索**（`/` → `Enter`、`n`/`N` で前後のマッチへ）
- **あいまいファイル検索**（`Ctrl-P`、nucleo = fzf 相当）
- **プロジェクト全体の本文検索**（`Ctrl-F`、部分一致・大文字小文字無視）でマッチ行へジャンプ
- **ツリー / アウトライン / オーバーレイの `/`** で入力しながら候補を絞り込み
- **`Ctrl-R` でリロード**（開いているファイル・git 状態・ツリー・索引、カーソル位置保持）
- **多言語シンタックスハイライト**（inkjet / tree-sitter、70+ 言語）。**Markdown はコードフェンス内の言語別ハイライトにも対応**
- **差分レビュー操作**：`n`/`N` で hunk 間ジャンプ、`]`/`[` で変更ファイル間を移動。差分は**既定で左右（side-by-side）**表示（新規/削除ファイルは自動で単一表示）、`s` で単一表示と切替
- ファイルツリーに変更ステータスを表示（`M`=変更 / `A`=追加 / `D`=削除 / `?`=未追跡、`.gitignore` 準拠）
- **コードビューでも HEAD との変更行を gutter に表示**（追加=緑 / 変更=青 / 直前に削除あり=赤、エディタ風）
- 単一バイナリ。Linux / macOS / Windows でビルド可能

---

## インストール

Rust ツールチェーン（`cargo`）と **C コンパイラ**が必要です（libgit2 と
tree-sitter 文法のビルドに使用）。bundled された文法ファイルを含むため、初回
ビルドは数分・バイナリは約 80 MB になります。

### crates.io から（推奨）

```sh
cargo install srev
```

### GitHub から（最新の開発版）

```sh
cargo install --git https://github.com/ore-public/srev
```

いずれもバイナリは `~/.cargo/bin/srev` に置かれます（`$PATH` が通っていれば
`srev` で起動できます）。

### ソースからビルド（開発用）

```sh
git clone https://github.com/ore-public/srev
cd srev
cargo build --release   # 出力: target/release/srev
cargo test              # ユニットテスト一式
```

---

## 使い方

### 起動

```sh
srev [PATH]   # PATH 省略時はカレントディレクトリ
```

- 起動時は**コードモード**（ファイルツリー閲覧）です。`d` で差分（作業ツリー vs HEAD）に切り替えます。

### 画面構成

```
┌──────────────┬──────────────────────────────┐
│ ファイルツリー │                              │
│（コードモード）│         本文                  │
│  変更ファイル  │   （コード または 差分）        │
│（差分モード）  │                              │
├──────────────┤                              │
│  アウトライン  │                              │
│  (Symbols)   │                              │
└──────────────┴──────────────────────────────┘
                  ステータス / ヘルプ行
```

- **左上**：コードモード時はファイルツリー、差分モード時は変更ファイル一覧
- **左下**：開いているファイルのシンボル一覧（Symbols）
- **右**：本文（コードまたは unified diff）
- **下部**：ステータス行とキーヒント

`d` キーでコード ⇄ 差分をトグルできます（見ている行に対応する位置を保持）。

### キーバインド

#### 共通

| キー | 動作 |
|------|------|
| `q` | 終了 |
| `Tab` | フォーカス巡回（ツリー → アウトライン → 本文） |
| `d` | 差分 ⇄ コードを切替（見ている行を保持） |
| `Ctrl-P` | ファイル名あいまい検索オーバーレイを開く |
| `Ctrl-F` | プロジェクト全体の本文検索（部分一致）。`Enter` で該当行へジャンプ |
| `Ctrl-R` | リロード（ファイル・git 状態・ツリー・索引を再読込、カーソル位置保持） |
| `]` / `[` | 次 / 前のファイルを開く（コードモード=全ファイル昇順、差分モード=変更ファイル） |
| `(` / `)` | ジャンプ履歴を戻る / 進む（`Ctrl-O` も戻る） |
| `J` | 右側のジャンプ履歴ペインの表示切替（既定で表示） |

#### ツリー（コードモード）/ 変更ファイル一覧（差分モード）

| キー | 動作 |
|------|------|
| `j` / `k`、`↓` / `↑` | 移動 |
| `Enter` / `l` | ファイルを開く / ディレクトリを展開 |
| `h` | ディレクトリを畳む（ツリー時のみ） |
| `/` | インラインあいまい絞り込み |

#### アウトライン（左下）

| キー | 動作 |
|------|------|
| `j` / `k` | シンボル選択 |
| `Enter` / `l` | その定義行へカーソル移動 |
| `/` | シンボルを絞り込み |

#### 本文（コードモード）

| キー | 動作 |
|------|------|
| `h` / `j` / `k` / `l` | カーソル移動 |
| `w` / `b` | 単語単位で前後（行内のみ） |
| `0` / `$` | 行頭 / 行末 |
| `gg` / `G` | ファイル先頭 / 末尾 |
| `Ctrl-d` / `Ctrl-u` | 半画面スクロール |
| `gd` | カーソル下の語の定義へジャンプ |
| `gr` | カーソル下の語の参照（呼び出し元）一覧。`Enter` でジャンプ（`↑`/`↓` または `Ctrl-n`/`Ctrl-p` で移動、`Esc` で閉じる） |
| `v` / `V` | visual mode 開始（文字単位 / 行単位） |
| `y` | 選択範囲をクリップボードへコピー |
| `Y` | 位置情報をコピー（選択なし=`パス:行:列`／単一行選択=`パス:行`／複数行選択=`パス:開始行-終了行`） |
| `Esc` | 選択解除 |
| `/` → 入力 → `Enter` | ファイル内検索 |
| `n` / `N` | 次 / 前のマッチへ |

> `gg`・`gd`・`gr` の `g` プレフィックスは固定で、設定ファイルによる変更対象外です。

#### 本文（差分モード）

| キー | 動作 |
|------|------|
| `j` / `k` | スクロール |
| `gg` / `G` | 先頭 / 末尾へ |
| `PageDown` / `PageUp` | ページスクロール |
| `n` / `N` | 次 / 前の hunk（変更ブロック）へジャンプ |
| `s` | side-by-side ⇄ unified の切替（既定は side-by-side。新規/削除ファイルは自動で単一表示） |

#### オーバーレイ / フィルタ / 検索入力中

| キー | 動作 |
|------|------|
| `Esc` | 取消・閉じる |
| `Enter` | 確定 |
| `Backspace` | 入力を編集 |
| `↑` / `↓` または `Ctrl-p` / `Ctrl-n` | 候補を上下移動 |

> `Ctrl-p` / `Ctrl-n` による候補移動は**フィルタ・オーバーレイ入力中のみ**有効です。
> 通常モードの `Ctrl-P`（大文字）はファイル検索オーバーレイを開くキーです。

---

## 設定

### キーバインドの変更

設定ファイル: `~/.config/srev/config.toml`（`SREV_CONFIG` 環境変数で別パスを指定可）

`[keys]` テーブルに `"キー" = "アクション名"` を記述します。

```toml
[keys]
"ctrl-r" = "reload"
"x"      = "toggle_diff"   # 別キーを割り当て
"d"      = "none"          # 既定の d を無効化
```

### キー表記

| 種類 | 例 |
|------|----|
| 単一文字 | `"a"`, `"/"`, `"$"` |
| Ctrl 修飾 | `"ctrl-p"`, `"ctrl-r"` |
| 大文字 | `"Y"`, `"G"`, `"N"` |
| 特殊キー名 | `tab`, `enter`, `esc`, `space`, `up`, `down`, `left`, `right`, `home`, `end`, `pageup`, `pagedown`, `backspace`, `del` |

### アクション名一覧

| アクション名 | 説明 |
|-------------|------|
| `quit` | 終了 |
| `focus_next` | フォーカス巡回 |
| `down` | 下移動 |
| `up` | 上移動 |
| `left` | 左移動 / ディレクトリを畳む |
| `right` | 右移動 / 開く |
| `activate` | 開く / 確定 |
| `top` | 先頭へ（`gg` 相当） |
| `bottom` | 末尾へ（`G` 相当） |
| `half_page_down` | 半画面下スクロール |
| `half_page_up` | 半画面上スクロール |
| `word_forward` | 単語前進 |
| `word_back` | 単語後退 |
| `line_start` | 行頭 |
| `line_end` | 行末 |
| `toggle_diff` | 差分 ⇄ コード切替 |
| `goto_def` | 定義へジャンプ |
| `goto_references` | カーソル下シンボルの参照（呼び出し元）一覧 |
| `find` | ファイル内検索開始 |
| `search_next` | 次のマッチ |
| `search_prev` | 前のマッチ |
| `visual_char` | 文字単位 visual mode |
| `visual_line` | 行単位 visual mode |
| `yank` | 選択をコピー |
| `yank_location` | 位置をコピー（選択時は行/行範囲） |
| `fuzzy_find` | ファイル検索オーバーレイ |
| `reload` | リロード |
| `cancel` | 選択解除 / キャンセル |
| `next_file` | 次のファイルを開く（コード=全ファイル / 差分=変更ファイル） |
| `prev_file` | 前のファイルを開く（コード=全ファイル / 差分=変更ファイル） |
| `toggle_split` | unified ⇄ side-by-side 切替 |
| `grep` | プロジェクト全体の本文検索 |
| `jump_back` | ジャンプ履歴を戻る |
| `jump_forward` | ジャンプ履歴を進む |
| `toggle_jumps` | ジャンプ履歴ペインの表示切替 |

`"none"` を指定するとそのキーを無効化できます。
`gg` / `gd` の `g` プレフィックスは設定変更の対象外です。

---

## 対応言語

### シンタックスハイライト

inkjet（tree-sitter ベース）により **70+ 言語**に対応しています。
**Markdown** は tree-sitter の block + inline 文法で対応し、コードフェンス
（```rust など）の中身も対応言語であれば言語別にハイライトします。

### コードジャンプ（`gd` / `gr`）とアウトライン

Rust / Python / JavaScript / Go / Ruby / C のみ対応しています。

---

## 技術スタック

| 役割 | クレート |
|------|----------|
| TUI / 端末抽象 | `ratatui` + `crossterm` |
| シンタックスハイライト | `inkjet`（tree-sitter ベース、70+ 言語） |
| あいまい検索 | `nucleo-matcher` |
| ファイル走査（gitignore 準拠） | `ignore` |
| git 差分 / 状態 | `git2`（vendored libgit2） |
| シンボル索引 / 定義ジャンプ | `tree-sitter-tags` |
| クリップボード | `arboard` |
| キーマップ設定 | `toml` |

---

## 既知の制限

- **横スクロール未対応**。長い行はカーソルでたどれますが、画面は横にスクロールしません。
- **side-by-side 表示**では各ペインが画面幅の約半分になるため、長い行はより早くクリップされます。
  また `s` でのトグル時、スクロール位置は厳密には対応せず近い位置に再表示されます。
- **`gd` の索引はバックグラウンド構築**。起動直後に作り始めるため、大規模プロジェクトで
  起動直後に `gd` すると「インデックス構築中…」と表示されることがあります（完了後は即ジャンプ）。
- **全角文字 / タブを含む行**：カーソルおよび選択ハイライトの表示位置がやや
  ずれることがあります。
- **ファイル内検索はマッチ行単位**のハイライトです（行内の位置は強調されません）。
- **クリップボード（arboard）はローカル環境向け**。SSH 経由での利用では
  端末クリップボードに届かないことがあります。

---

## ライセンス

このプロジェクトは **MIT OR Apache-2.0** のデュアルライセンスです。
詳細は [`LICENSE-MIT`](LICENSE-MIT) および [`LICENSE-APACHE`](LICENSE-APACHE) を参照してください。

第三者コンポーネントのライセンスは [`THIRD-PARTY-NOTICES.md`](THIRD-PARTY-NOTICES.md) を参照してください。
特記すべき点：

- **libgit2**（git2 クレートで vendored 静的リンク）：GPLv2 + linking exception
- **nucleo-matcher**：MPL-2.0
