github.com/april1989/origin-go-tools@v0.0.32/internal/lsp/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 // WritePlaceholder writes a tab stop and placeholder value to the Builder. 43 // The callback style allows for creating nested placeholders. To write an 44 // empty tab stop, provide a nil callback. 45 func (b *Builder) WritePlaceholder(fn func(*Builder)) { 46 fmt.Fprintf(&b.sb, "${%d:", b.nextTabStop()) 47 if fn != nil { 48 fn(b) 49 } 50 b.sb.WriteByte('}') 51 } 52 53 // WriteFinalTabstop marks where cursor ends up after the user has 54 // cycled through all the normal tab stops. It defaults to the 55 // character after the snippet. 56 func (b *Builder) WriteFinalTabstop() { 57 fmt.Fprint(&b.sb, "$0") 58 } 59 60 // In addition to '\', '}', and '$', snippet choices also use '|' and ',' as 61 // meta characters, so they must be escaped within the choices. 62 var choiceReplacer = strings.NewReplacer( 63 `\`, `\\`, 64 `}`, `\}`, 65 `$`, `\$`, 66 `|`, `\|`, 67 `,`, `\,`, 68 ) 69 70 // WriteChoice writes a tab stop and list of text choices to the Builder. 71 // The user's editor will prompt the user to choose one of the choices. 72 func (b *Builder) WriteChoice(choices []string) { 73 fmt.Fprintf(&b.sb, "${%d|", b.nextTabStop()) 74 for i, c := range choices { 75 if i != 0 { 76 b.sb.WriteByte(',') 77 } 78 choiceReplacer.WriteString(&b.sb, c) 79 } 80 b.sb.WriteString("|}") 81 } 82 83 // String returns the built snippet string. 84 func (b *Builder) String() string { 85 return b.sb.String() 86 } 87 88 // nextTabStop returns the next tab stop index for a new placeholder. 89 func (b *Builder) nextTabStop() int { 90 // Tab stops start from 1, so increment before returning. 91 b.currentTabStop++ 92 return b.currentTabStop 93 }