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>
This commit is contained in:
Ian Cottrell 2019-05-04 00:04:18 -04:00
Родитель 0e55654012
Коммит 4f9510c6a1
26 изменённых файлов: 189 добавлений и 110 удалений

48
internal/lsp/cache/check.go поставляемый
Просмотреть файл

@ -14,10 +14,11 @@ import (
"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 @@ func (v *view) parse(ctx context.Context, f *file) ([]packages.Error, error) {
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 @@ func (v *view) parse(ctx context.Context, f *file) ([]packages.Error, error) {
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 @@ func (v *view) parse(ctx context.Context, f *file) ([]packages.Error, error) {
// 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 @@ func (v *view) checkMetadata(ctx context.Context, f *file) ([]packages.Error, er
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 @@ func (v *view) checkMetadata(ctx context.Context, f *file) ([]packages.Error, er
// 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 @@ func (v *view) reparseImports(ctx context.Context, f *file, filename string) boo
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 @@ func (v *view) link(pkgPath string, pkg *packages.Package, parent *metadata) *me
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 @@ func (v *view) link(pkgPath string, pkg *packages.Package, parent *metadata) *me
}
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 @@ func (v *view) cachePackage(ctx context.Context, pkg *pkg, meta *metadata) {
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()

72
internal/lsp/cache/file.go поставляемый
Просмотреть файл

@ -16,17 +16,32 @@ import (
"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 @@ func basename(filename string) string {
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 @@ func (f *file) GetContent(ctx context.Context) []byte {
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 @@ func (f *file) GetToken(ctx context.Context) *token.File {
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 @@ func (f *file) GetAST(ctx context.Context) *ast.File {
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 @@ func (f *file) GetPackage(ctx context.Context) source.Package {
// 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 @@ func (f *file) read(ctx context.Context) {
}
}
// 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 @@ func (f *file) GetActiveReverseDeps(ctx context.Context) []source.File {
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 @@ func (f *file) GetActiveReverseDeps(ctx context.Context) []source.File {
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 @@ func (v *view) reverseDeps(ctx context.Context, seen map[string]struct{}, result
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 {

4
internal/lsp/cache/parse.go поставляемый
Просмотреть файл

@ -50,7 +50,9 @@ func (imp *importer) parseFiles(filenames []string) ([]*ast.File, []error) {
}
var fAST *ast.File
if f != nil {
fAST = f.ast
if gof, ok := f.(*goFile); ok {
fAST = gof.ast
}
}
wg.Add(1)

52
internal/lsp/cache/view.go поставляемый
Просмотреть файл

@ -51,8 +51,8 @@ type view struct {
// 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 @@ func NewView(ctx context.Context, log xlog.Logger, name string, folder span.URI,
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 @@ func (v *view) applyContentChange(uri span.URI, content []byte) {
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 @@ func (v *view) applyContentChange(uri span.URI, content []byte) {
// 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 @@ func (v *view) remove(pkgPath string, seen map[string]struct{}) {
// 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 @@ func (v *view) GetFile(ctx context.Context, uri span.URI) (source.File, error) {
}
// 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 @@ func (v *view) getFile(uri span.URI) (*file, error) {
} 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 @@ func (v *view) isIgnored(filename string) bool {
//
// 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 @@ func (v *view) findFile(uri span.URI) (*file, error) {
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 @@ func (v *view) findFile(uri span.URI) (*file, error) {
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)
}
}

Просмотреть файл

@ -17,7 +17,7 @@ import (
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 (s *Server) codeAction(ctx context.Context, params *protocol.CodeActionPara
}
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
}

Просмотреть файл

@ -18,7 +18,7 @@ import (
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
}

Просмотреть файл

@ -15,7 +15,7 @@ import (
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 @@ func (s *Server) definition(ctx context.Context, params *protocol.TextDocumentPo
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) definition(ctx context.Context, params *protocol.TextDocumentPo
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 @@ func (s *Server) typeDefinition(ctx context.Context, params *protocol.TextDocume
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
}

Просмотреть файл

@ -62,7 +62,7 @@ func (s *Server) publishDiagnostics(ctx context.Context, view source.View, uri s
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
}

Просмотреть файл

@ -22,7 +22,7 @@ func (s *Server) formatting(ctx context.Context, params *protocol.DocumentFormat
// 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
}

Просмотреть файл

@ -15,7 +15,7 @@ import (
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
}

Просмотреть файл

@ -16,7 +16,7 @@ import (
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
}

Просмотреть файл

@ -15,7 +15,7 @@ import (
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
}

Просмотреть файл

@ -293,7 +293,7 @@ func (r *runner) Format(t *testing.T, data tests.Formats) {
}
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)
}

Просмотреть файл

@ -15,7 +15,7 @@ import (
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
}

Просмотреть файл

@ -200,7 +200,7 @@ func (c *completer) found(obj types.Object, weight float64) {
// 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() {

Просмотреть файл

@ -55,7 +55,11 @@ func Diagnostics(ctx context.Context, v View, uri span.URI) (map[span.URI][]Diag
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 @@ func Diagnostics(ctx context.Context, v View, uri span.URI) (map[span.URI][]Diag
}
}
// 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 analyses(ctx context.Context, v View, pkg Package, reports map[span.URI][]D
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())

Просмотреть файл

@ -19,7 +19,7 @@ import (
)
// 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 @@ func hasParseErrors(errors []packages.Error) bool {
}
// 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

Просмотреть файл

@ -13,7 +13,7 @@ import (
"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)

Просмотреть файл

@ -20,7 +20,7 @@ import (
type IdentifierInfo struct {
Name string
Range span.Range
File File
File GoFile
Type struct {
Range span.Range
Object types.Object
@ -37,7 +37,7 @@ type IdentifierInfo struct {
// 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 @@ func Identifier(ctx context.Context, v View, f File, pos token.Pos) (*Identifier
}
// 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 @@ func identifier(ctx context.Context, v View, f File, pos token.Pos) (*Identifier
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 @@ func objToNode(ctx context.Context, v View, obj types.Object, rng span.Range) (a
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 {

Просмотреть файл

@ -24,7 +24,7 @@ type ParameterInformation struct {
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() {

Просмотреть файл

@ -133,9 +133,9 @@ func (r *runner) Completion(t *testing.T, data tests.Completions, snippets tests
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 @@ func (r *runner) Completion(t *testing.T, data tests.Completions, snippets tests
}
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 @@ func (r *runner) Format(t *testing.T, data tests.Formats) {
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 @@ func (r *runner) Definition(t *testing.T, data tests.Definitions) {
}
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 @@ func (r *runner) Highlight(t *testing.T, data tests.Highlights) {
}
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 @@ func (r *runner) Symbol(t *testing.T, data tests.Symbols) {
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 @@ func (r *runner) SignatureHelp(t *testing.T, data tests.Signatures) {
}
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)
}

Просмотреть файл

@ -40,7 +40,7 @@ type Symbol struct {
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)

Просмотреть файл

@ -35,22 +35,24 @@ type View interface {
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

Просмотреть файл

@ -15,7 +15,7 @@ import (
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
}

Просмотреть файл

@ -65,7 +65,7 @@ func (s *Server) applyChanges(ctx context.Context, params *protocol.DidChangeTex
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")
}

Просмотреть файл

@ -46,7 +46,7 @@ func PrintVersionInfo(w io.Writer, verbose bool, markdown bool) {
}
}
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 @@ func newColumnMap(ctx context.Context, v source.View, uri span.URI) (source.File
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
}