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  }