Bumping k8s dependencies to 1.13
This commit is contained in:
734
vendor/golang.org/x/tools/internal/lsp/source/completion.go
generated
vendored
Normal file
734
vendor/golang.org/x/tools/internal/lsp/source/completion.go
generated
vendored
Normal file
@@ -0,0 +1,734 @@
|
||||
package source
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
)
|
||||
|
||||
type CompletionItem struct {
|
||||
Label, Detail string
|
||||
Kind CompletionItemKind
|
||||
Score int
|
||||
}
|
||||
|
||||
type CompletionItemKind int
|
||||
|
||||
const (
|
||||
Unknown CompletionItemKind = iota
|
||||
InterfaceCompletionItem
|
||||
StructCompletionItem
|
||||
TypeCompletionItem
|
||||
ConstantCompletionItem
|
||||
FieldCompletionItem
|
||||
ParameterCompletionItem
|
||||
VariableCompletionItem
|
||||
FunctionCompletionItem
|
||||
MethodCompletionItem
|
||||
PackageCompletionItem
|
||||
)
|
||||
|
||||
func Completion(ctx context.Context, f *File, pos token.Pos) ([]CompletionItem, error) {
|
||||
file, err := f.GetAST()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pkg, err := f.GetPackage()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items, _, err := completions(file, pos, pkg.Fset, pkg.Types, pkg.TypesInfo)
|
||||
return items, err
|
||||
}
|
||||
|
||||
type finder func(types.Object, float64, []CompletionItem) []CompletionItem
|
||||
|
||||
// completions returns the map of possible candidates for completion, given a
|
||||
// position, a file AST, and type information. The prefix is computed based on
|
||||
// the preceding identifier and can be used by the client to score the quality
|
||||
// of the completion. For instance, some clients may tolerate imperfect matches
|
||||
// as valid completion results, since users may make typos.
|
||||
func completions(file *ast.File, pos token.Pos, fset *token.FileSet, pkg *types.Package, info *types.Info) (items []CompletionItem, prefix string, err error) {
|
||||
path, _ := astutil.PathEnclosingInterval(file, pos, pos)
|
||||
if path == nil {
|
||||
return nil, "", fmt.Errorf("cannot find node enclosing position")
|
||||
}
|
||||
// If the position is not an identifier but immediately follows
|
||||
// an identifier or selector period (as is common when
|
||||
// requesting a completion), use the path to the preceding node.
|
||||
if _, ok := path[0].(*ast.Ident); !ok {
|
||||
if p, _ := astutil.PathEnclosingInterval(file, pos-1, pos-1); p != nil {
|
||||
switch p[0].(type) {
|
||||
case *ast.Ident, *ast.SelectorExpr:
|
||||
path = p // use preceding ident/selector
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save certain facts about the query position, including the expected type
|
||||
// of the completion result, the signature of the function enclosing the
|
||||
// position.
|
||||
typ := expectedType(path, pos, info)
|
||||
sig := enclosingFunction(path, pos, info)
|
||||
pkgStringer := qualifier(file, pkg, info)
|
||||
|
||||
seen := make(map[types.Object]bool)
|
||||
|
||||
// found adds a candidate completion.
|
||||
// Only the first candidate of a given name is considered.
|
||||
found := func(obj types.Object, weight float64, items []CompletionItem) []CompletionItem {
|
||||
if obj.Pkg() != nil && obj.Pkg() != pkg && !obj.Exported() {
|
||||
return items // inaccessible
|
||||
}
|
||||
if !seen[obj] {
|
||||
seen[obj] = true
|
||||
if typ != nil && matchingTypes(typ, obj.Type()) {
|
||||
weight *= 10
|
||||
}
|
||||
if !strings.HasPrefix(obj.Name(), prefix) {
|
||||
return items
|
||||
}
|
||||
item := formatCompletion(obj, pkgStringer, weight, func(v *types.Var) bool {
|
||||
return isParameter(sig, v)
|
||||
})
|
||||
items = append(items, item)
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
// The position is within a composite literal.
|
||||
if items, ok := complit(path, pos, pkg, info, found); ok {
|
||||
return items, "", nil
|
||||
}
|
||||
switch n := path[0].(type) {
|
||||
case *ast.Ident:
|
||||
// Set the filter prefix.
|
||||
prefix = n.Name[:pos-n.Pos()]
|
||||
|
||||
// Is this the Sel part of a selector?
|
||||
if sel, ok := path[1].(*ast.SelectorExpr); ok && sel.Sel == n {
|
||||
return selector(sel, info, found)
|
||||
}
|
||||
// reject defining identifiers
|
||||
if obj, ok := info.Defs[n]; ok {
|
||||
if v, ok := obj.(*types.Var); ok && v.IsField() {
|
||||
// An anonymous field is also a reference to a type.
|
||||
} else {
|
||||
of := ""
|
||||
if obj != nil {
|
||||
qual := types.RelativeTo(pkg)
|
||||
of += ", of " + types.ObjectString(obj, qual)
|
||||
}
|
||||
return nil, "", fmt.Errorf("this is a definition%s", of)
|
||||
}
|
||||
}
|
||||
|
||||
items = append(items, lexical(path, pos, pkg, info, found)...)
|
||||
|
||||
// The function name hasn't been typed yet, but the parens are there:
|
||||
// recv.‸(arg)
|
||||
case *ast.TypeAssertExpr:
|
||||
// Create a fake selector expression.
|
||||
return selector(&ast.SelectorExpr{X: n.X}, info, found)
|
||||
|
||||
case *ast.SelectorExpr:
|
||||
return selector(n, info, found)
|
||||
|
||||
default:
|
||||
// fallback to lexical completions
|
||||
return lexical(path, pos, pkg, info, found), "", nil
|
||||
}
|
||||
|
||||
return items, prefix, nil
|
||||
}
|
||||
|
||||
// selector finds completions for
|
||||
// the specified selector expression.
|
||||
// TODO(rstambler): Set the prefix filter correctly for selectors.
|
||||
func selector(sel *ast.SelectorExpr, info *types.Info, found finder) (items []CompletionItem, prefix string, err error) {
|
||||
// Is sel a qualified identifier?
|
||||
if id, ok := sel.X.(*ast.Ident); ok {
|
||||
if pkgname, ok := info.Uses[id].(*types.PkgName); ok {
|
||||
// Enumerate package members.
|
||||
// TODO(adonovan): can Imported() be nil?
|
||||
scope := pkgname.Imported().Scope()
|
||||
// TODO testcase: bad import
|
||||
for _, name := range scope.Names() {
|
||||
items = found(scope.Lookup(name), 1, items)
|
||||
}
|
||||
return items, prefix, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Inv: sel is a true selector.
|
||||
tv, ok := info.Types[sel.X]
|
||||
if !ok {
|
||||
return nil, "", fmt.Errorf("cannot resolve %s", sel.X)
|
||||
}
|
||||
|
||||
// methods of T
|
||||
mset := types.NewMethodSet(tv.Type)
|
||||
for i := 0; i < mset.Len(); i++ {
|
||||
items = found(mset.At(i).Obj(), 1, items)
|
||||
}
|
||||
|
||||
// methods of *T
|
||||
if tv.Addressable() && !types.IsInterface(tv.Type) && !isPointer(tv.Type) {
|
||||
mset := types.NewMethodSet(types.NewPointer(tv.Type))
|
||||
for i := 0; i < mset.Len(); i++ {
|
||||
items = found(mset.At(i).Obj(), 1, items)
|
||||
}
|
||||
}
|
||||
|
||||
// fields of T
|
||||
for _, f := range fieldSelections(tv.Type) {
|
||||
items = found(f, 1, items)
|
||||
}
|
||||
|
||||
return items, prefix, nil
|
||||
}
|
||||
|
||||
// lexical finds completions in the lexical environment.
|
||||
func lexical(path []ast.Node, pos token.Pos, pkg *types.Package, info *types.Info, found finder) (items []CompletionItem) {
|
||||
var scopes []*types.Scope // scopes[i], where i<len(path), is the possibly nil Scope of path[i].
|
||||
for _, n := range path {
|
||||
switch node := n.(type) {
|
||||
case *ast.FuncDecl:
|
||||
n = node.Type
|
||||
case *ast.FuncLit:
|
||||
n = node.Type
|
||||
}
|
||||
scopes = append(scopes, info.Scopes[n])
|
||||
}
|
||||
scopes = append(scopes, pkg.Scope())
|
||||
|
||||
// Process scopes innermost first.
|
||||
for i, scope := range scopes {
|
||||
if scope == nil {
|
||||
continue
|
||||
}
|
||||
for _, name := range scope.Names() {
|
||||
declScope, obj := scope.LookupParent(name, pos)
|
||||
if declScope != scope {
|
||||
continue // Name was declared in some enclosing scope, or not at all.
|
||||
}
|
||||
// If obj's type is invalid, find the AST node that defines the lexical block
|
||||
// containing the declaration of obj. Don't resolve types for packages.
|
||||
if _, ok := obj.(*types.PkgName); !ok && obj.Type() == types.Typ[types.Invalid] {
|
||||
// Match the scope to its ast.Node. If the scope is the package scope,
|
||||
// use the *ast.File as the starting node.
|
||||
var node ast.Node
|
||||
if i < len(path) {
|
||||
node = path[i]
|
||||
} else if i == len(path) { // use the *ast.File for package scope
|
||||
node = path[i-1]
|
||||
}
|
||||
if node != nil {
|
||||
if resolved := resolveInvalid(obj, node, info); resolved != nil {
|
||||
obj = resolved
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
score := 1.0
|
||||
// Rank builtins significantly lower than other results.
|
||||
if scope == types.Universe {
|
||||
score *= 0.1
|
||||
}
|
||||
items = found(obj, score, items)
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
// complit finds completions for field names inside a composite literal.
|
||||
// It reports whether the node was handled as part of a composite literal.
|
||||
func complit(path []ast.Node, pos token.Pos, pkg *types.Package, info *types.Info, found finder) (items []CompletionItem, ok bool) {
|
||||
var lit *ast.CompositeLit
|
||||
|
||||
// First, determine if the pos is within a composite literal.
|
||||
switch n := path[0].(type) {
|
||||
case *ast.CompositeLit:
|
||||
// The enclosing node will be a composite literal if the user has just
|
||||
// opened the curly brace (e.g. &x{<>) or the completion request is triggered
|
||||
// from an already completed composite literal expression (e.g. &x{foo: 1, <>})
|
||||
//
|
||||
// If the cursor position is within a key-value expression inside the composite
|
||||
// literal, we try to determine if it is before or after the colon. If it is before
|
||||
// the colon, we return field completions. If the cursor does not belong to any
|
||||
// expression within the composite literal, we show composite literal completions.
|
||||
var expr ast.Expr
|
||||
for _, e := range n.Elts {
|
||||
if e.Pos() <= pos && pos < e.End() {
|
||||
expr = e
|
||||
break
|
||||
}
|
||||
}
|
||||
lit = n
|
||||
// If the position belongs to a key-value expression and is after the colon,
|
||||
// don't show composite literal completions.
|
||||
if kv, ok := expr.(*ast.KeyValueExpr); ok && pos > kv.Colon {
|
||||
lit = nil
|
||||
}
|
||||
case *ast.KeyValueExpr:
|
||||
// If the enclosing node is a key-value expression (e.g. &x{foo: <>}),
|
||||
// we show composite literal completions if the cursor position is before the colon.
|
||||
if len(path) > 1 && pos < n.Colon {
|
||||
if l, ok := path[1].(*ast.CompositeLit); ok {
|
||||
lit = l
|
||||
}
|
||||
}
|
||||
case *ast.Ident:
|
||||
// If the enclosing node is an identifier, it can either be an identifier that is
|
||||
// part of a composite literal (e.g. &x{fo<>}), or it can be an identifier that is
|
||||
// part of a key-value expression, which is part of a composite literal (e.g. &x{foo: ba<>).
|
||||
// We handle both of these cases, showing composite literal completions only if
|
||||
// the cursor position for the key-value expression is before the colon.
|
||||
if len(path) > 1 {
|
||||
if l, ok := path[1].(*ast.CompositeLit); ok {
|
||||
lit = l
|
||||
} else if len(path) > 2 {
|
||||
if l, ok := path[2].(*ast.CompositeLit); ok {
|
||||
// Confirm that cursor position is inside curly braces.
|
||||
if l.Lbrace <= pos && pos <= l.Rbrace {
|
||||
lit = l
|
||||
if kv, ok := path[1].(*ast.KeyValueExpr); ok {
|
||||
if pos > kv.Colon {
|
||||
lit = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// We are not in a composite literal.
|
||||
if lit == nil {
|
||||
return nil, false
|
||||
}
|
||||
// Mark fields of the composite literal that have already been set,
|
||||
// except for the current field.
|
||||
hasKeys := false // true if the composite literal already has key-value pairs
|
||||
addedFields := make(map[*types.Var]bool)
|
||||
for _, el := range lit.Elts {
|
||||
if kv, ok := el.(*ast.KeyValueExpr); ok {
|
||||
hasKeys = true
|
||||
if kv.Pos() <= pos && pos <= kv.End() {
|
||||
continue
|
||||
}
|
||||
if key, ok := kv.Key.(*ast.Ident); ok {
|
||||
if used, ok := info.Uses[key]; ok {
|
||||
if usedVar, ok := used.(*types.Var); ok {
|
||||
addedFields[usedVar] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If the underlying type of the composite literal is a struct,
|
||||
// collect completions for the fields of this struct.
|
||||
if tv, ok := info.Types[lit]; ok {
|
||||
var structPkg *types.Package // package containing the struct type declaration
|
||||
if s, ok := tv.Type.Underlying().(*types.Struct); ok {
|
||||
for i := 0; i < s.NumFields(); i++ {
|
||||
field := s.Field(i)
|
||||
if i == 0 {
|
||||
structPkg = field.Pkg()
|
||||
}
|
||||
if !addedFields[field] {
|
||||
items = found(field, 10, items)
|
||||
}
|
||||
}
|
||||
// Add lexical completions if the user hasn't typed a key value expression
|
||||
// and if the struct fields are defined in the same package as the user is in.
|
||||
if !hasKeys && structPkg == pkg {
|
||||
items = append(items, lexical(path, pos, pkg, info, found)...)
|
||||
}
|
||||
return items, true
|
||||
}
|
||||
}
|
||||
return items, false
|
||||
}
|
||||
|
||||
// formatCompletion creates a completion item for a given types.Object.
|
||||
func formatCompletion(obj types.Object, qualifier types.Qualifier, score float64, isParam func(*types.Var) bool) CompletionItem {
|
||||
label := obj.Name()
|
||||
detail := types.TypeString(obj.Type(), qualifier)
|
||||
var kind CompletionItemKind
|
||||
|
||||
switch o := obj.(type) {
|
||||
case *types.TypeName:
|
||||
detail, kind = formatType(o.Type(), qualifier)
|
||||
if obj.Parent() == types.Universe {
|
||||
detail = ""
|
||||
}
|
||||
case *types.Const:
|
||||
if obj.Parent() == types.Universe {
|
||||
detail = ""
|
||||
} else {
|
||||
val := o.Val().ExactString()
|
||||
if !strings.Contains(val, "\\n") { // skip any multiline constants
|
||||
label += " = " + o.Val().ExactString()
|
||||
}
|
||||
}
|
||||
kind = ConstantCompletionItem
|
||||
case *types.Var:
|
||||
if _, ok := o.Type().(*types.Struct); ok {
|
||||
detail = "struct{...}" // for anonymous structs
|
||||
}
|
||||
if o.IsField() {
|
||||
kind = FieldCompletionItem
|
||||
} else if isParam(o) {
|
||||
kind = ParameterCompletionItem
|
||||
} else {
|
||||
kind = VariableCompletionItem
|
||||
}
|
||||
case *types.Func:
|
||||
if sig, ok := o.Type().(*types.Signature); ok {
|
||||
label += formatParams(sig.Params(), sig.Variadic(), qualifier)
|
||||
detail = strings.Trim(types.TypeString(sig.Results(), qualifier), "()")
|
||||
kind = FunctionCompletionItem
|
||||
if sig.Recv() != nil {
|
||||
kind = MethodCompletionItem
|
||||
}
|
||||
}
|
||||
case *types.Builtin:
|
||||
item, ok := builtinDetails[obj.Name()]
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
label, detail = item.label, item.detail
|
||||
kind = FunctionCompletionItem
|
||||
case *types.PkgName:
|
||||
kind = PackageCompletionItem
|
||||
detail = fmt.Sprintf("\"%s\"", o.Imported().Path())
|
||||
case *types.Nil:
|
||||
kind = VariableCompletionItem
|
||||
detail = ""
|
||||
}
|
||||
detail = strings.TrimPrefix(detail, "untyped ")
|
||||
|
||||
return CompletionItem{
|
||||
Label: label,
|
||||
Detail: detail,
|
||||
Kind: kind,
|
||||
}
|
||||
}
|
||||
|
||||
// formatType returns the detail and kind for an object of type *types.TypeName.
|
||||
func formatType(typ types.Type, qualifier types.Qualifier) (detail string, kind CompletionItemKind) {
|
||||
if types.IsInterface(typ) {
|
||||
detail = "interface{...}"
|
||||
kind = InterfaceCompletionItem
|
||||
} else if _, ok := typ.(*types.Struct); ok {
|
||||
detail = "struct{...}"
|
||||
kind = StructCompletionItem
|
||||
} else if typ != typ.Underlying() {
|
||||
detail, kind = formatType(typ.Underlying(), qualifier)
|
||||
} else {
|
||||
detail = types.TypeString(typ, qualifier)
|
||||
kind = TypeCompletionItem
|
||||
}
|
||||
return detail, kind
|
||||
}
|
||||
|
||||
// formatParams correctly format the parameters of a function.
|
||||
func formatParams(t *types.Tuple, variadic bool, qualifier types.Qualifier) string {
|
||||
var b bytes.Buffer
|
||||
b.WriteByte('(')
|
||||
for i := 0; i < t.Len(); i++ {
|
||||
if i > 0 {
|
||||
b.WriteString(", ")
|
||||
}
|
||||
el := t.At(i)
|
||||
typ := types.TypeString(el.Type(), qualifier)
|
||||
// Handle a variadic parameter (can only be the final parameter).
|
||||
if variadic && i == t.Len()-1 {
|
||||
typ = strings.Replace(typ, "[]", "...", 1)
|
||||
}
|
||||
fmt.Fprintf(&b, "%v %v", el.Name(), typ)
|
||||
}
|
||||
b.WriteByte(')')
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// isParameter returns true if the given *types.Var is a parameter to the given
|
||||
// *types.Signature.
|
||||
func isParameter(sig *types.Signature, v *types.Var) bool {
|
||||
if sig == nil {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < sig.Params().Len(); i++ {
|
||||
if sig.Params().At(i) == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 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(pkg *types.Package) string {
|
||||
if pkg == pkg {
|
||||
return ""
|
||||
}
|
||||
if name, ok := imports[pkg]; ok {
|
||||
return name
|
||||
}
|
||||
return pkg.Name()
|
||||
}
|
||||
}
|
||||
|
||||
// enclosingFunction returns the signature of the function enclosing the given
|
||||
// position.
|
||||
func enclosingFunction(path []ast.Node, pos token.Pos, info *types.Info) *types.Signature {
|
||||
for _, node := range path {
|
||||
switch t := node.(type) {
|
||||
case *ast.FuncDecl:
|
||||
if obj, ok := info.Defs[t.Name]; ok {
|
||||
return obj.Type().(*types.Signature)
|
||||
}
|
||||
case *ast.FuncLit:
|
||||
if typ, ok := info.Types[t]; ok {
|
||||
return typ.Type.(*types.Signature)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// expectedType returns the expected type for an expression at the query position.
|
||||
func expectedType(path []ast.Node, pos token.Pos, info *types.Info) types.Type {
|
||||
for i, node := range path {
|
||||
if i == 2 {
|
||||
break
|
||||
}
|
||||
switch expr := node.(type) {
|
||||
case *ast.BinaryExpr:
|
||||
// Determine if query position comes from left or right of op.
|
||||
e := expr.X
|
||||
if pos < expr.OpPos {
|
||||
e = expr.Y
|
||||
}
|
||||
if tv, ok := info.Types[e]; ok {
|
||||
return tv.Type
|
||||
}
|
||||
case *ast.AssignStmt:
|
||||
// Only rank completions if you are on the right side of the token.
|
||||
if pos <= expr.TokPos {
|
||||
break
|
||||
}
|
||||
i := exprAtPos(pos, expr.Rhs)
|
||||
if i >= len(expr.Lhs) {
|
||||
i = len(expr.Lhs) - 1
|
||||
}
|
||||
if tv, ok := info.Types[expr.Lhs[i]]; ok {
|
||||
return tv.Type
|
||||
}
|
||||
case *ast.CallExpr:
|
||||
if tv, ok := info.Types[expr.Fun]; ok {
|
||||
if sig, ok := tv.Type.(*types.Signature); ok {
|
||||
if sig.Params().Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
i := exprAtPos(pos, expr.Args)
|
||||
// Make sure not to run past the end of expected parameters.
|
||||
if i >= sig.Params().Len() {
|
||||
i = sig.Params().Len() - 1
|
||||
}
|
||||
return sig.Params().At(i).Type()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// matchingTypes reports whether actual is a good candidate type
|
||||
// for a completion in a context of the expected type.
|
||||
func matchingTypes(expected, actual types.Type) bool {
|
||||
// Use a function's return type as its type.
|
||||
if sig, ok := actual.(*types.Signature); ok {
|
||||
if sig.Results().Len() == 1 {
|
||||
actual = sig.Results().At(0).Type()
|
||||
}
|
||||
}
|
||||
return types.Identical(types.Default(expected), types.Default(actual))
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// fieldSelections returns the set of fields that can
|
||||
// be selected from a value of type T.
|
||||
func fieldSelections(T types.Type) (fields []*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.
|
||||
|
||||
seen := make(map[types.Type]bool) // for termination on recursive types
|
||||
var visit func(T types.Type)
|
||||
visit = func(T types.Type) {
|
||||
if !seen[T] {
|
||||
seen[T] = true
|
||||
if T, ok := deref(T).Underlying().(*types.Struct); ok {
|
||||
for i := 0; i < T.NumFields(); i++ {
|
||||
f := T.Field(i)
|
||||
fields = append(fields, f)
|
||||
if f.Anonymous() {
|
||||
visit(f.Type())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
visit(T)
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
func isPointer(T types.Type) bool {
|
||||
_, ok := T.(*types.Pointer)
|
||||
return ok
|
||||
}
|
||||
|
||||
// deref returns a pointer's element type; otherwise it returns typ.
|
||||
func deref(typ types.Type) types.Type {
|
||||
if p, ok := typ.Underlying().(*types.Pointer); ok {
|
||||
return p.Elem()
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
// 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(obj types.Object, node ast.Node, info *types.Info) types.Object {
|
||||
// Construct a fake type for the object and return a fake object with this type.
|
||||
formatResult := func(expr ast.Expr) types.Object {
|
||||
var typename string
|
||||
switch t := expr.(type) {
|
||||
case *ast.SelectorExpr:
|
||||
typename = fmt.Sprintf("%s.%s", t.X, t.Sel)
|
||||
case *ast.Ident:
|
||||
typename = t.String()
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
typ := types.NewNamed(types.NewTypeName(token.NoPos, obj.Pkg(), typename, nil), nil, nil)
|
||||
return types.NewVar(obj.Pos(), obj.Pkg(), obj.Name(), typ)
|
||||
}
|
||||
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
|
||||
// TODO(rstambler): Handle range statements.
|
||||
default:
|
||||
return true
|
||||
}
|
||||
})
|
||||
return formatResult(resultExpr)
|
||||
}
|
||||
|
||||
type itemDetails struct {
|
||||
label, detail string
|
||||
}
|
||||
|
||||
var builtinDetails = map[string]itemDetails{
|
||||
"append": { // append(slice []T, elems ...T)
|
||||
label: "append(slice []T, elems ...T)",
|
||||
detail: "[]T",
|
||||
},
|
||||
"cap": { // cap(v []T) int
|
||||
label: "cap(v []T)",
|
||||
detail: "int",
|
||||
},
|
||||
"close": { // close(c chan<- T)
|
||||
label: "close(c chan<- T)",
|
||||
},
|
||||
"complex": { // complex(r, i float64) complex128
|
||||
label: "complex(real, imag float64)",
|
||||
detail: "complex128",
|
||||
},
|
||||
"copy": { // copy(dst, src []T) int
|
||||
label: "copy(dst, src []T)",
|
||||
detail: "int",
|
||||
},
|
||||
"delete": { // delete(m map[T]T1, key T)
|
||||
label: "delete(m map[K]V, key K)",
|
||||
},
|
||||
"imag": { // imag(c complex128) float64
|
||||
label: "imag(complex128)",
|
||||
detail: "float64",
|
||||
},
|
||||
"len": { // len(v T) int
|
||||
label: "len(T)",
|
||||
detail: "int",
|
||||
},
|
||||
"make": { // make(t T, size ...int) T
|
||||
label: "make(t T, size ...int)",
|
||||
detail: "T",
|
||||
},
|
||||
"new": { // new(T) *T
|
||||
label: "new(T)",
|
||||
detail: "*T",
|
||||
},
|
||||
"panic": { // panic(v interface{})
|
||||
label: "panic(interface{})",
|
||||
},
|
||||
"print": { // print(args ...T)
|
||||
label: "print(args ...T)",
|
||||
},
|
||||
"println": { // println(args ...T)
|
||||
label: "println(args ...T)",
|
||||
},
|
||||
"real": { // real(c complex128) float64
|
||||
label: "real(complex128)",
|
||||
detail: "float64",
|
||||
},
|
||||
"recover": { // recover() interface{}
|
||||
label: "recover()",
|
||||
detail: "interface{}",
|
||||
},
|
||||
}
|
95
vendor/golang.org/x/tools/internal/lsp/source/definition.go
generated
vendored
Normal file
95
vendor/golang.org/x/tools/internal/lsp/source/definition.go
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package source
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
)
|
||||
|
||||
func Definition(ctx context.Context, f *File, pos token.Pos) (Range, error) {
|
||||
fAST, err := f.GetAST()
|
||||
if err != nil {
|
||||
return Range{}, err
|
||||
}
|
||||
pkg, err := f.GetPackage()
|
||||
if err != nil {
|
||||
return Range{}, err
|
||||
}
|
||||
i, err := findIdentifier(fAST, pos)
|
||||
if err != nil {
|
||||
return Range{}, err
|
||||
}
|
||||
if i.ident == nil {
|
||||
return Range{}, fmt.Errorf("definition was not a valid identifier")
|
||||
}
|
||||
obj := pkg.TypesInfo.ObjectOf(i.ident)
|
||||
if obj == nil {
|
||||
return Range{}, fmt.Errorf("no object")
|
||||
}
|
||||
if i.wasEmbeddedField {
|
||||
// the original position was on the embedded field declaration
|
||||
// so we try to dig out the type and jump to that instead
|
||||
if v, ok := obj.(*types.Var); ok {
|
||||
if n, ok := v.Type().(*types.Named); ok {
|
||||
obj = n.Obj()
|
||||
}
|
||||
}
|
||||
}
|
||||
return Range{
|
||||
Start: obj.Pos(),
|
||||
End: obj.Pos() + token.Pos(len([]byte(obj.Name()))), // TODO: use real range of obj
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ident returns the ident plus any extra information needed
|
||||
type ident struct {
|
||||
ident *ast.Ident
|
||||
wasEmbeddedField bool
|
||||
}
|
||||
|
||||
// findIdentifier returns the ast.Ident for a position
|
||||
// in a file, accounting for a potentially incomplete selector.
|
||||
func findIdentifier(f *ast.File, pos token.Pos) (ident, error) {
|
||||
m, err := checkIdentifier(f, pos)
|
||||
if err != nil {
|
||||
return ident{}, err
|
||||
}
|
||||
if m.ident != nil {
|
||||
return m, nil
|
||||
}
|
||||
// If the position is not an identifier but immediately follows
|
||||
// an identifier or selector period (as is common when
|
||||
// requesting a completion), use the path to the preceding node.
|
||||
return checkIdentifier(f, pos-1)
|
||||
}
|
||||
|
||||
// checkIdentifier checks a single position for a potential identifier.
|
||||
func checkIdentifier(f *ast.File, pos token.Pos) (ident, error) {
|
||||
path, _ := astutil.PathEnclosingInterval(f, pos, pos)
|
||||
result := ident{}
|
||||
if path == nil {
|
||||
return result, fmt.Errorf("can't find node enclosing position")
|
||||
}
|
||||
switch node := path[0].(type) {
|
||||
case *ast.Ident:
|
||||
result.ident = node
|
||||
case *ast.SelectorExpr:
|
||||
result.ident = node.Sel
|
||||
}
|
||||
if result.ident != nil {
|
||||
for _, n := range path[1:] {
|
||||
if field, ok := n.(*ast.Field); ok {
|
||||
result.wasEmbeddedField = len(field.Names) == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
148
vendor/golang.org/x/tools/internal/lsp/source/diagnostics.go
generated
vendored
Normal file
148
vendor/golang.org/x/tools/internal/lsp/source/diagnostics.go
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package source
|
||||
|
||||
import (
|
||||
"context"
|
||||
"go/token"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
type Diagnostic struct {
|
||||
Range Range
|
||||
Severity DiagnosticSeverity
|
||||
Message string
|
||||
}
|
||||
|
||||
type DiagnosticSeverity int
|
||||
|
||||
const (
|
||||
SeverityError DiagnosticSeverity = iota
|
||||
SeverityWarning
|
||||
SeverityHint
|
||||
SeverityInformation
|
||||
)
|
||||
|
||||
func Diagnostics(ctx context.Context, v *View, f *File) (map[string][]Diagnostic, error) {
|
||||
pkg, err := f.GetPackage()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Prepare the reports we will send for this package.
|
||||
reports := make(map[string][]Diagnostic)
|
||||
for _, filename := range pkg.GoFiles {
|
||||
reports[filename] = []Diagnostic{}
|
||||
}
|
||||
var parseErrors, typeErrors []packages.Error
|
||||
for _, err := range pkg.Errors {
|
||||
switch err.Kind {
|
||||
case packages.ParseError:
|
||||
parseErrors = append(parseErrors, err)
|
||||
case packages.TypeError:
|
||||
typeErrors = append(typeErrors, err)
|
||||
default:
|
||||
// ignore other types of errors
|
||||
continue
|
||||
}
|
||||
}
|
||||
// Don't report type errors if there are parse errors.
|
||||
diags := typeErrors
|
||||
if len(parseErrors) > 0 {
|
||||
diags = parseErrors
|
||||
}
|
||||
for _, diag := range diags {
|
||||
filename, start := v.errorPos(diag)
|
||||
// TODO(rstambler): Add support for diagnostic ranges.
|
||||
end := start
|
||||
diagnostic := Diagnostic{
|
||||
Range: Range{
|
||||
Start: start,
|
||||
End: end,
|
||||
},
|
||||
Message: diag.Msg,
|
||||
Severity: SeverityError,
|
||||
}
|
||||
if _, ok := reports[filename]; ok {
|
||||
reports[filename] = append(reports[filename], diagnostic)
|
||||
}
|
||||
}
|
||||
return reports, nil
|
||||
}
|
||||
|
||||
func (v *View) errorPos(pkgErr packages.Error) (string, token.Pos) {
|
||||
remainder1, first, hasLine := chop(pkgErr.Pos)
|
||||
remainder2, second, hasColumn := chop(remainder1)
|
||||
var pos token.Position
|
||||
if hasLine && hasColumn {
|
||||
pos.Filename = remainder2
|
||||
pos.Line = second
|
||||
pos.Column = first
|
||||
} else if hasLine {
|
||||
pos.Filename = remainder1
|
||||
pos.Line = first
|
||||
}
|
||||
f := v.GetFile(ToURI(pos.Filename))
|
||||
if f == nil {
|
||||
return "", token.NoPos
|
||||
}
|
||||
tok, err := f.GetToken()
|
||||
if err != nil {
|
||||
return "", token.NoPos
|
||||
}
|
||||
return pos.Filename, fromTokenPosition(tok, pos)
|
||||
}
|
||||
|
||||
func chop(text string) (remainder string, value int, ok bool) {
|
||||
i := strings.LastIndex(text, ":")
|
||||
if i < 0 {
|
||||
return text, 0, false
|
||||
}
|
||||
v, err := strconv.ParseInt(text[i+1:], 10, 64)
|
||||
if err != nil {
|
||||
return text, 0, false
|
||||
}
|
||||
return text[:i], int(v), true
|
||||
}
|
||||
|
||||
// fromTokenPosition converts a token.Position (1-based line and column
|
||||
// number) to a token.Pos (byte offset value).
|
||||
// It requires the token file the pos belongs to in order to do this.
|
||||
func fromTokenPosition(f *token.File, pos token.Position) token.Pos {
|
||||
line := lineStart(f, pos.Line)
|
||||
return line + token.Pos(pos.Column-1) // TODO: this is wrong, bytes not characters
|
||||
}
|
||||
|
||||
// this functionality was borrowed from the analysisutil package
|
||||
func lineStart(f *token.File, line int) token.Pos {
|
||||
// Use binary search to find the start offset of this line.
|
||||
//
|
||||
// TODO(adonovan): eventually replace this function with the
|
||||
// simpler and more efficient (*go/token.File).LineStart, added
|
||||
// in go1.12.
|
||||
|
||||
min := 0 // inclusive
|
||||
max := f.Size() // exclusive
|
||||
for {
|
||||
offset := (min + max) / 2
|
||||
pos := f.Pos(offset)
|
||||
posn := f.Position(pos)
|
||||
if posn.Line == line {
|
||||
return pos - (token.Pos(posn.Column) - 1)
|
||||
}
|
||||
|
||||
if min+1 >= max {
|
||||
return token.NoPos
|
||||
}
|
||||
|
||||
if posn.Line < line {
|
||||
min = offset
|
||||
} else {
|
||||
max = offset
|
||||
}
|
||||
}
|
||||
}
|
131
vendor/golang.org/x/tools/internal/lsp/source/file.go
generated
vendored
Normal file
131
vendor/golang.org/x/tools/internal/lsp/source/file.go
generated
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package source
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"io/ioutil"
|
||||
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
// File holds all the information we know about a file.
|
||||
type File struct {
|
||||
URI URI
|
||||
view *View
|
||||
active bool
|
||||
content []byte
|
||||
ast *ast.File
|
||||
token *token.File
|
||||
pkg *packages.Package
|
||||
}
|
||||
|
||||
// Range represents a start and end position.
|
||||
// Because Range is based purely on two token.Pos entries, it is not self
|
||||
// contained. You need access to a token.FileSet to regain the file
|
||||
// information.
|
||||
type Range struct {
|
||||
Start token.Pos
|
||||
End token.Pos
|
||||
}
|
||||
|
||||
// TextEdit represents a change to a section of a document.
|
||||
// The text within the specified range should be replaced by the supplied new text.
|
||||
type TextEdit struct {
|
||||
Range Range
|
||||
NewText string
|
||||
}
|
||||
|
||||
// SetContent sets the overlay contents for a file.
|
||||
// Setting it to nil will revert it to the on disk contents, and remove it
|
||||
// from the active set.
|
||||
func (f *File) SetContent(content []byte) {
|
||||
f.view.mu.Lock()
|
||||
defer f.view.mu.Unlock()
|
||||
f.content = content
|
||||
// the ast and token fields are invalid
|
||||
f.ast = nil
|
||||
f.token = nil
|
||||
f.pkg = nil
|
||||
// and we might need to update the overlay
|
||||
switch {
|
||||
case f.active && content == nil:
|
||||
// we were active, and want to forget the content
|
||||
f.active = false
|
||||
if filename, err := f.URI.Filename(); err == nil {
|
||||
delete(f.view.Config.Overlay, filename)
|
||||
}
|
||||
f.content = nil
|
||||
case content != nil:
|
||||
// an active overlay, update the map
|
||||
f.active = true
|
||||
if filename, err := f.URI.Filename(); err == nil {
|
||||
f.view.Config.Overlay[filename] = f.content
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read returns the contents of the file, reading it from file system if needed.
|
||||
func (f *File) Read() ([]byte, error) {
|
||||
f.view.mu.Lock()
|
||||
defer f.view.mu.Unlock()
|
||||
return f.read()
|
||||
}
|
||||
|
||||
func (f *File) GetToken() (*token.File, error) {
|
||||
f.view.mu.Lock()
|
||||
defer f.view.mu.Unlock()
|
||||
if f.token == nil {
|
||||
if err := f.view.parse(f.URI); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if f.token == nil {
|
||||
return nil, fmt.Errorf("failed to find or parse %v", f.URI)
|
||||
}
|
||||
}
|
||||
return f.token, nil
|
||||
}
|
||||
|
||||
func (f *File) GetAST() (*ast.File, error) {
|
||||
f.view.mu.Lock()
|
||||
defer f.view.mu.Unlock()
|
||||
if f.ast == nil {
|
||||
if err := f.view.parse(f.URI); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return f.ast, nil
|
||||
}
|
||||
|
||||
func (f *File) GetPackage() (*packages.Package, error) {
|
||||
f.view.mu.Lock()
|
||||
defer f.view.mu.Unlock()
|
||||
if f.pkg == nil {
|
||||
if err := f.view.parse(f.URI); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return f.pkg, nil
|
||||
}
|
||||
|
||||
// read is the internal part of Read that presumes the lock is already held
|
||||
func (f *File) read() ([]byte, error) {
|
||||
if f.content != nil {
|
||||
return f.content, nil
|
||||
}
|
||||
// we don't know the content yet, so read it
|
||||
filename, err := f.URI.Filename()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
content, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.content = content
|
||||
return f.content, nil
|
||||
}
|
59
vendor/golang.org/x/tools/internal/lsp/source/format.go
generated
vendored
Normal file
59
vendor/golang.org/x/tools/internal/lsp/source/format.go
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package source
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
)
|
||||
|
||||
// Format formats a document with a given range.
|
||||
func Format(ctx context.Context, f *File, rng Range) ([]TextEdit, error) {
|
||||
fAST, err := f.GetAST()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path, exact := astutil.PathEnclosingInterval(fAST, rng.Start, rng.End)
|
||||
if !exact || len(path) == 0 {
|
||||
return nil, fmt.Errorf("no exact AST node matching the specified range")
|
||||
}
|
||||
node := path[0]
|
||||
// format.Node can fail when the AST contains a bad expression or
|
||||
// statement. For now, we preemptively check for one.
|
||||
// TODO(rstambler): This should really return an error from format.Node.
|
||||
var isBad bool
|
||||
ast.Inspect(node, func(n ast.Node) bool {
|
||||
switch n.(type) {
|
||||
case *ast.BadDecl, *ast.BadExpr, *ast.BadStmt:
|
||||
isBad = true
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
})
|
||||
if isBad {
|
||||
return nil, fmt.Errorf("unable to format file due to a badly formatted AST")
|
||||
}
|
||||
// format.Node changes slightly from one release to another, so the version
|
||||
// of Go used to build the LSP server will determine how it formats code.
|
||||
// This should be acceptable for all users, who likely be prompted to rebuild
|
||||
// the LSP server on each Go release.
|
||||
buf := &bytes.Buffer{}
|
||||
if err := format.Node(buf, f.view.Config.Fset, node); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO(rstambler): Compute text edits instead of replacing whole file.
|
||||
return []TextEdit{
|
||||
{
|
||||
Range: rng,
|
||||
NewText: buf.String(),
|
||||
},
|
||||
}, nil
|
||||
}
|
118
vendor/golang.org/x/tools/internal/lsp/source/signature_help.go
generated
vendored
Normal file
118
vendor/golang.org/x/tools/internal/lsp/source/signature_help.go
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package source
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
)
|
||||
|
||||
type SignatureInformation struct {
|
||||
Label string
|
||||
Parameters []ParameterInformation
|
||||
ActiveParameter int
|
||||
}
|
||||
|
||||
type ParameterInformation struct {
|
||||
Label string
|
||||
}
|
||||
|
||||
func SignatureHelp(ctx context.Context, f *File, pos token.Pos) (*SignatureInformation, error) {
|
||||
fAST, err := f.GetAST()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pkg, err := f.GetPackage()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Find a call expression surrounding the query position.
|
||||
var callExpr *ast.CallExpr
|
||||
path, _ := astutil.PathEnclosingInterval(fAST, pos, pos)
|
||||
if path == nil {
|
||||
return nil, fmt.Errorf("cannot find node enclosing position")
|
||||
}
|
||||
for _, node := range path {
|
||||
if c, ok := node.(*ast.CallExpr); ok {
|
||||
callExpr = c
|
||||
break
|
||||
}
|
||||
}
|
||||
if callExpr == nil || callExpr.Fun == nil {
|
||||
return nil, fmt.Errorf("cannot find an enclosing function")
|
||||
}
|
||||
|
||||
// Get the type information for the function corresponding to the call expression.
|
||||
var obj types.Object
|
||||
switch t := callExpr.Fun.(type) {
|
||||
case *ast.Ident:
|
||||
obj = pkg.TypesInfo.ObjectOf(t)
|
||||
case *ast.SelectorExpr:
|
||||
obj = pkg.TypesInfo.ObjectOf(t.Sel)
|
||||
default:
|
||||
return nil, fmt.Errorf("the enclosing function is malformed")
|
||||
}
|
||||
if obj == nil {
|
||||
return nil, fmt.Errorf("cannot resolve %s", callExpr.Fun)
|
||||
}
|
||||
// Find the signature corresponding to the object.
|
||||
var sig *types.Signature
|
||||
switch obj.(type) {
|
||||
case *types.Var:
|
||||
if underlying, ok := obj.Type().Underlying().(*types.Signature); ok {
|
||||
sig = underlying
|
||||
}
|
||||
case *types.Func:
|
||||
sig = obj.Type().(*types.Signature)
|
||||
}
|
||||
if sig == nil {
|
||||
return nil, fmt.Errorf("no function signatures found for %s", obj.Name())
|
||||
}
|
||||
pkgStringer := qualifier(fAST, pkg.Types, pkg.TypesInfo)
|
||||
var paramInfo []ParameterInformation
|
||||
for i := 0; i < sig.Params().Len(); i++ {
|
||||
param := sig.Params().At(i)
|
||||
label := types.TypeString(param.Type(), pkgStringer)
|
||||
if param.Name() != "" {
|
||||
label = fmt.Sprintf("%s %s", param.Name(), label)
|
||||
}
|
||||
paramInfo = append(paramInfo, ParameterInformation{
|
||||
Label: label,
|
||||
})
|
||||
}
|
||||
// Determine the query position relative to the number of parameters in the function.
|
||||
var activeParam int
|
||||
var start, end token.Pos
|
||||
for i, expr := range callExpr.Args {
|
||||
if start == token.NoPos {
|
||||
start = expr.Pos()
|
||||
}
|
||||
end = expr.End()
|
||||
if i < len(callExpr.Args)-1 {
|
||||
end = callExpr.Args[i+1].Pos() - 1 // comma
|
||||
}
|
||||
if start <= pos && pos <= end {
|
||||
break
|
||||
}
|
||||
activeParam++
|
||||
start = expr.Pos() + 1 // to account for commas
|
||||
}
|
||||
// Label for function, qualified by package name.
|
||||
label := obj.Name()
|
||||
if pkg := pkgStringer(obj.Pkg()); pkg != "" {
|
||||
label = pkg + "." + label
|
||||
}
|
||||
return &SignatureInformation{
|
||||
Label: label + formatParams(sig.Params(), sig.Variadic(), pkgStringer),
|
||||
Parameters: paramInfo,
|
||||
ActiveParameter: activeParam,
|
||||
}, nil
|
||||
}
|
47
vendor/golang.org/x/tools/internal/lsp/source/uri.go
generated
vendored
Normal file
47
vendor/golang.org/x/tools/internal/lsp/source/uri.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package source
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const fileSchemePrefix = "file://"
|
||||
|
||||
// URI represents the full uri for a file.
|
||||
type URI string
|
||||
|
||||
// Filename gets the file path for the URI.
|
||||
// It will return an error if the uri is not valid, or if the URI was not
|
||||
// a file URI
|
||||
func (uri URI) Filename() (string, error) {
|
||||
s := string(uri)
|
||||
if !strings.HasPrefix(s, fileSchemePrefix) {
|
||||
return "", fmt.Errorf("only file URI's are supported, got %v", uri)
|
||||
}
|
||||
s = s[len(fileSchemePrefix):]
|
||||
s, err := url.PathUnescape(s)
|
||||
if err != nil {
|
||||
return s, err
|
||||
}
|
||||
s = filepath.FromSlash(s)
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// ToURI returns a protocol URI for the supplied path.
|
||||
// It will always have the file scheme.
|
||||
func ToURI(path string) URI {
|
||||
const prefix = "$GOROOT"
|
||||
if strings.EqualFold(prefix, path[:len(prefix)]) {
|
||||
suffix := path[len(prefix):]
|
||||
//TODO: we need a better way to get the GOROOT that uses the packages api
|
||||
path = runtime.GOROOT() + suffix
|
||||
}
|
||||
return URI(fileSchemePrefix + filepath.ToSlash(path))
|
||||
}
|
82
vendor/golang.org/x/tools/internal/lsp/source/view.go
generated
vendored
Normal file
82
vendor/golang.org/x/tools/internal/lsp/source/view.go
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package source
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
type View struct {
|
||||
mu sync.Mutex // protects all mutable state of the view
|
||||
|
||||
Config *packages.Config
|
||||
|
||||
files map[URI]*File
|
||||
}
|
||||
|
||||
func NewView() *View {
|
||||
return &View{
|
||||
Config: &packages.Config{
|
||||
Mode: packages.LoadSyntax,
|
||||
Fset: token.NewFileSet(),
|
||||
Tests: true,
|
||||
Overlay: make(map[string][]byte),
|
||||
},
|
||||
files: make(map[URI]*File),
|
||||
}
|
||||
}
|
||||
|
||||
// GetFile returns a File for the given uri.
|
||||
// It will always succeed, adding the file to the managed set if needed.
|
||||
func (v *View) GetFile(uri URI) *File {
|
||||
v.mu.Lock()
|
||||
f := v.getFile(uri)
|
||||
v.mu.Unlock()
|
||||
return f
|
||||
}
|
||||
|
||||
// getFile is the unlocked internal implementation of GetFile.
|
||||
func (v *View) getFile(uri URI) *File {
|
||||
f, found := v.files[uri]
|
||||
if !found {
|
||||
f = &File{
|
||||
URI: uri,
|
||||
view: v,
|
||||
}
|
||||
v.files[f.URI] = f
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func (v *View) parse(uri URI) error {
|
||||
path, err := uri.Filename()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pkgs, err := packages.Load(v.Config, fmt.Sprintf("file=%s", path))
|
||||
if len(pkgs) == 0 {
|
||||
if err == nil {
|
||||
err = fmt.Errorf("no packages found for %s", path)
|
||||
}
|
||||
return err
|
||||
}
|
||||
for _, pkg := range pkgs {
|
||||
// add everything we find to the files cache
|
||||
for _, fAST := range pkg.Syntax {
|
||||
// if a file was in multiple packages, which token/ast/pkg do we store
|
||||
fToken := v.Config.Fset.File(fAST.Pos())
|
||||
fURI := ToURI(fToken.Name())
|
||||
f := v.getFile(fURI)
|
||||
f.token = fToken
|
||||
f.ast = fAST
|
||||
f.pkg = pkg
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user