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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5d2d270426e0c654c22de404d8745368dbc7a5eb98fc91eecad94624a3b91b6c
4
+ data.tar.gz: 75ad40c44e89934ab1746d43aee2ac09c695513d6edb6f7e71d0b888126931a8
5
+ SHA512:
6
+ metadata.gz: 938e1eb393efd57f97260526fc40f375d27ca0aecf9b370d0b0b41b7c3f535026b1c8be82588a2713025c615e4e5222abe871f46918fbc08f3fef9b35347e023
7
+ data.tar.gz: f0a3fb80d5f3e2395daf0c689fd4129bfd6ff026e3184eb8f0e58ffc68f19ede36be7ac6c914fa3a2a70bd9b7c9bb95ae60bf292f760ce7691b498a0b12d66a7
@@ -0,0 +1,15 @@
1
+ name: test
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ test:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+ - uses: ruby/setup-ruby@v1
13
+ with:
14
+ bundler-cache: true
15
+ - run: bundle exec rake ci
data/README.md ADDED
@@ -0,0 +1,135 @@
1
+ # RuVim
2
+
3
+ Ruby で実装した Vim ライクなターミナルエディタです。
4
+
5
+ - raw mode + ANSI 描画
6
+ - Normal / Insert / Command-line / Visual
7
+ - Ex コマンド(`:w`, `:q`, `:e`, `:help`, `:set` など)
8
+ - split / vsplit / tab(最小実装)
9
+ - Ruby DSL 設定(XDG)
10
+
11
+ ## 起動
12
+
13
+ 起動:
14
+
15
+ ```bash
16
+ ruvim
17
+ ```
18
+
19
+ ファイルを開いて起動:
20
+
21
+ ```bash
22
+ ruvim path/to/file.txt
23
+ ```
24
+
25
+ 主な CLI オプション:
26
+
27
+ - `ruvim --help`
28
+ - ヘルプを表示して終了
29
+ - `ruvim --version`
30
+ - バージョンを表示して終了
31
+ - `ruvim --clean`
32
+ - ユーザー設定 / ftplugin を読まずに起動
33
+ - `ruvim -R file.txt`
34
+ - readonly モードで開く(`w` を拒否)
35
+ - `ruvim -M file.txt`
36
+ - modifiable off 相当(編集操作を拒否、readonly も有効化)
37
+ - `ruvim -Z file.txt`
38
+ - restricted mode(config/ftplugin 無効、`:ruby` 無効)
39
+ - `ruvim -u /tmp/init.rb`
40
+ - 設定ファイルを指定
41
+ - `ruvim -u NONE`
42
+ - ユーザー設定を読まない(ftplugin は有効)
43
+ - `ruvim --cmd 'set number' file.txt`
44
+ - user config 読み込み前に Ex コマンドを実行
45
+ - `ruvim -c 'set number' file.txt`
46
+ - 起動後に Ex コマンドを実行
47
+ - `ruvim +10 file.txt`
48
+ - 起動後に 10 行目へ移動
49
+ - `ruvim + file.txt`
50
+ - 起動後に最終行へ移動
51
+ - `ruvim -o a.rb b.rb`
52
+ - 複数ファイルを水平 split で開く(最小実装)
53
+ - `ruvim -O a.rb b.rb`
54
+ - 複数ファイルを垂直 split で開く(最小実装)
55
+ - `ruvim -p a.rb b.rb`
56
+ - 複数ファイルを tab で開く(最小実装)
57
+ - `ruvim -d file.txt`
58
+ - diff mode placeholder(現状は未実装メッセージのみ)
59
+ - `ruvim -q errors.log`
60
+ - quickfix startup placeholder(現状は未実装メッセージのみ)
61
+ - `ruvim -S Session.vim`
62
+ - session startup placeholder(現状は未実装メッセージのみ)
63
+
64
+ 開発環境で gem 未インストールのまま試す場合:
65
+
66
+ ```bash
67
+ ruby -Ilib exe/ruvim
68
+ ```
69
+
70
+ ## 主な操作(抜粋)
71
+
72
+ - 移動: `h j k l`, `w b e`, `0 ^ $`, `gg`, `G`
73
+ - 挿入: `i`, `a`, `A`, `I`, `o`, `O`
74
+ - 編集: `x`, `dd`, `d{motion}`, `c{motion}`, `yy`, `yw`, `p`, `P`, `r<char>`
75
+ - 検索: `/`, `?`, `n`, `N`, `*`, `#`, `g*`, `g#`
76
+ - Visual: `v`, `V`, `y`, `d`
77
+ - Undo/Redo: `u`, `Ctrl-r`
78
+ - Ex: `:w`, `:q`, `:e`, `:help`, `:commands`, `:set`
79
+
80
+ 詳しくは `docs/tutorial.md` を参照してください。
81
+
82
+ ## 設定
83
+
84
+ 設定ファイルは Ruby DSL です。
85
+
86
+ - `$XDG_CONFIG_HOME/ruvim/init.rb`
87
+ - `~/.config/ruvim/init.rb`
88
+
89
+ filetype ごとの設定:
90
+
91
+ - `$XDG_CONFIG_HOME/ruvim/ftplugin/<filetype>.rb`
92
+ - `~/.config/ruvim/ftplugin/<filetype>.rb`
93
+
94
+ `--clean` で user config / ftplugin を無効化できます。
95
+
96
+ 詳しくは `docs/config.md` を参照してください。
97
+
98
+ ## ドキュメント
99
+
100
+ - `docs/tutorial.md` - 使い方
101
+ - `docs/spec.md` - 実装仕様 / 設計
102
+ - `docs/command.md` - コマンド一覧
103
+ - `docs/binding.md` - キーバインド
104
+ - `docs/config.md` - 設定
105
+ - `docs/plugin.md` - 拡張 / plugin 的な書き方(現状)
106
+ - `docs/vim_diff.md` - Vim との差分
107
+ - `docs/todo.md` - TODO / 次フェーズ案
108
+
109
+ ## 開発
110
+
111
+ テスト実行:
112
+
113
+ ```bash
114
+ rake test
115
+ ```
116
+
117
+ CI 相当(test + docs 整合チェック):
118
+
119
+ ```bash
120
+ rake ci
121
+ ```
122
+
123
+ lint / format 方針(現状):
124
+
125
+ - `rubocop` / 自動 formatter は未導入
126
+ - 変更時は `rake test` と必要に応じて `ruby -c` で構文確認
127
+ - docs を触ったときは `rake docs:check`(`rake ci` に含まれる)
128
+ ```
129
+
130
+ ## 注意(現状)
131
+
132
+ - Vim 完全互換ではありません
133
+ - 正規表現は Vim regex ではなく Ruby `Regexp` を使います
134
+ - 文字幅 / Unicode は改善済みですが、完全互換ではありません
135
+ - 複数ファイル引数や一部 Vim CLI オプションは未実装です
data/Rakefile ADDED
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "lib" << "test"
8
+ t.test_files = FileList["test/*_test.rb"]
9
+ end
10
+
11
+ namespace :docs do
12
+ task :check do
13
+ required = %w[
14
+ README.md
15
+ docs/tutorial.md
16
+ docs/spec.md
17
+ docs/command.md
18
+ docs/binding.md
19
+ docs/config.md
20
+ docs/vim_diff.md
21
+ docs/todo.md
22
+ ]
23
+ missing = required.reject { |p| File.file?(p) }
24
+ raise "Missing docs: #{missing.join(', ')}" unless missing.empty?
25
+
26
+ refs = Dir["README.md", "docs/*.md"].flat_map do |path|
27
+ File.read(path).scan(/`(docs\/[^`]+\.md)`/).flatten
28
+ end.uniq
29
+ bad_refs = refs.reject { |p| File.file?(p) }
30
+ raise "Broken docs refs: #{bad_refs.join(', ')}" unless bad_refs.empty?
31
+ end
32
+ end
33
+
34
+ task :ci => %i[test docs:check]
35
+
36
+ task default: %i[test]
data/docs/binding.md ADDED
@@ -0,0 +1,125 @@
1
+ # RuVim キーバインディング一覧
2
+
3
+ ## Normal mode
4
+
5
+ - `h` : 左へ移動
6
+ - `j` : 下へ移動
7
+ - `k` : 上へ移動
8
+ - `l` : 右へ移動
9
+ - `0` : 行頭へ移動
10
+ - `$` : 行末へ移動
11
+ - `^` : 行頭の最初の非空白へ移動
12
+ - `w` : 次の単語へ移動
13
+ - `b` : 前の単語へ移動
14
+ - `e` : 単語末へ移動
15
+ - `f{char}` / `F{char}` : 行内で次/前の文字へ移動
16
+ - `t{char}` / `T{char}` : 行内で文字の手前/直後へ移動
17
+ - `;` / `,` : 直前の `f/F/t/T` を再実行 / 逆方向再実行
18
+ - `%` : 対応する括弧へジャンプ(`()[]{}`)
19
+ - `gg` : バッファ先頭へ移動
20
+ - `G` : バッファ末尾へ移動
21
+ - `i` : Insert mode に入る
22
+ - `a` : カーソル後ろから Insert mode
23
+ - `A` : 行末から Insert mode
24
+ - `I` : 行頭の最初の非空白から Insert mode
25
+ - `o` : 下に新規行を開いて Insert mode
26
+ - `O` : 上に新規行を開いて Insert mode
27
+ - `:` : Command-line mode に入る
28
+ - `/` : 前方検索入力に入る
29
+ - `?` : 後方検索入力に入る
30
+ - `x` : カーソル位置の文字削除
31
+ - `dd` : 現在行を削除
32
+ - `d` + motion : operator-pending delete(`dw`, `dj`, `dk`, `d$`, `dh`, `dl`)
33
+ - `diw` / `daw` : 単語 text object delete(簡易)
34
+ - `di"` / `da"` : quote text object delete(簡易)
35
+ - `di)` / `da)` : paren text object delete(簡易)
36
+ - `di]` / `da]`, `di}` / `da}` : bracket / brace text object delete(簡易)
37
+ - ``di` `` / ``da` `` : backtick quote text object delete(簡易)
38
+ - `dip` / `dap` : paragraph text object delete(簡易)
39
+ - `yy` / `yw` : yank
40
+ - `yiw` / `yaw` : 単語 text object yank(簡易)
41
+ - `yi"` / `ya"` : quote text object yank(簡易)
42
+ - `yi)` / `ya)` : paren text object yank(簡易)
43
+ - `yi]` / `ya]`, `yi}` / `ya}`, ``yi` `` / ``ya` ``, `yip` / `yap`(簡易)
44
+ - `p` / `P` : paste
45
+ - `"a`, `"A`, `"_`, `"+`, `"*` + operator/paste : register 指定
46
+ - `m{a-zA-Z}` : mark を設定(小文字 local / 大文字 global)
47
+ - `'{mark}` / `` `{mark} `` : mark へ jump(行頭寄せ / 正確位置)
48
+ - `''` / `` `` `` : jumplist で前の位置へ jump(行頭寄せ / 正確位置)
49
+ - `r<char>` : 1文字置換
50
+ - `c` + motion / `cc` : change(削除して Insert mode)
51
+ - `ciw` / `caw` : 単語 text object change(簡易)
52
+ - `ci"` / `ca"` : quote text object change(簡易)
53
+ - `ci)` / `ca)` : paren text object change(簡易)
54
+ - `ci]` / `ca]`, `ci}` / `ca}`, ``ci` `` / ``ca` ``, `cip` / `cap`(簡易)
55
+ - `v` : Visual (characterwise)
56
+ - `V` : Visual (linewise)
57
+ - `Ctrl-w w` : 次の window へ移動
58
+ - `Ctrl-w h/j/k/l` : window 間移動(split UI)
59
+ - `u` : Undo
60
+ - `Ctrl-r` : Redo
61
+ - `.` : 直前変更の repeat(現状: `x`, `dd`, `d{motion}`, `p/P`, `r<char>`)
62
+ - `Ctrl-o` : jumplist の古い位置へ
63
+ - `Ctrl-i` : jumplist の新しい位置へ(端末では Tab と同じコード)
64
+ - `q{reg}` : macro 記録開始/終了(再度 `q` で停止)
65
+ - `@{reg}` / `@@` : macro 再生 / 直前 macro 再生
66
+ - `n` : 直前検索を次へ
67
+ - `N` : 直前検索を前へ(逆方向)
68
+ - `*` / `#` : カーソル下の単語検索(前/後)
69
+ - `g*` / `g#` : カーソル下の単語を部分一致検索(前/後)
70
+ - `Esc` : メッセージ/保留入力のクリア
71
+ - `矢印キー` : 移動
72
+ - `PageUp` / `PageDown` : 画面単位で移動(概ね表示高さ - 1 行)
73
+
74
+ ### count 対応(現状)
75
+
76
+ - `3j`, `5k`, `2x`, `3dd` など
77
+ - `0` は count ではなく行頭移動として扱う
78
+
79
+ ## Insert mode
80
+
81
+ - `文字` : 挿入
82
+ - `Enter` : 改行
83
+ - `Backspace` : 削除
84
+ - `Ctrl-n` : buffer words 補完(次候補)
85
+ - `Ctrl-p` : buffer words 補完(前候補)
86
+ - `Esc` : Normal mode に戻る
87
+ - `Ctrl-c` : Normal mode に戻る
88
+ - `矢印キー` : 移動
89
+ - `PageUp` / `PageDown` : 画面単位で移動
90
+
91
+ ## Visual mode(characterwise / linewise)
92
+
93
+ - `h/j/k/l`, `w/b/e`, `0/$/^`, `gg/G`, `矢印キー` : 範囲を伸縮
94
+ - `PageUp` / `PageDown` : 範囲を画面単位で伸縮
95
+ - `v` : characterwise Visual の開始/終了
96
+ - `V` : linewise Visual の開始/切替
97
+ - `y` : 選択範囲を yank
98
+ - `d` : 選択範囲を delete
99
+ - `i` / `a` + object : text object を選択(`iw`, `aw`, `ip`, `ap`, `i"`, `a"`, ``i` ``, ``a` ``, `i)`, `a)`, `i]`, `a]`, `i}`, `a}`)
100
+ - `Esc` / `Ctrl-c` : Normal mode に戻る
101
+
102
+ ## Command-line mode
103
+
104
+ - `文字` : 入力
105
+ - `Enter` : Ex コマンド実行
106
+ - `Backspace` : 1文字削除
107
+ - `Up` / `Down` : 履歴移動
108
+ - `Left` / `Right` : カーソル移動
109
+ - `Tab` (`Ctrl-i`) : Ex 補完(`:` prefix 時、コマンド名/一部引数の文脈対応)
110
+ - `Esc` : キャンセル
111
+ - `Ctrl-c` : キャンセル
112
+
113
+ ### prefix 別の Enter 動作
114
+
115
+ - `:` で始まる場合: Ex コマンドとして実行
116
+ - `/` で始まる場合: 前方検索
117
+ - `?` で始まる場合: 後方検索
118
+
119
+ ## メモ
120
+
121
+ - 実装場所(初期バインド): `lib/ruvim/app.rb`
122
+ - `d` は keymap の固定列ではなく operator-pending 状態機械で解釈
123
+ - keymap 解決順(現状実装): `filetype-local -> buffer-local -> mode-local -> global`
124
+ - `~/.config/ruvim/init.rb`(または `$XDG_CONFIG_HOME/ruvim/init.rb`)の `nmap` / `imap` / `map_global` で上書き・追加可能
125
+ - `~/.config/ruvim/ftplugin/<filetype>.rb`(または `$XDG_CONFIG_HOME/ruvim/ftplugin/<filetype>.rb`)では `nmap` / `imap` が filetype-local として登録される
data/docs/command.md ADDED
@@ -0,0 +1,306 @@
1
+ # RuVim コマンド一覧
2
+
3
+ ## 起動オプション(CLI, 現状)
4
+
5
+ - `--help`, `--version`
6
+ - `--clean`
7
+ - `-d`(diff mode placeholder, 未実装メッセージ表示)
8
+ - `-q {errorfile}`(quickfix startup placeholder。現状は未実装メッセージ表示)
9
+ - `-S [session]`(session startup placeholder, 未実装メッセージ表示)
10
+ - `-R`
11
+ - `-M`
12
+ - `-Z`
13
+ - `-n`(現状 no-op)
14
+ - `-o[N]`, `-O[N]`, `-p[N]`
15
+ - `-V[N]`, `--verbose[=N]`
16
+ - `--startuptime FILE`
17
+ - `--cmd {cmd}`(user config 読み込み前に Ex 実行)
18
+ - `-u {path|NONE}`
19
+ - `-c {cmd}`
20
+ - `+{cmd}`, `+{line}`, `+`
21
+
22
+ ## Ex コマンド(builtin)
23
+
24
+ ### `:w` / `:write`
25
+
26
+ - 形式: `:w [path]`
27
+ - 現在バッファを保存
28
+ - `path` 指定時はそのパスに保存
29
+ - `!` 対応: `:w!`(現状は保存メッセージに反映、権限昇格などは未実装)
30
+
31
+ ### `:q` / `:quit`
32
+
33
+ - 形式: `:q`, `:q!`
34
+ - 複数 window があるとき: current window を閉じる
35
+ - window が1つで tab が複数あるとき: current tab を閉じる
36
+ - 最後の window / tab のとき: エディタ終了
37
+ - 最後の window / tab で未保存変更がある場合、`!` なしでは拒否
38
+
39
+ ### `:wq`
40
+
41
+ - 形式: `:wq`, `:wq!`, `:wq [path]`
42
+ - 保存して `:q` 相当(window / tab / app を閉じる)
43
+
44
+ ### `:e` / `:edit`
45
+
46
+ - 形式: `:e[!] [path]`
47
+ - 引数あり: 別ファイルを開く
48
+ - 引数なし: 現在ファイルを再読込
49
+ - 未保存変更がある場合は `!` なしで拒否
50
+ - `:e!` は未保存変更を破棄して開き直す(undo/redo もクリア)
51
+
52
+ ### `:help`
53
+
54
+ - 形式: `:help [topic]`
55
+ - help 用の read-only バッファを開く(仮想バッファ)
56
+ - 例:
57
+ - `:help`
58
+ - `:help regex`
59
+ - `:help options`
60
+ - `:help w`
61
+
62
+ ### `:commands`
63
+
64
+ - 形式: `:commands`
65
+ - Ex コマンド一覧(alias 含む)を read-only バッファに表示
66
+
67
+ ### quickfix / location list(最小)
68
+
69
+ - `:vimgrep /pattern/`
70
+ - 現在開いている file buffer 群を検索して quickfix list を作成
71
+ - `:lvimgrep /pattern/`
72
+ - current window の current buffer を検索して location list を作成
73
+ - `:copen` / `:cclose`
74
+ - quickfix list を read-only な `qf` バッファで開く / 閉じる
75
+ - `:cnext` / `:cn`, `:cprev` / `:cp`
76
+ - quickfix 項目を前後移動して該当位置へジャンプ
77
+ - `:lopen` / `:lclose`
78
+ - current window の location list を開く / 閉じる
79
+ - `:lnext` / `:ln`, `:lprev` / `:lp`
80
+ - location list 項目を前後移動して該当位置へジャンプ
81
+ - 現状の制限:
82
+ - `:grep`, `:make`, `:cfile`, `:lgrep` は未実装
83
+ - 一覧バッファ上で `Enter` からのジャンプは未実装
84
+
85
+ ### `:command`
86
+
87
+ - 形式: `:command Name ex_body`
88
+ - 形式: `:command! Name ex_body`(上書き)
89
+ - ユーザー定義 Ex コマンドを追加
90
+ - `:command`(引数なし)でユーザー定義コマンド一覧を表示
91
+
92
+ ### `:ruby` / `:rb`
93
+
94
+ - 形式: `:ruby <code>`
95
+ - 形式: `:rb <code>`
96
+ - Ruby コードを評価し、返り値をステータスに表示
97
+ - 利用可能: `ctx`, `editor`, `buffer`, `window`
98
+
99
+ ### `:ls` / `:buffers`
100
+
101
+ - 形式: `:ls`
102
+ - 形式: `:buffers`
103
+ - バッファ一覧をステータスに表示
104
+ - `%=current`, `#=alternate`, `+=modified` のフラグを含む
105
+
106
+ ### `:bnext` / `:bn`
107
+
108
+ - 形式: `:bnext`
109
+ - 次のバッファへ切替
110
+ - `!` 対応(未保存変更を無視して切替)
111
+
112
+ ### `:bprev` / `:bp`
113
+
114
+ - 形式: `:bprev`
115
+ - 前のバッファへ切替
116
+ - `!` 対応(未保存変更を無視して切替)
117
+
118
+ ### `:buffer` / `:b`
119
+
120
+ - 形式: `:buffer <id|name|#>`
121
+ - バッファ ID / 名前 / `#`(alternate)で切替
122
+ - `!` 対応(未保存変更を無視して切替)
123
+
124
+ ### `:split`
125
+
126
+ - 形式: `:split`
127
+ - 現在 window を水平分割(簡易タイル)
128
+ - 同じ buffer を新しい window に表示
129
+
130
+ ### `:vsplit`
131
+
132
+ - 形式: `:vsplit`
133
+ - 現在 window を垂直分割(簡易タイル)
134
+ - 同じ buffer を新しい window に表示
135
+
136
+ ### `:tabnew`
137
+
138
+ - 形式: `:tabnew [path]`
139
+ - 新しいタブを作成
140
+ - `path` 指定時はそのファイルを開く
141
+
142
+ ### `:tabnext` / `:tabn`
143
+
144
+ - 形式: `:tabnext`
145
+ - 次のタブへ移動
146
+
147
+ ### `:tabprev` / `:tabp`
148
+
149
+ - 形式: `:tabprev`
150
+ - 前のタブへ移動
151
+
152
+ ## 内部コマンド(主なもの)
153
+
154
+ 内部コマンドは主に key binding から使われ、`RuVim::CommandRegistry` に登録されます。
155
+
156
+ - `cursor.left`
157
+ - `cursor.right`
158
+ - `cursor.up`
159
+ - `cursor.down`
160
+ - `cursor.line_start`
161
+ - `cursor.line_end`
162
+ - `cursor.first_nonblank`
163
+ - `cursor.buffer_start`
164
+ - `cursor.buffer_end`
165
+ - `cursor.word_forward`
166
+ - `cursor.word_backward`
167
+ - `cursor.word_end`
168
+ - `mode.insert`
169
+ - `mode.append`
170
+ - `mode.append_line_end`
171
+ - `mode.insert_nonblank`
172
+ - `mode.open_below`
173
+ - `mode.open_above`
174
+ - `mode.visual_char`
175
+ - `mode.visual_line`
176
+ - `window.split`
177
+ - `window.vsplit`
178
+ - `window.focus_next`
179
+ - `window.focus_left`
180
+ - `window.focus_right`
181
+ - `window.focus_up`
182
+ - `window.focus_down`
183
+ - `tab_new`
184
+ - `tab_next`
185
+ - `tab_prev`
186
+ - `mode.command_line`
187
+ - `mode.search_forward`
188
+ - `mode.search_backward`
189
+ - `buffer.delete_char`
190
+ - `buffer.delete_line`
191
+ - `buffer.delete_motion`
192
+ - `buffer.change_motion`
193
+ - `buffer.change_line`
194
+ - `buffer.yank_line`
195
+ - `buffer.yank_motion`
196
+ - `buffer.paste_after`
197
+ - `buffer.paste_before`
198
+ - `buffer.visual_yank`
199
+ - `buffer.visual_delete`
200
+ - `buffer.undo`
201
+ - `buffer.redo`
202
+ - `search.next`
203
+ - `search.prev`
204
+ - `editor.buffer_next`
205
+ - `editor.buffer_prev`
206
+ - `buffer.replace_char`
207
+ - `ui.clear_message`
208
+
209
+ ## 検索コマンド(Normal mode)
210
+
211
+ 検索は Ex コマンドではなく、command-line prefix `/` `?` を使う入力経路です。
212
+
213
+ - `/pattern` : 前方検索
214
+ - `?pattern` : 後方検索
215
+ - `n` : repeat
216
+ - `N` : reverse repeat
217
+ - `*` / `#` : カーソル下の単語検索(単語境界)
218
+ - `g*` / `g#` : カーソル下の単語検索(部分一致)
219
+ - `:vimgrep` / `:lvimgrep` : 検索結果を quickfix/location list に積む
220
+
221
+ ### `:%s/.../.../g`(最小実装)
222
+
223
+ - バッファ全体置換
224
+ - Ruby 正規表現 + Ruby 置換文字列を利用
225
+ - `g` フラグ対応(全置換)
226
+
227
+ ## Operator-pending(Normal mode)
228
+
229
+ 現状は delete / yank operator を実装:
230
+
231
+ - `d` + motion
232
+ - `y` + motion(現状 `yy`, `yw`)
233
+ - `c` + motion / `cc`
234
+ - 実装の内部コマンド: `buffer.delete_motion`
235
+ - `dd` は linewise delete として扱う
236
+
237
+ ### text object(現状)
238
+
239
+ - `iw`, `aw`, `ip`, `ap`(簡易)
240
+ - `i"`, `a"`, ``i` ``, ``a` ``(簡易)
241
+ - `i)`, `a)`, `i]`, `a]`, `i}`, `a}`(簡易)
242
+ - `d`, `y`, `c` から利用可能
243
+ - Visual mode からも利用可能(選択更新)
244
+
245
+ ## Visual mode
246
+
247
+ - `v` / `V` / `Ctrl-v` で characterwise / linewise / blockwise Visual mode
248
+ - `buffer.visual_yank`
249
+ - `buffer.visual_delete`
250
+ - blockwise は最小実装(矩形選択 + `y/d`)
251
+ - blockwise の text object 選択 / paste の Vim 互換挙動は未対応
252
+
253
+ ## repeat(Normal mode)
254
+
255
+ - `.` : 直前変更の repeat(拡張版)
256
+ - 現状の対象:
257
+ - `x`
258
+ - `dd`, `d{motion}`
259
+ - `p`, `P`
260
+ - `r<char>`
261
+ - `i`, `a`, `A`, `I`, `o`, `O`(Insert 入力列)
262
+ - `cc`, `c{motion}`(text object を含む)
263
+ - macro 記録中に `.` を押した場合、内部で再生されるキーは macro に混ざらない
264
+
265
+ ## レジスタ(現状)
266
+
267
+ - unnamed register(`"`)、named register(`"a`..`"z`)、append(`"A`..`"Z`)
268
+ - black hole register(`"_`)
269
+ - yank register `0`
270
+ - numbered delete registers `1-9`(簡易回転)
271
+ - `"+`, `"*` は system clipboard register(利用可能環境のみ)
272
+ - delete / yank で更新
273
+ - `p`, `P` で paste(register prefix 対応)
274
+
275
+ ## Option / Ex(現状)
276
+
277
+ - `:set`, `:setlocal`, `:setglobal`
278
+ - 現状の接続済み option:
279
+ - `number`(window-local / 行番号表示)
280
+ - `relativenumber`(window-local / 相対行番号)
281
+ - `ignorecase` / `smartcase` / `hlsearch`(global / 検索系)
282
+ - `tabstop`(buffer-local / タブ展開幅)
283
+ - `filetype`(buffer-local / 自動検出 + ftplugin 用)
284
+
285
+ ## 実装ポリシー
286
+
287
+ - Ex コマンド名(`:w` など)と内部コマンド名(`file_write` / `buffer.undo` など)を分離
288
+ - builtin は `Symbol` ベースで `RuVim::GlobalCommands` のメソッドに実装
289
+ - 拡張用に `Proc` の登録も可能
290
+ - key binding は `KeymapManager` の layered resolution(filetype / buffer / mode / global)で解決
291
+ - XDG 設定ファイル(`$XDG_CONFIG_HOME/ruvim/init.rb` または `~/.config/ruvim/init.rb`)から `ConfigDSL` 経由で command / ex command / key binding を追加可能
292
+
293
+ ## UI メモ(現状)
294
+
295
+ - `Screen` は行キャッシュを使った簡易差分描画を行う
296
+ - `SIGWINCH` + self-pipe + `IO.select` で入力待機中でもリサイズに即追従
297
+ - command-line は履歴と Ex 補完(コマンド名 + 一部引数の文脈補完)を持つ
298
+ - Insert mode は `Ctrl-n` / `Ctrl-p` の buffer words 補完を持つ
299
+ - 文字幅は `DisplayWidth` の近似実装(tab 展開 + 一部全角幅2)
300
+
301
+ ## テスト(現状)
302
+
303
+ - `Minitest`
304
+ - `test/buffer_test.rb`
305
+ - `test/dispatcher_test.rb`
306
+ - `test/keymap_manager_test.rb`