srev 0.1.1

A terminal code and diff viewer specialized for reading code
# srev — source reviewer

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

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

---

## 特長

- **作業ツリー vs HEAD の差分**`d` で表示(未コミットの変更をレビュー)
- **差分 ⇄ フルコードのトグル**`d`)。見ている行を保持したまま相互にジャンプ
- **vim ライクなカーソル**でコードを読み、カーソル下の語から**定義へジャンプ**`gd`- **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 状態・ツリー・索引を再読込、カーソル位置保持) |
| `]` / `[` | 次 / 前のファイルを開く(コードモード=全ファイル昇順、差分モード=変更ファイル) |

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

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

#### アウトライン(左下)

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

#### 本文(コードモード)

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

> `gg``gd``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` | 定義へジャンプ |
| `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` | プロジェクト全体の本文検索 |

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

---

## 対応言語

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

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

### コードジャンプ(`gd`)とアウトライン

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