@@ -68,13 +68,18 @@ type postfixTmplArgs struct {
68
68
// Type is the type of "foo.bar" in "foo.bar.print!".
69
69
Type types.Type
70
70
71
+ // FuncResult are results of the enclosed function
72
+ FuncResults []* types.Var
73
+
74
+ sel * ast.SelectorExpr
71
75
scope * types.Scope
72
76
snip snippet.Builder
73
77
importIfNeeded func (pkgPath string , scope * types.Scope ) (name string , edits []protocol.TextEdit , err error )
74
78
edits []protocol.TextEdit
75
79
qf types.Qualifier
76
80
varNames map [string ]bool
77
81
placeholders bool
82
+ currentTabStop int
78
83
}
79
84
80
85
var postfixTmpls = []postfixTmpl {{
@@ -250,26 +255,119 @@ if {{.X}} != nil {
250
255
body : `{{if (eq .Kind "slice" "map" "array" "chan") -}}
251
256
len({{.X}})
252
257
{{- 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}}` ,
253
339
}}
254
340
255
341
// Cursor indicates where the client's cursor should end up after the
256
342
// snippet is done.
257
343
func (a * postfixTmplArgs ) Cursor () string {
258
- a .snip .WriteFinalTabstop ()
259
- return ""
344
+ return "$0"
260
345
}
261
346
262
- // Placeholder indicate a tab stops with the placeholder string, the order
347
+ // Placeholder indicate a tab stop with the placeholder string, the order
263
348
// 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 = ""
271
369
}
272
- return ""
370
+ return fmt . Sprintf ( "${%d:%s}" , tabStop , placeholder )
273
371
}
274
372
275
373
// Import makes sure the package corresponding to path is imported,
@@ -309,7 +407,7 @@ func (a *postfixTmplArgs) KeyType() types.Type {
309
407
return a .Type .Underlying ().(* types.Map ).Key ()
310
408
}
311
409
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 .
313
411
func (a * postfixTmplArgs ) Tuple () []* types.Var {
314
412
tuple , _ := a .Type .(* types.Tuple )
315
413
if tuple == nil {
@@ -323,6 +421,18 @@ func (a *postfixTmplArgs) Tuple() []*types.Var {
323
421
return typs
324
422
}
325
423
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
+
326
436
// TypeName returns the textual representation of type t.
327
437
func (a * postfixTmplArgs ) TypeName (t types.Type ) (string , error ) {
328
438
if t == nil || t == types .Typ [types .Invalid ] {
@@ -331,6 +441,16 @@ func (a *postfixTmplArgs) TypeName(t types.Type) (string, error) {
331
441
return types .TypeString (t , a .qf ), nil
332
442
}
333
443
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
+
334
454
// VarName returns a suitable variable name for the type t. If t
335
455
// implements the error interface, "err" is used. If t is not a named
336
456
// 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
417
537
}
418
538
}
419
539
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
+
420
551
scope := c .pkg .GetTypes ().Scope ().Innermost (c .pos )
421
552
if scope == nil {
422
553
return
@@ -455,6 +586,8 @@ func (c *completer) addPostfixSnippetCandidates(ctx context.Context, sel *ast.Se
455
586
StmtOK : stmtOK ,
456
587
Obj : exprObj (c .pkg .GetTypesInfo (), sel .X ),
457
588
Type : selType ,
589
+ FuncResults : funcResults ,
590
+ sel : sel ,
458
591
qf : c .qf ,
459
592
importIfNeeded : c .importIfNeeded ,
460
593
scope : scope ,
@@ -497,7 +630,9 @@ func initPostfixRules() {
497
630
var idx int
498
631
for _ , rule := range postfixTmpls {
499
632
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 )
501
636
if err != nil {
502
637
log .Panicf ("error parsing postfix snippet template: %v" , err )
503
638
}
@@ -508,6 +643,10 @@ func initPostfixRules() {
508
643
})
509
644
}
510
645
646
+ func inc (i int ) int {
647
+ return i + 1
648
+ }
649
+
511
650
// importIfNeeded returns the package identifier and any necessary
512
651
// edits to import package pkgPath.
513
652
func (c * completer ) importIfNeeded (pkgPath string , scope * types.Scope ) (string , []protocol.TextEdit , error ) {
0 commit comments