Vim でコンテキストによって filetype を自動的に切り換えるプラグインをつくっている

この記事は Vim Advent Calendar 2012 194日目の記事になります。


例えば、html では

<script type="text/javascript">
console.log("Hello, JavaScript")
</script>


例えば、markdown では

```vim
echo "hello, vim"
```

```ruby
echo "hello, ruby"
```


例えば、Vim では

python << EOF

print map(str, range(1, 10))
print "-".join(map(str, range(1, 10)))
for value in range(1, 10):
    print "mami%d" % value

EOF


ruby << EOF

result = (1..10).map(&:to_s).join("-")
p result
(1..10).each { |n|
    p "homu" + n.to_s
}

EOF


このようにソースコード中に別の言語のソースコードを記述する事はあると思います。
この場合、基本的には元の filetype の状態で埋め込む言語のソースコードを書くことになるんですが、そうすると filetype に依存するような処理(ハイライトやコメントアウト系のプラグイン等)が使用できなくて大変不便です。
(例えば Vim で if_ruby を書く場合 filetype=vim の状態で書かなければいけない。


そこで、この問題を解決するためにカーソル位置のコンテキストから自動的に filetype を切り換える為のプラグインをつくっています。




このようにコンテキストによって動的に filetype を設定してソースコードを記述する事が出来ます。

[インストール]

NeoBundle "osyo-manga/vim-precious"

" 必須プラグイン
NeoBundle "Shougo/context_filetype.vim"

" textobj を使いたい場合必要
NeoBundle "kana/vim-textobj-user"

[対応コンテキスト]


詳しくは :help context_filetype-support を参照。

[使い方]

現在のカーソル位置のコンテキストによって filetype が自動的に切り替わります。
わたしは下記のような設定にしています。

" コピペ厳禁

" quickrun 用のマッピング
nmap <Space>q <Plug>(precious-quickrun-op)
omap ic <Plug>(textobj-precious-i)
vmap ic <Plug>(textobj-precious-i)


" コンテキストが切り替わった場合に処理をフック
" ※ここに載っている例はわたしの環境の場合の設定です
augroup my-augroup
    autocmd!
    " vim のコンテキストに切り替わった時に
    " いくつかのマッピングを削除する
"    autocmd User PreciousFileTypeLeave_vim iunmap <buffer> <CR>
"    autocmd User PreciousFileTypeLeave_vim nunmap <buffer> <Leader><Leader>r
augroup END


" filetype=help は insert 時のみ切り替わるように設定
let g:precious_enable_switch_CursorMoved = {
\   "help" : 0
\}

function! s:help()
    augroup my-help
        autocmd!
        autocmd InsertEnter <buffer> :PreciousSwitch
        autocmd InsertLeave <buffer> :PreciousReset
    augroup END
endfunction
autocmd FileType help call s:help()

[コンテキストの範囲を quickrun する]

下記のようにマッピングしておけばコンテキストの範囲を素早く quickrun する事も出来ます。

" quickrun.vim との連携
" <Space>qic で quickrun.vim する
" 内部でコンテキストの filetype を使用しているので
" setfiletype が行われていなくても
" そのコンテキストの filetype で quickrun が行われる
nmap <Space>q <Plug>(precious-quickrun-op)
omap ic <Plug>(textobj-precious-i)
vmap ic <Plug>(textobj-precious-i)


これで markdown 等で記事を書きながら記述しているソースコードを実行して出力し、その場でコードの動作確認する事も出来ます。

[自動切替のタイミングを変更する]

デフォルトの設定ではカーソルが移動するたびに filetype 等が切り替わるのですが、次のように設定することで insert に入った時に切り替わるようにする事も出来ます。

" カーソル移動時の自動切り替えを無効化
" このオプションは filetype ごとに設定可能
" "*" は全ての filetype に影響する
let g:precious_enable_switch_CursorMoved = {
\   "*" : 0
\}
let g:precious_enable_switch_CursorMoved_i = {
\   "*" : 0
\}

" insert に入った時にスイッチし、抜けた時に元に戻す
augroup test
    autocmd!
    autocmd InsertEnter * :PreciousSwitch
    autocmd InsertLeave * :PreciousReset
augroup END

[注意]

同じバッファで filetype を何回も切り替えている為、以前の filetype の設定が別の filetype でも残ってしまう可能性があります。
その場合は undo_ftplugin を使用したり、precious.vim で用意されている autocmd に処理をフックして設定を元に戻す等の工夫が必要になってきます。

" 例)
augroup test
    autocmd!
    autocmd FileType vim nnoremap <buffer> <leader><leader>r :so %<CR>
    " PreciousFileTypeLeave_vim は vim のコンテキストから抜ける時に呼ばれる
    " そのタイミングでマッピングを削除しておく
    autocmd User PreciousFileTypeLeave_vim nunmap <buffer> <Leader><Leader>r
augroup END


この辺りはもう少しどうにかしたいところ。
あとハイライトの誤爆も稀ある。


と、いう感じでひと通りやりたい事は実装できたので記事にしてみました。
まだまだ足りない部分はあると思うので何か欲しい機能がありましたら issues まで教えてもらえると助かります!(`・ω・´)