ruvim 0.3.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/AGENTS.md +68 -7
  3. data/README.md +30 -7
  4. data/Rakefile +7 -0
  5. data/benchmark/cext_compare.rb +165 -0
  6. data/benchmark/chunked_load.rb +256 -0
  7. data/benchmark/file_load.rb +140 -0
  8. data/benchmark/hotspots.rb +178 -0
  9. data/docs/binding.md +18 -1
  10. data/docs/command.md +156 -10
  11. data/docs/config.md +10 -2
  12. data/docs/done.md +23 -0
  13. data/docs/spec.md +162 -25
  14. data/docs/todo.md +9 -0
  15. data/docs/tutorial.md +33 -1
  16. data/docs/vim_diff.md +31 -8
  17. data/ext/ruvim/extconf.rb +5 -0
  18. data/ext/ruvim/ruvim_ext.c +519 -0
  19. data/lib/ruvim/app.rb +246 -2525
  20. data/lib/ruvim/browser.rb +104 -0
  21. data/lib/ruvim/buffer.rb +43 -20
  22. data/lib/ruvim/cli.rb +6 -0
  23. data/lib/ruvim/command_invocation.rb +2 -2
  24. data/lib/ruvim/completion_manager.rb +708 -0
  25. data/lib/ruvim/dispatcher.rb +14 -8
  26. data/lib/ruvim/display_width.rb +91 -45
  27. data/lib/ruvim/editor.rb +74 -80
  28. data/lib/ruvim/ex_command_registry.rb +3 -1
  29. data/lib/ruvim/file_watcher.rb +243 -0
  30. data/lib/ruvim/gh/link.rb +207 -0
  31. data/lib/ruvim/git/blame.rb +255 -0
  32. data/lib/ruvim/git/branch.rb +112 -0
  33. data/lib/ruvim/git/commit.rb +102 -0
  34. data/lib/ruvim/git/diff.rb +129 -0
  35. data/lib/ruvim/git/grep.rb +107 -0
  36. data/lib/ruvim/git/handler.rb +125 -0
  37. data/lib/ruvim/git/log.rb +41 -0
  38. data/lib/ruvim/git/status.rb +103 -0
  39. data/lib/ruvim/global_commands.rb +351 -77
  40. data/lib/ruvim/highlighter.rb +4 -11
  41. data/lib/ruvim/input.rb +1 -0
  42. data/lib/ruvim/key_handler.rb +1510 -0
  43. data/lib/ruvim/keymap_manager.rb +7 -7
  44. data/lib/ruvim/lang/base.rb +5 -0
  45. data/lib/ruvim/lang/c.rb +116 -0
  46. data/lib/ruvim/lang/cpp.rb +107 -0
  47. data/lib/ruvim/lang/csv.rb +4 -1
  48. data/lib/ruvim/lang/diff.rb +43 -0
  49. data/lib/ruvim/lang/dockerfile.rb +36 -0
  50. data/lib/ruvim/lang/elixir.rb +85 -0
  51. data/lib/ruvim/lang/erb.rb +30 -0
  52. data/lib/ruvim/lang/go.rb +83 -0
  53. data/lib/ruvim/lang/html.rb +34 -0
  54. data/lib/ruvim/lang/javascript.rb +83 -0
  55. data/lib/ruvim/lang/json.rb +40 -0
  56. data/lib/ruvim/lang/lua.rb +76 -0
  57. data/lib/ruvim/lang/makefile.rb +36 -0
  58. data/lib/ruvim/lang/markdown.rb +3 -4
  59. data/lib/ruvim/lang/ocaml.rb +77 -0
  60. data/lib/ruvim/lang/perl.rb +91 -0
  61. data/lib/ruvim/lang/python.rb +85 -0
  62. data/lib/ruvim/lang/registry.rb +102 -0
  63. data/lib/ruvim/lang/ruby.rb +7 -0
  64. data/lib/ruvim/lang/rust.rb +95 -0
  65. data/lib/ruvim/lang/scheme.rb +5 -0
  66. data/lib/ruvim/lang/sh.rb +76 -0
  67. data/lib/ruvim/lang/sql.rb +52 -0
  68. data/lib/ruvim/lang/toml.rb +36 -0
  69. data/lib/ruvim/lang/tsv.rb +4 -1
  70. data/lib/ruvim/lang/typescript.rb +53 -0
  71. data/lib/ruvim/lang/yaml.rb +62 -0
  72. data/lib/ruvim/rich_view/json_renderer.rb +131 -0
  73. data/lib/ruvim/rich_view/jsonl_renderer.rb +57 -0
  74. data/lib/ruvim/rich_view/table_renderer.rb +3 -3
  75. data/lib/ruvim/rich_view.rb +30 -7
  76. data/lib/ruvim/screen.rb +135 -84
  77. data/lib/ruvim/stream/file_load.rb +85 -0
  78. data/lib/ruvim/stream/follow.rb +40 -0
  79. data/lib/ruvim/stream/git.rb +43 -0
  80. data/lib/ruvim/stream/run.rb +74 -0
  81. data/lib/ruvim/stream/stdin.rb +55 -0
  82. data/lib/ruvim/stream.rb +35 -0
  83. data/lib/ruvim/stream_mixer.rb +394 -0
  84. data/lib/ruvim/terminal.rb +18 -4
  85. data/lib/ruvim/text_metrics.rb +84 -65
  86. data/lib/ruvim/version.rb +1 -1
  87. data/lib/ruvim/window.rb +5 -5
  88. data/lib/ruvim.rb +31 -4
  89. data/test/app_command_test.rb +382 -0
  90. data/test/app_completion_test.rb +65 -16
  91. data/test/app_dot_repeat_test.rb +27 -3
  92. data/test/app_ex_command_test.rb +154 -0
  93. data/test/app_motion_test.rb +13 -12
  94. data/test/app_register_test.rb +2 -1
  95. data/test/app_scenario_test.rb +182 -8
  96. data/test/app_startup_test.rb +70 -27
  97. data/test/app_text_object_test.rb +2 -1
  98. data/test/app_unicode_behavior_test.rb +3 -2
  99. data/test/browser_test.rb +88 -0
  100. data/test/buffer_test.rb +24 -0
  101. data/test/cli_test.rb +77 -0
  102. data/test/clipboard_test.rb +67 -0
  103. data/test/command_invocation_test.rb +33 -0
  104. data/test/command_line_test.rb +118 -0
  105. data/test/config_dsl_test.rb +134 -0
  106. data/test/dispatcher_test.rb +74 -4
  107. data/test/display_width_test.rb +41 -0
  108. data/test/ex_command_registry_test.rb +106 -0
  109. data/test/file_watcher_test.rb +197 -0
  110. data/test/follow_test.rb +198 -0
  111. data/test/gh_link_test.rb +141 -0
  112. data/test/git_blame_test.rb +792 -0
  113. data/test/git_grep_test.rb +64 -0
  114. data/test/highlighter_test.rb +169 -0
  115. data/test/indent_test.rb +223 -0
  116. data/test/input_screen_integration_test.rb +1 -1
  117. data/test/keyword_chars_test.rb +85 -0
  118. data/test/lang_test.rb +634 -0
  119. data/test/markdown_renderer_test.rb +5 -5
  120. data/test/on_save_hook_test.rb +12 -8
  121. data/test/render_snapshot_test.rb +78 -0
  122. data/test/rich_view_test.rb +279 -23
  123. data/test/run_command_test.rb +307 -0
  124. data/test/screen_test.rb +68 -5
  125. data/test/search_option_test.rb +19 -0
  126. data/test/stream_test.rb +165 -0
  127. data/test/test_helper.rb +9 -0
  128. data/test/window_test.rb +59 -0
  129. metadata +68 -2
data/docs/spec.md CHANGED
@@ -93,8 +93,8 @@ RuVim は Vim と同様に、用途ごとに座標系を分けています。
93
93
  - tabpages 管理(タブごとに layout tree / current window を保持)
94
94
  - current window の管理
95
95
  - mode 管理(`:normal`, `:insert`, `:command_line`, `:rich`)
96
- - ステータスメッセージ
97
- - コマンドライン状態
96
+ - message line のメッセージ管理
97
+ - command-line 状態
98
98
 
99
99
  ## コマンドモデル
100
100
 
@@ -166,7 +166,7 @@ RuVim::ExCommandRegistry.instance.register(
166
166
  2. `RuVim::App` が mode ごとに処理を分岐
167
167
  3. Normal mode のキーは `RuVim::KeymapManager` で解決
168
168
  4. `RuVim::Dispatcher` が内部コマンド or Ex コマンドを実行
169
- 5. Insert mode でキー処理後、stdin に未読データが残っていればレンダリングをスキップして追加のキーを読み取り・処理する(ペースト高速化)
169
+ 5. Insert mode でキー処理後、stdin に未読データが残っていればレンダリングをスキップして追加のキーを読み取り・処理する(ペースト高速化)。このバッチ処理中は autoindent を抑制し、貼り付けテキストが余分にインデントされるのを防ぐ
170
170
  6. `RuVim::Screen` が再描画
171
171
 
172
172
  ## 起動オプション(CLI, 現状)
@@ -192,6 +192,9 @@ RuVim::ExCommandRegistry.instance.register(
192
192
  - user config / ftplugin を読み込まない
193
193
  - `:ruby` / `:rb` を禁止する
194
194
  - `:!` を禁止する
195
+ - `:grep` / `:lgrep` を禁止する
196
+ - `:git` / `:gh` を禁止する
197
+ - Ruby 構文チェック(`on_save`)を無効化する
195
198
  - `-n`
196
199
  - 現状は no-op(将来の swap / 永続 undo / session 互換の先行予約)
197
200
  - `-o[N]`, `-O[N]`, `-p[N]`
@@ -216,7 +219,7 @@ RuVim::ExCommandRegistry.instance.register(
216
219
 
217
220
  - `stdin` が non-TTY で、起動引数ファイルがない場合は `stdin` を follow stream として開く
218
221
  - バッファ名は `[stdin]`
219
- - status line に `[stdin/live]`, `[stdin/closed]`, `[stdin/error]` を表示
222
+ - statusline に `[stdin]`(完了時 `[stdin/EOF]`、失敗時 `[stdin/error]`)を表示
220
223
  - Normal mode の `Ctrl-c` はデフォルトバインドで `stdin` stream stop(上流 PID へ直接 signal は送らない)
221
224
  - `Ctrl-z` は全モード共通で suspend
222
225
  - suspend 前に terminal を cooked + main screen に戻す
@@ -265,7 +268,7 @@ RuVim::ExCommandRegistry.instance.register(
265
268
  - `p`, `P`: paste
266
269
  - `r<char>`: 1文字置換
267
270
  - `c` + motion / `cc`: change(削除して Insert mode)
268
- - `=` + motion / `==`: auto-indent(Ruby filetype でインデント自動調整。`=j`, `=G` 等)
271
+ - `=` + motion / `==`: auto-indent(Ruby / JSON filetype でインデント自動調整。`=j`, `=G` 等)
269
272
  - `v`: Visual (characterwise)
270
273
  - `V`: Visual (linewise)
271
274
  - `Ctrl-v`: Visual (blockwise, 最小)
@@ -309,7 +312,7 @@ RuVim::ExCommandRegistry.instance.register(
309
312
  - Normal mode とほぼ同じキーバインドが使える(移動・検索・yank 等)
310
313
  - バッファを変更する操作(insert/delete/change/paste/replace)はブロック
311
314
  - `Esc` / `Ctrl-C` で Normal mode に戻る
312
- - ステータスラインに `-- RICH --` を表示
315
+ - statusline `-- RICH --` を表示
313
316
  - 描画時に表示行をテーブル整形(`TableRenderer` を利用)
314
317
  - wrap は強制 OFF
315
318
 
@@ -332,7 +335,7 @@ RuVim::ExCommandRegistry.instance.register(
332
335
 
333
336
  - 画面下部にメッセージ行をオーバーレイ描画
334
337
  - 最下行に「Press ENTER or type command to continue」を反転表示
335
- - ステータスラインは非表示(Vim と同様)
338
+ - statusline は非表示(Vim と同様)
336
339
  - 対象コマンド: `:ls` / `:buffers`, `:args`, `:set`(引数なし), `:command`(引数なし)
337
340
  - 1行以下の出力時は通常の `echo` を使用
338
341
 
@@ -370,7 +373,18 @@ RuVim::ExCommandRegistry.instance.register(
370
373
  - `:vimgrep`, `:lvimgrep`
371
374
  - `:copen`, `:cclose`, `:cnext` / `:cn`, `:cprev` / `:cp`
372
375
  - `:lopen`, `:lclose`, `:lnext` / `:ln`, `:lprev` / `:lp`
376
+ - `:grep /pattern/ [path...]`, `:lgrep /pattern/ [path...]`
377
+ - `grepprg` を argv 配列として実行(シェル経由ではない)
378
+ - ファイル引数の glob パターンは Ruby 側で展開
379
+ - restricted mode(`-Z`)では禁止
380
+ - `:git checkout <branch>` でブランチ切り替え
381
+ - ブランチ一覧(`:git branch`)で Enter を押すとコマンドラインにプリフィル、明示的に Enter で確定
382
+ - `:filter [/pattern/]` : 検索マッチ行のみのフィルタバッファを作成(`g/` キーバインド)
373
383
  - `:rich [format]`
384
+ - `:d [count]` / `:delete`
385
+ - `:y [count]` / `:yank`
386
+ - `:tabs`
387
+ - `:args`, `:next`, `:prev`, `:first`, `:last`
374
388
 
375
389
  ## 検索仕様(現状)
376
390
 
@@ -380,7 +394,7 @@ RuVim::ExCommandRegistry.instance.register(
380
394
  - `N` : 直前検索を逆方向に繰り返し
381
395
  - `*`, `#` : カーソル下の単語を前/後方検索(単語境界つき)
382
396
  - `g*`, `g#` : カーソル下の単語を前/後方検索(部分一致)
383
- - `:%s/pat/repl/g` : バッファ全体 substitute(最小実装)
397
+ - `:{range}s/pat/repl/[flags]` : substitute(フラグ: `g`, `i`, `I`, `n`, `e` 対応。`c` は未実装)
384
398
 
385
399
  ### 仕様メモ
386
400
 
@@ -404,11 +418,12 @@ RuVim::ExCommandRegistry.instance.register(
404
418
  - `:bindings` は current buffer 文脈の有効 key binding を layer 別(`buffer`, `filetype`, `app`)に一覧表示
405
419
  - 任意で mode filter を受ける(例 `:bindings normal`)
406
420
  - 大きいファイルを開くときは、閾値以上で段階読み込みになる場合がある
407
- - status line に `[load/live]`(失敗時 `[load/error]`)
408
- - デフォルト実装は先頭 `8MB` を先に表示し、残りをバックグラウンド読み込み後に反映
421
+ - statusline に `[load]`(失敗時 `[load/error]`)
422
+ - デフォルト実装は先頭 `8MB` を先に表示し、残りをチャンク単位でバックグラウンド読み込み
409
423
  - 環境変数:
410
424
  - `RUVIM_ASYNC_FILE_THRESHOLD_BYTES`
411
425
  - `RUVIM_ASYNC_FILE_PREFIX_BYTES`
426
+ - ファイルを開く際、FIFO・デバイス・ソケット等の特殊ファイルは拒否する(`File.file?` チェック)
412
427
 
413
428
  ### `:command`(現状仕様)
414
429
 
@@ -427,13 +442,26 @@ RuVim::ExCommandRegistry.instance.register(
427
442
  - `buffer`
428
443
  - `window`
429
444
  - `stdout` / `stderr` に出力があれば `[Ruby Output]` 仮想バッファに表示(返り値も表示)
430
- - 出力がない場合、返り値をステータスラインに表示(`inspect`)
445
+ - 出力がない場合、返り値を message line に表示(`inspect`)
446
+
447
+ ### `:r` / `:read`(ファイル・コマンド出力の挿入)
448
+
449
+ - `:r <file>` でファイルの内容をカーソル行の下に挿入
450
+ - `:r !<command>` でシェルコマンドの stdout を挿入
451
+ - 行番号指定可: `:3r file.txt`(3行目の下に挿入)
452
+ - stderr が出た場合、最初の行をエラーメッセージとして表示
453
+
454
+ ### `:w !`(バッファ内容をコマンドへパイプ)
455
+
456
+ - `:w !<command>` でバッファ全体をシェルコマンドの stdin に渡す
457
+ - 範囲指定可: `:'<,'>w !sort`(選択範囲のみ渡す)
431
458
 
432
459
  ### `:!`(shell 実行, 最小)
433
460
 
434
461
  - `:!<command>` で shell コマンドを同期実行
435
- - `stdout` / `stderr` があれば `[Shell Output]` 仮想バッファに表示(終了ステータスを含む)
436
- - 出力がない場合は `shell exit N` をステータス表示
462
+ - alternate screen を一時的に抜けて main screen 上でコマンドを実行(Vim 互換)
463
+ - 実行後 "Press ENTER or type command to continue" を表示し、入力待ち後にエディタに復帰
464
+ - 完了後は `shell exit N` をステータス表示
437
465
  - restricted mode(`-Z`)では禁止
438
466
 
439
467
  ### バッファ管理 Ex コマンド(現状仕様)
@@ -471,11 +499,15 @@ ANSI エスケープシーケンスによる再描画です。
471
499
  - カーソル非表示/表示 (`?25l / ?25h`)
472
500
  - 行キャッシュによる簡易差分描画(同サイズ時)
473
501
  - フッターは 2 行固定:
474
- - 最下段: command line / error message
475
- - 1つ上: status line
476
- - Command-line mode 時も status line は維持し、最下段だけを入力行として使う
502
+ - 最下段: 用途に応じて command-line(`:` `/` `?` 入力時)または message line(メッセージ表示時)として使用。複数行メッセージ時は hit-enter prompt で表示
503
+ - 1つ上: statusline(モード・ファイル名・カーソル位置)
504
+ - command-line mode 時も statusline は維持し、最下段だけを入力行として使う
477
505
  - ファイル未指定起動時は Vim 風 intro screen を表示(RuVim では intro 用の read-only 特殊バッファ)
478
506
  - カーソル位置の文字を反転表示(見やすさ向上)
507
+ - カーソル形状の制御(DECSCUSR)
508
+ - Normal / Visual mode: ターミナルカーソルを非表示にし、セル描画(反転)でカーソルを表現
509
+ - Insert / Command-line mode: ターミナルのバーカーソル(`\e[6 q`)を表示
510
+ - UI 開始時に非点滅ブロック(`\e[2 q`)を設定、終了時にデフォルト(`\e[0 q`)に復元
479
511
 
480
512
  ### split UI
481
513
 
@@ -494,7 +526,7 @@ ANSI エスケープシーケンスによる再描画です。
494
526
  - `:tabnew [path]` で新しいタブを作成
495
527
  - `:tabnext`, `:tabprev` で移動
496
528
  - `:tabs` で全タブ一覧を表示(各タブのウィンドウとバッファ名)
497
- - ステータスラインに `tab:n/m` を表示(タブが2つ以上のとき)
529
+ - statusline `tab:n/m` を表示(タブが2つ以上のとき)
498
530
  - 各タブは以下を独立に保持
499
531
  - window list(表示中 window 群)
500
532
  - current window
@@ -705,9 +737,30 @@ Vim 完全互換ではなく、まずは扱いやすい粒度を優先した仕
705
737
 
706
738
  - 描画時に filetype ごとの regex ベース highlighter を適用
707
739
  - 現状の対応 filetype:
708
- - `ruby`
709
- - `json`
740
+ - `ruby`(Prism lexer ベース。スマートインデント・保存時 ruby -wc チェック)
741
+ - `json` / `jsonl`(キー・値・真偽値・数値。スマートインデント)
710
742
  - `markdown`(見出し・フェンス・HR・ブロック引用・インライン装飾)
743
+ - `scheme`(キーワード・文字列・数値・真偽値・文字リテラル・コメント)
744
+ - `diff`(追加/削除/ハンク/ヘッダ/メタの行単位カラー)
745
+ - `c`(キーワード・型名・文字列・数値・コメント・プリプロセッサ・定数マクロ。スマートインデント・保存時 gcc チェック)
746
+ - `cpp`(C 拡張: `class`, `namespace`, `template`, `auto`, `nullptr` 等。アクセス指定子インデント・保存時 g++ チェック)
747
+ - `yaml`(キー・文字列・真偽値・アンカー・タグ・ブロックスカラー・コメント。インデント)
748
+ - `sh`(キーワード・変数・文字列・数値・コメント。インデント)
749
+ - `python`(キーワード・組み込み関数・デコレータ・文字列・数値・定数・コメント。インデント)
750
+ - `javascript` / `javascriptreact`(キーワード・テンプレートリテラル・文字列・数値・定数・コメント。インデント)
751
+ - `typescript` / `typescriptreact`(JS 拡張: `interface`, `type`, `enum`, `readonly` 等。インデント)
752
+ - `html`(タグ・属性・文字列・エンティティ・DOCTYPE・コメント)
753
+ - `toml`(テーブルヘッダ・キー・文字列・真偽値・日時・数値・コメント)
754
+ - `go`(キーワード・型名・文字列・raw 文字列・数値・定数・コメント。インデント)
755
+ - `rust`(キーワード・ライフタイム・マクロ・属性・文字列・数値・定数・コメント。インデント)
756
+ - `make`(ターゲット・変数定義・変数参照・ディレクティブ・関数・自動変数・コメント)
757
+ - `dockerfile`(命令キーワード・フラグ・変数・文字列・コメント)
758
+ - `sql`(キーワード(大文字小文字不問)・文字列・数値・パラメータ・コメント)
759
+ - `elixir`(キーワード・アトム・モジュール・属性・シジル・文字列・数値・コメント。インデント)
760
+ - `perl`(キーワード・スカラー/配列/ハッシュ変数・文字列・数値・POD・コメント。インデント)
761
+ - `lua`(キーワード・組み込み関数・長文字列・文字列・数値・コメント。インデント)
762
+ - `ocaml`(キーワード・型変数・モジュール名・文字列・数値・ブロックコメント。インデント)
763
+ - `erb`(HTML ベース + ERB デリミタ・ERB コメント)
711
764
  - 優先度(高 -> 低):
712
765
  - cursor / visual
713
766
  - search highlight
@@ -723,7 +776,10 @@ Visual mode と同様に Normal mode の上に乗るモードとして設計さ
723
776
  - **アーキテクチャ**: filetype ごとにレンダラーを登録できる汎用フレームワーク
724
777
  - `RuVim::RichView` モジュール(`lib/ruvim/rich_view.rb`)
725
778
  - レンダラー登録: `RichView.register(filetype, renderer)`
726
- - レンダラー: `TableRenderer`(TSV/CSV)、`MarkdownRenderer`(Markdown)
779
+ - レンダラー: `TableRenderer`(TSV/CSV)、`MarkdownRenderer`(Markdown)、`JsonRenderer`(JSON)、`JsonlRenderer`(JSONL
780
+ - `JsonRenderer` は仮想バッファ方式: ミニファイ JSON を `JSON.pretty_generate` で整形し、読み取り専用バッファに表示
781
+ - `JsonlRenderer` は仮想バッファ方式: 各行を個別にパース・整形し、`---` セパレータで区切って読み取り専用バッファに表示。パースエラー行はエラーマーカー付きで表示
782
+ - 仮想バッファ方式のレンダラーでは `Esc` / `C-c` でバッファを閉じて元に戻れる
727
783
  - **起動方法**:
728
784
  - `:rich [format]` Ex コマンド(トグル)
729
785
  - `gr` Normal mode キーバインド(トグル)
@@ -733,13 +789,14 @@ Visual mode と同様に Normal mode の上に乗るモードとして設計さ
733
789
  - Normal mode とほぼ同じキーバインドが使える(移動・検索・yank 等)
734
790
  - バッファを変更する操作(insert/delete/change/paste/replace)はブロック
735
791
  - `Esc` / `Ctrl-C` で Normal mode に戻る
736
- - ステータスラインに `-- RICH --` を表示
792
+ - statusline `-- RICH --` を表示
737
793
  - wrap は強制 OFF
738
794
  - **レンダリング**:
739
795
  - `Screen` の `plain_window_render_rows` で `editor.rich_mode?` を判定
740
796
  - Rich mode の場合、表示行だけを `RichView.render_visible_lines` で整形
741
797
  - バッファ内容は変更せず、描画パイプラインでの変換のみ
742
798
  - `render_rich_view_line_sc` は ANSI エスケープシーケンスを幅ゼロとして扱い、横スクロール時もスタイルを正しく維持
799
+ - Rich view に渡す前にバッファ行の制御文字(ESC 含む)をサニタイズし、terminal escape injection を防止
743
800
  - **横スクロール**:
744
801
  - 表示カラム(display column)ベースでスクロール量を管理(`@rich_col_offset_sc`)
745
802
  - `renderer.cursor_display_col` で raw バッファの `cursor_x` を整形後の表示カラムに変換し、スクロールオフセットを決定
@@ -786,6 +843,9 @@ Visual mode と同様に Normal mode の上に乗るモードとして設計さ
786
843
  - `nmap`
787
844
  - `imap`
788
845
  - `map_global`
846
+ - `set`
847
+ - `setlocal`
848
+ - `setglobal`
789
849
  - `command`
790
850
  - `ex_command`
791
851
  - `ex_command_call`
@@ -795,6 +855,85 @@ Visual mode と同様に Normal mode の上に乗るモードとして設計さ
795
855
  - XDG 設定ファイル(`init.rb`)は Ruby として評価されるため、信頼できる内容のみ使用する
796
856
  - 直接内部状態に触る代わりに、まずは DSL API を通す設計
797
857
 
858
+ ## Git 連携
859
+
860
+ ### Git Blame
861
+
862
+ `<C-g>` で `:git ` プリセットのコマンドラインモードに入る。`:git <subcommand>` で実行。
863
+ 未知のサブコマンドは `:!` と同様に alternate screen を抜けてシェルで直接実行する(例: `:git stash`)。`:gh` も同様(例: `:gh issue list`)。
864
+
865
+ ### GitStatus
866
+
867
+ `:git status` で `git status` の結果を `kind: :git_status` の読み取り専用バッファで表示。
868
+
869
+ ### GitDiff
870
+
871
+ `:git diff` で `git diff` の結果を `kind: :git_diff` の読み取り専用バッファで表示(filetype: diff)。追加引数をそのまま渡せる(例: `:git diff --cached`)。差分がない場合はメッセージ表示のみ。Enter で差分行に対応するファイルの該当行にジャンプ。`:git log -p` バッファでも同様に動作する。
872
+
873
+ ### GitLog
874
+
875
+ `:git log` で `git log` の結果を `kind: :git_log` の読み取り専用バッファで表示。追加引数をそのまま渡せる(例: `:git log -p`)。`-p` 指定時は filetype: diff で syntax highlight が効く。出力はストリーミングで逐次表示(カーソルは先頭行に固定)。バッファを閉じるとプロセスも停止する。
876
+
877
+ ### GitBranch
878
+
879
+ `:git branch` で `git branch -a` の結果を `kind: :git_branch` の読み取り専用バッファで表示。コミット日時の新しい順にソート。各行にブランチ名、日付、最新コミットのサブジェクトを表示。Enter でカーソル行のブランチ名を `:git checkout <branch>` としてコマンドラインにプリフィル(確認ステップあり、即時実行ではない)。
880
+
881
+ ### GitGrep
882
+
883
+ `:git grep <pattern> [<args>...]` で `git grep -n` を実行し、結果を `kind: :git_grep` の読み取り専用バッファで表示。追加引数をそのまま渡せる(例: `:git grep -i pattern`)。マッチがない場合はメッセージ表示のみ。Enter で該当ファイルの該当行にジャンプ。Esc / Ctrl-C でバッファを閉じる。
884
+
885
+ ### GitCommit
886
+
887
+ `:git commit` でコミットメッセージ編集バッファ(`kind: :git_commit`)を開く。`#` で始まる行はコメント(git status 情報を表示)。insert モードで開始。`:w` または `:wq` でコミット実行。`:q!` でキャンセル。メッセージが空の場合はコミットを中止。
888
+
889
+ ### GitBlame
890
+
891
+ - **Blame バッファ**: `kind: :blame`、readonly、modifiable=false
892
+ - 表示形式: ガター(行番号エリア)にメタ情報(短縮ハッシュ・著者名・日付)を暗灰色で表示、本文はコードのみで元ファイルの filetype に基づく syntax highlight が適用される
893
+ - `gutter_labels` オプション: バッファに行ごとのガターラベルを設定する汎用機能。設定されると行番号の代わりにラベルを表示
894
+ - 内部で `git blame --porcelain` を使用して構造化パース
895
+ - ソースファイルのカーソル行位置を引き継ぐ
896
+
897
+ Blame バッファ内のバッファローカルバインディング:
898
+
899
+ - `p` (GitBlamePrev): カーソル行のコミット C に対し `git blame C^` の結果に更新。履歴スタックに現在の状態を push
900
+ - `P` (GitBlameBack): 履歴スタックから pop して前の blame 状態に復元
901
+ - `c` (GitBlameCommit): カーソル行のコミットの `git show` 結果を `kind: :git_show` の読み取り専用バッファで表示(filetype: diff)
902
+
903
+ 実装: `lib/ruvim/git/blame.rb`(blame パース・実行)、`lib/ruvim/git/commands.rb`(status/diff/log 実行)、`lib/ruvim/global_commands.rb`(コマンドハンドラ)
904
+
905
+ ## GitHub 連携
906
+
907
+ ### gh link
908
+
909
+ `:gh link` で現在のファイル・カーソル行の GitHub URL を生成し、message line に表示する。同時に OSC 52 エスケープシーケンスでクリップボードにコピーする。
910
+
911
+ - ビジュアル選択範囲(`:'<,'>gh link`)を指定すると `#L5-L10` 形式の行範囲リンクを生成
912
+ - GitHub リモートを自動検出(全リモートを走査し、`origin` → `upstream` → その他の優先順位)
913
+ - SSH / HTTPS 両対応
914
+ - `:gh link <remote>` でリモート名を明示指定可能
915
+ - 現在のブランチ名を使用
916
+ - ファイルがリモートと異なる場合、URL の後に `(remote may differ)` を注記
917
+ - GitHub リモートが見つからない場合はエラー
918
+
919
+ ### gh browse
920
+
921
+ `:gh browse` で現在のファイル・カーソル行の GitHub URL をブラウザで開く。
922
+
923
+ - `:gh link` と同じ URL 解決ロジック(行範囲、リモート自動検出、リモート指定)
924
+ - ブラウザ起動は `Browser` モジュールを使用(macOS: `open`, Linux: `xdg-open`, WSL: `wslview` / PowerShell)
925
+ - 開いた URL を message line に表示
926
+ - ファイルがリモートと異なる場合、`(remote may differ)` を注記
927
+
928
+ ### gh pr
929
+
930
+ `:gh pr` で現在のブランチの PR ページをブラウザで開く。
931
+
932
+ - `https://github.com/user/repo/pulls?q=head:<branch>` を生成して開く
933
+ - ファイルパスがなくても動作(カレントディレクトリから git リポジトリを検出)
934
+
935
+ 実装: `lib/ruvim/gh/link.rb`, `lib/ruvim/browser.rb`
936
+
798
937
  ## テスト(現状)
799
938
 
800
939
  - `Minitest` を利用
@@ -807,10 +946,8 @@ Visual mode と同様に Normal mode の上に乗るモードとして設計さ
807
946
 
808
947
  - 永続 undo(`undofile` / `undodir` 相当)
809
948
  - session 保存/復元(`-S` / `:mksession` 相当の実体)
810
- - `:grep` / `:make` / `:cfile` / `:lgrep` / `:lfile` など quickfix 入口
811
- - Ex range/address(`:1,10d`, `:.,$s/.../.../` など)
812
- - `:substitute` フラグ拡張(`c`, `i`, `I`, `n`, `e` など)
813
- - arglist(複数ファイル起動 + `:args`, `:next`, `:prev` 等)
949
+ - `:make` / `:cfile` / `:lfile` など quickfix 入口(`:grep` / `:lgrep` は実装済み)
950
+ - `:substitute` の `c`(confirm)フラグ(`g`, `i`, `I`, `n`, `e` は実装済み)
814
951
  - `Ctrl-w` resize / close-others / equalize など window 操作拡張
815
952
  - `:set` 高度構文(`+=`, `-=`, `:set all`, 短縮名)
816
953
  - tag jump / folds / `:global` / `:normal`
data/docs/todo.md CHANGED
@@ -229,6 +229,15 @@
229
229
  - `spelllang`(スペルチェック言語)
230
230
  - `[PARTIAL]` `termguicolors`(検索/`cursorline`/`colorcolumn` 背景色の最小 truecolor 対応)
231
231
 
232
+ ## 大きいファイルの非同期ロード
233
+
234
+ - `-c` で渡したコマンドが非同期ロード中の不完全なバッファに対して実行される
235
+ - startup actions をロード完了後に遅延実行するコールバックを `finish_async_file_load!` に追加する
236
+ - ユーザーの対話操作は制限しない(`-c` 起動コマンドのみ遅延)
237
+ - 非同期ロードの性能改善(現状は同期パスの約3倍遅い)
238
+ - `Array<String>` ベースのバッファ構造が根本原因(split + GC 負荷)
239
+ - rope / lazy split 等のデータ構造変更が必要(大きな変更)
240
+
232
241
  ## メモ(実装方針)
233
242
 
234
243
  - Vim 完全互換の CLI を目指すより、よく使うフラグから互換寄りに実装する
data/docs/tutorial.md CHANGED
@@ -27,7 +27,8 @@ ruvim path/to/file.txt
27
27
  - `-S Session.vim`(session startup placeholder。現状は未実装メッセージのみ)
28
28
  - `-R`(readonly で開く。現在バッファの `:w` を拒否)
29
29
  - `-M`(modifiable off 相当。編集操作を拒否し、あわせて readonly)
30
- - `-Z`(restricted mode。config/ftplugin を読まず、`:ruby` と `:!` を無効化)
30
+ - `-Z`(restricted mode。config/ftplugin を読まず、`:ruby`、`:!`、`:grep`、`:lgrep`、`:git`、`:gh` を無効化)
31
+ - `-f`(follow mode: `tail -f` 相当。ファイルの追記をリアルタイムに追従)
31
32
  - `-n`(現状 no-op。将来の swap/永続機能向け互換フラグ)
32
33
  - `-o[N]` / `-O[N]` / `-p[N]`(複数ファイルを split / vsplit / tab で開く)
33
34
  - `-V[N]` / `--verbose[=N]`(起動/設定/Ex のログを stderr に出す)
@@ -140,10 +141,40 @@ ruvim -p a.rb b.rb
140
141
  - `:tabnew [path]`
141
142
  - `:tabnext` / `:tabn`
142
143
  - `:tabprev` / `:tabp`
144
+ - `:follow`(`tail -f` 相当の追従モードをトグル)
143
145
  - `:vimgrep /foo/`
144
146
  - `:copen`, `:cnext`, `:cprev`, `:cclose`
145
147
  - `:lvimgrep /foo/`
146
148
  - `:lopen`, `:lnext`, `:lprev`, `:lclose`
149
+ - `:grep pattern [files...]`(外部 grep → quickfix)
150
+ - `:lgrep pattern [files...]`(外部 grep → location list)
151
+ - `:git blame` / `:git status` / `:git diff` / `:git log` / `:git branch` / `:git commit` / `:git grep`
152
+ - `:gh link` / `:gh browse` / `:gh pr`
153
+ - `:r file.txt`(ファイルの内容をカーソル行の下に挿入)
154
+ - `:r !ls`(コマンドの出力をカーソル行の下に挿入)
155
+ - `:w !wc -l`(バッファの内容をコマンドの stdin にパイプ)
156
+ - `:!ls`(alternate screen でシェルコマンドを実行)
157
+
158
+ ## Follow mode(`tail -f` 相当)
159
+
160
+ ファイルへの追記をリアルタイムにバッファへ反映する追従モードです。
161
+
162
+ ```bash
163
+ ruvim -f /var/log/syslog # 起動時から follow mode
164
+ ruvim -f log1.txt log2.txt # 複数ファイルすべて follow
165
+ ```
166
+
167
+ 起動後に切り替えることもできます:
168
+
169
+ ```vim
170
+ :follow " follow 開始(トグル)
171
+ :follow " もう一度で停止
172
+ ```
173
+
174
+ - `Ctrl-C`(ノーマルモード)でも停止できます
175
+ - カーソルが最終行(`G`)にいると末尾を自動追従、途中にいればスクロール位置を維持
176
+ - follow 中はバッファ変更不可(停止後に編集可能)
177
+ - ファイルが truncate/削除された場合はメッセージを表示し、監視を継続(削除時は再作成を待機)
147
178
 
148
179
  ## Undo / Redo
149
180
 
@@ -238,6 +269,7 @@ register prefix を付けると register を指定できます。
238
269
  - `:set ignorecase` / `:set noignorecase` : 検索の大文字小文字を無視(global)
239
270
  - `:set smartcase` / `:set nosmartcase` : `ignorecase` 有効時に大文字を含む検索を大文字小文字区別にする(global)
240
271
  - `:set hlsearch` / `:set nohlsearch` : 検索ハイライトの ON/OFF(global)
272
+ - `:nohlsearch` (`:noh`) : 検索ハイライトを一時的にクリア(次の検索で自動復帰)
241
273
  - `:set tabstop=4` : tab 幅設定(既定スコープは buffer-local)
242
274
  - `:setlocal number` : 現在 window のみ変更
243
275
  - `:setglobal tabstop=8` : global 値を変更
data/docs/vim_diff.md CHANGED
@@ -7,7 +7,7 @@ RuVim は「Vim ライクな Ruby 製ターミナルエディタ」です。Vim
7
7
  ### Ruby ネイティブな拡張性
8
8
 
9
9
  - 設定ファイルは Ruby DSL(`~/.config/ruvim/init.rb`)
10
- - `nmap`, `imap`, `map_global`, `command`, `ex_command`, `ex_command_call`
10
+ - `nmap`, `imap`, `map_global`, `set`, `setlocal`, `setglobal`, `command`, `ex_command`, `ex_command_call`
11
11
  - Vim script 不要で Ruby の全機能を利用可能
12
12
  - `:ruby` / `:rb` で実行中に Ruby eval が可能(Vim の `:ruby` とは別物)
13
13
  - plugin 向け `ctx.editor / ctx.buffer / ctx.window` API
@@ -26,6 +26,13 @@ RuVim は「Vim ライクな Ruby 製ターミナルエディタ」です。Vim
26
26
  - TSV / CSV / Markdown をフォーマットして閲覧できる構造化データ表示モード
27
27
  - CJK 文字幅を考慮したカラム整列
28
28
 
29
+ ### Follow mode(`tail -f` 相当)
30
+
31
+ - `:follow` コマンドまたは `-f` CLI フラグでファイル追従モード
32
+ - Linux では inotify(fiddle 経由)を優先、使えない場合は polling(exponential backoff)にフォールバック
33
+ - ファイルの truncation/deletion を検知してメッセージ表示・自動復帰
34
+ - Vim にはない RuVim 独自機能
35
+
29
36
  ### 検索は Ruby 正規表現
30
37
 
31
38
  - `/`, `?`, `:s` はすべて Ruby の `Regexp` を使用
@@ -53,10 +60,12 @@ RuVim は「Vim ライクな Ruby 製ターミナルエディタ」です。Vim
53
60
 
54
61
  ### Ex コマンド
55
62
 
56
- - `:w`, `:q`, `:wq`, `:e`, `:buffer`, `:bnext`, `:bprev`, `:ls`, `:split`, `:vsplit`
63
+ - `:w`, `:q`, `:wq`, `:e`, `:buffer`, `:bnext`, `:bprev`, `:bdelete`, `:ls`, `:split`, `:vsplit`
57
64
  - `:qa`, `:qa!`, `:wqa`
58
- - quickfix / location list: `:vimgrep`, `:lvimgrep`, `:copen`, `:cnext`, `:lopen`, `:lnext`
59
- - tabpage: `:tabnew`, `:tabnext`, `:tabprev`
65
+ - quickfix / location list: `:vimgrep`, `:lvimgrep`, `:grep`, `:lgrep`, `:copen`, `:cnext`, `:lopen`, `:lnext`
66
+ - tabpage: `:tabnew`, `:tabnext`, `:tabprev`, `:tabs`
67
+ - arglist: `:args`, `:next`, `:prev`, `:first`, `:last`
68
+ - 行操作: `:d` / `:delete`, `:y` / `:yank`
60
69
 
61
70
  ### option system
62
71
 
@@ -65,14 +74,28 @@ RuVim は「Vim ライクな Ruby 製ターミナルエディタ」です。Vim
65
74
  - インデント: `shiftwidth`, `softtabstop`, `expandtab`, `autoindent`, `smartindent`, `tabstop`
66
75
  - 検索: `ignorecase`, `smartcase`, `hlsearch`, `incsearch`
67
76
  - 分割: `splitbelow`, `splitright`
68
- - その他: `hidden`, `autowrite`, `clipboard`, `timeoutlen`, `ttimeoutlen`, `backspace`, `whichwrap`, `iskeyword`, `filetype`, `path`, `suffixesadd`, `grepprg`, `grepformat`, 補完系(`completeopt`, `pumheight`, `wildmode`, `wildmenu`, `wildignore`, `wildignorecase`)
77
+ - grep: `grepprg`, `grepformat`
78
+ - その他: `hidden`, `autowrite`, `clipboard`, `timeoutlen`, `ttimeoutlen`, `backspace`, `whichwrap`, `iskeyword`, `filetype`, `path`, `suffixesadd`, 補完系(`completeopt`, `pumheight`, `wildmode`, `wildmenu`, `wildignore`, `wildignorecase`)
69
79
 
70
80
  ### その他
71
81
 
72
82
  - filetype 検出(拡張子 + shebang)/ ftplugin
73
83
  - syntax highlight: Ruby(Prism lexer), JSON, Markdown, Scheme, TSV/CSV
74
84
  - 補完: Ex コマンド名 + 引数補完, Insert mode buffer words(`Ctrl-n` / `Ctrl-p`)
75
- - CLI: `--help`, `--version`, `--clean`, `-u`, `-R`, `-M`, `-Z`, `-o/-O/-p`, `-c`, `+{cmd}`, `-V`, `--startuptime`, `--cmd`
85
+ - CLI: `--help`, `--version`, `--clean`, `-u`, `-R`, `-M`, `-Z`, `-f`, `-o/-O/-p`, `-c`, `+{cmd}`, `-V`, `--startuptime`, `--cmd`
86
+
87
+ ## セキュリティ / Restricted mode (`-Z`)
88
+
89
+ - Vim と同様に `-Z` で restricted mode を有効化
90
+ - RuVim では以下のコマンドが無効化される:
91
+ - `:!`(シェル実行)
92
+ - `:ruby` / `:rb`(Ruby eval)
93
+ - `:grep` / `:lgrep`(外部 grep 実行)
94
+ - `:git`(Git 操作全般)
95
+ - `:gh`(GitHub 操作全般)
96
+ - `:grep` / `:lgrep` はシェル経由ではなく argv 配列で安全に実行(シェルインジェクション対策済み)
97
+ - Rich view レンダリング時にバッファ内容の制御文字を無害化(ターミナルエスケープインジェクション対策)
98
+ - 特殊ファイル(FIFO/デバイス/ソケット)の読み込みを拒否(DoS 対策)
76
99
 
77
100
  ## 動作の微細な差分
78
101
 
@@ -96,7 +119,7 @@ RuVim は「Vim ライクな Ruby 製ターミナルエディタ」です。Vim
96
119
  - folds
97
120
  - LSP / diagnostics
98
121
  - job / channel / terminal 連携
99
- - `:grep`, `:make`, `:cfile`, `:lgrep`
100
- - substitute の高度なフラグ群(現状は `:%s/.../.../g` のみ)
122
+ - `:make`, `:cfile`, `:lfile`(`:grep`, `:lgrep` は実装済み)
123
+ - substitute `c`(confirm)フラグ(`g`, `i`, `I`, `n`, `e` は実装済み)
101
124
  - swap / backup / undofile(永続 undo)
102
125
  - `-d`(diff mode), `-q`(quickfix mode), `-S`(session)は placeholder のみ
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mkmf"
4
+
5
+ create_makefile("ruvim/ruvim_ext")