ruvim 0.1.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 (66) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/test.yml +15 -0
  3. data/README.md +135 -0
  4. data/Rakefile +36 -0
  5. data/docs/binding.md +125 -0
  6. data/docs/command.md +306 -0
  7. data/docs/config.md +155 -0
  8. data/docs/done.md +112 -0
  9. data/docs/plugin.md +559 -0
  10. data/docs/spec.md +655 -0
  11. data/docs/todo.md +63 -0
  12. data/docs/tutorial.md +490 -0
  13. data/docs/vim_diff.md +179 -0
  14. data/exe/ruvim +6 -0
  15. data/lib/ruvim/app.rb +1600 -0
  16. data/lib/ruvim/buffer.rb +421 -0
  17. data/lib/ruvim/cli.rb +264 -0
  18. data/lib/ruvim/clipboard.rb +73 -0
  19. data/lib/ruvim/command_invocation.rb +14 -0
  20. data/lib/ruvim/command_line.rb +63 -0
  21. data/lib/ruvim/command_registry.rb +38 -0
  22. data/lib/ruvim/config_dsl.rb +134 -0
  23. data/lib/ruvim/config_loader.rb +68 -0
  24. data/lib/ruvim/context.rb +26 -0
  25. data/lib/ruvim/dispatcher.rb +120 -0
  26. data/lib/ruvim/display_width.rb +110 -0
  27. data/lib/ruvim/editor.rb +1025 -0
  28. data/lib/ruvim/ex_command_registry.rb +80 -0
  29. data/lib/ruvim/global_commands.rb +1889 -0
  30. data/lib/ruvim/highlighter.rb +52 -0
  31. data/lib/ruvim/input.rb +66 -0
  32. data/lib/ruvim/keymap_manager.rb +96 -0
  33. data/lib/ruvim/screen.rb +452 -0
  34. data/lib/ruvim/terminal.rb +30 -0
  35. data/lib/ruvim/text_metrics.rb +96 -0
  36. data/lib/ruvim/version.rb +5 -0
  37. data/lib/ruvim/window.rb +71 -0
  38. data/lib/ruvim.rb +30 -0
  39. data/sig/ruvim.rbs +4 -0
  40. data/test/app_completion_test.rb +39 -0
  41. data/test/app_dot_repeat_test.rb +54 -0
  42. data/test/app_motion_test.rb +73 -0
  43. data/test/app_register_test.rb +47 -0
  44. data/test/app_scenario_test.rb +77 -0
  45. data/test/app_startup_test.rb +199 -0
  46. data/test/app_text_object_test.rb +54 -0
  47. data/test/app_unicode_behavior_test.rb +66 -0
  48. data/test/buffer_test.rb +72 -0
  49. data/test/cli_test.rb +165 -0
  50. data/test/config_dsl_test.rb +78 -0
  51. data/test/dispatcher_test.rb +124 -0
  52. data/test/editor_mark_test.rb +69 -0
  53. data/test/editor_register_test.rb +64 -0
  54. data/test/fixtures/render_basic_snapshot.txt +8 -0
  55. data/test/fixtures/render_basic_snapshot_nonumber.txt +8 -0
  56. data/test/fixtures/render_unicode_scrolled_snapshot.txt +7 -0
  57. data/test/highlighter_test.rb +16 -0
  58. data/test/input_screen_integration_test.rb +69 -0
  59. data/test/keymap_manager_test.rb +48 -0
  60. data/test/render_snapshot_test.rb +70 -0
  61. data/test/screen_test.rb +123 -0
  62. data/test/search_option_test.rb +39 -0
  63. data/test/test_helper.rb +15 -0
  64. data/test/text_metrics_test.rb +42 -0
  65. data/test/window_test.rb +21 -0
  66. metadata +106 -0
data/docs/todo.md ADDED
@@ -0,0 +1,63 @@
1
+ # RuVim TODO
2
+
3
+ ## 方針
4
+
5
+ - まずは「日常的に触れる Vim ライク編集体験」を優先
6
+ - その次に「拡張性(`:command`, `:ruby`, keymap layering)」を強化
7
+ - 最後に「性能・互換性・品質」を詰める
8
+ - このファイルは未完了項目を管理する
9
+ - 完了済みの項目は `docs/done.md` に移動して管理する
10
+
11
+ 作業時のルール:
12
+ - 着手時にこのファイルを編集する(`DOING` にするなど)
13
+ - 実装に合わせて docs を更新する
14
+ - `docs/spec.md`
15
+ - `docs/tutorial.md`
16
+ - `docs/binding.md`
17
+ - `docs/command.md`
18
+ - `docs/config.md`
19
+ - `docs/vim_diff.md`
20
+
21
+ ## TODO Vim 互換性の精度向上
22
+
23
+ - Vim 互換性の精度向上(word motion / paste / visual 挙動)
24
+ - `w`, `b`, `e` の境界判定を Vim に寄せる
25
+ - `p`, `P` のカーソル位置ルールを調整
26
+ - Visual mode の端点/inclusive ルールを整理
27
+
28
+ ## TODO 永続化
29
+
30
+ - 永続 undo / セッション
31
+ - undo history 保存
32
+ - session file(開いている buffer / cursor 位置)
33
+
34
+ ## TODO 長期(規模大)
35
+
36
+ ### P3: 長期(人気はあるが規模大)
37
+
38
+ - LSP / diagnostics(中長期)
39
+ - language server 起動管理
40
+ - diagnostics 表示
41
+ - definition / references ジャンプ
42
+
43
+ - LSP diagnostics + jump(最小)
44
+ - 効果: 高
45
+ - コスト: 高
46
+ - 依存:
47
+ - job/process 管理
48
+ - diagnostics モデル
49
+ - 画面表示(sign/underline/一覧)
50
+
51
+ - fuzzy finder(file/buffer/grep)
52
+ - 効果: 高
53
+ - コスト: 高
54
+ - 依存:
55
+ - picker UI
56
+ - grep/検索基盤
57
+ - preview(任意)
58
+
59
+ ## メモ(方針)
60
+
61
+ - Vim 完全互換の CLI を目指すより、よく使うフラグから互換寄りに実装する
62
+ - Ruby DSL 前提なので、Vim の `-u NONE` / `-U NONE` は RuVim 向けに意味を再定義してよい
63
+
data/docs/tutorial.md ADDED
@@ -0,0 +1,490 @@
1
+ # RuVim チュートリアル(使い方)
2
+
3
+ ## 起動
4
+
5
+ このリポジトリは Ruby 標準ライブラリのみで動きます。
6
+
7
+ ```bash
8
+ ruvim
9
+ ```
10
+
11
+ ファイルを指定しない起動では、Vim 風の `intro screen`(RuVim では intro 用の read-only 特殊バッファ)を表示します。
12
+ 編集を始めると通常の空バッファに置き換わります。
13
+
14
+ ファイルを開いて起動:
15
+
16
+ ```bash
17
+ ruvim path/to/file.txt
18
+ ```
19
+
20
+ ### 起動オプション(Vim 風・現状)
21
+
22
+ - `--help`(ヘルプを表示して終了)
23
+ - `--version`(バージョンを表示して終了)
24
+ - `--clean`(ユーザー設定と ftplugin を読まない)
25
+ - `-d`(diff mode placeholder。現状は未実装メッセージのみ)
26
+ - `-q errors.log`(quickfix startup placeholder。現状は未実装メッセージのみ)
27
+ - `-S Session.vim`(session startup placeholder。現状は未実装メッセージのみ)
28
+ - `-R`(readonly で開く。現在バッファの `:w` を拒否)
29
+ - `-M`(modifiable off 相当。編集操作を拒否し、あわせて readonly)
30
+ - `-Z`(restricted mode。config/ftplugin を読まず、`:ruby` を無効化)
31
+ - `-n`(現状 no-op。将来の swap/永続機能向け互換フラグ)
32
+ - `-o[N]` / `-O[N]` / `-p[N]`(複数ファイルを split / vsplit / tab で開く)
33
+ - `-V[N]` / `--verbose[=N]`(起動/設定/Ex のログを stderr に出す)
34
+ - `--startuptime file.log`(起動フェーズの簡易 timing log を書く)
35
+ - `--cmd 'set number'`(user config 読み込み前に Ex 実行)
36
+ - `-u path/to/init.rb`(設定ファイルを指定)
37
+ - `-u NONE`(ユーザー設定を読まない)
38
+ - `-c 'set number'`(起動後に Ex 実行)
39
+ - `+10`(起動後に 10 行目へ移動)
40
+ - `+`(起動後に最終行へ移動)
41
+
42
+ 例:
43
+
44
+ ```bash
45
+ ruvim --clean file.txt
46
+ ruvim -R file.txt
47
+ ruvim --cmd 'set number' -u /tmp/minimal_init.rb file.txt
48
+ ruvim -u /tmp/minimal_init.rb -c 'set number' file.txt
49
+ ruvim +10 file.txt
50
+ ruvim -o a.rb b.rb
51
+ ruvim -O a.rb b.rb
52
+ ruvim -p a.rb b.rb
53
+ ```
54
+
55
+ 開発環境で gem 未インストールのまま試す場合は `ruby -Ilib exe/ruvim` でも起動できます。
56
+
57
+ ## 基本操作
58
+
59
+ ### Normal mode
60
+
61
+ - `h` 左
62
+ - `j` 下
63
+ - `k` 上
64
+ - `l` 右
65
+ - `0` 行頭
66
+ - `$` 行末
67
+ - `^` 行頭の最初の非空白
68
+ - `w` 次の単語へ
69
+ - `b` 前の単語へ
70
+ - `e` 単語末へ
71
+ - `f<char>`, `F<char>` 行内文字移動
72
+ - `t<char>`, `T<char>` 行内「手前/直後」移動
73
+ - `;`, `,` 直前の `f/F/t/T` を繰り返し / 逆方向
74
+ - `%` 対応括弧ジャンプ(`()[]{}`)
75
+ - `gg` 先頭へ
76
+ - `G` 末尾へ
77
+ - `i` Insert mode
78
+ - `a`, `A`, `I` 挿入開始位置を変えて Insert mode
79
+ - `o`, `O` 行を開いて Insert mode
80
+ - `:` Command-line mode
81
+ - `/` 前方検索
82
+ - `?` 後方検索
83
+ - `x` 文字削除
84
+ - `dd` 行削除
85
+ - `d` + motion(例: `dw`, `dj`, `d$`)
86
+ - `yy`, `yw` yank
87
+ - `p`, `P` paste
88
+ - `r<char>` 1文字置換(例: `rx`)
89
+ - `v`, `V`, `Ctrl-v` Visual mode(char / line / block)
90
+ - `c` + motion / `cc` change(削除して Insert mode)
91
+ - `u` undo
92
+ - `Ctrl-r` redo
93
+ - `.` 直前変更の繰り返し(拡張版)
94
+ - `x`, `dd`, `d{motion}`, `p/P`, `r<char>`
95
+ - `i/a/A/I/o/O` の Insert 入力(`Esc` / `Ctrl-c` まで)
96
+ - `c{motion}`, `cc`(text object を含む)
97
+ - macro 記録中でも `.` の内部再生キーは記録しない
98
+ - `n` 検索を次へ
99
+ - `N` 検索を前へ
100
+ - `3j` など count 対応(一部コマンド)
101
+
102
+ ### Insert mode
103
+
104
+ - 文字入力で挿入
105
+ - `Enter` で改行
106
+ - `Backspace` で削除
107
+ - `Esc` で Normal mode に戻る
108
+ - `Ctrl-c` でも Normal mode に戻る(終了しない)
109
+
110
+ ### Command-line mode(Ex 風)
111
+
112
+ - `:` を押すと最下段で入力
113
+ - `Enter` で実行
114
+ - `Esc` でキャンセル
115
+ - `Up/Down` で履歴
116
+ - `Tab` で Ex 補完(`:` のとき。コマンド名/一部引数)
117
+
118
+ 使えるコマンド:
119
+
120
+ - `:w`
121
+ - `:w path/to/file.txt`
122
+ - `:q`
123
+ - `:q!`
124
+ - `:wq`
125
+ - `:e other.txt`
126
+ - `:e!`(現在ファイルを再読込)
127
+ - `:e! other.txt`(未保存変更を破棄して開く)
128
+ - `:help`
129
+ - `:help regex`, `:help options`, `:help w`(topic 指定)
130
+ - `:commands`
131
+ - `:command Name ex_body`
132
+ - `:command! Name ex_body`
133
+ - `:ruby <code>` / `:rb <code>`
134
+ - `:ls` / `:buffers`
135
+ - `:bnext` / `:bn`
136
+ - `:bprev` / `:bp`
137
+ - `:buffer <id|name|#>` / `:b <id|name|#>`
138
+ - `:split`
139
+ - `:vsplit`
140
+ - `:tabnew [path]`
141
+ - `:tabnext` / `:tabn`
142
+ - `:tabprev` / `:tabp`
143
+ - `:vimgrep /foo/`
144
+ - `:copen`, `:cnext`, `:cprev`, `:cclose`
145
+ - `:lvimgrep /foo/`
146
+ - `:lopen`, `:lnext`, `:lprev`, `:lclose`
147
+
148
+ ## Undo / Redo
149
+
150
+ - `u`: 直前の変更を取り消す
151
+ - `Ctrl-r`: 取り消した変更をやり直す
152
+
153
+ 現状の undo 粒度:
154
+
155
+ - Normal mode の `x`, `dd` などは 1 コマンド = 1 undo
156
+ - Insert mode は `i` で入って `Esc` / `Ctrl-c` で抜けるまでを 1 undo として扱う
157
+
158
+ ## 検索
159
+
160
+ - `/foo` : 前方検索
161
+ - `?foo` : 後方検索
162
+ - `n` : 同方向に繰り返し
163
+ - `N` : 逆方向に繰り返し
164
+ - `*` / `#` : カーソル下の単語を検索
165
+ - `g*` / `g#` : カーソル下の単語を部分一致検索
166
+
167
+ 検索は command-line の入力欄を再利用しています(prefix が `:` ではなく `/` または `?` になる)。
168
+ 検索パターンは Ruby 正規表現です(例: `/foo\d+/` 相当なら `foo\d+` を入力)。
169
+
170
+ ### quickfix / location list(最小)
171
+
172
+ - `:vimgrep /foo/` : 開いている file buffer 群を検索して quickfix list を作る
173
+ - `:copen` : quickfix list を read-only `qf` バッファで開く
174
+ - `:cnext`, `:cprev` : quickfix 項目を移動してジャンプ
175
+ - `:lvimgrep /foo/` : current buffer を検索して current window の location list を作る
176
+ - `:lopen`, `:lnext`, `:lprev`, `:lclose` : location list を操作
177
+
178
+ ### substitute(最小)
179
+
180
+ - `:%s/foo/bar/g`
181
+ - バッファ全体に対して置換(Ruby 正規表現 + Ruby の置換文字列)
182
+
183
+ ## `d` + motion(operator-pending)
184
+
185
+ 使える例(現状):
186
+
187
+ - `dd` : 行削除
188
+ - `dw` : 次の単語先頭まで削除
189
+ - `dj` : 現在行 + 次行を削除
190
+ - `dk` : 現在行 + 前行を削除
191
+ - `d$` : 行末まで削除
192
+ - `dh` / `dl` : 左右の文字削除
193
+ - `diw` / `daw` : 単語 text object(簡易)
194
+ - `di"` / `da"` : ダブルクォート text object(簡易)
195
+ - `di)` / `da)` : 丸括弧 text object(簡易)
196
+ - `di]` / `da]`, `di}` / `da}` : bracket / brace text object(簡易)
197
+ - ``di` `` / ``da` `` : backtick quote text object(簡易)
198
+ - `dip` / `dap` : paragraph text object(簡易)
199
+
200
+ ## yank / paste / replace
201
+
202
+ - `yy` : 現在行を yank
203
+ - `yw` : 単語方向に yank
204
+ - `yi]`, `yi}`, ``yi` ``, `yip` など text object yank(簡易)
205
+ - `p` : カーソル後ろに paste
206
+ - `P` : カーソル前に paste
207
+ - `r<char>` : カーソル位置の1文字を置換
208
+
209
+ register prefix を付けると register を指定できます。
210
+
211
+ - `"ayy` : register `a` に行 yank
212
+ - `"Ayy` : register `a` に追記 yank
213
+ - `"_dd` : black hole register に捨てる(unnamed/numbered を汚さない)
214
+ - `yy` の結果は register `0` にも入る
215
+ - `dd` / `d{motion}` の結果は register `1-9` に回転保存される(簡易)
216
+ - `"+p` : system clipboard を paste(backend が使える環境)
217
+ - `"*p` : system clipboard register `*` を paste(backend が使える環境)
218
+
219
+ ## Mark / Jump list
220
+
221
+ - `ma` : local mark `a` を設定
222
+ - `mA` : global mark `A` を設定
223
+ - `'a` : mark `a` の行へジャンプ(先頭の非空白)
224
+ - `` `a `` : mark `a` の正確な位置へジャンプ
225
+ - `Ctrl-o` / `Ctrl-i` : jump list を戻る / 進む(`Ctrl-i` は Tab と同じコード)
226
+
227
+ ## Macro
228
+
229
+ - `qa` : macro を register `a` に記録開始
230
+ - `q` : 記録停止
231
+ - `@a` : macro `a` を再生
232
+ - `@@` : 直前 macro を再生
233
+
234
+ ## Options(`:set`)
235
+
236
+ - `:set number` / `:set nonumber` : 行番号表示の ON/OFF(window-local)
237
+ - `:set relativenumber` / `:set norelativenumber` : 相対行番号(window-local)
238
+ - `:set ignorecase` / `:set noignorecase` : 検索の大文字小文字を無視(global)
239
+ - `:set smartcase` / `:set nosmartcase` : `ignorecase` 有効時に大文字を含む検索を大文字小文字区別にする(global)
240
+ - `:set hlsearch` / `:set nohlsearch` : 検索ハイライトの ON/OFF(global)
241
+ - `:set tabstop=4` : tab 幅設定(既定スコープは buffer-local)
242
+ - `:setlocal number` : 現在 window のみ変更
243
+ - `:setglobal tabstop=8` : global 値を変更
244
+ - `:set` : 現在の option 一覧を表示(簡易)
245
+
246
+ ## change operator(`c`)
247
+
248
+ - `cw` : 単語方向に change
249
+ - `cc` : 行を change
250
+ - `c$` : 行末まで change
251
+ - `ciw`, `caw` : 単語 text object を change(簡易)
252
+ - `ci]`, `ca}`, ``ci` ``, `cip`, `cap` なども利用可(簡易)
253
+
254
+ `c` は削除後に Insert mode に入ります。
255
+
256
+ ## Visual mode
257
+
258
+ - `v` : characterwise Visual
259
+ - `V` : linewise Visual
260
+ - `Ctrl-v` : blockwise Visual(最小)
261
+ - 移動して範囲を選択
262
+ - `y` : yank
263
+ - `d` : delete
264
+ - `i` / `a` + object : text object を選択(例: `vi"`, `va)`, `viw`, `vip`, `vi]`, ``vi` ``)
265
+ - `Esc` / `Ctrl-c` : キャンセル(Normal mode に戻る)
266
+ - 制限(現状):
267
+ - blockwise の text object 選択は未対応
268
+ - blockwise paste の Vim 互換挙動は未対応(yank/delete は対応)
269
+
270
+ ## ユーザー定義 Ex コマンド(`:command`)
271
+
272
+ 例:
273
+
274
+ ```vim
275
+ :command Hi help
276
+ :Hi
277
+ ```
278
+
279
+ 既存名を置き換える場合は `!` を使います。
280
+
281
+ ```vim
282
+ :command! Hi commands
283
+ ```
284
+
285
+ ## Ruby 実行(`:ruby` / `:rb`)
286
+
287
+ 例:
288
+
289
+ ```vim
290
+ :ruby buffer.line_count
291
+ :rb [window.cursor_y, window.cursor_x]
292
+ ```
293
+
294
+ `ctx`, `editor`, `buffer`, `window` を参照できます。
295
+
296
+ ## 画面まわり(現状)
297
+
298
+ - 端末リサイズに追従(`SIGWINCH` + `select` 起床 + 毎描画でサイズ再取得)
299
+ - カーソル文字は反転表示される(端末カーソルに加えて視認性向上)
300
+ - 同サイズ時は簡易差分描画で更新量を減らす
301
+ - タブ/全角文字の表示幅はベースライン対応(完全互換ではない)
302
+
303
+ ## Unicode 幅の設定(現状)
304
+
305
+ - `RUVIM_AMBIGUOUS_WIDTH=2` を設定すると、曖昧幅文字(例: 一部ギリシャ文字など)を幅2として扱います
306
+
307
+ 例:
308
+
309
+ ```bash
310
+ RUVIM_AMBIGUOUS_WIDTH=2 ruvim
311
+ ```
312
+
313
+ ## バッファ管理
314
+
315
+ - `:ls` / `:buffers` で一覧表示
316
+ - `:bnext`, `:bprev` で巡回
317
+ - `:buffer 2` のように ID 指定で切替
318
+ - `:buffer foo.txt` のように名前指定で切替
319
+ - `:buffer #` で直前バッファへ戻る
320
+ - `:bnext!`, `:bprev!`, `:buffer! ...` で未保存変更を無視して切替
321
+
322
+ ## 複数 window / split(現状)
323
+
324
+ - `:split` : 上下に分割
325
+ - `:vsplit` : 左右に分割
326
+ - `Ctrl-w w` : 次の window へ
327
+ - `Ctrl-w h/j/k/l` : 方向移動(簡易タイル前提)
328
+
329
+ 各 window はカーソル位置とスクロール位置を独立に持ちます。
330
+
331
+ ## Tabpage(現状)
332
+
333
+ - `:tabnew` : 新しいタブを作成
334
+ - `:tabnew path/to/file` : ファイルを開いたタブを作成
335
+ - `:tabnext` / `:tabn` : 次のタブへ
336
+ - `:tabprev` / `:tabp` : 前のタブへ
337
+
338
+ タブごとに split レイアウトと current window が保持されます。
339
+
340
+ ## 設定ファイル(XDG)
341
+
342
+ 起動時に以下を読み込みます(存在する場合)。
343
+
344
+ - `$XDG_CONFIG_HOME/ruvim/init.rb`
345
+ - `XDG_CONFIG_HOME` 未設定時は `~/.config/ruvim/init.rb`
346
+
347
+ 例:
348
+
349
+ ```ruby
350
+ nmap "H", "cursor.left"
351
+ nmap "L", "cursor.right"
352
+
353
+ command "user.say_hi" do |ctx, **|
354
+ ctx.editor.echo("hi from rc")
355
+ end
356
+
357
+ ex_command_call "Hi", "user.say_hi"
358
+ ```
359
+
360
+ 利用できる主な DSL:
361
+
362
+ - `nmap`, `imap`
363
+ - `map_global`
364
+ - `command`
365
+ - `ex_command`
366
+ - `ex_command_call`
367
+
368
+ ## テスト実行
369
+
370
+ ```bash
371
+ ruby -Ilib:test -e 'Dir["test/*_test.rb"].sort.each { |f| require File.expand_path(f) }'
372
+ ```
373
+
374
+ ## コマンド設計の見方
375
+
376
+ RuVim では、ユーザーが入力する Ex コマンド名(例: `:w`)と、内部の処理を分けています。
377
+
378
+ - Ex 名: `w`, `q`, `write`, `quit`
379
+ - 実処理: `RuVim::GlobalCommands` のメソッド(例: `file_write`, `app_quit`)
380
+
381
+ この分離で、ヘルプ・補完・引数チェックを後から足しやすくしています。
382
+
383
+ ## Builtin Ex コマンドを追加する
384
+
385
+ `lib/ruvim/app.rb` の `register_builtins!` に Ex 登録を追加します。
386
+
387
+ 例: `:bn`(次バッファ)を追加する場合のイメージ
388
+
389
+ ```ruby
390
+ register_ex_unless(ex, "bn", call: :buffer_next, desc: "Next buffer", nargs: 0)
391
+ ```
392
+
393
+ 次に `lib/ruvim/global_commands.rb` に実装を追加します。
394
+
395
+ ```ruby
396
+ def buffer_next(ctx, **)
397
+ # TODO: buffer list から次へ移動
398
+ end
399
+ ```
400
+
401
+ ## Normal mode のキーバインドを追加する
402
+
403
+ `lib/ruvim/app.rb` の `bind_default_keys!` で定義します。
404
+
405
+ 例: `H` を `cursor.left` に割り当てる:
406
+
407
+ ```ruby
408
+ @keymaps.bind(:normal, "H", "cursor.left")
409
+ ```
410
+
411
+ 複数キー列も使えます(現状 `dd` のような連続入力)。
412
+
413
+ ```ruby
414
+ @keymaps.bind(:normal, "dd", "buffer.delete_line")
415
+ ```
416
+
417
+ ## キーマップのレイヤー(現状 API)
418
+
419
+ `RuVim::KeymapManager` は以下の登録 API を持ちます。
420
+
421
+ - `bind(:normal, ...)` : mode-local
422
+ - `bind_global(...)`
423
+ - `bind_buffer(buffer_id, ...)`
424
+ - `bind_filetype("rb", ...)`
425
+
426
+ 解決順は `filetype -> buffer -> mode -> global` です。
427
+
428
+ ## ftplugin(filetype ごとの設定)
429
+
430
+ RuVim は buffer の path から `filetype` を簡易検出し、初回表示時に ftplugin を読み込みます。
431
+
432
+ - XDG: `~/.config/ruvim/ftplugin/<filetype>.rb`(または `$XDG_CONFIG_HOME/ruvim/ftplugin/<filetype>.rb`)
433
+
434
+ ftplugin の中では `nmap` / `imap` が filetype-local として登録されます。`setlocal` も使えます。
435
+
436
+ ```ruby
437
+ # ~/.config/ruvim/ftplugin/ruby.rb
438
+ nmap "K", "search.word_forward"
439
+ setlocal "tabstop=2"
440
+ ```
441
+
442
+ ## シンタックスハイライト(最小)
443
+
444
+ - filetype が `ruby` / `json` のとき、最小の regex ベース色付けを行います
445
+ - `search` / `cursor` / `visual` の強調が優先されるため、構文色は上書きされることがあります
446
+
447
+ ## 補完(現状の基礎)
448
+
449
+ - Command-line (`:`)
450
+ - `Tab` (`Ctrl-i`) で補完
451
+ - コマンド名に加え、`set` 系 option / `:e` `:w` の path / `:buffer` 引数を一部補完
452
+ - Insert mode
453
+ - `Ctrl-n` : buffer words 補完(次候補)
454
+ - `Ctrl-p` : buffer words 補完(前候補)
455
+
456
+ ## Symbol と Proc の使い分け
457
+
458
+ RuVim のコマンド定義は `Symbol` と `Proc` の両方に対応しています。
459
+
460
+ ### Symbol(推奨: builtin)
461
+
462
+ ```ruby
463
+ cmd.register("cursor.left", call: :cursor_left, desc: "Move cursor left")
464
+ ```
465
+
466
+ 利点:
467
+
468
+ - 一覧化しやすい
469
+ - ヘルプに載せやすい
470
+ - 実装の場所が追いやすい
471
+
472
+ ### Proc(推奨: 実験・拡張)
473
+
474
+ ```ruby
475
+ ex.register("Hello", call: ->(ctx, **) { ctx.editor.echo("hello") }, desc: "Demo", nargs: 0)
476
+ ```
477
+
478
+ 利点:
479
+
480
+ - その場定義しやすい
481
+ - プラグイン/設定向き
482
+
483
+ ## 次の拡張候補(おすすめ順)
484
+
485
+ 1. シンタックスハイライト(最小)
486
+ 2. 補完基盤(Ex 引数 / buffer word)
487
+ 3. Undo/Redo
488
+ 4. buffer-local keymap
489
+ 5. `:command` でユーザー定義 Ex コマンド
490
+ 6. `:ruby` で Ruby 実行