最近になってようやくlspを使い始めたのですが、結局補完とエラー取得と定義ジャンプしかしていないなーと思ったので、他にどんな事ができるのかを調べて見ました。
設定方法等はvim-lspのREADMEやWikiを見たら行けるかと思います。
また、最近では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等で試して見たんですけど、何も出てこなかったのでどういうコマンドかわかる人がいれば教えていただけると助かります![]()
↓ 2019/12/23 追記
language serverが定義しているCodeActionが出てくるみたいです。
例えば、goplsだと以下のようにimportしていないfmt上で:LspCodeActionを実行すると、Organize Importというものが出てきました。これでOrganize Importを選択するとfmtがimportされます。(goimportsで代替可能では?)
CodeActionの調べ方はよく分かってません。
↓ 2020/01/11 追記
最近は:LspCodeActionに引数を取れるようになりました。:LspCodeActionの後に<TAB>を押すと補完候補が出ます。もちろん引数を与えずに<CR>を押すと上記のように現在使えるCodeActionを取得してくれます。
もし画像のように補完はされるけれど候補の表示がされないなら.vimrcに以下の設定を加えてください。
set wildmenu
set wildmode=full
また、新たに:LspCodeActionSyncが追加されました。これによって以下のようにすることで、保存時にsource.organizeImportsを実行してくれるようになります。これもgoの場合はgoimportsで十分な気もしますがLSPでここまで出来るのは未来を感じますね。
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にしておくと競合しなくて済みそう。
let g:lsp_diagnostics_enabled = 0
また、エラーの表示をファイルに直接したい場合は以下のような設定でsign表示の設定をすればいいです。
※ 現在(2019/3/6)signの表示はneovimではできないらしい。詳しくはissueを確認してください。
→ 対応したみたいです。(2019/8/8追記)
let g:lsp_signs_enabled = 1
let g:lsp_diagnostics_echo_cursor = 1
他に、signのアイコンなどを変えたい場合は以下のように設定できます。
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修正が保存されないからかな?
ただし、保存時に動作を持って行かれるので、中断されるのが嫌な人は使わない方がいいかも?
autocmd BufWritePre <buffer> LspDocumentFormatSync
LspDocumentRangeFormat
Format the current document selection.
これは選択範囲をFormatしてくれるみたいですね。
特に説明いらないかも。
というか使わないかも。。
LspDocumentSymbol
Gets the symbols for the current document.
そのドキュメントの中のシンボルが出ます。
以下のようなgoのファイルで実行すると、quickfixウィンドウで以下のようなその下のような結果が出ます。
定義場所にジャンプするのに便利かもしれないけど、これもあまり使う機会が無いかも?
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()
}
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では使わないかも。。
⬇︎ 2019/8/8 追記
vimのpopupやneovimのfloating windowに対応したみたいなので、以下のような表示になります。
パッと確認したい時に便利です!!
僕は何かのシンボルについて調べたい時は:LspHoverでざっくり確認
→ もうちょっと詳しく調べたい時は :LspDefinitionや :LspTypeDefinitionで定義場所に飛んで確認する。
というように使っています。
LspImplementation
Find all implementation of interface.
全実装とinterfaceを見つけ出してくれるみたいです。
以下のようなコードがあったとします。(#の左側がカーソルの位置)
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にショートカットキーを追加しておくといいかと思います。
例)
nnoremap <silent> ]e :LspNextError<CR>
nnoremap <silent> [e :LspPreviousError<CR>
⬇︎ 2020/01/11 追記
Errorの他にDiagnosticsやWarningにも飛べるようになりました。
これらを使いこなすことでより素早くジャンプ出来るようになると思います。
LspReferences
Find all references.
これはカーソル以下にあるシンボルがどこで参照されているのかを見つけるためのコマンドみたいです。
以下のようなコードがあるとします。(#の左側がカーソルの位置)
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がその下で参照されている場所が表示されているのが分かります。
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.
これはカーソルの下にあるシンボルの名前を変更するコマンドです。
そのシンボルが関連する名前を全て変えてくれるものです。
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.
これはサーバーの状態が表示されます。
ステータスには以下の6つがあります。
サーバーが正常に起動していれば、runningになるかと思います。
- unknown server
- exited
- starting
- failed
- running
- not running
LspTypeDefinition
Go to the type definition.
これはそのシンボルのタイプへの定義ジャンプになります。
例えば以下のようなファイルがあるとします。(#の左側がカーソルの位置)
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が定義されている行にジャンプするので以下の行の場所にジャンプします。
var a testInterface
しかし、 :LspTypeDefinitionを用いるとaのタイプであるtestInterfaceの定義場所までジャンプします。
type testInterface interface {
:LspDefinitionと:LspTypeDefinitionは使い分けが少し難しいですが、結構使えるのではないかなと思います。
LspTypeHierarchy
これはclassなどの継承を知るのに使えるらしいです。調べた感じだと2020/01/11現在で対応しているLSはclangdとJavaのLSPしか確認できませんでした。(https://github.com/sorbet/sorbet/issues/1477)
最近の言語(GoやRust等)は継承を持たないものが増えてきているのであまり使うことは少なくなってきそうですね。
以下の形で継承を表現してくれます。
LspWorkspaceSymbol
Search and show workspace symbols.
これはWorkspace全体のSymbolを検索するためのコマンドみたいです。
先程の:LspDocumentSymbolではそのファイルで定義されているシンボルのみの検索でしたが、他にもimportしたファイルなどのシンボルまで検索できるようでした。
実際に以下のようなファイルで:LspWorkspaceSymbolを実行して、printを検索してみます。
すると、main.goで定義したprint以外にimportしているfmtのprintまで出てきました。
どのようなコマンドが使えるのかを調べるときに便利なのかもしれないです。
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()
}




