koturnの日記

普通の人です.ブログ上のコードはコピペ自由です.

VimのCtrl-X補完を使えるようになりたい

はじめに

Vimにはデフォルトで補完機能が備わっている. 実践Vim 思考のスピードで編集しよう!であったり,daisuzuさんの2015年のVim Advent Calendarの記事VimのCTRL-X補完について - daisuzu's notesを読むと,neocompletedeopleteに頼るだけではなく,デフォルトの補完機能を活用したいと考えるようになるものである.

しかし, ins-completion を見るとわかるように,Vim<C-x> から始まる補完は12種類もあり,初めのうちはとても覚えきれるものではない. そこで,この記事では, <C-x> 補完初心者が使いこなせるようになるまでのヒント表示キーマッピングを考えた.

ins-completion

まずは,補完にはどのような種類があるかを簡単にまとめる. 以下の表は ins-completion の内容と同じなので,先程見た人は無視してよい.

キーマッピング 補完 必須オプション
<C-X><C-l> 行全体
<C-X><C-n>, <C-X><C-n> 現在のファイルのキーワード
<C-X><C-k> 'dictionary' のキーワード 'dictionary'
<C-X><C-t> 'thesaurus' のキーワード 'thesaurus'
<C-X><C-i> 編集中と外部参照(インクルード)しているファイルのキーワード
<C-X><C-]> タグファイル('tags' で設定したパスで見つかるファイル)
<C-X><C-f> ファイル名
<C-X><C-d> 定義もしくはマクロ
<C-X><C-v> Vimコマンドライン
<C-X><C-u> ユーザ定義補完 'completefunc'
<C-X><C-o> オムニ補完 'omnifunc'
<C-X>s, <C-X><C-s> スペリング補完 'spell'
<C-n>, <C-p> 'complete' のキーワード

この内, <C-n>, <C-p><C-x> の先行入力を伴わないので,ヒント表示の対象から外す.

ヒント表示キーマッピング

とにかく,以下のようにしただけという話.

" 入力キーの辞書
let s:compl_key_dict = {
      \ char2nr("\<C-l>"): "\<C-x>\<C-l>",
      \ char2nr("\<C-n>"): "\<C-x>\<C-n>",
      \ char2nr("\<C-p>"): "\<C-x>\<C-p>",
      \ char2nr("\<C-k>"): "\<C-x>\<C-k>",
      \ char2nr("\<C-t>"): "\<C-x>\<C-t>",
      \ char2nr("\<C-i>"): "\<C-x>\<C-i>",
      \ char2nr("\<C-]>"): "\<C-x>\<C-]>",
      \ char2nr("\<C-f>"): "\<C-x>\<C-f>",
      \ char2nr("\<C-d>"): "\<C-x>\<C-d>",
      \ char2nr("\<C-v>"): "\<C-x>\<C-v>",
      \ char2nr("\<C-u>"): "\<C-x>\<C-u>",
      \ char2nr("\<C-o>"): "\<C-x>\<C-o>",
      \ char2nr('s'): "\<C-x>s",
      \ char2nr("\<C-s>"): "\<C-x>s"
      \}
" 表示メッセージ
let s:hint_i_ctrl_x_msg = join([
      \ '<C-l>: While lines',
      \ '<C-n>: keywords in the current file',
      \ "<C-k>: keywords in 'dictionary'",
      \ "<C-t>: keywords in 'thesaurus'",
      \ '<C-i>: keywords in the current and included files',
      \ '<C-]>: tags',
      \ '<C-f>: file names',
      \ '<C-d>: definitions or macros',
      \ '<C-v>: Vim command-line',
      \ "<C-u>: User defined completion ('completefunc')",
      \ "<C-o>: omni completion ('omnifunc')",
      \ "s: Spelling suggestions ('spell')"
      \], "\n")
function! s:hint_i_ctrl_x() abort
  echo s:hint_i_ctrl_x_msg
  let c = getchar()
  return get(s:compl_key_dict, c, nr2char(c))
endfunction

inoremap <expr> <C-x>  <SID>hint_i_ctrl_x()

途中で :echo した上でキー入力待ち状態を作るには, <expr> を活用するのがよい. 上記のキーマッピングを行うと,以下のようになる.

f:id:koturn:20180210164917g:plain

これで,補完を行おうとしたときに「あの補完はどの <C-x> の後にどのキーを押下すればよかったんだっけ?」とならず,とりあえず <C-x> を押下してみて,「お~このキーであの補完ができるんだった!」となるのではないかと思う. もちろん,これは <C-x> 補完に不慣れな人向けのキーマッピングである. いちいち,複数行の :echo が表示されるのは,常用するレベルになると鬱陶しいことこの上ないだろう. 慣れるまではヒント表示を使い,慣れた頃に .vimrc から削除するのがよいと思われる.

おまけ

本記事におけるヒントの表示は,レジスタ内容やマーク位置の表示にも応用できるのではないかと思い,以下のキーマッピングを考えた.

function! s:hint_cmd_output(prefix, cmd) abort
  redir => str
    execute a:cmd
  redir END
  echo str
  return a:prefix . nr2char(getchar())
endfunction

" カーソル位置のマーク
nnoremap <expr> m  <SID>hint_cmd_output('m', 'marks')
" マーク位置へのジャンプ
nnoremap <expr> `  <SID>hint_cmd_output('`', 'marks')
" マーク位置へのジャンプ
nnoremap <expr> '  <SID>hint_cmd_output("'", 'marks')
" レジスタ参照(ヤンクや削除)
nnoremap <expr> "  <SID>hint_cmd_output('"', 'registers')
" マクロ記録
nnoremap <expr> q  <SID>hint_cmd_output('q', 'registers')
" マクロ再生
nnoremap <expr> @  <SID>hint_cmd_output('@', 'registers')

これらは次のようにヒントを表示する.

まずは,マークの例.

f:id:koturn:20180210164931g:plain

次に,ヤンクや削除時のレジスタ指定. 4"ayy のような行数指定を行っても問題ない.

f:id:koturn:20180210164938g:plain

まとめ

この記事では,VimのCtrl-X補完を使いこなせるようになるための初心者練習用ヒント表示のキーマッピングを紹介した. また,ヒント表示を応用して,マークやレジスタの内容を表示するキーマッピングも紹介した. あくまで初心者向けのキーマッピングであり,熟達するにつれて,ヒント表示がだんだん鬱陶しくなると思う. その頃に,この記事で紹介したキーマッピング.vimrc から削除し,よりスパルタンを目指すとよいだろう.

参考文献