golang.org/x/tools/gopls@v0.15.3/internal/golang/completion/snippet/snippet_builder.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 snippet implements the specification for the LSP snippet format. 6 // 7 // Snippets are "tab stop" templates returned as an optional attribute of LSP 8 // completion candidates. As the user presses tab, they cycle through a series of 9 // tab stops defined in the snippet. Each tab stop can optionally have placeholder 10 // text, which can be pre-selected by editors. For a full description of syntax 11 // and features, see "Snippet Syntax" at 12 // https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#textDocument_completion. 13 // 14 // A typical snippet looks like "foo(${1:i int}, ${2:s string})". 15 package snippet 16 17 import ( 18 "fmt" 19 "strings" 20 ) 21 22 // A Builder is used to build an LSP snippet piecemeal. 23 // The zero value is ready to use. Do not copy a non-zero Builder. 24 type Builder struct { 25 // currentTabStop is the index of the previous tab stop. The 26 // next tab stop will be currentTabStop+1. 27 currentTabStop int 28 sb strings.Builder 29 } 30 31 // Escape characters defined in https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#textDocument_completion under "Grammar". 32 var replacer = strings.NewReplacer( 33 `\`, `\\`, 34 `}`, `\}`, 35 `$`, `\$`, 36 ) 37 38 func (b *Builder) WriteText(s string) { 39 replacer.WriteString(&b.sb, s) 40 } 41 42 func (b *Builder) PrependText(s string) { 43 rawSnip := b.String() 44 b.sb.Reset() 45 b.WriteText(s) 46 b.sb.WriteString(rawSnip) 47 } 48 49 func (b *Builder) Write(data []byte) (int, error) { 50 return b.sb.Write(data) 51 } 52 53 // WritePlaceholder writes a tab stop and placeholder value to the Builder. 54 // The callback style allows for creating nested placeholders. To write an 55 // empty tab stop, provide a nil callback. 56 func (b *Builder) WritePlaceholder(fn func(*Builder)) { 57 fmt.Fprintf(&b.sb, "${%d:", b.nextTabStop()) 58 if fn != nil { 59 fn(b) 60 } 61 b.sb.WriteByte('}') 62 } 63 64 // WriteFinalTabstop marks where cursor ends up after the user has 65 // cycled through all the normal tab stops. It defaults to the 66 // character after the snippet. 67 func (b *Builder) WriteFinalTabstop() { 68 fmt.Fprint(&b.sb, "$0") 69 } 70 71 // In addition to '\', '}', and '$', snippet choices also use '|' and ',' as 72 // meta characters, so they must be escaped within the choices. 73 var choiceReplacer = strings.NewReplacer( 74 `\`, `\\`, 75 `}`, `\}`, 76 `$`, `\$`, 77 `|`, `\|`, 78 `,`, `\,`, 79 ) 80 81 // WriteChoice writes a tab stop and list of text choices to the Builder. 82 // The user's editor will prompt the user to choose one of the choices. 83 func (b *Builder) WriteChoice(choices []string) { 84 fmt.Fprintf(&b.sb, "${%d|", b.nextTabStop()) 85 for i, c := range choices { 86 if i != 0 { 87 b.sb.WriteByte(',') 88 } 89 choiceReplacer.WriteString(&b.sb, c) 90 } 91 b.sb.WriteString("|}") 92 } 93 94 // String returns the built snippet string. 95 func (b *Builder) String() string { 96 return b.sb.String() 97 } 98 99 // Clone returns a copy of b. 100 func (b *Builder) Clone() *Builder { 101 var clone Builder 102 clone.sb.WriteString(b.String()) 103 return &clone 104 } 105 106 // nextTabStop returns the next tab stop index for a new placeholder. 107 func (b *Builder) nextTabStop() int { 108 // Tab stops start from 1, so increment before returning. 109 b.currentTabStop++ 110 return b.currentTabStop 111 }