なにかと誤解が非常に多いVimのfiletype pluginについての解説。VimM#2でfiletype pluginについてスルーしたのは、以下の分量から察してください。

概要

Vimはバッファ毎にその内容の種類、例えばPerl/Python/Rubyスクリプトといった情報が保持されており、それは'filetype'というオプションで表される。Filetype pluginとは'filetype'に応じた機能を提供するためのスクリプトである。その性質上、提供される機能はバッファローカルである。

Vimは標準で多数のfiletype pluginが同梱されている。どのようなものがあるかはVim内で:edit $VIMRUNTIME/ftplugin/を実行してみれば分かる。:help ft-filetype-plugin'filetype'に対するfiletype pluginのドキュメントが読める。標準添付されているfiletype pluginの多数はいくつかのオプションを設定するだけであるため、敢えてドキュメントがないものが多い。

Vimのfiletype pluginはEmacsで言えばmajor modeや一部のminor modeに近い存在である。あくまで近いのであって、同じものではないことに注意。

インストール方法

各filetype pluginは'runtimepath'ftplugin/ディレクトリ下に配置される。例えばVimに標準添付されているfiletype pluginは全て$VIMRUNTIME/ftplugin/下にある。

自分で独自のfiletype pluginを作成・使用する場合、'runtimepath'に記述されている最初のディレクトリ下のftplugin/ディレクトリにファイルをコピーする('runtimepath'のデフォルト値であれば~/.vim/ftplugin/などである。この値は環境により異なり、また'runtimepath'を変更してる場合はこの限りではない)。場合によってはftplugin/ではなくafter/ftplugin/下にコピーする必要がある(詳細は後述)。

ファイル名

Filetype pluginのファイル名は次の3つのいずれかに合致しなければならない:

ここでfiletypeはそのfiletype pluginが適用されるべき'filetype'の値と同じ文字列であり、fragは任意の文字列である。fragを含む形式は、同一'filetype'に対して複数のfiletype pluginを用いたい場合のために用意されている。例えばpython/smartchr.vimにはsmartchrを利用した設定を記述しておき、python/textobj.vim独自のtext objectsに関する定義を記述するなどが考えられる。

ロードされるタイミング

Filetype pluginはファイルが開かれた場合などのイベントに応じて自動的にロード(:source)される。具体的にはFileTypeが発生したときにロードされ、FileTypeが発生する条件は以下の通りである:

ロード順序

Filetype pluginは'runtimepath'に記述されたディレクトリから順々に該当するファイルがロードされる。同一ディレクトリ下のファイルに関しては次の順番でロードされる:

  1. filetype.vim
  2. filetype_frag.vim
  3. filetype/frag.vim

後者2つについてはfragの辞書式順序でロードされる。

Filetype pluginを書く際の諸注意点

Filetype pluginで行う設定は全てバッファローカルなものにする
オプションを設定する場合、必ず:setlocalを使用する。詳細: Vim: オプションのグローバルな値とローカルな値
Key mappingsやabbreviationsを定義する場合、必ず<buffer>を指定する。詳細: Vim: Key mappingを極める
Exコマンドの定義をする場合、必ず-bufferを指定する。
バッファ固有の情報を記録する場合、バッファローカル変数b:varを使う。
以上の設定は:bwipeoutによって完全に削除される可能性があることに注意する。また、:bdeleteでは削除されないということにも注意する。
b:did_ftpluginによる無効化
諸事情(例えばユーザー側で特定のfiletype pluginのロードを抑止したいケースや、無引数:editによるバッファ内容のリロード時などによる複数回のロードを抑止したいケース)のため、filetype pluginの冒頭で以下のような記述をする:
if exists('b:did_ftplugin')
  finish
endif
let b:did_ftplugin = 1
このチェック用変数名はb:did_ftpluginでなければならない。
b:undo_ftpluginに設定のリセット情報を記述する
例えば:setlocal expandtabをするならば、:let b:undo_ftplugin = 'setlocal expandtab<'のようにし、設定した事項をリセットするためのVim scriptコードを文字列としてセットする。
Key mappingsを定義する場合、<LocalLeader>を用いる
Filetype pluginの機能を実行するためのkey mappingsを定義する場合、特に強い理由がない限り、<LocalLeader>で始まるキーシーケンスをmapする。
<LocalLeader>に対応するキーはg:maplocalleaderでユーザー側が任意に選択できる。

要はfiletype pluginはこう書け

ここでは説明のために以下の用語を用いる:

Normal filetype plugin
ftplugin/に配置されるfiletype plugin。
Additional filetype plugin
after/ftplugin/に配置されるfiletype plugin。
~/.vim/
'runtimepath'のデフォルト値の先頭にあるディレクトリ。具体的な値は環境によって異なる。

Filetype pluginを書く場合、どういう目的で書くかによって書き方が異なる。具体的な目的は以下の3つである:

新たな'filetype'についてのfiletype pluginを書きたい
Normal filetype pluginを書く。
インストール場所はどこでもよいが、~/.vim/ftplugin/が望ましい。
標準のfiletype pluginを自分のもので置き換えたい
Normal filetype pluginを書く。
インストール場所は~/.vim/ftplugin/にする。
必ずb:did_ftpluginを定義し、標準添付のfiletype pluginがロードされることを抑止する。
標準のfiletype pluginに加えて自分独自の設定を追加したい
Additional filetype pluginを書く。
インストール場所は~/.vim/after/ftplugin/にする。
b:did_ftpluginのチェックとセットは行わない。何故なら標準添付のfiletype pluginが既にロード済みであるため、必ずb:did_ftpluginは存在し、結果として常にロードが抑止されることになるからだ。同様に他のadditional filetype pluginsに影響が出るため、セットもしてはならない。
ただしb:did_ftpluginと同様のチェックは必要である。この場合、b:did_ftplugin_filetype_fragなどの変数を使い、b:did_ftpluginを使ってはならない。

その他