github.com/april1989/origin-go-tools@v0.0.32/internal/lsp/source/completion_format.go (about) 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. 4 5 package source 6 7 import ( 8 "context" 9 "fmt" 10 "go/ast" 11 "go/types" 12 "strings" 13 14 "github.com/april1989/origin-go-tools/internal/event" 15 "github.com/april1989/origin-go-tools/internal/imports" 16 "github.com/april1989/origin-go-tools/internal/lsp/debug/tag" 17 "github.com/april1989/origin-go-tools/internal/lsp/protocol" 18 "github.com/april1989/origin-go-tools/internal/lsp/snippet" 19 "github.com/april1989/origin-go-tools/internal/span" 20 ) 21 22 // formatCompletion creates a completion item for a given candidate. 23 func (c *completer) item(ctx context.Context, cand candidate) (CompletionItem, error) { 24 obj := cand.obj 25 26 // Handle builtin types separately. 27 if obj.Parent() == types.Universe { 28 return c.formatBuiltin(ctx, cand) 29 } 30 31 var ( 32 label = cand.name 33 detail = types.TypeString(obj.Type(), c.qf) 34 insert = label 35 kind = protocol.TextCompletion 36 snip *snippet.Builder 37 protocolEdits []protocol.TextEdit 38 ) 39 if obj.Type() == nil { 40 detail = "" 41 } 42 43 // expandFuncCall mutates the completion label, detail, and snippet 44 // to that of an invocation of sig. 45 expandFuncCall := func(sig *types.Signature) error { 46 s, err := newSignature(ctx, c.snapshot, c.pkg, c.file, "", sig, nil, c.qf) 47 if err != nil { 48 return err 49 } 50 snip = c.functionCallSnippet(label, s.params) 51 detail = "func" + s.format() 52 return nil 53 } 54 55 switch obj := obj.(type) { 56 case *types.TypeName: 57 detail, kind = formatType(obj.Type(), c.qf) 58 case *types.Const: 59 kind = protocol.ConstantCompletion 60 case *types.Var: 61 if _, ok := obj.Type().(*types.Struct); ok { 62 detail = "struct{...}" // for anonymous structs 63 } else if obj.IsField() { 64 detail = formatVarType(ctx, c.snapshot, c.pkg, c.file, obj, c.qf) 65 } 66 if obj.IsField() { 67 kind = protocol.FieldCompletion 68 snip = c.structFieldSnippet(label, detail) 69 } else { 70 kind = protocol.VariableCompletion 71 } 72 if obj.Type() == nil { 73 break 74 } 75 76 if sig, ok := obj.Type().Underlying().(*types.Signature); ok && cand.expandFuncCall { 77 if err := expandFuncCall(sig); err != nil { 78 return CompletionItem{}, err 79 } 80 } 81 case *types.Func: 82 sig, ok := obj.Type().Underlying().(*types.Signature) 83 if !ok { 84 break 85 } 86 kind = protocol.FunctionCompletion 87 if sig != nil && sig.Recv() != nil { 88 kind = protocol.MethodCompletion 89 } 90 91 if cand.expandFuncCall { 92 if err := expandFuncCall(sig); err != nil { 93 return CompletionItem{}, err 94 } 95 } 96 case *types.PkgName: 97 kind = protocol.ModuleCompletion 98 detail = fmt.Sprintf("%q", obj.Imported().Path()) 99 case *types.Label: 100 kind = protocol.ConstantCompletion 101 detail = "label" 102 } 103 104 // If this candidate needs an additional import statement, 105 // add the additional text edits needed. 106 if cand.imp != nil { 107 addlEdits, err := c.importEdits(ctx, cand.imp) 108 if err != nil { 109 return CompletionItem{}, err 110 } 111 112 protocolEdits = append(protocolEdits, addlEdits...) 113 if kind != protocol.ModuleCompletion { 114 if detail != "" { 115 detail += " " 116 } 117 detail += fmt.Sprintf("(from %q)", cand.imp.importPath) 118 } 119 } 120 121 // Prepend "&" or "*" operator as appropriate. 122 var prefixOp string 123 if cand.takeAddress { 124 prefixOp = "&" 125 } else if cand.makePointer { 126 prefixOp = "*" 127 } else if cand.dereference > 0 { 128 prefixOp = strings.Repeat("*", cand.dereference) 129 } 130 131 if prefixOp != "" { 132 // If we are in a selector, add an edit to place prefix before selector. 133 if sel := enclosingSelector(c.path, c.pos); sel != nil { 134 edits, err := prependEdit(c.snapshot.FileSet(), c.mapper, sel, prefixOp) 135 if err != nil { 136 return CompletionItem{}, err 137 } 138 protocolEdits = append(protocolEdits, edits...) 139 } else { 140 // If there is no selector, just stick the prefix at the start. 141 insert = prefixOp + insert 142 } 143 144 label = prefixOp + label 145 } 146 147 // Add variadic "..." if we are filling in a variadic param. 148 if cand.variadic { 149 insert += "..." 150 if snip != nil { 151 snip.WriteText("...") 152 } 153 } 154 155 detail = strings.TrimPrefix(detail, "untyped ") 156 item := CompletionItem{ 157 Label: label, 158 InsertText: insert, 159 AdditionalTextEdits: protocolEdits, 160 Detail: detail, 161 Kind: kind, 162 Score: cand.score, 163 Depth: len(c.deepState.chain), 164 snippet: snip, 165 obj: obj, 166 } 167 // If the user doesn't want documentation for completion items. 168 if !c.opts.documentation { 169 return item, nil 170 } 171 pos := c.snapshot.FileSet().Position(obj.Pos()) 172 173 // We ignore errors here, because some types, like "unsafe" or "error", 174 // may not have valid positions that we can use to get documentation. 175 if !pos.IsValid() { 176 return item, nil 177 } 178 uri := span.URIFromPath(pos.Filename) 179 180 // Find the source file of the candidate, starting from a package 181 // that should have it in its dependencies. 182 searchPkg := c.pkg 183 if cand.imp != nil && cand.imp.pkg != nil { 184 searchPkg = cand.imp.pkg 185 } 186 187 pgf, pkg, err := findPosInPackage(c.snapshot, searchPkg, obj.Pos()) 188 if err != nil { 189 return item, nil 190 } 191 192 posToDecl, err := c.snapshot.PosToDecl(ctx, pgf) 193 if err != nil { 194 return CompletionItem{}, err 195 } 196 decl := posToDecl[obj.Pos()] 197 if decl == nil { 198 return item, nil 199 } 200 201 hover, err := hoverInfo(pkg, obj, decl) 202 if err != nil { 203 event.Error(ctx, "failed to find Hover", err, tag.URI.Of(uri)) 204 return item, nil 205 } 206 item.Documentation = hover.Synopsis 207 if c.opts.fullDocumentation { 208 item.Documentation = hover.FullDocumentation 209 } 210 return item, nil 211 } 212 213 // importEdits produces the text edits necessary to add the given import to the current file. 214 func (c *completer) importEdits(ctx context.Context, imp *importInfo) ([]protocol.TextEdit, error) { 215 if imp == nil { 216 return nil, nil 217 } 218 219 pgf, err := c.pkg.File(span.URIFromPath(c.filename)) 220 if err != nil { 221 return nil, err 222 } 223 224 return computeOneImportFixEdits(ctx, c.snapshot, pgf, &imports.ImportFix{ 225 StmtInfo: imports.ImportInfo{ 226 ImportPath: imp.importPath, 227 Name: imp.name, 228 }, 229 // IdentName is unused on this path and is difficult to get. 230 FixType: imports.AddImport, 231 }) 232 } 233 234 func (c *completer) formatBuiltin(ctx context.Context, cand candidate) (CompletionItem, error) { 235 obj := cand.obj 236 item := CompletionItem{ 237 Label: obj.Name(), 238 InsertText: obj.Name(), 239 Score: cand.score, 240 } 241 switch obj.(type) { 242 case *types.Const: 243 item.Kind = protocol.ConstantCompletion 244 case *types.Builtin: 245 item.Kind = protocol.FunctionCompletion 246 sig, err := newBuiltinSignature(ctx, c.snapshot, obj.Name()) 247 if err != nil { 248 return CompletionItem{}, err 249 } 250 item.Detail = "func" + sig.format() 251 item.snippet = c.functionCallSnippet(obj.Name(), sig.params) 252 case *types.TypeName: 253 if types.IsInterface(obj.Type()) { 254 item.Kind = protocol.InterfaceCompletion 255 } else { 256 item.Kind = protocol.ClassCompletion 257 } 258 case *types.Nil: 259 item.Kind = protocol.VariableCompletion 260 } 261 return item, nil 262 } 263 264 // qualifier returns a function that appropriately formats a types.PkgName 265 // appearing in a *ast.File. 266 func qualifier(f *ast.File, pkg *types.Package, info *types.Info) types.Qualifier { 267 // Construct mapping of import paths to their defined or implicit names. 268 imports := make(map[*types.Package]string) 269 for _, imp := range f.Imports { 270 var obj types.Object 271 if imp.Name != nil { 272 obj = info.Defs[imp.Name] 273 } else { 274 obj = info.Implicits[imp] 275 } 276 if pkgname, ok := obj.(*types.PkgName); ok { 277 imports[pkgname.Imported()] = pkgname.Name() 278 } 279 } 280 // Define qualifier to replace full package paths with names of the imports. 281 return func(p *types.Package) string { 282 if p == pkg { 283 return "" 284 } 285 if name, ok := imports[p]; ok { 286 return name 287 } 288 return p.Name() 289 } 290 }