LoginSignup
145
97

More than 3 years have passed since last update.

vim-lspでできること

Last updated at Posted at 2019-03-07

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

また、最近では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

↓ 2020/01/11 追記

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

image.png

もし画像のように補完はされるけれど候補の表示がされないなら.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にしておくと競合しなくて済みそう。

.vimrc
let g:lsp_diagnostics_enabled = 0

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

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

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

.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修正が保存されないからかな?
ただし、保存時に動作を持って行かれるので、中断されるのが嫌な人は使わない方がいいかも?

.vimrc
autocmd BufWritePre <buffer> LspDocumentFormatSync

LspDocumentRangeFormat

Format the current document selection.

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

LspDocumentSymbol

Gets the symbols for the current document.

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

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

⬇︎ 2019/8/8 追記

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

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

スクリーンショット 2019-08-08 12.00.48.png

LspImplementation

Find all implementation of interface.

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

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 の部分への参照も出てますね!
testStructprint()への参照が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.

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

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先に飛べるようになりました。これはある変数がどこで使われているかを見たり編集するために飛ぶのにかなり使えそうです。しかし、goplsclangdで試してみましたがうまく動きませんでした。また様子を見ながら試してみようと思っています。

LspRename

Rename the symbol.

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

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.

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

  • unknown server
  • exited
  • starting
  • failed
  • running
  • not running

LspTypeDefinition

Go to the type definition.

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

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が定義されている行にジャンプするので以下の行の場所にジャンプします。

    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等)は継承を持たないものが増えてきているのであまり使うことは少なくなってきそうですね。
以下の形で継承を表現してくれます。

image.png

LspWorkspaceSymbol

Search and show workspace symbols.

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

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

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()
}
145
97
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
145
97