internal/lsp: prepare for non go files
This abstracts out the concrete file type so that we can support non go files.
Change-Id: I7447daa2ce076ec2867de9e59a0dedfe1a0553f5
Reviewed-on: https://go-review.googlesource.com/c/tools/+/175217
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/lsp/cache/check.go b/internal/lsp/cache/check.go
index 4a9ec80..90eb5a5 100644
--- a/internal/lsp/cache/check.go
+++ b/internal/lsp/cache/check.go
@@ -14,10 +14,11 @@
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/packages"
+ "golang.org/x/tools/internal/lsp/source"
"golang.org/x/tools/internal/span"
)
-func (v *view) parse(ctx context.Context, f *file) ([]packages.Error, error) {
+func (v *view) parse(ctx context.Context, file source.File) ([]packages.Error, error) {
v.mcache.mu.Lock()
defer v.mcache.mu.Unlock()
@@ -26,6 +27,11 @@
return nil, err
}
+ f, ok := file.(*goFile)
+ if !ok {
+ return nil, fmt.Errorf("not a go file: %v", file.URI())
+ }
+
// If the package for the file has not been invalidated by the application
// of the pending changes, there is no need to continue.
if f.isPopulated() {
@@ -37,7 +43,7 @@
return errs, err
}
if f.meta == nil {
- return nil, fmt.Errorf("no metadata found for %v", f.filename)
+ return nil, fmt.Errorf("no metadata found for %v", f.filename())
}
imp := &importer{
view: v,
@@ -56,19 +62,19 @@
// If we still have not found the package for the file, something is wrong.
if f.pkg == nil {
- return nil, fmt.Errorf("parse: no package found for %v", f.filename)
+ return nil, fmt.Errorf("parse: no package found for %v", f.filename())
}
return nil, nil
}
-func (v *view) checkMetadata(ctx context.Context, f *file) ([]packages.Error, error) {
- if v.reparseImports(ctx, f, f.filename) {
+func (v *view) checkMetadata(ctx context.Context, f *goFile) ([]packages.Error, error) {
+ if v.reparseImports(ctx, f, f.filename()) {
cfg := v.config
cfg.Mode = packages.LoadImports | packages.NeedTypesSizes
- pkgs, err := packages.Load(&cfg, fmt.Sprintf("file=%s", f.filename))
+ pkgs, err := packages.Load(&cfg, fmt.Sprintf("file=%s", f.filename()))
if len(pkgs) == 0 {
if err == nil {
- err = fmt.Errorf("no packages found for %s", f.filename)
+ err = fmt.Errorf("no packages found for %s", f.filename())
}
// Return this error as a diagnostic to the user.
return []packages.Error{
@@ -84,7 +90,7 @@
if len(pkg.Errors) > 0 {
return pkg.Errors, fmt.Errorf("package %s has errors, skipping type-checking", pkg.PkgPath)
}
- v.link(pkg.PkgPath, pkg, nil)
+ v.link(ctx, pkg.PkgPath, pkg, nil)
}
}
return nil, nil
@@ -92,7 +98,7 @@
// reparseImports reparses a file's import declarations to determine if they
// have changed.
-func (v *view) reparseImports(ctx context.Context, f *file, filename string) bool {
+func (v *view) reparseImports(ctx context.Context, f *goFile, filename string) bool {
if f.meta == nil {
return true
}
@@ -113,7 +119,7 @@
return false
}
-func (v *view) link(pkgPath string, pkg *packages.Package, parent *metadata) *metadata {
+func (v *view) link(ctx context.Context, pkgPath string, pkg *packages.Package, parent *metadata) *metadata {
m, ok := v.mcache.packages[pkgPath]
if !ok {
m = &metadata{
@@ -130,7 +136,12 @@
m.files = pkg.CompiledGoFiles
for _, filename := range m.files {
if f, _ := v.getFile(span.FileURI(filename)); f != nil {
- f.meta = m
+ gof, ok := f.(*goFile)
+ if !ok {
+ v.Logger().Errorf(ctx, "not a go file: %v", f.URI())
+ continue
+ }
+ gof.meta = m
}
}
// Connect the import graph.
@@ -140,7 +151,7 @@
}
for importPath, importPkg := range pkg.Imports {
if _, ok := m.children[importPath]; !ok {
- v.link(importPath, importPkg, m)
+ v.link(ctx, importPath, importPkg, m)
}
}
// Clear out any imports that have been removed.
@@ -273,10 +284,15 @@
v.Logger().Errorf(ctx, "no file: %v", err)
continue
}
- f.token = tok
- f.ast = file
- f.imports = f.ast.Imports
- f.pkg = pkg
+ gof, ok := f.(*goFile)
+ if !ok {
+ v.Logger().Errorf(ctx, "not a go file: %v", f.URI())
+ continue
+ }
+ gof.token = tok
+ gof.ast = file
+ gof.imports = gof.ast.Imports
+ gof.pkg = pkg
}
v.pcache.mu.Lock()
diff --git a/internal/lsp/cache/file.go b/internal/lsp/cache/file.go
index 1e69dfe..3711e96 100644
--- a/internal/lsp/cache/file.go
+++ b/internal/lsp/cache/file.go
@@ -16,17 +16,32 @@
"golang.org/x/tools/internal/span"
)
-// file holds all the information we know about a file.
-type file struct {
- uris []span.URI
- filename string
- basename string
+// viewFile extends source.File with helper methods for the view package.
+type viewFile interface {
+ source.File
+ setContent(content []byte)
+ filename() string
+ addURI(uri span.URI) int
+ isActive() bool
+}
+
+// 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
active bool
content []byte
- ast *ast.File
token *token.File
+}
+
+// goFile holds all the information we know about a go file.
+type goFile struct {
+ fileBase
+
+ ast *ast.File
pkg *pkg
meta *metadata
imports []*ast.ImportSpec
@@ -36,17 +51,25 @@
return strings.ToLower(filepath.Base(filename))
}
-func (f *file) URI() span.URI {
+func (f *fileBase) URI() span.URI {
return f.uris[0]
}
+func (f *fileBase) filename() string {
+ return f.fname
+}
+
+func (f *fileBase) isActive() bool {
+ return f.active
+}
+
// View returns the view associated with the file.
-func (f *file) View() source.View {
+func (f *fileBase) View() source.View {
return f.view
}
// GetContent returns the contents of the file, reading it from file system if needed.
-func (f *file) GetContent(ctx context.Context) []byte {
+func (f *fileBase) GetContent(ctx context.Context) []byte {
f.view.mu.Lock()
defer f.view.mu.Unlock()
@@ -57,14 +80,13 @@
return f.content
}
-func (f *file) GetFileSet(ctx context.Context) *token.FileSet {
+func (f *fileBase) GetFileSet(ctx context.Context) *token.FileSet {
return f.view.config.Fset
}
-func (f *file) GetToken(ctx context.Context) *token.File {
+func (f *goFile) GetToken(ctx context.Context) *token.File {
f.view.mu.Lock()
defer f.view.mu.Unlock()
-
if f.token == nil || len(f.view.contentChanges) > 0 {
if _, err := f.view.parse(ctx, f); err != nil {
return nil
@@ -73,7 +95,7 @@
return f.token
}
-func (f *file) GetAST(ctx context.Context) *ast.File {
+func (f *goFile) GetAST(ctx context.Context) *ast.File {
f.view.mu.Lock()
defer f.view.mu.Unlock()
@@ -85,7 +107,7 @@
return f.ast
}
-func (f *file) GetPackage(ctx context.Context) source.Package {
+func (f *goFile) GetPackage(ctx context.Context) source.Package {
f.view.mu.Lock()
defer f.view.mu.Unlock()
@@ -103,7 +125,7 @@
// read is the internal part of GetContent. It assumes that the caller is
// holding the mutex of the file's view.
-func (f *file) read(ctx context.Context) {
+func (f *fileBase) read(ctx context.Context) {
if f.content != nil {
if len(f.view.contentChanges) == 0 {
return
@@ -118,25 +140,25 @@
}
}
// We might have the content saved in an overlay.
- if content, ok := f.view.config.Overlay[f.filename]; ok {
+ if content, ok := f.view.config.Overlay[f.filename()]; ok {
f.content = content
return
}
// We don't know the content yet, so read it.
- content, err := ioutil.ReadFile(f.filename)
+ content, err := ioutil.ReadFile(f.filename())
if err != nil {
- f.view.Logger().Errorf(ctx, "unable to read file %s: %v", f.filename, err)
+ f.view.Logger().Errorf(ctx, "unable to read file %s: %v", f.filename(), err)
return
}
f.content = content
}
// isPopulated returns true if all of the computed fields of the file are set.
-func (f *file) isPopulated() bool {
+func (f *goFile) isPopulated() bool {
return f.ast != nil && f.token != nil && f.pkg != nil && f.meta != nil && f.imports != nil
}
-func (f *file) GetActiveReverseDeps(ctx context.Context) []source.File {
+func (f *goFile) GetActiveReverseDeps(ctx context.Context) []source.GoFile {
pkg := f.GetPackage(ctx)
if pkg == nil {
return nil
@@ -149,10 +171,10 @@
defer f.view.mcache.mu.Unlock()
seen := make(map[string]struct{}) // visited packages
- results := make(map[*file]struct{})
+ results := make(map[*goFile]struct{})
f.view.reverseDeps(ctx, seen, results, pkg.PkgPath())
- files := make([]source.File, 0, len(results))
+ files := make([]source.GoFile, 0, len(results))
for rd := range results {
if rd == nil {
continue
@@ -166,7 +188,7 @@
return files
}
-func (v *view) reverseDeps(ctx context.Context, seen map[string]struct{}, results map[*file]struct{}, pkgPath string) {
+func (v *view) reverseDeps(ctx context.Context, seen map[string]struct{}, results map[*goFile]struct{}, pkgPath string) {
if _, ok := seen[pkgPath]; ok {
return
}
@@ -176,8 +198,8 @@
return
}
for _, filename := range m.files {
- if f, err := v.getFile(span.FileURI(filename)); err == nil && f.active {
- results[f] = struct{}{}
+ if f, err := v.getFile(span.FileURI(filename)); err == nil && f.isActive() {
+ results[f.(*goFile)] = struct{}{}
}
}
for parentPkgPath := range m.parents {
diff --git a/internal/lsp/cache/parse.go b/internal/lsp/cache/parse.go
index 0cb67e0..b8264b5 100644
--- a/internal/lsp/cache/parse.go
+++ b/internal/lsp/cache/parse.go
@@ -50,7 +50,9 @@
}
var fAST *ast.File
if f != nil {
- fAST = f.ast
+ if gof, ok := f.(*goFile); ok {
+ fAST = gof.ast
+ }
}
wg.Add(1)
diff --git a/internal/lsp/cache/view.go b/internal/lsp/cache/view.go
index 8117065..15c054e 100644
--- a/internal/lsp/cache/view.go
+++ b/internal/lsp/cache/view.go
@@ -51,8 +51,8 @@
// keep track of files by uri and by basename, a single file may be mapped
// to multiple uris, and the same basename may map to multiple files
- filesByURI map[span.URI]*file
- filesByBase map[string][]*file
+ filesByURI map[span.URI]viewFile
+ filesByBase map[string][]viewFile
// contentChanges saves the content changes for a given state of the view.
// When type information is requested by the view, all of the dirty changes
@@ -105,8 +105,8 @@
config: *config,
name: name,
folder: folder,
- filesByURI: make(map[span.URI]*file),
- filesByBase: make(map[string][]*file),
+ filesByURI: make(map[span.URI]viewFile),
+ filesByBase: make(map[string][]viewFile),
contentChanges: make(map[span.URI]func()),
mcache: &metadataCache{
packages: make(map[string]*metadata),
@@ -227,6 +227,10 @@
if err != nil {
return
}
+ f.setContent(content)
+}
+
+func (f *goFile) setContent(content []byte) {
f.content = content
// TODO(rstambler): Should we recompute these here?
@@ -235,19 +239,19 @@
// Remove the package and all of its reverse dependencies from the cache.
if f.pkg != nil {
- v.remove(f.pkg.pkgPath, map[string]struct{}{})
+ f.view.remove(f.pkg.pkgPath, map[string]struct{}{})
}
switch {
case f.active && content == nil:
// The file was active, so we need to forget its content.
f.active = false
- delete(f.view.config.Overlay, f.filename)
+ delete(f.view.config.Overlay, f.filename())
f.content = nil
case content != nil:
// This is an active overlay, so we update the map.
f.active = true
- f.view.config.Overlay[f.filename] = f.content
+ f.view.config.Overlay[f.filename()] = f.content
}
}
@@ -270,14 +274,16 @@
// invalidated package.
for _, filename := range m.files {
if f, _ := v.findFile(span.FileURI(filename)); f != nil {
- f.pkg = nil
+ if gof, ok := f.(*goFile); ok {
+ gof.pkg = nil
+ }
}
}
delete(v.pcache.packages, pkgPath)
}
// FindFile returns the file if the given URI is already a part of the view.
-func (v *view) FindFile(ctx context.Context, uri span.URI) *file {
+func (v *view) FindFile(ctx context.Context, uri span.URI) source.File {
v.mu.Lock()
defer v.mu.Unlock()
f, err := v.findFile(uri)
@@ -301,7 +307,7 @@
}
// getFile is the unlocked internal implementation of GetFile.
-func (v *view) getFile(uri span.URI) (*file, error) {
+func (v *view) getFile(uri span.URI) (viewFile, error) {
filename, err := uri.Filename()
if err != nil {
return nil, err
@@ -314,9 +320,11 @@
} else if f != nil {
return f, nil
}
- f := &file{
- view: v,
- filename: filename,
+ f := &goFile{
+ fileBase: fileBase{
+ view: v,
+ fname: filename,
+ },
}
v.mapFile(uri, f)
return f, nil
@@ -340,7 +348,7 @@
//
// 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) (*file, error) {
+func (v *view) findFile(uri span.URI) (viewFile, error) {
if f := v.filesByURI[uri]; f != nil {
// a perfect match
return f, nil
@@ -360,7 +368,7 @@
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 cStat, err := os.Stat(c.filename()); err == nil {
if os.SameFile(pathStat, cStat) {
// same file, map it
v.mapFile(uri, c)
@@ -373,12 +381,16 @@
return nil, nil
}
-func (v *view) mapFile(uri span.URI, f *file) {
- v.filesByURI[uri] = f
+func (f *fileBase) addURI(uri span.URI) int {
f.uris = append(f.uris, uri)
- if f.basename == "" {
- f.basename = basename(f.filename)
- v.filesByBase[f.basename] = append(v.filesByBase[f.basename], f)
+ return len(f.uris)
+}
+
+func (v *view) mapFile(uri span.URI, f viewFile) {
+ v.filesByURI[uri] = f
+ if f.addURI(uri) == 1 {
+ basename := basename(f.filename())
+ v.filesByBase[basename] = append(v.filesByBase[basename], f)
}
}
diff --git a/internal/lsp/code_action.go b/internal/lsp/code_action.go
index e320f73..7518e9a 100644
--- a/internal/lsp/code_action.go
+++ b/internal/lsp/code_action.go
@@ -17,7 +17,7 @@
func (s *Server) codeAction(ctx context.Context, params *protocol.CodeActionParams) ([]protocol.CodeAction, error) {
uri := span.NewURI(params.TextDocument.URI)
view := s.findView(ctx, uri)
- _, m, err := newColumnMap(ctx, view, uri)
+ _, m, err := getSourceFile(ctx, view, uri)
if err != nil {
return nil, err
}
@@ -63,7 +63,7 @@
}
func organizeImports(ctx context.Context, v source.View, s span.Span) ([]protocol.TextEdit, error) {
- f, m, err := newColumnMap(ctx, v, s.URI())
+ f, m, err := getGoFile(ctx, v, s.URI())
if err != nil {
return nil, err
}
diff --git a/internal/lsp/completion.go b/internal/lsp/completion.go
index bcc4e0e..b9d82f4 100644
--- a/internal/lsp/completion.go
+++ b/internal/lsp/completion.go
@@ -18,7 +18,7 @@
func (s *Server) completion(ctx context.Context, params *protocol.CompletionParams) (*protocol.CompletionList, error) {
uri := span.NewURI(params.TextDocument.URI)
view := s.findView(ctx, uri)
- f, m, err := newColumnMap(ctx, view, uri)
+ f, m, err := getGoFile(ctx, view, uri)
if err != nil {
return nil, err
}
diff --git a/internal/lsp/definition.go b/internal/lsp/definition.go
index 5a1fe94..b0f996c 100644
--- a/internal/lsp/definition.go
+++ b/internal/lsp/definition.go
@@ -15,7 +15,7 @@
func (s *Server) definition(ctx context.Context, params *protocol.TextDocumentPositionParams) ([]protocol.Location, error) {
uri := span.NewURI(params.TextDocument.URI)
view := s.findView(ctx, uri)
- f, m, err := newColumnMap(ctx, view, uri)
+ f, m, err := getGoFile(ctx, view, uri)
if err != nil {
return nil, err
}
@@ -35,7 +35,7 @@
if err != nil {
return nil, err
}
- _, decM, err := newColumnMap(ctx, view, decSpan.URI())
+ _, decM, err := getSourceFile(ctx, view, decSpan.URI())
if err != nil {
return nil, err
}
@@ -49,7 +49,7 @@
func (s *Server) typeDefinition(ctx context.Context, params *protocol.TextDocumentPositionParams) ([]protocol.Location, error) {
uri := span.NewURI(params.TextDocument.URI)
view := s.findView(ctx, uri)
- f, m, err := newColumnMap(ctx, view, uri)
+ f, m, err := getGoFile(ctx, view, uri)
if err != nil {
return nil, err
}
@@ -69,7 +69,7 @@
if err != nil {
return nil, err
}
- _, identM, err := newColumnMap(ctx, view, identSpan.URI())
+ _, identM, err := getSourceFile(ctx, view, identSpan.URI())
if err != nil {
return nil, err
}
diff --git a/internal/lsp/diagnostics.go b/internal/lsp/diagnostics.go
index 0312d4e..1d0ac83 100644
--- a/internal/lsp/diagnostics.go
+++ b/internal/lsp/diagnostics.go
@@ -62,7 +62,7 @@
func toProtocolDiagnostics(ctx context.Context, v source.View, diagnostics []source.Diagnostic) ([]protocol.Diagnostic, error) {
reports := []protocol.Diagnostic{}
for _, diag := range diagnostics {
- _, m, err := newColumnMap(ctx, v, diag.Span.URI())
+ _, m, err := getSourceFile(ctx, v, diag.Span.URI())
if err != nil {
return nil, err
}
diff --git a/internal/lsp/format.go b/internal/lsp/format.go
index f591ae1..0c672e8 100644
--- a/internal/lsp/format.go
+++ b/internal/lsp/format.go
@@ -22,7 +22,7 @@
// formatRange formats a document with a given range.
func formatRange(ctx context.Context, v source.View, s span.Span) ([]protocol.TextEdit, error) {
- f, m, err := newColumnMap(ctx, v, s.URI())
+ f, m, err := getGoFile(ctx, v, s.URI())
if err != nil {
return nil, err
}
diff --git a/internal/lsp/highlight.go b/internal/lsp/highlight.go
index 288587d..22bb169 100644
--- a/internal/lsp/highlight.go
+++ b/internal/lsp/highlight.go
@@ -15,7 +15,7 @@
func (s *Server) documentHighlight(ctx context.Context, params *protocol.TextDocumentPositionParams) ([]protocol.DocumentHighlight, error) {
uri := span.NewURI(params.TextDocument.URI)
view := s.findView(ctx, uri)
- f, m, err := newColumnMap(ctx, view, uri)
+ f, m, err := getGoFile(ctx, view, uri)
if err != nil {
return nil, err
}
diff --git a/internal/lsp/hover.go b/internal/lsp/hover.go
index 7d9ae2c..a3d66aa 100644
--- a/internal/lsp/hover.go
+++ b/internal/lsp/hover.go
@@ -16,7 +16,7 @@
func (s *Server) hover(ctx context.Context, params *protocol.TextDocumentPositionParams) (*protocol.Hover, error) {
uri := span.NewURI(params.TextDocument.URI)
view := s.findView(ctx, uri)
- f, m, err := newColumnMap(ctx, view, uri)
+ f, m, err := getGoFile(ctx, view, uri)
if err != nil {
return nil, err
}
diff --git a/internal/lsp/link.go b/internal/lsp/link.go
index 33e9188..ecf6e3e 100644
--- a/internal/lsp/link.go
+++ b/internal/lsp/link.go
@@ -15,7 +15,7 @@
func (s *Server) documentLink(ctx context.Context, params *protocol.DocumentLinkParams) ([]protocol.DocumentLink, error) {
uri := span.NewURI(params.TextDocument.URI)
view := s.findView(ctx, uri)
- f, m, err := newColumnMap(ctx, view, uri)
+ f, m, err := getGoFile(ctx, view, uri)
if err != nil {
return nil, err
}
diff --git a/internal/lsp/lsp_test.go b/internal/lsp/lsp_test.go
index caa9b4b..04eaa81 100644
--- a/internal/lsp/lsp_test.go
+++ b/internal/lsp/lsp_test.go
@@ -293,7 +293,7 @@
}
continue
}
- _, m, err := newColumnMap(ctx, r.server.findView(ctx, uri), uri)
+ _, m, err := getSourceFile(ctx, r.server.findView(ctx, uri), uri)
if err != nil {
t.Error(err)
}
diff --git a/internal/lsp/signature_help.go b/internal/lsp/signature_help.go
index 7471321..b28f860 100644
--- a/internal/lsp/signature_help.go
+++ b/internal/lsp/signature_help.go
@@ -15,7 +15,7 @@
func (s *Server) signatureHelp(ctx context.Context, params *protocol.TextDocumentPositionParams) (*protocol.SignatureHelp, error) {
uri := span.NewURI(params.TextDocument.URI)
view := s.findView(ctx, uri)
- f, m, err := newColumnMap(ctx, view, uri)
+ f, m, err := getGoFile(ctx, view, uri)
if err != nil {
return nil, err
}
diff --git a/internal/lsp/source/completion.go b/internal/lsp/source/completion.go
index 2a132d5..d654397 100644
--- a/internal/lsp/source/completion.go
+++ b/internal/lsp/source/completion.go
@@ -200,7 +200,7 @@
// The prefix is computed based on the preceding identifier and can be used by
// the client to score the quality of the completion. For instance, some clients
// may tolerate imperfect matches as valid completion results, since users may make typos.
-func Completion(ctx context.Context, f File, pos token.Pos) ([]CompletionItem, Prefix, error) {
+func Completion(ctx context.Context, f GoFile, pos token.Pos) ([]CompletionItem, Prefix, error) {
file := f.GetAST(ctx)
pkg := f.GetPackage(ctx)
if pkg == nil || pkg.IsIllTyped() {
diff --git a/internal/lsp/source/diagnostics.go b/internal/lsp/source/diagnostics.go
index 032c4af..23b33b6 100644
--- a/internal/lsp/source/diagnostics.go
+++ b/internal/lsp/source/diagnostics.go
@@ -55,7 +55,11 @@
if err != nil {
return singleDiagnostic(uri, "no file found for %s", uri), nil
}
- pkg := f.GetPackage(ctx)
+ gof, ok := f.(GoFile)
+ if !ok {
+ return singleDiagnostic(uri, "%s is not a go file", uri), nil
+ }
+ pkg := gof.GetPackage(ctx)
if pkg == nil {
return singleDiagnostic(uri, "%s is not part of a package", uri), nil
}
@@ -72,7 +76,7 @@
}
}
// Updates to the diagnostics for this package may need to be propagated.
- for _, f := range f.GetActiveReverseDeps(ctx) {
+ for _, f := range gof.GetActiveReverseDeps(ctx) {
pkg := f.GetPackage(ctx)
if pkg == nil {
continue
@@ -151,11 +155,16 @@
func pointToSpan(ctx context.Context, v View, spn span.Span) span.Span {
// Don't set a range if it's anything other than a type error.
- diagFile, err := v.GetFile(ctx, spn.URI())
+ f, err := v.GetFile(ctx, spn.URI())
if err != nil {
v.Logger().Errorf(ctx, "Could find file for diagnostic: %v", spn.URI())
return spn
}
+ diagFile, ok := f.(GoFile)
+ if !ok {
+ v.Logger().Errorf(ctx, "Not a go file: %v", spn.URI())
+ return spn
+ }
tok := diagFile.GetToken(ctx)
if tok == nil {
v.Logger().Errorf(ctx, "Could not find tokens for diagnostic: %v", spn.URI())
diff --git a/internal/lsp/source/format.go b/internal/lsp/source/format.go
index bcc0d7b..682f075 100644
--- a/internal/lsp/source/format.go
+++ b/internal/lsp/source/format.go
@@ -19,7 +19,7 @@
)
// Format formats a file with a given range.
-func Format(ctx context.Context, f File, rng span.Range) ([]TextEdit, error) {
+func Format(ctx context.Context, f GoFile, rng span.Range) ([]TextEdit, error) {
pkg := f.GetPackage(ctx)
if hasParseErrors(pkg.GetErrors()) {
return nil, fmt.Errorf("%s has parse errors, not formatting", f.URI())
@@ -52,7 +52,7 @@
}
// Imports formats a file using the goimports tool.
-func Imports(ctx context.Context, f File, rng span.Range) ([]TextEdit, error) {
+func Imports(ctx context.Context, f GoFile, rng span.Range) ([]TextEdit, error) {
formatted, err := imports.Process(f.GetToken(ctx).Name(), f.GetContent(ctx), nil)
if err != nil {
return nil, err
diff --git a/internal/lsp/source/highlight.go b/internal/lsp/source/highlight.go
index 2952d13..8e81139 100644
--- a/internal/lsp/source/highlight.go
+++ b/internal/lsp/source/highlight.go
@@ -13,7 +13,7 @@
"golang.org/x/tools/internal/span"
)
-func Highlight(ctx context.Context, f File, pos token.Pos) []span.Span {
+func Highlight(ctx context.Context, f GoFile, pos token.Pos) []span.Span {
fAST := f.GetAST(ctx)
fset := f.GetFileSet(ctx)
path, _ := astutil.PathEnclosingInterval(fAST, pos, pos)
diff --git a/internal/lsp/source/identifier.go b/internal/lsp/source/identifier.go
index aaf4696..6e43fcc 100644
--- a/internal/lsp/source/identifier.go
+++ b/internal/lsp/source/identifier.go
@@ -20,7 +20,7 @@
type IdentifierInfo struct {
Name string
Range span.Range
- File File
+ File GoFile
Type struct {
Range span.Range
Object types.Object
@@ -37,7 +37,7 @@
// Identifier returns identifier information for a position
// in a file, accounting for a potentially incomplete selector.
-func Identifier(ctx context.Context, v View, f File, pos token.Pos) (*IdentifierInfo, error) {
+func Identifier(ctx context.Context, v View, f GoFile, pos token.Pos) (*IdentifierInfo, error) {
if result, err := identifier(ctx, v, f, pos); err != nil || result != nil {
return result, err
}
@@ -52,7 +52,7 @@
}
// identifier checks a single position for a potential identifier.
-func identifier(ctx context.Context, v View, f File, pos token.Pos) (*IdentifierInfo, error) {
+func identifier(ctx context.Context, v View, f GoFile, pos token.Pos) (*IdentifierInfo, error) {
fAST := f.GetAST(ctx)
pkg := f.GetPackage(ctx)
if pkg == nil || pkg.IsIllTyped() {
@@ -128,7 +128,7 @@
return result, nil
}
-func checkImportSpec(f File, fAST *ast.File, pkg Package, pos token.Pos) (*IdentifierInfo, error) {
+func checkImportSpec(f GoFile, fAST *ast.File, pkg Package, pos token.Pos) (*IdentifierInfo, error) {
// Check if pos is in an *ast.ImportSpec.
for _, imp := range fAST.Imports {
if imp.Pos() <= pos && pos < imp.End() {
@@ -204,10 +204,14 @@
if err != nil {
return nil, err
}
- declFile, err := v.GetFile(ctx, s.URI())
+ f, err := v.GetFile(ctx, s.URI())
if err != nil {
return nil, err
}
+ declFile, ok := f.(GoFile)
+ if !ok {
+ return nil, fmt.Errorf("not a go file %v", s.URI())
+ }
declAST := declFile.GetAST(ctx)
path, _ := astutil.PathEnclosingInterval(declAST, rng.Start, rng.End)
if path == nil {
diff --git a/internal/lsp/source/signature_help.go b/internal/lsp/source/signature_help.go
index ea5a644..250d428 100644
--- a/internal/lsp/source/signature_help.go
+++ b/internal/lsp/source/signature_help.go
@@ -24,7 +24,7 @@
Label string
}
-func SignatureHelp(ctx context.Context, f File, pos token.Pos) (*SignatureInformation, error) {
+func SignatureHelp(ctx context.Context, f GoFile, pos token.Pos) (*SignatureInformation, error) {
fAST := f.GetAST(ctx)
pkg := f.GetPackage(ctx)
if pkg == nil || pkg.IsIllTyped() {
diff --git a/internal/lsp/source/source_test.go b/internal/lsp/source/source_test.go
index 69005c8..b08b110 100644
--- a/internal/lsp/source/source_test.go
+++ b/internal/lsp/source/source_test.go
@@ -133,9 +133,9 @@
if err != nil {
t.Fatalf("failed for %v: %v", src, err)
}
- tok := f.GetToken(ctx)
+ tok := f.(source.GoFile).GetToken(ctx)
pos := tok.Pos(src.Start().Offset())
- list, prefix, err := source.Completion(ctx, f, pos)
+ list, prefix, err := source.Completion(ctx, f.(source.GoFile), pos)
if err != nil {
t.Fatalf("failed for %v: %v", src, err)
}
@@ -164,7 +164,7 @@
}
tok := f.GetToken(ctx)
pos := tok.Pos(src.Start().Offset())
- list, _, err := source.Completion(ctx, f, pos)
+ list, _, err := source.Completion(ctx, f.(source.GoFile), pos)
if err != nil {
t.Fatalf("failed for %v: %v", src, err)
}
@@ -273,7 +273,7 @@
if err != nil {
t.Fatalf("failed for %v: %v", spn, err)
}
- edits, err := source.Format(ctx, f, rng)
+ edits, err := source.Format(ctx, f.(source.GoFile), rng)
if err != nil {
if gofmted != "" {
t.Error(err)
@@ -297,7 +297,7 @@
}
tok := f.GetToken(ctx)
pos := tok.Pos(d.Src.Start().Offset())
- ident, err := source.Identifier(ctx, r.view, f, pos)
+ ident, err := source.Identifier(ctx, r.view, f.(source.GoFile), pos)
if err != nil {
t.Fatalf("failed for %v: %v", d.Src, err)
}
@@ -341,7 +341,7 @@
}
tok := f.GetToken(ctx)
pos := tok.Pos(src.Start().Offset())
- highlights := source.Highlight(ctx, f, pos)
+ highlights := source.Highlight(ctx, f.(source.GoFile), pos)
if len(highlights) != len(locations) {
t.Fatalf("got %d highlights for %s, expected %d", len(highlights), name, len(locations))
}
@@ -360,7 +360,7 @@
if err != nil {
t.Fatalf("failed for %v: %v", uri, err)
}
- symbols := source.DocumentSymbols(ctx, f)
+ symbols := source.DocumentSymbols(ctx, f.(source.GoFile))
if len(symbols) != len(expectedSymbols) {
t.Errorf("want %d top-level symbols in %v, got %d", len(expectedSymbols), uri, len(symbols))
@@ -424,7 +424,7 @@
}
tok := f.GetToken(ctx)
pos := tok.Pos(spn.Start().Offset())
- gotSignature, err := source.SignatureHelp(ctx, f, pos)
+ gotSignature, err := source.SignatureHelp(ctx, f.(source.GoFile), pos)
if err != nil {
t.Fatalf("failed for %v: %v", spn, err)
}
diff --git a/internal/lsp/source/symbols.go b/internal/lsp/source/symbols.go
index c0dcaf4..f91f8f4 100644
--- a/internal/lsp/source/symbols.go
+++ b/internal/lsp/source/symbols.go
@@ -40,7 +40,7 @@
Children []Symbol
}
-func DocumentSymbols(ctx context.Context, f File) []Symbol {
+func DocumentSymbols(ctx context.Context, f GoFile) []Symbol {
fset := f.GetFileSet(ctx)
file := f.GetAST(ctx)
pkg := f.GetPackage(ctx)
diff --git a/internal/lsp/source/view.go b/internal/lsp/source/view.go
index 91c82c0..fbbf91f 100644
--- a/internal/lsp/source/view.go
+++ b/internal/lsp/source/view.go
@@ -35,22 +35,24 @@
Shutdown(ctx context.Context)
}
-// File represents a Go source file that has been type-checked. It is the input
-// to most of the exported functions in this package, as it wraps up the
-// building blocks for most queries. Users of the source package can abstract
-// the loading of packages into their own caching systems.
+// File represents a source file of any type.
type File interface {
URI() span.URI
View() View
- GetAST(ctx context.Context) *ast.File
- GetFileSet(ctx context.Context) *token.FileSet
- GetPackage(ctx context.Context) Package
- GetToken(ctx context.Context) *token.File
GetContent(ctx context.Context) []byte
+ GetFileSet(ctx context.Context) *token.FileSet
+ GetToken(ctx context.Context) *token.File
+}
+
+// GoFile represents a Go source file that has been type-checked.
+type GoFile interface {
+ File
+ GetAST(ctx context.Context) *ast.File
+ GetPackage(ctx context.Context) Package
// GetActiveReverseDeps returns the active files belonging to the reverse
// dependencies of this file's package.
- GetActiveReverseDeps(ctx context.Context) []File
+ GetActiveReverseDeps(ctx context.Context) []GoFile
}
// Package represents a Go package that has been type-checked. It maintains
diff --git a/internal/lsp/symbols.go b/internal/lsp/symbols.go
index da57ec2..2ee526a 100644
--- a/internal/lsp/symbols.go
+++ b/internal/lsp/symbols.go
@@ -15,7 +15,7 @@
func (s *Server) documentSymbol(ctx context.Context, params *protocol.DocumentSymbolParams) ([]protocol.DocumentSymbol, error) {
uri := span.NewURI(params.TextDocument.URI)
view := s.findView(ctx, uri)
- f, m, err := newColumnMap(ctx, view, uri)
+ f, m, err := getGoFile(ctx, view, uri)
if err != nil {
return nil, err
}
diff --git a/internal/lsp/text_synchronization.go b/internal/lsp/text_synchronization.go
index 1667773..5402f23 100644
--- a/internal/lsp/text_synchronization.go
+++ b/internal/lsp/text_synchronization.go
@@ -65,7 +65,7 @@
uri := span.NewURI(params.TextDocument.URI)
view := s.findView(ctx, uri)
- file, m, err := newColumnMap(ctx, view, uri)
+ file, m, err := getSourceFile(ctx, view, uri)
if err != nil {
return "", jsonrpc2.NewErrorf(jsonrpc2.CodeInternalError, "file not found")
}
diff --git a/internal/lsp/util.go b/internal/lsp/util.go
index 244e2cb..0d61de5 100644
--- a/internal/lsp/util.go
+++ b/internal/lsp/util.go
@@ -46,7 +46,7 @@
}
}
-func newColumnMap(ctx context.Context, v source.View, uri span.URI) (source.File, *protocol.ColumnMapper, error) {
+func getSourceFile(ctx context.Context, v source.View, uri span.URI) (source.File, *protocol.ColumnMapper, error) {
f, err := v.GetFile(ctx, uri)
if err != nil {
return nil, nil, err
@@ -58,3 +58,15 @@
m := protocol.NewColumnMapper(f.URI(), f.GetFileSet(ctx), tok, f.GetContent(ctx))
return f, m, nil
}
+
+func getGoFile(ctx context.Context, v source.View, uri span.URI) (source.GoFile, *protocol.ColumnMapper, error) {
+ f, m, err := getSourceFile(ctx, v, uri)
+ if err != nil {
+ return nil, nil, err
+ }
+ gof, ok := f.(source.GoFile)
+ if !ok {
+ return nil, nil, fmt.Errorf("not a go file %v", f.URI())
+ }
+ return gof, m, nil
+}