VSCode + golangでの快適な補完について自分なりにまとめてみた

# この記事について
* この記事は Go7 Advent Calendar 2019 の 8 日目の記事です。
* [Umeda.go 2019 Autumn](https://umedago.connpass.com/event/145782/) で発表した内容をQiitaにまとめた記事です
    * [umeda.go](https://umedago.connpass.com/)は大阪の梅田近辺を中心に活動しているGo Communityです
    * 春夏秋冬の年４回開催を心がけております
    * もし機会が合えばぜひお気軽にご参加いただければと思います
* [スライドはすでに公開しています](https://docs.google.com/presentation/d/11wLgBTXo5ZRO9UY5rV85HXHiqNvd7qnwzWhYp8h4ovs/edit?usp=sharing)
* **限りなくGolang成分が薄いです（すみません）**
* 間違っていたらご指摘お願い致しますm(_ _)m

# この記事の目的

* golangでコーディングを快適にしたい
* 最近リリースされたTabNineを紹介したい

# コードエディタのコーディングサポート事情について

昨今、プログラミング言語、コードエディタも多くのものがリリースされました
言語はC、C++、Golang、Rust、Python、etc...
エディタはVim、emacs、VSCode、etc...
エディタのプラグインによって快適な補完が実現できているエディタも多いと思います
ただ、エディタ側のコーディングサポートについては多少の違いはありますがサポート内容は大体一緒かと思います
定義へのジャンプ、入力補完、カーソルホバーによる情報表示（変数型、関数の引数詳細など）、etc...

# LSP（Langauge Server Protocol）について

プログラム言語をエディタでサポートするためにエディタの拡張言語でサポートプラグインを書くことが必要かと思います
これをそれぞれのエディタで各プログラム言語用のプラグインを書くと
**プログラム言語×エディタ種類という組合せ爆発が発生します**
同じようなプラグインロジックなのに、エディタごとに用意されているプラグイン用言語に書き直さないといけないという事が起きてしまいます。

先程書いたように、言語は変わってもエディタ側が提供するコーディングサポートについては大差ありません
つまり、ある程度コーディングサポートのための内容を規格化することができます
Microsoft社がその規格化を行い、発表しました
**それが [LSP（Langauge Server Protocol）](https://microsoft.github.io/language-server-protocol/)です**

言語情報をもとにエディタサポートロジックを計算するのは「言語サーバー」
エディタは、言語サーバーに対して情報を送信し、返ってきた内容をエディタに反映するという実装で
プログラム言語×エディタ種類の組合せ爆発を抑えることができます。

「言語サーバー」というと大掛かりに聞こえますが、
「言語ごとにコーディングサポート情報を返答してくれる別プロセス」
という方がしっくり来るかもしれないです
言語サーバーはローカル環境に立てても動きます。

ここまで話しましたが、 Qiitaにも[LSPについて詳しく説明している記事](https://qiita.com/atsushieno/items/ce31df9bd88e98eec5c4) がありますのでそちらをご覧になったほうがいいかと思います。

# golangの言語サーバーについて

以下の表にまとめました

|golangの言語サーバー|メンテ状況|備考|
|-------------------|---------|----|
|https://github.com/sourcegraph/go-langserver/|今も続いている・・・？<br>（発表当時は止まっていたはずなのに。。。）|初代golangの言語サーバー|
|https://github.com/saibing/bingo|2019/3/31に<br>メンテしないことを宣言|sourcegraph/go-langserverに<br>影響されて作成された言語サーバー|
|https://github.com/golang/tools/tree/master/gopls|golangチームがサポート中|golang公式が提供する言語サーバー<br>元々「golsp」でしたが<br>「gopls」にリネーム|

表を見てわかると思いいますが実質「[gopls](https://github.com/golang/tools/tree/master/gopls)」一択です

# gopls + VSCodeとの連携について

Microsoft社が提供している[golangプラグイン](https://marketplace.visualstudio.com/items?itemName=ms-vscode.Go)がすでにサポートしています
**ただし言語サーバー関連の機能はデフォルトOFFになっています**
言語サーバーを有効化するには色々初期設定が必要です

2019年9月頃だと、誤った設定を行うと「保存するたびにカーソルが1行目1列に戻ってしまう」という
ストレスMAXな挙動になっていましたが現在は解消されているようです。
[該当Issue](https://github.com/golang/go/issues/31937)

ちなみに設定グループ名が「go.languageServerExperimentalFeatures」
Experimental抜くのはいつになるのでしょうか・・・

# 具体的な設定方法について

詳しくは[README](https://github.com/microsoft/vscode-go/blob/2bb53a5be04edbd60342e1976feefd4963a82229/README.md#settings-to-control-the-use-of-the-go-language-server)に載っています。

発表当時に調べた内容だけ記載しておきます

```jsonnet:settings.json
{
    "go.languageServerExperimentalFeatures": {
      "format": false,              // trueの場合、言語サーバーはgofmtを使用してファイルをフォーマットする　※当時はオフにしないと、1行1列目に戻ってしまってました
      "autoComplete": true,         // trueの場合、言語サーバーが提供する補完結果を表示する
      "rename": true,               // trueの場合、適切な名前変更機能が有効化される
      "goToDefinition": true,       // trueの場合、関数定義へのジャンプ機能を有効化する
      "hover": true,                // trueの場合、マウスを関数の上にかざすと定義表示がされるようになる
      "signatureHelp": true,        // trueの場合、関数のヘルプやパラメータのヒントを表示するようになる
      "goToTypeDefinition": true,   // trueの場合、型定義へのジャンプを有効化する
      "documentSymbols": true,      // trueの場合、ドキュメントシンボル表示、アウトライン表示、パンくずリスト表示を有効化する
      "workspaceSymbols": true,     // trueの場合、ワークスペースにあるシンボルジャンプを有効化する
      "findReferences": true,       // trueの場合、検索参照機能を有効化する
      "diagnostics": true,          // trueの場合、`go vet` を相当の機能を提供し、`buildOnSave`　`vetOnSave` を無視する
      "documentLink": true,         // trueの場合、importされているパッケージに対するドキュメントリンクが直接踏めるようになる

      "goToImplementation": true,   // ※ソースコード上に存在するだけでドキュメントがない
      "incrementalSync": true,      // trueの場合、差分（増分）ドキュメント更新が有効化される　※デフォルトfalseな上に使われてない?
    },

    // goplsのデバッグをしたい場合、以下の設定を行うことでログを見ることができます
    {
        "go.languageServerFlags": [
        "-rpc.trace",             // LSPの内容をダンプしつつ実行する
        "serve",                  // LS起動
        "--debug=localhost:6060", // このアドレスにデバッグ情報を表示する
      ],
    }
}
```

# TabNineについて

Deep Learningを応用して「補完機能のみ」を強化したツールです
Githubで公開されている約200万のソースファイルを使って学習しているようです
特定の言語に特化しているのではなく多くの言語に対応しています
各種コードエディタですぐ使えるようにプラグインも配っています
詳しくは[公式サイトやブログを参照してください](https://tabnine.com/)


## TabNineのSemantic completion機能

特定の言語に特化しているのではなく多くの言語に対応してるとはいえ、
どうしても言語特有の機能や書き方というのは存在するものです
その情報を補う機能が **「Semantic completion」** です
「Semantic completion」は言語サーバーと通信して定義内容や型情報を参照し、補完精度を高める機能です
デメリットとして、TabNine用のLSを立ち上げないといけないため
普段使い用＋TabNine用でCPUリソースを倍消費してしまうということです
もしかすると共有できるかもしれないですが正直わかっていません

## TabNineの注意事項

Professional版かEnterprise版でないと深層学習を利用した補完はできません
ですが、Professional版は現在Beta版なのでメールを送れば誰でも使用できます
深層学習を利用した補完は「TabNine Cloud」と「TabNine Local」の2種類があります

### TabNine Cloud
クラウドにあるGPUを利用して計算するためマシンスペックを利用しません
**ただし、コードをクラウドに送信してしまうためコードが意図せぬところから漏れる可能性がある**

### TabNine Local
自分のPCで計算するためコードリークする可能性はありません
**ただし、CPUがある程度の命令をサポートしているものでないと使えません**
最低でもFMA命令(積和演算)はサポート必須のようです
Intel製CPU i7であればであれば第4世代以降のようです

# VSCodeでTabNineを有効化し、goplsと連携する方法

コマンドパレット（Ctrl+P）に「ext install TabNine.tabnine-vscode」を入力すればインストールできます
または以下のようにVSCodeは拡張検索で「TabNine」と検索すれば候補が出てきます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102564/ecd6ebd2-021e-906a-b513-66aa662abfb4.png)

## TabNineのSemantic completion有効化方法

TabNineは設定方法が独特で、ソースコードに「TabNine::config」を入力するすると、
デフォルトブラウザにTabNineの設定ページが立ち上がります
「Configuration path」にtabnine_config.jsonのPathがあるのでtabnine_config.jsonがあるPathまで開いてください
同じパスある「TabNineExample.toml」を「TabNine.toml」にリネーム or コピーします
対象言語の言語サーバー設定を記載します

```TabNine.toml
[language.go]
command = "gopls"
args = ["serve"]
install = [["go", "get", "-u", "golang.org/x/tools/cmd/gopls"]]
```

実は現状、デフォルトで上記の設定なっているのであえて設定する必要はありません

あとは「Semantic completion」を有効化するソースコード上で「TabNine::sem」を入力すれば有効化できます
今回の場合、いずれかの.goファイル上で「TabNine::sem」を入力すればgolang用のSemantic completionを有効化できます

実際に使ってみた動画をYouTubeにアップしているのでよろしければ御覧ください
[![F9EdVJ44OYA](http://img.youtube.com/vi/F9EdVJ44OYA/0.jpg)](http://www.youtube.com/watch?v=F9EdVJ44OYA)

# まとめ

* コーディングサポートで必要な機能は固定化されつつあります
* Microsoft社がLSPとして言語サーバーとやり取りする情報を規格化し、それに則って実装するエディタや言語サーバーがでてきました
* 更に補完に関して深層学習を利用したTabNineが出てきて結構快適にコーディングできるようになってきたと思います
* gopls + TabNine + VSCodeおすすめです！
  * ぶっちゃけ他のエディタでもいいので使ってみてほしいです