internal/lsp: stop requiring file kind when fetching a file
This change consolidates the FileKind into only the FileHandle.
Previously, it had been set in multiple places, which required users to
pass in a FileKind when fetching a file. This resulted in confusion,
particularly in places when users did not have access to the file kind.
Change-Id: I9e07d7320c46a21d453ffe108d1431a615706a71
Reviewed-on: https://go-review.googlesource.com/c/tools/+/213459
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
diff --git a/internal/lsp/cache/builtin.go b/internal/lsp/cache/builtin.go
index 00d6832..f9d2fe5 100644
--- a/internal/lsp/cache/builtin.go
+++ b/internal/lsp/cache/builtin.go
@@ -44,7 +44,7 @@
pkg := pkgs[0]
files := make(map[string]*ast.File)
for _, filename := range pkg.GoFiles {
- fh := v.session.GetFile(span.FileURI(filename), source.Go)
+ fh := v.session.GetFile(span.FileURI(filename))
ph := v.session.cache.ParseGoHandle(fh, source.ParseFull)
v.builtin.files = append(v.builtin.files, ph)
file, _, _, err := ph.Parse(ctx)
diff --git a/internal/lsp/cache/cache.go b/internal/lsp/cache/cache.go
index 41d3d80..e6a185a 100644
--- a/internal/lsp/cache/cache.go
+++ b/internal/lsp/cache/cache.go
@@ -56,8 +56,8 @@
err error
}
-func (c *cache) GetFile(uri span.URI, kind source.FileKind) source.FileHandle {
- underlying := c.fs.GetFile(uri, kind)
+func (c *cache) GetFile(uri span.URI) source.FileHandle {
+ underlying := c.fs.GetFile(uri)
key := fileKey{
identity: underlying.Identity(),
}
diff --git a/internal/lsp/cache/external.go b/internal/lsp/cache/external.go
index 2498bf6..3f98b13 100644
--- a/internal/lsp/cache/external.go
+++ b/internal/lsp/cache/external.go
@@ -27,11 +27,12 @@
identity source.FileIdentity
}
-func (fs *nativeFileSystem) GetFile(uri span.URI, kind source.FileKind) source.FileHandle {
+func (fs *nativeFileSystem) GetFile(uri span.URI) source.FileHandle {
identifier := "DOES NOT EXIST"
if fi, err := os.Stat(uri.Filename()); err == nil {
identifier = fi.ModTime().String()
}
+ kind := source.DetectLanguage("", uri.Filename())
return &nativeFileHandle{
fs: fs,
identity: source.FileIdentity{
diff --git a/internal/lsp/cache/file.go b/internal/lsp/cache/file.go
deleted file mode 100644
index 534892b..0000000
--- a/internal/lsp/cache/file.go
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package cache
-
-import (
- "go/token"
- "path/filepath"
- "strings"
-
- "golang.org/x/tools/internal/lsp/source"
- "golang.org/x/tools/internal/span"
-)
-
-// fileBase holds the common functionality for all files.
-// It is intended to be embedded in the file implementations
-type fileBase struct {
- uris []span.URI
- fname string
- kind source.FileKind
-
- view *view
-}
-
-func dir(filename string) string {
- return strings.ToLower(filepath.Dir(filename))
-}
-
-func basename(filename string) string {
- return strings.ToLower(filepath.Base(filename))
-}
-
-func (f *fileBase) URI() span.URI {
- return f.uris[0]
-}
-
-func (f *fileBase) filename() string {
- return f.fname
-}
-
-// View returns the view associated with the file.
-func (f *fileBase) View() source.View {
- return f.view
-}
-
-func (f *fileBase) FileSet() *token.FileSet {
- return f.view.Session().Cache().FileSet()
-}
diff --git a/internal/lsp/cache/overlay.go b/internal/lsp/cache/overlay.go
index 8e6b794..ad07a18 100644
--- a/internal/lsp/cache/overlay.go
+++ b/internal/lsp/cache/overlay.go
@@ -41,7 +41,7 @@
return o.text, o.hash, nil
}
-func (s *session) updateOverlay(ctx context.Context, c source.FileModification) error {
+func (s *session) updateOverlay(ctx context.Context, c source.FileModification) (source.FileKind, error) {
s.overlayMu.Lock()
defer s.overlayMu.Unlock()
@@ -54,18 +54,18 @@
kind = source.DetectLanguage(c.LanguageID, c.URI.Filename())
default:
if !ok {
- return errors.Errorf("updateOverlay: modifying unopened overlay %v", c.URI)
+ return -1, errors.Errorf("updateOverlay: modifying unopened overlay %v", c.URI)
}
kind = o.kind
}
if kind == source.UnknownKind {
- return errors.Errorf("updateOverlay: unknown file kind for %s", c.URI)
+ return -1, errors.Errorf("updateOverlay: unknown file kind for %s", c.URI)
}
// Closing a file just deletes its overlay.
if c.Action == source.Close {
delete(s.overlays, c.URI)
- return nil
+ return kind, nil
}
// If the file is on disk, check if its content is the same as the overlay.
@@ -77,15 +77,15 @@
var sameContentOnDisk bool
switch c.Action {
case source.Open:
- _, h, err := s.cache.GetFile(c.URI, kind).Read(ctx)
+ _, h, err := s.cache.GetFile(c.URI).Read(ctx)
sameContentOnDisk = (err == nil && h == hash)
case source.Save:
// Make sure the version and content (if present) is the same.
if o.version != c.Version {
- return errors.Errorf("updateOverlay: saving %s at version %v, currently at %v", c.URI, c.Version, o.version)
+ return -1, errors.Errorf("updateOverlay: saving %s at version %v, currently at %v", c.URI, c.Version, o.version)
}
if c.Text != nil && o.hash != hash {
- return errors.Errorf("updateOverlay: overlay %s changed on save", c.URI)
+ return -1, errors.Errorf("updateOverlay: overlay %s changed on save", c.URI)
}
sameContentOnDisk = true
}
@@ -98,7 +98,7 @@
hash: hash,
sameContentOnDisk: sameContentOnDisk,
}
- return nil
+ return kind, nil
}
func (s *session) readOverlay(uri span.URI) *overlay {
diff --git a/internal/lsp/cache/session.go b/internal/lsp/cache/session.go
index 2c23aa8..00f59f4 100644
--- a/internal/lsp/cache/session.go
+++ b/internal/lsp/cache/session.go
@@ -256,7 +256,8 @@
ctx = telemetry.URI.With(ctx, c.URI)
// Perform the session-specific updates.
- if err := s.updateOverlay(ctx, c); err != nil {
+ kind, err := s.updateOverlay(ctx, c)
+ if err != nil {
return nil, err
}
@@ -265,12 +266,11 @@
if view.Ignore(c.URI) {
return nil, errors.Errorf("ignored file %v", c.URI)
}
- // Set the content for the file, only for didChange and didClose events.
- f, err := view.getFileLocked(ctx, c.URI)
- if err != nil {
+ // Make sure to add the file to the view.
+ if _, err := view.getFileLocked(ctx, c.URI); err != nil {
return nil, err
}
- snapshots = append(snapshots, view.invalidateContent(ctx, c.URI, f.kind, c.Action))
+ snapshots = append(snapshots, view.invalidateContent(ctx, c.URI, kind, c.Action))
}
return snapshots, nil
}
@@ -283,12 +283,12 @@
return open
}
-func (s *session) GetFile(uri span.URI, kind source.FileKind) source.FileHandle {
+func (s *session) GetFile(uri span.URI) source.FileHandle {
if overlay := s.readOverlay(uri); overlay != nil {
return overlay
}
// Fall back to the cache-level file system.
- return s.cache.GetFile(uri, kind)
+ return s.cache.GetFile(uri)
}
func (s *session) DidChangeOutOfBand(ctx context.Context, uri span.URI, action source.FileAction) bool {
@@ -296,10 +296,12 @@
if err != nil {
return false
}
- f, err := view.getFileLocked(ctx, uri)
- if err != nil {
+ // Make sure that the file is part of the view.
+ if _, err := view.getFileLocked(ctx, uri); err != nil {
return false
}
- view.invalidateContent(ctx, f.URI(), f.kind, action)
+ // TODO(golang/go#31553): Remove this when this issue has been resolved.
+ kind := source.DetectLanguage("", uri.Filename())
+ view.invalidateContent(ctx, uri, kind, action)
return true
}
diff --git a/internal/lsp/cache/snapshot.go b/internal/lsp/cache/snapshot.go
index d9c4142..7e3d3b7 100644
--- a/internal/lsp/cache/snapshot.go
+++ b/internal/lsp/cache/snapshot.go
@@ -8,6 +8,8 @@
"context"
"io"
"os"
+ "path/filepath"
+ "strings"
"sync"
"golang.org/x/tools/go/analysis"
@@ -92,7 +94,7 @@
return nil, nil, err
}
// Go directly to disk to get the correct FileHandle, since we just copied the file without invalidating the snapshot.
- tempfh := s.view.Session().Cache().GetFile(span.FileURI(s.view.modfiles.temp), source.Mod)
+ tempfh := s.view.Session().Cache().GetFile(span.FileURI(s.view.modfiles.temp))
if tempfh == nil {
return nil, nil, errors.Errorf("temporary go.mod filehandle is nil")
}
@@ -533,9 +535,7 @@
s.view.mu.Lock()
defer s.view.mu.Unlock()
- // TODO(rstambler): Should there be a version that provides a kind explicitly?
- kind := source.DetectLanguage("", uri.Filename())
- f, err := s.view.getFile(ctx, uri, kind)
+ f, err := s.view.getFile(ctx, uri)
if err != nil {
return nil, err
}
@@ -547,7 +547,7 @@
defer s.mu.Unlock()
if _, ok := s.files[f.URI()]; !ok {
- s.files[f.URI()] = s.view.session.GetFile(f.URI(), f.kind)
+ s.files[f.URI()] = s.view.session.GetFile(f.URI())
}
return s.files[f.URI()]
}
@@ -633,7 +633,7 @@
result.files[k] = v
}
// Handle the invalidated file; it may have new contents or not exist.
- currentFH := s.view.session.GetFile(withoutURI, withoutFileKind)
+ currentFH := s.view.session.GetFile(withoutURI)
if _, _, err := currentFH.Read(ctx); os.IsNotExist(err) {
delete(result.files, withoutURI)
} else {
@@ -680,6 +680,10 @@
return result
}
+func dir(filename string) string {
+ return strings.ToLower(filepath.Dir(filename))
+}
+
func (s *snapshot) ID() uint64 {
return s.id
}
diff --git a/internal/lsp/cache/view.go b/internal/lsp/cache/view.go
index c546ff7..f335c9c 100644
--- a/internal/lsp/cache/view.go
+++ b/internal/lsp/cache/view.go
@@ -96,6 +96,28 @@
initializationError error
}
+// fileBase holds the common functionality for all files.
+// It is intended to be embedded in the file implementations
+type fileBase struct {
+ uris []span.URI
+ fname string
+
+ view *view
+}
+
+func (f *fileBase) URI() span.URI {
+ return f.uris[0]
+}
+
+func (f *fileBase) filename() string {
+ return f.fname
+}
+
+func (f *fileBase) addURI(uri span.URI) int {
+ f.uris = append(f.uris, uri)
+ return len(f.uris)
+}
+
// modfiles holds the real and temporary go.mod files that are attributed to a view.
type modfiles struct {
real, temp string
@@ -278,7 +300,7 @@
// and modules included by a replace directive. Return true if
// any of these file versions do not match.
for filename, version := range v.modFileVersions {
- if version != v.fileVersion(filename, source.Mod) {
+ if version != v.fileVersion(filename) {
return true
}
}
@@ -297,17 +319,101 @@
// and modules included by a replace directive in the resolver.
for _, mod := range r.ModsByModPath {
if (mod.Main || mod.Replace != nil) && mod.GoMod != "" {
- v.modFileVersions[mod.GoMod] = v.fileVersion(mod.GoMod, source.Mod)
+ v.modFileVersions[mod.GoMod] = v.fileVersion(mod.GoMod)
}
}
}
-func (v *view) fileVersion(filename string, kind source.FileKind) string {
+func (v *view) fileVersion(filename string) string {
uri := span.FileURI(filename)
- fh := v.session.GetFile(uri, kind)
+ fh := v.session.GetFile(uri)
return fh.Identity().String()
}
+func (v *view) mapFile(uri span.URI, f *fileBase) {
+ v.filesByURI[uri] = f
+ if f.addURI(uri) == 1 {
+ basename := basename(f.filename())
+ v.filesByBase[basename] = append(v.filesByBase[basename], f)
+ }
+}
+
+func basename(filename string) string {
+ return strings.ToLower(filepath.Base(filename))
+}
+
+// FindFile returns the file if the given URI is already a part of the view.
+func (v *view) findFileLocked(ctx context.Context, uri span.URI) *fileBase {
+ v.mu.Lock()
+ defer v.mu.Unlock()
+
+ f, err := v.findFile(uri)
+ if err != nil {
+ return nil
+ }
+ return f
+}
+
+// getFileLocked returns a File for the given URI. It will always succeed because it
+// adds the file to the managed set if needed.
+func (v *view) getFileLocked(ctx context.Context, uri span.URI) (*fileBase, error) {
+ v.mu.Lock()
+ defer v.mu.Unlock()
+
+ return v.getFile(ctx, uri)
+}
+
+// getFile is the unlocked internal implementation of GetFile.
+func (v *view) getFile(ctx context.Context, uri span.URI) (*fileBase, error) {
+ f, err := v.findFile(uri)
+ if err != nil {
+ return nil, err
+ } else if f != nil {
+ return f, nil
+ }
+ f = &fileBase{
+ view: v,
+ fname: uri.Filename(),
+ }
+ v.mapFile(uri, f)
+ return f, nil
+}
+
+// findFile checks the cache for any file matching the given uri.
+//
+// An error is only returned for an irreparable failure, for example, if the
+// filename in question does not exist.
+func (v *view) findFile(uri span.URI) (*fileBase, error) {
+ if f := v.filesByURI[uri]; f != nil {
+ // a perfect match
+ return f, nil
+ }
+ // no exact match stored, time to do some real work
+ // check for any files with the same basename
+ fname := uri.Filename()
+ basename := basename(fname)
+ if candidates := v.filesByBase[basename]; candidates != nil {
+ pathStat, err := os.Stat(fname)
+ if os.IsNotExist(err) {
+ return nil, err
+ }
+ if err != nil {
+ return nil, nil // the file may exist, return without an error
+ }
+ for _, c := range candidates {
+ if cStat, err := os.Stat(c.filename()); err == nil {
+ if os.SameFile(pathStat, cStat) {
+ // same file, map it
+ v.mapFile(uri, c)
+ return c, nil
+ }
+ }
+ }
+ }
+ // no file with a matching name was found, it wasn't in our cache
+ return nil, nil
+}
+
func (v *view) Shutdown(ctx context.Context) {
v.session.removeView(ctx, v)
}
@@ -427,94 +533,6 @@
v.backgroundCtx, v.cancel = context.WithCancel(v.baseCtx)
}
-// FindFile returns the file if the given URI is already a part of the view.
-func (v *view) findFileLocked(ctx context.Context, uri span.URI) *fileBase {
- v.mu.Lock()
- defer v.mu.Unlock()
-
- f, err := v.findFile(uri)
- if err != nil {
- return nil
- }
- return f
-}
-
-// getFileLocked returns a File for the given URI. It will always succeed because it
-// adds the file to the managed set if needed.
-func (v *view) getFileLocked(ctx context.Context, uri span.URI) (*fileBase, error) {
- v.mu.Lock()
- defer v.mu.Unlock()
-
- // TODO(rstambler): Should there be a version that provides a kind explicitly?
- kind := source.DetectLanguage("", uri.Filename())
- return v.getFile(ctx, uri, kind)
-}
-
-// getFile is the unlocked internal implementation of GetFile.
-func (v *view) getFile(ctx context.Context, uri span.URI, kind source.FileKind) (*fileBase, error) {
- f, err := v.findFile(uri)
- if err != nil {
- return nil, err
- } else if f != nil {
- return f, nil
- }
- f = &fileBase{
- view: v,
- fname: uri.Filename(),
- kind: kind,
- }
- v.mapFile(uri, f)
- return f, nil
-}
-
-// findFile checks the cache for any file matching the given uri.
-//
-// An error is only returned for an irreparable failure, for example, if the
-// filename in question does not exist.
-func (v *view) findFile(uri span.URI) (*fileBase, error) {
- if f := v.filesByURI[uri]; f != nil {
- // a perfect match
- return f, nil
- }
- // no exact match stored, time to do some real work
- // check for any files with the same basename
- fname := uri.Filename()
- basename := basename(fname)
- if candidates := v.filesByBase[basename]; candidates != nil {
- pathStat, err := os.Stat(fname)
- if os.IsNotExist(err) {
- return nil, err
- }
- if err != nil {
- return nil, nil // the file may exist, return without an error
- }
- for _, c := range candidates {
- if cStat, err := os.Stat(c.filename()); err == nil {
- if os.SameFile(pathStat, cStat) {
- // same file, map it
- v.mapFile(uri, c)
- return c, nil
- }
- }
- }
- }
- // no file with a matching name was found, it wasn't in our cache
- return nil, nil
-}
-
-func (f *fileBase) addURI(uri span.URI) int {
- f.uris = append(f.uris, uri)
- return len(f.uris)
-}
-
-func (v *view) mapFile(uri span.URI, f *fileBase) {
- v.filesByURI[uri] = f
- if f.addURI(uri) == 1 {
- basename := basename(f.filename())
- v.filesByBase[basename] = append(v.filesByBase[basename], f)
- }
-}
-
func (v *view) FindPosInPackage(searchpkg source.Package, pos token.Pos) (*ast.File, source.Package, error) {
tok := v.session.cache.fset.File(pos)
if tok == nil {
diff --git a/internal/lsp/source/view.go b/internal/lsp/source/view.go
index 541d9e2..3dd8ec9 100644
--- a/internal/lsp/source/view.go
+++ b/internal/lsp/source/view.go
@@ -235,7 +235,7 @@
// FileSystem is the interface to something that provides file contents.
type FileSystem interface {
// GetFile returns a handle for the specified file.
- GetFile(uri span.URI, kind FileKind) FileHandle
+ GetFile(uri span.URI) FileHandle
}
// ParseGoHandle represents a handle to the AST for a file.
diff --git a/internal/lsp/text_synchronization.go b/internal/lsp/text_synchronization.go
index 3795e70..2f2ad81 100644
--- a/internal/lsp/text_synchronization.go
+++ b/internal/lsp/text_synchronization.go
@@ -144,7 +144,7 @@
}
func (s *Server) applyIncrementalChanges(ctx context.Context, uri span.URI, changes []protocol.TextDocumentContentChangeEvent) ([]byte, error) {
- content, _, err := s.session.GetFile(uri, source.UnknownKind).Read(ctx)
+ content, _, err := s.session.GetFile(uri).Read(ctx)
if err != nil {
return nil, jsonrpc2.NewErrorf(jsonrpc2.CodeInternalError, "file not found (%v)", err)
}