クールでモダンなVim導入備忘録

# はじめに
こんにちは。私しがないエンジニアをしております。
今回は、業務をする中でだんだんトラックパッドを使うのが面倒になり、Vimmerへの道を歩み始めた著者の`Vim`(`neovim`)環境構築の備忘録(一部始終)です。
「これから`Vim`を使おうか」、「`Vim`ってどんなことができるんだい」って思う人は是非読み進めていただきたいです。

完成図はこんなイメージです(今の僕の`iTerm2`上の`Vim`エディタです)。
![Screen Shot 2021-12-29 at 0.03.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676004/901f18c6-ccab-ce0b-c399-cfb35c10d519.png)


# 目次

1. neovimの導入
2. dein.vimの導入
3. Vimの設定
4. Vimプラグインのインストール   
番外編: エラー対処と使用言語のLSP設定    
・エラー対処    
・Ruby    
・C++

# 前提(導入時の自分の環境)

- 環境
・MacBook Air (M1, 2020)
・Big Sur 11.6.1

- `HomeBrew`を使用
- `iTerm2`にてPowerlevel10kを使用している(エディタにアイコンなど特殊文字を表示させる設定が追加で必要かと)

# 1. neovimの導入
`neovim`というのは、`Vim`をベースとしたテキストエディタとのことです。
前提にも書きましたが著者は`HomeBrew`を愛用しているので、`neovim`のインストールも`HomeBrew`を使用していきます。
コマンドは至極簡単で、

```
brew install neovim
```

です。
インストールが終われば、`nvim --version`を打ってインストールがされているか確認しましょう。
そして、インストールができれば`vi`コマンドで`neovim`が使えるように`.zshrc`にエイリアスを書き足します。

```:~/.zshrc
alias vi="nvim"
```

そして、`neovim`は起動時の挙動を設定するために`init.vim`というファイルが利用できるので、そのファイルを作成してあげます。

```
mkdir -p ~/.config/nvim
touch ~/.config/nvim/init.vim
```

# 2. dein.vimの導入
`Vim`にはプラグインが多数存在していますが、プラグインをたくさん導入すればするほどその管理はスパゲッティのように複雑になるようです。先輩Vimmerの方々は、プラグイン管理のためにプラグインマネージャーを使っているようで、`dein.vim`は有名な`Vim`プラグインマネージャーの1つです。
インストール方法は以降で紹介しますが、[本家のリポジトリ](https://github.com/Shougo/dein.vim)のリンクも貼っておきます。

本来なら`git clone`をしないといけないのですが、今回は自動的に`dein.vim`が追加されるように設定していきます。

1. `~/.config/nvim/init.vim`内に下記コードをペースト
1. `nvim`を起動(以下`vi`コマンドは`nvim`コマンド実行と同じ扱い)

> 編集(2021/12/31): `/.cache/dein`ディレクトリはインストール時に自動生成されるので作成不要です。

```:~/.config/nvim/init.vim
" インストールディレクトリの指定
let s:dein_dir = expand('~/.cache/dein')
let s:dein_repo_dir = s:dein_dir . '/repos/github.com/Shougo/dein.vim'
let s:toml_file = expand('~/.config/nvim/dein') . '/plugins.toml'
let s:toml_dir = expand('~/.config/nvim/dein/plugins')
if match( &runtimepath, '/dein.vim' ) == -1
	if !isdirectory(s:dein_repo_dir)
		execute '!git clone https://github.com/Shougo/dein.vim' s:dein_repo_dir
	endif
	execute 'set runtimepath+=' . fnamemodify(s:dein_repo_dir, ':p')
endif
" dein の設定
if dein#load_state(s:dein_dir)
	call dein#begin(s:dein_dir)
  " 各プラグインのtomlを読み込む
	call dein#load_toml( s:toml_dir . '/init_plugin.toml', {'lazy': 0} )
	call dein#load_toml( s:toml_dir . '/lazy_plugin.toml', {'lazy': 1} )
  call dein#end()
	call dein#save_state()
endif
" 各プラグインのインストールチェック（なかったら自動的に追加される）
if dein#check_install()
	call dein#install()
endif
```

細かいコードの意味等は解説を控えますが、`Vim`を再起動すれば自動でインストールされます。

# 3. `Vim`の設定
ここからが本題です。そして、ここからが`Vim`の本領発揮する場面であり、沼であり、自分の知識がまだまだ足りていない箇所になります。

それが`Vim`の設定です。

ここまで色々インストールしてきて、モダンなIDEみたいになったと思ったら大きな勘違い。
まだまだ行番号すらないような、見づらい真っ黒い画面のままです。

そこで、先程の起動時のファイル`init.vim`に書き込みます。個人的には`init.vim`が長くなりすぎるのは嫌なので、`init_config.vim`という別ファイルを作成して設定を書き加えていきます。

`Vim`の設定は細かくでき、キーマップなども自分で設定することができますが、今回は自分も`Vim`を使って日が浅いということでキーマップの紹介は辞めておきます。

`init_config.vim`内で使用している設定は以下のものです。
設定を書き終えたら、`init.vim`内に`source ~/.config/nvim/init_config.vim`を書いて設定を読み込ませるのを忘れないようにしましょう。

```:init_config.vim
" #####表示設定#####
set number "行番号を表示する
set title "編集中のファイル名を表示
set showmatch "括弧入力時の対応する括弧を表示
set expandtab "タブ文字の代わりにスペースを使う
set splitbelow  " 水平分割時に下に表示
set splitright  " 縦分割時を右に表示
set ruler " カーソルの位置表示
set cursorline " カーソルハイライト
syntax on "コードの色分け
set tabstop=4 "インデントをスペース4つ分に設定
set smartindent "オートインデント
set laststatus=2 "常にステータスを表示
set autoindent "改行時に自動的にインデントを適用
set list "タブ、空白、改行の可視化
set showcmd "入力中のコマンドを表示

" #####検索設定#####
set ignorecase "大文字/小文字の区別なく検索する
set smartcase "検索文字列に大文字が含まれている場合は区別して検索する
set wrapscan "検索時に最後まで行ったら最初に戻る
set hlsearch "検索結果をハイライト表示
set incsearch
set wildmenu "コマンドラインモードの補完を見やすく

```

# 4. Vimプラグインのインストール
2でインストールした`dein.vim`を用いて、Vimプラグインをインストールしていきます。
僕は`toml`ファイルを活用してプラグインをインストールしているのでその方法を紹介します。

先程の`init.vim`ファイルのこの記述をご覧ください。

```:init.vim
let s:toml_dir = expand('~/.config/nvim/dein/plugins')
...中略
" 各プラグインのtomlを読み込む
	call dein#load_toml( s:toml_dir . '/init_plugin.toml', {'lazy': 0} )
	call dein#load_toml( s:toml_dir . '/lazy_plugin.toml', {'lazy': 1} )
```

解説すると、

- `~/.config/nvim/dein/plugins`ディレクトリを`toml`ファイルを置くフォルダとして定義しています
- `~/.config/nvim/dein/plugins`ディレクトリにプラグインの`toml`ファイルを設置しています
- `init_plugin.toml`と`lazy_plugin.toml`でプラグインを分類しています
- `init_plugin.toml`はVim起動時に機能して欲しいプラグインになります(`{'lazy':0}`で設定)
- `lazy_plugin.toml`は遅延ロードを適用したいプラグインで、特定のタイミングにVimに起動してほしいプラグインです(`{'lazy':1}`で設定). 

    => 遅延ロードは、`Ruby`用のプラグインだから拡張子が`.rb`のファイルの時だけ起動してほしいな、というように`Vim`起動後にロードされるものになります(起動時だと拡張子関係なく無差別にロードされます)。

さっそくIDEライクな機能にするためにプラグインをインストールしていきます。

#### 1. `~/.config/nvim/dein/plugins`配下に`toml`ファイルを配置する
ディレクトリの場所についてですが、`let s:toml_dir`で設定できるので、任意の場所に設定してください。
また、`init_plugin.toml`という風に一つのファイルに複数のプラグインをまとめて書くのではなく、`{plugin_name}.toml`という風にプラグインごとにファイルを作成するという方もいらっしゃるので自分の好きな方法をお使いください

#### 2. `toml`ファイル内に使いたいプラグインを書き足していく
`toml`ファイル内での基本的な設定方法は以下の通りです。

```
[[plugins]]
repo = 'ユーザ名/リポジトリ名'
hook_add = '''
  プラグインの個別設定を記述
'''
```

プラグインによってはレポジトリの設定や、依存関係の設定も必要なので参考程度に書いておきます([引用記事](https://leico.github.io/TechnicalNote/VimR/VimR-dein-toml))。

```
# ブランチやタグを指定したいとき
[[plugins]]
repo = 'delphinus35/typescript-vim'
rev  = 'colorize-template-strings'

# 特定の条件で読み込みたいとき
[[plugins]]
repo = 'elzr/vim-json'
if   = '''! has('kaoriya')'''

# 依存関係を指定したいとき
[[plugins]]
repo    = 'vim-airline/vim-airline'
depends = ['vim-airline-themes']

# 依存関係を指定したからと言って、自動でインストールはされない（ここは NeoBundle と違う）
[[plugins]]
repo = 'vim-airline/vim-airline-themes'
```

そして、こちらが最上部に掲載した写真のようにするのに僕が使用しているプラグインになります(一部おすすめプラグインを入れています)。

<details><summary><u>**各プラグインのレポジトリ一覧**</u></summary><div>

- [neoclide/coc.nvim](https://github.com/neoclide/coc.nvim)
IDEのような補完を可能にするプラグインです。
![coc.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676004/7734ffaf-0722-b550-203e-b23dc169266e.gif)

- [junegunn/fzf.vim](https://github.com/junegunn/fzf.vim)
ファイル検索をするのに必要なプラグインです。
![fzf.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676004/d3ec3154-3589-6636-8115-99c2ae9944dd.png)

- [preservim/nerdtree](https://github.com/preservim/nerdtree)
ディレクトリをツリー構造で表示、ファイルの追加・編集・削除を可能にするプラグインです。
![nerdtree.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676004/a94ff13a-af0c-23c7-f00e-bb0b59c6f5b4.png)

- [vim-airline/vim-airline](https://github.com/vim-airline/vim-airline)
`Vim`エディタ下部にブランチ名などを表示させるプラグインです。
![airline.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676004/f43dff4d-1ec0-9ffd-09a3-db82e02affb8.gif)

- [nathanaelkane/vim-indent-guides](https://github.com/nathanaelkane/vim-indent-guides)
インデントを可視化させるプラグインです。
![indent.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676004/c859a15f-af0f-6467-2c2f-758d6bb513b9.png)

- [luochen1990/rainbow](https://github.com/luochen1990/rainbow)
![rainbow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676004/7160e017-48b3-17bf-b5d7-d26cb99e4678.png)

- [simeji/winresizer](https://github.com/simeji/winresizer)
分割したウインドウのサイズを簡単に調整できるようになるプラグインです。
![winresize.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/676004/ada0e3f1-3b55-4374-90ab-88057a8783aa.gif)


</div></details>

```:init_plugin.toml
# vim-airlineと依存関係にあるプラグイン(後述に詳細記述)
[[plugins]]
repo = 'tpope/vim-fugitive'

# 言語ごとのLSPを使用可能にする
[[plugins]]
repo = 'neoclide/coc.nvim'
rev = 'release'
on_i = 1
merged = 0

# ファイル検索用
[[plugins]]
repo = 'junegunn/fzf'
hook_post_update = './install --all'
merged = 0

[[plugins]]
repo = 'junegunn/fzf.vim'
depends = 'fzf'
hook_add = '''
        command! -bang -nargs=* Rg
        \ call fzf#vim#grep(
        \ 'rg --column --line-number --hidden --ignore-case --no-heading --color=always '.shellescape(<q-args>), 1,
        \ <bang>0 ? fzf#vim#with_preview({'options': '--delimiter : --nth 4..'}, 'up:60%')
        \ : fzf#vim#with_preview({'options': '--delimiter : --nth 4..'}, 'right:50%:hidden', '?'),
        \ <bang>0)
        nnoremap <C-g> :Rg<Space>
        nnoremap <silent> FF :Files<CR>
        nnoremap <silent> HH :History<CR>
'''

# IDEのようにディレクトリをツリー構造で表示させる
[[plugins]]
repo = 'preservim/nerdtree'
depends = ['Xuyuanp/nerdtree-git-plugin', 'ryanoasis/vim-devicons']
hook_add = '''
        nmap <C-f> :NERDTreeToggle<CR>
        let g:airline#extensions#tabline#enabled = 1
        autocmd vimenter * NERDTree
'''

# 上記NerdTREEプラグインにgit管理状態を表示させる
[[plugins]]
repo = 'Xuyuanp/nerdtree-git-plugin'

# 上記NerdTREEプラグインに言語ごとにアイコンを表示させる
[[plugins]]
repo = 'ryanoasis/vim-devicons'

# 画面下部のステータスに編集ファイル、ブランチ名、エラーなどをかっこよく表示させる
[[plugins]]
repo = 'vim-airline/vim-airline'
depends =['vim-airline/vim-airline-themes', 'tpope/vim-fugitive']
hook_add = '''
        let g:airline_powerline_fonts = 1
        let g:airline#extensions#tabline#enabled = 1
        let g:airline#extensions#tabline#buffer_nr_show = 1
        let g:airline#extensions#branch#enabled=1
        let g:airline#extensions#hunks#enabled=0
        if !exists('g:airline_symbols')
                let g:airline_symbols = {}
        endif
        let g:airline_section_a = airline#section#create(['mode', 'crypt'])
'''

# 上記airlineのテーマを設定する
[[plugins]]
repo = 'vim-airline/vim-airline-themes'
hook_add = '''
        let g:airline_theme = 'powerlineish'
'''

# インデント表示を可視化
[[plugins]]
repo = 'nathanaelkane/vim-indent-guides'
hook_add = '''
        let g:indent_guides_enable_on_vim_startup = 1
'''

# vimエディタのテーマにSolarizedを使用する
[[plugins]]
repo = 'altercation/vim-colors-solarized'

# 直近のコミットからどの行に変更があったかを表示
[[plugins]]
repo = 'airblade/vim-gitgutter'
hook_add = '''
        set signcolumn=yes
        let g:gitgutter_async = 1
        let g:gitgutter_sign_modified = 'rw'
        highlight GitGutterAdd ctermfg=green
        highlight GitGutterChange ctermfg=yellow
        highlight GitGutterDelete ctermfg=red
        highlight GitGutterChangeDelete ctermfg=yellow
'''

# コメントアウトを簡単にする
# ビジュアルモード時に"?"を入力したら指定範囲がコメントアウト
[[plugins]]
repo = 'tomtom/tcomment_vim'
hook_add = '''
    vnoremap ? :'<,'>TComment<CR>
'''

# {},[]などカッコが色分けされる
[[plugins]]
repo = 'luochen1990/rainbow'
hook_add = '''
        let g:rainbow_active = 1
'''

# ウインドウのリサイズが簡単になる
[[plugins]]
repo = 'simeji/winresizer'
hook_add = '''
        let g:winresizer_vert_resize = 1
        let g:winresizer_horiz_resize = 1
'''
```

> 編集(2021/12/31): `fzf`を使用する際には、`HomeBrew`で`fzf`をインストールしておく必要があります。ですので、上記`toml`ファイルへの追加に加えて、`brew install fzf`も実行しておく必要があります。

あとは`Vim`を再起動すればプラグインがインストールされて、`Vim`がIDEライクなビジュアルになるはずです。

---

### 番外編
#### エラー対処まとめ

- `vim-airline`導入したけどブランチ名が表示されない
こちら最近(2021年12月)導入した自分で発生したので、これから挑戦しようと思われる方は同じものに遭遇されるかもしれません。
以下の順番で試してもらえれば解決するかと思います。

**1. `vim-fugitive`をインストールする**
こちら`vim-airline`と依存関係にあるプラグインです。
重要なのは、`vim-airline`の記述よりも上部に`vim-fugitive`の記述を書くことです。

```:tomlファイル
# vim-airlineよりも上部にvim-fugitiveを記載
[[plugins]]
repo = 'tpope/vim-fugitive'

# dependsにvim-fugitiveを記述
[[plugins]]
repo = 'vim-airline/vim-airline'
depends =['vim-airline/vim-airline-themes', 'tpope/vim-fugitive']
```

**2. `vim-airline`を最新のコミットからチェックアウトする**
まず、`.cache/dein/repos/github.com/vim-airline/vim-airline`に移動してください。
インストールして特別なことをしていない限り、`master`ブランチの最新コミットになっていると思うので、以下コマンドを打って、前のコミットに戻ってください。

```
git checkout HEAD~1
```

自分は上記2つを組み合わせたらブランチ名が表示されるようになりました。

#### Ruby環境構築

<details><summary>**`coc.nvim`を用いた各言語用の拡張機能インストール方法(`coc.nvim`の使い方がわからない場合、先にこちらをお読みください)**
</summary><div>

基本的に補完は上記`coc.nvim`を用いたものを使っています。
まず、`coc.nvim`の設定方法について紹介していきます。
方法は`json`ファイルを使った方法と、`:CocInstall`コマンドを使った二つの方法があるのでそれぞれ紹介していこうと思います(おそらく両方利用すると思います)。

言語の対応状況はこちらの[公式ドキュメント](https://github.com/neoclide/coc.nvim/wiki/Language-servers)をご覧ください。

- `json`ファイルを使った場合
    1. `~/.config/nvim`配下に`coc-settings.json`ファイルを作成
    2. 任意の言語に対応した記述を追加

- `CocIntall`コマンドを使った場合
    1. 任意の言語に置き換えて`:CocInstall {coc_lsp_name}`を打つ

以上です。
`:CocList extensions`でインストールされているか一覧が表示されるので確認しましょう。


</div></details>

`coc.nvim`では`solargraph`を使用しているのでそれを使います。
加えて、`if`などの終了を示す`end`を自動で追加してもらいたいので、`vim-endwise`を遅延ロードさせます。

1. `gem install solargraph`で`solargraph`自体をOSにインストールする
1. `:CocInstall coc-solargraph`で`coc.nvim`の拡張機能に追加する
1. 遅延ロードを設定した`toml`ファイル内に以下のコードを追加する
    
    ```:lazy_plugin.toml
    [[plugins]]
    repo = 'tpope/vim-endwise'
    on_ft = ['ruby']

    ```
以上で、`Vim`エディタ内で補完機能が有効化されているはずです。

#### C++環境構築
競プロにより本格的に取り組みたいと思い、最近`C++`を学び始めました。
まだまだ改善の余地はありますが、少なくともコードフォーマット、補完、という最低限のことはできる環境は構築できたので、参考程度にご覧ください(自分自身の備忘録という意味合いが大きいです)。

`coc.nvim`の`ccls`を使用し、且つフォーマット用に`clang-format`を使用します

1. `brew install clang-format ccls`を入力
2. `Vim`起動時の設定ファイルに以下を追加(ファイル保存時に自動フォーマット)

    ```
    "C++のコードフォーマッタの自動実行
    function! s:clang_format()
      let now_line = line(".")
      exec ":%! clang-format"
      exec ":" . now_line
    endfunction

    if executable('clang-format')
      augroup cpp_clang_format
        autocmd!
        autocmd BufWrite,FileWritePre,FileAppendPre *.[ch]pp call s:clang_format()
      augroup END
    endif
    ```
3. `coc-settings.json`に以下を追加

    ```
    "ccls": {
          "command": "ccls",
          "filetypes": ["c", "cc", "cpp", "c++", "objc", "objcpp"],
          "rootPatterns": [".ccls", "compile_commands.json", ".git/", ".hg/"],
          "initializationOptions": {
              "cache": {
               "directory": "/tmp/ccls"
              }
            }
        }
    ```

以上で、コード補完とフォーマットが`C++`環境で機能するはずです。

# おわりに
まだまだ`Vim`を使って日が浅いですが、`Vim`に慣れるともう抜け出せないのだろうな、というのは感じています。
この記事を読んで`Vim`入門のきっかけとなれば嬉しいです。

間違えている箇所があればコメントいただけると嬉しいです！

拙い文章でしたが、最後までご覧いただきありがとうございました!!