1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
|
// Copyright 2018 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 lsp
import (
"bytes"
"fmt"
"sort"
"strings"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/lsp/source"
)
func toProtocolCompletionItems(candidates []source.CompletionItem, prefix string, pos protocol.Position, snippetsSupported, signatureHelpEnabled bool) []protocol.CompletionItem {
insertTextFormat := protocol.PlainTextFormat
if snippetsSupported {
insertTextFormat = protocol.SnippetTextFormat
}
sort.SliceStable(candidates, func(i, j int) bool {
return candidates[i].Score > candidates[j].Score
})
var items []protocol.CompletionItem
for i, candidate := range candidates {
// Matching against the label.
if !strings.HasPrefix(candidate.Label, prefix) {
continue
}
insertText, triggerSignatureHelp := labelToProtocolSnippets(candidate.Label, candidate.Kind, insertTextFormat, signatureHelpEnabled)
if strings.HasPrefix(insertText, prefix) {
insertText = insertText[len(prefix):]
}
item := protocol.CompletionItem{
Label: candidate.Label,
Detail: candidate.Detail,
Kind: float64(toProtocolCompletionItemKind(candidate.Kind)),
InsertTextFormat: insertTextFormat,
TextEdit: &protocol.TextEdit{
NewText: insertText,
Range: protocol.Range{
Start: pos,
End: pos,
},
},
// InsertText is deprecated in favor of TextEdit.
InsertText: insertText,
// This is a hack so that the client sorts completion results in the order
// according to their score. This can be removed upon the resolution of
// https://github.com/Microsoft/language-server-protocol/issues/348.
SortText: fmt.Sprintf("%05d", i),
}
// If we are completing a function, we should trigger signature help if possible.
if triggerSignatureHelp && signatureHelpEnabled {
item.Command = &protocol.Command{
Command: "editor.action.triggerParameterHints",
}
}
items = append(items, item)
}
return items
}
func toProtocolCompletionItemKind(kind source.CompletionItemKind) protocol.CompletionItemKind {
switch kind {
case source.InterfaceCompletionItem:
return protocol.InterfaceCompletion
case source.StructCompletionItem:
return protocol.StructCompletion
case source.TypeCompletionItem:
return protocol.TypeParameterCompletion // ??
case source.ConstantCompletionItem:
return protocol.ConstantCompletion
case source.FieldCompletionItem:
return protocol.FieldCompletion
case source.ParameterCompletionItem, source.VariableCompletionItem:
return protocol.VariableCompletion
case source.FunctionCompletionItem:
return protocol.FunctionCompletion
case source.MethodCompletionItem:
return protocol.MethodCompletion
case source.PackageCompletionItem:
return protocol.ModuleCompletion // ??
default:
return protocol.TextCompletion
}
}
func labelToProtocolSnippets(label string, kind source.CompletionItemKind, insertTextFormat protocol.InsertTextFormat, signatureHelpEnabled bool) (string, bool) {
switch kind {
case source.ConstantCompletionItem:
// The label for constants is of the format "<identifier> = <value>".
// We should not insert the " = <value>" part of the label.
if i := strings.Index(label, " ="); i >= 0 {
return label[:i], false
}
case source.FunctionCompletionItem, source.MethodCompletionItem:
var trimmed, params string
if i := strings.Index(label, "("); i >= 0 {
trimmed = label[:i]
params = strings.Trim(label[i:], "()")
}
if params == "" || trimmed == "" {
return label, true
}
// Don't add parameters or parens for the plaintext insert format.
if insertTextFormat == protocol.PlainTextFormat {
return trimmed, true
}
// If we do have signature help enabled, the user can see parameters as
// they type in the function, so we just return empty parentheses.
if signatureHelpEnabled {
return trimmed + "($1)", true
}
// If signature help is not enabled, we should give the user parameters
// that they can tab through. The insert text format follows the
// specification defined by Microsoft for LSP. The "$", "}, and "\"
// characters should be escaped.
r := strings.NewReplacer(
`\`, `\\`,
`}`, `\}`,
`$`, `\$`,
)
b := bytes.NewBufferString(trimmed)
b.WriteByte('(')
for i, p := range strings.Split(params, ",") {
if i != 0 {
b.WriteString(", ")
}
fmt.Fprintf(b, "${%v:%v}", i+1, r.Replace(strings.Trim(p, " ")))
}
b.WriteByte(')')
return b.String(), false
}
return label, false
}
|