このリポジトリは Ruby 標準ライブラリのみで動きます。
ruvim
ファイルを指定しない起動では、Vim 風の intro screen(RuVim では intro 用の read-only 特殊バッファ)を表示します。
編集を始めると通常の空バッファに置き換わります。
ファイルを開いて起動:
ruvim path/to/file.txt
--help(ヘルプを表示して終了)--version(バージョンを表示して終了)--clean(ユーザー設定と ftplugin を読まない)-d(diff mode placeholder。現状は未実装メッセージのみ)-q errors.log(quickfix startup placeholder。現状は未実装メッセージのみ)-S Session.vim(session startup placeholder。現状は未実装メッセージのみ)-R(readonly で開く。現在バッファの :w を拒否)-M(modifiable off 相当。編集操作を拒否し、あわせて readonly)-Z(restricted mode。config/ftplugin を読まず、:ruby、:!、:grep、:lgrep、:git、:gh を無効化)-f(follow mode: tail -f 相当。ファイルの追記をリアルタイムに追従)-n(現状 no-op。将来の swap/永続機能向け互換フラグ)-o[N] / -O[N] / -p[N](複数ファイルを split / vsplit / tab で開く)-V[N] / --verbose[=N](起動/設定/Ex のログを stderr に出す)--startuptime file.log(起動フェーズの簡易 timing log を書く)--cmd 'set number'(user config 読み込み前に Ex 実行)-u path/to/init.rb(設定ファイルを指定)-u NONE(ユーザー設定を読まない)-c 'set number'(起動後に Ex 実行)+10(起動後に 10 行目へ移動)+(起動後に最終行へ移動)例:
ruvim --clean file.txt
ruvim -R file.txt
ruvim --cmd 'set number' -u /tmp/minimal_init.rb file.txt
ruvim -u /tmp/minimal_init.rb -c 'set number' file.txt
ruvim +10 file.txt
ruvim -o a.rb b.rb
ruvim -O a.rb b.rb
ruvim -p a.rb b.rb
開発環境で gem 未インストールのまま試す場合は ruby -Ilib exe/ruvim でも起動できます。
h 左j 下k 上l 右0 行頭$ 行末^ 行頭の最初の非空白w 次の単語へb 前の単語へe 単語末へf<char>, F<char> 行内文字移動t<char>, T<char> 行内「手前/直後」移動;, , 直前の f/F/t/T を繰り返し / 逆方向% 対応括弧ジャンプ(()[]{})gg 先頭へG 末尾へi Insert modea, A, I 挿入開始位置を変えて Insert modeo, O 行を開いて Insert mode: Command-line mode/ 前方検索? 後方検索x 文字削除dd 行削除d + motion(例: dw, dj, d$)yy, yw yankp, P paster<char> 1文字置換(例: rx)v, V, Ctrl-v Visual mode(char / line / block)c + motion / cc change(削除して Insert mode)u undoCtrl-r redo. 直前変更の繰り返し(拡張版)
x, dd, d{motion}, p/P, r<char>i/a/A/I/o/O の Insert 入力(Esc / Ctrl-c まで)c{motion}, cc(text object を含む). の内部再生キーは記録しないn 検索を次へN 検索を前へ3j など count 対応(一部コマンド)Enter で改行Backspace で削除Esc で Normal mode に戻るCtrl-c でも Normal mode に戻る(終了しない): を押すと最下段で入力Enter で実行Esc でキャンセルUp/Down で履歴Tab で Ex 補完(: のとき。コマンド名/一部引数)使えるコマンド:
:w:w path/to/file.txt:q:q!:wq:e other.txt:e!(現在ファイルを再読込):e! other.txt(未保存変更を破棄して開く):help:help regex, :help options, :help w(topic 指定):commands:command Name ex_body:command! Name ex_body:ruby <code> / :rb <code>:ls / :buffers:bnext / :bn:bprev / :bp:buffer <id|name|#> / :b <id|name|#>:split:vsplit:tabnew [path]:tabnext / :tabn:tabprev / :tabp:follow(tail -f 相当の追従モードをトグル):vimgrep /foo/:copen, :cnext, :cprev, :cclose:lvimgrep /foo/:lopen, :lnext, :lprev, :lclose:grep pattern [files...](外部 grep → quickfix):lgrep pattern [files...](外部 grep → location list):git blame / :git status / :git diff / :git log / :git branch / :git commit / :git grep:gh link / :gh browse / :gh pr:r file.txt(ファイルの内容をカーソル行の下に挿入):r !ls(コマンドの出力をカーソル行の下に挿入):w !wc -l(バッファの内容をコマンドの stdin にパイプ):!ls(alternate screen でシェルコマンドを実行)tail -f 相当)ファイルへの追記をリアルタイムにバッファへ反映する追従モードです。
ruvim -f /var/log/syslog # 起動時から follow mode
ruvim -f log1.txt log2.txt # 複数ファイルすべて follow
起動後に切り替えることもできます:
:follow " follow 開始(トグル)
:follow " もう一度で停止
Ctrl-C(ノーマルモード)でも停止できますG)にいると末尾を自動追従、途中にいればスクロール位置を維持u: 直前の変更を取り消すCtrl-r: 取り消した変更をやり直す現状の undo 粒度:
x, dd などは 1 コマンド = 1 undoi で入って Esc / Ctrl-c で抜けるまでを 1 undo として扱う/foo : 前方検索?foo : 後方検索n : 同方向に繰り返しN : 逆方向に繰り返し* / # : カーソル下の単語を検索g* / g# : カーソル下の単語を部分一致検索検索は command-line の入力欄を再利用しています(prefix が : ではなく / または ? になる)。
検索パターンは Ruby 正規表現です(例: /foo\d+/ 相当なら foo\d+ を入力)。
:vimgrep /foo/ : 開いている file buffer 群を検索して quickfix list を作る:copen : quickfix list を read-only qf バッファで開く:cnext, :cprev : quickfix 項目を移動してジャンプ:lvimgrep /foo/ : current buffer を検索して current window の location list を作る:lopen, :lnext, :lprev, :lclose : location list を操作:%s/foo/bar/gd + motion(operator-pending)使える例(現状):
dd : 行削除dw : 次の単語先頭まで削除dj : 現在行 + 次行を削除dk : 現在行 + 前行を削除d$ : 行末まで削除dh / dl : 左右の文字削除diw / daw : 単語 text object(簡易)di" / da" : ダブルクォート text object(簡易)di) / da) : 丸括弧 text object(簡易)di] / da], di} / da} : bracket / brace text object(簡易)di` / da` : backtick quote text object(簡易)dip / dap : paragraph text object(簡易)yy : 現在行を yankyw : 単語方向に yankyi], yi}, yi`, yip など text object yank(簡易)p : カーソル後ろに pasteP : カーソル前に paster<char> : カーソル位置の1文字を置換register prefix を付けると register を指定できます。
"ayy : register a に行 yank"Ayy : register a に追記 yank"_dd : black hole register に捨てる(unnamed/numbered を汚さない)yy の結果は register 0 にも入るdd / d{motion} の結果は register 1-9 に回転保存される(簡易)"+p : system clipboard を paste(backend が使える環境)"*p : system clipboard register * を paste(backend が使える環境)ma : local mark a を設定mA : global mark A を設定'a : mark a の行へジャンプ(先頭の非空白)`a : mark a の正確な位置へジャンプCtrl-o / Ctrl-i : jump list を戻る / 進む(Ctrl-i は Tab と同じコード)qa : macro を register a に記録開始q : 記録停止@a : macro a を再生@@ : 直前 macro を再生:set):set number / :set nonumber : 行番号表示の ON/OFF(window-local):set relativenumber / :set norelativenumber : 相対行番号(window-local):set ignorecase / :set noignorecase : 検索の大文字小文字を無視(global):set smartcase / :set nosmartcase : ignorecase 有効時に大文字を含む検索を大文字小文字区別にする(global):set hlsearch / :set nohlsearch : 検索ハイライトの ON/OFF(global):nohlsearch (:noh) : 検索ハイライトを一時的にクリア(次の検索で自動復帰):set tabstop=4 : tab 幅設定(既定スコープは buffer-local):setlocal number : 現在 window のみ変更:setglobal tabstop=8 : global 値を変更:set : 現在の option 一覧を表示(簡易)c)cw : 単語方向に changecc : 行を changec$ : 行末まで changeciw, caw : 単語 text object を change(簡易)ci], ca}, ci`, cip, cap なども利用可(簡易)c は削除後に Insert mode に入ります。
v : characterwise VisualV : linewise VisualCtrl-v : blockwise Visual(最小)y : yankd : deletei / a + object : text object を選択(例: vi", va), viw, vip, vi], vi`)Esc / Ctrl-c : キャンセル(Normal mode に戻る):command)例:
:command Hi help
:Hi
既存名を置き換える場合は ! を使います。
:command! Hi commands
:ruby / :rb)例:
:ruby buffer.line_count
:rb [window.cursor_y, window.cursor_x]
ctx, editor, buffer, window を参照できます。
SIGWINCH + select 起床 + 毎描画でサイズ再取得)RUVIM_AMBIGUOUS_WIDTH=2 を設定すると、曖昧幅文字(例: 一部ギリシャ文字など)を幅2として扱います例:
RUVIM_AMBIGUOUS_WIDTH=2 ruvim
:ls / :buffers で一覧表示:bnext, :bprev で巡回:buffer 2 のように ID 指定で切替:buffer foo.txt のように名前指定で切替:buffer # で直前バッファへ戻る:bnext!, :bprev!, :buffer! ... で未保存変更を無視して切替:split : 上下に分割:vsplit : 左右に分割Ctrl-w w : 次の window へCtrl-w h/j/k/l : 方向移動(簡易タイル前提)各 window はカーソル位置とスクロール位置を独立に持ちます。
:tabnew : 新しいタブを作成:tabnew path/to/file : ファイルを開いたタブを作成:tabnext / :tabn : 次のタブへ:tabprev / :tabp : 前のタブへタブごとに split レイアウトと current window が保持されます。
起動時に以下を読み込みます(存在する場合)。
$XDG_CONFIG_HOME/ruvim/init.rbXDG_CONFIG_HOME 未設定時は ~/.config/ruvim/init.rb例:
nmap "H", "cursor.left"
nmap "L", "cursor.right"
command "user.say_hi" do |ctx, **|
ctx.editor.echo("hi from rc")
end
ex_command_call "Hi", "user.say_hi"
利用できる主な DSL:
nmap, imapmap_globalcommandex_commandex_command_callruby -Ilib:test -e 'Dir["test/*_test.rb"].sort.each { |f| require File.expand_path(f) }'
RuVim では、ユーザーが入力する Ex コマンド名(例: :w)と、内部の処理を分けています。
w, q, write, quitRuVim::GlobalCommands のメソッド(例: file_write, app_quit)この分離で、ヘルプ・補完・引数チェックを後から足しやすくしています。
lib/ruvim/app.rb の register_builtins! に Ex 登録を追加します。
例: :bn(次バッファ)を追加する場合のイメージ
register_ex_unless(ex, "bn", call: :buffer_next, desc: "Next buffer", nargs: 0)
次に lib/ruvim/global_commands.rb に実装を追加します。
def buffer_next(ctx, **)
# TODO: buffer list から次へ移動
end
lib/ruvim/app.rb の bind_default_keys! で定義します。
例: H を cursor.left に割り当てる:
@keymaps.bind(:normal, "H", "cursor.left")
複数キー列も使えます(現状 dd のような連続入力)。
@keymaps.bind(:normal, "dd", "buffer.delete_line")
RuVim::KeymapManager は以下の登録 API を持ちます。
bind(:normal, ...) : mode-localbind_global(...)bind_buffer(buffer_id, ...)bind_filetype("rb", ...)解決順は filetype -> buffer -> mode -> global です。
RuVim は buffer の path から filetype を簡易検出し、初回表示時に ftplugin を読み込みます。
~/.config/ruvim/ftplugin/<filetype>.rb(または $XDG_CONFIG_HOME/ruvim/ftplugin/<filetype>.rb)ftplugin の中では nmap / imap が filetype-local として登録されます。setlocal も使えます。
# ~/.config/ruvim/ftplugin/ruby.rb
nmap "K", "search.word_forward"
setlocal "tabstop=2"
ruby / json のとき、最小の regex ベース色付けを行いますsearch / cursor / visual の強調が優先されるため、構文色は上書きされることがあります:)
Tab (Ctrl-i) で補完set 系 option / :e :w の path / :buffer 引数を一部補完Ctrl-n : buffer words 補完(次候補)Ctrl-p : buffer words 補完(前候補)RuVim のコマンド定義は Symbol と Proc の両方に対応しています。
cmd.register("cursor.left", call: :cursor_left, desc: "Move cursor left")
利点:
ex.register("Hello", call: ->(ctx, **) { ctx.editor.echo("hello") }, desc: "Demo", nargs: 0)
利点:
:command でユーザー定義 Ex コマンド:ruby で Ruby 実行