RuVim は Ruby で実装する Vim ライクなターミナルエディタです。
: Ex 風コマンドこの文書は「現状の実装」と「今後の拡張前提の設計」をまとめた仕様です。
テキスト本体を保持する単位です。
linespathmodified?Buffer は表示状態(カーソル位置・スクロール)を持ちません。
現状の RuVim::Buffer は「行配列 + Ruby String 直接編集」です。
将来の候補(検討済み):
piece table
rope
当面の方針:
永続 undo を実装する段階で piece table 移行を再評価するBuffer をどの位置で表示するかを持つビューです。
buffer_idcursor_x, cursor_yrow_offset, col_offset同一 buffer を複数 window で表示できる前提の設計です(現状 UI は単一 window)。 同一 buffer を複数 window で表示できる前提の設計です(現状 UI は simple split 対応)。
RuVim は Vim と同様に、用途ごとに座標系を分けています。
buffer row (cursor_y, row_offset)
char index (cursor_x, col_offset)
String に対する文字 index(byte offset ではない)grapheme boundary
RuVim::TextMetrics で grapheme cluster 境界に揃えるscreen column
役割分担(現状):
RuVim::Window
char index)ensure_visible で screen column ベースの横スクロールRuVim::TextMetrics
char index <-> screen column 変換RuVim::Screen
エディタ全体の実行状態です。
:normal, :insert, :command_line, :rich)CommandRegistry)内部コマンドは ID で管理します(例: cursor.left, buffer.delete_line)。
RuVim::CommandRegistry.instancecall: に Symbol または ProcRuVim::GlobalCommands.instance例:
RuVim::CommandRegistry.instance.register(
"cursor.left",
call: :cursor_left,
desc: "Move cursor left"
)
ExCommandRegistry)ユーザーが : 行で入力するコマンド名を管理します。
RuVim::ExCommandRegistry.instancealiasesnargs, bang, desc を保持例:
RuVim::ExCommandRegistry.instance.register(
"w",
call: :file_write,
aliases: %w[write],
nargs: :maybe_one,
bang: true,
desc: "Write current buffer"
)
alias_for は持ちません。aliases を登録時に展開し、同じ spec を参照させます。
GlobalCommands)コマンド本体は RuVim::GlobalCommands.instance のメソッドで実装します。
Symbol 指定時は public_sendProc 指定時は call想定の引数形:
ctx (RuVim::Context)argv: []kwargs: {}bang: falsecount: 1RuVim::Input が raw mode のキー入力を読むRuVim::App が mode ごとに処理を分岐RuVim::KeymapManager で解決RuVim::Dispatcher が内部コマンド or Ex コマンドを実行RuVim::Screen が再描画インストール後の ruvim コマンド(実体は exe/ruvim)は RuVim::CLI を通して起動オプションを解釈します(bin/ruvim は互換ラッパー)。
--help, --version--clean
-R
-d
-q {errorfile}
-q {errorfile} は quickfix 起動互換 placeholder(現状は未実装メッセージ表示):vimgrep, :lvimgrep, :copen, :cnext など)-S [session]
-M
modifiable=false + readonly=true にする-Z
:ruby / :rb を禁止する:! を禁止する:grep / :lgrep を禁止する:git / :gh を禁止するon_save)を無効化する-n
-o[N], -O[N], -p[N]
N は現状受理するがレイアウト数の厳密制御には未使用(将来拡張用)-V[N], --verbose[=N]
stderr に出力(現状は startup / config / startup actions / Ex submit の簡易ログ)--startuptime FILE
init.start, pre_config_actions.done, config.loaded, signals.installed, buffers.opened, ftplugin.loaded, startup_actions.done--cmd {cmd}
-u {path|NONE}
path: 指定ファイルを設定として読み込むNONE: user config のみ無効化(ftplugin は有効)-c {cmd}
+{cmd}, +{line}, +
補足(現状実装):
stdin が non-TTY で、起動引数ファイルがない場合は stdin を follow stream として開く
[stdin][stdin](完了時 [stdin/EOF]、失敗時 [stdin/error])を表示Ctrl-c はデフォルトバインドで stdin stream stop(上流 PID へ直接 signal は送らない)Ctrl-z は全モード共通で suspend
SIGTSTP を自身に送って停止fg 復帰後に raw + alt screen を再有効化して再描画起動時コマンド(-c, +...)は、初期 buffer / file open / intro screen 構築の後に実行します。
--cmd はそれより前で、user config 読み込み前に実行します。
RuVim::KeymapManager は以下の優先順で解決します(高 -> 低)。
現状の標準バインドは mode-local のみですが、内部 API として各レイヤーの登録を持っています。
h/j/k/l: 移動0: 行頭へ移動$: 行末へ移動^: 行頭の最初の非空白へ移動w/b/e: 単語移動f/F/t/T + 文字: 行内文字移動;, ,: 直前の行内文字移動を繰り返し / 逆方向%: 対応括弧ジャンプ(()[]{})gg: 先頭へ移動G: 末尾へ移動i: Insert modea, A, I: 挿入開始位置を変えて Insert modeo, O: 下/上に行を開いて Insert mode:: Command-line mode/: 前方検索の command-line mode?: 後方検索の command-line modex: カーソル位置の文字削除dd: 現在行削除d + motion: operator-pending delete(例: dw, dj, dk, d$, dh, dl)iw, aw, ip, ap, i", a", i`, a`, i), a), i], a], i}, a})を d/y/c と Visual で一部利用可yy, yw: yankp, P: paster<char>: 1文字置換c + motion / cc: change(削除して Insert mode)= + motion / ==: auto-indent(Ruby / JSON filetype でインデント自動調整。=j, =G 等)v: Visual (characterwise)V: Visual (linewise)Ctrl-v: Visual (blockwise, 最小)u: UndoCtrl-r: Redo.: 直前変更の repeat(拡張版。x, dd, d{motion}, p/P, r<char>, i/a/A/I/o/O, cc, c{motion})n: 直前検索を同方向に繰り返しN: 直前検索を逆方向に繰り返し1..9 + 動作: count(例: 3j, 2x)Ctrl-z: shell へ suspend(fg で復帰)Enter: 改行Backspace: 1文字削除 / 行結合Ctrl-n / Ctrl-p: buffer words 補完(次/前)Esc: Normal mode に戻るCtrl-c: Normal mode に戻る(終了しない)Ctrl-z: shell へ suspend(fg で復帰)v: characterwise Visual の開始 / 終了V: linewise Visual の開始 / 切替Ctrl-v: blockwise Visual の開始 / 切替(最小)h/j/k/l, w/b/e, 0/$/^, gg/G, 矢印キーy: 選択範囲を yankd: 選択範囲を deletei/a + object: text object を選択(iw, aw, ip, ap, i", a", i`, a`, i), a), i], a], i}, a})Esc / Ctrl-c: Normal mode に戻るCtrl-z: shell へ suspend(fg で復帰):rich [format] / gr で入る(トグル)Esc / Ctrl-C で Normal mode に戻る-- RICH -- を表示TableRenderer を利用): で入る/ ? でも入る(検索用)BackspaceEnter で Ex 実行Esc でキャンセルLeft / Right でカーソル移動Tab (Ctrl-i) で Ex 補完
Ctrl-z で shell へ suspend(fg で復帰):ls や :set(引数なし)など、複数行にわたる出力を行うコマンドの結果を表示するモード。
:ls / :buffers, :args, :set(引数なし), :command(引数なし)echo を使用Enter / Space / Escape / Ctrl-C / その他のキー → dismiss(通常モードに戻る): → dismiss して Command-line mode に入る/ / ? → dismiss して検索 Command-line mode に入る:w [path] / :write [path]:q[!] / :quit[!]:qa[!] / :qall[!]:wq[!] [path]:wqa[!] / :wqall[!] / :xa[!] / :xall[!]:e <path> / :edit <path>:e[!] [path] / :edit[!] [path]:help [topic]:commands:bindings [mode]:command[!] <Name> <ex-body>:ruby <code> / :rb <code>:!<command>:ls / :buffers:bnext / :bn:bprev / :bp:buffer <id|name|#> / :b <id|name|#>:bdelete[!] [id|name|#] / :bd[!] [id|name|#]:split:vsplit:tabnew [path]:tabnext / :tabn:tabprev / :tabp:vimgrep, :lvimgrep:copen, :cclose, :cnext / :cn, :cprev / :cp:lopen, :lclose, :lnext / :ln, :lprev / :lp:grep /pattern/ [path...], :lgrep /pattern/ [path...]
grepprg を argv 配列として実行(シェル経由ではない)-Z)では禁止:git checkout <branch> でブランチ切り替え
:git branch)で Enter を押すとコマンドラインにプリフィル、明示的に Enter で確定:filter [/pattern/] : 検索マッチ行のみのフィルタバッファを作成(g/ キーバインド):rich [format]:d [count] / :delete:y [count] / :yank:tabs:args, :next, :prev, :first, :last/pattern : 前方検索?pattern : 後方検索n : 直前検索を同方向に繰り返しN : 直前検索を逆方向に繰り返し*, # : カーソル下の単語を前/後方検索(単語境界つき)g*, g# : カーソル下の単語を前/後方検索(部分一致):{range}s/pat/repl/[flags] : substitute(フラグ: g, i, I, n, e, c 対応):[range]g/pattern/command : global(マッチ行に Ex コマンドを実行):[range]v/pattern/command : vglobal(非マッチ行に Ex コマンドを実行):{range}p / :{range}nu : 行の表示 / 行番号付き表示:{range}m {addr} / :{range}t {addr} : 行の移動 / コピー:{range}j : 行の結合:{range}> / :{range}< : インデント増減:{range}normal {keys} : 各行で Normal mode コマンドを実行補足:
:q は未保存変更があると拒否:q は Vim 寄りに、複数 window 時は current window を閉じる(window が1つで tab が複数なら current tab を閉じる):q! は強制的に window / tab / app を閉じる:qa は全ウィンドウ/タブを無視して一括終了(未保存バッファがあると拒否、:qa! で強制):wqa は全バッファを保存して一括終了:e は未保存変更があると拒否(! で破棄可):e!(引数なし)は現在ファイルの再読込(undo/redo クリア):buffer!, :bnext!, :bprev! は未保存変更があっても切替:w! は現状 :w と同等に受理(権限昇格などは未実装):bindings は current buffer 文脈の有効 key binding を layer 別(buffer, filetype, app)に一覧表示
:bindings normal)[load](失敗時 [load/error])8MB を先に表示し、残りをチャンク単位でバックグラウンド読み込みRUVIM_ASYNC_FILE_THRESHOLD_BYTESRUVIM_ASYNC_FILE_PREFIX_BYTESFile.file? チェック)path:line / path:line:col 形式でファイルを開くと、指定行・桁にジャンプする
:e、gf のいずれでも対応:command(現状仕様):command Name ex_body でユーザー定義 Ex コマンドを追加:command! Name ex_body で上書き:command(引数なし)でユーザー定義コマンド名一覧表示:Name で実行:ruby / :rb(現状仕様)ctxeditorbufferwindowstdout / stderr に出力があれば [Ruby Output] 仮想バッファに表示(返り値も表示)inspect):r / :read(ファイル・コマンド出力の挿入):r <file> でファイルの内容をカーソル行の下に挿入:r !<command> でシェルコマンドの stdout を挿入:3r file.txt(3行目の下に挿入):w !(バッファ内容をコマンドへパイプ):w !<command> でバッファ全体をシェルコマンドの stdin に渡す:'<,'>w !sort(選択範囲のみ渡す):!(shell 実行, 最小):!<command> で shell コマンドを同期実行shell exit N をステータス表示-Z)では禁止:ls / :buffers : バッファ一覧表示:bnext / :bn : 次バッファへ:bprev / :bp : 前バッファへ:buffer / :b : バッファ切替
#(alternate buffer):bdelete / :bd : バッファ削除(未保存は ! 必須)#)Editor#alternate_buffer_id を保持:buffer # で切替可能Editor#arglist と Editor#arglist_index を保持:ls に表示される):args : arglistを表示(現在の引数は [filename] で表示):next / :prev : arglist内を移動:first / :last : arglistの最初/最後に移動ANSI エスケープシーケンスによる再描画です。
?1049h / ?1049l)?25l / ?25h): / ? 入力時)または message line(メッセージ表示時)として使用。複数行メッセージ時は hit-enter prompt で表示\e[6 q)を表示\e[2 q)を設定、終了時にデフォルト(\e[0 q)に復元:split / :vsplit で複数 window を作成hsplit: 上下分割vsplit: 左右分割close_window でツリーを簡略化(子が 1 個になった分割ノードは子に置き換え)close_other_windows で current 以外の全 window を閉じる(Ctrl-w o)focus_window_direction は正規化座標空間で最近接ウィンドウを選択resize_window で分割ノードに weight を設定してサイズ調整(Ctrl-w +/-/</> )equalize_windows で全 weight をリセット(Ctrl-w =):tabnew [path] で新しいタブを作成:tabnext, :tabprev で移動:tabs で全タブ一覧を表示(各タブのウィンドウとバッファ名)tab:n/m を表示(タブが2つ以上のとき)SIGWINCH を trap して Screen キャッシュを無効化IO.pipe) で resize 通知を select 待ちへ伝播stdin + resize通知 を IO.select で待つwinsize を再取得して viewport を再計算Ctrl-z 入力は app レベルで処理し、モードに関係なく suspend するSIGTSTP を送るfg 復帰時は alt screen を再有効化し、Screen キャッシュを破棄して全面再描画する: / ?)Up/Down で履歴移動Tab で Ex コマンド名補完(prefix : のとき)TABSTOP=2)RUVIM_AMBIGUOUS_WIDTH=2 で曖昧幅文字を幅2として扱える描画の幅処理(現状):
RuVim::TextMetrics.clip_cells_for_width を共通利用
source_col 保持(cursor/search/syntax highlight の重ね合わせ用)char index <-> screen column 変換で揃える制約:
h/l・矢印左右)は grapheme cluster を考慮するが、他の移動は未統一screen column ベース(TextMetrics 利用)File.binread で bytes を取得RuVim::Buffer.decode_text で UTF-8 へ変換して保持Encoding.default_external を試し、それでもダメなら scrubBuffer#lines は UTF-8 String 前提File.binwrite で lines.join("\n") をそのまま保存RuVim::Buffer ごとの undo / redo stacku: undoCtrl-r: redoxdd3x, 3dd はそれぞれ 1 undoi で入り、複数文字入力して Esc/Ctrl-c で抜けると 1 回の u で戻るVim 完全互換ではなく、まずは扱いやすい粒度を優先した仕様です。
d を押すと delete operator pending 状態に入る対応している d + motion:
dd : 行削除dh : 左方向に文字削除dl : 右方向に文字削除dj : 下方向行を含めて行削除dk : 上方向行を含めて行削除d$ : 行末まで削除dw : 次の単語先頭まで削除(簡易 word 定義)diw, daw : text object word(簡易)di", da", di), da) : delimiter text object(簡易・同一行中心)設計上は operator-pending 状態機械を導入しており、d/y/c/= を同じ流れで扱います。
補足:
y operator も実装済み(yy, yw)c operator も実装済み(cw, cc, c$, ciw, caw など)= operator も実装済み(==, =j, =k, =G, =gg、Visual =)
iw, awi", a"i), a)d/y/ci" / a" / i) / a) は簡易実装(主に同一行)") を実装"a.."z)を実装"A.."Z)を実装(小文字 register へ追記)"_)を実装0 を実装(yank 操作で更新)1-9 を簡易実装(delete/change 操作で回転)"+, "* は system clipboard register として扱う(利用可能 backend がある場合)p, P は指定 register(なければ unnamed)を paste:charwise:linewiseyy, yw, Visual y などで yankx, dd, d{motion}, Visual d などで delete した内容も保存m{a-z}: local mark(buffer-local)m{A-Z}: global mark'{mark}: 行単位ジャンプ(行頭の最初の非空白へ)`{mark}: 位置ジャンプ<C-o>: 古い位置へ<C-i>: 新しい位置へ(端末では Tab と同じ入力コード)'': 行単位で古い位置へ ``: 位置ジャンプで古い位置へgg, G/, ?, n, N, *, #, g*, g#):bnext, :bprev, :buffer)q{reg}: macro 記録開始(q で停止)@{reg}: macro 再生@@: 直前に再生した macro を再生a-z, A-Z, 0-9 を許可(A-Z は既存 macro に追記):set:setlocal:setglobalRuVim::Editor::OPTION_DEFS に定義(現状 51 個)number, relativenumber, wrap, linebreak, breakindent, cursorline, scrolloff, sidescrolloffignorecase, smartcase, hlsearch, incsearch, splitbelow, splitright, hidden, clipboard, timeoutlen, syncloadtabstop, expandtab, shiftwidth, softtabstop, autoindent, smartindent, filetype, onsavehookdocs/config.md を参照filetype を簡易検出して buffer-local option に保存$XDG_CONFIG_HOME/ruvim/ftplugin/<filetype>.rb~/.config/ruvim/ftplugin/<filetype>.rbnmap / imap -> filetype-local keymap として登録setlocal / setglobal / set(DSL)で option 変更可on_save(ctx, path) ライフサイクルフックを定義:w でファイル保存後、onsavehook オプションが有効(デフォルト true)なら buffer.lang_module.on_save(ctx, target) を呼び出すLang::Base.on_save はデフォルトで何もしない(no-op)Lang::Ruby.on_save は ruby -wc で構文チェックを実行し、エラー/警告を quickfix list に展開する
filename:line: 形式でパースし quickfix items に変換(]q to see next, N total) を表示:set noonsavehook で無効化可能ruby(Prism lexer ベース。スマートインデント・保存時 ruby -wc チェック)json / jsonl(キー・値・真偽値・数値。スマートインデント)markdown(見出し・フェンス・HR・ブロック引用・インライン装飾)scheme(キーワード・文字列・数値・真偽値・文字リテラル・コメント)diff(追加/削除/ハンク/ヘッダ/メタの行単位カラー)c(キーワード・型名・文字列・数値・コメント・プリプロセッサ・定数マクロ。スマートインデント・保存時 gcc チェック)cpp(C 拡張: class, namespace, template, auto, nullptr 等。アクセス指定子インデント・保存時 g++ チェック)yaml(キー・文字列・真偽値・アンカー・タグ・ブロックスカラー・コメント。インデント)sh(キーワード・変数・文字列・数値・コメント。インデント)python(キーワード・組み込み関数・デコレータ・文字列・数値・定数・コメント。インデント)javascript / javascriptreact(キーワード・テンプレートリテラル・文字列・数値・定数・コメント。インデント)typescript / typescriptreact(JS 拡張: interface, type, enum, readonly 等。インデント)html(タグ・属性・文字列・エンティティ・DOCTYPE・コメント)toml(テーブルヘッダ・キー・文字列・真偽値・日時・数値・コメント)go(キーワード・型名・文字列・raw 文字列・数値・定数・コメント。インデント)rust(キーワード・ライフタイム・マクロ・属性・文字列・数値・定数・コメント。インデント)make(ターゲット・変数定義・変数参照・ディレクティブ・関数・自動変数・コメント)dockerfile(命令キーワード・フラグ・変数・文字列・コメント)sql(キーワード(大文字小文字不問)・文字列・数値・パラメータ・コメント)elixir(キーワード・アトム・モジュール・属性・シジル・文字列・数値・コメント。インデント)perl(キーワード・スカラー/配列/ハッシュ変数・文字列・数値・POD・コメント。インデント)lua(キーワード・組み込み関数・長文字列・文字列・数値・コメント。インデント)ocaml(キーワード・型変数・モジュール名・文字列・数値・ブロックコメント。インデント)erb(HTML ベース + ERB デリミタ・ERB コメント)lib/ruvim/highlighter.rb(ディスパッチャ)+ lib/ruvim/lang/markdown.rb(言語固有ロジック)構造化データ(TSV/CSV)や Markdown を見やすく整形して表示するモードです。 Visual mode と同様に Normal mode の上に乗るモードとして設計されています。
RuVim::RichView モジュール(lib/ruvim/rich_view.rb)RichView.register(filetype, renderer)TableRenderer(TSV/CSV)、MarkdownRenderer(Markdown)、JsonRenderer(JSON)、JsonlRenderer(JSONL)、ImageRenderer(画像) 画像行: sixel対応ターミナルでは画像を表示、非対応時は [Image: alt] プレースホルダーを表示:set sixel=auto|on|off で sixel 出力を制御(auto はDA1クエリで自動検出)JsonRenderer は仮想バッファ方式: ミニファイ JSON を JSON.pretty_generate で整形し、読み取り専用バッファに表示JsonlRenderer は仮想バッファ方式: 各行を個別にパース・整形し、--- セパレータで区切って読み取り専用バッファに表示。パースエラー行はエラーマーカー付きで表示ImageRenderer は仮想バッファ方式: 画像ファイル(.png/.jpg/.jpeg/.gif/.bmp/.webp)を開くと自動的に  の Markdown 画像行として仮想バッファに表示し、sixel でレンダリングEsc / C-c でバッファを閉じて元に戻れる:rich [format] Ex コマンド(トグル)gr Normal mode キーバインド(トグル)Editor#rich_state に format/delimiter を保持Esc / Ctrl-C で Normal mode に戻る-- RICH -- を表示Screen の plain_window_render_rows で editor.rich_mode? を判定RichView.render_visible_lines で整形render_rich_view_line_sc は ANSI エスケープシーケンスを幅ゼロとして扱い、横スクロール時もスタイルを正しく維持@rich_col_offset_sc)renderer.cursor_display_col で raw バッファの cursor_x を整形後の表示カラムに変換し、スクロールオフセットを決定render_rich_view_line_sc で各行を同じ表示カラム数だけスキップして描画(CJK/ASCII 混在でも列揃えが保たれる)| 区切り表示: ` | `(スペース+パイプ+スペース) |
DisplayWidth.display_width で正確に計算#, **, *, ` 等)は残し、ANSI スタイルを重ねる**bold**, *italic*, `code`, [text](url), チェックボックス /~~~` フェンスで状態追跡、内容を暖色表示pre_context_lines で表示領域前の行から引き継ぎ|...| パターンを検出し、列幅揃え + box-drawing 罫線(│, ─, ┼, ├, ┤)---/***/___ を ─ 線に置換> で始まる行を cyan 表示.tsv → tsv, .csv → csv, .md → markdown:rich(引数なし)は filetype から判定、不明なら内容を見て自動推測グローバルに共有して良い「定義系」だけシングルトンにしています。
RuVim::CommandRegistry.instanceRuVim::ExCommandRegistry.instanceRuVim::GlobalCommands.instanceRuVim::Editor はシングルトンではありません(実行状態の分離のため)。
$XDG_CONFIG_HOME/ruvim/init.rb~/.config/ruvim/init.rbRuVim::ConfigLoader + RuVim::ConfigDSL を使用BasicObject ベースで、主に以下を提供:
nmapimapmap_globalsetsetlocalsetglobalcommandex_commandex_command_call安全性メモ:
init.rb)は Ruby として評価されるため、信頼できる内容のみ使用する<C-g> で :git プリセットのコマンドラインモードに入る。:git <subcommand> で実行。
未知のサブコマンドは :! と同様に alternate screen を抜けてシェルで直接実行する(例: :git stash)。:gh も同様(例: :gh issue list)。
:git status で git status の結果を kind: :git_status の読み取り専用バッファで表示。
:git diff で git diff の結果を kind: :git_diff の読み取り専用バッファで表示(filetype: diff)。追加引数をそのまま渡せる(例: :git diff --cached)。差分がない場合はメッセージ表示のみ。Enter で差分行に対応するファイルの該当行にジャンプ。:git log -p バッファでも同様に動作する。
:git log で git log の結果を kind: :git_log の読み取り専用バッファで表示。追加引数をそのまま渡せる(例: :git log -p)。-p 指定時は filetype: diff で syntax highlight が効く。出力はストリーミングで逐次表示(カーソルは先頭行に固定)。バッファを閉じるとプロセスも停止する。
:git branch で git branch -a の結果を kind: :git_branch の読み取り専用バッファで表示。コミット日時の新しい順にソート。各行にブランチ名、日付、最新コミットのサブジェクトを表示。Enter でカーソル行のブランチ名を :git checkout <branch> としてコマンドラインにプリフィル(確認ステップあり、即時実行ではない)。
:git grep <pattern> [<args>...] で git grep -n を実行し、結果を kind: :git_grep の読み取り専用バッファで表示。追加引数をそのまま渡せる(例: :git grep -i pattern)。マッチがない場合はメッセージ表示のみ。Enter で該当ファイルの該当行にジャンプ。Esc / Ctrl-C でバッファを閉じる。
:git commit でコミットメッセージ編集バッファ(kind: :git_commit)を開く。# で始まる行はコメント(git status 情報を表示)。insert モードで開始。:w または :wq でコミット実行。:q! でキャンセル。メッセージが空の場合はコミットを中止。
kind: :blame、readonly、modifiable=falsegutter_labels オプション: バッファに行ごとのガターラベルを設定する汎用機能。設定されると行番号の代わりにラベルを表示git blame --porcelain を使用して構造化パースBlame バッファ内のバッファローカルバインディング:
p (GitBlamePrev): カーソル行のコミット C に対し git blame C^ の結果に更新。履歴スタックに現在の状態を pushP (GitBlameBack): 履歴スタックから pop して前の blame 状態に復元c (GitBlameCommit): カーソル行のコミットの git show 結果を kind: :git_show の読み取り専用バッファで表示(filetype: diff)実装: lib/ruvim/git/blame.rb(blame パース・実行)、lib/ruvim/git/commands.rb(status/diff/log 実行)、lib/ruvim/global_commands.rb(コマンドハンドラ)
:gh link で現在のファイル・カーソル行の GitHub URL を生成し、message line に表示する。同時に OSC 52 エスケープシーケンスでクリップボードにコピーする。
:'<,'>gh link)を指定すると #L5-L10 形式の行範囲リンクを生成origin → upstream → その他の優先順位):gh link <remote> でリモート名を明示指定可能(remote may differ) を注記:gh browse で現在のファイル・カーソル行の GitHub URL をブラウザで開く。
:gh link と同じ URL 解決ロジック(行範囲、リモート自動検出、リモート指定)Browser モジュールを使用(macOS: open, Linux: xdg-open, WSL: wslview / PowerShell)(remote may differ) を注記:gh pr で現在のブランチの PR ページをブラウザで開く。
https://github.com/user/repo/pulls?q=head:<branch> を生成して開く実装: lib/ruvim/gh/link.rb, lib/ruvim/browser.rb
Minitest を利用test/buffer_test.rbtest/dispatcher_test.rbtest/keymap_manager_test.rbundofile / undodir — 実装済み。:set undofile で有効化)-S / :mksession 相当の実体):make / :cfile / :lfile など quickfix 入口(:grep / :lgrep は実装済み):substitute の c(confirm)フラグ — 実装済み(y/n/a/q/l/Esc 対応)Ctrl-w window 操作 — c(close), o(only), =(equalize), +/-/</> (resize) 実装済み:set 高度構文(+=, -=, :set all, 短縮名):global / :normal