vim-lspでできること

最近になってようやくlspを使い始めたのですが、結局補完とエラー取得と定義ジャンプしかしていないなーと思ったので、他にどんな事ができるのかを調べて見ました。
設定方法等はvim-lspの[README](https://github.com/prabirshrestha/vim-lsp)や[Wiki](https://github.com/prabirshrestha/vim-lsp/wiki)を見たら行けるかと思います。

また、最近では[mattn/vim-lsp-settings](https://github.com/mattn/vim-lsp-settings)というプラグインが作成され、LSPの面倒くさい設定を書かなくてもローカルに自動でLSをインストールしてくれるようになりました。
もし面倒くさくてインストールしていないという方がいれば、試してみることをおすすめします。

基本的にはヘルプで確認すればいいんですが、僕のLSPに対する知識のなさと英語力のなさで結構わからない事があったのでメモがわりに書きます。

※ この記事は定期的に便利なコマンドに気づいたときに加筆したりしています。なので古い情報と新しい情報が混ざっていることがあるので注意ください。

基本的にできることは次の通りです。

| Command | Description |
|:---:|:---:|
| :LspCodeAction | Gets a list of possible commands that can be applied to a file so it can be fixed (quick fix) |
| :LspDeclaration | Go to the declaration of the word under the cursor, and open in the current window |
| :LspDefinition | Go to the definition of the word under the cursor, and open in the current window |
| :LspDocumentDiagnostics | Get current document diagnostics information |
| :LspDocumentFormat | Format entire document |
| :LspDocumentRangeFormat | Format document selection |
| :LspDocumentSymbol | Show document symbols |
| :LspHover | Show hover information |
| :LspImplementation  | Show implementation of interface in the current window |
| :LspNextDiagnostic | jump to next diagnostic (all of error, warning, information, hint) |
| :LspNextError | jump to next error |
| :LspNextReference | jump to next reference to the symbol under cursor |
| :LspNextWarning | jump to next warning |
| :LspPeekDeclaration | Go to the declaration of the word under the cursor, but open in preview window |
| :LspPeekDefinition | Go to the definition of the word under the cursor, but open in preview window |
| :LspPeekImplementation | Go to the implementation of an interface, but open in preview window |
| :LspPeekTypeDefinition | Go to the type definition of the word under the cursor, but open in preview window |
| :LspPreviousDiagnostic | jump to previous diagnostic (all of error, warning, information, hint) |
| :LspPreviousError | jump to previous error |
| :LspPreviousReference | jump to previous reference to the symbol under cursor |
| :LspPreviousWarning | jump to previous warning |
| :LspReferences | Find references |
| :LspRename | Rename symbol |
| :LspStatus  | Show the status of the language server |
| :LspTypeDefinition | Go to the type definition of the word under the cursor, and open in the current window |
| :LspTypeHierarchy | View type hierarchy of the symbol under the cursor |
| :LspWorkspaceSymbol | Search/Show workspace symbol |


上から一つづつ見ていこうと思います。

## LspCodeAction

> Gets a list of possible commands that can be applied to a file so it can be
fixed (quick fix).

codeに対して可能なコマンドのリストを取得するみたいな感じですね。
go・python等で試して見たんですけど、何も出てこなかったのでどういうコマンドかわかる人がいれば教えていただけると助かります:bow:

↓ 2019/12/23 追記

language serverが定義しているCodeActionが出てくるみたいです。
例えば、`gopls`だと以下のようにimportしていない`fmt`上で`:LspCodeAction`を実行すると、Organize Importというものが出てきました。これでOrganize Importを選択すると`fmt`がimportされます。(goimportsで代替可能では？)
CodeActionの調べ方はよく分かってません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172408/66e974b4-f88b-408f-2846-466667639674.png)

↓ 2020/01/11 追記

最近は`:LspCodeAction`に引数を取れるようになりました。`:LspCodeAction`の後に`<TAB>`を押すと補完候補が出ます。もちろん引数を与えずに`<CR>`を押すと上記のように現在使えるCodeActionを取得してくれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172408/d8ec8ecc-fca2-d5af-dd58-bf44d57000bf.png)

もし画像のように補完はされるけれど候補の表示がされないなら.vimrcに以下の設定を加えてください。

```vim
set wildmenu
set wildmode=full
```

また、新たに`:LspCodeActionSync`が追加されました。これによって以下のようにすることで、保存時に`source.organizeImports`を実行してくれるようになります。これもgoの場合はgoimportsで十分な気もしますがLSPでここまで出来るのは未来を感じますね。

```vim
autocmd BufWritePre <buffer>
                \ call execute('LspCodeActionSync source.organizeImports')
```

## LspDeclaration

> Go to declaration. Useful for languages such as C/C++ where there is a clear
distinction between declaration and definition.

宣言場所へのジャンプですかね。
CやC++みたいに宣言する場所と実装する場所が異なる言語で使用可能のようです。

僕にはあまり使う場面が想像できなかったので特にこのメソッドを使うことは無いかな。

↓ 2020/01/11 追記

`:LspPeekDeclaration`というものが追加されました。
このコマンドは`:LspDeclaration`違ってポップアップウィンドウのようなpreview用の画面にDeclarationを表示してくれるというものです。


## LspDefinition

> Go to definition.

これは定義場所にジャンプしてくれるメソッドのようです。
カーソルの下にあるメソッドを定義している場所に飛んでくれます。

↓ 2020/01/11 追記

`:LspPeekDefinition`というものが追加されました。
このコマンドは`:LspDefinition`違ってポップアップウィンドウのようなpreview用の画面にDefinitionを表示してくれるというものです。


## LspDocumentDiagnostics

> Gets the current document diagnostics.

ファイルの診断を行ってくれます。
要するに、errorやwarningの取得みたいな感じらしい。
quickfixでエラーの場所を表示してくれます。

以下の変数を`1`, `0`(defaultは`1`)を切り替えることで、診断を自動で行うかを判断してくれるらしい。
vimが重たいなと思ったら、`0`にするのもいいかもしれない。
また、別のプラグインを使う場合にも`0`にしておくと競合しなくて済みそう。

```vim:.vimrc
let g:lsp_diagnostics_enabled = 0
```

また、エラーの表示をファイルに直接したい場合は以下のような設定でsign表示の設定をすればいいです。
~~※ 現在(2019/3/6)signの表示はneovimではできないらしい。詳しくは[issue](https://github.com/prabirshrestha/vim-lsp/issues/322)を確認してください。~~
→ 対応したみたいです。(2019/8/8追記)

```vim:.vimrc
let g:lsp_signs_enabled = 1
let g:lsp_diagnostics_echo_cursor = 1
```

他に、signのアイコンなどを変えたい場合は以下のように設定できます。

```vim:.vimrc
let g:lsp_signs_error = {'text': '✗'}
let g:lsp_signs_warning = {'text': '‼', 'icon': '/path/to/some/icon'}
let g:lsp_signs_hint = {'icon': '/path/to/some/other/icon'}
```


## LspDocumentFormat

> Format the entire document.

現在開いているファイルのフォーマットを修正してくれるらしいです。
これは結構便利そう。

保存時に使うにはLspDocumentFormatSyncを使ってください。
同期処理をしないとFormat修正が保存されないからかな？
ただし、保存時に動作を持って行かれるので、中断されるのが嫌な人は使わない方がいいかも？

```vim:.vimrc
autocmd BufWritePre <buffer> LspDocumentFormatSync
```

## LspDocumentRangeFormat

> Format the current document selection.

これは選択範囲をFormatしてくれるみたいですね。
特に説明いらないかも。
というか使わないかも。。


## LspDocumentSymbol

> Gets the symbols for the current document.

そのドキュメントの中のシンボルが出ます。
以下のようなgoのファイルで実行すると、quickfixウィンドウで以下のようなその下のような結果が出ます。
定義場所にジャンプするのに便利かもしれないけど、これもあまり使う機会が無いかも？

```go:main.go
package main

import "fmt"

type testStruct struct {
	content string
}

func (a testStruct) Print() {
	fmt.Println(a.content)
}

func main() {
	var a = testStruct{content: "テスト"}
	a.Print()
}
```

```:quickfix
main.go|6 col 2| field : content
main.go|5 col 6| class : testStruct
main.go|9 col 16| method : Print
main.go|13 col 6| function : main
main.go|14 col 6| variable : a
```


## LspHover

> Gets the hover information and displays it in the |preview-window|.

現在のカーソルの下にあるシンボルの情報をpreview windowに出してくれるらしいですね。
~~よくIDE等にある↓みたいな動きをイメージしてると思うんですけど、あまりvimでは使わないかも。。~~

![hovers.gif](https://qiita-image-store.s3.amazonaws.com/0/172408/8b2c8724-b926-5c2a-85bf-4dee244aff17.gif)

⬇︎ 2019/8/8 追記

vimのpopupやneovimのfloating windowに対応したみたいなので、以下のような表示になります。
パッと確認したい時に便利です！！

僕は何かのシンボルについて調べたい時は`:LspHover`でざっくり確認
→ もうちょっと詳しく調べたい時は `:LspDefinition`や `:LspTypeDefinition`で定義場所に飛んで確認する。
というように使っています。

![スクリーンショット 2019-08-08 12.00.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172408/bd4e1bb1-4007-a124-e41b-f0e83810f8f1.png)



## LspImplementation

> Find all implementation of interface.

全実装とinterfaceを見つけ出してくれるみたいです。
以下のようなコードがあったとします。(`#`の左側がカーソルの位置)

```go:main.go
package main

import "fmt"

type testInterface interface {
	print()
}

type testStruct struct {
	content string
}

func (a testStruct) print() {
	fmt.Println(a.content)
}

func main() {
	var a testInterface
	a# = testStruct{content: "テスト"}
	a.print()
}
```

ここで`:LspImplementation`と入力すると、以下のようなquickfixウィンドウが出てきます。
こんな感じで、`a`の構造体が実装されている場所とinterfaceの場所が出てきます。
Language Serverにはbingoを使っているんですが、2つ同じ物が出てくるのはバグなんでしょうか。。

```
main.go|5 col 6| type testInterface interface {
main.go|9 col 6| type testStruct struct {
main.go|9 col 6| type testStruct struct {
main.go|5 col 6| type testInterface interface {
```

メソッドにもこのコマンドは使えるみたいで先程の `main.go`の　`a.print()`の `print()`の部分にカーソルを置いて、`:LspImplementation`を実行すると、以下のような結果になります。
`testInterface` の部分への参照も出てますね！
`testStruct`の`print()`への参照が2つ含まれてるのも相変わらずですが。。

```
main.go|6 col 2| print()
main.go|13 col 21| func (a testStruct) print() {
main.go|13 col 21| func (a testStruct) print() {
```

## LspNextError

> Jump to Next err diagnostics

これはdiagnosticsの次のエラーにジャンプするためのコマンドですね

## LspPreviousError

> Jump to Previous err diagnostics

これはdiagnosticsの前のエラーにジャンプするためのコマンドですね

`:LspNextError`や`:LspPreviousError`は使う場合は、vimrcにショートカットキーを追加しておくといいかと思います。

例）

```vim
nnoremap <silent> ]e  :LspNextError<CR>
nnoremap <silent> [e  :LspPreviousError<CR>
```

⬇︎ 2020/01/11 追記

Errorの他にDiagnosticsやWarningにも飛べるようになりました。
これらを使いこなすことでより素早くジャンプ出来るようになると思います。


## LspReferences

> Find all references.

これはカーソル以下にあるシンボルがどこで参照されているのかを見つけるためのコマンドみたいです。
以下のようなコードがあるとします。(#の左側がカーソルの位置)

```go:main.go
package main

import "fmt"

type testInterface interface {
	print()
}

type testStruct struct {
	content string
}

func (a testStruct) print() {
	fmt.Println(a.content)
}

func main() {
	var a testInterface
	a# = testStruct{content: "テスト"}
	a.print()

	a.print()

	a.print()
}
```

ここで`:LspReferences`を実行すると以下のような結果になります。
変数`a`がその下で参照されている場所が表示されているのが分かります。

```:quickfix
main.go|19 col 2| a = testStruct{content: "テスト"}
main.go|20 col 2| a.print()
main.go|22 col 2| a.print()
main.go|24 col 2| a.print()
```

⬇︎ 2020/01/11 追記

`:LspNextReference`と`:LspPreviousReference`が追加され、Reference先に飛べるようになりました。これはある変数がどこで使われているかを見たり編集するために飛ぶのにかなり使えそうです。しかし、`gopls`や`clangd`で試してみましたがうまく動きませんでした。また様子を見ながら試してみようと思っています。

## LspRename

> Rename the symbol.

これはカーソルの下にあるシンボルの名前を変更するコマンドです。
そのシンボルが関連する名前を全て変えてくれるものです。

```go:main.go
package main

import "fmt"

type testInterface interface {
	print()
}

type testStruct struct {
	content string
}

func (a testStruct) print() {
	fmt.Println(a.content)
}

func main() {
	var a testInterface
	a# = testStruct{content: "テスト"}
	a.print()

	a.print()

	a.print()
}
```

このときに `:LspRename`を行うとmain関数の中で出てくる全ての `a`という変数が指定した名前に変更されます。
これは結構便利そうですね。

## LspStatus

> Prints the status of all registered servers.

これはサーバーの状態が表示されます。
ステータスには以下の６つがあります。
サーバーが正常に起動していれば、runningになるかと思います。

- unknown server
- exited
- starting
- failed
- running
- not running


## LspTypeDefinition

> Go to the type definition.

これはそのシンボルのタイプへの定義ジャンプになります。
例えば以下のようなファイルがあるとします。(#の左側がカーソルの位置)

```go:main.go
package main

import "fmt"

type testInterface interface {
	print()
}

type testStruct struct {
	content string
}

func (a testStruct) print() {
	fmt.Println(a.content)
}

func main() {
	var a testInterface
	a# = testStruct{content: "テスト"}
	a.print()

	a.print()

	a.print()
}
```

ここで`:LspDefinition`を行うと、`a`が定義されている行にジャンプするので以下の行の場所にジャンプします。

```go
	var a testInterface
```

しかし、 `:LspTypeDefinition`を用いると`a`のタイプである`testInterface`の定義場所までジャンプします。

```go
type testInterface interface {
```

`:LspDefinition`と`:LspTypeDefinition`は使い分けが少し難しいですが、結構使えるのではないかなと思います。

## LspTypeHierarchy

これはclassなどの継承を知るのに使えるらしいです。調べた感じだと2020/01/11現在で対応しているLSはclangdとJavaのLSPしか確認できませんでした。(https://github.com/sorbet/sorbet/issues/1477)

最近の言語(GoやRust等)は継承を持たないものが増えてきているのであまり使うことは少なくなってきそうですね。
以下の形で継承を表現してくれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172408/16bc9296-3b1b-41c4-ce31-4d8e2391709a.png)


## LspWorkspaceSymbol

> Search and show workspace symbols.

これはWorkspace全体のSymbolを検索するためのコマンドみたいです。
先程の`:LspDocumentSymbol`ではそのファイルで定義されているシンボルのみの検索でしたが、他にもimportしたファイルなどのシンボルまで検索できるようでした。

実際に以下のようなファイルで`:LspWorkspaceSymbol`を実行して、`print`を検索してみます。
すると、`main.go`で定義したprint以外にimportしている`fmt`の`print`まで出てきました。
どのようなコマンドが使えるのかを調べるときに便利なのかもしれないです。

```go:main.go
package main

import "fmt"

type testInterface interface {
	print()
}

type testStruct struct {
	content string
}

func (a testStruct) print() {
	fmt.Println(a.content)
}

func main() {
	var a testInterface
	a = testStruct{content: "テスト"}
	a.print()
}
```