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 pkg *packages.Package
} }
// Read returns the contents of the file, reading it from file system if needed. // GetContent returns the contents of the file, reading it from file system if needed.
func (f *File) Read() ([]byte, error) { func (f *File) GetContent() []byte {
f.view.mu.Lock() f.view.mu.Lock()
defer f.view.mu.Unlock() defer f.view.mu.Unlock()
return f.read() f.read()
return f.content
} }
func (f *File) GetFileSet() *token.FileSet { 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 // 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 { if f.content != nil {
return f.content, nil return
} }
// we don't know the content yet, so read it // we don't know the content yet, so read it
filename, err := f.URI.Filename() filename, err := f.URI.Filename()
if err != nil { if err != nil {
return nil, err return
} }
content, err := ioutil.ReadFile(filename) content, err := ioutil.ReadFile(filename)
if err != nil { if err != nil {
return nil, err return
} }
f.content = content f.content = content
return f.content, nil
} }

View File

@ -2,7 +2,6 @@ package lsp
import ( import (
"context" "context"
"go/token"
"golang.org/x/tools/internal/lsp/protocol" "golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/lsp/source" "golang.org/x/tools/internal/lsp/source"
@ -26,21 +25,19 @@ func formatRange(ctx context.Context, v source.View, uri protocol.DocumentURI, r
} else { } else {
r = fromProtocolRange(tok, *rng) r = fromProtocolRange(tok, *rng)
} }
content, err := f.Read()
if err != nil {
return nil, err
}
edits, err := source.Format(ctx, f, r) edits, err := source.Format(ctx, f, r)
if err != nil { if err != nil {
return nil, err 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 { if edits == nil {
return nil return nil
} }
tok := f.GetToken()
content := f.GetContent()
// When a file ends with an empty line, the newline character is counted // 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 // as part of the previous line. This causes the formatter to insert
// another unnecessary newline on each formatting. We handle this case by // 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), Start: tok.Pos(0),
End: tok.Pos(tok.Size()), End: tok.Pos(tok.Size()),
} }
content, err := f.Read() edits, err := source.Imports(ctx, f, r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
edits, err := source.Imports(ctx, tok, content, r) return toProtocolEdits(f, edits), nil
if err != nil {
return nil, err
}
return toProtocolEdits(tok, content, edits), nil
} }

View File

@ -374,10 +374,6 @@ func (f formats) test(t *testing.T, s *server) {
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
original, err := f.Read()
if err != nil {
t.Error(err)
}
var ops []*diff.Op var ops []*diff.Op
for _, edit := range edits { for _, edit := range edits {
start := int(edit.Range.Start.Line) 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), "") got := strings.Join(diff.ApplyEdits(split, ops), "")
if gofmted != got { if gofmted != got {
t.Errorf("format failed for %s: expected '%v', got '%v'", filename, 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 continue
} }
diagTok := diagFile.GetToken() diagTok := diagFile.GetToken()
content, err := diagFile.Read() end, err := identifierEnd(diagFile.GetContent(), pos.Line, pos.Column)
if err != nil {
continue
}
end, err := identifierEnd(content, pos.Line, pos.Column)
// Don't set a range if it's anything other than a type error. // Don't set a range if it's anything other than a type error.
if err != nil || diag.Kind != packages.TypeError { if err != nil || diag.Kind != packages.TypeError {
end = 0 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 { if err := format.Node(buf, fset, node); err != nil {
return nil, err return nil, err
} }
content, err := f.Read() return computeTextEdits(rng, f, buf.String()), nil
if err != nil {
return nil, err
}
tok := f.GetToken()
return computeTextEdits(rng, tok, string(content), buf.String()), nil
} }
// Imports formats a file using the goimports tool. // Imports formats a file using the goimports tool.
func Imports(ctx context.Context, tok *token.File, content []byte, rng Range) ([]TextEdit, error) { func Imports(ctx context.Context, f File, rng Range) ([]TextEdit, error) {
formatted, err := imports.Process(tok.Name(), content, nil) formatted, err := imports.Process(f.GetToken().Name(), f.GetContent(), nil)
if err != nil { if err != nil {
return nil, err 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) { func computeTextEdits(rng Range, file File, formatted string) (edits []TextEdit) {
u := strings.SplitAfter(unformatted, "\n") u := strings.SplitAfter(string(file.GetContent()), "\n")
tok := file.GetToken()
f := strings.SplitAfter(formatted, "\n") f := strings.SplitAfter(formatted, "\n")
for _, op := range diff.Operations(u, f) { for _, op := range diff.Operations(u, f) {
start := lineStart(tok, op.I1+1) start := lineStart(tok, op.I1+1)

View File

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