VimでC++のコードリーディング


この記事は [初心者C++er Advent Calendar 2017](https://qiita.com/advent-calendar/2017/syoshinsya-cpper)、20日目の記事です。

## **VimでC++のコードリーディング**

私はC++の開発にVimを使用しています。
IDEにはさすがに負けるかもしれませんが、Vimでも結構快適にコードを読むことができます。

以下にVimを使用してC++のコードリーディングを行う際のTipsについて記載していきたいと思いますー。

## 環境
Windows ７

VIM - Vi IMproved 8.0 (2016 Sep 12, compiled May  2 2017 12:56:41)
MS-Windows 64 ビット GUI 版
適用済パッチ: 1-596


## デフォルトで使用できるキーマップ

多用するのは以下です。

|map |説明|
|---|----------------------|
|[# |カーソルが`#if-#endif`内ならば`#if` or `#else`にジャンプ|
|]# |カーソルが`#if-#endif`内ならば`#else` of `#endif`にジャンプ|
|gd |変数の定義位置にジャンプ(大抵うまくジャンプできるらしい)|
|gD |変数(global)の定義位置にジャンプ(大抵うまくジャンプできるらしい)|
|[[ |一桁目が`{`の行(大抵関数の開始位置(?))にジャンプ |
|]] |一桁目が`}`の行(大抵関数の終了位置(?))にジャンプ |
|[*  or	[/|C コメントの始まり(`/*`)に移動します。 |
|]*  or	]/|C コメントの終わり(`*/`)に移動します。 |
|gf | カーソル下のファイルを開く(環境によって`path`の設定が必要です)
|\<C-^\> or \<C-6\>| 直前に開いたファイルを開く(厳密には違います。詳細は`:h CTRL-6`)|

このあたりのマップは実はC++用ではなくC言語用のマップかもしれません。
ただ、C++でも問題なく使用できます。

## 定義位置ジャンプ、参照元ジャンプ

`ctags`と`cscope`を使用します
Windowsの場合にctagsは[ここ](https://github.com/universal-ctags/ctags-win32/releases)から、cscopeは[ここ](https://code.google.com/archive/p/cscope-win32/)からバイナリをダウンロードできます。
ダウンロード後はパスが通っている場所に配置すればよいだけ。
あとは以下のような設定をvimrc内に記載して、少し便利にします。
(一部[vim-submode](https://github.com/kana/vim-submode)というプラグインを使用して、\<C-t\>と逆方向への移動を楽にしてます。)

``` vim
" cscope show/killをマッピング
nnoremap <Leader>zs :<C-u>cscope show<CR>
nnoremap <Leader>zk :<C-u>cscope kill -1<CR>

" :cscope find c <関数名> " 指定した関数を呼び出している関数を検索
" :cscope find f <ファイル名> " ファイルを検索
" :cscope find s <Cのシンボル> " シンボルを検索
" 最後に指定する文字を大文字にするとクリップボード内の文字列を使用して:cscope findを実行します。
" NOTE: @+ についてはclipboardの設定により、適宜変更してください
nnoremap <Leader>zfc :<C-u>cscope find c <C-r>=expand('<cword>')<CR><CR>
nnoremap <Leader>zfC :<C-u>cscope find c <C-r>=@+<CR><CR>
nnoremap <Leader>zfs :<C-u>cscope find s <C-r>=expand('<cword>')<CR><CR>
nnoremap <Leader>zfS :<C-u>cscope find s <C-r>=@+<CR><CR>
nnoremap <Leader>zff :<C-u>cscope find f <C-r>=expand('<cfile>')<CR><CR>
nnoremap <Leader>zfF :<C-u>cscope find f <C-r>=@+<CR><CR>

" 定義位置へのジャンプ時に複数候補表示
" クリップボード内の文字列使って:tjump
nnoremap <C-]> g<C-]>
nnoremap g<C-]> :<C-u>tjump <C-r>=@+<CR><CR>

" <C-t>と逆方向への移動をg<C-t>tttt..とできるよう設定
" NOTE: <C-t>は定義への移動前にいた場所にジャンプ。
" NOTE: vimデフォルトだと<C-t>の逆方向への移動用マップはない(<C-o>で逆移動っぽい動作はするが、少し違う)ので、定義
call submode#enter_with('tag', 'n', '', 'g<C-t>', ':<C-u>tag<CR>')
call submode#map('tag', 'n', '', 't', ':<C-u>tag<CR>')


```

私の場合にはSVNの各ブランチのルートディレクトリ(`.svn` or `.git`ディレクトリがある場所)でctags, cscopeを実行したかったので、
以下のような設定も追加しています。

``` vim

" 特定のファイルを開いた場合にはカレントディレクトリをルートディレクトリに変更
function! s:root_dir() abort
  let search_dir_names = ['.svn', '.git']
  for dir_name in search_dir_names
    let ret_dir = finddir(dir_name, expand('%:p:h') . ';')
    if ret_dir != ''
      return substitute(fnamemodify(ret_dir, ':p:h'), dir_name . '$', '', 'g')
    endif
  endfor

  return ''
endfunction
augroup cd_project_root
  autocmd!
  autocmd BufEnter *.cpp,*.h,*.c, silent execute 'lcd' s:root_dir()
augroup END

" do ctags
function! s:do_ctags() abort
  if isdirectory('.svn') || isdirectory('.git')
    if input('now: ' . getcwd() . "\nrun ctags? (y)es or (n)o : ") =~ '^y$\|^yes$'
      silent !start ctags -R
    endif
  else
    echohl ErrorMsg | echo 'check current dir!' | echohl none
  endif
endfunction

" cscope build/add
function! s:cscope_build() abort
  if isdirectory('.svn') || isdirectory('.git')
    if input('now: ' . getcwd() . "\nrun cscope -Rb? (y)es or (n)o : ") =~ '^y$\|^yes$'
      silent !start cscope -Rb
    endif
  else
    echohl ErrorMsg | echo 'check current dir!' | echohl none
  endif
endfunction
function! s:cscope_add() abort
  if (isdirectory('.svn') || isdirectory('.git')) && filereadable('cscope.out')
    cscope add cscope.out
    echo "cscope add cscope.out!"
  else
    echohl ErrorMsg | echo 'check current dir!' | echohl none
  endif
endfunction

" ctags, cscope build/add用のマップ
nmap <Leader>ct :<C-u>call <SID>do_ctags()<CR>
nmap <Leader>zb :<C-u>call <SID>cscope_build()<CR>
nmap <Leader>za :<C-u>call <SID>cscope_add()<CR>

```


## cscopeについて

記事を書いていく中で知ったのですが、cscope[はC言語用のパーサとして作られている](http://cscope.sourceforge.net/)ようですね。。
c++でも大抵動くらしいですが、以下のような記載もあり油断は禁物らしいです。
> The fuzzy parser supports C, but is flexible enough to be useful for C++ and Java, and for use as a generalized 'grep database' 

現状は[rtags](https://github.com/Andersbakken/rtags)というのが流行っているそうですね。
どなたかWindowsでrtagsを使う方法をまとめてください(切実)


## まとめ
いかがだったでしょうか？
Vimをつかえば設定しなくとも快適にコードを読めますし、設定すれば更に快適にコードを読むことができます！
~~他のIDEの使い方がわからないだけとは言えない~~

また、ここでは紹介しませんが、Vimのプラグインである[unite.vim](https://github.com/Shougo/unite.vim)のmru, bookmark, file機能をつかえば
ファイルを開くまでの時間を短縮できますし、[unite-outline](https://github.com/Shougo/unite-outline)を使用すれば
ファイル内の定義の一覧を常に表示でき、VimをIDEっぽくすることもできます。

皆様もいろいろ試してみて、Vimで快適なコードリーディングを行ってみましょー！
**Happy C++ Life!**