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