実践vimはいい本。vimを今後長く付き合っていくパートナーと考えたならば、一度は読んでおきたいと思いました。本記事は実践vimで得たtipsの自分用羅列です。
この投稿は順次アップデートしていく予定です。
はじめに
初めてvimをやろうかと思った人は、この本買うよりも前に、
- ブラインドタッチをある程度できるようになっておくこと
- vimのインストール
- vimインストール時点で同梱されている
vimtutor
(インタラクティブなvimのチュートリアル)を一通りやっておきましょう。起動はターミナルにvimtutor
と打つだけ。
つぎに
素のvimのまま使っている人はあんまり居ないでしょうから、vimを起動する際に
$ vim -u NONE -N
のようにオプションをつけて起動すると、.vimrcを無視して素のvimが開きます。
日本語doc
本の中でもよく:h {コマンド}
でヘルプを参照しています。
ヘルプ:h
をよく参照する癖をつけましょう。
プラグインマネージャで'vim-jp/vimdoc-ja'を入れると日本語になってわかりやすいです。
私の場合はdeinを使ってtomlファイルに書き込んでいます。
[[plugins]] # Japanese doc
repo = 'vim-jp/vimdoc-ja'
hook_add = 'set helplang=ja,en'
tips
-
繰り返ししやすいようにエディットする癖をつける。
- VIM Golf: vimエディットのスコアリング
- 理想は移動にキー1入力、何かするのにキー1入力
- f,tコマンドやオペレータ(d,y,c+移動キー)の組み合わせで移動とアクションを同時に行う
- j.や.;.;だけで高速編集したい。
- dap, caw, ciw, yaw, yapとか。pはパラグラフ。{}で移動できる範囲
-
ワンストロークでツーアクションするキーを覚える
- I, A: 行頭、行末へ移動しながら挿入モードへ移行
- D, (C): 行末へ向かって削除(からの挿入モードへ移行)
- O, o: 改行しながら挿入モードへ移行
-
繰り返しコマンドをなるべく使う
-
.
動作の繰り返し -
;
移動の繰り返し -
@
マクロの繰り返し -
&
exコマンドの繰り返し
-
-
インサートモード中に矢印キーで移動すると、ノーマルモードで移動したとvimは考える。
- つまり、
u
による移動に区切りができる。 - アンドゥする単位を決める、すなわち粒度を制御をするためにわざと矢印キーを使うのも手かな。
- <CR>で改行するよりは<Esc> oで改行する癖をつける。
- つまり、
ファイル内移動
:h motion.txt
- H,Lで画面の上下にカーソルを移動して、zzで中央に寄せるのが速くて読みやすい。
- もちろん半ページ移動の<C-D>(反対方向は<C-U>)も使える。
- 表示行と論理行を意識する。この区別はvim独特のもの。
- j,kの移動で「あれ?思った通りの場所に移動しない」と思ったら表示行移動コマンドgj, gkを使用する。
- より押しやすい移動キーを覚える
- 行頭移動
0
より_
の方が押しやすい, 行末移動$
よりg_
の方が押しやすい - 移動後に挿入モードに移行する気があるなら、行頭に移動して挿入モード
I
, 行末に移動して挿入モードA
が早い
- 行頭移動
- f{char}(逆方向はT{char}), t{char}(逆方向はF{char})と;(逆方向は
,
)は他のエディタにはない横移動最強コマンド。- f{char}: 右に向かって [count] 番目に現れる {char} に移動。カーソルがその {char} 上に置かれる。
- f{char}と
;
で移動して、行き過ぎたら,
で戻す。- 例えば
fe
なんかは役に立ちづらい。なぜならe
という{char}は英文の中で最も頻出の{char}だから。 -
f
を使うときは英文の中で出現頻度の低い{大文字}{句読点}{カッコ}を頼りに移動するのが良い。
- 例えば
- t{char}: 右に向かって [count] 番目に現れる {char} に移動。カーソルがその {char} 左に置かれる。fと違うのは検索対象を移動先に含めるか否か。
- tが使用されるのは、以下の表に示すように、ドットまで削除したい時なんかは、
dfd
を覚えておくよりは、dt.
を覚えておいたほうが汎用的に使える。
キーストローク | バッファの内容 |
---|---|
{start} | [I]'ve been expecting you, Mister Bond. |
f, | I've been expecting you[,] Mister Bond. |
dfd | I've been expecting you[.] |
dt. | I've been expecting you[.] |
検索
- '/'よりは'*'
- '?'よりは'#'
- <C-A><C-X>は行内の数字を勝手に探して、インクリメント、デクリメントしてくれる。
テキストオブジェクト
- daw
- ciw
複数ファイルの管理
vimでは一度に複数のファイルを扱える。
vimの作業スペース
作業スペースは小さい方からbuffer < window < tabの順
buffer
- バッファは散らかった机、引数リストは整頓された作業スペースのイメージ
- :lsはバッファを見るためのvimから備わった機能
- :argsはex時代からある引数リストと言われる機能
- argsの引数にはshellコマンドが使える。バッククォート`を使おう。
- argsの引数には*(ファイル名)や**(ディレクトリ名)が使える。
- argsは自分の好きなように
-
:argdo
や:bufdo
する前にset hidden
設定しておくことで、バッファの保存をしなくてもバッファ移動ができる。 - <C-^>: で代替ファイル,さっき編集していたファイルへ移動。
- 移動は
:bn[ext]
(反対はbp[revious]
)- 以下のようにキーマップを変えるのも良い。
nnoremap <silent> [b :bprevious<CR>
nnoremap <silent> ]b :bnext<CR>
nnoremap <silent> [B :bfirst<CR>
nnoremap <silent> ]B :blast<CR>
-
:e %:h
- e: edit
- %: 現在アクティブなファイルのファイル名
- h: ファイル名を隠す
-
:!mkdir -p %:h
: 存在しないディレクトリにファイルを保存するコマンド -
w !sudo tee % > /dev/null
: bufferの内容をスーパーユーザーとして保存
" `:e %%`アクティブなファイルが含まれているディレクトリを手早く展開する
" :eだけでなく:wや:rでも使える。
cnoremap <expr> %% getcmdtype() == ':' ? expand('%:h').'/' : '%%'
window
- vim用語でウィンドウとは、バッファに対するビューポートのこと。
- 移動は<C-W> h[jkl]
- <C-W><C-L>: 二分割の場合、水平分割を垂直分割に置き換える
- <C-W><C-K>: 二分割の場合、垂直分割を水平分割に置き換える
- <C-W><C-O>: 現在のウィンドウのみにする(only)
- <C-W><C-C>: 現在のウィンドウを閉じる(close)
-
lcd
で作業スペースごとに現在のディレクトリを指定する。
tab
- タブはウィンドウのコレクションを格納するコンテナだと考える。
- 仕事をいくつかのワークスペースに区切ることができる。
- 急な案件や、全く別の仕事を始めたい時にタブを活用して全く別の作業空間を作り出す使い方をする。
- 多くのソフトウェアにおける、「新しいウィンドウ」のイメージ。「タブ」という名前だが、多くのソフトウェアのそれとは別物であることに注意。
-
:tabedit {filename}
<C-W> Tで新たにウィンドウを開く - 移動は
gt
(反対はgT) - <C-PageUp>(<C-PageDown>)でも切り替えられる。
:find
- ファイル名を指定してファイルをオープンする。
-
:edit
はパス指定。 -
:find
はファイルが何階層かにネストしている時にパスしていしなくても、ファイル名指定だけでオープンできる。
-
:set path+=~/path/to/somedirectory " path/to/somedirectoryをパスに追加
:find hoge.txt " hoge.txtを開く
-
set path+=app/**
のようにしてapp以下のサブディレクトリすべてを:find
検索対象にする。 set path+=app/**
- 詳細は
:h file-searching
netrw
- vimのネイティブなファイルエクスプローラー(NERDTreeに手を出す前に…)
- 最低限以下の設定は.vimrcに必要
set nocp " 'compatible' をオフにする
filetype plugin on " プラグインを有効にする
- netrw開き方:
- shell上でvimを開くときにファイルではなくディレクトリを指定する
- vim上で
:edit .
または:e.
-> 現在の作業ディレクトリでnetrwをオープン - vim上で
:Explorer
または:E
-> アクティブなバッファのディレクトリでnetrwをオープン
- 前のバッファに戻る<C-^>キーと組み合わせると、ファイル編集とディレクトリ移動が交互に行える。
- netrw開いている状態で次のキーを押す
-
-
上位のディレクトリへ移動 -
%
ファイルの新規作成 -
D
ファイル,ディレクトリの削除 -
R
ファイル,ディレクトリのリネーム -
p
プレビューウィンドウでファイルの中身を開く(ディレクトリはプレビューできない) -
ALT+O
水平分割ウィンドウで開く -
ALT+V
垂直分割ウィンドウで開く
-
- より詳細には
:h netrw
,:h netrw-ref
レジスタ
-
xp
: "Transpose the next two characters"(次の2文字を入れ替える)- ただの1文字切り取って貼り付け
-
ddp
: "Transpose the order of this line and its successor"(カーソル行と次の順番を入れ替える)- 行入れ替えを連続して行いたいならば、
qdddpq
(ddp
をdレジスタに登録する)のようにしてマクロを組み、@d
を繰り返す。 -
ddp
(現在行を切り取って下に貼り付ける) - ドットで繰り返すと、切り取った行を連続して貼り付けてしまう
-
@@
で最後に使ったマクロを繰り返すので、一度@d
を使ったら@@
で繰り返せるので、@キー押しっぱなしで良い - まとめると
qd ddp q @d @@ @@(移行@@の繰り返し)
- スペース入れているのは視認性良くするため。実際スペースは入力しない。
-
qd
: 移行のアクションをレジスタdに記録開始 -
ddp
: 現在行を切り取り、すぐ下の行に貼り付け(=「現在行と下の行の入れ替え」と同じ意味)
- 行入れ替えを連続して行いたいならば、
-
yyp
: 現在行を下にコピペ
レジスタの種類
- 無名レジスタ("")
- ヤンクレジスタ("0) <-オーではなくゼロ
- dコマンドでは上書きされない、明示的にヤンクしなければ"0レジスタは上書きされない。
- 名前付きレジスタ("a ~ "z)
-
"ad{motion}
: カット,"ay{motion}
: コピー,"ap
: 貼り付け aレジスタに登録 -
"a
のように小文字にすると名前付きレジスタに上書き -
"A
のように大文字にすると名前付きレジスタに追加で書き込み -
:reg a
でレジスタaの内容を表示する
-
- ブラックホールレジスタ("_)
- 消去専用レジスタ
- どこかにコピーすることなく削除する
- 無名レジスタ上書きを避ける
- システムクリップボードレジスタ("+)
- X11のクリップボード
:h quote+
- 選択範囲レジスタ("*)
- X11のプライマリといわれるクリップボード
virtual machine使っている俺にはカンケーねぇな。 クリップボード使いづらい
- Expressionレジスタ("=)
- 式を評価する。
"=5*4
でレジスタに20(5かける4の結果)が登録される。
- 式を評価する。
- 現在ファイルのファイル名("%)
- 代替ファイルのファイル名("#)
- 直前に挿入されたテキスト(".)
- 直前に実行されたExコマンド(":)
- 直前に検索された検索パターン("/)
挿入モードでレジスタ貼り付け
* <C-r>": 無名レジスタの内容を挿入モード中に貼り付けできる
* <C-r>0: ヤンク名レジスタの内容を挿入モード中に貼り付けできる
- p, P, gp, gP: 貼り付け後のカーソルの位置。これは言葉にするより体感して覚えたほうが良い。
マクロ
ドットコマンドは変更ワンアクションの繰り返しには使えるが、多くの変更を繰り返したい場合はマクロを使うべき。
マクロを使う前に、レジスタの使い方を覚えておいたほうが良い。なぜなら、マクロはレジスタに格納されるため。
マクロの公式は
q{レジスタ(a~z)}{アクション}q "マクロ記録
@{レジスタ(a~z)} "マクロ実行
最初のqはマクロ記録開始、最後のqはマクロ記録終了。
例えばレジスタaの内容を調べるときは:reg a
マクロの直列と並列
- 直列実行: ひとつのマクロ実行を行ってから、次のマクロ実行を行う。もし、マクロ実行に伴いエラーが生じたら、連続したマクロ実行は停止する。(安全装置的な意味で。)
- 並列実行: 複数箇所に一度にマクロを実行して、マクロの実行に伴いエラーが生じても、エラー箇所はそのまま放置、うまく行くところだけ処理する。
直列・並列はあくまで著者が説明しやすくするための比喩的表現であって、Vimが複数の変更を直列的、並列的に実行するわけではありません。
-
例えば以下のように途中でコメントが入った文を繰り返し修正したい時
1. one 2. two // break up the monotony 3. three 4. four
11@a
aレジスタを11回繰り返す
レジスタaの内容0f.r)w~j
1) One 2) Two // break up the monotony 3. three 4. four
変更が途中で止まってしまう。3行目で
f.
がエラーとなるため。ここでレジスタaの内容を
0f.r)w~
に変更した。(さっきのレジスタaに比べて、最後のjがないだけ)変更したい部分を
VG
で選択して
:normal @a
で各行に対してレジスタaを実行する1) One 2) Two // break up the monotony 3) Three 4) Four
今度は最後まで実行できた。
11a
としたときのように3行目でエラーとなるが、それとは独立に(=並列に)4,5行目が処理されるため。 -
結局直列、並列どちらが良いのか?それはケースバイケース。
- 直列だとエラー箇所が見える
- 並列だと堅牢性は高いが、失敗した部分が見えない
黄金律: マクロを記録するときには、すべてのコマンドは必ず繰り返し可能であるようにしよう
- カーソル位置の正規化
- マクロの最初のコマンドは、必ずマクロの開始位置への移動にすること
- 検索語への移動( /{char} n)
- 行頭へ移動(0)
- 先頭行に移動(GG)
- マクロの最初のコマンドは、必ずマクロの開始位置への移動にすること
- 繰り返し可能なモーションでターゲットを撃破しよう
- 単語単位のモーションw,b,e,geなど
- 特定文字の前に移動(f{char},t{char})など
- 当然、マウスは使うな
- 単語単位のモーションw,b,e,geなど
- マクロ実行時にモーションが失敗するとVimは残りのマクロの実行を中断する。これはバグではなくて仕様である。
- この機能のおかげで、マクロを何回実行するか数える必要はなく、十分量の回数指定をすれば繰り返されることはない。つまり、17行程度の文章に同じマクロを実行したいときは
100@a
でレジスタaの内容が17行全てに反映されて、18回目の実行で安全にマクロが停止する。
- この機能のおかげで、マクロを何回実行するか数える必要はなく、十分量の回数指定をすれば繰り返されることはない。つまり、17行程度の文章に同じマクロを実行したいときは
- 繰り返しをマクロ化
- 移動して変更...移動して変更...移動して変更...、移動の繰り返しは
;
キー, 変更の繰り返しは.
キー - つまり
;.
を繰り返す -
11:
で;.
が11回繰り返されるか?いいえ、;
による移動を11回繰り返した後に.
による変更が一回だけ実行されます。 - 上でやりたいことは
qq;.q
でqレジスタにマクロ記録し、11@q
とすれば実現できます。11回の移動;
して変更.
を繰り返します。
- 移動して変更...移動して変更...移動して変更...、移動の繰り返しは
マクロにコマンドを追記する
- レジスタaの内容に下行への移動
j
を追記したいとき、qA
としてレジスタ追記を指示して、jq
で移動を記録してマクロ記録を終了する。こうするとレジスタaにjが追記されている。 - 繰り返しになるがレジスタaの内容を見たいときは
:reg a
複数のファイルにマクロを適用する
引数リスト内のファイルに同じマクロを適用して同様の変更を複数ファイルに渡って行う。
- 並列に実行
-
args *.rb
などとして、変更したい特定のファイルを引数リストに格納
引数リストについては
:h args
-
:first
で引数リストの最初に戻っておく<-地味に重要 - 変更を与えるマクロをレジスタに記録して(ここでは仮にレジスタaに記録する)
- 加えた変更を一度
edit!
で変更前に戻す。
この後、現在バッファを含めたすべてのファイルに適用するので、変更を戻しておかないと最初のファイルだけ同じ変更が2回重なってしまう。
-
argdo normal @a
で並列にすべてのファイルにマクロを適用する。
-
- 直列に実行
- 先のレジスタaに
:next
を追加すれば良いqA:next<CR>
- 念の為
:e!
して:first
で戻って@11a
とすれば最後の引数リストまでレジスタaが実行される
- 先のレジスタaに
- すべて変更
-
argdo write
もしくはwall
-
argdo write
は引数リストすべての変更を保存する
wallは引数リストではなく、バッファリストすべてのファイルを保存するという違いがある
- 複数ファイルに渡る変更で並列で実行するときは相当自身を持って変更を行えるとき。なぜならば、失敗が起こった時メッセージは表示されず、エラー箇所はリストをブラウズして自分の目で探さなければならないため。
- その点直列実行はエラーが起きたファイルのその箇所で実行が止まってくれるので、エラー箇所の確認時間が必要ない。
- しかし、並列実行のほうが処理速度は早い可能性がある。(一方でエラーが見えなくなる)
続きます。