internal/lsp: change File.Read to a File.GetContent accessor

Like the previous change to the FIle interface, we treat Read as if it were an
accessor, we remember the content part but not the error part, and we may fill
it in asynchronously, so this change makes it explicit.
In the future we should probably trap the error in the read and push it back
through another channel though, it will be the root cause of later errors.

Change-Id: I3d374dd557178b4e8c5544813cd77f5c0faefe5b
Reviewed-on: https://go-review.googlesource.com/c/162403
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Ian Cottrell 2019-02-13 16:08:29 -05:00
parent 88f95592de
commit 508f945e1a
7 changed files with 24 additions and 43 deletions

View File

@ -24,11 +24,12 @@ type File struct {
pkg *packages.Package
}
// Read returns the contents of the file, reading it from file system if needed.
func (f *File) Read() ([]byte, error) {
// GetContent returns the contents of the file, reading it from file system if needed.
func (f *File) GetContent() []byte {
f.view.mu.Lock()
defer f.view.mu.Unlock()
return f.read()
f.read()
return f.content
}
func (f *File) GetFileSet() *token.FileSet {
@ -69,19 +70,18 @@ func (f *File) GetPackage() *packages.Package {
}
// read is the internal part of Read that presumes the lock is already held
func (f *File) read() ([]byte, error) {
func (f *File) read() {
if f.content != nil {
return f.content, nil
return
}
// we don't know the content yet, so read it
filename, err := f.URI.Filename()
if err != nil {
return nil, err
return
}
content, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
return
}
f.content = content
return f.content, nil
}

View File

@ -2,7 +2,6 @@ package lsp
import (
"context"
"go/token"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/lsp/source"
@ -26,21 +25,19 @@ func formatRange(ctx context.Context, v source.View, uri protocol.DocumentURI, r
} else {
r = fromProtocolRange(tok, *rng)
}
content, err := f.Read()
if err != nil {
return nil, err
}
edits, err := source.Format(ctx, f, r)
if err != nil {
return nil, err
}
return toProtocolEdits(tok, content, edits), nil
return toProtocolEdits(f, edits), nil
}
func toProtocolEdits(tok *token.File, content []byte, edits []source.TextEdit) []protocol.TextEdit {
func toProtocolEdits(f source.File, edits []source.TextEdit) []protocol.TextEdit {
if edits == nil {
return nil
}
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

View File

@ -25,13 +25,9 @@ func organizeImports(ctx context.Context, v source.View, uri protocol.DocumentUR
Start: tok.Pos(0),
End: tok.Pos(tok.Size()),
}
content, err := f.Read()
edits, err := source.Imports(ctx, f, r)
if err != nil {
return nil, err
}
edits, err := source.Imports(ctx, tok, content, r)
if err != nil {
return nil, err
}
return toProtocolEdits(tok, content, edits), nil
return toProtocolEdits(f, edits), nil
}

View File

@ -374,10 +374,6 @@ func (f formats) test(t *testing.T, s *server) {
if err != nil {
t.Error(err)
}
original, err := f.Read()
if err != nil {
t.Error(err)
}
var ops []*diff.Op
for _, edit := range edits {
start := int(edit.Range.Start.Line)
@ -400,7 +396,7 @@ func (f formats) test(t *testing.T, s *server) {
})
}
}
split := strings.SplitAfter(string(original), "\n")
split := strings.SplitAfter(string(f.GetContent()), "\n")
got := strings.Join(diff.ApplyEdits(split, ops), "")
if gofmted != got {
t.Errorf("format failed for %s: expected '%v', got '%v'", filename, gofmted, got)

View File

@ -80,11 +80,7 @@ func Diagnostics(ctx context.Context, v View, uri URI) (map[string][]Diagnostic,
continue
}
diagTok := diagFile.GetToken()
content, err := diagFile.Read()
if err != nil {
continue
}
end, err := identifierEnd(content, pos.Line, pos.Column)
end, err := identifierEnd(diagFile.GetContent(), pos.Line, pos.Column)
// Don't set a range if it's anything other than a type error.
if err != nil || diag.Kind != packages.TypeError {
end = 0

View File

@ -52,25 +52,21 @@ func Format(ctx context.Context, f File, rng Range) ([]TextEdit, error) {
if err := format.Node(buf, fset, node); err != nil {
return nil, err
}
content, err := f.Read()
if err != nil {
return nil, err
}
tok := f.GetToken()
return computeTextEdits(rng, tok, string(content), buf.String()), nil
return computeTextEdits(rng, f, buf.String()), nil
}
// Imports formats a file using the goimports tool.
func Imports(ctx context.Context, tok *token.File, content []byte, rng Range) ([]TextEdit, error) {
formatted, err := imports.Process(tok.Name(), content, nil)
func Imports(ctx context.Context, f File, rng Range) ([]TextEdit, error) {
formatted, err := imports.Process(f.GetToken().Name(), f.GetContent(), nil)
if err != nil {
return nil, err
}
return computeTextEdits(rng, tok, string(content), string(formatted)), nil
return computeTextEdits(rng, f, string(formatted)), nil
}
func computeTextEdits(rng Range, tok *token.File, unformatted, formatted string) (edits []TextEdit) {
u := strings.SplitAfter(unformatted, "\n")
func computeTextEdits(rng Range, file File, formatted string) (edits []TextEdit) {
u := strings.SplitAfter(string(file.GetContent()), "\n")
tok := file.GetToken()
f := strings.SplitAfter(formatted, "\n")
for _, op := range diff.Operations(u, f) {
start := lineStart(tok, op.I1+1)

View File

@ -31,7 +31,7 @@ type File interface {
GetFileSet() *token.FileSet
GetPackage() *packages.Package
GetToken() *token.File
Read() ([]byte, error)
GetContent() []byte
}
// Range represents a start and end position.