Skip to content

Commit 57eff0d

Browse files
committed
internal/lsp: add support for running goimports as a code action
This change adds support for goimports as a code action that can be run on save. However, there do appear to be issues with the propagation of the context.Only field of the CodeActionParams, so we treat every codeAction as an organizeImports action - this should be fixed in the next vscode-languageclient release (microsoft/vscode-languageserver-node#442). Change-Id: I64ca0034c393762248fde6521aba86ed9d41bf70 Reviewed-on: https://go-review.googlesource.com/c/154338 Reviewed-by: Ian Cottrell <[email protected]>
1 parent 17661a9 commit 57eff0d

File tree

3 files changed

+69
-6
lines changed

3 files changed

+69
-6
lines changed

internal/lsp/imports.go

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2018 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package lsp
6+
7+
import (
8+
"context"
9+
10+
"golang.org/x/tools/internal/lsp/cache"
11+
"golang.org/x/tools/internal/lsp/protocol"
12+
"golang.org/x/tools/internal/lsp/source"
13+
)
14+
15+
func organizeImports(ctx context.Context, v *cache.View, uri protocol.DocumentURI) ([]protocol.TextEdit, error) {
16+
f := v.GetFile(source.URI(uri))
17+
tok, err := f.GetToken()
18+
if err != nil {
19+
return nil, err
20+
}
21+
r := source.Range{
22+
Start: tok.Pos(0),
23+
End: tok.Pos(tok.Size()),
24+
}
25+
content, err := f.Read()
26+
if err != nil {
27+
return nil, err
28+
}
29+
edits, err := source.Imports(ctx, tok.Name(), content, r)
30+
if err != nil {
31+
return nil, err
32+
}
33+
return toProtocolEdits(tok, content, edits), nil
34+
}

internal/lsp/server.go

+17-2
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ func (s *server) Initialize(ctx context.Context, params *protocol.InitializePara
5656

5757
return &protocol.InitializeResult{
5858
Capabilities: protocol.ServerCapabilities{
59+
CodeActionProvider: true,
5960
CompletionProvider: protocol.CompletionOptions{
6061
TriggerCharacters: []string{"."},
6162
},
@@ -250,8 +251,22 @@ func (s *server) DocumentSymbol(context.Context, *protocol.DocumentSymbolParams)
250251
return nil, notImplemented("DocumentSymbol")
251252
}
252253

253-
func (s *server) CodeAction(context.Context, *protocol.CodeActionParams) ([]protocol.CodeAction, error) {
254-
return nil, notImplemented("CodeAction")
254+
func (s *server) CodeAction(ctx context.Context, params *protocol.CodeActionParams) ([]protocol.CodeAction, error) {
255+
edits, err := organizeImports(ctx, s.view, params.TextDocument.URI)
256+
if err != nil {
257+
return nil, err
258+
}
259+
return []protocol.CodeAction{
260+
{
261+
Title: "Organize Imports",
262+
Kind: protocol.SourceOrganizeImports,
263+
Edit: protocol.WorkspaceEdit{
264+
Changes: map[protocol.DocumentURI][]protocol.TextEdit{
265+
params.TextDocument.URI: edits,
266+
},
267+
},
268+
},
269+
}, nil
255270
}
256271

257272
func (s *server) CodeLens(context.Context, *protocol.CodeLensParams) ([]protocol.CodeLens, error) {

internal/lsp/source/format.go

+18-4
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ import (
1313
"go/format"
1414

1515
"golang.org/x/tools/go/ast/astutil"
16+
"golang.org/x/tools/imports"
1617
)
1718

18-
// Format formats a document with a given range.
19+
// Format formats a file with a given range.
1920
func Format(ctx context.Context, f File, rng Range) ([]TextEdit, error) {
2021
fAST, err := f.GetAST()
2122
if err != nil {
@@ -54,11 +55,24 @@ func Format(ctx context.Context, f File, rng Range) ([]TextEdit, error) {
5455
if err := format.Node(buf, fset, node); err != nil {
5556
return nil, err
5657
}
57-
// TODO(rstambler): Compute text edits instead of replacing whole file.
58+
return computeTextEdits(rng, buf.String()), nil
59+
}
60+
61+
// Imports formats a file using the goimports tool.
62+
func Imports(ctx context.Context, filename string, content []byte, rng Range) ([]TextEdit, error) {
63+
content, err := imports.Process(filename, content, nil)
64+
if err != nil {
65+
return nil, err
66+
}
67+
return computeTextEdits(rng, string(content)), nil
68+
}
69+
70+
// TODO(rstambler): Compute text edits instead of replacing whole file.
71+
func computeTextEdits(rng Range, content string) []TextEdit {
5872
return []TextEdit{
5973
{
6074
Range: rng,
61-
NewText: buf.String(),
75+
NewText: content,
6276
},
63-
}, nil
77+
}
6478
}

0 commit comments

Comments
 (0)