// Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package completion import ( "go/ast" "golang.org/x/tools/gopls/internal/golang/completion/snippet" "golang.org/x/tools/gopls/internal/util/safetoken" ) // structFieldSnippet calculates the snippet for struct literal field names. func (c *completer) structFieldSnippet(cand candidate, detail string, snip *snippet.Builder) { if !wantStructFieldCompletions(c.enclosingCompositeLiteral) { return } // If we are in a deep completion then we can't be completing a field // name (e.g. "Foo{f<>}" completing to "Foo{f.Bar}" should not generate // a snippet). if len(cand.path) > 0 { return } clInfo := c.enclosingCompositeLiteral // If we are already in a key-value expression, we don't want a snippet. if clInfo.kv != nil { return } // A plain snippet turns "Foo{Ba<>" into "Foo{Bar: <>". snip.WriteText(": ") snip.WritePlaceholder(func(b *snippet.Builder) { // A placeholder snippet turns "Foo{Ba<>" into "Foo{Bar: <*int*>". if c.opts.placeholders { b.WriteText(detail) } }) fset := c.pkg.FileSet() // If the cursor position is on a different line from the literal's opening brace, // we are in a multiline literal. Ignore line directives. if safetoken.StartPosition(fset, c.pos).Line != safetoken.StartPosition(fset, clInfo.cl.Lbrace).Line { snip.WriteText(",") } } // functionCallSnippet calculates the snippet for function calls. // // Callers should omit the suffix of type parameters that are // constrained by the argument types, to avoid offering completions // that contain instantiations that are redundant because of type // inference, such as f[int](1) for func f[T any](x T). func (c *completer) functionCallSnippet(name string, tparams, params []string, snip *snippet.Builder) { if !c.opts.completeFunctionCalls { snip.WriteText(name) return } // If there is no suffix then we need to reuse existing call parens // "()" if present. If there is an identifier suffix then we always // need to include "()" since we don't overwrite the suffix. if c.surrounding != nil && c.surrounding.Suffix() == "" && len(c.path) > 1 { // If we are the left side (i.e. "Fun") part of a call expression, // we don't want a snippet since there are already parens present. switch n := c.path[1].(type) { case *ast.CallExpr: // The Lparen != Rparen check detects fudged CallExprs we // inserted when fixing the AST. In this case, we do still need // to insert the calling "()" parens. if n.Fun == c.path[0] && n.Lparen != n.Rparen { return } case *ast.SelectorExpr: if len(c.path) > 2 { if call, ok := c.path[2].(*ast.CallExpr); ok && call.Fun == c.path[1] && call.Lparen != call.Rparen { return } } } } snip.WriteText(name) if len(tparams) > 0 { snip.WriteText("[") if c.opts.placeholders { for i, tp := range tparams { if i > 0 { snip.WriteText(", ") } snip.WritePlaceholder(func(b *snippet.Builder) { b.WriteText(tp) }) } } else { snip.WritePlaceholder(nil) } snip.WriteText("]") } snip.WriteText("(") if c.opts.placeholders { // A placeholder snippet turns "someFun<>" into "someFunc(<*i int*>, *s string*)". for i, p := range params { if i > 0 { snip.WriteText(", ") } snip.WritePlaceholder(func(b *snippet.Builder) { b.WriteText(p) }) } } else { // A plain snippet turns "someFun<>" into "someFunc(<>)". if len(params) > 0 { snip.WritePlaceholder(nil) } } snip.WriteText(")") }