【2020年冬】Vim/NeovimでDenoの開発環境を構築する

Deno Advent Calendar 2020 3日目の記事です🦕

今日は、Vim/NeovimでDenoの開発環境を構築する方法について説明します。

## 概要

この記事では、Vim/Neovimにおける主要なLSPクライアントである[coc.nvim](https://github.com/neoclide/coc.nvim)及び[vim-lsp](https://github.com/prabirshrestha/vim-lsp)のそれぞれについて、Denoの開発環境をセットアップする方法を説明します。

## 想定読者

この記事は以下のような方を想定して書かれています。

* 普段の開発でVimまたはNeovimを使用している
* [dein.vim](https://github.com/Shougo/dein.vim)や[vim-plug](https://github.com/junegunn/vim-plug)等のプラグインマネージャの使用方法を把握している
  * dein.vimの使用方法について
      * [おすすめプラグイン 〜 Vimはいいぞ！ゴリラと学ぶVim講座(7)](https://knowledge.sakura.ad.jp/23248/)
  * vim-plugの使用方法について
      * [pathogen から vim-plug に乗り換えた](https://kengotakimoto.com/post-3076/)  

## coc.nvimを使ってDenoの開発環境をセットアップする

### 要約

[coc-deno](https://github.com/fannheyward/coc-deno)を使うとよいと思います。

### coc.nvimとは?

Node.jsを使って実装されたVim/NeovimのLSPクライアントです。詳しくは[GitHubリポジトリ](https://github.com/neoclide/coc.nvim)または以下のページを参照ください :bow:

* [Vimにcoc.nvimを入れたら便利すぎて感動したっていう話](https://qiita.com/Amadeus_vn/items/5dfcf8d3676c2c5576ef)

### セットアップ方法

#### coc.nvimのインストール

まず、coc.nvimはNode.jsに依存しているため、入っていなければ事前にインストールしてください。(Node.jsの10.12以上のバージョンのものが必要です)

次に、coc.nvimをインストールします。

dein.vimをお使いの場合は、TOMLファイルに以下の記述を追加すればcoc.nvimをインストールできます。

```toml:dein.toml
 [[plugins]]
 repo = 'neoclide/coc.nvim'
 rev = 'release'
 merged = 0
```

vim-plugをお使いの場合は、`vimrc`または`init.vim`に次のように記述すればインストールできると思います。

```vim:.vimrc
Plug 'neoclide/coc.nvim', {'branch': 'release'}
```

#### coc.nvimの拡張をインストール

次に、[coc-tsserver](https://github.com/neoclide/coc-tsserver)と[coc-deno](https://github.com/fannheyward/coc-deno)をインストールします。

[coc-deno](https://github.com/fannheyward/coc-deno)とは、coc.nvimでDenoの開発をサポートするための拡張です。
この拡張を[coc-tsserver](https://github.com/neoclide/coc-tsserver)と併用することで、Denoの開発が容易になります。

Vim/Neovimで以下のコマンドを実行すると、coc-tsserverとcoc-denoをインストールできます。

```vim
:CocInstall coc-tsserver coc-deno
```

### 動作例

上記セットアップを実施すると、自動補完やシンボルジャンプ等の便利な機能が使えるようになります。
![coc-deno](https://i.gyazo.com/thumb/1000/8bd3ccb4841b1049c0429e2264d841b3-png.png)
より詳細なセットアップ方法については、以下を参照ください

* [Vimでdenoの開発環境を構築する2020](https://scrapbox.io/uki00a/Vim%E3%81%A7deno%E3%81%AE%E9%96%8B%E7%99%BA%E7%92%B0%E5%A2%83%E3%82%92%E6%A7%8B%E7%AF%89%E3%81%99%E3%82%8B2020)

## vim-lspを使ってDenoの開発環境をセットアップする

### 要約

[typescript-language-server](https://github.com/theia-ide/typescript-language-server)と[typescript-deno-plugin](https://github.com/justjavac/typescript-deno-plugin) [※](#typescript-deno-pluginとは)を併用するとよいと思います。

### vim-lspとは?

VimScriptのみで実装されたLSPプラグインです。
詳しくは[Gitリポジトリ](https://github.com/prabirshrestha/vim-lsp)または以下を参照ください :bow:

* [vim-lspでできること](https://qiita.com/kitagry/items/216c2cf0066ff046d200)

### セットアップ方法

#### vim-lspのインストール

まず、vim-lspをインストールします。

必要に応じて、[asyncomplete.vim](https://github.com/prabirshrestha/asyncomplete.vim)や[asyncomplete-lsp.vim](https://github.com/prabirshrestha/asyncomplete-lsp.vim)等を追加でインストールすると、自動補完ができるようになります。

dein.vimをお使いの場合は、TOMLファイルに以下の記述を追加するとインストールできます。

```toml:dein.toml
[[plugins]]
repo = 'prabirshrestha/vim-lsp'

# 以下の2つのプラグインは必要に応じてインストールする
[[plugins]]
repo = 'prabirshrestha/asyncomplete.vim'

[[plugins]]
repo = 'prabirshrestha/asyncomplete-lsp.vim'
```

vim-plugをお使いの場合は、`vimrc`または`init.vim`で以下のように記述するとインストールできると思います。

```vim:.vimrc
Plug 'prabirshrestha/vim-lsp'
Plug 'prabirshrestha/asyncomplete.vim'
Plug 'prabirshrestha/asyncomplete-lsp.vim'
```

#### npmパッケージのインストール

次に、以下の3つのnpmパッケージをインストールします。

* [typescript-language-server](https://github.com/theia-ide/typescript-language-server)
* [typescript](https://www.npmjs.com/package/typescript)
* [typescript-deno-plugin](https://github.com/justjavac/typescript-deno-plugin) [※](#typescript-deno-pluginとは)

```shell
$ npm install -g typescript-language-server typescript typescript-deno-plugin
```

#### vim-lspの設定

以下の設定を`vimrc`または`init.vim`に追加してください。

```vimscript
if executable("typescript-language-server")
  " グローバルインストールされたnpmモジュールの保存場所
  let s:npm_root = trim(system("npm root -g"))

  " vim-lspのinitialization_optionsを使用して、typescript-deno-pluginのインストール場所をtypescript-language-serverへ伝えます
  let s:has_typescript_deno_plugin = isdirectory(s:npm_root . "/typescript-deno-plugin")
  let s:plugins = s:has_typescript_deno_plugin
    \ ? [{ "name": "typescript-deno-plugin", "location": s:npm_root }]
    \ : []
  augroup LspTypeScript
    autocmd!
    autocmd User lsp_setup call lsp#register_server({
    \   "name": "typescript-language-server",
    \   "cmd": {server_info -> ["typescript-language-server", "--stdio"]},
    \   "root_uri": {server_info->lsp#utils#path_to_uri(lsp#utils#find_nearest_parent_file_directory(lsp#utils#get_buffer_path(), 'tsconfig.json'))},
    \   "whitelist": ["typescript", "typescript.tsx"],
    \   "initialization_options": { "plugins": s:plugins },
    \ })
  augroup END
endif
```

これでセットアップは完了です。

### 動作例

上記セットアップを実施すると、入力補完や診断等の機能が動作します。
![vim-lsp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240412/fdbbe058-5cf1-9b42-0fe0-89025b6da14a.png)

### 注意点

この記事で紹介したtypescript-language-serverからtypescript-deno-pluginを読み込ませる手法は、typescript-language-serverのドキュメントには記載されていません。
そのため、将来的には同様の方法では動作しなくなる可能性があります。

## 補足

### deno_stdやサードパーティモジュールの補完や型チェックが効かないときはどうしたいいの?

おそらく、依存モジュールがインストールされていないのが原因だと思います。
`deno cache`コマンドで依存モジュールをインストールすれば動作すると思います。

```shell
$ deno cache mod.ts
```

### typescript-deno-pluginとは?

Denoにはいくつか独自の機能が存在します。

* [@deno-types](https://scrapbox.io/deno-ja/JavaScript%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%81%AB%E5%9E%8B%E3%82%92%E9%81%A9%E7%94%A8%E3%81%99%E3%82%8B)
* リモートURL(例: `import {} from "https://deno.land/std/ws/mod.ts"`)
* [Import Maps](https://scrapbox.io/deno-ja/Import_Maps)

標準ではtsserverは上記の機能を認識することができず、うまく機能しません。
typescript-deno-pluginを使用することで、tsserverが上記の機能を認識してくれるようになります。

### vscodeで開発したいときはどうすればいいの?

[vscode-deno](https://marketplace.visualstudio.com/items?itemName=denoland.vscode-deno)というDenoのコアチームによってメンテナンスされているvscode拡張があります。
vscodeをお使いの方は、こちらの拡張を使用することをおすすめします。

### coc.nvimとvim-lspのどちらを使ったらいいの?

それぞれに利点はあるものの、個人的な意見としては、以下のような理由から、特にこだわりがなければcoc.nvimがよいのではないかと思います :thinking: 

* セットアップが容易であること
* GitHubのスター数が多い

## Denoの開発の将来について

現在、DenoではLanguage Serverの実装が計画されています :rocket: 

* https://github.com/denoland/deno/pull/8515

`deno lsp`コマンドを叩くことで、Denoに組み込まれたLanguage Serverが起動するようになります。

このコマンドが実装されれば、この記事で紹介した手法に限らず、**あらゆるエディタ・IDEでDenoを使った開発が容易になる**と想像しています :heart_eyes: 

※~~実は筆者はちょうどこのPR([#8515](https://github.com/denoland/deno/pull/8515))が建てられる数日前に[deno-lsp](https://github.com/uki00a/deno-lsp)というLSPサーバを自作していました...:expressionless::neutral_face::innocent:~~

## まとめ

この記事ではVim/NeovimでDenoの開発環境をセットアップする方法について紹介しました。

現時点では、vscode以外のエディタで開発する際は少々工夫が必要ですが、`deno lsp`コマンドが実装されれば、この状況も改善されると思います :rocket: 

もし興味があれば、ぜひDenoを触ってみてください! 🦕