かなりすごいブログ

neosnippetとsmartinputで<CR>キーのマッピングを共存させる

Vim Advent Calendar 2012、355日目の記事になります。

<cr>のマッピングについて

本日は、<CR>のマッピングを複数のプラグイン間で共有する設定について記事にしたいと思います。

例として、neosnippet及びneocompletesmartinput及びsmartinput-endwiseの設定を共存させる方法をお伝えします。

行いたい動作

まず、私はneocompleteneosnippetの候補を補完し、さらに候補にフォーカスが当たっている状態で<CR>を押下することにより、スニペットを展開したいと考えています。

更に、補完ポップアップメニューが表示されていない状態で<CR>を押下した際には、smartinput-endwiseにより、関数定義の閉じendなどを文脈から判断じて自動挿入したいと思います。

設定

前提として、以下のプラグインをneobundleで管理しているものとします。

if neobundle#tap('vim-smartinput')
  call neobundle#config({
        \   'autoload' : {
        \     'insert' : 1
        \   }
        \ })

  function! neobundle#tapped.hooks.on_post_source(bundle)
    call smartinput_endwise#define_default_rules()
  endfunction

  call neobundle#untap()
endif

if neobundle#tap('vim-smartinput-endwise')
  function! neobundle#tapped.hooks.on_post_source(bundle)
    " neosnippet and neocomplete compatible
    call smartinput#map_to_trigger('i', '<Plug>(vimrc_cr)', '<Enter>', '<Enter>')
    imap <expr><CR> !pumvisible() ? "\<Plug>(vimrc_cr)" :
          \ neosnippet#expandable() ? "\<Plug>(neosnippet_expand)" :
          \ neocomplete#close_popup()
  endfunction
  call neobundle#untap()
endif

smartinput_endwise#define_default_rules()を実行した時点で、<CR>のマップがsmartinputによって上書きされてしまいます。ですので、neobundle#tapped.hooks.on_post_source(bundle)によって、その後に再度<CR>をオリジナルの設定でマッピングします。

call smartinput#map_to_trigger('i', '<Plug>(vimrc_cr)', '<Enter>', '<Enter>')
imap <expr><CR> !pumvisible() ? "\<Plug>(vimrc_cr)" :
      \ neosnippet#expandable() ? "\<Plug>(neosnippet_expand)" :
      \ neocomplete#close_popup()

こちらについて解説します。 まず、smartinput#map_to_triggerによって、<Plug>(vimrc_cr)が押下された時にsmartinput-endwiseが動作するように設定します。 次にimapによって<CR>のマッピングを上書きします。imap <expr><CR>といった様に、<expr>を使うことでマップ内容に式を書くことができます。<CR>が押下されるたびに設定した式が評価されます。

式の内容を見て行きましょう。まず、!pumvisible()pumvisible()が偽である時、つまり補完ポップアップメニューが開いていない時を意味します。補完を行っていない時はsmartinputに処理を渡したいので、先ほど設定した<Plug>(vimrc_cr)を呼び出しています。次にpumvisible()が真である場合ですが、ここで三項演算子がネストしています。こちらは、neosnippet#expandable()が真(現在選択されている補完候補がneosnippetのものであり、スニペットが展開できる状態)であればスニペットを展開し、そうでなければ補完ポップアップメニューを閉じるという式になっています。

これにより、<CR>を押下した時の動作を、以下のように定義することができました。

  • 分岐1:補完ポップアップメニューが表示されていれば分岐2へ、そうでなければsmartinputにて<CR>を押下時に呼び出される関数を呼び出す
  • 分岐2:スニペット候補が展開されていればスニペットを展開し、そうでなければ補完ポップアップメニューを閉じる

まとめ

かなりべんり(かなり)