golang.org/x/tools/gopls@v0.15.3/internal/golang/completion/postfix_snippets.go (about)

     1  // Copyright 2020 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 completion
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"go/ast"
    11  	"go/token"
    12  	"go/types"
    13  	"log"
    14  	"reflect"
    15  	"strings"
    16  	"sync"
    17  	"text/template"
    18  
    19  	"golang.org/x/tools/gopls/internal/cache/metadata"
    20  	"golang.org/x/tools/gopls/internal/golang"
    21  	"golang.org/x/tools/gopls/internal/golang/completion/snippet"
    22  	"golang.org/x/tools/gopls/internal/protocol"
    23  	"golang.org/x/tools/gopls/internal/util/safetoken"
    24  	"golang.org/x/tools/internal/event"
    25  	"golang.org/x/tools/internal/imports"
    26  )
    27  
    28  // Postfix snippets are artificial methods that allow the user to
    29  // compose common operations in an "argument oriented" fashion. For
    30  // example, instead of "sort.Slice(someSlice, ...)" a user can expand
    31  // "someSlice.sort!".
    32  
    33  // postfixTmpl represents a postfix snippet completion candidate.
    34  type postfixTmpl struct {
    35  	// label is the completion candidate's label presented to the user.
    36  	label string
    37  
    38  	// details is passed along to the client as the candidate's details.
    39  	details string
    40  
    41  	// body is the template text. See postfixTmplArgs for details on the
    42  	// facilities available to the template.
    43  	body string
    44  
    45  	tmpl *template.Template
    46  }
    47  
    48  // postfixTmplArgs are the template execution arguments available to
    49  // the postfix snippet templates.
    50  type postfixTmplArgs struct {
    51  	// StmtOK is true if it is valid to replace the selector with a
    52  	// statement. For example:
    53  	//
    54  	//    func foo() {
    55  	//      bar.sort! // statement okay
    56  	//
    57  	//      someMethod(bar.sort!) // statement not okay
    58  	//    }
    59  	StmtOK bool
    60  
    61  	// X is the textual SelectorExpr.X. For example, when completing
    62  	// "foo.bar.print!", "X" is "foo.bar".
    63  	X string
    64  
    65  	// Obj is the types.Object of SelectorExpr.X, if any.
    66  	Obj types.Object
    67  
    68  	// Type is the type of "foo.bar" in "foo.bar.print!".
    69  	Type types.Type
    70  
    71  	// FuncResult are results of the enclosed function
    72  	FuncResults []*types.Var
    73  
    74  	sel            *ast.SelectorExpr
    75  	scope          *types.Scope
    76  	snip           snippet.Builder
    77  	importIfNeeded func(pkgPath string, scope *types.Scope) (name string, edits []protocol.TextEdit, err error)
    78  	edits          []protocol.TextEdit
    79  	qf             types.Qualifier
    80  	varNames       map[string]bool
    81  	placeholders   bool
    82  	currentTabStop int
    83  }
    84  
    85  var postfixTmpls = []postfixTmpl{{
    86  	label:   "sort",
    87  	details: "sort.Slice()",
    88  	body: `{{if and (eq .Kind "slice") .StmtOK -}}
    89  {{.Import "sort"}}.Slice({{.X}}, func({{.VarName nil "i"}}, {{.VarName nil "j"}} int) bool {
    90  	{{.Cursor}}
    91  })
    92  {{- end}}`,
    93  }, {
    94  	label:   "last",
    95  	details: "s[len(s)-1]",
    96  	body: `{{if and (eq .Kind "slice") .Obj -}}
    97  {{.X}}[len({{.X}})-1]
    98  {{- end}}`,
    99  }, {
   100  	label:   "reverse",
   101  	details: "reverse slice",
   102  	body: `{{if and (eq .Kind "slice") .StmtOK -}}
   103  {{$i := .VarName nil "i"}}{{$j := .VarName nil "j" -}}
   104  for {{$i}}, {{$j}} := 0, len({{.X}})-1; {{$i}} < {{$j}}; {{$i}}, {{$j}} = {{$i}}+1, {{$j}}-1 {
   105  	{{.X}}[{{$i}}], {{.X}}[{{$j}}] = {{.X}}[{{$j}}], {{.X}}[{{$i}}]
   106  }
   107  {{end}}`,
   108  }, {
   109  	label:   "range",
   110  	details: "range over slice",
   111  	body: `{{if and (eq .Kind "slice") .StmtOK -}}
   112  for {{.VarName nil "i" | .Placeholder }}, {{.VarName .ElemType "v" | .Placeholder}} := range {{.X}} {
   113  	{{.Cursor}}
   114  }
   115  {{- end}}`,
   116  }, {
   117  	label:   "for",
   118  	details: "range over slice by index",
   119  	body: `{{if and (eq .Kind "slice") .StmtOK -}}
   120  for {{ .VarName nil "i" | .Placeholder }} := range {{.X}} {
   121  	{{.Cursor}}
   122  }
   123  {{- end}}`,
   124  }, {
   125  	label:   "forr",
   126  	details: "range over slice by index and value",
   127  	body: `{{if and (eq .Kind "slice") .StmtOK -}}
   128  for {{.VarName nil "i" | .Placeholder }}, {{.VarName .ElemType "v" | .Placeholder }} := range {{.X}} {
   129  	{{.Cursor}}
   130  }
   131  {{- end}}`,
   132  }, {
   133  	label:   "append",
   134  	details: "append and re-assign slice",
   135  	body: `{{if and (eq .Kind "slice") .StmtOK .Obj -}}
   136  {{.X}} = append({{.X}}, {{.Cursor}})
   137  {{- end}}`,
   138  }, {
   139  	label:   "append",
   140  	details: "append to slice",
   141  	body: `{{if and (eq .Kind "slice") (not .StmtOK) -}}
   142  append({{.X}}, {{.Cursor}})
   143  {{- end}}`,
   144  }, {
   145  	label:   "copy",
   146  	details: "duplicate slice",
   147  	body: `{{if and (eq .Kind "slice") .StmtOK .Obj -}}
   148  {{$v := (.VarName nil (printf "%sCopy" .X))}}{{$v}} := make([]{{.TypeName .ElemType}}, len({{.X}}))
   149  copy({{$v}}, {{.X}})
   150  {{end}}`,
   151  }, {
   152  	label:   "range",
   153  	details: "range over map",
   154  	body: `{{if and (eq .Kind "map") .StmtOK -}}
   155  for {{.VarName .KeyType "k" | .Placeholder}}, {{.VarName .ElemType "v" | .Placeholder}} := range {{.X}} {
   156  	{{.Cursor}}
   157  }
   158  {{- end}}`,
   159  }, {
   160  	label:   "for",
   161  	details: "range over map by key",
   162  	body: `{{if and (eq .Kind "map") .StmtOK -}}
   163  for {{.VarName .KeyType "k" | .Placeholder}} := range {{.X}} {
   164  	{{.Cursor}}
   165  }
   166  {{- end}}`,
   167  }, {
   168  	label:   "forr",
   169  	details: "range over map by key and value",
   170  	body: `{{if and (eq .Kind "map") .StmtOK -}}
   171  for {{.VarName .KeyType "k" | .Placeholder}}, {{.VarName .ElemType "v" | .Placeholder}} := range {{.X}} {
   172  	{{.Cursor}}
   173  }
   174  {{- end}}`,
   175  }, {
   176  	label:   "clear",
   177  	details: "clear map contents",
   178  	body: `{{if and (eq .Kind "map") .StmtOK -}}
   179  {{$k := (.VarName .KeyType "k")}}for {{$k}} := range {{.X}} {
   180  	delete({{.X}}, {{$k}})
   181  }
   182  {{end}}`,
   183  }, {
   184  	label:   "keys",
   185  	details: "create slice of keys",
   186  	body: `{{if and (eq .Kind "map") .StmtOK -}}
   187  {{$keysVar := (.VarName nil "keys")}}{{$keysVar}} := make([]{{.TypeName .KeyType}}, 0, len({{.X}}))
   188  {{$k := (.VarName .KeyType "k")}}for {{$k}} := range {{.X}} {
   189  	{{$keysVar}} = append({{$keysVar}}, {{$k}})
   190  }
   191  {{end}}`,
   192  }, {
   193  	label:   "range",
   194  	details: "range over channel",
   195  	body: `{{if and (eq .Kind "chan") .StmtOK -}}
   196  for {{.VarName .ElemType "e" | .Placeholder}} := range {{.X}} {
   197  	{{.Cursor}}
   198  }
   199  {{- end}}`,
   200  }, {
   201  	label:   "for",
   202  	details: "range over channel",
   203  	body: `{{if and (eq .Kind "chan") .StmtOK -}}
   204  for {{.VarName .ElemType "e" | .Placeholder}} := range {{.X}} {
   205  	{{.Cursor}}
   206  }
   207  {{- end}}`,
   208  }, {
   209  	label:   "var",
   210  	details: "assign to variables",
   211  	body: `{{if and (eq .Kind "tuple") .StmtOK -}}
   212  {{$a := .}}{{range $i, $v := .Tuple}}{{if $i}}, {{end}}{{$a.VarName $v.Type $v.Name | $a.Placeholder }}{{end}} := {{.X}}
   213  {{- end}}`,
   214  }, {
   215  	label:   "var",
   216  	details: "assign to variable",
   217  	body: `{{if and (ne .Kind "tuple") .StmtOK -}}
   218  {{.VarName .Type "" | .Placeholder }} := {{.X}}
   219  {{- end}}`,
   220  }, {
   221  	label:   "print",
   222  	details: "print to stdout",
   223  	body: `{{if and (ne .Kind "tuple") .StmtOK -}}
   224  {{.Import "fmt"}}.Printf("{{.EscapeQuotes .X}}: %v\n", {{.X}})
   225  {{- end}}`,
   226  }, {
   227  	label:   "print",
   228  	details: "print to stdout",
   229  	body: `{{if and (eq .Kind "tuple") .StmtOK -}}
   230  {{.Import "fmt"}}.Println({{.X}})
   231  {{- end}}`,
   232  }, {
   233  	label:   "split",
   234  	details: "split string",
   235  	body: `{{if (eq (.TypeName .Type) "string") -}}
   236  {{.Import "strings"}}.Split({{.X}}, "{{.Cursor}}")
   237  {{- end}}`,
   238  }, {
   239  	label:   "join",
   240  	details: "join string slice",
   241  	body: `{{if and (eq .Kind "slice") (eq (.TypeName .ElemType) "string") -}}
   242  {{.Import "strings"}}.Join({{.X}}, "{{.Cursor}}")
   243  {{- end}}`,
   244  }, {
   245  	label:   "ifnotnil",
   246  	details: "if expr != nil",
   247  	body: `{{if and (or (eq .Kind "pointer") (eq .Kind "chan") (eq .Kind "signature") (eq .Kind "interface") (eq .Kind "map") (eq .Kind "slice")) .StmtOK -}}
   248  if {{.X}} != nil {
   249  	{{.Cursor}}
   250  }
   251  {{- end}}`,
   252  }, {
   253  	label:   "len",
   254  	details: "len(s)",
   255  	body: `{{if (eq .Kind "slice" "map" "array" "chan") -}}
   256  len({{.X}})
   257  {{- end}}`,
   258  }, {
   259  	label:   "iferr",
   260  	details: "check error and return",
   261  	body: `{{if and .StmtOK (eq (.TypeName .Type) "error") -}}
   262  {{- $errName := (or (and .IsIdent .X) "err") -}}
   263  if {{if not .IsIdent}}err := {{.X}}; {{end}}{{$errName}} != nil {
   264  	return {{$a := .}}{{range $i, $v := .FuncResults}}
   265  		{{- if $i}}, {{end -}}
   266  		{{- if eq ($a.TypeName $v.Type) "error" -}}
   267  			{{$a.Placeholder $errName}}
   268  		{{- else -}}
   269  			{{$a.Zero $v.Type}}
   270  		{{- end -}}
   271  	{{end}}
   272  }
   273  {{end}}`,
   274  }, {
   275  	label:   "iferr",
   276  	details: "check error and return",
   277  	body: `{{if and .StmtOK (eq .Kind "tuple") (len .Tuple) (eq (.TypeName .TupleLast.Type) "error") -}}
   278  {{- $a := . -}}
   279  if {{range $i, $v := .Tuple}}{{if $i}}, {{end}}{{if and (eq ($a.TypeName $v.Type) "error") (eq (inc $i) (len $a.Tuple))}}err{{else}}_{{end}}{{end}} := {{.X -}}
   280  ; err != nil {
   281  	return {{range $i, $v := .FuncResults}}
   282  		{{- if $i}}, {{end -}}
   283  		{{- if eq ($a.TypeName $v.Type) "error" -}}
   284  			{{$a.Placeholder "err"}}
   285  		{{- else -}}
   286  			{{$a.Zero $v.Type}}
   287  		{{- end -}}
   288  	{{end}}
   289  }
   290  {{end}}`,
   291  }, {
   292  	// variferr snippets use nested placeholders, as described in
   293  	// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#snippet_syntax,
   294  	// so that users can wrap the returned error without modifying the error
   295  	// variable name.
   296  	label:   "variferr",
   297  	details: "assign variables and check error",
   298  	body: `{{if and .StmtOK (eq .Kind "tuple") (len .Tuple) (eq (.TypeName .TupleLast.Type) "error") -}}
   299  {{- $a := . -}}
   300  {{- $errName := "err" -}}
   301  {{- range $i, $v := .Tuple -}}
   302  	{{- if $i}}, {{end -}}
   303  	{{- if and (eq ($a.TypeName $v.Type) "error") (eq (inc $i) (len $a.Tuple)) -}}
   304  		{{$errName | $a.SpecifiedPlaceholder (len $a.Tuple)}}
   305  	{{- else -}}
   306  		{{$a.VarName $v.Type $v.Name | $a.Placeholder}}
   307  	{{- end -}}
   308  {{- end}} := {{.X}}
   309  if {{$errName | $a.SpecifiedPlaceholder (len $a.Tuple)}} != nil {
   310  	return {{range $i, $v := .FuncResults}}
   311  		{{- if $i}}, {{end -}}
   312  		{{- if eq ($a.TypeName $v.Type) "error" -}}
   313  			{{$errName | $a.SpecifiedPlaceholder (len $a.Tuple) |
   314  				$a.SpecifiedPlaceholder (inc (len $a.Tuple))}}
   315  		{{- else -}}
   316  			{{$a.Zero $v.Type}}
   317  		{{- end -}}
   318  	{{end}}
   319  }
   320  {{end}}`,
   321  }, {
   322  	label:   "variferr",
   323  	details: "assign variables and check error",
   324  	body: `{{if and .StmtOK (eq (.TypeName .Type) "error") -}}
   325  {{- $a := . -}}
   326  {{- $errName := .VarName nil "err" -}}
   327  {{$errName | $a.SpecifiedPlaceholder 1}} := {{.X}}
   328  if {{$errName | $a.SpecifiedPlaceholder 1}} != nil {
   329  	return {{range $i, $v := .FuncResults}}
   330  		{{- if $i}}, {{end -}}
   331  		{{- if eq ($a.TypeName $v.Type) "error" -}}
   332  			{{$errName | $a.SpecifiedPlaceholder 1 | $a.SpecifiedPlaceholder 2}}
   333  		{{- else -}}
   334  			{{$a.Zero $v.Type}}
   335  		{{- end -}}
   336  	{{end}}
   337  }
   338  {{end}}`,
   339  }}
   340  
   341  // Cursor indicates where the client's cursor should end up after the
   342  // snippet is done.
   343  func (a *postfixTmplArgs) Cursor() string {
   344  	return "$0"
   345  }
   346  
   347  // Placeholder indicate a tab stop with the placeholder string, the order
   348  // of tab stops is the same as the order of invocation
   349  func (a *postfixTmplArgs) Placeholder(placeholder string) string {
   350  	if !a.placeholders {
   351  		placeholder = ""
   352  	}
   353  	return fmt.Sprintf("${%d:%s}", a.nextTabStop(), placeholder)
   354  }
   355  
   356  // nextTabStop returns the next tab stop index for a new placeholder.
   357  func (a *postfixTmplArgs) nextTabStop() int {
   358  	// Tab stops start from 1, so increment before returning.
   359  	a.currentTabStop++
   360  	return a.currentTabStop
   361  }
   362  
   363  // SpecifiedPlaceholder indicate a specified tab stop with the placeholder string.
   364  // Sometimes the same tab stop appears in multiple places and their numbers
   365  // need to be specified. e.g. variferr
   366  func (a *postfixTmplArgs) SpecifiedPlaceholder(tabStop int, placeholder string) string {
   367  	if !a.placeholders {
   368  		placeholder = ""
   369  	}
   370  	return fmt.Sprintf("${%d:%s}", tabStop, placeholder)
   371  }
   372  
   373  // Import makes sure the package corresponding to path is imported,
   374  // returning the identifier to use to refer to the package.
   375  func (a *postfixTmplArgs) Import(path string) (string, error) {
   376  	name, edits, err := a.importIfNeeded(path, a.scope)
   377  	if err != nil {
   378  		return "", fmt.Errorf("couldn't import %q: %w", path, err)
   379  	}
   380  	a.edits = append(a.edits, edits...)
   381  
   382  	return name, nil
   383  }
   384  
   385  func (a *postfixTmplArgs) EscapeQuotes(v string) string {
   386  	return strings.ReplaceAll(v, `"`, `\\"`)
   387  }
   388  
   389  // ElemType returns the Elem() type of xType, if applicable.
   390  func (a *postfixTmplArgs) ElemType() types.Type {
   391  	if e, _ := a.Type.(interface{ Elem() types.Type }); e != nil {
   392  		return e.Elem()
   393  	}
   394  	return nil
   395  }
   396  
   397  // Kind returns the underlying kind of type, e.g. "slice", "struct",
   398  // etc.
   399  func (a *postfixTmplArgs) Kind() string {
   400  	t := reflect.TypeOf(a.Type.Underlying())
   401  	return strings.ToLower(strings.TrimPrefix(t.String(), "*types."))
   402  }
   403  
   404  // KeyType returns the type of X's key. KeyType panics if X is not a
   405  // map.
   406  func (a *postfixTmplArgs) KeyType() types.Type {
   407  	return a.Type.Underlying().(*types.Map).Key()
   408  }
   409  
   410  // Tuple returns the tuple result vars if the type of X is tuple.
   411  func (a *postfixTmplArgs) Tuple() []*types.Var {
   412  	tuple, _ := a.Type.(*types.Tuple)
   413  	if tuple == nil {
   414  		return nil
   415  	}
   416  
   417  	typs := make([]*types.Var, 0, tuple.Len())
   418  	for i := 0; i < tuple.Len(); i++ {
   419  		typs = append(typs, tuple.At(i))
   420  	}
   421  	return typs
   422  }
   423  
   424  // TupleLast returns the last tuple result vars if the type of X is tuple.
   425  func (a *postfixTmplArgs) TupleLast() *types.Var {
   426  	tuple, _ := a.Type.(*types.Tuple)
   427  	if tuple == nil {
   428  		return nil
   429  	}
   430  	if tuple.Len() == 0 {
   431  		return nil
   432  	}
   433  	return tuple.At(tuple.Len() - 1)
   434  }
   435  
   436  // TypeName returns the textual representation of type t.
   437  func (a *postfixTmplArgs) TypeName(t types.Type) (string, error) {
   438  	if t == nil || t == types.Typ[types.Invalid] {
   439  		return "", fmt.Errorf("invalid type: %v", t)
   440  	}
   441  	return types.TypeString(t, a.qf), nil
   442  }
   443  
   444  // Zero return the zero value representation of type t
   445  func (a *postfixTmplArgs) Zero(t types.Type) string {
   446  	return formatZeroValue(t, a.qf)
   447  }
   448  
   449  func (a *postfixTmplArgs) IsIdent() bool {
   450  	_, ok := a.sel.X.(*ast.Ident)
   451  	return ok
   452  }
   453  
   454  // VarName returns a suitable variable name for the type t. If t
   455  // implements the error interface, "err" is used. If t is not a named
   456  // type then nonNamedDefault is used. Otherwise a name is made by
   457  // abbreviating the type name. If the resultant name is already in
   458  // scope, an integer is appended to make a unique name.
   459  func (a *postfixTmplArgs) VarName(t types.Type, nonNamedDefault string) string {
   460  	if t == nil {
   461  		t = types.Typ[types.Invalid]
   462  	}
   463  
   464  	var name string
   465  	// go/types predicates are undefined on types.Typ[types.Invalid].
   466  	if !types.Identical(t, types.Typ[types.Invalid]) && types.Implements(t, errorIntf) {
   467  		name = "err"
   468  	} else if _, isNamed := golang.Deref(t).(*types.Named); !isNamed {
   469  		name = nonNamedDefault
   470  	}
   471  
   472  	if name == "" {
   473  		name = types.TypeString(t, func(p *types.Package) string {
   474  			return ""
   475  		})
   476  		name = abbreviateTypeName(name)
   477  	}
   478  
   479  	if dot := strings.LastIndex(name, "."); dot > -1 {
   480  		name = name[dot+1:]
   481  	}
   482  
   483  	uniqueName := name
   484  	for i := 2; ; i++ {
   485  		if s, _ := a.scope.LookupParent(uniqueName, token.NoPos); s == nil && !a.varNames[uniqueName] {
   486  			break
   487  		}
   488  		uniqueName = fmt.Sprintf("%s%d", name, i)
   489  	}
   490  
   491  	a.varNames[uniqueName] = true
   492  
   493  	return uniqueName
   494  }
   495  
   496  func (c *completer) addPostfixSnippetCandidates(ctx context.Context, sel *ast.SelectorExpr) {
   497  	if !c.opts.postfix {
   498  		return
   499  	}
   500  
   501  	initPostfixRules()
   502  
   503  	if sel == nil || sel.Sel == nil {
   504  		return
   505  	}
   506  
   507  	selType := c.pkg.GetTypesInfo().TypeOf(sel.X)
   508  	if selType == nil {
   509  		return
   510  	}
   511  
   512  	// Skip empty tuples since there is no value to operate on.
   513  	if tuple, ok := selType.Underlying().(*types.Tuple); ok && tuple == nil {
   514  		return
   515  	}
   516  
   517  	tokFile := c.pkg.FileSet().File(c.pos)
   518  
   519  	// Only replace sel with a statement if sel is already a statement.
   520  	var stmtOK bool
   521  	for i, n := range c.path {
   522  		if n == sel && i < len(c.path)-1 {
   523  			switch p := c.path[i+1].(type) {
   524  			case *ast.ExprStmt:
   525  				stmtOK = true
   526  			case *ast.AssignStmt:
   527  				// In cases like:
   528  				//
   529  				//   foo.<>
   530  				//   bar = 123
   531  				//
   532  				// detect that "foo." makes up the entire statement since the
   533  				// apparent selector spans lines.
   534  				stmtOK = safetoken.Line(tokFile, c.pos) < safetoken.Line(tokFile, p.TokPos)
   535  			}
   536  			break
   537  		}
   538  	}
   539  
   540  	var funcResults []*types.Var
   541  	if c.enclosingFunc != nil {
   542  		results := c.enclosingFunc.sig.Results()
   543  		if results != nil {
   544  			funcResults = make([]*types.Var, results.Len())
   545  			for i := 0; i < results.Len(); i++ {
   546  				funcResults[i] = results.At(i)
   547  			}
   548  		}
   549  	}
   550  
   551  	scope := c.pkg.GetTypes().Scope().Innermost(c.pos)
   552  	if scope == nil {
   553  		return
   554  	}
   555  
   556  	// afterDot is the position after selector dot, e.g. "|" in
   557  	// "foo.|print".
   558  	afterDot := sel.Sel.Pos()
   559  
   560  	// We must detect dangling selectors such as:
   561  	//
   562  	//    foo.<>
   563  	//    bar
   564  	//
   565  	// and adjust afterDot so that we don't mistakenly delete the
   566  	// newline thinking "bar" is part of our selector.
   567  	if startLine := safetoken.Line(tokFile, sel.Pos()); startLine != safetoken.Line(tokFile, afterDot) {
   568  		if safetoken.Line(tokFile, c.pos) != startLine {
   569  			return
   570  		}
   571  		afterDot = c.pos
   572  	}
   573  
   574  	for _, rule := range postfixTmpls {
   575  		// When completing foo.print<>, "print" is naturally overwritten,
   576  		// but we need to also remove "foo." so the snippet has a clean
   577  		// slate.
   578  		edits, err := c.editText(sel.Pos(), afterDot, "")
   579  		if err != nil {
   580  			event.Error(ctx, "error calculating postfix edits", err)
   581  			return
   582  		}
   583  
   584  		tmplArgs := postfixTmplArgs{
   585  			X:              golang.FormatNode(c.pkg.FileSet(), sel.X),
   586  			StmtOK:         stmtOK,
   587  			Obj:            exprObj(c.pkg.GetTypesInfo(), sel.X),
   588  			Type:           selType,
   589  			FuncResults:    funcResults,
   590  			sel:            sel,
   591  			qf:             c.qf,
   592  			importIfNeeded: c.importIfNeeded,
   593  			scope:          scope,
   594  			varNames:       make(map[string]bool),
   595  			placeholders:   c.opts.placeholders,
   596  		}
   597  
   598  		// Feed the template straight into the snippet builder. This
   599  		// allows templates to build snippets as they are executed.
   600  		err = rule.tmpl.Execute(&tmplArgs.snip, &tmplArgs)
   601  		if err != nil {
   602  			event.Error(ctx, "error executing postfix template", err)
   603  			continue
   604  		}
   605  
   606  		if strings.TrimSpace(tmplArgs.snip.String()) == "" {
   607  			continue
   608  		}
   609  
   610  		score := c.matcher.Score(rule.label)
   611  		if score <= 0 {
   612  			continue
   613  		}
   614  
   615  		c.items = append(c.items, CompletionItem{
   616  			Label:               rule.label + "!",
   617  			Detail:              rule.details,
   618  			Score:               float64(score) * 0.01,
   619  			Kind:                protocol.SnippetCompletion,
   620  			snippet:             &tmplArgs.snip,
   621  			AdditionalTextEdits: append(edits, tmplArgs.edits...),
   622  		})
   623  	}
   624  }
   625  
   626  var postfixRulesOnce sync.Once
   627  
   628  func initPostfixRules() {
   629  	postfixRulesOnce.Do(func() {
   630  		var idx int
   631  		for _, rule := range postfixTmpls {
   632  			var err error
   633  			rule.tmpl, err = template.New("postfix_snippet").Funcs(template.FuncMap{
   634  				"inc": inc,
   635  			}).Parse(rule.body)
   636  			if err != nil {
   637  				log.Panicf("error parsing postfix snippet template: %v", err)
   638  			}
   639  			postfixTmpls[idx] = rule
   640  			idx++
   641  		}
   642  		postfixTmpls = postfixTmpls[:idx]
   643  	})
   644  }
   645  
   646  func inc(i int) int {
   647  	return i + 1
   648  }
   649  
   650  // importIfNeeded returns the package identifier and any necessary
   651  // edits to import package pkgPath.
   652  func (c *completer) importIfNeeded(pkgPath string, scope *types.Scope) (string, []protocol.TextEdit, error) {
   653  	defaultName := imports.ImportPathToAssumedName(pkgPath)
   654  
   655  	// Check if file already imports pkgPath.
   656  	for _, s := range c.file.Imports {
   657  		// TODO(adonovan): what if pkgPath has a vendor/ suffix?
   658  		// This may be the cause of go.dev/issue/56291.
   659  		if string(metadata.UnquoteImportPath(s)) == pkgPath {
   660  			if s.Name == nil {
   661  				return defaultName, nil, nil
   662  			}
   663  			if s.Name.Name != "_" {
   664  				return s.Name.Name, nil, nil
   665  			}
   666  		}
   667  	}
   668  
   669  	// Give up if the package's name is already in use by another object.
   670  	if _, obj := scope.LookupParent(defaultName, token.NoPos); obj != nil {
   671  		return "", nil, fmt.Errorf("import name %q of %q already in use", defaultName, pkgPath)
   672  	}
   673  
   674  	edits, err := c.importEdits(&importInfo{
   675  		importPath: pkgPath,
   676  	})
   677  	if err != nil {
   678  		return "", nil, err
   679  	}
   680  
   681  	return defaultName, edits, nil
   682  }