C++開発環境としての(neo)vim :LSPを使おう

イントロ

C++開発環境としてVimを使って、コード補完、リンティング、フォーマット、スニペットなどの機能を追加していく方法を紹介する。

問題点

IDE勢だった自分的にはやはりVimIDEのような機能を提供するプラグインはほしい。YouCompleteMeのようなプラグインを導入するのがよいだろう。しかし設定は複雑だ。そんなときは以下のようにするといい。

解決策 : language server protocol

イデアは、ルールのセットを作成し、サーバーとルールに従うクライアントを実装し、プロトコルを介して通信し、自動保管なような機能性を提供することができるThe language server protocol (LSP)を使うことだ。定義にJump、検索機能の呼び出しなどができるようになる。

langserver.org goもつかえるよ!

セットアップ

サーバーはホストシステムにインストールする必要があり、クライアントはエディタがプラグインまたはビルドインとして提供する必要がある。Vimではプラグインでなければならないが、NeoVimでは実験的にサポートしている。CLionのようないくつかのIDEは、両方をパッケージ外で提供している。

GitHub - neovim/nvim-lspconfig: Collection of (useful, but unsupported) configurations for the Nvim LSP client

server

C++ のために活発に開発されている言語サーバは clangd と ccls の 2 つ。私は clangd の方がインストールが簡単。

apt-add-repository 'deb http://apt.llvm.org/buster/ llvm-toolchain-buster-11 main'

wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -

apt-get update

apt-get install -qq clang-11 clang-tools-11 clang-11-doc libclang-common-11-dev \
   libclang-11-dev libclang1-11 clang-format-11 clangd-11 clang-tidy-11 \
   libc++-11-dev libc++abi-11-dev

とかやればok さて、clangdと一緒に、最新の安定したC++コンパイラ、標準ライブラリの実装(言語に追加された最新の機能を楽しむことができる)、リンタ、コードフォーマッタを手に入れた。サーバは手に入れたが、クライアントはどうする?

というわけで、

client

clangdを利用するには、言語クライアント/LSPが必要です。様々な実装が用意されていますが、とりあえずcoc.nvimを使う。TypeScriptで書かれているのでNode.jsのランタイムが必要だ。

https://github.com/neoclide/coc.nvim/

apt-get install npm

coc.nvimなどのプラグインを簡単に追加するには、プラグインマネージャーをつかうとよい。vim-plugを使えば

call plug#begin('~/.vim/plugged')

Plug 'neoclide/coc.nvim', {'for':['zig','cmake','rust',
     \'java','json', 'haskell', 'ts','sh', 'cs',
     \'yaml', 'c', 'cpp', 'd', 'go',
     \'python', 'dart', 'javascript', 'vim'], 'branch': 'release'}

call plug#end()

プラグインはplug#beginとplug#endの間にPlug 'Github_URI_path'としてリストアップされ、プラグインが特定のファイルタイプで動作するようにするものでforの後に表示されます。私はこのプラグインを.txt, .mdなどの他のファイルタイプで動作させる必要はない。

coc.nvimはサーバの設定をcoc-settings.jsonというJSONファイル( :CocConfigと一緒に)に格納します。このファイルを使ってサーバの動作をカスタマイズすることができる。全てのスキームはここから確認できる。とりあえず、言語サーバを登録して、他はデフォルトのままにしておく。

{
  "languageserver":{
    "clangd":{
      "command":"clangd",
      "filetypes":[
        "c",
        "cpp"
      ],
      "rootPatterns":[
        "compile_commands.json",
        ".git"
      ],
      "args":[
        "--compile-commands-dir=build",
        "--compile_args_from=filesystem",
        "--all-scopes-completion",
        "--background-index",
        "--clang-tidy",
        "--completion-parse=always",
        "--completion-style=detailed",
        "--cross-file-rename",
        "--function-arg-placeholders",
        "--header-insertion-decorators",
        "--query-driver=/usr/bin/**/clang-*,/home/adem/GCC-10/bin/g++*",
        "--header-insertion=never",
        "--cross-file-rename",
        "--limit-results=0",
        "-j=6",
        "--pch-storage=memory",
      ]
    }
  }
}

args配列のパラメータを指定してclangdを呼び出します。clangdのすべてのオプションはclangd --helpで確認可能だ。

languageserverオブジェクトには何台でもサーバーを追加することができ、同じエディター上で異なる言語の編集経験、マッピング、テーマなどを同じにすることができます。

{
  "languageserver":{
    "clangd":{
       // clangd options
     }, 
    "rls" {
      // rls options
    },
    "bash-lsp" {
      // bash-lsp options
    }
  }
}

マッピングVimでは再割り当て可能なショートカットに使われる用語)については、設定例から始めるべきだ。見たい方は、このバカがGithubで設定を公開しています。

これでサーバーとクライアントができましたが、もう一つ必要なものがある。

compile_commands.jsonファイル生成

式の最後にはCMakeを使います。必要なのは、トップレベルのCMakeLists.txtに一行の定義を追加することだけです。

set(CMAKE_EXPORT_COMPILE_COMMANDS ON) ビルドディレクトリに compile_commands.json というファイルを生成します。これには、インクルードパス、コンパイラコマンド、オプションが含まれています。これらは、何がどこにあるのかを把握するのに役立ちます。

CMakeを使わずにmakeを使うプロジェクトの場合は、Bearを使って bear makeコマンドでこのファイルを生成することができます。

リンティング clangdを経由して呼び出すことができるclang-tidyリンターというツールがあります。リンターとは、コードの怪しい部分を叫ぶためのツールです。coc-setting.jsonファイルのclangdに--clang-tidy argを渡したことを確認してください。

いくつかのチェックのファミリーを有効にして、それをファイルにダンプすることができます。

clang-tidy --checks='-,bugprone-' --dump-config > .clang-tidy。 Clangdはこのファイルを検出し、いくつかの基準に基づいてコードをリントします。全てのチェックを見るには次のようにします: clang-tidy --list-checks