зеркало из https://github.com/golang/tools.git
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:
Родитель
0e55654012
Коммит
4f9510c6a1
|
@ -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()
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче