golang.org/x/tools/gopls@v0.15.3/internal/golang/completion/snippet.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 completion 6 7 import ( 8 "go/ast" 9 10 "golang.org/x/tools/gopls/internal/golang/completion/snippet" 11 "golang.org/x/tools/gopls/internal/util/safetoken" 12 ) 13 14 // structFieldSnippet calculates the snippet for struct literal field names. 15 func (c *completer) structFieldSnippet(cand candidate, detail string, snip *snippet.Builder) { 16 if !c.wantStructFieldCompletions() { 17 return 18 } 19 20 // If we are in a deep completion then we can't be completing a field 21 // name (e.g. "Foo{f<>}" completing to "Foo{f.Bar}" should not generate 22 // a snippet). 23 if len(cand.path) > 0 { 24 return 25 } 26 27 clInfo := c.enclosingCompositeLiteral 28 29 // If we are already in a key-value expression, we don't want a snippet. 30 if clInfo.kv != nil { 31 return 32 } 33 34 // A plain snippet turns "Foo{Ba<>" into "Foo{Bar: <>". 35 snip.WriteText(": ") 36 snip.WritePlaceholder(func(b *snippet.Builder) { 37 // A placeholder snippet turns "Foo{Ba<>" into "Foo{Bar: <*int*>". 38 if c.opts.placeholders { 39 b.WriteText(detail) 40 } 41 }) 42 43 fset := c.pkg.FileSet() 44 45 // If the cursor position is on a different line from the literal's opening brace, 46 // we are in a multiline literal. Ignore line directives. 47 if safetoken.StartPosition(fset, c.pos).Line != safetoken.StartPosition(fset, clInfo.cl.Lbrace).Line { 48 snip.WriteText(",") 49 } 50 } 51 52 // functionCallSnippet calculates the snippet for function calls. 53 // 54 // Callers should omit the suffix of type parameters that are 55 // constrained by the argument types, to avoid offering completions 56 // that contain instantiations that are redundant because of type 57 // inference, such as f[int](1) for func f[T any](x T). 58 func (c *completer) functionCallSnippet(name string, tparams, params []string, snip *snippet.Builder) { 59 if !c.opts.completeFunctionCalls { 60 snip.WriteText(name) 61 return 62 } 63 64 // If there is no suffix then we need to reuse existing call parens 65 // "()" if present. If there is an identifier suffix then we always 66 // need to include "()" since we don't overwrite the suffix. 67 if c.surrounding != nil && c.surrounding.Suffix() == "" && len(c.path) > 1 { 68 // If we are the left side (i.e. "Fun") part of a call expression, 69 // we don't want a snippet since there are already parens present. 70 switch n := c.path[1].(type) { 71 case *ast.CallExpr: 72 // The Lparen != Rparen check detects fudged CallExprs we 73 // inserted when fixing the AST. In this case, we do still need 74 // to insert the calling "()" parens. 75 if n.Fun == c.path[0] && n.Lparen != n.Rparen { 76 return 77 } 78 case *ast.SelectorExpr: 79 if len(c.path) > 2 { 80 if call, ok := c.path[2].(*ast.CallExpr); ok && call.Fun == c.path[1] && call.Lparen != call.Rparen { 81 return 82 } 83 } 84 } 85 } 86 87 snip.WriteText(name) 88 89 if len(tparams) > 0 { 90 snip.WriteText("[") 91 if c.opts.placeholders { 92 for i, tp := range tparams { 93 if i > 0 { 94 snip.WriteText(", ") 95 } 96 snip.WritePlaceholder(func(b *snippet.Builder) { 97 b.WriteText(tp) 98 }) 99 } 100 } else { 101 snip.WritePlaceholder(nil) 102 } 103 snip.WriteText("]") 104 } 105 106 snip.WriteText("(") 107 108 if c.opts.placeholders { 109 // A placeholder snippet turns "someFun<>" into "someFunc(<*i int*>, *s string*)". 110 for i, p := range params { 111 if i > 0 { 112 snip.WriteText(", ") 113 } 114 snip.WritePlaceholder(func(b *snippet.Builder) { 115 b.WriteText(p) 116 }) 117 } 118 } else { 119 // A plain snippet turns "someFun<>" into "someFunc(<>)". 120 if len(params) > 0 { 121 snip.WritePlaceholder(nil) 122 } 123 } 124 125 snip.WriteText(")") 126 }