最近になってようやく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()
}