読者です 読者をやめる 読者になる 読者になる

txt ファイルと filetype

.txt と言う拡張子はテキストファイルの拡張子なわけだけど、テキストファイルって一言で言っても色々あるわけです。
で、私ははてダの下書きを .txt で保存してたり、仕事で書いた readme.txt をこっそり reStructuredText で書いてみたりしてるので、そういう場合もある程度自動で filetype も検出して欲しい。
てわけで、私は以下のように設定している。

" ftdetect/text.vim
autocmd BufReadPost,BufNewFile *.txt call s:structured_text()

function! s:structured_text()
  let line1 = getline(1)
  let line2 = getline(2)
  execute 'setfiletype'
  \ line1 =~ '^\([=-`:''"~^_*+#<>]\)\1*$' ? 'rst' :
  \ line1 =~ '^*'      ? 'hatena'   :
  \ line1 =~ '^#' ||
  \ line2 =~ '^=\+$'   ? 'markdown' :
  \                      'text'
endfunction

ftdetect は 'runtimepath' のどこかに置く。大体の場合は ~/.vim/ftdetect/text.vim とか。詳しくは :help add-filetype を参照。
要は中身を見てそれっぽいのに設定している。これで最初の行が * だったら "hatena"、# だったら "markdown" みたいになる。
そして更にもう一工夫してあって、filetype=text の場合 ftplugin を使ってファイルを書き込んだ際に :filetype detect が実行されるようになっている。なので白紙の .txt で始めても 1 行目を書いて保存すれば自動的に filetype の検出が行われる。

" vimrc
augroup vimrc-misc  " {{{
  " ... 中略 ...
  " Update filetype.
  autocmd BufWritePost * if &l:filetype == '' || exists('b:ftdetect')
  \                      | unlet! b:ftdetect | filetype detect | endif
  " ... 中略 ...
augroup END  " }}}
" ftplugin/text.vim
let b:ftdetect = 1

ちょっと汎用的にしたかったので b:ftdetect とか言う独自の変数を使ってるけど、普通に ftplugin で autocmd してもいいと思う*1

*1:その場合は b:undo_ftplugin とかも考えないといけないのでそれはそれで面倒だったりする