Vim-Galore : Vimについて知っておくべき全てのこと (1/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


はじめに

Vimとは何か?

Vimは1991年にブラム・ムーリナーがリリースしたテキストエディタで、その祖先はQEDまでさかのぼります。

vim.orgで、オンラインでホストされています。

Vimを入手するには、お好みのパッケージマネージャを利用するか、vim.orgのダウンロードページを訪問してください。

ディスカッションやユーザの質問はvim_useというメーリングリスト、または#vimチャネルのIRC(Freenode)で行うのが一番いいでしょう。

Github上で開発されていて、ディスカッションはvim_devメーリングリストで行われています。

なぜ頭のおかしい人はviを使うのかを読めば、Vimに対する一般的な勘違いについてお分かりいただけると思います。

Vimの基本原則

Vimはモーダルエディタの原則を順守しています。つまり、複数のモードが提供されていて、モードによってキーの意味が変化します。ノーマルモードでファイル操作を行い、入力モードでテキスト入力し、ビジュアルモードで行を選択し、コマンドラインモードでコマンドにアクセスする、といった具合です。最初は複雑に感じるかもしれませんが、これには非常に大きなメリットがあり、一度に何個ものキーを押さえて指を痛める必要がありません。ほとんどの場合1つずつ押せばいいのです。一般的なタスクになるほど、少ないキーで対応できます。

他にモーダルエディタで便利なのは、似たようなコンセプトですが、オペレータとモーションです。オペレータは特定のアクションを起動します。例えば変更、削除、またはテキストの選択などです。その後モーションを使って、アクティブにしたいテキストの領域を特定します。括弧に囲まれた部分を変更するにはci(チェンジ・インナー・パーレンと読む)を使います。テキストの全パラグラフを削除するにはdapデリート・アラウンド・パラグラフと読む)を使います。

ベテランのVimユーザを見れば、まるでピアニストがピアノを自在に弾くかのように、Vim言語を操っているのに気付くでしょう。

複雑な処理でも数個のキーの組み合わせだけで実行できます。これらの動作はすでにマッスルメモリ化しているため、考える必要もありません。認知的負荷を減らし、実際のタスクに集中できます。

最初の一歩

Vimには対話式のチュートリアルが付属していて、知っておくべき基本的なことについてはほぼ全て学ぶことができます。これはシェルから起動します。

$ vimtutor

退屈に思えても、先延ばしにせずに演習をしてみてください。IDEはほとんど全てがモーダルでないはずなので、最初はモードを切り替えながら作業するのを面倒に感じるかもしれません。しかしVimを使うにつれ、どんどんマッスルメモリ化していきます。

Vimは、viのクローンであるStevieをベースにしていて、2つの処理モード、「互換」と「非互換」をサポートしています。互換モードでVimを使うと、全てのオプションがviのデフォルトの状態で使われることになり、Vimのデフォルトで使うのとは異なります。vimrcをまだ作成していない場合や、Vimをvim -Nで起動した場合は、互換モードと見なされます。決して互換モードでVimを使わないようにしましょう。

次のステップは以下の通りです。

  1. 独自のvimrcを作成します。
  2. 最初の数週間のためにチートシートを用意します。
  3. 基礎の項を読んで、何ができるのか学んでください。
  4. 必要に応じて学んでください。Vimの勉強にはキリがありません。問題に突き当たった時は、インターネットで情報を探せばきっと解決法が見つかるはずです。Vimには素晴らしいマニュアルが付いてきますから、操作方法を学んでください。オフラインでのヘルプ
  5. その他の資料をのぞいてみてください。

最後のアドバイスとしては、様々な過剰なプラグインを追加する前にVimの適切な使い方をまず学ぶことです。Vimがネイティブにサポートしている機能を追加するだけだった、ということにならないように。

ミニマルなvimrc

ユーザのvimrcは~/.vimrcに設定するか、よりきちんと分けるためには、~/.vim/vimrcに設定します。後者のほうがバージョン管理をしながら全コンフィギュレーションを設定することが簡単ですし、例えばGithubにアップロードするのも楽です。ネット上のあちこちで「ミニマルなvimrc」を見つけられるはずです。私のバージョンはそこまでミニマルではありませんが、かなり良い一揃いの設定を提供しているので、最初に使うのには便利だと思います。

最終的には全ての設定を読んで、自分自身にとってベストなものを決定しなくてはなりません。

ここにミニマルvimrcがあります。

もし興味があれば、私のvimrcはここにあります。

ヒント:多くのプラグインの著者はいくつかのプラグインを保守していますし、よくvimrcもGithub上で公開しています(”vim-config”や”dotfiles”と呼ばれるレポジトリによくあります)。気に入ったプラグインを見つけたら、管理者のGithubページに行き、レポジトリを探してみてください。

自分の実行しているVimの種類を知る

:versionを見れば、現在実行されているVimのバイナリがどのようにコンパイルされたか情報を知ることができます。

最初の行ではそのバイナリがいつコンパイルされたか、そして例えば7.4など、バージョンを知ることができます。次の数行のうちの1行はIncluded patches: 1-1051、つまりパッチレベルを宣言しています。すなわちこの場合のVimのバージョンは7.4.1051です。

他の行にはTiny version without GUIや、Huge version with GUIなどと宣言されています。ここから明確になる情報は、あなたのVimがGUIをサポートしているかどうかです。つまり、シェルからgvimで起動しているのか、ターミナルエミュレータ内でVimから:guiを実行しているのかということです。他の重要な情報は、TinyHugeです。Vimはtinysmallnormalbigそしてhugeという機能セットを区別していて、それぞれに違う機能のサブセットを有効にするのです。

:versionのアウトプットの大半は機能リストそのものに費されます。+clipboardは、クリップボード機能がコンパイル時に組み込まれているということを、-clipboardではコンパイル時に組み込まれていないということを意味します。

Vimのいくつかの機能を正しく機能させるためにはコンパイル時に組み込む必要があります。例えば、:profを機能させるためには、+profile機能を有効にするhuge版の機能セットのVimが必要になります。

もしそうなっておらず、あなたがパッケージマネージャでVimをインストールした場合は、vim-xvim-x11vim-gtkvim-gnomeや類似のパッケージをインストールするようにしてください。なぜなら、これらのパッケージは通常huge版の機能セットについてくるからです。

バージョンや機能をプログラム的にテストすることも可能です。

" Do something if running at least Vim 7.4.42 with +profile enabled.
if (v:version > 704 || v:version == 704 && has('patch42')) && has('profile')
  " do stuff
endif

関連するヘルプ:

:h :version
:h feature-list
:h +feature-list
:h has-patch

チートシート

著作権の問題を回避するため、外部URLだけにリンクすることにします。

基礎

バッファ/ウィンドウ/タブ

Vimはテキストエディタです。表示されているテキストは全て、バッファの一部です。各ファイルはそれぞれのバッファ内でオープンされます。プラグインの内容は、それぞれのバッファなどで表示されます。

バッファは多くの属性を持ちます。属性の例としては、バッファに含まれているテキストは書き換え可能か、そのバッファは特定のファイルと連係しているかどうか、従ってディスクに書き込んで保存する際に同期する必要があるかどうか、などがあります。

ウィンドウはバッファの中身を見るためののぞき窓のようなものです。複数のファイルの内容を同時に表示したい場合や、1つのファイルの複数箇所を参照したい場合は、ウィンドウを使います。

注意が必要なのは、ウィンドウをスプリットと混同してはならない点です。ウィンドウを2つに分割することはできますが、分割後の各部分はスプリットではありません。

ウィンドウは垂直方向にも水平方向にも分割することができます。分割した場合、既存ウィンドウのサイズも変更することができます。従って、好みに合わせて、レイアウトを柔軟に変更することができます。

タブページ(単に「タブ」と呼ばれることもある)は、ウィンドウのコレクションです。従って、複数のウィンドウを並べて表示したい場合は、タブを使います。

要点をまとめると、引数を指定してVimを起動した場合、タブページが1枚表示され、そこには1つのバッファを表示するウィンドウが1個含まれます。

一方、バッファリストはグローバルで、どのタブからでも任意のバッファにアクセスすることができます。

アクティブ/読み込み済み/一覧表示/名前付きバッファ

Vimを起動するには、例えばvim file1のように入力します。ファイルの内容はバッファに読み込まれます。この状態のバッファを読み込み済みバッファといいます。バッファの内容はVim上で保存された場合にのみ、ディスクと同期されます(ファイルに書き戻されます)。

バッファはウィンドウにも表示されますが、この場合はアクティブバッファと呼びます。ここで、仮に:e file2を実行して別のファイルを読み込んだとすると、先に読み込んだfile1隠しバッファとなり、file2がアクティブになります。

バッファは両方とも一覧表示されるので、:lsを実行した時の出力にも表示されます。プラグインバッファ、またはヘルプバッファは一覧には非表示としてマークされます。これはテキストエディタで日頃編集している、通常のファイルではないからです。一覧に表示されるバッファも表示されないバッファも、:ls!を実行すると表示されます。

無名バッファは、プラグインが時折利用するもので、連係するファイル名を特に持たないバッファです。例えば:enewを実行すると、空の無名バッファが新たに作成されます。何かテキストを入力し、:w /tmp/fooを実行してそのテキストをディスクに書き込むと、そのバッファは名前付きバッファとなります。

引数リスト

グローバルのバッファリストはVimの特徴的な機能です。Vimの前身であるviでは、引数リストしか使えませんでした。なお引数リストはVimでも使えます。

Vimでは、ファイル名は全てシェルのコマンドライン上で割り当てますが、これが引数リストの中で記憶されます。引数リストは複数保持することができます。デフォルトでは、全ての引数がグローバルの引数リストに置かれます。ただし:arglocalを使えば、そのウィンドウだけで有効な、新しい引数リストを作成することができます。

現在の引数リストを表示させるには:argsを使います。引数リストの表示からファイルの表示に切り替えるには、:next:previous:first:lastなどを使います。これらを:argadd:argdelete:argsのいずれかで置き換えて、ファイル名も追加してください。

ファイルとのやりとりに使う際に、バッファリストと引数リストのどちらが適しているかは、好みの問題です。個人的には、バッファリスト派が多い印象があります。

しかしながら引数リストのユースケースも膨大な数に上ります。例えば:argdo!を経由したバッチ処理があります。ここでは単純なリファクタリングの例を挙げます。

:args **/*.[ch]
:argdo %s/foo/bar/ge | update

この例では、現在のディレクトリ以下に含まれている全てのC言語のソースファイルとヘッダファイルについて、文字列「foo」を「bar」で置き換えています。

関連するヘルプ: :h argument-list

マッピング

:mapファミリーのコマンドで、ユーザはそれぞれ独自のマッピングを設定することができます。ファミリー内の各コマンドは、特定のモードのマッピングを定義する際に使います。厳密に言えば、Vimには12種ものモードがあり、そのうち6種に対してマッピングを設定できます。さらに、コマンドの中には同時に複数のモードで動作するものもあります。

再帰 非再帰 マッピングの取り消し モード
:map :noremap :unmap ノーマル、ビジュアル、オペレータ待機
:nmap :nnoremap :nunmap ノーマル
:xmap :xnoremap :xunmap ビジュアル
:cmap :cnoremap :cunmap コマンドライン
:omap :onoremap :ounmap オペレータ待機
:imap :inoremap :iunmap 挿入

例: ここではマッピングをノーマルモードのみとすることを定義しています。

:nmap <space> :echo "foo"<cr> 

 :nunmap <space>を使うと、再びマッピングを取り消せます。

上記以外に、ごくたまにしか使われないモードがあと数種類ありますが、それらの詳細は:h map-modesを実行して確認してください。

ここまではまずまず順調ですね。しかし1つだけ、初心者がかなりつまずきやすい問題があります。:nmap再帰的です。つまり、右側は別のマッピングを実行していることを考慮しなければなりません。

そこで、単純に「Foo」をマッピングする操作を定義するには、次のように入力します。

:nmap b :echo "Foo"<cr>

では、b(1ワード戻る)のデフォルトの処理を別のキーに割り当てたい時にはどうすればいいのでしょうか。

:nmap a b

「a」のキーを打って、カーソルが1ワード分後戻りするだろうと思っているところで、コマンドラインに「Foo」と表示されてしまうのです。なぜなら右側の文字bは、既に別のアクション、具体的に言えば:echo "Foo"<cr>にマッピングされているからです。

この問題の適切な解決法は、再帰的ではなく非再帰のマッピングを使うことです。

:nnoremap a b

大原則として、どうしても再帰的なマッピングが必要な場合を除き、常に非再帰のマッピングを使うことです。

マッピングを確認する時は、右側を指定するのを避けましょう。例えば、:nmapと入力するとノーマルのマッピングが全て表示され、:nmap <leader>と入力すると、マップリーダーで起動されるノーマルのマッピングが全て表示されます。

通常のマッピングを無効にしたい場合は、マッピングを全て特殊な文字、<nop>に設定します。例えば次のコマンドを実行します:noremap <left> <nop>

関連するヘルプ:

:h key-notation
:h mapping
:h 05.3

マップリーダー

マップリーダーとは、カスタムマッピングに使う、単純なプレースホルダで、デフォルトでは\に設定されています。

nnoremap <leader>h :helpgrep<space>

このマッピングは\hによってトリガされます。<space>hを使いたい場合、代わりに以下のように記述します。

let mapleader = ' ' 
nnoremap <leader>h :helpgrep<space>

さらに、ローカルで<leader>と同様の役割を果たす存在として<localleader>があります。これは例えばあるファイル型固有のプラグインなど、ローカルからバッファへのマッピングに利用できます。これもデフォルトでは\に割り当てられています。

注: マップリーダーはマッピングを実行する前に設定しましょう。既に利用しているマップリーダーのマッピング設定が全て有効になる点は、マップリーダーが変更されても、変わりません。:nmap <leader>を実行すると、ノーマルモードでマップリーダーが既に解決済みのマッピングが全て表示されるので、念のためあなた自身のマッピングと突き合わせて確認してください。

詳細は:h mapleader、または:h maplocalleaderと入力して、表示されるテキストを参照してください。

レジスタ

レジスタとは、テキストを保存するスロットです。Vimではレジスタからテキストをコピーする処理を「ヤンク」、レジスタからテキストを抽出する処理を「ペースト」といいます。

vimで提供しているレジスタは以下の通りです。

種類 文字 埋め文字 読み取り専用か否か テキストの送信元
無名 " vim [ ] 最後のヤンクまたは削除(d, c, s, x, y)(d, c, s, x, y)
番号付き 0 to 9 vim [ ] レジスタ 0: 最後のヤンク。レジスタ 1: 最後の削除。 レジスタ 2: 最後の1つ前の削除。以下同様。レジスタ 19 は、要素が9個の読み取り専用キューと考えること。
小削除 - vim [ ] 最後の1行未満の削除
名前付き az, AZ user [ ] レジスタaへのヤンクを実行すると、テキストはレジスタの内容で置き換えられる。レジスタAへのヤンクを実行すると、レジスタaに格納されているテキストにレジスタAの内容を追加する。
読み取り専用 :, ., % vim [x] :: 直近に実行したコマンド、 .: 直近に挿入したテキスト、 %: 現在のファイル名
代替バッファ # vim [ ] ほとんどの場合、現在のウィンドウ内で直近にアクセスしたバッファが代替バッファとなります。詳細は:h alternate-fileと入力して、表示されるテキストを参照してください。
= user [ ] ヤンクされたVimL式の評価です。例えば、挿入モードで<c-r>=5+5<cr>と入力すると、バッファに「10」が挿入されます。
選択 + vim [ ] +クリップボード のレジスタです。
ドロップ ~ vim [x] 最後のドラッグアンドドロップです。
ブラックホール _ vim [ ] 他のレジスタに暗黙的に影響を与えたくない場合に使います。例えば、"_ddと入力して実行すると、現在の行を削除しますが、"1+*の各レジスタの内容には影響しません。
最後の検索パターン / vim [ ] 最後の検索パターンに/?:globalを含んでいた場合、このレジスタに格納されます。

読み取り専用ではないレジスタは、ユーザが値を設定してもかまいません。

:let @/ = 'register'

これを実行しておくと、後で「n」と打てば次の「レジスタ」の内容にジャンプすることができます。

いつの間にかレジスタが一杯になってしまうと、たくさんの例外が出てきますので、文字列:h registersを必ず確認するようにしてください。

yでヤンク、pまたはPでペーストするのですが、Vimではビジュアルモードでの文字単位(characterwise)選択と行単位(linewise)選択を区別していることに注意してください。:h linewiseを参照してください。

例:行単位

yy(あるいは単にY)でカレント行をヤンクし、カーソルをどこか他のところに移動します。pでカレント行の下に、Pで上にペーストします。

例:文字単位

0ywで最初の単語をヤンクし、どこか他のところに移動します。pでカレント行におけるカーソルの後ろに、Pで前にペーストします。

例:レジスタの明確な呼称

"aYはレジスタaにカレント行をヤンクします。そして別の行に移動します。"AYはレジスタaにカレント行を追加します。

試しに、こうしたレジスタを一通り使って、:regを常にチェックしてみることをお勧めします。そうすれば、実際に起こっている動きを確認することができるはずです。

面白い事実: Emacsでは、「ヤンク」はコピーではなくペースト(または、その前にkillした文字列の再挿入)を指しています。

範囲

範囲はとても理解しやすいものですが、その実力を存分に活用しているVimユーザは多くないようです。

  • 多くのコマンドが範囲を必要とする
  • アドレスはある行を意味する
  • 範囲は,または;によって区切られたシングルアドレスかペアアドレスのいずれかである
  • コマンドがどの行に作用するかは範囲によって決まる
  • デフォルトでは、ほとんどのコマンドはカレント行にのみ作用する
  • :writeならびに:globalだけはデフォルトでも全ての行に作用する

範囲の使い方はかなり直感的なものなので、ここにいくつか例を挙げてみます(:deleteの略で:dを使っています)。

コマンド 作用する行
:d カレント行
:.d カレント行
:1d 最初の行
:$d 最後の行
:1,$d 全ての行
:%d 全ての行(1,$)のためのシンタックスシュガー)
:.,5d カレント行から5行目まで
:,5d 同じくカレント行から5行目まで
:,+3d カレント行と次の3行目まで
:1,+3d 最初の行からカレント行の3行目まで
:,-3d カレント行と最後の3行分(逆に作用する範囲であるため、Vimがプロンプトを表示する)
:3,'xdelete 3行目からxでマークされた行まで
:/^foo/,$delete カレント位置から最初に”foo”がマッチする行から最後の行まで
:/^foo/+1,$delete カレント位置から最初に”foo”がマッチする行の1行後から最後の行まで

,の代わりに、;がセパレータとして使えることに注目してください。違いは、from,toの場合、toはカレント行に関連していますが、from;toを使う場合、tofromのアドレスに関連しているということです。例えば、5行目にいるとしましょう。:1,+1dは1行目から6行目を削除するのですが、対して:1;+1dは1行目と2行目を削除するということになります。

/アドレスは別のアドレスに先行することができます。これにより、パターンをスタックすることができます。例えば次のようになります。

:/foo//bar//quux/d

これは、カレント行の後で、”foo”にマッチする最初の行の後、さらに”bar”にマッチする最初の行の後、そして”quux”にマッチする最初の行を削除します。

時にVimは自動的に先頭に範囲を含むコマンドラインを追加します。例えば、Vでビジュアルモードに移行し、いくつかの行を選択した後、:をタイプします。するとコマンドラインに範囲の'<,'>が入力されます。これは、次のコマンドがその既に選択されている行に作用するということを意味しています。(これは、あなたが時々:vnoremap foo :<c-u>commandといったマッピングを目にする理由でもあります。ここで<c-u>は範囲を取り除くために使われています。Vimは範囲がサポートしていないコマンドに与えられるとエラーを発するからです。)

また別の例としては、ノーマルモードで!!を使っていることです。これが:.!を含むコマンドラインに入力します。外部プログラムが続く場合は、そのプログラムのアウトプットがカレント行に置き換わることになります。そのため、カレントパラグラフを、:?^$?+1,/^$/-1!lsを使うことによりlsのアウトプットと置き換えることができるのです。素敵ですよね。

関連するヘルプ:

:h cmdline-ranges
:h 10.3