Vim-Galore : Vimについて知っておくべき全てのこと (2/5)

(訳注: 2016/2/26、記事タイトルを変更いたしました。)

vim-galore


特定のトピックについての記述をご希望ですか? Issueを立てるか、Twitterで私までお知らせください!ありがとう!


はじめに

基礎

使用方法

  • オフラインでのヘルプ
  • オフラインでのヘルプ(代替案)
  • オンラインでのヘルプ
  • クリップボード
    • クリップボードの使用法(Windows、OS X)
    • クリップボードの使用法(Linux、BSD他)
  • ファイルを開いたときのカーソルの位置を元に戻す
  • バックアップ/スワップ/アンドゥ/viminfoファイル
  • リモートファイルの編集
  • プラグインの管理
  • ブロックの挿入
  • 外部プログラムの実行とフィルタの使用
  • matchit

ヒント

  • 適切なnとNの振る舞い
  • 適切なコマンドライン履歴
  • 適切なCTRL-L
  • ビープ音とビジュアルベルを無効にする
  • 現在の行を動かす
  • 空行を追加する
  • マクロを編集する
  • ヘッダやソースファイルにジャンプする
  • GUIのフォントサイズを変更する
  • モードに応じてカーソルスタイルを変更する
  • セレクションを横に移動させた時に見失わない
  • 保存時にファイルをリロードする
  • スマートなカーソルライン
  • より速いキーワード補完

コマンド

  • :global – マッチする全ての行でコマンドを実行する
  • :normalと:execute – スクリプトの夢のチーム
  • :redir – メッセージのリダイレクト

デバッグ

  • 一般的なヒント
  • 起動時間のプロファイル
  • ランタイムでのプロファイル
  • verbose
  • Vimスクリプトのデバッグ
  • 構文ファイルのデバッグ

その他

  • その他の資料
  • Vimのディストリビューション
  • 標準プラグイン
  • CapsLockをCtrlにマップする
  • イースターエッグ
  • なぜナビゲーションはhjklなのか?

Quirks

  • 小さいファイルの編集が遅い
  • 大きいファイルの編集が遅い
  • NULに使われる改行
  • 括弧付きペースト(なぜいつも”paste”を設定しなくてはならないのか)
  • ターミナルでEscを使った時の遅延

カラースキームのリスト

プラグインのリスト

Neovim


マーク機能

ファイルの中の位置、つまり行番号と列番号を記憶するためにマークを使用します。

マーク 設定者 用途
az ユーザ ファイル内でローカル。つまり、ファイル1つに対してのみ有効。小文字のマークへのジャンプは、現在のファイル内へのジャンプを意味する。
AZ ユーザ グローバル。つまり、ファイル間で有効。ファイルマークとも呼ばれる。ファイルマークへのジャンプは、別のバッファに切り替えることになる。
09 viminfo 0は、viminfoファイルが最後に書かれた時の位置。つまり、Vimプロセスが終わった時の位置。 1は最後から2番目のVimプロセスが終わった時の位置ということになる。これ以降も同様。

モーションを記述するには'/g'もしくは`/g` をマークの前に付けます。

mmを使えば、現在の位置をマーク”m”で記憶できます。ファイルの中であちこち移動したあとでも、'm(空白ではない最初のところ)もしくは`m(その列)を使えばジャンプして元の位置に戻ることができます。Viminfoファイルに指定しておけば、Vimを終了したあとも小文字のマークは記憶されたままになります。`:h viminfo-'を見てください。

mMを使えば、ファイルマーク”M”で現在の位置が記憶できます。'Mもしくは`Mを通して別のバッファに移ったり、元のものに戻ったりできます。

他のモーションは以下のとおりです。

モーション ジャンプ先
'[, `[ 直前に変更またはヤンクされたテキストの最初の行、もしくは文字。
'], `] 直前に変更またはヤンクされたテキストの最後の行、もしくは文字。
'<, `< 最後にビジュアル選択された部分の初めの行、もしくは文字。
'>, `> 最後にビジュアル選択された部分の終わりの行、もしくは文字。
'', `` 最新のジャンプの前の位置。
'", `" 現在のバッファが最後に終了した時の位置。
'^, `^ 最後に行った挿入を終えた位置。
'., `. 最後の変更が加えられた位置。
'(, `( 現在の文章の始まり。
'), `) 現在の文章の終わり。
'{, `{ 現在のパラグラフの始まり。
'}, `} 現在のパラグラフの終わり。

マークは範囲でも使うことができます。おそらく見かけたことがあって、どんな意味だろうと疑問に思ったことのある人が多いのではないでしょうか。ビジュアルモードでテキスト選択して:を実行すると、:'<,'>がコマンドラインにプリペンドされます。つまり、ビジュアル選択された部分を示す範囲(レンジ)が、次のコマンドに与えられます。

:marksを使用すれば、全てのマークがリスト化できます。全てを:h mark-motionsで読み込んでください。

補完

Vimは様々な種類の挿入モード補完を提供します。もし複数の一致候補があれば、ポップアップメニューが表示され、選択するよう誘導してくれます。

一般的に補完の対象となるものは、タグ、インポートしたモジュール/ライブラリの関数、ファイル名、ディレクトリ名、同じバッファに存在する単語などです。

Vimは補完のそれぞれの種類のマッピングを提供しています。全てが、<c-x>で始まっています(挿入モードでこれらを使うのを忘れないでください)。

マッピング 種類 関連するヘルプ
<c-x><c-l> 全ての行 :h i^x^l
<c-x><c-n> 現在のファイルのキーワード :h i^x^n
<c-x><c-k> 'dictionary'オプションからのキーワード :h i^x^k
<c-x><c-t> 'thesaurus'オプションからのキーワード :h i^x^t
<c-x><c-i> 現在のファイルと、インクルードされているファイルからのキーワード :h i^x^i
<c-x><c-]> タグ :h i^x^]
<c-x><c-f> ファイルネーム :h i^x^f
<c-x><c-d> 定義とマクロ :h i^x^d
<c-x><c-v> Vimコマンド :h i^x^v
<c-x><c-u> ユーザ定義('completefunc'で特定される) :h i^x^u
<c-x><c-o> オムニ補完 ('omnifunc'で特定される) :h i^x^o
<c-x>s スペル提案 :h i^Xs

ユーザ定義補完とオムニ補完の違いが分からない人もいるでしょうが、技術的にはこの2つは同じことをします。両方とも、現在の位置を調べて、提案のリストを返すという関数を使います。ユーザ定義補完は、ユーザの個人的な目的のためにユーザによって定義される補完のことです。(サプライズ!)これは何にでも使えますね。オムニ補完はファイルタイプ固有の目的で、構造体メンバやクラスメソッドを補完したりします。そして、しばしばファイルタイププラグインで設定されます。

また、Vimは'complete'オプションを設定することで、複数の種類を一度に補完することもできます。デフォルトのままではオプションの種類が多いので、自分の好みに合わせて減らすのを忘れないでください。<c-n>(次)か、<c-p>(前)のどちらかを使用することで、この補完をトリガさせることができます。そしてこれは、ポップアップメニューでエントリを選択するときに使うキーでもあります。他の例は、:h i^n:h 'complete'を見てください。

ポップアップメニューの動作を設定するには、:h 'completeopt'を確認しましょう。デフォルトはまあまあマトモですが、私は”noselect”を追加するほうが好きです。

関連するヘルプ:

:h ins-completion
:h popupmenu-keys
:h new-omni-completion

モーション/オペレータ/テキストオブジェクト

モーションはカーソルを動かします。h/j/k/l/のことは分かりますよね。もしくはw/b。さらに、/もモーションです。また、回数を使うこともできます。2?the<cr>は、最後から2番目に使われた”the”にジャンプすることを意味します。

:h navigationと下記の記述をみて、使用できるモーションを確認してみてください。

オペレータは、ある範囲のテキストに対して動作します。例えば、d~gU>などです。他にもたくさんあります。これらは、ノーマルモードかビジュアルモードでしか使えません。ノーマルモードでは、オペレータはモーションのすぐあとにきます。例えば>jなどです。ビジュアルモードでは、オペレータは単純に、選択範囲上で機能します。例えばVjdです。

モーションと同様、オペレータも数を使うことができます。例えば2gUwは、現在の単語と2番目に出てくる単語を大文字にするという意味です。モーションとオペレータは両方とも数を数えられるので、2gU2wも機能し、gU2wを2回繰り返すことができます。

:h operatorを見て、使用できるオペレーションを確認してみてください。~をオペレータとして機能させるには:set tildeopを使用してください。

テキストオブジェクトは、モーションが1つの方向にしか機能しないのに対して、多方面のエリアで機能します。実際に、単語全体、文章全体、カッコ間の全てなどのオブジェクト上で機能します。

テキストオブジェクトは、ノーマルモードではカーソルを動かすのには使えません。なぜなら、どんなに優秀なカーソルでも2カ所に同時に飛ぶことはできないからです。しかし、ビジュアルモードでは使うことができます。なぜなら、オブジェクトの一方は、すでに選択され、カーソルはもう一方のほうへジャンプすればいいからです。

テキストオブジェクトは、オブジェクトを意味する文字に続いて、i(innerの頭文字)もしくはa(aroundの頭文字)のどちらかから始めます。iであれば、そのオブジェクトだけに機能し、aであればそのオブジェクトと続く空白にも機能します。例えば、diwであれば現在の単語だけを削除し、ci(であれば、カッコ内の全てを変更します。

テキストオブジェクトも数を数えることができます。例えば、((( )))で、カーソルが一番内側のカッコ上もしくはそのカッコの間にあるとすると、d2a(は内側の2組のカッコと、その間にある全てを削除します。

:h text-objectsを見て、使用できるテキストオブジェクトを確認してみてください。

Autocmd

多くの場合、Vimはイベントを発行します。イベントに入るにはautocmdを使用します。

もしautocmdがなかったら、Vimを使うことはないでしょう。ユーザが気付かなくても、autocmdは常時使用されています。信じられませんか? では、:auとタイプしてみてください。出力を見て圧倒されないでくださいね。これは全部、現時点で有効になっているautcmdです。

利用可能な全イベントの概要を簡単に見るには、:h {event}とタイプし、詳細を見るには、:h autocmd-events-abcとタイプします。

典型例として、ファイルタイプ特定の設定を行う例を挙げます。

autocmd FileType ruby setlocal shiftwidth=2 softtabstop=2 comments-=:#

でも、どうしてバッファには、ここにRubyのコードが含まれていることが分かるのでしょうか? それは、別のautocmdがそれを検出し、それに従ってファイルタイプを設定し、その設定がさらにFileTypeイベントをトリガしたからです。

誰もが最初にvimrcに追加するものの1つは、filetype onです。これは単に、起動時にfiletype.vimが読み込まれ、世の中のほぼ全てのファイルタイプにautocmdを設定することを意味します。

勇気があるなら、:e $VIMRUNTIME/filetype.vimを見てみましょう。文字列”Ruby”が見つかれば、Vimが単にファイル拡張子.rbを使ってRubyファイルを探していることが分かります。

注:同じイベントのautocmdは、作成された順序で実行されます。:auで、正確な順序が表示されます。

au BufNewFile,BufRead *.rb,*.rbw  setf ruby

BufNewFileイベントとBufReadイベントは、この場合、VimのCソースにハードコードされていて、:eやそれに似たコマンドを通してファイルを開くたびに発行されます。その後、filetype.vimから何百ものファイルタイプが全てテストされます。

簡単に言えば、Vimはイベントとautcmdを多用しますが、そのイベントドリブン・システムに入ってカスタマイズするための簡潔なインターフェイスを開示する、ということです。

チェンジリストとジャンプリスト

最近の100回の変更の位置がチェンジリストに保持されます。同じ行内の小さな変更は1つにまとめられますが、チェンジリストに保持される位置は最後に加えた変更の位置になります(行の途中に何かを追加した場合)。

ジャンプを行うと、ジャンプする前の位置がジャンプリストに記憶されます。ジャンプリストには100件までのエントリがあります。各ウィンドウについて、それぞれのジャンプリストがあります。ウィンドウを分割すると、ジャンプリストがコピーされます。

ジャンプは、'`G/?nN%()[[]]{}:s:tagLMHのいずれかのコマンドか、新しいファイルの編集を始めるコマンドです。

リスト 全エントリのリスト表示 戻る 進む
ジャンプリスト :jumps [count]<c-o> [count]<c-i>
チェンジリスト :changes [count]g; [count]g,

全てのエントリのリストを表示すると、現在の位置がマーカ>を使って示されます。通常は、最新の位置である第1の位置の下に示されます。

Vimを再起動した後も両方のリストを残しておきたい場合は、viminfoファイルと:h viminfo-'を使用する必要があります。

注:最後にジャンプする前の位置もマークとして保持され、``または''を使ってそこにジャンプすることができます。

関連するヘルプ:

:h changelist
:h jumplist

Undoツリー

テキストの状態に加えた最新の変更が記憶されます。変更を元に戻すにはundoを使い、戻した変更をやり直すにはredoを使います。

重要なことは、最近の変更を保持するデータ構造はキューではなく、ツリー(木構造)だということです。変更はツリー内のノードであり、(トップノード以外の)ノードにはそれぞれ親ノードがあります。各ノードは変更されたテキストと時刻に関する情報を保持します。枝は、任意のノードから始まってトップノードにまでさかのぼる一連のノードです。変更をundoしてから他のものを挿入すると、新しい枝が生成されます。

ifoo<esc>
obar<esc>
obaz<esc>
u
oquux<esc>

現在、3本の線があって、ツリーは次のようになります。

     foo(1)
       /
    bar(2)
   /      \
baz(3)   quux(4)

undoツリーには、4つの変更が保持されています。数字は、ノードが作成された時刻を表します。

このツリーを辿るには、2つの経路があります。それぞれ、枝方向時間方向と呼びましょう。

Undo(u)とredo(<c-r>)は、枝方向に働いて、現在の枝を上り下りします。uは、テキスト状態をノード”bar”に戻します。もう一度uを行うと、テキスト状態はさらにノード”foo”に戻されます。次に<c-r>でノード”bar”の状態に逆戻りして、その次の<c-r>ではノード”quux”の状態になります(枝方向のコマンドを使って、ノード”baz”には行けません)。

これとは異なり、g-g+は時間方向に働きます。よって、g-
ではuのようにノード”bar”の状態には戻らず、時系列的に直前の状態であるノード”baz”に戻ります。もう一度g-を行うと、状態はノード”bar”に戻り、以下同様に続きます。このように、g-g+は単純に時間の中で進んだり戻ったりします。

コマンド/マッピング 動作
[count]u, :undo [count] [count]回の変更をundoする。
[count]<c-r>, :redo [count]回の変更をredoする。
U 最後に変更した行の中の全ての変更をundoする。
[count]g-, :earlier [count]? 以前のテキスト状態に[count]回戻る。”?”は、”s”、”m”、”h”、”d”、”f”のいずれかを指定できる。例えば、:earlier 2dは、2日前からのテキスト状態に進む。:earlier 1fは、最後にファイル保存したときの状態に進む。
[count]g+, :later [count]? 上と同じで、逆方向。

undoツリーはメモリ内に保持され、Vimの終了時に消去されます。永続的なundoを可能にする方法については、Handling backup, swap, undo, and viminfo filesを参照してください。

undoツリーが分からなくなったときには、ツリーを視覚化するためにundotreeが役立ちます。

関連するヘルプ:

:h undo.txt
:h usr_32

quickfixリストとlocationリスト

アクションが位置のリストを返す必要がある時には、quickfixリストまたはlocationリストがいつも使用できます。この場合、location(位置)は、ファイル、行番号、列番号(オプション)です。

例としては、コンパイラエラーをquickfixリストにまとめたり、外部grepツールの一致結果をlocationリストにまとめたりすることなどがあります。

この内容を空白のバッファに入れるだけで、エントリを閲覧するための優れた統一インターフェースが得られるという、大きな利点があります。

quickfixリストは常に1つしかありませんが、各ウィンドウにはそれぞれ専用のlocationリストがあります。どちらのタイプのリストも同じような感じですが、ナビゲーションに使うコマンドは少し異なります。

よく使うコマンド:

動作 Quickfix Location
ウィンドウを開く :copen :lopen
ウィンドウを閉じる :cclose :lclose
次のエントリ :cnext :lnext
前のエントリ :cprevious :lprevious
最初のエントリ :cfirst :lfirst
最後のエントリ :clast :llast

この他、全てのコマンドについては、:ccを参照してください。

昔ながらのgrepを使って、現在のディレクトリ内のファイルで、あるクエリを再帰的に検索し、結果をquickfixファイルに書き込みましょう。

:let &grepprg = 'grep -Rn $* .'
:grep! foo
<grep output - hit enter>
:copen

文字列”foo”を含むファイルがあったとすれば、今、そのファイルがウィンドウに表示されているはずです。

マクロ

Vimでは、タイプ入力された文字をレジスタ記録することができます。ある作業を素早く自動的に行うための素晴らしいツールです(手の込んだ作業には、Vim scriptingを使うべきです)。

  • qの後に、例えばqなどレジスタを指定して、記録を開始します(コマンドラインは、”recording @q”を使ってこれを表します)。
  • もう一度qとタイプすると、記録が終了します。
  • マクロを実行するには、[count]@qを使います。
  • 最後に使ったマクロを繰り返すには、[count]@@を使います。

例1

1行挿入して、それを10回繰り返すマクロ。

qq
iabc<cr><esc>
q
10@q

(マクロを使わずに同じことができます:oabc<esc>10

例2

全ての行の前に行番号を追加します。まず、最初の行に手入力で”1.”を追加します。<c-a>を使用して、^Aと表示されるカーソルの下の番号をインクリメントします。

qq
0yf jP0^A
q
1000@q

1000@qを使う場合は、ファイル内の行数が1000行を超えないことを願うだけですが、再帰マクロを使えば、マクロが適用できる行がなくなるまでマクロが実行されます。

qq
0yf jP0^A@q
q
@q

(マクロを使わずに同じことができます::%s/^/\=line('.') . '. ')

マクロを使わずに同じことをする方法をご紹介しましたが、ほとんどの場合、マクロを使わない方法は簡単な例でしか機能しません。複雑な自動化には、マクロが非常に効果的です。

併せてQuickly edit your macrosも参照してください。

関連するヘルプ:

:h recording
:h 'lazyredraw'

カラースキーム

カラースキームは、Vimのスタイルを設定する方法です。Vimには多くの構成要素があり、それぞれを別々の前景色と背景色、太字などのその他の属性を使ってカスタマイズすることができます。カラースキームは次のように設定することができます。

:highlight Normal ctermbg=1 guibg=red

こうすると、エディタの背景が赤色になります。詳細については、:h :highlightを参照してください。

つまり、カラースキームは、主として:highlightコマンドの集合です。

実際には、ほとんどのカラースキームは2つのカラースキームなのです。上の例は、ctermbgguibgを使って色を設定します。前者の定義は、Vimがxtermなどの端末エミュレータ内で起動された場合にのみ使用されます。後者は、gVimのようなグラフィカル環境で使用されます。

あるカラースキームを端末エミュレータ内で実行中のVimで使ったらスクリーンショットと同じ色に見えないという場合は、そのカラースキームはGUI向けの色しか定義していない可能性があります。

私は、GUIにはgruvbox、端末にはjanahを使っています。

その他のカラースキーム

折り畳み

どんなテキストにも(あるいはソースコードにも)何らかの構造があります。構造があるということは、テキストが論理的にいくつかの範囲に分割されているということです。折り畳みの機能を使えば、分割されたテキストの1つの範囲を「折り畳んで」1行にして、短い説明を表示させることができます。このような折り畳みと呼ばれる行範囲で使えるコマンドはたくさんあります。折り畳みは入れ子にできます。

Vimの折り畳みの方法はいくつかに区別されます。

‘foldmethod’ 使い方
diff diffウィンドウ内で使用して、未変更のテキストを折り畳む
expr foldexprを使って、要するに折り畳みのメソッドを新規に作成します
indent インデントの数で折り畳みのレベル(深さ)を決めて折り畳みます
manual zfzF:foldを使って手動で折り畳みます
marker テキスト(多くはコメント)内のマーカーに基づいて折り畳みます
syntax 構文により折り畳みます 例:if ブロックの折り畳み

注:折り畳みの機能はリソースを大量に使います! もしパフォーマンスの低下(タイピング時の遅延)があれば、FastFoldのページを参照して、Vimが折り畳みを不要時にアップデートしないようにしてください。

関連するヘルプ:

:h usr_28
:h folds

セッション

ビュー:h :mkview)を保存すると、ウィンドウの設定(とそれに関連したオプションやマッピング)が保存されるので、後で復元して作業を再開することことができます。(:h :loadview

セッションは全てのウィンドウのビューとグローバルな設定を保存します。基本的にはVimのインスタンスのスナップショットが作成され、セッションファイルに保存されます。

これでプロジェクトを完全に保存できますし、プロジェクト間の切り替えも簡単に行えます。

それでは試してみましょう! ウインドウとタブをいくつか開いて、:mksession! Foo.vimを実行してください。ファイル名を省略した場合は、Session.vimに保存されます。ファイルはカレントディレクトリに保存されます。カレントディレクトリは:pwdで確認してください。Vimを再起動して:source Foo.vimを実行したら、ほらこの通り、バッファリスト、ウィンドウのレイアウト、マッピング、作業ディレクトリ他が全てセッション保存前と同じ状態です。

ただ、セッションファイルはVimコマンドの集合体に過ぎない、ということは気に留めて、いつでも:vs Foo.vimを見てください。

'sessionoptions'を設定すれば、セッションのどの部分を保存するかVimに指定できます。

関連するヘルプ:

:h Session
:h 'sessionoptions'

局所性

ここまでに挙げたグローバルな概念の多くには、相対するローカルな概念があります。

グローバル ローカル スコープ ヘルプ
:set :setlocal バッファ または 
ウィンドウ
:h local-options
:map :map <buffer> バッファ :h :map-local
:autocmd :autocmd * <buffer> バッファ :h autocmd-buflocal
:cd :lcd ウィンドウ :h :lcd
<leader> <localleader> バッファ :h maplocalleader

また、変数にはいろいろなスコープがありますが、Vim scriptingの中に説明があります。