2018-11-07 13:21:31 -07:00
|
|
|
// 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.
|
|
|
|
|
2018-11-05 19:23:02 -07:00
|
|
|
package lsp
|
|
|
|
|
|
|
|
import (
|
2019-04-04 17:33:08 -06:00
|
|
|
"context"
|
2018-11-20 14:05:10 -07:00
|
|
|
"fmt"
|
2018-11-07 18:57:08 -07:00
|
|
|
"sort"
|
2018-11-20 14:05:10 -07:00
|
|
|
"strings"
|
2018-11-05 19:23:02 -07:00
|
|
|
|
|
|
|
"golang.org/x/tools/internal/lsp/protocol"
|
|
|
|
"golang.org/x/tools/internal/lsp/source"
|
2019-04-04 17:33:08 -06:00
|
|
|
"golang.org/x/tools/internal/span"
|
2018-11-05 19:23:02 -07:00
|
|
|
)
|
|
|
|
|
2019-04-04 17:33:08 -06:00
|
|
|
func (s *Server) completion(ctx context.Context, params *protocol.CompletionParams) (*protocol.CompletionList, error) {
|
|
|
|
uri := span.NewURI(params.TextDocument.URI)
|
2019-05-15 10:24:49 -06:00
|
|
|
view := s.session.ViewOf(uri)
|
2019-05-03 22:04:18 -06:00
|
|
|
f, m, err := getGoFile(ctx, view, uri)
|
2019-04-04 17:33:08 -06:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
spn, err := m.PointSpan(params.Position)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
rng, err := spn.Range(m.Converter)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
items, prefix, err := source.Completion(ctx, f, rng.Start)
|
|
|
|
if err != nil {
|
2019-05-15 10:24:49 -06:00
|
|
|
s.session.Logger().Infof(ctx, "no completions found for %s:%v:%v: %v", uri, int(params.Position.Line), int(params.Position.Character), err)
|
2019-05-13 14:49:29 -06:00
|
|
|
}
|
|
|
|
// We might need to adjust the position to account for the prefix.
|
2019-05-16 13:57:51 -06:00
|
|
|
prefixRng := protocol.Range{
|
|
|
|
Start: params.Position,
|
|
|
|
End: params.Position,
|
|
|
|
}
|
2019-05-13 14:49:29 -06:00
|
|
|
if prefix.Pos().IsValid() {
|
|
|
|
spn, err := span.NewRange(view.FileSet(), prefix.Pos(), 0).Span()
|
|
|
|
if err != nil {
|
2019-05-15 10:24:49 -06:00
|
|
|
s.session.Logger().Infof(ctx, "failed to get span for prefix position: %s:%v:%v: %v", uri, int(params.Position.Line), int(params.Position.Character), err)
|
2019-05-13 14:49:29 -06:00
|
|
|
}
|
|
|
|
if prefixPos, err := m.Position(spn.Start()); err == nil {
|
2019-05-16 13:57:51 -06:00
|
|
|
prefixRng.End = prefixPos
|
2019-05-13 14:49:29 -06:00
|
|
|
} else {
|
2019-05-15 10:24:49 -06:00
|
|
|
s.session.Logger().Infof(ctx, "failed to convert prefix position: %s:%v:%v: %v", uri, int(params.Position.Line), int(params.Position.Character), err)
|
2019-05-13 14:49:29 -06:00
|
|
|
}
|
2019-04-04 17:33:08 -06:00
|
|
|
}
|
|
|
|
return &protocol.CompletionList{
|
|
|
|
IsIncomplete: false,
|
2019-05-16 13:57:51 -06:00
|
|
|
Items: toProtocolCompletionItems(items, prefix.Content(), prefixRng, s.insertTextFormat, s.usePlaceholders),
|
2019-04-04 17:33:08 -06:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2019-05-16 13:57:51 -06:00
|
|
|
func toProtocolCompletionItems(candidates []source.CompletionItem, prefix string, rng protocol.Range, insertTextFormat protocol.InsertTextFormat, usePlaceholders bool) []protocol.CompletionItem {
|
2018-12-10 22:52:31 -07:00
|
|
|
sort.SliceStable(candidates, func(i, j int) bool {
|
|
|
|
return candidates[i].Score > candidates[j].Score
|
|
|
|
})
|
2019-05-13 14:49:29 -06:00
|
|
|
items := make([]protocol.CompletionItem, 0, len(candidates))
|
2018-12-10 22:52:31 -07:00
|
|
|
for i, candidate := range candidates {
|
2019-04-04 17:33:08 -06:00
|
|
|
// Match against the label.
|
2018-12-10 22:52:31 -07:00
|
|
|
if !strings.HasPrefix(candidate.Label, prefix) {
|
2018-11-21 21:51:01 -07:00
|
|
|
continue
|
|
|
|
}
|
2019-04-29 17:47:54 -06:00
|
|
|
insertText := candidate.InsertText
|
2019-04-28 21:19:54 -06:00
|
|
|
if insertTextFormat == protocol.SnippetTextFormat {
|
2019-05-14 12:15:18 -06:00
|
|
|
insertText = candidate.Snippet(usePlaceholders)
|
2019-04-28 21:19:54 -06:00
|
|
|
}
|
2018-12-10 22:52:31 -07:00
|
|
|
item := protocol.CompletionItem{
|
2019-03-13 12:25:56 -06:00
|
|
|
Label: candidate.Label,
|
|
|
|
Detail: candidate.Detail,
|
|
|
|
Kind: toProtocolCompletionItemKind(candidate.Kind),
|
2018-11-21 21:51:01 -07:00
|
|
|
TextEdit: &protocol.TextEdit{
|
|
|
|
NewText: insertText,
|
2019-05-16 13:57:51 -06:00
|
|
|
Range: rng,
|
2018-11-21 21:51:01 -07:00
|
|
|
},
|
2019-03-29 16:16:43 -06:00
|
|
|
InsertTextFormat: insertTextFormat,
|
2018-11-21 21:51:01 -07:00
|
|
|
// 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.
|
2019-03-13 18:59:59 -06:00
|
|
|
SortText: fmt.Sprintf("%05d", i),
|
2019-05-13 14:49:29 -06:00
|
|
|
FilterText: candidate.InsertText,
|
2019-03-13 19:41:54 -06:00
|
|
|
Preselect: i == 0,
|
2018-11-20 16:18:33 -07:00
|
|
|
}
|
2019-04-17 12:31:17 -06:00
|
|
|
// Trigger signature help for any function or method completion.
|
|
|
|
// This is helpful even if a function does not have parameters,
|
|
|
|
// since we show return types as well.
|
|
|
|
switch item.Kind {
|
|
|
|
case protocol.FunctionCompletion, protocol.MethodCompletion:
|
|
|
|
item.Command = &protocol.Command{
|
|
|
|
Command: "editor.action.triggerParameterHints",
|
|
|
|
}
|
|
|
|
}
|
2018-12-10 22:52:31 -07:00
|
|
|
items = append(items, item)
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|
2018-12-10 22:52:31 -07:00
|
|
|
return items
|
2018-11-07 18:57:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
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 // ??
|
2018-11-05 19:23:02 -07:00
|
|
|
default:
|
2018-11-07 18:57:08 -07:00
|
|
|
return protocol.TextCompletion
|
2018-11-05 19:23:02 -07:00
|
|
|
}
|
2018-11-20 14:05:10 -07:00
|
|
|
}
|