Skip to content

internal/lsp: error handling #75

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions cmd/gopls/GOPLS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# `GOPLS.md`

## development

1. Create a fork of <https://github.com/golang/tools>, e.g., <https://github.com/banaio/tools>.
2. Clone your fork.
3. Modify some code in the `internal/lsp` directory. A good candidate is the `Initialize` function
in `internal/lsp/server.go`.
4. Run the `cmd/gopls/install-gopls.sh` script to update the `gopls` binary.

```sh
git clone git@github.com:banaio/tools.git
cd tools
vim internal/lsp/server.go # modify some code
cmd/gopls/install-gopls.sh
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is not needed.

24 changes: 24 additions & 0 deletions cmd/gopls/install-gopls.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/bash -e
RACE=false
if [[ "${1}" == "-race" ]]; then
RACE=true
fi

echo -e "\\033[92m ---> currrent cmd/gopls \\033[0m"
find "$(command -v gopls)" -printf "%c %p\\n"

echo -e "\\033[92m ---> testing internal/lsp (race=${RACE}) \\033[0m"
if ${RACE}; then
go test -race "$(pwd)"/internal/lsp/...
else
go test "$(pwd)"/internal/lsp/...
fi

if ${RACE}; then
go install -a -race "$(pwd)"/cmd/gopls
else
go install -a "$(pwd)"/cmd/gopls
fi

echo -e "\\033[92m ---> installed cmd/gopls (race=${RACE}) \\033[0m"
find "$(command -v gopls)" -printf "%c %p\\n"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

43 changes: 24 additions & 19 deletions internal/lsp/cache/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,63 +25,68 @@ type File struct {
}

// GetContent returns the contents of the file, reading it from file system if needed.
func (f *File) GetContent() []byte {
func (f *File) GetContent() ([]byte, error) {
f.view.mu.Lock()
defer f.view.mu.Unlock()
f.read()
return f.content
if f.content != nil {
return f.content, nil
}
content, err := f.read()
if err != nil {
return nil, err
}
f.content = content
return f.content, nil
}

func (f *File) GetFileSet() *token.FileSet {
return f.view.Config.Fset
}

func (f *File) GetToken() *token.File {
func (f *File) GetToken() (*token.File, error) {
f.view.mu.Lock()
defer f.view.mu.Unlock()
if f.token == nil {
if err := f.view.parse(f.URI); err != nil {
return nil
return nil, err
}
}
return f.token
return f.token, nil
}

func (f *File) GetAST() *ast.File {
func (f *File) GetAST() (*ast.File, error) {
f.view.mu.Lock()
defer f.view.mu.Unlock()
if f.ast == nil {
if err := f.view.parse(f.URI); err != nil {
return nil
return nil, err
}
}
return f.ast
return f.ast, nil
}

func (f *File) GetPackage() *packages.Package {
func (f *File) GetPackage() (*packages.Package, error) {
f.view.mu.Lock()
defer f.view.mu.Unlock()
if f.pkg == nil {
if err := f.view.parse(f.URI); err != nil {
return nil
return nil, err
}
}
return f.pkg
return f.pkg, nil
}

// read is the internal part of Read that presumes the lock is already held
func (f *File) read() {
if f.content != nil {
return
}
func (f *File) read() ([]byte, error) {
// we don't know the content yet, so read it
filename, err := f.URI.Filename()
if err != nil {
return
return nil, err
}
content, err := ioutil.ReadFile(filename)
if err != nil {
return
return nil, err
}
f.content = content
// f.content = content
return content, nil
}
5 changes: 4 additions & 1 deletion internal/lsp/cache/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ func (v *View) FileSet() *token.FileSet {
}

func (v *View) GetAnalysisCache() *source.AnalysisCache {
v.mu.Lock()
defer v.mu.Unlock()

if v.analysisCache == nil {
v.analysisCache = source.NewAnalysisCache()
}
Expand Down Expand Up @@ -91,8 +94,8 @@ func (v *View) SetContent(ctx context.Context, uri source.URI, content []byte) (
// adds the file to the managed set if needed.
func (v *View) GetFile(ctx context.Context, uri source.URI) (source.File, error) {
v.mu.Lock()
defer v.mu.Unlock()
f := v.getFile(uri)
v.mu.Unlock()
return f, nil
}

Expand Down
10 changes: 8 additions & 2 deletions internal/lsp/cmd/definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ func (d *definition) Run(ctx context.Context, args ...string) error {
if err != nil {
return err
}
tok := f.GetToken()
tok, err := f.GetToken()
if err != nil {
return err
}
pos := tok.Pos(from.Start.Offset)
ident, err := source.Identifier(ctx, view, f, pos)
if err != nil {
Expand Down Expand Up @@ -115,7 +118,10 @@ func buildDefinition(view source.View, ident *source.IdentifierInfo) (*Definitio

func buildGuruDefinition(view source.View, ident *source.IdentifierInfo) (*guru.Definition, error) {
loc := newLocation(view.FileSet(), ident.Declaration.Range)
pkg := ident.File.GetPackage()
pkg, err := ident.File.GetPackage()
if err != nil {
return nil, err
}
// guru does not support ranges
loc.End = loc.Start
// Behavior that attempts to match the expected output for guru. For an example
Expand Down
29 changes: 22 additions & 7 deletions internal/lsp/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package lsp

import (
"context"
"errors"

"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/lsp/source"
Expand All @@ -17,7 +18,10 @@ func formatRange(ctx context.Context, v source.View, uri protocol.DocumentURI, r
if err != nil {
return nil, err
}
tok := f.GetToken()
tok, err := f.GetToken()
if err != nil {
return nil, err
}
var r source.Range
if rng == nil {
r.Start = tok.Pos(0)
Expand All @@ -29,15 +33,26 @@ func formatRange(ctx context.Context, v source.View, uri protocol.DocumentURI, r
if err != nil {
return nil, err
}
return toProtocolEdits(f, edits), nil

protocolEdits, err := toProtocolEdits(f, edits)
if err != nil {
return nil, err
}
return protocolEdits, nil
}

func toProtocolEdits(f source.File, edits []source.TextEdit) []protocol.TextEdit {
func toProtocolEdits(f source.File, edits []source.TextEdit) ([]protocol.TextEdit, error) {
if edits == nil {
return nil
return nil, errors.New("toProtocolEdits, edits == nil")
}
tok, err := f.GetToken()
if err != nil {
return nil, err
}
content, err := f.GetContent()
if err != nil {
return nil, err
}
tok := f.GetToken()
content := f.GetContent()
// When a file ends with an empty line, the newline character is counted
// as part of the previous line. This causes the formatter to insert
// another unnecessary newline on each formatting. We handle this case by
Expand All @@ -56,5 +71,5 @@ func toProtocolEdits(f source.File, edits []source.TextEdit) []protocol.TextEdit
NewText: edit.NewText,
}
}
return result
return result, nil
}
11 changes: 9 additions & 2 deletions internal/lsp/imports.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ func organizeImports(ctx context.Context, v source.View, uri protocol.DocumentUR
if err != nil {
return nil, err
}
tok := f.GetToken()
tok, err := f.GetToken()
if err != nil {
return nil, err
}
r := source.Range{
Start: tok.Pos(0),
End: tok.Pos(tok.Size()),
Expand All @@ -29,5 +32,9 @@ func organizeImports(ctx context.Context, v source.View, uri protocol.DocumentUR
if err != nil {
return nil, err
}
return toProtocolEdits(f, edits), nil
protocolEdits, err := toProtocolEdits(f, edits)
if err != nil {
return nil, err
}
return protocolEdits, nil
}
15 changes: 12 additions & 3 deletions internal/lsp/lsp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,11 @@ func (f formats) test(t *testing.T, s *server) {
})
}
}
split := strings.SplitAfter(string(f.GetContent()), "\n")
contents, err := f.GetContent()
if err != nil {
t.Error(err)
}
split := strings.SplitAfter(string(contents), "\n")
got := strings.Join(diff.ApplyEdits(split, ops), "")
if gofmted != got {
t.Errorf("format failed for %s: expected '%v', got '%v'", filename, gofmted, got)
Expand Down Expand Up @@ -440,6 +444,11 @@ func (d definitions) test(t *testing.T, s *server, typ bool) {
}

func (d definitions) collect(fset *token.FileSet, src, target packagestest.Range) {
loc := toProtocolLocation(fset, source.Range(src))
d[loc] = toProtocolLocation(fset, source.Range(target))
srcLocation, err := toProtocolLocation(fset, source.Range(src))
if err == nil {
targetLocation, err := toProtocolLocation(fset, source.Range(target))
if err == nil {
d[*srcLocation] = *targetLocation
}
}
}
15 changes: 11 additions & 4 deletions internal/lsp/position.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package lsp

import (
"context"
"fmt"
"go/token"
"net/url"

Expand Down Expand Up @@ -36,18 +37,24 @@ func fromProtocolLocation(ctx context.Context, v *cache.View, loc protocol.Locat
if err != nil {
return source.Range{}, err
}
tok := f.GetToken()
tok, err := f.GetToken()
if err != nil {
return source.Range{}, err
}
return fromProtocolRange(tok, loc.Range), nil
}

// toProtocolLocation converts from a source range back to a protocol location.
func toProtocolLocation(fset *token.FileSet, r source.Range) protocol.Location {
func toProtocolLocation(fset *token.FileSet, r source.Range) (*protocol.Location, error) {
tok := fset.File(r.Start)
if tok == nil {
return nil, fmt.Errorf("cannot get File for position=%+v", r.Start)
}
uri := source.ToURI(tok.Name())
return protocol.Location{
return &protocol.Location{
URI: protocol.DocumentURI(uri),
Range: toProtocolRange(tok, r),
}
}, nil
}

// fromProtocolRange converts a protocol range to a source range.
Expand Down
Loading