C++ Advent Calendar 2013 前編 - Vim で C++ のコーディングを行う
この記事は C++ Advent Calendar 2013 の 19日目の前編の記事になります。
本記事は 2011 年に書いた
の 2013 年版になります。
2013 年版といいつつ全然 10個じゃないんですが…。
えー Vim 本体の機能はさほど変わっていませんが、使用しているプラグインなどはだいぶ変わりました。
普段 Vim を使用していない方でも
「Vim でこんな事ができるんだーへー」
ぐらいに感じてもらえればと思います。
犬さんの記事なんて見なかった
ぶっちゃけ書きたかった事をほとんど rhysd さんに書かれてしまったので一部かぶっている内容がありますがご了承下さい。
あと量が多いので誤字脱字があったらごめんなさい。
[本記事を読む前に]
vimrc の設定方法や Vim に慣れていない方は以下の記事を読んでみると参考になると思います。
Vim で分からない事があれば Lingr の Vim 部屋で質問してみると変態な Vimmer が答えてを教えてくれるかもしれません。
また、毎週土曜日の23時から他の方の vimrc を読んで Vim 力を高める読書会にも開催しているので Vim 力を高めたい方はそちらに参加してみるとよいと思います。
[プラグインの導入方法]
本記事ではいくつかの Vim プラグインを使用しています。
Vim でプラグインを使用する場合、いくつか手段があります。
git が導入されていれば、neobundle.vim を使用すると比較的簡単にプラグインの管理を行うことが出来ます。
neobundle.vim でのプラグインの導入方法は下記を参照してみて下さい。
それ以外では pathogen を使用すると導入が比較的簡単です。
こちらは git が必要なく、指定したディレクトリにプラグインを展開するだけなので導入する敷居が低いです。
プレーンな Vim もよいのですが、Vim プラグインを使用するともっと便利になるので、まだ導入されていない方はこの機会に導入してみるといいと思います!
また、プラグインによっては依存しているプラグインや外部コマンド、Vim でサポートがされている機能等が必要な場合があるので、うまく動作しない場合は各プラグインの :help などをご確認下さい。
[インクルードディレクトリを設定]
Vim でインクルードディレクトリは 'path' に設定しておくとよいです。
こうしておく事で gf などでヘッダーファイルを開きたい場合に 'path' を参照してヘッダーファイルを開くことができます。
他にもプラグインなどはこの 'path' を参照して処理を行う事が多いです。
" , 区切りで複数のディレクトリを設定する事ができる setlocal path+=C:/MinGW/lib/gcc/mingw32/4.6.2/include/c++,D:/home/cpp/boost,D:/home/cpp/sprout
また、'path' にインクルードディレクトリを設定する場合は setlocal を使用しましょう。
詳しくは次の項目を参照して下さい。
[Vim で C++ の設定を記述する]
Vim ではファイルを開くとバッファに 'filetype' が設定され、各言語はこれにより識別が行われます。
例えば *.cpp ファイルを Vim で開くと filetype=cpp が設定されています。
Vim で特定の言語に対する設定を記述する場合はこの filetype を使用します。
実際に C++ でのみ対する設定を行うと次のようなコードになります。
" Vim で C++ の設定例 " filetype=cpp が設定された時に呼ばれる関数 "Vim で C++ の設定を行う場合はこの関数内で記述する " ここで設定する項目は各自好きに行って下さい function! s:cpp() " インクルードパスを設定する " gf などでヘッダーファイルを開きたい場合に影響する setlocal path+=D:/home/cpp/boost,D:/home/cpp/sprout "タブ文字の長さ setlocal tabstop=4 setlocal shiftwidth=4 " 空白文字ではなくてタブ文字を使用する setlocal noexpandtab " 括弧を構成する設定に <> を追加する " template<> を多用するのであれば setlocal matchpairs+=<:> " 最後に定義された include 箇所へ移動してを挿入モードへ nnoremap <buffer><silent> <Space>ii :execute "?".&include<CR> :noh<CR> o " BOOST_PP_XXX 等のハイライトを行う syntax match boost_pp /BOOST_PP_[A-z0-9_]*/ highlight link boost_pp cppStatement endfunction augroup vimrc-cpp autocmd! " filetype=cpp が設定された場合に関数を呼ぶ autocmd FileType cpp call s:cpp() augroup END
この時に注意することは set ではなくて setlocal を、マッピングには
setlocal や
[標準ライブラリを開いた時に C++ と認識させる]
Vim では上記でも書いたとおり拡張子やモードラインなどを参照して自動的に filetype が設定されます。
しかし、C++ の標準ライブラリは拡張子がなく、C++ のファイルとして認識されません。
これを解決する場合、次のようにファイルが開かれた時に自動で filetype=cpp を設定するとよいでしょう。
" 標準ライブラリへのパスを設定 let $CPP_STDLIB = "C:/MinGW/lib/gcc/mingw32/4.6.2/include/c++" augroup vimrc-set_filetype_cpp autocmd! " $CPP_STDLIB よりも下の階層のファイルが開かれて " filetype が設定されていない場合に filetype=cpp を設定する autocmd BufReadPost $CPP_STDLIB/* if empty(&filetype) | set filetype=cpp | endif augroup END
これで C:/MinGW/lib/gcc/mingw32/4.6.2/include/c++/iostream などを開いた場合に自動的に filetype=cpp が設定されます。
[素早くコメントアウトする]
コーディングを行っている際にカーソル行、もしくは複数行をコメントアウトしたい事は多々あると思います。
そういう場合は caw.vim を使用するとスムーズにコメントアウトを行うことができます。
- プラグイン
- 参考記事
- インストール
NeoBundle "tyru/caw.vim"
- 設定例
" コメントアウトを切り替えるマッピング " \c でカーソル行をコメントアウト " 再度 \c でコメントアウトを解除 " 選択してから複数行の \c も可能 nmap \c <Plug>(caw:I:toggle) vmap \c <Plug>(caw:I:toggle) " \C でコメントアウトの解除 nmap \C <Plug>(caw:I:uncomment) vmap \C <Plug>(caw:I:uncomment)
[任意の単語をハイライトする]
例えば、コードを読んでいる時に関数の引数やローカル変数がどこで使用されているのか確認したい場合があると思います。
そんな時に vim-quickhl を利用すれば素早く任意の単語のハイライトし、強調する事ができます。
- プラグイン
- 参考記事
- インストール
NeoBundle "t9md/vim-quickhl"
- 設定例
" <Space>m でカーソル下の単語、もしくは選択した範囲のハイライトを行う " 再度 <Space>m を行うとカーソル下のハイライトを解除する " これは複数の単語のハイライトを行う事もできる " <Space>M で全てのハイライトを解除する nmap <Space>m <Plug>(quickhl-manual-this) xmap <Space>m <Plug>(quickhl-manual-this) nmap <Space>M <Plug>(quickhl-manual-reset) xmap <Space>M <Plug>(quickhl-manual-reset)
[バッファのアウトラインを表示する]
unite-outline を使用すればバッファ内のアウトラインを表示する事ができます。
これを利用するとバッファ内の関数やクラスの一覧などが表示され、その位置にスムーズに移動する事ができます。
また unite-outline を利用するためには unite.vim が必要になります。
unite.vim については以下の記事を参照してみてください。
- プラグイン
- 参考記事
- インストール
NeoBundle "Shougo/unite.vim" NeoBundle "Shougo/unite-outline"
- 使用例
" アウトラインを出力したいバッファで起動 :Unite outline " 横に split して開きたい場合 " 幅は -winwidth= で指定 :Unite file -vertical -winwidth=40
unite-outline のオリジナル版は h1mesuke さんが作成されましたが今はメンテされていないので Shougo さんの fork 版を使用する必要があります。
また、unite-outline は C++ 以外の言語でも利用することが可能です。
[汎用的な補完を行う]
neocomplete.vim を使用するとバッファ内のワードやシンタックス、ファイル名など汎用的な補完を行うことができます。
以前は neocomplcache という名前で開発されていましたが neocomplete.vim では lua を使用することでより高速に補完が行われるようになりました。
その為、neocomplete.vim を使用する場合は Vim が +lua でビルドされている必要があります。
+lua が有効化どうかは
" 1 が返ってくれば +lua echo has("lua")
で確認する事ができます。
もし、+lua がない環境であれば代替として neocomplcache を利用する事もできます。
- プラグイン
- 参考記事
- インストール
NeoBundle "Shougo/neocomplete.vim"
- 設定例
" 補完を有効にする let g:neocomplete#enable_at_startup = 1 " 補完に時間がかかってもスキップしない let g:neocomplete#skip_auto_completion_time = ""
neocomplete.vim はオプションで細く設定を行うことができます。
既存の挙動が気になった場合は制御するオプションを探してみたりするとよいと思います。
詳しくは help などを参照して下さい。
普段は補完を使わないような方も1度試してみるとよいと思います。
[インクルードされているファイルの一覧を表示する]
neocomplete.vim と unite.vim の組み合わせるとバッファでインクルードされているヘッダーファイルの一覧を出力する事ができます。
また、これを利用する場合はインクルードディレクトリが 'path' に設定されている必要があります。
- プラグイン
- インストール
NeoBundle "Shougo/neocomplete.vim" NeoBundle "Shougo/unite.vim"
- 使用例
:Unite file_include
ただし、処理が重くなる為、深すぎる階層のヘッダーファイルは出力されないので注意して下さい。
[#include 時にファイル名の補完を行う]
neocomplete.vim を使用すれば #include 時にヘッダーファイル名の補完を行うことができます。
また、インクルードディレクトリが 'path' に設定されている必要があります。
- プラグイン
- インストール
NeoBundle "Shougo/neocomplete.vim"
- 使用例
#include <ここで補完が行われる
[高度なコード補完を行う]
Vim では Clang の構文解析器を利用したコード補完を行うプラグインがいくつか存在します。
例えば clang_complete や YouCompleteMe、そしてわたしが作成した marching.vim などがあります。
Clang の構文解析器を使用しているので Visual Studio のインテリセンス並に補完精度は高いです。
clang_complete の導入記事はいくつか存在するので、今回は marching.vim の設定方法を書いておきます。
- プラグイン
- 参考記事
- インストール
NeoBundle "osyo-manga/vim-marching"
- 使用方法
- 補完を行いたい位置で
を入力する
- 補完を行いたい位置で
- 設定例
" 非同期ではなくて同期処理で補完する let g:marching_backend = "sync_clang_command" " オプションの設定 " これは clang のコマンドに渡される let g:marching_clang_command_option="-std=c++1y" " neocomplete.vim と併用して使用する場合 " neocomplete.vim を使用すれば自動補完になる let g:marching_enable_neocomplete = 1 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*'
単体で使用する場合は手動で補完を行う必要があります。
自動補完を行いたい場合は neocomplete.vim を使用して下さい。
marching.vim は非同期補完にも対応していますが、依存するプラグインがいくつかあるので今回は同期版の設定方法のみ紹介しておきます。
気になる方は非同期版の方も試してみるとよいでしょう。
clang_complete と marching.vim の主な違いは以下の通りです。
- clang_complete では補完以外の機能(スニペット等)を提供している
- marching.vim は補完機能のみ提供
- clang_complete も marching.vim も補完中は入力がブロックされない
- ただし、上記の設定では marching.vim は入力をブロックする
- clang_complete は Vim に +python が必要
- marching.vim は +python は必要がないが他のプラグインに依存する
- 補完中に入力がブロックされても構わないのであれば単体で使用することも可能
- 補完速度は clang_complete の方が高速
- marching.vim は補完速度は早くはないが結果のキャッシングをしている為、同じ候補であれば即座に補完が行われる
- ただし、この時に使用するコンテキストは完璧ではないので誤爆する可能性はある
- これに関しては改善する予定
- 補完精度は clang_complete も marching.vim も同等
- marching.vim は Wandbox を使用したコード補完を実装
- これによりローカルマシンに Clang がなくても marching.vim が利用できる
- Wandbox との通信を行う必要があるのでは高速ではない
基本的には clang_complete の方が補完速度は高速なのですが、毎回補完が行われます。
marching.vim は補完速度は clang_complete よりも遅いですがキャッシングを行っているので同じ補完であれば2回目以降は即座に候補が表示されます。
ただ、使用してるコンテキストのパースが完璧ではないので誤爆する事はありますが…。
このあたりは今後改善したと思っています。
他には依存しているプラグインや Vim の組み込み機能ぐらいで補完の精度に関しては clang_complete も marching.vim も内部で Clang の構文解析器を使用しているので変わりません。
あと marching.vim は clang のコマンドを実行しているだけなのでランタイムライブラリを読み込んでいる clang_complete よりは安定しているとも言えます。
[スニペットを利用する]
コードを書いているとよく利用する定型文(if 文などの構文やヘッダーファイルに記述するインクルードガードなど)を挿入したいと思うことがあると思います。
そういう時に neosnippet.vim を使用すると定型文を簡単に挿入する事ができます。
予め設定しておいた定型文を利用することで無駄なコーディングやバグなどを防ぐ事ができます。
- プラグイン
- 参考記事
- インストール
NeoBundle "Shougo/neosnippet.vim" NeoBundle "Shougo/neocomplete.vim"
- 設定例
" スニペットを展開するキーマッピング " <Tab> で選択されているスニペットの展開を行う " 選択されている候補がスニペットであれば展開し、 " それ以外であれば次の候補を選択する " また、既にスニペットが展開されている場合は次のマークへと移動する imap <expr><Tab> neosnippet#expandable_or_jumpable() ? \ "\<Plug>(neosnippet_expand_or_jump)" \: pumvisible() ? "\<C-n>" : "\<TAB>" smap <expr><TAB> neosnippet#expandable_or_jumpable() ? \ "\<Plug>(neosnippet_expand_or_jump)" \: "\<TAB>" " 現在の filetype のスニペットを編集する為のキーマッピング " こうしておくことでサッと編集や追加などを行うことができる " 以下の設定では新しいタブでスニペットファイルを開く nnoremap <Space>ns :execute "tabnew\|:NeoSnippetEdit ".&filetype<CR> " スニペットファイルの保存ディレクトリを設定 let g:neosnippet#snippets_directory = "~/.neosnippet"
元々は neocomplcache に組み込まれていた機能なのですが、現在は独立して別のプラグインとなっています。
その為、単体でも使用することは可能ですが、neocomplete.vim と一緒に使用するとよいでしょう。
neosnippet.vim には各言語ごとに予めいくつかのスニペットが用意されています。
それをそのまま利用しても良いですし、自分で定義する事もできるのでよく利用するような定型文は予め登録しておくとよいでしょう。
:NeoSnippetEdit でスニペットファイルの編集を行う事ができます。
[バッファのコードを実行する]
以前は SingleCompile を使用していましたが今は quickrun.vim を使用しています。
quickrun.vim を使用するとバッファに書かれているコードを実行し、専用のバッファへと出力を行う事ができます。
- プラグイン
- 参考記事
- インストール
NeoBundle "thinca/vim-quickrun"
- 設定例
" 出力先 " 成功した場合:quickrun 専用の出力バッファ " 失敗した場合:quickfix を :copen で開く " バッファの開き方:botright 8sp " " cpp.type にて使用するコンパイラなどを設定する " cpp.cmdopt にコマンドラインオプションを設定 let g:quickrun_config = { \ "_" : { \ "outputter" : "error", \ "outputter/error/success" : "buffer", \ "outputter/error/error" : "quickfix", \ "outputter/buffer/split" : ":botright 8sp", \ "outputter/quickfix/open_cmd" : "copen", \ }, \ "cpp" : { \ "type" : "cpp/clang++", \ "cmdopt" : "-std=c++1y -ID:/home/cpp/boost/boost_1_55_0", \ }, \} " :QuickRun 時に quickfix の中身をクリアする " こうしておかないと quickfix の中身が残ったままになってしまうため let s:hook = { \ "name" : "clear_quickfix", \ "kind" : "hook", \} function! s:hook.on_normalized(session, context) call setqflist([]) endfunction call quickrun#module#register(s:hook, 1) unlet s:hook
- 使用例
" バッファのコードを実行する " この時に使用される設定は " g:quickrun_config[{バッファの filetype}] " になる :QuickRun
quickrun.vim は g:quickrun_config で挙動の制御や設定などを行う事ができます。
これにより g:quickrun_config では出力先や各コマンドのオプションなどの細かい設定を行う事ができます。
これで何かしら C++ のコードを検証したい場合など、素早くコンパイルし実行する事ができます。
実行結果なども専用バッファに出力されるのでコピペなども簡単に行うことができます。
[quickrun.vim で非同期でコンパイルを行う]
ご存知の通り C++ のコンパイルは boooooooost してしまう場合があります。
そういうコードを :QuickRun した場合、コンパイル中は Vim が止まってしまうので大変不便です。
そういう時は vimproc.vim を使用して非同期で :QuickRun を行う事ができます。
- プラグイン
- インストール
" vimproc.vim はプラグインの導入の他に C のコードをビルドする必要がある " 'build' を設定する事で自動的に vimproc.vim がビルドされる " また Kaoriya 版 Vim を使用している場合は vimproc.vim が同梱されているので " プラグインを導入する必要はない NeoBundle 'Shougo/vimproc.vim', { \ 'build' : { \ 'mac' : 'make -f make_mac.mak', \ 'unix' : 'make -f make_unix.mak', \ }, \ }
- 設定例
" 出力先 " 成功した場合:quickrun 専用の出力バッファ " 失敗した場合:quickfix を :copen で開く " バッファの開き方:botright 8sp " " コマンドの実行は vimproc.vim を使用する " runner に vimproc を設定 " runner/vimproc/updatetime には更新するタイミングを設定 " この値が大きいとコンパイルが終了していても " 結果が出力されるまでに時間がかかる可能性がある。 " " cpp.type にて使用するコンパイラなどを設定する " cpp.cmdopt にコマンドラインオプションを設定 let g:quickrun_config = { \ "_" : { \ "outputter" : "error", \ "outputter/error/success" : "buffer", \ "outputter/error/error" : "quickfix", \ "outputter/buffer/split" : ":botright 8sp", \ "outputter/quickfix/open_cmd" : "copen", \ "runner" : "vimproc", \ "runner/vimproc/updatetime" : 500, \ }, \ "cpp" : { \ "type" : "cpp/clang++", \ "cmdopt" : "-std=c++1y -ID:/home/cpp/boost/boost_1_55_0", \ }, \} " clear_quickfix は省略
- 使用例
" 既に g:quickrun_config へと設定しているので実行するコマンドはそのまま
:QuickRun
- コンパイルに失敗
- 成功
vimproc.vim はプラグインの導入の他に C のコードをビルドする必要があるので注意して下さい。
また neobundle.vim を使用していれば Windows 以外の環境は自動的にビルドを行うことができます。
vimproc.vim を導入するのは少し手間なのですが、他のプラグイン(e.g. neocomplete.vim wandbox.vim など)でも利用されているので予め導入しておくとよいと思います。
[quickrun.vim から wandbox を使用する]
最近話題の Wandbox を Vim から実行する wandbox.vim なのですがこれを quickrun.vim からも利用する事ができます。
wandbox.vim ではなくて quickrun.vim を利用する利点は出力先が自由だという事が大きいです。
例えば、バッファへと出力する事により結果のコピーなどを簡単に行うことができたり、実行結果が HTML 形式であればブラウザへと出力する事も可能です。
- プラグイン
- インストール
NeoBundle "rhysd/wandbox-vim" NeoBundle "thinca/vim-quickrun" NeoBundle 'Shougo/vimproc.vim', { \ 'build' : { \ 'mac' : 'make -f make_mac.mak', \ 'unix' : 'make -f make_unix.mak', \ }, \ }
- 設定例
" runner を wandbox に変更する " compiler や options などを個別に設定可能 " それ以外は今まで紹介した設定の挙動と同等 let g:quickrun_config = { \ "_" : { \ "runner" : "wandbox", \ "outputter" : "error", \ "outputter/error/success" : "buffer", \ "outputter/error/error" : "quickfix", \ "outputter/buffer/split" : ":botright 8sp", \ "outputter/quickfix/open_cmd" : "copen", \ }, \ "cpp" : { \ "runner/wandbox/compiler" : "clang-head", \ "runner/wandbox/options" : "warning,c++1y,boost-1.55", \ }, \} " clear_quickfix は省略
- 使用例
" 既に g:quickrun_config へと設定しているので実行するコマンドはそのまま
:QuickRun
[エラー箇所をハイライトしたり内容をコマンドラインに出力したり]
:QuickRun でコンパイルエラーになった場合に quickfix へと出力が行われます
この時にエラー箇所がハイライトされればわかりやすいですね。
vim-hier を導入すると quickfix の該当箇所が波線でハイライトされます。
NeoBundle "jceb/vim-hier"
[自動でシンタックスチェックを行う]
IDE などによくある自動シンタックスチェックを Vim でも行うことが可能です。
quickrun.vim と watchdogs.vim と shabadou.vim の3つのプラグインを使用します(あと vimproc.vim も。
watchdogs.vim は quickrun.vim を利用して非同期でシンタックスチェックを行います。
また、shabadou.vim は quickrun.vim の拡張プラグインになります。
- プラグイン
- 動作イメージ
- インストール
NeoBundle "thinca/vim-quickrun" NeoBundle "osyo-manga/vim-watchdogs" NeoBundle "osyo-manga/shabadou.vim"
- 設定例
" watchdogs.vim は g:quickrun_config でコマンドの制御を行う " cpp/watchdogs_checker.type に使用するコンパイラやコマンドを設定する " watchdogs_checker/g++ などでコマンドオプションを設定 let g:quickrun_config = { \ "_" : { ...省略... \ }, \ \ "cpp/watchdogs_checker" : { \ "type" : "watchdogs_checker/clang++", \ }, \ \ "watchdogs_checker/g++" : { \ "cmdopt" : "-Wall", \ }, \ \ "watchdogs_checker/clang++" : { \ "cmdopt" : "-Wall", \ }, \} " ファイルの保存時にチェックを行う let g:watchdogs_check_BufWritePost_enable = 1
watchdogs.vim は quickrun.vim を利用してシンタックスチェックを行っています。
その為、コマンドの設定などは quickrun.vim と同様に g:quickrun_config に設定を記述します。
quickrun.vim を利用して非同期で実行を行っているのでチェックを行っている間は Vim の入力がブロックされずにコーディングしながらチェックを行うことが可能です。
設定方法は g:quickrun_config を活用しているので少し癖がありますが、細かい部分まで制御することが可能です。
設定方法などは watchdogs.vim の help を参照してみてください。
[まとめ]
と、言うことで普段コーディングしている時に使用しているいくつかの機能やプラグインなどを紹介してみました。
C++ あんまり関係なかった
前回と比べると本質的にはそこまで変わっていませんが使用しているプラグインなどはガラッと変わりましたね。
Vim は日々進化し続けています。
C++ には優秀な IDE も多く存在するのですが、やはり Vim の操作性や拡張性に取り憑かれると他の環境には戻れませんね。
近年では Clang という素晴らしい構文解析ツールの登場で Visual Studio のインテリセンスに引けをとらないコード補完を実現する事もできます。
Clang は素晴らしい。
ただ、IDE と比べるとどうしてもデバッグ面で劣ってしまうので、そのあたりは今後何かしら対応したいですね。
来年こそデバッグ用のプラグインつくるんだ、俺。
Vim の環境に関して言えば他には operator や textobj なども積極的に使用するようにもなりました。
今回は紹介できませんでしたが Vim を使用しているのであればこれらの機能はとても強力なので使ってみるとよいと思います。
また、本記事や Vim でわからないことがあれば Lingr の Vim 部屋によく出没するので気軽にご質問下さい。
答えられる範囲であれば受け答えします。
あと今回紹介しいたプラグインや設定などを1つの vimrc にまとめて github で公開してみました。
ぶっちゃけだいぶ突貫工事で作ったので不完全な部分があると思いますが、何かの参考になればと思います。
一部の設定をコピペするなり改造するなり好きに使って下さい。
需要があればもりもり改良していくかもしれない。
[動作イメージ]
最後にキャプチャした動画を載せておきます。
だいたいの雰囲気を感じてもらえればと。