github.com/jhump/golang-x-tools@v0.0.0-20220218190644-4958d6d39439/internal/lsp/source/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 "github.com/jhump/golang-x-tools/internal/lsp/snippet" 11 ) 12 13 // structFieldSnippets calculates the snippet for struct literal field names. 14 func (c *completer) structFieldSnippet(cand candidate, detail string, snip *snippet.Builder) { 15 if !c.wantStructFieldCompletions() { 16 return 17 } 18 19 // If we are in a deep completion then we can't be completing a field 20 // name (e.g. "Foo{f<>}" completing to "Foo{f.Bar}" should not generate 21 // a snippet). 22 if len(cand.path) > 0 { 23 return 24 } 25 26 clInfo := c.enclosingCompositeLiteral 27 28 // If we are already in a key-value expression, we don't want a snippet. 29 if clInfo.kv != nil { 30 return 31 } 32 33 // A plain snippet turns "Foo{Ba<>" into "Foo{Bar: <>". 34 snip.WriteText(": ") 35 snip.WritePlaceholder(func(b *snippet.Builder) { 36 // A placeholder snippet turns "Foo{Ba<>" into "Foo{Bar: <*int*>". 37 if c.opts.placeholders { 38 b.WriteText(detail) 39 } 40 }) 41 42 fset := c.snapshot.FileSet() 43 44 // If the cursor position is on a different line from the literal's opening brace, 45 // we are in a multiline literal. 46 if fset.Position(c.pos).Line != fset.Position(clInfo.cl.Lbrace).Line { 47 snip.WriteText(",") 48 } 49 } 50 51 // functionCallSnippets calculates the snippet for function calls. 52 func (c *completer) functionCallSnippet(name string, params []string, snip *snippet.Builder) { 53 // If there is no suffix then we need to reuse existing call parens 54 // "()" if present. If there is an identifier suffix then we always 55 // need to include "()" since we don't overwrite the suffix. 56 if c.surrounding != nil && c.surrounding.Suffix() == "" && len(c.path) > 1 { 57 // If we are the left side (i.e. "Fun") part of a call expression, 58 // we don't want a snippet since there are already parens present. 59 switch n := c.path[1].(type) { 60 case *ast.CallExpr: 61 // The Lparen != Rparen check detects fudged CallExprs we 62 // inserted when fixing the AST. In this case, we do still need 63 // to insert the calling "()" parens. 64 if n.Fun == c.path[0] && n.Lparen != n.Rparen { 65 return 66 } 67 case *ast.SelectorExpr: 68 if len(c.path) > 2 { 69 if call, ok := c.path[2].(*ast.CallExpr); ok && call.Fun == c.path[1] && call.Lparen != call.Rparen { 70 return 71 } 72 } 73 } 74 } 75 76 snip.WriteText(name + "(") 77 78 if c.opts.placeholders { 79 // A placeholder snippet turns "someFun<>" into "someFunc(<*i int*>, *s string*)". 80 for i, p := range params { 81 if i > 0 { 82 snip.WriteText(", ") 83 } 84 snip.WritePlaceholder(func(b *snippet.Builder) { 85 b.WriteText(p) 86 }) 87 } 88 } else { 89 // A plain snippet turns "someFun<>" into "someFunc(<>)". 90 if len(params) > 0 { 91 snip.WritePlaceholder(nil) 92 } 93 } 94 95 snip.WriteText(")") 96 }