mirror of https://github.com/golang/go.git
internal/lsp/source: move completion to its own package
Completion is slowly becoming a large part of internal/lsp/source and it makes sense to move to its own seperate package inside source to make future refactors easier. As a part of this change, any unexported members from source required by completion are now exported. Util functions only required by completion are moved from internal/lsp/source/util.go to internal/lsp/source/completion/util.go. Change-Id: I6b7405ec598c910545e649bb0e6aa02ffa653b38 Reviewed-on: https://go-review.googlesource.com/c/tools/+/253178 Run-TryBot: Danish Dua <danishdua@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Heschi Kreinick <heschi@google.com>
This commit is contained in:
parent
ea3a2cdbfb
commit
acefd226e2
|
|
@ -13,6 +13,7 @@ import (
|
|||
"golang.org/x/tools/internal/lsp/debug/tag"
|
||||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
"golang.org/x/tools/internal/lsp/source"
|
||||
"golang.org/x/tools/internal/lsp/source/completion"
|
||||
"golang.org/x/tools/internal/span"
|
||||
)
|
||||
|
||||
|
|
@ -22,11 +23,11 @@ func (s *Server) completion(ctx context.Context, params *protocol.CompletionPara
|
|||
if !ok {
|
||||
return nil, err
|
||||
}
|
||||
var candidates []source.CompletionItem
|
||||
var surrounding *source.Selection
|
||||
var candidates []completion.CompletionItem
|
||||
var surrounding *completion.Selection
|
||||
switch fh.Kind() {
|
||||
case source.Go:
|
||||
candidates, surrounding, err = source.Completion(ctx, snapshot, fh, params.Position, params.Context.TriggerCharacter)
|
||||
candidates, surrounding, err = completion.Completion(ctx, snapshot, fh, params.Position, params.Context.TriggerCharacter)
|
||||
case source.Mod:
|
||||
candidates, surrounding = nil, nil
|
||||
}
|
||||
|
|
@ -103,7 +104,7 @@ func (s *Server) completion(ctx context.Context, params *protocol.CompletionPara
|
|||
}, nil
|
||||
}
|
||||
|
||||
func toProtocolCompletionItems(candidates []source.CompletionItem, rng protocol.Range, options source.Options) []protocol.CompletionItem {
|
||||
func toProtocolCompletionItems(candidates []completion.CompletionItem, rng protocol.Range, options source.Options) []protocol.CompletionItem {
|
||||
var (
|
||||
items = make([]protocol.CompletionItem, 0, len(candidates))
|
||||
numDeepCompletionsSeen int
|
||||
|
|
@ -115,7 +116,7 @@ func toProtocolCompletionItems(candidates []source.CompletionItem, rng protocol.
|
|||
if !options.DeepCompletion {
|
||||
continue
|
||||
}
|
||||
if numDeepCompletionsSeen >= source.MaxDeepCompletions {
|
||||
if numDeepCompletionsSeen >= completion.MaxDeepCompletions {
|
||||
continue
|
||||
}
|
||||
numDeepCompletionsSeen++
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ outer:
|
|||
nameStart, nameEnd = funcLit.Type.Func, funcLit.Type.Params.Pos()
|
||||
kind = protocol.Function
|
||||
}
|
||||
rng, err := newMappedRange(snapshot.FileSet(), pgf.Mapper, nameStart, nameEnd).Range()
|
||||
rng, err := NewMappedRange(snapshot.FileSet(), pgf.Mapper, nameStart, nameEnd).Range()
|
||||
if err != nil {
|
||||
return protocol.CallHierarchyItem{}, err
|
||||
}
|
||||
|
|
@ -229,7 +229,7 @@ func collectCallExpressions(fset *token.FileSet, mapper *protocol.ColumnMapper,
|
|||
|
||||
callRanges := []protocol.Range{}
|
||||
for _, call := range callPositions {
|
||||
callRange, err := newMappedRange(fset, mapper, call.start, call.end).Range()
|
||||
callRange, err := NewMappedRange(fset, mapper, call.start, call.end).Range()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ func runTestCodeLens(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]p
|
|||
if !strings.HasSuffix(fh.URI().Filename(), "_test.go") {
|
||||
return nil, nil
|
||||
}
|
||||
pkg, pgf, err := getParsedFile(ctx, snapshot, fh, WidestPackage)
|
||||
pkg, pgf, err := GetParsedFile(ctx, snapshot, fh, WidestPackage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -54,7 +54,7 @@ func runTestCodeLens(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]p
|
|||
if benchmarkRe.MatchString(fn.Name.Name) {
|
||||
benchFns = append(benchFns, fn.Name.Name)
|
||||
}
|
||||
rng, err := newMappedRange(snapshot.FileSet(), pgf.Mapper, d.Pos(), d.Pos()).Range()
|
||||
rng, err := NewMappedRange(snapshot.FileSet(), pgf.Mapper, d.Pos(), d.Pos()).Range()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -90,7 +90,7 @@ func runTestCodeLens(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]p
|
|||
}
|
||||
}
|
||||
// add a code lens to the top of the file which runs all benchmarks in the file
|
||||
rng, err := newMappedRange(snapshot.FileSet(), pgf.Mapper, pgf.File.Package, pgf.File.Package).Range()
|
||||
rng, err := NewMappedRange(snapshot.FileSet(), pgf.Mapper, pgf.File.Package, pgf.File.Package).Range()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -158,7 +158,7 @@ func goGenerateCodeLens(ctx context.Context, snapshot Snapshot, fh FileHandle) (
|
|||
if !strings.HasPrefix(l.Text, ggDirective) {
|
||||
continue
|
||||
}
|
||||
rng, err := newMappedRange(snapshot.FileSet(), pgf.Mapper, l.Pos(), l.Pos()+token.Pos(len(ggDirective))).Range()
|
||||
rng, err := NewMappedRange(snapshot.FileSet(), pgf.Mapper, l.Pos(), l.Pos()+token.Pos(len(ggDirective))).Range()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -209,7 +209,7 @@ func regenerateCgoLens(ctx context.Context, snapshot Snapshot, fh FileHandle) ([
|
|||
if c == nil {
|
||||
return nil, nil
|
||||
}
|
||||
rng, err := newMappedRange(snapshot.FileSet(), pgf.Mapper, c.Pos(), c.EndPos).Range()
|
||||
rng, err := NewMappedRange(snapshot.FileSet(), pgf.Mapper, c.Pos(), c.EndPos).Range()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -230,11 +230,11 @@ func regenerateCgoLens(ctx context.Context, snapshot Snapshot, fh FileHandle) ([
|
|||
}
|
||||
|
||||
func toggleDetailsCodeLens(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]protocol.CodeLens, error) {
|
||||
_, pgf, err := getParsedFile(ctx, snapshot, fh, WidestPackage)
|
||||
_, pgf, err := GetParsedFile(ctx, snapshot, fh, WidestPackage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rng, err := newMappedRange(snapshot.FileSet(), pgf.Mapper, pgf.File.Package, pgf.File.Package).Range()
|
||||
rng, err := NewMappedRange(snapshot.FileSet(), pgf.Mapper, pgf.File.Package, pgf.File.Package).Range()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ func (c *Command) SuggestedFix(ctx context.Context, snapshot Snapshot, fh Versio
|
|||
// getAllSuggestedFixInputs is a helper function to collect all possible needed
|
||||
// inputs for an AppliesFunc or SuggestedFixFunc.
|
||||
func getAllSuggestedFixInputs(ctx context.Context, snapshot Snapshot, fh FileHandle, pRng protocol.Range) (*token.FileSet, span.Range, []byte, *ast.File, *protocol.ColumnMapper, *types.Package, *types.Info, error) {
|
||||
pkg, pgf, err := getParsedFile(ctx, snapshot, fh, NarrowestPackage)
|
||||
pkg, pgf, err := GetParsedFile(ctx, snapshot, fh, NarrowestPackage)
|
||||
if err != nil {
|
||||
return nil, span.Range{}, nil, nil, nil, nil, nil, errors.Errorf("getting file for Identifier: %w", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package source
|
||||
// Package completion provides core functionality for code completion in Go
|
||||
// editors and tools.
|
||||
package completion
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -25,6 +27,7 @@ import (
|
|||
"golang.org/x/tools/internal/lsp/fuzzy"
|
||||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
"golang.org/x/tools/internal/lsp/snippet"
|
||||
"golang.org/x/tools/internal/lsp/source"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
|
|
@ -84,6 +87,18 @@ type CompletionItem struct {
|
|||
obj types.Object
|
||||
}
|
||||
|
||||
// completionOptions holds completion specific configuration.
|
||||
type completionOptions struct {
|
||||
deepCompletion bool
|
||||
unimported bool
|
||||
documentation bool
|
||||
fullDocumentation bool
|
||||
placeholders bool
|
||||
literal bool
|
||||
matcher source.Matcher
|
||||
budget time.Duration
|
||||
}
|
||||
|
||||
// Snippet is a convenience returns the snippet if available, otherwise
|
||||
// the InsertText.
|
||||
// used for an item, depending on if the callee wants placeholders or not.
|
||||
|
|
@ -135,8 +150,8 @@ func (ipm insensitivePrefixMatcher) Score(candidateLabel string) float32 {
|
|||
|
||||
// completer contains the necessary information for a single completion request.
|
||||
type completer struct {
|
||||
snapshot Snapshot
|
||||
pkg Package
|
||||
snapshot source.Snapshot
|
||||
pkg source.Package
|
||||
qf types.Qualifier
|
||||
opts *completionOptions
|
||||
|
||||
|
|
@ -228,7 +243,7 @@ type compLitInfo struct {
|
|||
type importInfo struct {
|
||||
importPath string
|
||||
name string
|
||||
pkg Package
|
||||
pkg source.Package
|
||||
}
|
||||
|
||||
type methodSetKey struct {
|
||||
|
|
@ -240,7 +255,7 @@ type methodSetKey struct {
|
|||
type Selection struct {
|
||||
content string
|
||||
cursor token.Pos
|
||||
mappedRange
|
||||
source.MappedRange
|
||||
}
|
||||
|
||||
func (p Selection) Content() string {
|
||||
|
|
@ -248,19 +263,19 @@ func (p Selection) Content() string {
|
|||
}
|
||||
|
||||
func (p Selection) Start() token.Pos {
|
||||
return p.mappedRange.spanRange.Start
|
||||
return p.MappedRange.SpanRange().Start
|
||||
}
|
||||
|
||||
func (p Selection) End() token.Pos {
|
||||
return p.mappedRange.spanRange.End
|
||||
return p.MappedRange.SpanRange().End
|
||||
}
|
||||
|
||||
func (p Selection) Prefix() string {
|
||||
return p.content[:p.cursor-p.spanRange.Start]
|
||||
return p.content[:p.cursor-p.SpanRange().Start]
|
||||
}
|
||||
|
||||
func (p Selection) Suffix() string {
|
||||
return p.content[p.cursor-p.spanRange.Start:]
|
||||
return p.content[p.cursor-p.SpanRange().Start:]
|
||||
}
|
||||
|
||||
func (c *completer) setSurrounding(ident *ast.Ident) {
|
||||
|
|
@ -275,7 +290,7 @@ func (c *completer) setSurrounding(ident *ast.Ident) {
|
|||
content: ident.Name,
|
||||
cursor: c.pos,
|
||||
// Overwrite the prefix only.
|
||||
mappedRange: newMappedRange(c.snapshot.FileSet(), c.mapper, ident.Pos(), ident.End()),
|
||||
MappedRange: source.NewMappedRange(c.snapshot.FileSet(), c.mapper, ident.Pos(), ident.End()),
|
||||
}
|
||||
|
||||
c.setMatcherFromPrefix(c.surrounding.Prefix())
|
||||
|
|
@ -283,9 +298,9 @@ func (c *completer) setSurrounding(ident *ast.Ident) {
|
|||
|
||||
func (c *completer) setMatcherFromPrefix(prefix string) {
|
||||
switch c.opts.matcher {
|
||||
case Fuzzy:
|
||||
case source.Fuzzy:
|
||||
c.matcher = fuzzy.NewMatcher(prefix)
|
||||
case CaseSensitive:
|
||||
case source.CaseSensitive:
|
||||
c.matcher = prefixMatcher(prefix)
|
||||
default:
|
||||
c.matcher = insensitivePrefixMatcher(strings.ToLower(prefix))
|
||||
|
|
@ -297,7 +312,7 @@ func (c *completer) getSurrounding() *Selection {
|
|||
c.surrounding = &Selection{
|
||||
content: "",
|
||||
cursor: c.pos,
|
||||
mappedRange: newMappedRange(c.snapshot.FileSet(), c.mapper, c.pos, c.pos),
|
||||
MappedRange: source.NewMappedRange(c.snapshot.FileSet(), c.mapper, c.pos, c.pos),
|
||||
}
|
||||
}
|
||||
return c.surrounding
|
||||
|
|
@ -470,13 +485,13 @@ func (e ErrIsDefinition) Error() string {
|
|||
// The selection 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, snapshot Snapshot, fh FileHandle, protoPos protocol.Position, triggerCharacter string) ([]CompletionItem, *Selection, error) {
|
||||
func Completion(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle, protoPos protocol.Position, triggerCharacter string) ([]CompletionItem, *Selection, error) {
|
||||
ctx, done := event.Start(ctx, "source.Completion")
|
||||
defer done()
|
||||
|
||||
startTime := time.Now()
|
||||
|
||||
pkg, pgf, err := getParsedFile(ctx, snapshot, fh, NarrowestPackage)
|
||||
pkg, pgf, err := source.GetParsedFile(ctx, snapshot, fh, source.NarrowestPackage)
|
||||
if err != nil || pgf.File.Package == token.NoPos {
|
||||
// If we can't parse this file or find position for the package
|
||||
// keyword, it may be missing a package declaration. Try offering
|
||||
|
|
@ -485,7 +500,7 @@ func Completion(ctx context.Context, snapshot Snapshot, fh FileHandle, protoPos
|
|||
// present but no package name exists.
|
||||
items, surrounding, innerErr := packageClauseCompletions(ctx, snapshot, fh, protoPos)
|
||||
if innerErr != nil {
|
||||
// return the error for getParsedFile since it's more relevant in this situation.
|
||||
// return the error for GetParsedFile since it's more relevant in this situation.
|
||||
return nil, nil, errors.Errorf("getting file for Completion: %w", err)
|
||||
|
||||
}
|
||||
|
|
@ -547,7 +562,7 @@ func Completion(ctx context.Context, snapshot Snapshot, fh FileHandle, protoPos
|
|||
c := &completer{
|
||||
pkg: pkg,
|
||||
snapshot: snapshot,
|
||||
qf: qualifier(pgf.File, pkg.GetTypes(), pkg.GetTypesInfo()),
|
||||
qf: source.Qualifier(pgf.File, pkg.GetTypes(), pkg.GetTypesInfo()),
|
||||
triggerCharacter: triggerCharacter,
|
||||
filename: fh.URI().Filename(),
|
||||
file: pgf.File,
|
||||
|
|
@ -561,7 +576,7 @@ func Completion(ctx context.Context, snapshot Snapshot, fh FileHandle, protoPos
|
|||
deepCompletion: opts.DeepCompletion,
|
||||
unimported: opts.UnimportedCompletion,
|
||||
documentation: opts.CompletionDocumentation,
|
||||
fullDocumentation: opts.HoverKind == FullDocumentation,
|
||||
fullDocumentation: opts.HoverKind == source.FullDocumentation,
|
||||
placeholders: opts.Placeholders,
|
||||
literal: opts.LiteralCompletions && opts.InsertTextFormat == protocol.SnippetTextFormat,
|
||||
budget: opts.CompletionBudget,
|
||||
|
|
@ -777,7 +792,7 @@ func (c *completer) populateImportCompletions(ctx context.Context, searchImport
|
|||
c.surrounding = &Selection{
|
||||
content: searchImport.Path.Value,
|
||||
cursor: c.pos,
|
||||
mappedRange: newMappedRange(c.snapshot.FileSet(), c.mapper, searchImport.Path.Pos(), searchImport.Path.End()),
|
||||
MappedRange: source.NewMappedRange(c.snapshot.FileSet(), c.mapper, searchImport.Path.Pos(), searchImport.Path.End()),
|
||||
}
|
||||
|
||||
seenImports := make(map[string]struct{})
|
||||
|
|
@ -1010,7 +1025,7 @@ func (c *completer) setSurroundingForComment(comments *ast.CommentGroup) {
|
|||
c.surrounding = &Selection{
|
||||
content: cursorComment.Text[start:end],
|
||||
cursor: c.pos,
|
||||
mappedRange: newMappedRange(c.snapshot.FileSet(), c.mapper,
|
||||
MappedRange: source.NewMappedRange(c.snapshot.FileSet(), c.mapper,
|
||||
token.Pos(int(cursorComment.Slash)+start), token.Pos(int(cursorComment.Slash)+end)),
|
||||
}
|
||||
c.setMatcherFromPrefix(c.surrounding.Prefix())
|
||||
|
|
@ -1242,7 +1257,7 @@ func (c *completer) methodsAndFields(ctx context.Context, typ types.Type, addres
|
|||
|
||||
// lexical finds completions in the lexical environment.
|
||||
func (c *completer) lexical(ctx context.Context) error {
|
||||
scopes := collectScopes(c.pkg.GetTypesInfo(), c.path, c.pos)
|
||||
scopes := source.CollectScopes(c.pkg.GetTypesInfo(), c.path, c.pos)
|
||||
scopes = append(scopes, c.pkg.GetTypes().Scope(), types.Universe)
|
||||
|
||||
var (
|
||||
|
|
@ -1320,7 +1335,7 @@ func (c *completer) lexical(ctx context.Context) error {
|
|||
}
|
||||
|
||||
if c.inference.objType != nil {
|
||||
if named, _ := deref(c.inference.objType).(*types.Named); named != nil {
|
||||
if named, _ := source.Deref(c.inference.objType).(*types.Named); named != nil {
|
||||
// If we expected a named type, check the type's package for
|
||||
// completion items. This is useful when the current file hasn't
|
||||
// imported the type's package yet.
|
||||
|
|
@ -1356,7 +1371,7 @@ func (c *completer) lexical(ctx context.Context) error {
|
|||
}
|
||||
|
||||
if t := c.inference.objType; t != nil {
|
||||
t = deref(t)
|
||||
t = source.Deref(t)
|
||||
|
||||
// If we have an expected type and it is _not_ a named type,
|
||||
// handle it specially. Non-named types like "[]int" will never be
|
||||
|
|
@ -1390,26 +1405,6 @@ func (c *completer) lexical(ctx context.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func collectScopes(info *types.Info, path []ast.Node, pos token.Pos) []*types.Scope {
|
||||
// scopes[i], where i<len(path), is the possibly nil Scope of path[i].
|
||||
var scopes []*types.Scope
|
||||
for _, n := range path {
|
||||
// Include *FuncType scope if pos is inside the function body.
|
||||
switch node := n.(type) {
|
||||
case *ast.FuncDecl:
|
||||
if node.Body != nil && nodeContains(node.Body, pos) {
|
||||
n = node.Type
|
||||
}
|
||||
case *ast.FuncLit:
|
||||
if node.Body != nil && nodeContains(node.Body, pos) {
|
||||
n = node.Type
|
||||
}
|
||||
}
|
||||
scopes = append(scopes, info.Scopes[n])
|
||||
}
|
||||
return scopes
|
||||
}
|
||||
|
||||
func (c *completer) unimportedPackages(ctx context.Context, seen map[string]struct{}) error {
|
||||
var prefix string
|
||||
if c.surrounding != nil {
|
||||
|
|
@ -1507,27 +1502,13 @@ func (c *completer) unimportedPackages(ctx context.Context, seen map[string]stru
|
|||
// alreadyImports reports whether f has an import with the specified path.
|
||||
func alreadyImports(f *ast.File, path string) bool {
|
||||
for _, s := range f.Imports {
|
||||
if importPath(s) == path {
|
||||
if source.ImportPath(s) == path {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// importPath returns the unquoted import path of s,
|
||||
// or "" if the path is not properly quoted.
|
||||
func importPath(s *ast.ImportSpec) string {
|
||||
t, err := strconv.Unquote(s.Path.Value)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func nodeContains(n ast.Node, pos token.Pos) bool {
|
||||
return n != nil && n.Pos() <= pos && pos <= n.End()
|
||||
}
|
||||
|
||||
func (c *completer) inConstDecl() bool {
|
||||
for _, n := range c.path {
|
||||
if decl, ok := n.(*ast.GenDecl); ok && decl.Tok == token.CONST {
|
||||
|
|
@ -1614,7 +1595,7 @@ func enclosingCompositeLiteral(path []ast.Node, pos token.Pos, info *types.Info)
|
|||
|
||||
clInfo := compLitInfo{
|
||||
cl: n,
|
||||
clType: deref(tv.Type).Underlying(),
|
||||
clType: source.Deref(tv.Type).Underlying(),
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
@ -2083,7 +2064,7 @@ Nodes:
|
|||
}
|
||||
return inf
|
||||
case *ast.RangeStmt:
|
||||
if nodeContains(node.X, c.pos) {
|
||||
if source.NodeContains(node.X, c.pos) {
|
||||
inf.objKind |= kindSlice | kindArray | kindMap | kindString
|
||||
if node.Value == nil {
|
||||
inf.objKind |= kindChan
|
||||
|
|
@ -2239,11 +2220,11 @@ func breaksExpectedTypeInference(n ast.Node, pos token.Pos) bool {
|
|||
case *ast.CompositeLit:
|
||||
// Doesn't break inference if pos is in type name.
|
||||
// For example: "Foo<>{Bar: 123}"
|
||||
return !nodeContains(n.Type, pos)
|
||||
return !source.NodeContains(n.Type, pos)
|
||||
case *ast.CallExpr:
|
||||
// Doesn't break inference if pos is in func name.
|
||||
// For example: "Foo<>(123)"
|
||||
return !nodeContains(n.Fun, pos)
|
||||
return !source.NodeContains(n.Fun, pos)
|
||||
case *ast.FuncLit, *ast.IndexExpr, *ast.SliceExpr:
|
||||
return true
|
||||
default:
|
||||
|
|
@ -2356,7 +2337,7 @@ Nodes:
|
|||
case *ast.MapType:
|
||||
inf.wantTypeName = true
|
||||
if n.Key != nil {
|
||||
inf.wantComparable = nodeContains(n.Key, c.pos)
|
||||
inf.wantComparable = source.NodeContains(n.Key, c.pos)
|
||||
} else {
|
||||
// If the key is empty, assume we are completing the key if
|
||||
// pos is directly after the "map[".
|
||||
|
|
@ -2364,10 +2345,10 @@ Nodes:
|
|||
}
|
||||
break Nodes
|
||||
case *ast.ValueSpec:
|
||||
inf.wantTypeName = nodeContains(n.Type, c.pos)
|
||||
inf.wantTypeName = source.NodeContains(n.Type, c.pos)
|
||||
break Nodes
|
||||
case *ast.TypeSpec:
|
||||
inf.wantTypeName = nodeContains(n.Type, c.pos)
|
||||
inf.wantTypeName = source.NodeContains(n.Type, c.pos)
|
||||
default:
|
||||
if breaksExpectedTypeInference(p, c.pos) {
|
||||
return typeNameInference{}
|
||||
|
|
@ -2752,7 +2733,7 @@ func (c *completer) matchingTypeName(cand *candidate) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
if !isInterface(t) && typeMatches(types.NewPointer(t)) {
|
||||
if !source.IsInterface(t) && typeMatches(types.NewPointer(t)) {
|
||||
if c.inference.typeName.compLitType {
|
||||
// If we are completing a composite literal type as in
|
||||
// "foo<>{}", to make a pointer we must prepend "&".
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package source
|
||||
package completion
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -2,12 +2,11 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package source
|
||||
package completion
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/types"
|
||||
"strings"
|
||||
|
||||
|
|
@ -16,6 +15,7 @@ import (
|
|||
"golang.org/x/tools/internal/lsp/debug/tag"
|
||||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
"golang.org/x/tools/internal/lsp/snippet"
|
||||
"golang.org/x/tools/internal/lsp/source"
|
||||
"golang.org/x/tools/internal/span"
|
||||
)
|
||||
|
||||
|
|
@ -43,25 +43,25 @@ func (c *completer) item(ctx context.Context, cand candidate) (CompletionItem, e
|
|||
// expandFuncCall mutates the completion label, detail, and snippet
|
||||
// to that of an invocation of sig.
|
||||
expandFuncCall := func(sig *types.Signature) error {
|
||||
s, err := newSignature(ctx, c.snapshot, c.pkg, c.file, "", sig, nil, c.qf)
|
||||
s, err := source.NewSignature(ctx, c.snapshot, c.pkg, c.file, "", sig, nil, c.qf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
snip = c.functionCallSnippet(label, s.params)
|
||||
detail = "func" + s.format()
|
||||
snip = c.functionCallSnippet(label, s.Params())
|
||||
detail = "func" + s.Format()
|
||||
return nil
|
||||
}
|
||||
|
||||
switch obj := obj.(type) {
|
||||
case *types.TypeName:
|
||||
detail, kind = formatType(obj.Type(), c.qf)
|
||||
detail, kind = source.FormatType(obj.Type(), c.qf)
|
||||
case *types.Const:
|
||||
kind = protocol.ConstantCompletion
|
||||
case *types.Var:
|
||||
if _, ok := obj.Type().(*types.Struct); ok {
|
||||
detail = "struct{...}" // for anonymous structs
|
||||
} else if obj.IsField() {
|
||||
detail = formatVarType(ctx, c.snapshot, c.pkg, c.file, obj, c.qf)
|
||||
detail = source.FormatVarType(ctx, c.snapshot, c.pkg, c.file, obj, c.qf)
|
||||
}
|
||||
if obj.IsField() {
|
||||
kind = protocol.FieldCompletion
|
||||
|
|
@ -184,7 +184,7 @@ func (c *completer) item(ctx context.Context, cand candidate) (CompletionItem, e
|
|||
searchPkg = cand.imp.pkg
|
||||
}
|
||||
|
||||
pgf, pkg, err := findPosInPackage(c.snapshot, searchPkg, obj.Pos())
|
||||
pgf, pkg, err := source.FindPosInPackage(c.snapshot, searchPkg, obj.Pos())
|
||||
if err != nil {
|
||||
return item, nil
|
||||
}
|
||||
|
|
@ -198,7 +198,7 @@ func (c *completer) item(ctx context.Context, cand candidate) (CompletionItem, e
|
|||
return item, nil
|
||||
}
|
||||
|
||||
hover, err := hoverInfo(pkg, obj, decl)
|
||||
hover, err := source.HoverInfo(pkg, obj, decl)
|
||||
if err != nil {
|
||||
event.Error(ctx, "failed to find Hover", err, tag.URI.Of(uri))
|
||||
return item, nil
|
||||
|
|
@ -221,7 +221,7 @@ func (c *completer) importEdits(ctx context.Context, imp *importInfo) ([]protoco
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return computeOneImportFixEdits(ctx, c.snapshot, pgf, &imports.ImportFix{
|
||||
return source.ComputeOneImportFixEdits(ctx, c.snapshot, pgf, &imports.ImportFix{
|
||||
StmtInfo: imports.ImportInfo{
|
||||
ImportPath: imp.importPath,
|
||||
Name: imp.name,
|
||||
|
|
@ -243,12 +243,12 @@ func (c *completer) formatBuiltin(ctx context.Context, cand candidate) (Completi
|
|||
item.Kind = protocol.ConstantCompletion
|
||||
case *types.Builtin:
|
||||
item.Kind = protocol.FunctionCompletion
|
||||
sig, err := newBuiltinSignature(ctx, c.snapshot, obj.Name())
|
||||
sig, err := source.NewBuiltinSignature(ctx, c.snapshot, obj.Name())
|
||||
if err != nil {
|
||||
return CompletionItem{}, err
|
||||
}
|
||||
item.Detail = "func" + sig.format()
|
||||
item.snippet = c.functionCallSnippet(obj.Name(), sig.params)
|
||||
item.Detail = "func" + sig.Format()
|
||||
item.snippet = c.functionCallSnippet(obj.Name(), sig.Params())
|
||||
case *types.TypeName:
|
||||
if types.IsInterface(obj.Type()) {
|
||||
item.Kind = protocol.InterfaceCompletion
|
||||
|
|
@ -260,31 +260,3 @@ func (c *completer) formatBuiltin(ctx context.Context, cand candidate) (Completi
|
|||
}
|
||||
return item, nil
|
||||
}
|
||||
|
||||
// qualifier returns a function that appropriately formats a types.PkgName
|
||||
// appearing in a *ast.File.
|
||||
func qualifier(f *ast.File, pkg *types.Package, info *types.Info) types.Qualifier {
|
||||
// Construct mapping of import paths to their defined or implicit names.
|
||||
imports := make(map[*types.Package]string)
|
||||
for _, imp := range f.Imports {
|
||||
var obj types.Object
|
||||
if imp.Name != nil {
|
||||
obj = info.Defs[imp.Name]
|
||||
} else {
|
||||
obj = info.Implicits[imp]
|
||||
}
|
||||
if pkgname, ok := obj.(*types.PkgName); ok {
|
||||
imports[pkgname.Imported()] = pkgname.Name()
|
||||
}
|
||||
}
|
||||
// Define qualifier to replace full package paths with names of the imports.
|
||||
return func(p *types.Package) string {
|
||||
if p == pkg {
|
||||
return ""
|
||||
}
|
||||
if name, ok := imports[p]; ok {
|
||||
return name
|
||||
}
|
||||
return p.Name()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,14 @@
|
|||
package source
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package completion
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
|
||||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
"golang.org/x/tools/internal/lsp/source"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -69,7 +74,7 @@ func (c *completer) addKeywordCompletions() {
|
|||
if len(c.path) > 2 {
|
||||
// Offer "range" if we are in ast.ForStmt.Init. This is what the
|
||||
// AST looks like before "range" is typed, e.g. "for i := r<>".
|
||||
if loop, ok := c.path[2].(*ast.ForStmt); ok && nodeContains(loop.Init, c.pos) {
|
||||
if loop, ok := c.path[2].(*ast.ForStmt); ok && source.NodeContains(loop.Init, c.pos) {
|
||||
c.addKeywordItems(seen, stdScore, RANGE)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package source
|
||||
package completion
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package source
|
||||
package completion
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -17,6 +17,7 @@ import (
|
|||
"golang.org/x/tools/internal/lsp/diff"
|
||||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
"golang.org/x/tools/internal/lsp/snippet"
|
||||
"golang.org/x/tools/internal/lsp/source"
|
||||
)
|
||||
|
||||
// literal generates composite literal, function literal, and make()
|
||||
|
|
@ -52,7 +53,7 @@ func (c *completer) literal(ctx context.Context, literalType types.Type, imp *im
|
|||
// don't offer "mySlice{}" since we have already added a candidate
|
||||
// of "[]int{}".
|
||||
if _, named := literalType.(*types.Named); named && expType != nil {
|
||||
if _, named := deref(expType).(*types.Named); !named {
|
||||
if _, named := source.Deref(expType).(*types.Named); !named {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
@ -129,7 +130,7 @@ func (c *completer) literal(ctx context.Context, literalType types.Type, imp *im
|
|||
// Add a literal completion for a signature type that implements
|
||||
// an interface. For example, offer "http.HandlerFunc()" when
|
||||
// expected type is "http.Handler".
|
||||
if isInterface(expType) {
|
||||
if source.IsInterface(expType) {
|
||||
c.basicLiteral(t, typeName, float64(score), addlEdits)
|
||||
}
|
||||
case *types.Basic:
|
||||
|
|
@ -137,7 +138,7 @@ func (c *completer) literal(ctx context.Context, literalType types.Type, imp *im
|
|||
// expected interface (e.g. named string type http.Dir
|
||||
// implements http.FileSystem), or are identical to our expected
|
||||
// type (i.e. yielding a type conversion such as "float64()").
|
||||
if isInterface(expType) || types.Identical(expType, literalType) {
|
||||
if source.IsInterface(expType) || types.Identical(expType, literalType) {
|
||||
c.basicLiteral(t, typeName, float64(score), addlEdits)
|
||||
}
|
||||
}
|
||||
|
|
@ -159,7 +160,7 @@ func (c *completer) literal(ctx context.Context, literalType types.Type, imp *im
|
|||
}
|
||||
|
||||
// If prefix matches "func", client may want a function literal.
|
||||
if score := c.matcher.Score("func"); !cand.takeAddress && score > 0 && !isInterface(expType) {
|
||||
if score := c.matcher.Score("func"); !cand.takeAddress && score > 0 && !source.IsInterface(expType) {
|
||||
switch t := literalType.Underlying().(type) {
|
||||
case *types.Signature:
|
||||
c.functionLiteral(ctx, t, float64(score))
|
||||
|
|
@ -170,12 +171,12 @@ func (c *completer) literal(ctx context.Context, literalType types.Type, imp *im
|
|||
// prependEdit produces text edits that preprend the specified prefix
|
||||
// to the specified node.
|
||||
func prependEdit(fset *token.FileSet, m *protocol.ColumnMapper, node ast.Node, prefix string) ([]protocol.TextEdit, error) {
|
||||
rng := newMappedRange(fset, m, node.Pos(), node.Pos())
|
||||
rng := source.NewMappedRange(fset, m, node.Pos(), node.Pos())
|
||||
spn, err := rng.Span()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ToProtocolEdits(m, []diff.TextEdit{{
|
||||
return source.ToProtocolEdits(m, []diff.TextEdit{{
|
||||
Span: spn,
|
||||
NewText: prefix,
|
||||
}})
|
||||
|
|
@ -210,7 +211,7 @@ func (c *completer) functionLiteral(ctx context.Context, sig *types.Signature, m
|
|||
// If the param has no name in the signature, guess a name based
|
||||
// on the type. Use an empty qualifier to ignore the package.
|
||||
// For example, we want to name "http.Request" "r", not "hr".
|
||||
name = formatVarType(ctx, c.snapshot, c.pkg, c.file, p, func(p *types.Package) string {
|
||||
name = source.FormatVarType(ctx, c.snapshot, c.pkg, c.file, p, func(p *types.Package) string {
|
||||
return ""
|
||||
})
|
||||
name = abbreviateTypeName(name)
|
||||
|
|
@ -264,7 +265,7 @@ func (c *completer) functionLiteral(ctx context.Context, sig *types.Signature, m
|
|||
// of "i int, j int".
|
||||
if i == sig.Params().Len()-1 || !types.Identical(p.Type(), sig.Params().At(i+1).Type()) {
|
||||
snip.WriteText(" ")
|
||||
typeStr := formatVarType(ctx, c.snapshot, c.pkg, c.file, p, c.qf)
|
||||
typeStr := source.FormatVarType(ctx, c.snapshot, c.pkg, c.file, p, c.qf)
|
||||
if sig.Variadic() && i == sig.Params().Len()-1 {
|
||||
typeStr = strings.Replace(typeStr, "[]", "...", 1)
|
||||
}
|
||||
|
|
@ -292,7 +293,7 @@ func (c *completer) functionLiteral(ctx context.Context, sig *types.Signature, m
|
|||
if name := r.Name(); name != "" {
|
||||
snip.WriteText(name + " ")
|
||||
}
|
||||
snip.WriteText(formatVarType(ctx, c.snapshot, c.pkg, c.file, r, c.qf))
|
||||
snip.WriteText(source.FormatVarType(ctx, c.snapshot, c.pkg, c.file, r, c.qf))
|
||||
}
|
||||
if resultsNeedParens {
|
||||
snip.WriteText(")")
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package source
|
||||
package completion
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -17,16 +17,17 @@ import (
|
|||
|
||||
"golang.org/x/tools/internal/lsp/fuzzy"
|
||||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
"golang.org/x/tools/internal/lsp/source"
|
||||
"golang.org/x/tools/internal/span"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// packageClauseCompletions offers completions for a package declaration when
|
||||
// one is not present in the given file.
|
||||
func packageClauseCompletions(ctx context.Context, snapshot Snapshot, fh FileHandle, pos protocol.Position) ([]CompletionItem, *Selection, error) {
|
||||
func packageClauseCompletions(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle, pos protocol.Position) ([]CompletionItem, *Selection, error) {
|
||||
// We know that the AST for this file will be empty due to the missing
|
||||
// package declaration, but parse it anyway to get a mapper.
|
||||
pgf, err := snapshot.ParseGo(ctx, fh, ParseFull)
|
||||
pgf, err := snapshot.ParseGo(ctx, fh, source.ParseFull)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
@ -67,7 +68,7 @@ func packageClauseCompletions(ctx context.Context, snapshot Snapshot, fh FileHan
|
|||
// packageCompletionSurrounding returns surrounding for package completion if a
|
||||
// package completions can be suggested at a given position. A valid location
|
||||
// for package completion is above any declarations or import statements.
|
||||
func packageCompletionSurrounding(ctx context.Context, fset *token.FileSet, fh FileHandle, pgf *ParsedGoFile, pos token.Pos) (*Selection, error) {
|
||||
func packageCompletionSurrounding(ctx context.Context, fset *token.FileSet, fh source.FileHandle, pgf *source.ParsedGoFile, pos token.Pos) (*Selection, error) {
|
||||
src, err := fh.Read()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -98,7 +99,7 @@ func packageCompletionSurrounding(ctx context.Context, fset *token.FileSet, fh F
|
|||
return &Selection{
|
||||
content: name.Name,
|
||||
cursor: cursor,
|
||||
mappedRange: newMappedRange(fset, m, name.Pos(), name.End()),
|
||||
MappedRange: source.NewMappedRange(fset, m, name.Pos(), name.End()),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
|
@ -135,7 +136,7 @@ func packageCompletionSurrounding(ctx context.Context, fset *token.FileSet, fh F
|
|||
return &Selection{
|
||||
content: content,
|
||||
cursor: cursor,
|
||||
mappedRange: newMappedRange(fset, m, start, end),
|
||||
MappedRange: source.NewMappedRange(fset, m, start, end),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
|
@ -162,7 +163,7 @@ func packageCompletionSurrounding(ctx context.Context, fset *token.FileSet, fh F
|
|||
return &Selection{
|
||||
content: "",
|
||||
cursor: cursor,
|
||||
mappedRange: newMappedRange(fset, m, start, end),
|
||||
MappedRange: source.NewMappedRange(fset, m, start, end),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
@ -207,7 +208,7 @@ func (c *completer) packageNameCompletions(ctx context.Context, fileURI span.URI
|
|||
// have the given prefix and are used in the the same directory as the given
|
||||
// file. This also includes test packages for these packages (<pkg>_test) and
|
||||
// the directory name itself.
|
||||
func packageSuggestions(ctx context.Context, snapshot Snapshot, fileURI span.URI, prefix string) ([]candidate, error) {
|
||||
func packageSuggestions(ctx context.Context, snapshot source.Snapshot, fileURI span.URI, prefix string) ([]candidate, error) {
|
||||
workspacePackages, err := snapshot.WorkspacePackages(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package source
|
||||
package completion
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package source
|
||||
package completion
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package source
|
||||
package completion
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package source
|
||||
package completion
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
|
@ -12,6 +12,7 @@ import (
|
|||
|
||||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
"golang.org/x/tools/internal/lsp/snippet"
|
||||
"golang.org/x/tools/internal/lsp/source"
|
||||
)
|
||||
|
||||
// addStatementCandidates adds full statement completion candidates
|
||||
|
|
@ -79,7 +80,7 @@ func (c *completer) addAssignAppend() {
|
|||
}
|
||||
|
||||
// The name or our slice is whatever's in the LHS expression.
|
||||
sliceText = formatNode(fset, n.Lhs[exprIdx])
|
||||
sliceText = source.FormatNode(fset, n.Lhs[exprIdx])
|
||||
case *ast.SelectorExpr:
|
||||
// Make sure we are a selector at the beginning of a statement.
|
||||
if _, parentIsExprtStmt := c.path[2].(*ast.ExprStmt); !parentIsExprtStmt {
|
||||
|
|
@ -89,7 +90,7 @@ func (c *completer) addAssignAppend() {
|
|||
// So far we only know the first part of our slice name. For
|
||||
// example in "s.a<>" we only know our slice begins with "s."
|
||||
// since the user could still be typing.
|
||||
sliceText = formatNode(fset, n.X) + "."
|
||||
sliceText = source.FormatNode(fset, n.X) + "."
|
||||
needsLHS = true
|
||||
case *ast.ExprStmt:
|
||||
needsLHS = true
|
||||
|
|
@ -205,7 +206,7 @@ func (c *completer) addErrCheckAndReturn() {
|
|||
|
||||
var (
|
||||
// errText is e.g. "err" in "foo, err := bar()".
|
||||
errText = formatNode(c.snapshot.FileSet(), lastAssignee)
|
||||
errText = source.FormatNode(c.snapshot.FileSet(), lastAssignee)
|
||||
|
||||
// Whether we need to include the "if" keyword in our candidate.
|
||||
needsIf = true
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package source
|
||||
package completion
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package source
|
||||
package completion
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
|
@ -0,0 +1,295 @@
|
|||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package completion
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/internal/lsp/source"
|
||||
)
|
||||
|
||||
// exprAtPos returns the index of the expression containing pos.
|
||||
func exprAtPos(pos token.Pos, args []ast.Expr) int {
|
||||
for i, expr := range args {
|
||||
if expr.Pos() <= pos && pos <= expr.End() {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return len(args)
|
||||
}
|
||||
|
||||
// eachField invokes fn for each field that can be selected from a
|
||||
// value of type T.
|
||||
func eachField(T types.Type, fn func(*types.Var)) {
|
||||
// TODO(adonovan): this algorithm doesn't exclude ambiguous
|
||||
// selections that match more than one field/method.
|
||||
// types.NewSelectionSet should do that for us.
|
||||
|
||||
// for termination on recursive types
|
||||
var seen map[*types.Struct]bool
|
||||
|
||||
var visit func(T types.Type)
|
||||
visit = func(T types.Type) {
|
||||
if T, ok := source.Deref(T).Underlying().(*types.Struct); ok {
|
||||
if seen[T] {
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < T.NumFields(); i++ {
|
||||
f := T.Field(i)
|
||||
fn(f)
|
||||
if f.Anonymous() {
|
||||
if seen == nil {
|
||||
// Lazily create "seen" since it is only needed for
|
||||
// embedded structs.
|
||||
seen = make(map[*types.Struct]bool)
|
||||
}
|
||||
seen[T] = true
|
||||
visit(f.Type())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
visit(T)
|
||||
}
|
||||
|
||||
// typeIsValid reports whether typ doesn't contain any Invalid types.
|
||||
func typeIsValid(typ types.Type) bool {
|
||||
// Check named types separately, because we don't want
|
||||
// to call Underlying() on them to avoid problems with recursive types.
|
||||
if _, ok := typ.(*types.Named); ok {
|
||||
return true
|
||||
}
|
||||
|
||||
switch typ := typ.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
return typ.Kind() != types.Invalid
|
||||
case *types.Array:
|
||||
return typeIsValid(typ.Elem())
|
||||
case *types.Slice:
|
||||
return typeIsValid(typ.Elem())
|
||||
case *types.Pointer:
|
||||
return typeIsValid(typ.Elem())
|
||||
case *types.Map:
|
||||
return typeIsValid(typ.Key()) && typeIsValid(typ.Elem())
|
||||
case *types.Chan:
|
||||
return typeIsValid(typ.Elem())
|
||||
case *types.Signature:
|
||||
return typeIsValid(typ.Params()) && typeIsValid(typ.Results())
|
||||
case *types.Tuple:
|
||||
for i := 0; i < typ.Len(); i++ {
|
||||
if !typeIsValid(typ.At(i).Type()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case *types.Struct, *types.Interface:
|
||||
// Don't bother checking structs, interfaces for validity.
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// resolveInvalid traverses the node of the AST that defines the scope
|
||||
// containing the declaration of obj, and attempts to find a user-friendly
|
||||
// name for its invalid type. The resulting Object and its Type are fake.
|
||||
func resolveInvalid(fset *token.FileSet, obj types.Object, node ast.Node, info *types.Info) types.Object {
|
||||
var resultExpr ast.Expr
|
||||
ast.Inspect(node, func(node ast.Node) bool {
|
||||
switch n := node.(type) {
|
||||
case *ast.ValueSpec:
|
||||
for _, name := range n.Names {
|
||||
if info.Defs[name] == obj {
|
||||
resultExpr = n.Type
|
||||
}
|
||||
}
|
||||
return false
|
||||
case *ast.Field: // This case handles parameters and results of a FuncDecl or FuncLit.
|
||||
for _, name := range n.Names {
|
||||
if info.Defs[name] == obj {
|
||||
resultExpr = n.Type
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
})
|
||||
// Construct a fake type for the object and return a fake object with this type.
|
||||
typename := source.FormatNode(fset, resultExpr)
|
||||
typ := types.NewNamed(types.NewTypeName(token.NoPos, obj.Pkg(), typename, nil), types.Typ[types.Invalid], nil)
|
||||
return types.NewVar(obj.Pos(), obj.Pkg(), obj.Name(), typ)
|
||||
}
|
||||
|
||||
func isPointer(T types.Type) bool {
|
||||
_, ok := T.(*types.Pointer)
|
||||
return ok
|
||||
}
|
||||
|
||||
func isVar(obj types.Object) bool {
|
||||
_, ok := obj.(*types.Var)
|
||||
return ok
|
||||
}
|
||||
|
||||
func isTypeName(obj types.Object) bool {
|
||||
_, ok := obj.(*types.TypeName)
|
||||
return ok
|
||||
}
|
||||
|
||||
func isFunc(obj types.Object) bool {
|
||||
_, ok := obj.(*types.Func)
|
||||
return ok
|
||||
}
|
||||
|
||||
func isEmptyInterface(T types.Type) bool {
|
||||
intf, _ := T.(*types.Interface)
|
||||
return intf != nil && intf.NumMethods() == 0
|
||||
}
|
||||
|
||||
func isUntyped(T types.Type) bool {
|
||||
if basic, ok := T.(*types.Basic); ok {
|
||||
return basic.Info()&types.IsUntyped > 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isPkgName(obj types.Object) bool {
|
||||
_, ok := obj.(*types.PkgName)
|
||||
return ok
|
||||
}
|
||||
|
||||
func isASTFile(n ast.Node) bool {
|
||||
_, ok := n.(*ast.File)
|
||||
return ok
|
||||
}
|
||||
|
||||
func deslice(T types.Type) types.Type {
|
||||
if slice, ok := T.Underlying().(*types.Slice); ok {
|
||||
return slice.Elem()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isSelector returns the enclosing *ast.SelectorExpr when pos is in the
|
||||
// selector.
|
||||
func enclosingSelector(path []ast.Node, pos token.Pos) *ast.SelectorExpr {
|
||||
if len(path) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if sel, ok := path[0].(*ast.SelectorExpr); ok {
|
||||
return sel
|
||||
}
|
||||
|
||||
if _, ok := path[0].(*ast.Ident); ok && len(path) > 1 {
|
||||
if sel, ok := path[1].(*ast.SelectorExpr); ok && pos >= sel.Sel.Pos() {
|
||||
return sel
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func enclosingValueSpec(path []ast.Node) *ast.ValueSpec {
|
||||
for _, n := range path {
|
||||
if vs, ok := n.(*ast.ValueSpec); ok {
|
||||
return vs
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// exprObj returns the types.Object associated with the *ast.Ident or
|
||||
// *ast.SelectorExpr e.
|
||||
func exprObj(info *types.Info, e ast.Expr) types.Object {
|
||||
var ident *ast.Ident
|
||||
switch expr := e.(type) {
|
||||
case *ast.Ident:
|
||||
ident = expr
|
||||
case *ast.SelectorExpr:
|
||||
ident = expr.Sel
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
return info.ObjectOf(ident)
|
||||
}
|
||||
|
||||
// typeConversion returns the type being converted to if call is a type
|
||||
// conversion expression.
|
||||
func typeConversion(call *ast.CallExpr, info *types.Info) types.Type {
|
||||
// Type conversion (e.g. "float64(foo)").
|
||||
if fun, _ := exprObj(info, call.Fun).(*types.TypeName); fun != nil {
|
||||
return fun.Type()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// fieldsAccessible returns whether s has at least one field accessible by p.
|
||||
func fieldsAccessible(s *types.Struct, p *types.Package) bool {
|
||||
for i := 0; i < s.NumFields(); i++ {
|
||||
f := s.Field(i)
|
||||
if f.Exported() || f.Pkg() == p {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// prevStmt returns the statement that precedes the statement containing pos.
|
||||
// For example:
|
||||
//
|
||||
// foo := 1
|
||||
// bar(1 + 2<>)
|
||||
//
|
||||
// If "<>" is pos, prevStmt returns "foo := 1"
|
||||
func prevStmt(pos token.Pos, path []ast.Node) ast.Stmt {
|
||||
var blockLines []ast.Stmt
|
||||
for i := 0; i < len(path) && blockLines == nil; i++ {
|
||||
switch n := path[i].(type) {
|
||||
case *ast.BlockStmt:
|
||||
blockLines = n.List
|
||||
case *ast.CommClause:
|
||||
blockLines = n.Body
|
||||
case *ast.CaseClause:
|
||||
blockLines = n.Body
|
||||
}
|
||||
}
|
||||
|
||||
for i := len(blockLines) - 1; i >= 0; i-- {
|
||||
if blockLines[i].End() < pos {
|
||||
return blockLines[i]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// formatZeroValue produces Go code representing the zero value of T. It
|
||||
// returns the empty string if T is invalid.
|
||||
func formatZeroValue(T types.Type, qf types.Qualifier) string {
|
||||
switch u := T.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
switch {
|
||||
case u.Info()&types.IsNumeric > 0:
|
||||
return "0"
|
||||
case u.Info()&types.IsString > 0:
|
||||
return `""`
|
||||
case u.Info()&types.IsBoolean > 0:
|
||||
return "false"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
case *types.Pointer, *types.Interface, *types.Chan, *types.Map, *types.Slice, *types.Signature:
|
||||
return "nil"
|
||||
default:
|
||||
return types.TypeString(T, qf) + "{}"
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package source
|
||||
package completion
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
|
|
@ -129,7 +129,7 @@ func FileDiagnostics(ctx context.Context, snapshot Snapshot, uri span.URI) (Vers
|
|||
if err != nil {
|
||||
return VersionedFileIdentity{}, nil, err
|
||||
}
|
||||
pkg, _, err := getParsedFile(ctx, snapshot, fh, NarrowestPackage)
|
||||
pkg, _, err := GetParsedFile(ctx, snapshot, fh, NarrowestPackage)
|
||||
if err != nil {
|
||||
return VersionedFileIdentity{}, nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ func calculateIndentation(content []byte, tok *token.File, insertBeforeStmt ast.
|
|||
// generateAvailableIdentifier adjusts the new function name until there are no collisons in scope.
|
||||
// Possible collisions include other function and variable names.
|
||||
func generateAvailableIdentifier(pos token.Pos, file *ast.File, path []ast.Node, info *types.Info, prefix string, idx int) string {
|
||||
scopes := collectScopes(info, path, pos)
|
||||
scopes := CollectScopes(info, path, pos)
|
||||
name := prefix + fmt.Sprintf("%d", idx)
|
||||
for file.Scope.Lookup(name) != nil || !isValidName(name, scopes) {
|
||||
idx++
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import (
|
|||
|
||||
// FoldingRangeInfo holds range and kind info of folding for an ast.Node
|
||||
type FoldingRangeInfo struct {
|
||||
mappedRange
|
||||
MappedRange
|
||||
Kind protocol.FoldingRangeKind
|
||||
}
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ func foldingRangeFunc(fset *token.FileSet, m *protocol.ColumnMapper, n ast.Node,
|
|||
return nil
|
||||
}
|
||||
return &FoldingRangeInfo{
|
||||
mappedRange: newMappedRange(fset, m, start, end),
|
||||
MappedRange: NewMappedRange(fset, m, start, end),
|
||||
Kind: kind,
|
||||
}
|
||||
}
|
||||
|
|
@ -144,7 +144,7 @@ func commentsFoldingRange(fset *token.FileSet, m *protocol.ColumnMapper, file *a
|
|||
}
|
||||
comments = append(comments, &FoldingRangeInfo{
|
||||
// Fold from the end of the first line comment to the end of the comment block.
|
||||
mappedRange: newMappedRange(fset, m, commentGrp.List[0].End(), commentGrp.End()),
|
||||
MappedRange: NewMappedRange(fset, m, commentGrp.List[0].End(), commentGrp.End()),
|
||||
Kind: protocol.Comment,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,7 +133,8 @@ func computeImportEdits(ctx context.Context, snapshot Snapshot, pgf *ParsedGoFil
|
|||
return allFixEdits, editsPerFix, nil
|
||||
}
|
||||
|
||||
func computeOneImportFixEdits(ctx context.Context, snapshot Snapshot, pgf *ParsedGoFile, fix *imports.ImportFix) ([]protocol.TextEdit, error) {
|
||||
// ComputeOneImportFixEdits returns text edits for a single import fix.
|
||||
func ComputeOneImportFixEdits(ctx context.Context, snapshot Snapshot, pgf *ParsedGoFile, fix *imports.ImportFix) ([]protocol.TextEdit, error) {
|
||||
options := &imports.Options{
|
||||
LocalPrefix: snapshot.View().Options().LocalPrefix,
|
||||
// Defaults.
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ func Highlight(ctx context.Context, snapshot Snapshot, fh FileHandle, pos protoc
|
|||
ctx, done := event.Start(ctx, "source.Highlight")
|
||||
defer done()
|
||||
|
||||
pkg, pgf, err := getParsedFile(ctx, snapshot, fh, WidestPackage)
|
||||
pkg, pgf, err := GetParsedFile(ctx, snapshot, fh, WidestPackage)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("getting file for Highlight: %w", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ func pathLinkAndSymbolName(i *IdentifierInfo) (string, string, string) {
|
|||
return "", "", ""
|
||||
}
|
||||
if r := typ.Recv(); r != nil {
|
||||
switch rtyp := deref(r.Type()).(type) {
|
||||
switch rtyp := Deref(r.Type()).(type) {
|
||||
case *types.Struct:
|
||||
rTypeName = r.Name()
|
||||
case *types.Named:
|
||||
|
|
@ -226,10 +226,12 @@ func hover(ctx context.Context, fset *token.FileSet, pkg Package, d Declaration)
|
|||
_, done := event.Start(ctx, "source.hover")
|
||||
defer done()
|
||||
|
||||
return hoverInfo(pkg, d.obj, d.node)
|
||||
return HoverInfo(pkg, d.obj, d.node)
|
||||
}
|
||||
|
||||
func hoverInfo(pkg Package, obj types.Object, node ast.Node) (*HoverInformation, error) {
|
||||
// HoverInfo returns a HoverInformation struct for an ast node and its type
|
||||
// object.
|
||||
func HoverInfo(pkg Package, obj types.Object, node ast.Node) (*HoverInformation, error) {
|
||||
var info *HoverInformation
|
||||
|
||||
switch node := node.(type) {
|
||||
|
|
|
|||
|
|
@ -22,10 +22,10 @@ import (
|
|||
type IdentifierInfo struct {
|
||||
Name string
|
||||
Snapshot Snapshot
|
||||
mappedRange
|
||||
MappedRange
|
||||
|
||||
Type struct {
|
||||
mappedRange
|
||||
MappedRange
|
||||
Object types.Object
|
||||
}
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ type IdentifierInfo struct {
|
|||
}
|
||||
|
||||
type Declaration struct {
|
||||
MappedRange []mappedRange
|
||||
MappedRange []MappedRange
|
||||
node ast.Node
|
||||
obj types.Object
|
||||
|
||||
|
|
@ -108,7 +108,7 @@ func findIdentifier(ctx context.Context, snapshot Snapshot, pkg Package, file *a
|
|||
return nil, ErrNoIdentFound
|
||||
}
|
||||
|
||||
qf := qualifier(file, pkg.GetTypes(), pkg.GetTypesInfo())
|
||||
qf := Qualifier(file, pkg.GetTypes(), pkg.GetTypesInfo())
|
||||
|
||||
ident, _ := path[0].(*ast.Ident)
|
||||
if ident == nil {
|
||||
|
|
@ -138,13 +138,13 @@ func findIdentifier(ctx context.Context, snapshot Snapshot, pkg Package, file *a
|
|||
return &IdentifierInfo{
|
||||
Name: file.Name.Name,
|
||||
ident: file.Name,
|
||||
mappedRange: rng,
|
||||
MappedRange: rng,
|
||||
pkg: pkg,
|
||||
qf: qf,
|
||||
Snapshot: snapshot,
|
||||
Declaration: Declaration{
|
||||
node: declAST.Name,
|
||||
MappedRange: []mappedRange{declRng},
|
||||
MappedRange: []MappedRange{declRng},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -167,7 +167,7 @@ func findIdentifier(ctx context.Context, snapshot Snapshot, pkg Package, file *a
|
|||
|
||||
result.Name = result.ident.Name
|
||||
var err error
|
||||
if result.mappedRange, err = posToMappedRange(snapshot, pkg, result.ident.Pos(), result.ident.End()); err != nil {
|
||||
if result.MappedRange, err = posToMappedRange(snapshot, pkg, result.ident.Pos(), result.ident.End()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
@ -206,7 +206,7 @@ func findIdentifier(ctx context.Context, snapshot Snapshot, pkg Package, file *a
|
|||
|
||||
// The builtin package isn't in the dependency graph, so the usual utilities
|
||||
// won't work here.
|
||||
rng := newMappedRange(snapshot.FileSet(), builtin.ParsedFile.Mapper, decl.Pos(), decl.Pos()+token.Pos(len(result.Name)))
|
||||
rng := NewMappedRange(snapshot.FileSet(), builtin.ParsedFile.Mapper, decl.Pos(), decl.Pos()+token.Pos(len(result.Name)))
|
||||
result.Declaration.MappedRange = append(result.Declaration.MappedRange, rng)
|
||||
|
||||
return result, nil
|
||||
|
|
@ -242,7 +242,7 @@ func findIdentifier(ctx context.Context, snapshot Snapshot, pkg Package, file *a
|
|||
if hasErrorType(result.Type.Object) {
|
||||
return result, nil
|
||||
}
|
||||
if result.Type.mappedRange, err = objToMappedRange(snapshot, pkg, result.Type.Object); err != nil {
|
||||
if result.Type.MappedRange, err = objToMappedRange(snapshot, pkg, result.Type.Object); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
|
@ -254,7 +254,7 @@ func searchForEnclosing(info *types.Info, path []ast.Node) types.Type {
|
|||
switch n := n.(type) {
|
||||
case *ast.SelectorExpr:
|
||||
if sel, ok := info.Selections[n]; ok {
|
||||
recv := deref(sel.Recv())
|
||||
recv := Deref(sel.Recv())
|
||||
|
||||
// Keep track of the last exported type seen.
|
||||
var exported types.Type
|
||||
|
|
@ -265,7 +265,7 @@ func searchForEnclosing(info *types.Info, path []ast.Node) types.Type {
|
|||
// method itself.
|
||||
for _, index := range sel.Index()[:len(sel.Index())-1] {
|
||||
if r, ok := recv.Underlying().(*types.Struct); ok {
|
||||
recv = deref(r.Field(index).Type())
|
||||
recv = Deref(r.Field(index).Type())
|
||||
if named, ok := recv.(*types.Named); ok && named.Obj().Exported() {
|
||||
exported = named
|
||||
}
|
||||
|
|
@ -304,7 +304,7 @@ func hasErrorType(obj types.Object) bool {
|
|||
}
|
||||
|
||||
func objToDecl(ctx context.Context, snapshot Snapshot, srcPkg Package, obj types.Object) (ast.Decl, error) {
|
||||
pgf, _, err := findPosInPackage(snapshot, srcPkg, obj.Pos())
|
||||
pgf, _, err := FindPosInPackage(snapshot, srcPkg, obj.Pos())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -335,7 +335,7 @@ func importSpec(snapshot Snapshot, pkg Package, file *ast.File, pos token.Pos) (
|
|||
Name: importPath,
|
||||
pkg: pkg,
|
||||
}
|
||||
if result.mappedRange, err = posToMappedRange(snapshot, pkg, imp.Path.Pos(), imp.Path.End()); err != nil {
|
||||
if result.MappedRange, err = posToMappedRange(snapshot, pkg, imp.Path.Pos(), imp.Path.End()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Consider the "declaration" of an import spec to be the imported package.
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ func implementations(ctx context.Context, s Snapshot, f FileHandle, pp protocol.
|
|||
// concreteImplementsIntf returns true if a is an interface type implemented by
|
||||
// concrete type b, or vice versa.
|
||||
func concreteImplementsIntf(a, b types.Type) bool {
|
||||
aIsIntf, bIsIntf := isInterface(a), isInterface(b)
|
||||
aIsIntf, bIsIntf := IsInterface(a), IsInterface(b)
|
||||
|
||||
// Make sure exactly one is an interface type.
|
||||
if aIsIntf == bIsIntf {
|
||||
|
|
@ -184,7 +184,7 @@ func concreteImplementsIntf(a, b types.Type) bool {
|
|||
// type. This is useful to make sure you consider a named type's full method
|
||||
// set.
|
||||
func ensurePointer(T types.Type) types.Type {
|
||||
if _, ok := T.(*types.Named); ok && !isInterface(T) {
|
||||
if _, ok := T.(*types.Named); ok && !IsInterface(T) {
|
||||
return types.NewPointer(T)
|
||||
}
|
||||
|
||||
|
|
@ -248,7 +248,7 @@ func qualifiedObjsAtProtocolPos(ctx context.Context, s Snapshot, fh FileHandle,
|
|||
// Look up the implicit *types.PkgName.
|
||||
obj := searchpkg.GetTypesInfo().Implicits[leaf]
|
||||
if obj == nil {
|
||||
return nil, xerrors.Errorf("%w for import %q", errNoObjectFound, importPath(leaf))
|
||||
return nil, xerrors.Errorf("%w for import %q", errNoObjectFound, ImportPath(leaf))
|
||||
}
|
||||
objs = append(objs, obj)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -250,17 +250,6 @@ func (s ImportShortcut) ShowDefinition() bool {
|
|||
return s == Both || s == Definition
|
||||
}
|
||||
|
||||
type completionOptions struct {
|
||||
deepCompletion bool
|
||||
unimported bool
|
||||
documentation bool
|
||||
fullDocumentation bool
|
||||
placeholders bool
|
||||
literal bool
|
||||
matcher Matcher
|
||||
budget time.Duration
|
||||
}
|
||||
|
||||
// Hooks contains configuration that is provided to the Gopls command by the
|
||||
// main package.
|
||||
type Hooks struct {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import (
|
|||
// ReferenceInfo holds information about reference to an identifier in Go source.
|
||||
type ReferenceInfo struct {
|
||||
Name string
|
||||
mappedRange
|
||||
MappedRange
|
||||
ident *ast.Ident
|
||||
obj types.Object
|
||||
pkg Package
|
||||
|
|
@ -78,7 +78,7 @@ func references(ctx context.Context, snapshot Snapshot, qos []qualifiedObject, i
|
|||
return nil, err
|
||||
}
|
||||
references = append(references, &ReferenceInfo{
|
||||
mappedRange: ident.mappedRange,
|
||||
MappedRange: ident.MappedRange,
|
||||
Name: qos[0].obj.Name(),
|
||||
ident: ident.ident,
|
||||
obj: qos[0].obj,
|
||||
|
|
@ -118,7 +118,7 @@ func references(ctx context.Context, snapshot Snapshot, qos []qualifiedObject, i
|
|||
ident: ident,
|
||||
pkg: pkg,
|
||||
obj: obj,
|
||||
mappedRange: rng,
|
||||
MappedRange: rng,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ func Rename(ctx context.Context, s Snapshot, f FileHandle, pp protocol.Position,
|
|||
for _, ref := range refs {
|
||||
if obj, ok := ref.obj.(*types.Func); ok {
|
||||
recv := obj.Type().(*types.Signature).Recv()
|
||||
if recv != nil && isInterface(recv.Type().Underlying()) {
|
||||
if recv != nil && IsInterface(recv.Type().Underlying()) {
|
||||
r.changeMethods = true
|
||||
break
|
||||
}
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@ func forEachLexicalRef(pkg Package, obj types.Object, fn func(id *ast.Ident, blo
|
|||
if !ok {
|
||||
return visit(nil) // pop stack, don't descend
|
||||
}
|
||||
if _, ok := deref(tv.Type).Underlying().(*types.Struct); ok {
|
||||
if _, ok := Deref(tv.Type).Underlying().(*types.Struct); ok {
|
||||
if n.Type != nil {
|
||||
ast.Inspect(n.Type, visit)
|
||||
}
|
||||
|
|
@ -450,7 +450,7 @@ func (r *renamer) checkStructField(from *types.Var) {
|
|||
if from.Anonymous() {
|
||||
if named, ok := from.Type().(*types.Named); ok {
|
||||
r.check(named.Obj())
|
||||
} else if named, ok := deref(from.Type()).(*types.Named); ok {
|
||||
} else if named, ok := Deref(from.Type()).(*types.Named); ok {
|
||||
r.check(named.Obj())
|
||||
}
|
||||
}
|
||||
|
|
@ -570,7 +570,7 @@ func (r *renamer) checkMethod(from *types.Func) {
|
|||
// Check for conflict at point of declaration.
|
||||
// Check to ensure preservation of assignability requirements.
|
||||
R := recv(from).Type()
|
||||
if isInterface(R) {
|
||||
if IsInterface(R) {
|
||||
// Abstract method
|
||||
|
||||
// declaration
|
||||
|
|
@ -587,7 +587,7 @@ func (r *renamer) checkMethod(from *types.Func) {
|
|||
for _, pkg := range r.packages {
|
||||
// Start with named interface types (better errors)
|
||||
for _, obj := range pkg.GetTypesInfo().Defs {
|
||||
if obj, ok := obj.(*types.TypeName); ok && isInterface(obj.Type()) {
|
||||
if obj, ok := obj.(*types.TypeName); ok && IsInterface(obj.Type()) {
|
||||
f, _, _ := types.LookupFieldOrMethod(
|
||||
obj.Type(), false, from.Pkg(), from.Name())
|
||||
if f == nil {
|
||||
|
|
@ -659,7 +659,7 @@ func (r *renamer) checkMethod(from *types.Func) {
|
|||
// yields abstract method I.f. This can make error
|
||||
// messages less than obvious.
|
||||
//
|
||||
if !isInterface(key.RHS) {
|
||||
if !IsInterface(key.RHS) {
|
||||
// The logic below was derived from checkSelections.
|
||||
|
||||
rtosel := rmethods.Lookup(from.Pkg(), r.to)
|
||||
|
|
@ -734,7 +734,7 @@ func (r *renamer) checkMethod(from *types.Func) {
|
|||
//
|
||||
for key := range r.satisfy() {
|
||||
// key = (lhs, rhs) where lhs is always an interface.
|
||||
if isInterface(key.RHS) {
|
||||
if IsInterface(key.RHS) {
|
||||
continue
|
||||
}
|
||||
rsel := r.msets.MethodSet(key.RHS).Lookup(from.Pkg(), from.Name())
|
||||
|
|
@ -936,10 +936,6 @@ func isPackageLevel(obj types.Object) bool {
|
|||
return obj.Pkg().Scope().Lookup(obj.Name()) == obj
|
||||
}
|
||||
|
||||
func isInterface(T types.Type) bool {
|
||||
return T != nil && types.IsInterface(T)
|
||||
}
|
||||
|
||||
// -- Plundered from go/scanner: ---------------------------------------
|
||||
|
||||
func isLetter(ch rune) bool {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ func SignatureHelp(ctx context.Context, snapshot Snapshot, fh FileHandle, pos pr
|
|||
ctx, done := event.Start(ctx, "source.SignatureHelp")
|
||||
defer done()
|
||||
|
||||
pkg, pgf, err := getParsedFile(ctx, snapshot, fh, NarrowestPackage)
|
||||
pkg, pgf, err := GetParsedFile(ctx, snapshot, fh, NarrowestPackage)
|
||||
if err != nil {
|
||||
return nil, 0, errors.Errorf("getting file for SignatureHelp: %w", err)
|
||||
}
|
||||
|
|
@ -58,7 +58,7 @@ FindCall:
|
|||
return nil, 0, errors.Errorf("cannot find an enclosing function")
|
||||
}
|
||||
|
||||
qf := qualifier(pgf.File, pkg.GetTypes(), pkg.GetTypesInfo())
|
||||
qf := Qualifier(pgf.File, pkg.GetTypes(), pkg.GetTypesInfo())
|
||||
|
||||
// Get the object representing the function, if available.
|
||||
// There is no object in certain cases such as calling a function returned by
|
||||
|
|
@ -116,7 +116,7 @@ FindCall:
|
|||
} else {
|
||||
name = "func"
|
||||
}
|
||||
s, err := newSignature(ctx, snapshot, pkg, pgf.File, name, sig, comment, qf)
|
||||
s, err := NewSignature(ctx, snapshot, pkg, pgf.File, name, sig, comment, qf)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
|
@ -125,14 +125,14 @@ FindCall:
|
|||
paramInfo = append(paramInfo, protocol.ParameterInformation{Label: p})
|
||||
}
|
||||
return &protocol.SignatureInformation{
|
||||
Label: name + s.format(),
|
||||
Label: name + s.Format(),
|
||||
Documentation: doc.Synopsis(s.doc),
|
||||
Parameters: paramInfo,
|
||||
}, activeParam, nil
|
||||
}
|
||||
|
||||
func builtinSignature(ctx context.Context, snapshot Snapshot, callExpr *ast.CallExpr, name string, pos token.Pos) (*protocol.SignatureInformation, int, error) {
|
||||
sig, err := newBuiltinSignature(ctx, snapshot, name)
|
||||
sig, err := NewBuiltinSignature(ctx, snapshot, name)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
|
@ -142,7 +142,7 @@ func builtinSignature(ctx context.Context, snapshot Snapshot, callExpr *ast.Call
|
|||
}
|
||||
activeParam := activeParameter(callExpr, len(sig.params), sig.variadic, pos)
|
||||
return &protocol.SignatureInformation{
|
||||
Label: sig.name + sig.format(),
|
||||
Label: sig.name + sig.Format(),
|
||||
Documentation: doc.Synopsis(sig.doc),
|
||||
Parameters: paramInfo,
|
||||
}, activeParam, nil
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
"golang.org/x/tools/internal/lsp/fuzzy"
|
||||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
"golang.org/x/tools/internal/lsp/source"
|
||||
"golang.org/x/tools/internal/lsp/source/completion"
|
||||
"golang.org/x/tools/internal/lsp/tests"
|
||||
"golang.org/x/tools/internal/span"
|
||||
"golang.org/x/tools/internal/testenv"
|
||||
|
|
@ -304,11 +305,11 @@ func (r *runner) callCompletion(t *testing.T, src span.Span, options func(*sourc
|
|||
}
|
||||
defer r.view.SetOptions(r.ctx, original)
|
||||
|
||||
list, surrounding, err := source.Completion(r.ctx, r.snapshot, fh, protocol.Position{
|
||||
list, surrounding, err := completion.Completion(r.ctx, r.snapshot, fh, protocol.Position{
|
||||
Line: float64(src.Start().Line() - 1),
|
||||
Character: float64(src.Start().Column() - 1),
|
||||
}, "")
|
||||
if err != nil && !errors.As(err, &source.ErrIsDefinition{}) {
|
||||
if err != nil && !errors.As(err, &completion.ErrIsDefinition{}) {
|
||||
t.Fatalf("failed for %v: %v", src, err)
|
||||
}
|
||||
var prefix string
|
||||
|
|
@ -317,14 +318,14 @@ func (r *runner) callCompletion(t *testing.T, src span.Span, options func(*sourc
|
|||
}
|
||||
|
||||
var numDeepCompletionsSeen int
|
||||
var items []source.CompletionItem
|
||||
var items []completion.CompletionItem
|
||||
// Apply deep completion filtering.
|
||||
for _, item := range list {
|
||||
if item.Depth > 0 {
|
||||
if !modified.DeepCompletion {
|
||||
continue
|
||||
}
|
||||
if numDeepCompletionsSeen >= source.MaxDeepCompletions {
|
||||
if numDeepCompletionsSeen >= completion.MaxDeepCompletions {
|
||||
continue
|
||||
}
|
||||
numDeepCompletionsSeen++
|
||||
|
|
|
|||
|
|
@ -19,13 +19,13 @@ func DocumentSymbols(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]p
|
|||
ctx, done := event.Start(ctx, "source.DocumentSymbols")
|
||||
defer done()
|
||||
|
||||
pkg, pgf, err := getParsedFile(ctx, snapshot, fh, NarrowestPackage)
|
||||
pkg, pgf, err := GetParsedFile(ctx, snapshot, fh, NarrowestPackage)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("getting file for DocumentSymbols: %w", err)
|
||||
}
|
||||
|
||||
info := pkg.GetTypesInfo()
|
||||
q := qualifier(pgf.File, pkg.GetTypes(), info)
|
||||
q := Qualifier(pgf.File, pkg.GetTypes(), info)
|
||||
|
||||
symbolsToReceiver := make(map[types.Type]int)
|
||||
var symbols []protocol.DocumentSymbol
|
||||
|
|
@ -113,7 +113,7 @@ func typeSymbol(snapshot Snapshot, pkg Package, info *types.Info, spec *ast.Type
|
|||
s := protocol.DocumentSymbol{
|
||||
Name: obj.Name(),
|
||||
}
|
||||
s.Detail, _ = formatType(obj.Type(), qf)
|
||||
s.Detail, _ = FormatType(obj.Type(), qf)
|
||||
s.Kind = typeToKind(obj.Type())
|
||||
|
||||
var err error
|
||||
|
|
@ -134,7 +134,7 @@ func typeSymbol(snapshot Snapshot, pkg Package, info *types.Info, spec *ast.Type
|
|||
Name: f.Name(),
|
||||
Kind: protocol.Field,
|
||||
}
|
||||
child.Detail, _ = formatType(f.Type(), qf)
|
||||
child.Detail, _ = FormatType(f.Type(), qf)
|
||||
|
||||
spanNode, selectionNode := nodesForStructField(i, st)
|
||||
if span, err := nodeToProtocolRange(snapshot, pkg, spanNode); err == nil {
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ import (
|
|||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
)
|
||||
|
||||
// formatType returns the detail and kind for a types.Type.
|
||||
func formatType(typ types.Type, qf types.Qualifier) (detail string, kind protocol.CompletionItemKind) {
|
||||
// FormatType returns the detail and kind for a types.Type.
|
||||
func FormatType(typ types.Type, qf types.Qualifier) (detail string, kind protocol.CompletionItemKind) {
|
||||
if types.IsInterface(typ) {
|
||||
detail = "interface{...}"
|
||||
kind = protocol.InterfaceCompletion
|
||||
|
|
@ -28,7 +28,7 @@ func formatType(typ types.Type, qf types.Qualifier) (detail string, kind protoco
|
|||
detail = "struct{...}"
|
||||
kind = protocol.StructCompletion
|
||||
} else if typ != typ.Underlying() {
|
||||
detail, kind = formatType(typ.Underlying(), qf)
|
||||
detail, kind = FormatType(typ.Underlying(), qf)
|
||||
} else {
|
||||
detail = types.TypeString(typ, qf)
|
||||
kind = protocol.ClassCompletion
|
||||
|
|
@ -43,7 +43,7 @@ type signature struct {
|
|||
needResultParens bool
|
||||
}
|
||||
|
||||
func (s *signature) format() string {
|
||||
func (s *signature) Format() string {
|
||||
var b strings.Builder
|
||||
b.WriteByte('(')
|
||||
for i, p := range s.params {
|
||||
|
|
@ -73,7 +73,13 @@ func (s *signature) format() string {
|
|||
return b.String()
|
||||
}
|
||||
|
||||
func newBuiltinSignature(ctx context.Context, snapshot Snapshot, name string) (*signature, error) {
|
||||
func (s *signature) Params() []string {
|
||||
return s.params
|
||||
}
|
||||
|
||||
// NewBuiltinSignature returns signature for the builtin object with a given
|
||||
// name, if a builtin object with the name exists.
|
||||
func NewBuiltinSignature(ctx context.Context, snapshot Snapshot, name string) (*signature, error) {
|
||||
builtin, err := snapshot.BuiltinPackage(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -153,11 +159,12 @@ func formatFieldList(ctx context.Context, snapshot Snapshot, list *ast.FieldList
|
|||
return result, writeResultParens
|
||||
}
|
||||
|
||||
func newSignature(ctx context.Context, s Snapshot, pkg Package, file *ast.File, name string, sig *types.Signature, comment *ast.CommentGroup, qf types.Qualifier) (*signature, error) {
|
||||
// NewSignature returns formatted signature for a types.Signature struct.
|
||||
func NewSignature(ctx context.Context, s Snapshot, pkg Package, file *ast.File, name string, sig *types.Signature, comment *ast.CommentGroup, qf types.Qualifier) (*signature, error) {
|
||||
params := make([]string, 0, sig.Params().Len())
|
||||
for i := 0; i < sig.Params().Len(); i++ {
|
||||
el := sig.Params().At(i)
|
||||
typ := formatVarType(ctx, s, pkg, file, el, qf)
|
||||
typ := FormatVarType(ctx, s, pkg, file, el, qf)
|
||||
p := typ
|
||||
if el.Name() != "" {
|
||||
p = el.Name() + " " + typ
|
||||
|
|
@ -171,7 +178,7 @@ func newSignature(ctx context.Context, s Snapshot, pkg Package, file *ast.File,
|
|||
needResultParens = true
|
||||
}
|
||||
el := sig.Results().At(i)
|
||||
typ := formatVarType(ctx, s, pkg, file, el, qf)
|
||||
typ := FormatVarType(ctx, s, pkg, file, el, qf)
|
||||
if el.Name() == "" {
|
||||
results = append(results, typ)
|
||||
} else {
|
||||
|
|
@ -194,11 +201,11 @@ func newSignature(ctx context.Context, s Snapshot, pkg Package, file *ast.File,
|
|||
}, nil
|
||||
}
|
||||
|
||||
// formatVarType formats a *types.Var, accounting for type aliases.
|
||||
// FormatVarType formats a *types.Var, accounting for type aliases.
|
||||
// To do this, it looks in the AST of the file in which the object is declared.
|
||||
// On any errors, it always fallbacks back to types.TypeString.
|
||||
func formatVarType(ctx context.Context, snapshot Snapshot, srcpkg Package, srcfile *ast.File, obj *types.Var, qf types.Qualifier) string {
|
||||
pgf, pkg, err := findPosInPackage(snapshot, srcpkg, obj.Pos())
|
||||
func FormatVarType(ctx context.Context, snapshot Snapshot, srcpkg Package, srcfile *ast.File, obj *types.Var, qf types.Qualifier) string {
|
||||
pgf, pkg, err := FindPosInPackage(snapshot, srcpkg, obj.Pos())
|
||||
if err != nil {
|
||||
return types.TypeString(obj.Type(), qf)
|
||||
}
|
||||
|
|
@ -218,7 +225,7 @@ func formatVarType(ctx context.Context, snapshot Snapshot, srcpkg Package, srcfi
|
|||
// If the request came from a different package than the one in which the
|
||||
// types are defined, we may need to modify the qualifiers.
|
||||
qualified = qualifyExpr(snapshot.FileSet(), qualified, srcpkg, pkg, srcfile, clonedInfo, qf)
|
||||
fmted := formatNode(snapshot.FileSet(), qualified)
|
||||
fmted := FormatNode(snapshot.FileSet(), qualified)
|
||||
return fmted
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import (
|
|||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
|
|
@ -22,7 +23,9 @@ import (
|
|||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type mappedRange struct {
|
||||
// MappedRange provides mapped protocol.Range for a span.Range, accounting for
|
||||
// UTF-16 code points.
|
||||
type MappedRange struct {
|
||||
spanRange span.Range
|
||||
m *protocol.ColumnMapper
|
||||
|
||||
|
|
@ -31,8 +34,9 @@ type mappedRange struct {
|
|||
protocolRange *protocol.Range
|
||||
}
|
||||
|
||||
func newMappedRange(fset *token.FileSet, m *protocol.ColumnMapper, start, end token.Pos) mappedRange {
|
||||
return mappedRange{
|
||||
// NewMappedRange returns a MappedRange for the given start and end token.Pos.
|
||||
func NewMappedRange(fset *token.FileSet, m *protocol.ColumnMapper, start, end token.Pos) MappedRange {
|
||||
return MappedRange{
|
||||
spanRange: span.Range{
|
||||
FileSet: fset,
|
||||
Start: start,
|
||||
|
|
@ -43,7 +47,7 @@ func newMappedRange(fset *token.FileSet, m *protocol.ColumnMapper, start, end to
|
|||
}
|
||||
}
|
||||
|
||||
func (s mappedRange) Range() (protocol.Range, error) {
|
||||
func (s MappedRange) Range() (protocol.Range, error) {
|
||||
if s.protocolRange == nil {
|
||||
spn, err := s.spanRange.Span()
|
||||
if err != nil {
|
||||
|
|
@ -58,17 +62,22 @@ func (s mappedRange) Range() (protocol.Range, error) {
|
|||
return *s.protocolRange, nil
|
||||
}
|
||||
|
||||
func (s mappedRange) Span() (span.Span, error) {
|
||||
func (s MappedRange) Span() (span.Span, error) {
|
||||
return s.spanRange.Span()
|
||||
}
|
||||
|
||||
func (s mappedRange) URI() span.URI {
|
||||
func (s MappedRange) SpanRange() span.Range {
|
||||
return s.spanRange
|
||||
}
|
||||
|
||||
func (s MappedRange) URI() span.URI {
|
||||
return s.m.URI
|
||||
}
|
||||
|
||||
// getParsedFile is a convenience function that extracts the Package and ParsedGoFile for a File in a Snapshot.
|
||||
// selectPackage is typically Narrowest/WidestPackageHandle below.
|
||||
func getParsedFile(ctx context.Context, snapshot Snapshot, fh FileHandle, selectPackage PackagePolicy) (Package, *ParsedGoFile, error) {
|
||||
// GetParsedFile is a convenience function that extracts the Package and
|
||||
// ParsedGoFile for a File in a Snapshot. selectPackage is typically
|
||||
// Narrowest/WidestPackageHandle below.
|
||||
func GetParsedFile(ctx context.Context, snapshot Snapshot, fh FileHandle, selectPackage PackagePolicy) (Package, *ParsedGoFile, error) {
|
||||
phs, err := snapshot.PackagesForFile(ctx, fh.URI(), TypecheckWorkspace)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
|
@ -158,7 +167,7 @@ func nodeToProtocolRange(snapshot Snapshot, pkg Package, n ast.Node) (protocol.R
|
|||
return mrng.Range()
|
||||
}
|
||||
|
||||
func objToMappedRange(snapshot Snapshot, pkg Package, obj types.Object) (mappedRange, error) {
|
||||
func objToMappedRange(snapshot Snapshot, pkg Package, obj types.Object) (MappedRange, error) {
|
||||
if pkgName, ok := obj.(*types.PkgName); ok {
|
||||
// An imported Go package has a package-local, unqualified name.
|
||||
// When the name matches the imported package name, there is no
|
||||
|
|
@ -177,23 +186,23 @@ func objToMappedRange(snapshot Snapshot, pkg Package, obj types.Object) (mappedR
|
|||
return nameToMappedRange(snapshot, pkg, obj.Pos(), obj.Name())
|
||||
}
|
||||
|
||||
func nameToMappedRange(snapshot Snapshot, pkg Package, pos token.Pos, name string) (mappedRange, error) {
|
||||
func nameToMappedRange(snapshot Snapshot, pkg Package, pos token.Pos, name string) (MappedRange, error) {
|
||||
return posToMappedRange(snapshot, pkg, pos, pos+token.Pos(len(name)))
|
||||
}
|
||||
|
||||
func posToMappedRange(snapshot Snapshot, pkg Package, pos, end token.Pos) (mappedRange, error) {
|
||||
func posToMappedRange(snapshot Snapshot, pkg Package, pos, end token.Pos) (MappedRange, error) {
|
||||
logicalFilename := snapshot.FileSet().File(pos).Position(pos).Filename
|
||||
pgf, _, err := findFileInDeps(pkg, span.URIFromPath(logicalFilename))
|
||||
if err != nil {
|
||||
return mappedRange{}, err
|
||||
return MappedRange{}, err
|
||||
}
|
||||
if !pos.IsValid() {
|
||||
return mappedRange{}, errors.Errorf("invalid position for %v", pos)
|
||||
return MappedRange{}, errors.Errorf("invalid position for %v", pos)
|
||||
}
|
||||
if !end.IsValid() {
|
||||
return mappedRange{}, errors.Errorf("invalid position for %v", end)
|
||||
return MappedRange{}, errors.Errorf("invalid position for %v", end)
|
||||
}
|
||||
return newMappedRange(snapshot.FileSet(), pgf.Mapper, pos, end), nil
|
||||
return NewMappedRange(snapshot.FileSet(), pgf.Mapper, pos, end), nil
|
||||
}
|
||||
|
||||
// Matches cgo generated comment as well as the proposed standard:
|
||||
|
|
@ -231,7 +240,8 @@ func (k FileKind) String() string {
|
|||
}
|
||||
}
|
||||
|
||||
// Returns the index and the node whose position is contained inside the node list.
|
||||
// nodeAtPos returns the index and the node whose position is contained inside
|
||||
// the node list.
|
||||
func nodeAtPos(nodes []ast.Node, pos token.Pos) (ast.Node, int) {
|
||||
if nodes == nil {
|
||||
return nil, -1
|
||||
|
|
@ -244,121 +254,13 @@ func nodeAtPos(nodes []ast.Node, pos token.Pos) (ast.Node, int) {
|
|||
return nil, -1
|
||||
}
|
||||
|
||||
// indexExprAtPos returns the index of the expression containing pos.
|
||||
func exprAtPos(pos token.Pos, args []ast.Expr) int {
|
||||
for i, expr := range args {
|
||||
if expr.Pos() <= pos && pos <= expr.End() {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return len(args)
|
||||
// IsInterface returns if a types.Type is an interface
|
||||
func IsInterface(T types.Type) bool {
|
||||
return T != nil && types.IsInterface(T)
|
||||
}
|
||||
|
||||
// eachField invokes fn for each field that can be selected from a
|
||||
// value of type T.
|
||||
func eachField(T types.Type, fn func(*types.Var)) {
|
||||
// TODO(adonovan): this algorithm doesn't exclude ambiguous
|
||||
// selections that match more than one field/method.
|
||||
// types.NewSelectionSet should do that for us.
|
||||
|
||||
// for termination on recursive types
|
||||
var seen map[*types.Struct]bool
|
||||
|
||||
var visit func(T types.Type)
|
||||
visit = func(T types.Type) {
|
||||
if T, ok := deref(T).Underlying().(*types.Struct); ok {
|
||||
if seen[T] {
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < T.NumFields(); i++ {
|
||||
f := T.Field(i)
|
||||
fn(f)
|
||||
if f.Anonymous() {
|
||||
if seen == nil {
|
||||
// Lazily create "seen" since it is only needed for
|
||||
// embedded structs.
|
||||
seen = make(map[*types.Struct]bool)
|
||||
}
|
||||
seen[T] = true
|
||||
visit(f.Type())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
visit(T)
|
||||
}
|
||||
|
||||
// typeIsValid reports whether typ doesn't contain any Invalid types.
|
||||
func typeIsValid(typ types.Type) bool {
|
||||
// Check named types separately, because we don't want
|
||||
// to call Underlying() on them to avoid problems with recursive types.
|
||||
if _, ok := typ.(*types.Named); ok {
|
||||
return true
|
||||
}
|
||||
|
||||
switch typ := typ.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
return typ.Kind() != types.Invalid
|
||||
case *types.Array:
|
||||
return typeIsValid(typ.Elem())
|
||||
case *types.Slice:
|
||||
return typeIsValid(typ.Elem())
|
||||
case *types.Pointer:
|
||||
return typeIsValid(typ.Elem())
|
||||
case *types.Map:
|
||||
return typeIsValid(typ.Key()) && typeIsValid(typ.Elem())
|
||||
case *types.Chan:
|
||||
return typeIsValid(typ.Elem())
|
||||
case *types.Signature:
|
||||
return typeIsValid(typ.Params()) && typeIsValid(typ.Results())
|
||||
case *types.Tuple:
|
||||
for i := 0; i < typ.Len(); i++ {
|
||||
if !typeIsValid(typ.At(i).Type()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case *types.Struct, *types.Interface:
|
||||
// Don't bother checking structs, interfaces for validity.
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// resolveInvalid traverses the node of the AST that defines the scope
|
||||
// containing the declaration of obj, and attempts to find a user-friendly
|
||||
// name for its invalid type. The resulting Object and its Type are fake.
|
||||
func resolveInvalid(fset *token.FileSet, obj types.Object, node ast.Node, info *types.Info) types.Object {
|
||||
var resultExpr ast.Expr
|
||||
ast.Inspect(node, func(node ast.Node) bool {
|
||||
switch n := node.(type) {
|
||||
case *ast.ValueSpec:
|
||||
for _, name := range n.Names {
|
||||
if info.Defs[name] == obj {
|
||||
resultExpr = n.Type
|
||||
}
|
||||
}
|
||||
return false
|
||||
case *ast.Field: // This case handles parameters and results of a FuncDecl or FuncLit.
|
||||
for _, name := range n.Names {
|
||||
if info.Defs[name] == obj {
|
||||
resultExpr = n.Type
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
})
|
||||
// Construct a fake type for the object and return a fake object with this type.
|
||||
typename := formatNode(fset, resultExpr)
|
||||
typ := types.NewNamed(types.NewTypeName(token.NoPos, obj.Pkg(), typename, nil), types.Typ[types.Invalid], nil)
|
||||
return types.NewVar(obj.Pos(), obj.Pkg(), obj.Name(), typ)
|
||||
}
|
||||
|
||||
func formatNode(fset *token.FileSet, n ast.Node) string {
|
||||
// FormatNode returns the "pretty-print" output for an ast node.
|
||||
func FormatNode(fset *token.FileSet, n ast.Node) string {
|
||||
var buf strings.Builder
|
||||
if err := printer.Fprint(&buf, fset, n); err != nil {
|
||||
return ""
|
||||
|
|
@ -366,19 +268,9 @@ func formatNode(fset *token.FileSet, n ast.Node) string {
|
|||
return buf.String()
|
||||
}
|
||||
|
||||
func isPointer(T types.Type) bool {
|
||||
_, ok := T.(*types.Pointer)
|
||||
return ok
|
||||
}
|
||||
|
||||
func isVar(obj types.Object) bool {
|
||||
_, ok := obj.(*types.Var)
|
||||
return ok
|
||||
}
|
||||
|
||||
// deref returns a pointer's element type, traversing as many levels as needed.
|
||||
// Deref returns a pointer's element type, traversing as many levels as needed.
|
||||
// Otherwise it returns typ.
|
||||
func deref(typ types.Type) types.Type {
|
||||
func Deref(typ types.Type) types.Type {
|
||||
for {
|
||||
p, ok := typ.Underlying().(*types.Pointer)
|
||||
if !ok {
|
||||
|
|
@ -388,113 +280,6 @@ func deref(typ types.Type) types.Type {
|
|||
}
|
||||
}
|
||||
|
||||
func isTypeName(obj types.Object) bool {
|
||||
_, ok := obj.(*types.TypeName)
|
||||
return ok
|
||||
}
|
||||
|
||||
func isFunc(obj types.Object) bool {
|
||||
_, ok := obj.(*types.Func)
|
||||
return ok
|
||||
}
|
||||
|
||||
func isEmptyInterface(T types.Type) bool {
|
||||
intf, _ := T.(*types.Interface)
|
||||
return intf != nil && intf.NumMethods() == 0
|
||||
}
|
||||
|
||||
func isUntyped(T types.Type) bool {
|
||||
if basic, ok := T.(*types.Basic); ok {
|
||||
return basic.Info()&types.IsUntyped > 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isPkgName(obj types.Object) bool {
|
||||
_, ok := obj.(*types.PkgName)
|
||||
return ok
|
||||
}
|
||||
|
||||
func isASTFile(n ast.Node) bool {
|
||||
_, ok := n.(*ast.File)
|
||||
return ok
|
||||
}
|
||||
|
||||
func deslice(T types.Type) types.Type {
|
||||
if slice, ok := T.Underlying().(*types.Slice); ok {
|
||||
return slice.Elem()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isSelector returns the enclosing *ast.SelectorExpr when pos is in the
|
||||
// selector.
|
||||
func enclosingSelector(path []ast.Node, pos token.Pos) *ast.SelectorExpr {
|
||||
if len(path) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if sel, ok := path[0].(*ast.SelectorExpr); ok {
|
||||
return sel
|
||||
}
|
||||
|
||||
if _, ok := path[0].(*ast.Ident); ok && len(path) > 1 {
|
||||
if sel, ok := path[1].(*ast.SelectorExpr); ok && pos >= sel.Sel.Pos() {
|
||||
return sel
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func enclosingValueSpec(path []ast.Node) *ast.ValueSpec {
|
||||
for _, n := range path {
|
||||
if vs, ok := n.(*ast.ValueSpec); ok {
|
||||
return vs
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// exprObj returns the types.Object associated with the *ast.Ident or
|
||||
// *ast.SelectorExpr e.
|
||||
func exprObj(info *types.Info, e ast.Expr) types.Object {
|
||||
var ident *ast.Ident
|
||||
switch expr := e.(type) {
|
||||
case *ast.Ident:
|
||||
ident = expr
|
||||
case *ast.SelectorExpr:
|
||||
ident = expr.Sel
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
return info.ObjectOf(ident)
|
||||
}
|
||||
|
||||
// typeConversion returns the type being converted to if call is a type
|
||||
// conversion expression.
|
||||
func typeConversion(call *ast.CallExpr, info *types.Info) types.Type {
|
||||
// Type conversion (e.g. "float64(foo)").
|
||||
if fun, _ := exprObj(info, call.Fun).(*types.TypeName); fun != nil {
|
||||
return fun.Type()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// fieldsAccessible returns whether s has at least one field accessible by p.
|
||||
func fieldsAccessible(s *types.Struct, p *types.Package) bool {
|
||||
for i := 0; i < s.NumFields(); i++ {
|
||||
f := s.Field(i)
|
||||
if f.Exported() || f.Pkg() == p {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func SortDiagnostics(d []*Diagnostic) {
|
||||
sort.Slice(d, func(i int, j int) bool {
|
||||
return CompareDiagnostic(d[i], d[j]) < 0
|
||||
|
|
@ -517,7 +302,9 @@ func CompareDiagnostic(a, b *Diagnostic) int {
|
|||
return 1
|
||||
}
|
||||
|
||||
func findPosInPackage(snapshot Snapshot, searchpkg Package, pos token.Pos) (*ParsedGoFile, Package, error) {
|
||||
// FindPosInPackage finds the parsed file for a position in a given search
|
||||
// package.
|
||||
func FindPosInPackage(snapshot Snapshot, searchpkg Package, pos token.Pos) (*ParsedGoFile, Package, error) {
|
||||
tok := snapshot.FileSet().File(pos)
|
||||
if tok == nil {
|
||||
return nil, nil, errors.Errorf("no file for pos in package %s", searchpkg.ID())
|
||||
|
|
@ -553,57 +340,6 @@ func findFileInDeps(pkg Package, uri span.URI) (*ParsedGoFile, Package, error) {
|
|||
return nil, nil, errors.Errorf("no file for %s in package %s", uri, pkg.ID())
|
||||
}
|
||||
|
||||
// prevStmt returns the statement that precedes the statement containing pos.
|
||||
// For example:
|
||||
//
|
||||
// foo := 1
|
||||
// bar(1 + 2<>)
|
||||
//
|
||||
// If "<>" is pos, prevStmt returns "foo := 1"
|
||||
func prevStmt(pos token.Pos, path []ast.Node) ast.Stmt {
|
||||
var blockLines []ast.Stmt
|
||||
for i := 0; i < len(path) && blockLines == nil; i++ {
|
||||
switch n := path[i].(type) {
|
||||
case *ast.BlockStmt:
|
||||
blockLines = n.List
|
||||
case *ast.CommClause:
|
||||
blockLines = n.Body
|
||||
case *ast.CaseClause:
|
||||
blockLines = n.Body
|
||||
}
|
||||
}
|
||||
|
||||
for i := len(blockLines) - 1; i >= 0; i-- {
|
||||
if blockLines[i].End() < pos {
|
||||
return blockLines[i]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// formatZeroValue produces Go code representing the zero value of T. It
|
||||
// returns the empty string if T is invalid.
|
||||
func formatZeroValue(T types.Type, qf types.Qualifier) string {
|
||||
switch u := T.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
switch {
|
||||
case u.Info()&types.IsNumeric > 0:
|
||||
return "0"
|
||||
case u.Info()&types.IsString > 0:
|
||||
return `""`
|
||||
case u.Info()&types.IsBoolean > 0:
|
||||
return "false"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
case *types.Pointer, *types.Interface, *types.Chan, *types.Map, *types.Slice, *types.Signature:
|
||||
return "nil"
|
||||
default:
|
||||
return types.TypeString(T, qf) + "{}"
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalArgs encodes the given arguments to json.RawMessages. This function
|
||||
// is used to construct arguments to a protocol.Command.
|
||||
//
|
||||
|
|
@ -647,3 +383,68 @@ func UnmarshalArgs(jsonArgs []json.RawMessage, args ...interface{}) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ImportPath returns the unquoted import path of s,
|
||||
// or "" if the path is not properly quoted.
|
||||
func ImportPath(s *ast.ImportSpec) string {
|
||||
t, err := strconv.Unquote(s.Path.Value)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// NodeContains returns true if a node encloses a given position pos.
|
||||
func NodeContains(n ast.Node, pos token.Pos) bool {
|
||||
return n != nil && n.Pos() <= pos && pos <= n.End()
|
||||
}
|
||||
|
||||
// CollectScopes returns all scopes in an ast path, ordered as innermost scope
|
||||
// first.
|
||||
func CollectScopes(info *types.Info, path []ast.Node, pos token.Pos) []*types.Scope {
|
||||
// scopes[i], where i<len(path), is the possibly nil Scope of path[i].
|
||||
var scopes []*types.Scope
|
||||
for _, n := range path {
|
||||
// Include *FuncType scope if pos is inside the function body.
|
||||
switch node := n.(type) {
|
||||
case *ast.FuncDecl:
|
||||
if node.Body != nil && NodeContains(node.Body, pos) {
|
||||
n = node.Type
|
||||
}
|
||||
case *ast.FuncLit:
|
||||
if node.Body != nil && NodeContains(node.Body, pos) {
|
||||
n = node.Type
|
||||
}
|
||||
}
|
||||
scopes = append(scopes, info.Scopes[n])
|
||||
}
|
||||
return scopes
|
||||
}
|
||||
|
||||
// Qualifier returns a function that appropriately formats a types.PkgName
|
||||
// appearing in a *ast.File.
|
||||
func Qualifier(f *ast.File, pkg *types.Package, info *types.Info) types.Qualifier {
|
||||
// Construct mapping of import paths to their defined or implicit names.
|
||||
imports := make(map[*types.Package]string)
|
||||
for _, imp := range f.Imports {
|
||||
var obj types.Object
|
||||
if imp.Name != nil {
|
||||
obj = info.Defs[imp.Name]
|
||||
} else {
|
||||
obj = info.Implicits[imp]
|
||||
}
|
||||
if pkgname, ok := obj.(*types.PkgName); ok {
|
||||
imports[pkgname.Imported()] = pkgname.Name()
|
||||
}
|
||||
}
|
||||
// Define qualifier to replace full package paths with names of the imports.
|
||||
return func(p *types.Package) string {
|
||||
if p == pkg {
|
||||
return ""
|
||||
}
|
||||
if name, ok := imports[p]; ok {
|
||||
return name
|
||||
}
|
||||
return p.Name()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -512,7 +512,7 @@ func (sc *symbolCollector) match(name string, kind protocol.SymbolKind, node ast
|
|||
return
|
||||
}
|
||||
|
||||
mrng := newMappedRange(sc.current.snapshot.FileSet(), sc.curFile.Mapper, node.Pos(), node.End())
|
||||
mrng := NewMappedRange(sc.current.snapshot.FileSet(), sc.curFile.Mapper, node.Pos(), node.End())
|
||||
rng, err := mrng.Range()
|
||||
if err != nil {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import (
|
|||
"golang.org/x/tools/go/packages/packagestest"
|
||||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
"golang.org/x/tools/internal/lsp/source"
|
||||
"golang.org/x/tools/internal/lsp/source/completion"
|
||||
"golang.org/x/tools/internal/span"
|
||||
"golang.org/x/tools/internal/testenv"
|
||||
"golang.org/x/tools/txtar"
|
||||
|
|
@ -46,7 +47,7 @@ var UpdateGolden = flag.Bool("golden", false, "Update golden files")
|
|||
type CallHierarchy map[span.Span]*CallHierarchyResult
|
||||
type CodeLens map[span.URI][]protocol.CodeLens
|
||||
type Diagnostics map[span.URI][]*source.Diagnostic
|
||||
type CompletionItems map[token.Pos]*source.CompletionItem
|
||||
type CompletionItems map[token.Pos]*completion.CompletionItem
|
||||
type Completions map[span.Span][]Completion
|
||||
type CompletionSnippets map[span.Span][]CompletionSnippet
|
||||
type UnimportedCompletions map[span.Span][]Completion
|
||||
|
|
@ -1037,7 +1038,7 @@ func (data *Data) collectCompletionItems(pos token.Pos, args []string) {
|
|||
if len(args) == 4 {
|
||||
documentation = args[3]
|
||||
}
|
||||
data.CompletionItems[pos] = &source.CompletionItem{
|
||||
data.CompletionItems[pos] = &completion.CompletionItem{
|
||||
Label: label,
|
||||
Detail: detail,
|
||||
Kind: protocol.ParseCompletionItemKind(kind),
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import (
|
|||
"golang.org/x/tools/internal/lsp/diff/myers"
|
||||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
"golang.org/x/tools/internal/lsp/source"
|
||||
"golang.org/x/tools/internal/lsp/source/completion"
|
||||
"golang.org/x/tools/internal/span"
|
||||
)
|
||||
|
||||
|
|
@ -332,7 +333,7 @@ func DiffCallHierarchyItems(gotCalls []protocol.CallHierarchyItem, expectedCalls
|
|||
return ""
|
||||
}
|
||||
|
||||
func ToProtocolCompletionItems(items []source.CompletionItem) []protocol.CompletionItem {
|
||||
func ToProtocolCompletionItems(items []completion.CompletionItem) []protocol.CompletionItem {
|
||||
var result []protocol.CompletionItem
|
||||
for _, item := range items {
|
||||
result = append(result, ToProtocolCompletionItem(item))
|
||||
|
|
@ -340,7 +341,7 @@ func ToProtocolCompletionItems(items []source.CompletionItem) []protocol.Complet
|
|||
return result
|
||||
}
|
||||
|
||||
func ToProtocolCompletionItem(item source.CompletionItem) protocol.CompletionItem {
|
||||
func ToProtocolCompletionItem(item completion.CompletionItem) protocol.CompletionItem {
|
||||
pItem := protocol.CompletionItem{
|
||||
Label: item.Label,
|
||||
Kind: item.Kind,
|
||||
|
|
@ -463,7 +464,7 @@ func DiffSnippets(want string, got *protocol.CompletionItem) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func FindItem(list []protocol.CompletionItem, want source.CompletionItem) *protocol.CompletionItem {
|
||||
func FindItem(list []protocol.CompletionItem, want completion.CompletionItem) *protocol.CompletionItem {
|
||||
for _, item := range list {
|
||||
if item.Label == want.Label {
|
||||
return &item
|
||||
|
|
|
|||
Loading…
Reference in New Issue