Linuxを日常的に使う実験ブログ

vim/neovim + coc.nvim + HLSで快適なHaskellの開発環境を整える

 2021-10-17

 プログラミング

こんにちは。今回のテーマは「vim/neovim + coc.nvim + HLSで快適なHaskellの開発環境を整える」です。これまでvimでHaskellを書くときには補完プラグインであるneco-ghcを使用して書いていました。neco-ghcも使い勝手の良いプラグインで大変重宝していたのですが、開発環境を見直してみようということでvim + HLSで環境を構築してみようと思いました。結果大変快適な環境となりましたので記事にしておきます。

[adsense02]

LSPによってエディタがIDE化する

プログラムを書く人間にとってエディタのIDE(Integrated Developmet Environment : 統合開発環境)化というのは長年のテーマだったような気がします。LSP(Language Server Protocol)が登場する以前、IDEのようにプログラムを解析しアラートを出し、補完機能を有するエディタに変身させるために言語ごとに多くのプラグインを入れて複雑な設定ファイルを書き、工夫をしてきました。VimやEmacsといった昔ながらのエディタでプログラムを書くというのは多くのプラグインで武装し、それでもリッチで重いIDEには一歩及ばない心許なさを感じながらも軽快で手に馴染んだツールを選ぶという感じだったと思います。

LSPはもともとはMicrosoft Visual Studio Code(以下VSCode)のために開発され、その後公開されました。このプロトコルの策定にはマイクロソフトを中心にRed Hat等の企業が関わっています。VSCodeという多機能なエディタにIDEの機能を付与するために生み出されたこのプロトコルはオープン化されることで、LSPに対応しているエディタならIDEの機能を使えるようになったのです。2021年現在、多くの言語でLSPに即したLanguage Serverが開発されて、公開されています。(参考:wikipedia)

LSPの登場によってエディタはLanguage Serverに接続するだけでIDEの能力を手に入れることができるようになりました。これまで言語ごとに多くのプラグインを入れて対応していた状況に比べると画期的な進歩を遂げたと言えると思います。

coc.nvimとは

Typescriptで書かれたvim/neovimのためのプラグインです。LSPをサポートしていてvim/neovimをMS VSCodeのように補完や定義へのジャンプができたり、警告が出せるエディタにするために作られました。HLSの公式ページでもcoc.nvimが推奨されています。

coc.nvim + HLSでの補完の例 coc.nvim + HLSでドキュメント表示の例

HLSのインストール

HLS(Haskell Language Server)はLSPの仕様に則ったHaskellの言語サーバです。このHLSのおかげでvimやemacs等のエディタはHaskellのIDEとしての能力を得ることになります。HLSのインストールはghcupを利用することをおすすめします。こちらの記事を参考にインストールしましょう。

nodejsのインストール

coc.nvmiはTypescriptで書かれており、2021年10月現在ではバージョンは12.12以上のNode.jsが必要です。UbuntuやDebianベースのディストロですと、パッケージマネージャでインストールされるNode.jsはバージョンが古い可能性があります。その場合はこちらを参考にリポジトリを追加してください。

coc.nvimをインストールする

今回はプラグイン管理ツールとしてvim-plugを使用していることを前提とします。もちろん、deinやVundle等のプラグイン管理ツールを使用してもインストールできます。vimの場合は~/.vimrc、neovimの場合は~/.config/nvim/init.vimに以下を追加します。

Plug "neoclide/coc.nvim", {'branch': 'release'}

そしてvim/neovim上で以下のコマンドを実行します。これでcoc.nvmがインストールされます。

:PlugInstall

注意事項ですが、もしプラグインマネージャとしてVundleを使用してインストールする場合はyarnを使用してビルドをする必要があるのでご注意ください。以下のような要領です。

# nvimの場合
cd ~/.config/nvim/bundle/coc.nvim/
# vimの場合
cd ~/.vim/bundle/coc.nvim/
yarn isntall
yarn build

HLSに接続するための設定をする

cocはインストールしただけでは単なる補完機能のみしか提供してくれません。HLSに接続するための設定を書く必要があります。neovimをコマンドモードにして以下のように実行します。

:Coc.Config

cocの設定ファイルが開きますので、ここに以下の設定を加えます。

{
  "languageserver": {
    "haskell": {
      "command": "haskell-language-server-wrapper",
      "args": ["--lsp"],
      "rootPatterns": ["*.cabal", "stack.yaml", "cabal.project", "package.yaml", "hie.yaml"],
      "filetypes": ["haskell", "lhaskell"]
    }
  }
}

保存して閉じます。

キーマッピングを設定する

公式ページにはキーマッピングの設定例が紹介されており、これをコピーするだけでも十分使えます。ここでは必要最低限必要だと思う設定を挙げておきます。 vimの場合は~/.vimrc、neovimの場合は~/.config/nvim/init.vimに設定を追加します。

" ctrl + spaceで補完を開始
if has('nvim')
  inoremap <silent><expr> <c-space> coc#refresh()
else
  inoremap <silent><expr> <c-@> coc#refresh()
endif

" shift + kでキーでプレビュー画面にドキュメントを表示
nnoremap <silent> K :call <SID>show_documentation()<CR>

function! s:show_documentation()
  if (index(['vim','help'], &filetype) >= 0)
    execute 'h '.expand('<cword>')
  elseif (coc#rpc#ready())
    call CocActionAsync('doHover')
  else
    execute '!' . &keywordprg . " " . expand('<cword>')
  endif
endfunction

" カーソル保持で該当単語と参照箇所をハイライトする
autocmd CursorHold * silent call CocActionAsync('highlight')

" コード間の移動コマンド
nmap <silent> gd <Plug>(coc-definition)
nmap <silent> gy <Plug>(coc-type-definition)
nmap <silent> gi <Plug>(coc-implementation)
nmap <silent> gr <Plug>(coc-references)

cocの機能をもっと知りたい方はneovim上で以下のコマンドを入力してヘルプ画面を出して見ることができます。

:h coc-nvim

最後に

今回はHaskellを書くための環境を構築しましたが、coc.nvimはLSPに対応したプラグインですのでHaskell以外の言語にも応用可能です。設定方法は各言語ごとの設定を参考にしてください。 本記事がお役に立てば幸いです。

[adsense] 【関連記事】 ghcupでHaskellの開発環境を構築する FizzBuzz問題で考えるHaskellと問題解決のアプローチ