1 // Copyright 2019 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
13 "golang.org/x/tools/internal/event"
14 "golang.org/x/tools/internal/imports"
15 "golang.org/x/tools/internal/lsp/debug/tag"
16 "golang.org/x/tools/internal/lsp/protocol"
17 "golang.org/x/tools/internal/lsp/snippet"
18 "golang.org/x/tools/internal/lsp/source"
19 "golang.org/x/tools/internal/span"
20 errors "golang.org/x/xerrors"
23 // item formats a candidate to a CompletionItem.
24 func (c *completer) item(ctx context.Context, cand candidate) (CompletionItem, error) {
27 // if the object isn't a valid match against the surrounding, return early.
28 matchScore := c.matcher.Score(cand.name)
30 return CompletionItem{}, errors.New("not a surrounding match")
32 cand.score *= float64(matchScore)
34 // Ignore deep candidates that wont be in the MaxDeepCompletions anyway.
35 if len(cand.path) != 0 && !c.deepState.isHighScore(cand.score) {
36 return CompletionItem{}, errors.New("not a high scoring candidate")
39 // Handle builtin types separately.
40 if obj.Parent() == types.Universe {
41 return c.formatBuiltin(ctx, cand)
46 detail = types.TypeString(obj.Type(), c.qf)
48 kind = protocol.TextCompletion
50 protocolEdits []protocol.TextEdit
52 if obj.Type() == nil {
56 // expandFuncCall mutates the completion label, detail, and snippet
57 // to that of an invocation of sig.
58 expandFuncCall := func(sig *types.Signature) {
59 s := source.NewSignature(ctx, c.snapshot, c.pkg, sig, nil, c.qf)
60 snip = c.functionCallSnippet(label, s.Params())
61 detail = "func" + s.Format()
64 switch obj := obj.(type) {
66 detail, kind = source.FormatType(obj.Type(), c.qf)
68 kind = protocol.ConstantCompletion
70 if _, ok := obj.Type().(*types.Struct); ok {
71 detail = "struct{...}" // for anonymous structs
72 } else if obj.IsField() {
73 detail = source.FormatVarType(ctx, c.snapshot, c.pkg, obj, c.qf)
76 kind = protocol.FieldCompletion
77 snip = c.structFieldSnippet(cand, label, detail)
79 kind = protocol.VariableCompletion
81 if obj.Type() == nil {
85 if sig, ok := obj.Type().Underlying().(*types.Signature); ok && cand.expandFuncCall {
89 sig, ok := obj.Type().Underlying().(*types.Signature)
93 kind = protocol.FunctionCompletion
94 if sig != nil && sig.Recv() != nil {
95 kind = protocol.MethodCompletion
98 if cand.expandFuncCall {
102 kind = protocol.ModuleCompletion
103 detail = fmt.Sprintf("%q", obj.Imported().Path())
105 kind = protocol.ConstantCompletion
109 // If this candidate needs an additional import statement,
110 // add the additional text edits needed.
112 addlEdits, err := c.importEdits(cand.imp)
114 return CompletionItem{}, err
117 protocolEdits = append(protocolEdits, addlEdits...)
118 if kind != protocol.ModuleCompletion {
122 detail += fmt.Sprintf("(from %q)", cand.imp.importPath)
126 // Prepend "&" or "*" operator as appropriate.
128 if cand.takeAddress {
130 } else if cand.makePointer {
132 } else if cand.dereference > 0 {
133 prefixOp = strings.Repeat("*", cand.dereference)
137 // If we are in a selector, add an edit to place prefix before selector.
138 if sel := enclosingSelector(c.path, c.pos); sel != nil {
139 edits, err := prependEdit(c.snapshot.FileSet(), c.mapper, sel, prefixOp)
141 return CompletionItem{}, err
143 protocolEdits = append(protocolEdits, edits...)
145 // If there is no selector, just stick the prefix at the start.
146 insert = prefixOp + insert
149 label = prefixOp + label
152 // Add variadic "..." if we are filling in a variadic param.
156 snip.WriteText("...")
160 detail = strings.TrimPrefix(detail, "untyped ")
161 // override computed detail with provided detail, if something is provided.
162 if cand.detail != "" {
165 item := CompletionItem{
168 AdditionalTextEdits: protocolEdits,
172 Depth: len(cand.path),
176 // If the user doesn't want documentation for completion items.
177 if !c.opts.documentation {
180 pos := c.snapshot.FileSet().Position(obj.Pos())
182 // We ignore errors here, because some types, like "unsafe" or "error",
183 // may not have valid positions that we can use to get documentation.
187 uri := span.URIFromPath(pos.Filename)
189 // Find the source file of the candidate, starting from a package
190 // that should have it in its dependencies.
192 if cand.imp != nil && cand.imp.pkg != nil {
193 searchPkg = cand.imp.pkg
196 pgf, pkg, err := source.FindPosInPackage(c.snapshot, searchPkg, obj.Pos())
201 posToDecl, err := c.snapshot.PosToDecl(ctx, pgf)
203 return CompletionItem{}, err
205 decl := posToDecl[obj.Pos()]
210 hover, err := source.HoverInfo(ctx, pkg, obj, decl)
212 event.Error(ctx, "failed to find Hover", err, tag.URI.Of(uri))
215 item.Documentation = hover.Synopsis
216 if c.opts.fullDocumentation {
217 item.Documentation = hover.FullDocumentation
223 // importEdits produces the text edits necessary to add the given import to the current file.
224 func (c *completer) importEdits(imp *importInfo) ([]protocol.TextEdit, error) {
229 pgf, err := c.pkg.File(span.URIFromPath(c.filename))
234 return source.ComputeOneImportFixEdits(c.snapshot, pgf, &imports.ImportFix{
235 StmtInfo: imports.ImportInfo{
236 ImportPath: imp.importPath,
239 // IdentName is unused on this path and is difficult to get.
240 FixType: imports.AddImport,
244 func (c *completer) formatBuiltin(ctx context.Context, cand candidate) (CompletionItem, error) {
246 item := CompletionItem{
248 InsertText: obj.Name(),
253 item.Kind = protocol.ConstantCompletion
255 item.Kind = protocol.FunctionCompletion
256 sig, err := source.NewBuiltinSignature(ctx, c.snapshot, obj.Name())
258 return CompletionItem{}, err
260 item.Detail = "func" + sig.Format()
261 item.snippet = c.functionCallSnippet(obj.Name(), sig.Params())
262 case *types.TypeName:
263 if types.IsInterface(obj.Type()) {
264 item.Kind = protocol.InterfaceCompletion
266 item.Kind = protocol.ClassCompletion
269 item.Kind = protocol.VariableCompletion