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

Vim で C++ を書くときの逆引きリファレンス

C++ Vim

この記事は C++ AdventCalendar 2013 の 10 日目の記事です.

本記事では VimC++ を書いている人を対象として,逆引きで Vim の機能やプラグインについて紹介していきます. すべてについて細かく書いているとキリが無いので,基本的な使い方とプラグインのリポジトリへのポインタ,主要な記事へのリンクを載せています. より詳しく知りたい場合はリポジトリ内の README やドキュメント(/doc内にあります),プラグインインストール後の :help コマンドを利用してください. また,何か問題が発生した場合など,助けが必要な場合は Lingr というチャットサービスの Vim 部屋でも対応してもらえることがあります.

http://lingr.com/room/vim

目次

プラグインマネージャについて

本記事では多くのプラグインを紹介しています.Vim のプラグインを導入する際にはプラグインマネージャを利用するのが一般的になってきています. プラグインマネージャとしては下記のようなものが挙げられます.

neobundle.vim vundle vim-plug pathogen

本記事ではプラグインの読み込みを必要になるまで遅らせられるなど高機能な neobundle.vim を設定例に使います.

シンタックスハイライト

C++11 に入り,ラムダ式やいくつかのキーワードなどの拡張が行われました. Vim における C++ のシンタックスハイライトは vim-jp(日本の Vim ユーザグループ)で管理されています.

vim-jp/cpp-vim

最近の Vim では C++11 が既にサポートされていますが,バグフィックスや最新版の C++ への対応がいち早く行われるので,上記リポジトリを利用するのがおすすめです.

NeoBundleLazy 'vim-jp/cpp-vim', {
            \ 'autoload' : {'filetypes' : 'cpp'}
            \ }

補完する

ワード・インクルードパス補完

変数名,関数名やクラス名,#include 時のファイル名などの補完候補するためのプラグインです. インクルード補完を正しく動作させるためには,path を正しく設定しておく必要があります.

以下は一例で,環境に合わせる必要があります.

augroup cpp-path
    autocmd!
    autocmd FileType cpp setlocal path=.,/usr/include,/usr/local/include,/usr/lib/c++/v1
augroup END

neocomplete

Shougo/neocomplete

NeoBundle 'Shougo/neocomplete.vim'

neocomplete は neocomplcache の後継で,バッファ内の単語補完やインクルードパスなどの補完候補を自動生成し,ポップアップウィンドウで編集中に選択できます.

非常にカスタマイズ性が高く,様々な言語に対応しており,もちろん C++ にも対応しています. 補完を高速化するために Lua インターフェースを使用しているため,:version+lua になっていることを確認する必要があります.

基本的な設定例は :help neocomplete で確認できます.

YouCompleteMe

Valloric/YouCOmpleteMe

NeoBundleLazy 'Valloric/YouCompleteMe', {
      \ 'build' : {
      \   'mac' : './install.sh --clang-completer',
      \   'unix' : './install.sh --clang-completer',
      \ }
      \ }

YouCompleteMe は neocomplete と同様に補完を自動で行うためのプラグインです. 補完エンジンに C++ を使っており,補完生成が neocomplete よりも高速です. また,試していませんが,clang による補完も実装されているようです. ただ,あまり拡張することを考慮されておらず,neocomplete に比べるとカスタマイズ性が落ちるのと,OS によってはビルドが面倒です.

neosnippet

Shougo/neosnippet.vim

NeoBundle 'Shougo/neosnippet'

neosnippet は様々な言語でスニペット(コード片)を挿入できるようになるプラグインです. C++ についても多くのスニペット(lambda や range based for など)が用意されています.

また,独自にスニペットを追加することもできます.その辺りは :help neosnippet に載っています.

stargate.vim

osyo-manga/vim-stargate

NeoBundleLazy 'osyo-manga/vim-stargate', {
            \ 'autoload' : {'filetypes' : 'cpp'}
            \ }

#include を簡単に追加できるようにするプラグインです.:StargateInclude コマンドの後に対象のライブラリ(例えば boost/optional.hpp )と入力すると,ファイルの先頭付近に自動で #include を挿入してくれます.コマンドの補完が効きます.

文脈を考慮した賢い補完

clang の構文解析を利用した補完機能を提供するプラグインです. オムニ補完の機能を使っており,インサートモードで .:: の後に Ctrl + x → Ctrl + o と入力すると補完候補が表示されます.

neocomplete で自動で候補を表示させるには次の設定を追加する必要があります.

if !exists('g:neocomplete#force_omni_input_patterns')
    let g:neocomplete#force_omni_input_patterns = {}
endif
let g:neocomplete#force_omni_input_patterns.cpp =
        \ '[^.[:digit:] *\t]\%(\.\|->\)\w*\|\h\w*::\w*'

clang_complete

Rip-Rip/clang_complete

NeoBundleLazy 'Rip-Rip/clang_complete', {
            \ 'autoload' : {'filetypes' : ['c', 'cpp']}
            \ }

libclang の python バインディングを使って補完候補を生成する補完プラグインです. quickfix によるエラーや警告の表示,スニペット機能,カーソルしたの変数の定義位置へのジャンプなどかなり多機能ですが,最近はメンテナンスされていないようです.

marching.vim

osyo-manga/vim-marching

NeoBundleLazy 'osyo-manga/vim-marching', {
            \ 'depends' : ['Shougo/vimproc.vim', 'osyo-manga/vim-reunions'],
            \ 'autoload' : {'filetypes' : ['c', 'cpp']}
            \ }

clang コマンドを利用して補完候補を生成するプラグインです. vimproc.vim を利用して,非同期に補完候補を生成するため,補完生成によって Vim がブロックされません. neocomplete と一緒に使う場合は let g:marching_enable_neocomplete = 1 とする必要があります.

名前空間の入力を簡単にする

std::boost:: などの名前空間を入力するのはなかなか手間ですが,いちいち using するのもそれはそれで大変です. よく入力する名前空間は入力を楽にしたいです. 下記のように .vimrc で設定すると b;;boost::s;;std::d;;detail:: に自動で展開されるため,入力が楽になります.

augroup cpp-namespace
    autocmd!
    autocmd FileType cpp inoremap <buffer><expr>; <SID>expand_namespace()
augroup END
function! s:expand_namespace()
    let s = getline('.')[0:col('.')-1]
    if s =~# '\<b;$'
        return "\<BS>oost::"
    elseif s =~# '\<s;$'
        return "\<BS>td::"
    elseif s =~# '\<d;$'
        return "\<BS>etail::"
    else
        return ';'
    endif
endfunction

現在のバッファを実行する

ライブラリを試しに使ってみたいときなど,簡単なコード片を書いてさっと実行したいことがよくあります. ここでは,そのためのプラグインを紹介します.

thinca/vim-quickrun

NeoBundle 'thinca/vim-quickrun'

実行したいコードを開き,:QuickRun を実行するだけで良い感じに実行し,良い感じに表示してくれます. また,g:quickrun_config に設定を書くことにより,挙動を細かくカスタマイズできます.

let g:quickrun_config = get(g:, 'quickrun_config', {})
" vimproc を使って非同期に実行し,結果を quickfix に出力する
let g:quickrun_config._ = {
            \ 'outputter' : 'quickfix',
            \ 'runner' : 'vimproc'
            \ }

デフォルトで clang を使い,好みのオプションを付けるには次のようにします

let g:quickrun_config.cpp = {
            \ 'command' : 'clang++',
            \ 'cmdopt' : '-std=c++1y -Wall -Wextra',
            \ }

また,gcc と結果を見比べたい場合などのために,gcc で実行する用の設定も追加で書けます.

let g:quickrun_config['cpp/gcc'] = {
            \ 'command' : 'g++',
            \ 'cmdopt' : '-std=c++11 -Wall -Wextra',
            \ }

この場合,:QuickRun cpp/gcc とすると gcc で実行できます.

また,boost の一部のライブラリなど,ライブラリのリンクが必要な場合は,quickrunex-vim というプラグインが用意されています.

mattn/quickrunex-vim

NeoBundle 'thinca/vim-quickrun', {
            \ 'depends' : 'mattn/quickrunex-vim',
            \ }

次のように hook を追加すると :QuickRun 時に自動でライブラリのリンクをするためのオプション(-lboost_system とか)を行ってくれます.

let g:quickrun_config.cpp = {
            \ 'command' : 'clang++',
            \ 'cmdopt' : '-std=c++1y -Wall -Wextra',
            \ 'hook/quickrunex/enable' : 1,
            \ }

wandbox-vim

rhysd/wandbox-vim

NeoBundle 'rhysd/wandbox-vim'

@melponn さんと @kikairoya さんが作成したソーシャルコンパイルサービス,Wandbox を Vim から利用するためのプラグインです. :Wandbox とすると現在のバッファを Wandbox で実行し,コンパイラからのエラーや警告を quickfix に,実行結果をメッセージウィンドウに表示します. curl または wget コマンドと vimproc.vim をインストールしていると非同期にリクエストを投げるようになり,結果が返ってくるまでの間待たなくて良くなります.

また,:Wandbox --compiler=clang-head,gcc-head のようにすると複数のコンパイラで一度に実行できます.

なお,上記の vim-quickrun との連携もあり,:QuickRun -runner wandbox のようにすると quickrun の実行部分を Wandbox で行うことができます.

調べる

Boost のドキュメントを調べる

osyo-manga/unite-boost-online-doc

NeoBundleLazy 'osyo-manga/unite-boost-online-doc', {
            \ 'depends' : [
            \      'Shougo/unite.vim',
            \      'tyru/open-browser.vim',
            \      'mattn/webapi-vim',
            \   ],
            \ 'autoload' : 'cpp'
            \ }

Boost のドキュメントの一覧を unite.vim で開き,絞り込み,ブラウザで表示することができます. 次のように :UniteWithCursorWord をマッピングしておくとすぐに目的のドキュメントにたどり着きます.

augroup cpp-unite
    autocmd!
    autocmd FileType cpp nnoremap <Space>ub :<C-u>UniteWithCursorWord boost-online-doc
augroup END

規格書を調べる

rhysd/unite-n3337

NeoBundleLazy 'rhysd/unite-n3337', {
            \ 'depends' : 'Shougo/unite.vim',
            \ 'autoload' : {'filetypes' : 'cpp'}
            \ }

unite.vim で C++11 規格書直近のドラフト N3337 を章タイトルで絞り込み検索し,読みやすいようにシンタックスハイライト付きで表示するプラグインです. 勝手に n3337.pdf を同梱するのはまずそうだったため,n3337.pdf は自前で用意してもらい,それを pdftotext コマンドで変換してもらう形になっています. なので,poppler パッケージが必要です.

カーソル移動

カーソル下のクラス・変数などの定義位置へジャンプする

補完のところでも紹介した clang_complete を使います. Ctrl + ] を押すと,カーソル下のクラス名や変数名などの定義位置へジャンプすることができます. 他のマッピングに割り当てたい場合は次のようにします.

" Ctrl + t でカーソル下のシンボルの定義位置へジャンプ
let g:clang_jumpto_declaration_key = '<C-t>'

現在のバッファのアウトラインから移動する

Shougo/unite-outline

NeoBundle 'Shougo/unite-outline'

unite-outline.vim というプラグインを用い,unite.vim のインターフェースで現在のバッファのアウトラインを表示できます. 目的のクラスに行を合わせ,Enter でその位置にジャンプできます. 特にヘッダオンリーのライブラリなどの宣言と定義が同じファイルにある場合は特に有効です.

ヘッダファイルとソースファイルを切り替える

kana/vim-altr

NeoBundleLazy 'kana/vim-altr'

vim-altr は予め定義された複数のファイル間を行き来できるようにするプラグインです.

nnoremap <Leader>a <Plug>(altr-forward)

とすると \a で現在のヘッダファイル(.hpp.h など)とソースファイル(.cpp.cc など)の間を簡単に行き来できます. さらに,挙動をかなり細かくカスタマイズできます.(詳しくは :help altr)

インクルード先へジャンプする

Vim では標準でカーソル下のパスに飛ぶ機能があります. #include <hoge><hoge> の部分で gf を入力すると新しいバッファで対象のヘッダファイルを開けます. また,<C-w>f で新しいウィンドウ,<C-w>gf で新しいタブで対象のヘッダファイルを開くことができます.

ソースコードを整形する

インデントルールを設定する

C や C++ における Vim のインデントルールは cinoptions というオプションで細かく変更することができます. 例えば,ラベルでインデントを深くされたくない場合は次のようにします.

set cinoptions+=:0,g0

他にも名前空間や switch 文でインデントを深くするかなどを決めることが出来ます. cinoptions の設定項目は大量にあるので,:help cinoptions:help cinoptions-values を参照してください.

clang-format.py

clang の付属ツールには C や C++ のコードを整形できる clang-format というコマンドラインツールがあります.これを利用すると,ソースコードを一定のスタイルに合わせて簡単に整形できます. clang-format には clang-format.py という Python スクリプトが付属しており,それを Vim から呼び出すことで Vim 内からコードを整形できます.

augroup cpp-clangformat
    autocmd!
    autocmd FileType c,cpp,objc noremap <C-K> :pyf /path/to/clang-format.py<CR>
augroup END

CTRL + k と入力するとファイル全体がフォーマットされます.

vim-clang-format

rhysd/vim-clang-format

 NeoBundleLazy 'rhysd/vim-clang-format', {
             \ 'autoload' : {'filetypes' : ['c', 'cpp', 'objc']}
             \ }

:ClangFormat とするとバッファ全体を整形できます.また,範囲選択するとその範囲を整形できます.

上記の clang-format.py の違いとしては,

  1. 整形ルールを Vim の設定として細かく記述できる(clang-format.py は整形するファイルと同じディレクトリに .clang-format というスタイル記述ファイルが必要になる)
  2. vim-operator-user をインストールしておくと,Vim のオペレータマッピングが使えるようになります.(オペレータマッピングについては :help operator

\1. については,.vimrc 内で次のように辞書で書けます.

let g:clang_format#style_options = {
            \ 'AccessModifierOffset' : -4,
            \ 'AllowShortIfStatementsOnASingleLine' : 'true',
            \ 'AlwaysBreakTemplateDeclarations' : 'true',
            \ 'Standard' : 'C++11',
            \ 'BreakBeforeBraces' : 'Stroustrup',
            \ }

キーと値に何を入れれば良いかは ClangFormatStyleOptions に載っています.

まとめ

Vim から C++ を利用する際に有用なものを逆引きで書いてみました. 普段から Vim を使っているけれど,あまりカスタマイズしていない方や,これから Vim を使っていきたい方が便利な設定やプラグインを知るための足がかりとなれば幸いです.

プラグインの紹介が多くなってしまいましたが,思いついたときに随時追加していきたいと思います.