この前、Gistに投稿した記事だが、どうせなので、はてなの方にも投稿しておこうと思う。
元はこちら:https://gist.github.com/koturn/5929673
NeoBundleでインサートモード時に遅延読み込みすると、インサートモード時にステータスラインの色を変更するというよくある設定が うまくいかないということをTwitterで呟いたところ、暗黒美夢王さんから返答がきたので、そのことについて書いてみようかと思う。
まず、当初、私が.vimrcに書いていたのは、以下のような設定である。
例として、neocompleteをインサートモード時に遅延読み込みするようにしてある。
let $DOTVIM = $HOME . '/.vim' if has('vim_starting') set runtimepath+=$DOTVIM/bundle/neobundle.vim endif call neobundle#rc(expand('$DOTVIM/bundle/')) NeoBundle 'Shougo/neobundle.vim' NeoBundleLazy 'Shougo/neocomplete.vim', \ {'autoload' : {'insert' : 1} \} " ==================================================================== " インサートモードでステータスバーの色を変更する " NeoBundleでインサートモード時に読み込むプラグインが1つでもあると、 " うまく動作しなくなる。 " ==================================================================== set laststatus=2 set statusline=%<%f\ %m\ %r%h%w%{'[fenc='.(&fenc!=#''?&fenc:&enc).']\ [ff='.&ff.']\ [ft='.(&ft==#''?'null':&ft).']\ [ascii=0x'}%B]%=\ (%v,%l)/%L%8P let g:hi_insert = 'highlight StatusLine guifg=darkblue guibg=darkyellow gui=none ctermfg=blue ctermbg=yellow cterm=none' if has('syntax') augroup InsertHook autocmd! autocmd InsertEnter * call s:StatusLine('Enter') autocmd InsertLeave * call s:StatusLine('Leave') augroup END endif let s:slhlcmd = '' function! s:StatusLine(mode) if a:mode ==# 'Enter' silent! let s:slhlcmd = 'highlight ' . s:GetHighlight('StatusLine') silent exec g:hi_insert else highlight clear StatusLine silent exec s:slhlcmd endif endfunction function! s:GetHighlight(hi) redir => hl exec 'highlight ' . a:hi redir END let hl = substitute(hl, '[\r\n]', '', 'g') let hl = substitute(hl, 'xxx', '', '') return hl endfunction colorscheme default
これは最小構成であるが、うまくいかないということが確認できると思う。
暗黒美夢王さんによると、NeoBundleはインサートモード時に遅延読み込みを行うときに、InsertEnterのautocmdを発行しているらしい。
つまり、最初にインサートモードに入ったときに、InsertEnterが2回呼ばれているというになり、上記コードのs:slhlcmdという変数が、ハイライトコマンドをうまく保存できないということになる。
これは、NeoBundleの仕様であり、複数回InsertEnterが発行されても大丈夫なように、autocmdを書くべきとのこと。
上記のインサートモード時にステータスラインの色を変更するという設定は、InsertEnterの後にInsertLeaveが発行されると仮定しているから起こる問題なので、それを解消すればよい。
あと、個人的に気に入らないところ(関数や変数のスコープ、命名規則など)があるので、その部分も修正したコードを、以下に記す。
let $DOTVIM = $HOME . '/.vim' if has('vim_starting') set runtimepath+=$DOTVIM/bundle/neobundle.vim endif call neobundle#rc(expand('$DOTVIM/bundle/')) NeoBundle 'Shougo/neobundle.vim' NeoBundleLazy 'Shougo/neocomplete.vim', \ {'autoload' : {'insert' : 1} \} " Show status line at the second line from the last line. set laststatus=2 set statusline=%<%f\ %m\ %r%h%w%{'[fenc='.(&fenc!=#''?&fenc:&enc).']\ [ff='.&ff.']\ [ft='.(&ft==#''?'null':&ft).']\ [ascii=0x'}%B]%=\ (%v,%l)/%L%8P " Change color of status line depending on mode. if has('syntax') augroup InsertHook autocmd! au InsertEnter * call s:hi_status_line(1) au InsertLeave * call s:hi_status_line(0) au ColorScheme * silent! let s:slhlcmd = 'highlight ' . s:get_highlight('StatusLine') augroup END endif function! s:hi_status_line(mode) if a:mode == 1 highlight StatusLine guifg=white guibg=MediumOrchid gui=none ctermfg=white ctermbg=DarkRed cterm=none else highlight clear StatusLine silent exec s:slhlcmd endif endfunction function! s:get_highlight(hi) let l:hl = '' redir => l:hl exec 'highlight ' . a:hi redir END let l:hl = substitute(l:hl, '[\r\n]', '', 'g') let l:hl = substitute(l:hl, 'xxx', '', '') return l:hl endfunction colorscheme default
ステータスラインの色は、カラースキームの変更時に取得するようにしておけば、ステータスラインの色を変更するだけの処理をしない限りは問題ないだろう。
このような初心者的な問題に引っかからないように、Vim力を向上させていきたいものである。