@@ -68,13 +68,18 @@ type postfixTmplArgs struct {
6868 // Type is the type of "foo.bar" in "foo.bar.print!".
6969 Type types.Type
7070
71+ // FuncResult are results of the enclosed function
72+ FuncResults []* types.Var
73+
74+ sel * ast.SelectorExpr
7175 scope * types.Scope
7276 snip snippet.Builder
7377 importIfNeeded func (pkgPath string , scope * types.Scope ) (name string , edits []protocol.TextEdit , err error )
7478 edits []protocol.TextEdit
7579 qf types.Qualifier
7680 varNames map [string ]bool
7781 placeholders bool
82+ currentTabStop int
7883}
7984
8085var postfixTmpls = []postfixTmpl {{
@@ -250,26 +255,119 @@ if {{.X}} != nil {
250255 body : `{{if (eq .Kind "slice" "map" "array" "chan") -}}
251256len({{.X}})
252257{{- 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}}` ,
253339}}
254340
255341// Cursor indicates where the client's cursor should end up after the
256342// snippet is done.
257343func (a * postfixTmplArgs ) Cursor () string {
258- a .snip .WriteFinalTabstop ()
259- return ""
344+ return "$0"
260345}
261346
262- // Placeholder indicate a tab stops with the placeholder string, the order
347+ // Placeholder indicate a tab stop with the placeholder string, the order
263348// of tab stops is the same as the order of invocation
264- func (a * postfixTmplArgs ) Placeholder (s string ) string {
265- if a .placeholders {
266- a .snip .WritePlaceholder (func (b * snippet.Builder ) {
267- b .WriteText (s )
268- })
269- } else {
270- a .snip .WritePlaceholder (nil )
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 = ""
271369 }
272- return ""
370+ return fmt . Sprintf ( "${%d:%s}" , tabStop , placeholder )
273371}
274372
275373// Import makes sure the package corresponding to path is imported,
@@ -309,7 +407,7 @@ func (a *postfixTmplArgs) KeyType() types.Type {
309407 return a .Type .Underlying ().(* types.Map ).Key ()
310408}
311409
312- // Tuple returns the tuple result vars if X is a call expression .
410+ // Tuple returns the tuple result vars if the type of X is tuple .
313411func (a * postfixTmplArgs ) Tuple () []* types.Var {
314412 tuple , _ := a .Type .(* types.Tuple )
315413 if tuple == nil {
@@ -323,6 +421,18 @@ func (a *postfixTmplArgs) Tuple() []*types.Var {
323421 return typs
324422}
325423
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+
326436// TypeName returns the textual representation of type t.
327437func (a * postfixTmplArgs ) TypeName (t types.Type ) (string , error ) {
328438 if t == nil || t == types .Typ [types .Invalid ] {
@@ -331,6 +441,16 @@ func (a *postfixTmplArgs) TypeName(t types.Type) (string, error) {
331441 return types .TypeString (t , a .qf ), nil
332442}
333443
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+
334454// VarName returns a suitable variable name for the type t. If t
335455// implements the error interface, "err" is used. If t is not a named
336456// type then nonNamedDefault is used. Otherwise a name is made by
@@ -417,6 +537,17 @@ func (c *completer) addPostfixSnippetCandidates(ctx context.Context, sel *ast.Se
417537 }
418538 }
419539
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+
420551 scope := c .pkg .GetTypes ().Scope ().Innermost (c .pos )
421552 if scope == nil {
422553 return
@@ -455,6 +586,8 @@ func (c *completer) addPostfixSnippetCandidates(ctx context.Context, sel *ast.Se
455586 StmtOK : stmtOK ,
456587 Obj : exprObj (c .pkg .GetTypesInfo (), sel .X ),
457588 Type : selType ,
589+ FuncResults : funcResults ,
590+ sel : sel ,
458591 qf : c .qf ,
459592 importIfNeeded : c .importIfNeeded ,
460593 scope : scope ,
@@ -497,7 +630,9 @@ func initPostfixRules() {
497630 var idx int
498631 for _ , rule := range postfixTmpls {
499632 var err error
500- rule .tmpl , err = template .New ("postfix_snippet" ).Parse (rule .body )
633+ rule .tmpl , err = template .New ("postfix_snippet" ).Funcs (template.FuncMap {
634+ "inc" : inc ,
635+ }).Parse (rule .body )
501636 if err != nil {
502637 log .Panicf ("error parsing postfix snippet template: %v" , err )
503638 }
@@ -508,6 +643,10 @@ func initPostfixRules() {
508643 })
509644}
510645
646+ func inc (i int ) int {
647+ return i + 1
648+ }
649+
511650// importIfNeeded returns the package identifier and any necessary
512651// edits to import package pkgPath.
513652func (c * completer ) importIfNeeded (pkgPath string , scope * types.Scope ) (string , []protocol.TextEdit , error ) {
0 commit comments