diff options
Diffstat (limited to 'internal/lsp/source/completion/format.go')
-rw-r--r-- | internal/lsp/source/completion/format.go | 340 |
1 files changed, 0 insertions, 340 deletions
diff --git a/internal/lsp/source/completion/format.go b/internal/lsp/source/completion/format.go deleted file mode 100644 index e67456911..000000000 --- a/internal/lsp/source/completion/format.go +++ /dev/null @@ -1,340 +0,0 @@ -// Copyright 2019 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 ( - "context" - "fmt" - "go/ast" - "go/doc" - "go/types" - "strings" - - "golang.org/x/tools/internal/event" - "golang.org/x/tools/internal/imports" - "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" - "golang.org/x/tools/internal/typeparams" - errors "golang.org/x/xerrors" -) - -var ( - errNoMatch = errors.New("not a surrounding match") - errLowScore = errors.New("not a high scoring candidate") -) - -// item formats a candidate to a CompletionItem. -func (c *completer) item(ctx context.Context, cand candidate) (CompletionItem, error) { - obj := cand.obj - - // if the object isn't a valid match against the surrounding, return early. - matchScore := c.matcher.Score(cand.name) - if matchScore <= 0 { - return CompletionItem{}, errNoMatch - } - cand.score *= float64(matchScore) - - // Ignore deep candidates that wont be in the MaxDeepCompletions anyway. - if len(cand.path) != 0 && !c.deepState.isHighScore(cand.score) { - return CompletionItem{}, errLowScore - } - - // Handle builtin types separately. - if obj.Parent() == types.Universe { - return c.formatBuiltin(ctx, cand) - } - - var ( - label = cand.name - detail = types.TypeString(obj.Type(), c.qf) - insert = label - kind = protocol.TextCompletion - snip snippet.Builder - protocolEdits []protocol.TextEdit - ) - if obj.Type() == nil { - detail = "" - } - if isTypeName(obj) && c.wantTypeParams() { - x := cand.obj.(*types.TypeName) - if named, ok := x.Type().(*types.Named); ok { - tp := typeparams.ForNamed(named) - label += source.FormatTypeParams(tp) - insert = label // maintain invariant above (label == insert) - } - } - - snip.WriteText(insert) - - switch obj := obj.(type) { - case *types.TypeName: - 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 = source.FormatVarType(ctx, c.snapshot, c.pkg, obj, c.qf) - } - if obj.IsField() { - kind = protocol.FieldCompletion - c.structFieldSnippet(cand, detail, &snip) - } else { - kind = protocol.VariableCompletion - } - if obj.Type() == nil { - break - } - case *types.Func: - sig, ok := obj.Type().Underlying().(*types.Signature) - if !ok { - break - } - kind = protocol.FunctionCompletion - if sig != nil && sig.Recv() != nil { - kind = protocol.MethodCompletion - } - case *types.PkgName: - kind = protocol.ModuleCompletion - detail = fmt.Sprintf("%q", obj.Imported().Path()) - case *types.Label: - kind = protocol.ConstantCompletion - detail = "label" - } - - var prefix string - for _, mod := range cand.mods { - switch mod { - case reference: - prefix = "&" + prefix - case dereference: - prefix = "*" + prefix - case chanRead: - prefix = "<-" + prefix - } - } - - var ( - suffix string - funcType = obj.Type() - ) -Suffixes: - for _, mod := range cand.mods { - switch mod { - case invoke: - if sig, ok := funcType.Underlying().(*types.Signature); ok { - s := source.NewSignature(ctx, c.snapshot, c.pkg, sig, nil, c.qf) - c.functionCallSnippet("", s.TypeParams(), s.Params(), &snip) - if sig.Results().Len() == 1 { - funcType = sig.Results().At(0).Type() - } - detail = "func" + s.Format() - } - - if !c.opts.snippets { - // Without snippets the candidate will not include "()". Don't - // add further suffixes since they will be invalid. For - // example, with snippets "foo()..." would become "foo..." - // without snippets if we added the dotDotDot. - break Suffixes - } - case takeSlice: - suffix += "[:]" - case takeDotDotDot: - suffix += "..." - case index: - snip.WriteText("[") - snip.WritePlaceholder(nil) - snip.WriteText("]") - } - } - - // If this candidate needs an additional import statement, - // add the additional text edits needed. - if cand.imp != nil { - addlEdits, err := c.importEdits(cand.imp) - - if err != nil { - return CompletionItem{}, err - } - - protocolEdits = append(protocolEdits, addlEdits...) - if kind != protocol.ModuleCompletion { - if detail != "" { - detail += " " - } - detail += fmt.Sprintf("(from %q)", cand.imp.importPath) - } - } - - if cand.convertTo != nil { - typeName := types.TypeString(cand.convertTo, c.qf) - - switch cand.convertTo.(type) { - // We need extra parens when casting to these types. For example, - // we need "(*int)(foo)", not "*int(foo)". - case *types.Pointer, *types.Signature: - typeName = "(" + typeName + ")" - } - - prefix = typeName + "(" + prefix - suffix = ")" - } - - if prefix != "" { - // If we are in a selector, add an edit to place prefix before selector. - if sel := enclosingSelector(c.path, c.pos); sel != nil { - edits, err := c.editText(sel.Pos(), sel.Pos(), prefix) - if err != nil { - return CompletionItem{}, err - } - protocolEdits = append(protocolEdits, edits...) - } else { - // If there is no selector, just stick the prefix at the start. - insert = prefix + insert - snip.PrependText(prefix) - } - } - - if suffix != "" { - insert += suffix - snip.WriteText(suffix) - } - - detail = strings.TrimPrefix(detail, "untyped ") - // override computed detail with provided detail, if something is provided. - if cand.detail != "" { - detail = cand.detail - } - item := CompletionItem{ - Label: label, - InsertText: insert, - AdditionalTextEdits: protocolEdits, - Detail: detail, - Kind: kind, - Score: cand.score, - Depth: len(cand.path), - snippet: &snip, - obj: obj, - } - // If the user doesn't want documentation for completion items. - if !c.opts.documentation { - return item, nil - } - pos := c.snapshot.FileSet().Position(obj.Pos()) - - // We ignore errors here, because some types, like "unsafe" or "error", - // may not have valid positions that we can use to get documentation. - if !pos.IsValid() { - return item, nil - } - uri := span.URIFromPath(pos.Filename) - - // Find the source file of the candidate. - pkg, err := source.FindPackageFromPos(ctx, c.snapshot, obj.Pos()) - if err != nil { - return item, nil - } - - decl, err := c.snapshot.PosToDecl(ctx, pkg, obj.Pos()) - if err != nil { - return CompletionItem{}, err - } - hover, err := source.FindHoverContext(ctx, c.snapshot, pkg, obj, decl, nil) - if err != nil { - event.Error(ctx, "failed to find Hover", err, tag.URI.Of(uri)) - return item, nil - } - if c.opts.fullDocumentation { - item.Documentation = hover.Comment.Text() - } else { - item.Documentation = doc.Synopsis(hover.Comment.Text()) - } - // The desired pattern is `^// Deprecated`, but the prefix has been removed - if strings.HasPrefix(hover.Comment.Text(), "Deprecated") { - if c.snapshot.View().Options().CompletionTags { - item.Tags = []protocol.CompletionItemTag{protocol.ComplDeprecated} - } else if c.snapshot.View().Options().CompletionDeprecated { - item.Deprecated = true - } - } - - return item, nil -} - -// importEdits produces the text edits necessary to add the given import to the current file. -func (c *completer) importEdits(imp *importInfo) ([]protocol.TextEdit, error) { - if imp == nil { - return nil, nil - } - - pgf, err := c.pkg.File(span.URIFromPath(c.filename)) - if err != nil { - return nil, err - } - - return source.ComputeOneImportFixEdits(c.snapshot, pgf, &imports.ImportFix{ - StmtInfo: imports.ImportInfo{ - ImportPath: imp.importPath, - Name: imp.name, - }, - // IdentName is unused on this path and is difficult to get. - FixType: imports.AddImport, - }) -} - -func (c *completer) formatBuiltin(ctx context.Context, cand candidate) (CompletionItem, error) { - obj := cand.obj - item := CompletionItem{ - Label: obj.Name(), - InsertText: obj.Name(), - Score: cand.score, - } - switch obj.(type) { - case *types.Const: - item.Kind = protocol.ConstantCompletion - case *types.Builtin: - item.Kind = protocol.FunctionCompletion - sig, err := source.NewBuiltinSignature(ctx, c.snapshot, obj.Name()) - if err != nil { - return CompletionItem{}, err - } - item.Detail = "func" + sig.Format() - item.snippet = &snippet.Builder{} - c.functionCallSnippet(obj.Name(), sig.TypeParams(), sig.Params(), item.snippet) - case *types.TypeName: - if types.IsInterface(obj.Type()) { - item.Kind = protocol.InterfaceCompletion - } else { - item.Kind = protocol.ClassCompletion - } - case *types.Nil: - item.Kind = protocol.VariableCompletion - } - return item, nil -} - -// decide if the type params (if any) should be part of the completion -// which only possible for types.Named and types.Signature -// (so far, only in receivers, e.g.; func (s *GENERIC[K, V])..., which is a types.Named) -func (c *completer) wantTypeParams() bool { - // Need to be lexically in a receiver, and a child of an IndexListExpr - // (but IndexListExpr only exists with go1.18) - start := c.path[0].Pos() - for i, nd := range c.path { - if fd, ok := nd.(*ast.FuncDecl); ok { - if i > 0 && fd.Recv != nil && start < fd.Recv.End() { - return true - } else { - return false - } - } - } - return false -} |