1
0
mirror of https://github.com/golang/go synced 2024-10-01 10:38:33 -06:00
go/internal/lsp/snippet/snippet_builder.go
Muir Manders dd2b5c81c5 internal/lsp: simplify snippet config/generation
I moved the "usePlaceholders" config field on to CompletionOptions.
This way the completion code generates a single snippet with a little
conditional logic based on the "WantPlaceholders" option instead of
juggling the generation of two almost identical "plain" and
"placeholder" snippets at the same time. It also reduces the work done
generating completion candidates a little.

I also made a minor tweak to the snippet builder where empty
placeholders are now always represented as e.g "${1:}" instead of
"${1}" or "${1:}", depending on if you passed a callback to
WritePlaceholder() or not.

Change-Id: Ib84cc0cd729a11b9e13ad3ac4b6fd2d82460acd5
Reviewed-on: https://go-review.googlesource.com/c/tools/+/193697
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2019-09-10 04:45:52 +00:00

87 lines
2.6 KiB
Go

// 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 snippet implements the specification for the LSP snippet format.
//
// Snippets are "tab stop" templates returned as an optional attribute of LSP
// completion candidates. As the user presses tab, they cycle through a series of
// tab stops defined in the snippet. Each tab stop can optionally have placeholder
// text, which can be pre-selected by editors. For a full description of syntax
// and features, see "Snippet Syntax" at
// https://microsoft.github.io/language-server-protocol/specification#textDocument_completion.
//
// A typical snippet looks like "foo(${1:i int}, ${2:s string})".
package snippet
import (
"fmt"
"strings"
)
// A Builder is used to build an LSP snippet piecemeal.
// The zero value is ready to use. Do not copy a non-zero Builder.
type Builder struct {
// currentTabStop is the index of the previous tab stop. The
// next tab stop will be currentTabStop+1.
currentTabStop int
sb strings.Builder
}
// Escape characters defined in https://microsoft.github.io/language-server-protocol/specification#textDocument_completion under "Grammar".
var replacer = strings.NewReplacer(
`\`, `\\`,
`}`, `\}`,
`$`, `\$`,
)
func (b *Builder) WriteText(s string) {
replacer.WriteString(&b.sb, s)
}
// WritePlaceholder writes a tab stop and placeholder value to the Builder.
// The callback style allows for creating nested placeholders. To write an
// empty tab stop, provide a nil callback.
func (b *Builder) WritePlaceholder(fn func(*Builder)) {
fmt.Fprintf(&b.sb, "${%d:", b.nextTabStop())
if fn != nil {
fn(b)
}
b.sb.WriteByte('}')
}
// In addition to '\', '}', and '$', snippet choices also use '|' and ',' as
// meta characters, so they must be escaped within the choices.
var choiceReplacer = strings.NewReplacer(
`\`, `\\`,
`}`, `\}`,
`$`, `\$`,
`|`, `\|`,
`,`, `\,`,
)
// WriteChoice writes a tab stop and list of text choices to the Builder.
// The user's editor will prompt the user to choose one of the choices.
func (b *Builder) WriteChoice(choices []string) {
fmt.Fprintf(&b.sb, "${%d|", b.nextTabStop())
for i, c := range choices {
if i != 0 {
b.sb.WriteByte(',')
}
choiceReplacer.WriteString(&b.sb, c)
}
b.sb.WriteString("|}")
}
// String returns the built snippet string.
func (b *Builder) String() string {
return b.sb.String()
}
// nextTabStop returns the next tab stop index for a new placeholder.
func (b *Builder) nextTabStop() int {
// Tab stops start from 1, so increment before returning.
b.currentTabStop++
return b.currentTabStop
}