internal/lsp: support builtin types without hardcoding
This change uses an *ast.Package built from the file go/src/builtin/builtin.go. Completion (and ultimately other features) will be resolved using this AST instead of being hardcoded. Change-Id: I3e34030b3236994faa484cf17cf75da511165133 Reviewed-on: https://go-review.googlesource.com/c/tools/+/174381 Run-TryBot: Rebecca Stambler <rstambler@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Cottrell <iancottrell@google.com>
This commit is contained in:
parent
2d28432af7
commit
2346320968
|
@ -6,6 +6,8 @@ package cache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
"os"
|
"os"
|
||||||
|
@ -63,6 +65,9 @@ type View struct {
|
||||||
|
|
||||||
// pcache caches type information for the packages of the opened files in a view.
|
// pcache caches type information for the packages of the opened files in a view.
|
||||||
pcache *packageCache
|
pcache *packageCache
|
||||||
|
|
||||||
|
// builtinPkg is the AST package used to resolve builtin types.
|
||||||
|
builtinPkg *ast.Package
|
||||||
}
|
}
|
||||||
|
|
||||||
type metadataCache struct {
|
type metadataCache struct {
|
||||||
|
@ -93,6 +98,7 @@ func NewView(ctx context.Context, log xlog.Logger, name string, folder span.URI,
|
||||||
v := &View{
|
v := &View{
|
||||||
baseCtx: ctx,
|
baseCtx: ctx,
|
||||||
backgroundCtx: backgroundCtx,
|
backgroundCtx: backgroundCtx,
|
||||||
|
builtinPkg: builtinPkg(*config),
|
||||||
cancel: cancel,
|
cancel: cancel,
|
||||||
log: log,
|
log: log,
|
||||||
Config: *config,
|
Config: *config,
|
||||||
|
@ -118,6 +124,32 @@ func (v *View) BackgroundContext() context.Context {
|
||||||
return v.backgroundCtx
|
return v.backgroundCtx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *View) BuiltinPackage() *ast.Package {
|
||||||
|
return v.builtinPkg
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinPkg(cfg packages.Config) *ast.Package {
|
||||||
|
var bpkg *ast.Package
|
||||||
|
cfg.Mode = packages.LoadFiles
|
||||||
|
pkgs, _ := packages.Load(&cfg, "builtin")
|
||||||
|
if len(pkgs) != 1 {
|
||||||
|
bpkg, _ = ast.NewPackage(cfg.Fset, nil, nil, nil)
|
||||||
|
return bpkg
|
||||||
|
}
|
||||||
|
pkg := pkgs[0]
|
||||||
|
files := make(map[string]*ast.File)
|
||||||
|
for _, filename := range pkg.GoFiles {
|
||||||
|
file, err := parser.ParseFile(cfg.Fset, filename, nil, parser.ParseComments)
|
||||||
|
if err != nil {
|
||||||
|
bpkg, _ = ast.NewPackage(cfg.Fset, nil, nil, nil)
|
||||||
|
return bpkg
|
||||||
|
}
|
||||||
|
files[filename] = file
|
||||||
|
}
|
||||||
|
bpkg, _ = ast.NewPackage(cfg.Fset, files, nil, nil)
|
||||||
|
return bpkg
|
||||||
|
}
|
||||||
|
|
||||||
func (v *View) FileSet() *token.FileSet {
|
func (v *View) FileSet() *token.FileSet {
|
||||||
return v.Config.Fset
|
return v.Config.Fset
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,24 +14,17 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Server) cacheAndDiagnose(ctx context.Context, uri span.URI, content string) error {
|
func (s *Server) cacheAndDiagnose(ctx context.Context, uri span.URI, content string) error {
|
||||||
s.log.Debugf(ctx, "cacheAndDiagnose: %s", uri)
|
|
||||||
|
|
||||||
view := s.findView(ctx, uri)
|
view := s.findView(ctx, uri)
|
||||||
if err := view.SetContent(ctx, uri, []byte(content)); err != nil {
|
if err := view.SetContent(ctx, uri, []byte(content)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.log.Debugf(ctx, "cacheAndDiagnose: set content for %s", uri)
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
ctx := view.BackgroundContext()
|
ctx := view.BackgroundContext()
|
||||||
if ctx.Err() != nil {
|
if ctx.Err() != nil {
|
||||||
s.log.Errorf(ctx, "canceling diagnostics for %s: %v", uri, ctx.Err())
|
s.log.Errorf(ctx, "canceling diagnostics for %s: %v", uri, ctx.Err())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.log.Debugf(ctx, "cacheAndDiagnose: going to get diagnostics for %s", uri)
|
|
||||||
|
|
||||||
reports, err := source.Diagnostics(ctx, view, uri)
|
reports, err := source.Diagnostics(ctx, view, uri)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf(ctx, "failed to compute diagnostics for %s: %v", uri, err)
|
s.log.Errorf(ctx, "failed to compute diagnostics for %s: %v", uri, err)
|
||||||
|
@ -41,8 +34,6 @@ func (s *Server) cacheAndDiagnose(ctx context.Context, uri span.URI, content str
|
||||||
s.undeliveredMu.Lock()
|
s.undeliveredMu.Lock()
|
||||||
defer s.undeliveredMu.Unlock()
|
defer s.undeliveredMu.Unlock()
|
||||||
|
|
||||||
s.log.Debugf(ctx, "cacheAndDiagnose: publishing diagnostics")
|
|
||||||
|
|
||||||
for uri, diagnostics := range reports {
|
for uri, diagnostics := range reports {
|
||||||
if err := s.publishDiagnostics(ctx, view, uri, diagnostics); err != nil {
|
if err := s.publishDiagnostics(ctx, view, uri, diagnostics); err != nil {
|
||||||
if s.undelivered == nil {
|
if s.undelivered == nil {
|
||||||
|
@ -54,9 +45,6 @@ func (s *Server) cacheAndDiagnose(ctx context.Context, uri span.URI, content str
|
||||||
// In case we had old, undelivered diagnostics.
|
// In case we had old, undelivered diagnostics.
|
||||||
delete(s.undelivered, uri)
|
delete(s.undelivered, uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.log.Debugf(ctx, "cacheAndDiagnose: publishing undelivered diagnostics")
|
|
||||||
|
|
||||||
// Anytime we compute diagnostics, make sure to also send along any
|
// Anytime we compute diagnostics, make sure to also send along any
|
||||||
// undelivered ones (only for remaining URIs).
|
// undelivered ones (only for remaining URIs).
|
||||||
for uri, diagnostics := range s.undelivered {
|
for uri, diagnostics := range s.undelivered {
|
||||||
|
@ -66,8 +54,6 @@ func (s *Server) cacheAndDiagnose(ctx context.Context, uri span.URI, content str
|
||||||
delete(s.undelivered, uri)
|
delete(s.undelivered, uri)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
s.log.Debugf(ctx, "cacheAndDiagnose: returned from diagnostics for %s", uri)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,12 @@ type completer struct {
|
||||||
types *types.Package
|
types *types.Package
|
||||||
info *types.Info
|
info *types.Info
|
||||||
qf types.Qualifier
|
qf types.Qualifier
|
||||||
fset *token.FileSet
|
|
||||||
|
// view is the View associated with this completion request.
|
||||||
|
view View
|
||||||
|
|
||||||
|
// ctx is the context associated with this completion request.
|
||||||
|
ctx context.Context
|
||||||
|
|
||||||
// pos is the position at which the request was triggered.
|
// pos is the position at which the request was triggered.
|
||||||
pos token.Pos
|
pos token.Pos
|
||||||
|
@ -186,7 +191,8 @@ func Completion(ctx context.Context, f File, pos token.Pos) ([]CompletionItem, s
|
||||||
types: pkg.GetTypes(),
|
types: pkg.GetTypes(),
|
||||||
info: pkg.GetTypesInfo(),
|
info: pkg.GetTypesInfo(),
|
||||||
qf: qualifier(file, pkg.GetTypes(), pkg.GetTypesInfo()),
|
qf: qualifier(file, pkg.GetTypes(), pkg.GetTypesInfo()),
|
||||||
fset: f.GetFileSet(ctx),
|
view: f.View(),
|
||||||
|
ctx: ctx,
|
||||||
path: path,
|
path: path,
|
||||||
pos: pos,
|
pos: pos,
|
||||||
seen: make(map[types.Object]bool),
|
seen: make(map[types.Object]bool),
|
||||||
|
|
|
@ -5,8 +5,10 @@
|
||||||
package source
|
package source
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
|
"go/printer"
|
||||||
"go/types"
|
"go/types"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -15,6 +17,11 @@ import (
|
||||||
|
|
||||||
// formatCompletion creates a completion item for a given types.Object.
|
// formatCompletion creates a completion item for a given types.Object.
|
||||||
func (c *completer) item(obj types.Object, score float64) CompletionItem {
|
func (c *completer) item(obj types.Object, score float64) CompletionItem {
|
||||||
|
// Handle builtin types separately.
|
||||||
|
if obj.Parent() == types.Universe {
|
||||||
|
return c.formatBuiltin(obj, score)
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
label = obj.Name()
|
label = obj.Name()
|
||||||
detail = types.TypeString(obj.Type(), c.qf)
|
detail = types.TypeString(obj.Type(), c.qf)
|
||||||
|
@ -27,9 +34,6 @@ func (c *completer) item(obj types.Object, score float64) CompletionItem {
|
||||||
switch o := obj.(type) {
|
switch o := obj.(type) {
|
||||||
case *types.TypeName:
|
case *types.TypeName:
|
||||||
detail, kind = formatType(o.Type(), c.qf)
|
detail, kind = formatType(o.Type(), c.qf)
|
||||||
if obj.Parent() == types.Universe {
|
|
||||||
detail = ""
|
|
||||||
}
|
|
||||||
case *types.Const:
|
case *types.Const:
|
||||||
if obj.Parent() == types.Universe {
|
if obj.Parent() == types.Universe {
|
||||||
detail = ""
|
detail = ""
|
||||||
|
@ -57,27 +61,17 @@ func (c *completer) item(obj types.Object, score float64) CompletionItem {
|
||||||
if !ok {
|
if !ok {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
params := formatEachParam(sig, c.qf)
|
params := formatParams(sig.Params(), sig.Variadic(), c.qf)
|
||||||
label += formatParamParts(params)
|
results, writeParens := formatResults(sig.Results(), c.qf)
|
||||||
detail = strings.Trim(types.TypeString(sig.Results(), c.qf), "()")
|
label, detail = formatFunction(obj.Name(), params, results, writeParens)
|
||||||
|
plainSnippet, placeholderSnippet = c.functionCallSnippets(obj.Name(), params)
|
||||||
kind = FunctionCompletionItem
|
kind = FunctionCompletionItem
|
||||||
if sig.Recv() != nil {
|
if sig.Recv() != nil {
|
||||||
kind = MethodCompletionItem
|
kind = MethodCompletionItem
|
||||||
}
|
}
|
||||||
plainSnippet, placeholderSnippet = c.functionCallSnippets(obj.Name(), params)
|
|
||||||
case *types.Builtin:
|
|
||||||
item, ok := builtinDetails[obj.Name()]
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
label, detail = item.label, item.detail
|
|
||||||
kind = FunctionCompletionItem
|
|
||||||
case *types.PkgName:
|
case *types.PkgName:
|
||||||
kind = PackageCompletionItem
|
kind = PackageCompletionItem
|
||||||
detail = fmt.Sprintf("\"%s\"", o.Imported().Path())
|
detail = fmt.Sprintf("\"%s\"", o.Imported().Path())
|
||||||
case *types.Nil:
|
|
||||||
kind = VariableCompletionItem
|
|
||||||
detail = ""
|
|
||||||
}
|
}
|
||||||
detail = strings.TrimPrefix(detail, "untyped ")
|
detail = strings.TrimPrefix(detail, "untyped ")
|
||||||
|
|
||||||
|
@ -106,71 +100,77 @@ func (c *completer) isParameter(v *types.Var) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// formatType returns the detail and kind for an object of type *types.TypeName.
|
func (c *completer) formatBuiltin(obj types.Object, score float64) CompletionItem {
|
||||||
func formatType(typ types.Type, qf types.Qualifier) (detail string, kind CompletionItemKind) {
|
item := CompletionItem{
|
||||||
if types.IsInterface(typ) {
|
Label: obj.Name(),
|
||||||
detail = "interface{...}"
|
InsertText: obj.Name(),
|
||||||
kind = InterfaceCompletionItem
|
Score: score,
|
||||||
} else if _, ok := typ.(*types.Struct); ok {
|
}
|
||||||
detail = "struct{...}"
|
switch obj.(type) {
|
||||||
kind = StructCompletionItem
|
case *types.Const:
|
||||||
} else if typ != typ.Underlying() {
|
item.Kind = ConstantCompletionItem
|
||||||
detail, kind = formatType(typ.Underlying(), qf)
|
case *types.Builtin:
|
||||||
|
fn := c.view.BuiltinPackage().Scope.Lookup(obj.Name())
|
||||||
|
decl, ok := fn.Decl.(*ast.FuncDecl)
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
params, _ := c.formatFieldList(decl.Type.Params)
|
||||||
|
results, writeResultParens := c.formatFieldList(decl.Type.Results)
|
||||||
|
item.Label, item.Detail = formatFunction(obj.Name(), params, results, writeResultParens)
|
||||||
|
item.Snippet, item.PlaceholderSnippet = c.functionCallSnippets(obj.Name(), params)
|
||||||
|
item.Kind = FunctionCompletionItem
|
||||||
|
case *types.TypeName:
|
||||||
|
if types.IsInterface(obj.Type()) {
|
||||||
|
item.Kind = InterfaceCompletionItem
|
||||||
} else {
|
} else {
|
||||||
detail = types.TypeString(typ, qf)
|
item.Kind = TypeCompletionItem
|
||||||
kind = TypeCompletionItem
|
|
||||||
}
|
}
|
||||||
return detail, kind
|
case *types.Nil:
|
||||||
|
item.Kind = VariableCompletionItem
|
||||||
|
}
|
||||||
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
// formatParams correctly formats the parameters of a function.
|
var replacer = strings.NewReplacer(
|
||||||
func formatParams(sig *types.Signature, qualifier types.Qualifier) string {
|
`ComplexType`, `complex128`,
|
||||||
return formatParamParts(formatEachParam(sig, qualifier))
|
`FloatType`, `float64`,
|
||||||
}
|
`IntegerType`, `int`,
|
||||||
|
)
|
||||||
|
|
||||||
func formatParamParts(params []string) string {
|
func (c *completer) formatFieldList(list *ast.FieldList) ([]string, bool) {
|
||||||
totalLen := 2 // parens
|
if list == nil {
|
||||||
|
return nil, false
|
||||||
// length of each param itself
|
|
||||||
for _, p := range params {
|
|
||||||
totalLen += len(p)
|
|
||||||
}
|
}
|
||||||
// length of ", " separator
|
var writeResultParens bool
|
||||||
if len(params) > 1 {
|
var result []string
|
||||||
totalLen += 2 * (len(params) - 1)
|
for i := 0; i < len(list.List); i++ {
|
||||||
|
if i >= 1 {
|
||||||
|
writeResultParens = true
|
||||||
}
|
}
|
||||||
|
p := list.List[i]
|
||||||
var b strings.Builder
|
cfg := printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 4}
|
||||||
b.Grow(totalLen)
|
b := &bytes.Buffer{}
|
||||||
|
if err := cfg.Fprint(b, c.view.FileSet(), p.Type); err != nil {
|
||||||
b.WriteByte('(')
|
c.view.Logger().Errorf(c.ctx, "unable to print type %v", p.Type)
|
||||||
for i, p := range params {
|
continue
|
||||||
if i > 0 {
|
|
||||||
b.WriteString(", ")
|
|
||||||
}
|
}
|
||||||
b.WriteString(p)
|
typ := replacer.Replace(b.String())
|
||||||
|
if len(p.Names) == 0 {
|
||||||
|
result = append(result, fmt.Sprintf("%s", typ))
|
||||||
}
|
}
|
||||||
b.WriteByte(')')
|
for _, name := range p.Names {
|
||||||
|
if name.Name != "" {
|
||||||
return b.String()
|
if i == 0 {
|
||||||
|
writeResultParens = true
|
||||||
}
|
}
|
||||||
|
result = append(result, fmt.Sprintf("%s %s", name.Name, typ))
|
||||||
func formatEachParam(sig *types.Signature, qualifier types.Qualifier) []string {
|
|
||||||
params := make([]string, 0, sig.Params().Len())
|
|
||||||
for i := 0; i < sig.Params().Len(); i++ {
|
|
||||||
el := sig.Params().At(i)
|
|
||||||
typ := types.TypeString(el.Type(), qualifier)
|
|
||||||
// Handle a variadic parameter (can only be the final parameter).
|
|
||||||
if sig.Variadic() && i == sig.Params().Len()-1 {
|
|
||||||
typ = strings.Replace(typ, "[]", "...", 1)
|
|
||||||
}
|
|
||||||
if el.Name() == "" {
|
|
||||||
params = append(params, typ)
|
|
||||||
} else {
|
} else {
|
||||||
params = append(params, el.Name()+" "+typ)
|
result = append(result, fmt.Sprintf("%s", typ))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return params
|
}
|
||||||
|
return result, writeResultParens
|
||||||
}
|
}
|
||||||
|
|
||||||
// qualifier returns a function that appropriately formats a types.PkgName
|
// qualifier returns a function that appropriately formats a types.PkgName
|
||||||
|
@ -200,65 +200,3 @@ func qualifier(f *ast.File, pkg *types.Package, info *types.Info) types.Qualifie
|
||||||
return p.Name()
|
return p.Name()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 float64, imag float64)",
|
|
||||||
detail: "complex128",
|
|
||||||
},
|
|
||||||
"copy": { // copy(dst, src []T) int
|
|
||||||
label: "copy(dst []T, 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{}",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ func (c *completer) structFieldSnippets(label, detail string) (*snippet.Builder,
|
||||||
|
|
||||||
// If the cursor position is on a different line from the literal's opening brace,
|
// If the cursor position is on a different line from the literal's opening brace,
|
||||||
// we are in a multiline literal.
|
// we are in a multiline literal.
|
||||||
if c.fset.Position(c.pos).Line != c.fset.Position(lit.Lbrace).Line {
|
if c.view.FileSet().Position(c.pos).Line != c.view.FileSet().Position(lit.Lbrace).Line {
|
||||||
plain.WriteText(",")
|
plain.WriteText(",")
|
||||||
placeholder.WriteText(",")
|
placeholder.WriteText(",")
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,23 +105,18 @@ func SignatureHelp(ctx context.Context, f File, pos token.Pos) (*SignatureInform
|
||||||
obj = pkg.GetTypesInfo().ObjectOf(t.Sel)
|
obj = pkg.GetTypesInfo().ObjectOf(t.Sel)
|
||||||
}
|
}
|
||||||
|
|
||||||
var label string
|
var name string
|
||||||
if obj != nil {
|
if obj != nil {
|
||||||
label = obj.Name()
|
name = obj.Name()
|
||||||
} else {
|
} else {
|
||||||
label = "func"
|
name = "func"
|
||||||
}
|
}
|
||||||
|
|
||||||
label += formatParams(sig, qf)
|
results, writeResultParens := formatResults(sig.Results(), qf)
|
||||||
|
label, detail := formatFunction(name, formatParams(sig.Params(), sig.Variadic(), qf), results, writeResultParens)
|
||||||
if sig.Results().Len() > 0 {
|
if sig.Results().Len() > 0 {
|
||||||
results := types.TypeString(sig.Results(), qf)
|
label += " " + detail
|
||||||
if sig.Results().Len() == 1 && sig.Results().At(0).Name() == "" {
|
|
||||||
// Trim off leading/trailing parens to avoid results like "foo(a int) (int)".
|
|
||||||
results = strings.Trim(results, "()")
|
|
||||||
}
|
}
|
||||||
label += " " + results
|
|
||||||
}
|
|
||||||
|
|
||||||
return &SignatureInformation{
|
return &SignatureInformation{
|
||||||
Label: label,
|
Label: label,
|
||||||
Parameters: paramInfo,
|
Parameters: paramInfo,
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// indexExprAtPos returns the index of the expression containing pos.
|
// indexExprAtPos returns the index of the expression containing pos.
|
||||||
|
@ -109,3 +110,90 @@ func deref(typ types.Type) types.Type {
|
||||||
}
|
}
|
||||||
return typ
|
return typ
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func formatParams(tup *types.Tuple, variadic bool, qf types.Qualifier) []string {
|
||||||
|
params := make([]string, 0, tup.Len())
|
||||||
|
for i := 0; i < tup.Len(); i++ {
|
||||||
|
el := tup.At(i)
|
||||||
|
typ := types.TypeString(el.Type(), qf)
|
||||||
|
|
||||||
|
// Handle a variadic parameter (can only be the final parameter).
|
||||||
|
if variadic && i == tup.Len()-1 {
|
||||||
|
typ = strings.Replace(typ, "[]", "...", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if el.Name() == "" {
|
||||||
|
params = append(params, typ)
|
||||||
|
} else {
|
||||||
|
params = append(params, el.Name()+" "+typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatResults(tup *types.Tuple, qf types.Qualifier) ([]string, bool) {
|
||||||
|
var writeResultParens bool
|
||||||
|
results := make([]string, 0, tup.Len())
|
||||||
|
for i := 0; i < tup.Len(); i++ {
|
||||||
|
if i >= 1 {
|
||||||
|
writeResultParens = true
|
||||||
|
}
|
||||||
|
el := tup.At(i)
|
||||||
|
typ := types.TypeString(el.Type(), qf)
|
||||||
|
|
||||||
|
if el.Name() == "" {
|
||||||
|
results = append(results, typ)
|
||||||
|
} else {
|
||||||
|
if i == 0 {
|
||||||
|
writeResultParens = true
|
||||||
|
}
|
||||||
|
results = append(results, el.Name()+" "+typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results, writeResultParens
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatType returns the detail and kind for an object of type *types.TypeName.
|
||||||
|
func formatType(typ types.Type, qf 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(), qf)
|
||||||
|
} else {
|
||||||
|
detail = types.TypeString(typ, qf)
|
||||||
|
kind = TypeCompletionItem
|
||||||
|
}
|
||||||
|
return detail, kind
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatFunction(name string, params []string, results []string, writeResultParens bool) (string, string) {
|
||||||
|
var label, detail strings.Builder
|
||||||
|
label.WriteString(name)
|
||||||
|
label.WriteByte('(')
|
||||||
|
for i, p := range params {
|
||||||
|
if i > 0 {
|
||||||
|
label.WriteString(", ")
|
||||||
|
}
|
||||||
|
label.WriteString(p)
|
||||||
|
}
|
||||||
|
label.WriteByte(')')
|
||||||
|
|
||||||
|
if writeResultParens {
|
||||||
|
detail.WriteByte('(')
|
||||||
|
}
|
||||||
|
for i, p := range results {
|
||||||
|
if i > 0 {
|
||||||
|
detail.WriteString(", ")
|
||||||
|
}
|
||||||
|
detail.WriteString(p)
|
||||||
|
}
|
||||||
|
if writeResultParens {
|
||||||
|
detail.WriteByte(')')
|
||||||
|
}
|
||||||
|
|
||||||
|
return label.String(), detail.String()
|
||||||
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
type View interface {
|
type View interface {
|
||||||
Logger() xlog.Logger
|
Logger() xlog.Logger
|
||||||
FileSet() *token.FileSet
|
FileSet() *token.FileSet
|
||||||
|
BuiltinPackage() *ast.Package
|
||||||
GetFile(ctx context.Context, uri span.URI) (File, error)
|
GetFile(ctx context.Context, uri span.URI) (File, error)
|
||||||
SetContent(ctx context.Context, uri span.URI, content []byte) error
|
SetContent(ctx context.Context, uri span.URI, content []byte) error
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,35 +5,35 @@ func _() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create markers for builtin types. Only for use by this test.
|
/* Create markers for builtin types. Only for use by this test.
|
||||||
/* append(slice []T, elems ...T) []T */ //@item(append, "append(slice []T, elems ...T)", "[]T", "func")
|
/* append(slice []Type, elems ...Type) []Type */ //@item(append, "append(slice []Type, elems ...Type)", "[]Type", "func")
|
||||||
/* bool */ //@item(bool, "bool", "", "type")
|
/* bool */ //@item(bool, "bool", "", "type")
|
||||||
/* byte */ //@item(byte, "byte", "", "type")
|
/* byte */ //@item(byte, "byte", "", "type")
|
||||||
/* cap(v []T) int */ //@item(cap, "cap(v []T)", "int", "func")
|
/* cap(v Type) int */ //@item(cap, "cap(v Type)", "int", "func")
|
||||||
/* close(c chan<- T) */ //@item(close, "close(c chan<- T)", "", "func")
|
/* close(c chan<- Type) */ //@item(close, "close(c chan<- Type)", "", "func")
|
||||||
/* complex(real float64, imag float64) */ //@item(complex, "complex(real float64, imag float64)", "complex128", "func")
|
/* complex(r float64, i float64) */ //@item(complex, "complex(r float64, i float64)", "complex128", "func")
|
||||||
/* complex128 */ //@item(complex128, "complex128", "", "type")
|
/* complex128 */ //@item(complex128, "complex128", "", "type")
|
||||||
/* complex64 */ //@item(complex64, "complex64", "", "type")
|
/* complex64 */ //@item(complex64, "complex64", "", "type")
|
||||||
/* copy(dst []T, src []T) int */ //@item(copy, "copy(dst []T, src []T)", "int", "func")
|
/* copy(dst []Type, src []Type) int */ //@item(copy, "copy(dst []Type, src []Type)", "int", "func")
|
||||||
/* delete(m map[K]V, key K) */ //@item(delete, "delete(m map[K]V, key K)", "", "func")
|
/* delete(m map[Type]Type1, key Type) */ //@item(delete, "delete(m map[Type]Type1, key Type)", "", "func")
|
||||||
/* error */ //@item(error, "error", "", "interface")
|
/* error */ //@item(error, "error", "", "interface")
|
||||||
/* false */ //@item(_false, "false", "", "const")
|
/* false */ //@item(_false, "false", "", "const")
|
||||||
/* float32 */ //@item(float32, "float32", "", "type")
|
/* float32 */ //@item(float32, "float32", "", "type")
|
||||||
/* float64 */ //@item(float64, "float64", "", "type")
|
/* float64 */ //@item(float64, "float64", "", "type")
|
||||||
/* imag(complex128) float64 */ //@item(imag, "imag(complex128)", "float64", "func")
|
/* imag(c complex128) float64 */ //@item(imag, "imag(c complex128)", "float64", "func")
|
||||||
/* int */ //@item(int, "int", "", "type")
|
/* int */ //@item(int, "int", "", "type")
|
||||||
/* int16 */ //@item(int16, "int16", "", "type")
|
/* int16 */ //@item(int16, "int16", "", "type")
|
||||||
/* int32 */ //@item(int32, "int32", "", "type")
|
/* int32 */ //@item(int32, "int32", "", "type")
|
||||||
/* int64 */ //@item(int64, "int64", "", "type")
|
/* int64 */ //@item(int64, "int64", "", "type")
|
||||||
/* int8 */ //@item(int8, "int8", "", "type")
|
/* int8 */ //@item(int8, "int8", "", "type")
|
||||||
/* iota */ //@item(iota, "iota", "", "const")
|
/* iota */ //@item(iota, "iota", "", "const")
|
||||||
/* len(T) int */ //@item(len, "len(T)", "int", "func")
|
/* len(v Type) int */ //@item(len, "len(v Type)", "int", "func")
|
||||||
/* make(t T, size ...int) T */ //@item(make, "make(t T, size ...int)", "T", "func")
|
/* make(t Type, size ...int) Type */ //@item(make, "make(t Type, size ...int)", "Type", "func")
|
||||||
/* new(T) *T */ //@item(new, "new(T)", "*T", "func")
|
/* new(Type) *Type */ //@item(new, "new(Type)", "*Type", "func")
|
||||||
/* nil */ //@item(_nil, "nil", "", "var")
|
/* nil */ //@item(_nil, "nil", "", "var")
|
||||||
/* panic(interface{}) */ //@item(panic, "panic(interface{})", "", "func")
|
/* panic(v interface{}) */ //@item(panic, "panic(v interface{})", "", "func")
|
||||||
/* print(args ...T) */ //@item(print, "print(args ...T)", "", "func")
|
/* print(args ...Type) */ //@item(print, "print(args ...Type)", "", "func")
|
||||||
/* println(args ...T) */ //@item(println, "println(args ...T)", "", "func")
|
/* println(args ...Type) */ //@item(println, "println(args ...Type)", "", "func")
|
||||||
/* real(complex128) float64 */ //@item(real, "real(complex128)", "float64", "func")
|
/* real(c complex128) float64 */ //@item(real, "real(c complex128)", "float64", "func")
|
||||||
/* recover() interface{} */ //@item(recover, "recover()", "interface{}", "func")
|
/* recover() interface{} */ //@item(recover, "recover()", "interface{}", "func")
|
||||||
/* rune */ //@item(rune, "rune", "", "type")
|
/* rune */ //@item(rune, "rune", "", "type")
|
||||||
/* string */ //@item(string, "string", "", "type")
|
/* string */ //@item(string, "string", "", "type")
|
||||||
|
|
Loading…
Reference in New Issue