2016年2月22日
Vim-Galore : Vimについて知っておくべき全てのこと (1/5)
本記事は、原著者の許諾のもとに翻訳・掲載しております。
(編注:2020/08/18、いただいたフィードバックをもとに記事を修正いたしました。)
(訳注: 2016/2/26、記事タイトルを変更いたしました。)
特定のトピックについての記述をご希望ですか? Issue を立てるか、 Twitter で私までお知らせください!ありがとう!
はじめに
基礎
- バッファ/ウインドウ/タブ
- アクティブ/読み込み済み/一覧表示/名前付きバッファ
- 引数リスト
- マッピング
- マップリーダー
- レジスタ
- 範囲
- マーク機能
- 補完
- モーション/オペレータ/テキストオブジェクト
- autocmd
- 変更履歴リスト/ジャンプリスト
- アンドゥツリー
- クイックフィックスと位置リスト
- マクロ
- カラースキーム
- 折り畳み機能
- セッション
- ローカリティ
使用方法
- オフラインでのヘルプ
- オフラインでのヘルプ(代替案)
- オンラインでのヘルプ
- クリップボード
- クリップボードの使用法(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を使わないようにしましょう。
次のステップは以下の通りです。
- 独自の vimrc を作成します。
- 最初の数週間のために チートシート を用意します。
- 基礎 の項を読んで、何ができるのか学んでください。
- 必要に応じて学んでください。Vimの勉強にはキリがありません。問題に突き当たった時は、インターネットで情報を探せばきっと解決法が見つかるはずです。Vimには素晴らしいマニュアルが付いてきますから、操作方法を学んでください。 オフラインでのヘルプ
- その他の資料 をのぞいてみてください。
最後のアドバイスとしては、様々な過剰な プラグイン を追加する前に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
を実行しているのかということです。他の重要な情報は、 Tiny
と Huge
です。Vimは tiny
、 small
、 normal
、 big
そして huge
という機能セットを区別していて、それぞれに違う機能のサブセットを有効にするのです。
:version
のアウトプットの大半は機能リストそのものに費されます。 +clipboard
は、クリップボード機能がコンパイル時に組み込まれているということを、 -clipboard
ではコンパイル時に組み込まれていないということを意味します。
Vimのいくつかの機能を正しく機能させるためにはコンパイル時に組み込む必要があります。例えば、 :prof
を機能させるためには、 +profile
機能を有効にするhuge版の機能セットのVimが必要になります。
もしそうなっておらず、あなたがパッケージマネージャでVimをインストールした場合は、 vim-x
、 vim-x11
、 vim-gtk
、 vim-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だけにリンクすることにします。
- http://people.csail.mit.edu/vgod/vim/vim-cheat-sheet-en.png
- https://cdn.shopify.com/s/files/1/0165/4168/files/preview.png
- http://www.nathael.org/Data/vi-vim-cheat-sheet.svg
- http://michael.peopleofhonoronly.com/vim/vim_cheat_sheet_for_programmers_screen.png
- http://www.rosipov.com/images/posts/vim-movement-commands-cheatsheet.png
基礎
バッファ/ウィンドウ/タブ
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つ前の削除。以下同様。レジスタ 1 – 9 は、要素が9個の読み取り専用 キュー と考えること。 |
小削除 | - |
vim | [ ] | 最後の1行未満の削除 |
名前付き | a ~ z , A ~ Z |
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
を使う場合、 to は from のアドレスに関連しているということです。例えば、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
株式会社リクルート プロダクト統括本部 プロダクト開発統括室 グループマネジャー 株式会社ニジボックス デベロップメント室 室長 Node.js 日本ユーザーグループ代表
- Twitter: @yosuke_furukawa
- Github: yosuke-furukawa