mirror of
https://github.com/golang/go
synced 2024-11-18 23:24:39 -07:00
2208e1677e
This change eliminates the extra step of calling GetFile on the view and getting the FileHandle from the snapshot. It also eliminiates the redundant source.File type. Follow up changes will clean up the file kind handling, since it still exists on the fileBase type. Change-Id: I635ab8632821b36e062be5151eaab425a5698f60 Reviewed-on: https://go-review.googlesource.com/c/tools/+/211778 Run-TryBot: Rebecca Stambler <rstambler@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Heschi Kreinick <heschi@google.com>
143 lines
4.5 KiB
Go
143 lines
4.5 KiB
Go
// 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 (
|
|
"context"
|
|
"fmt"
|
|
"sort"
|
|
|
|
"golang.org/x/tools/internal/lsp/protocol"
|
|
"golang.org/x/tools/internal/lsp/source"
|
|
"golang.org/x/tools/internal/span"
|
|
"golang.org/x/tools/internal/telemetry/log"
|
|
"golang.org/x/tools/internal/telemetry/tag"
|
|
)
|
|
|
|
func (s *Server) completion(ctx context.Context, params *protocol.CompletionParams) (*protocol.CompletionList, error) {
|
|
uri := span.NewURI(params.TextDocument.URI)
|
|
view, err := s.session.ViewOf(uri)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
snapshot := view.Snapshot()
|
|
options := view.Options()
|
|
fh, err := snapshot.GetFile(ctx, uri)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var candidates []source.CompletionItem
|
|
var surrounding *source.Selection
|
|
switch fh.Identity().Kind {
|
|
case source.Go:
|
|
options.Completion.FullDocumentation = options.HoverKind == source.FullDocumentation
|
|
candidates, surrounding, err = source.Completion(ctx, snapshot, fh, params.Position, options.Completion)
|
|
case source.Mod:
|
|
candidates, surrounding = nil, nil
|
|
}
|
|
|
|
if err != nil {
|
|
log.Print(ctx, "no completions found", tag.Of("At", params.Position), tag.Of("Failure", err))
|
|
}
|
|
if candidates == nil {
|
|
return &protocol.CompletionList{
|
|
Items: []protocol.CompletionItem{},
|
|
}, nil
|
|
}
|
|
// We might need to adjust the position to account for the prefix.
|
|
rng, err := surrounding.Range()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Sort the candidates by score, since that is not supported by LSP yet.
|
|
sort.SliceStable(candidates, func(i, j int) bool {
|
|
return candidates[i].Score > candidates[j].Score
|
|
})
|
|
|
|
// When using deep completions/fuzzy matching, report results as incomplete so
|
|
// client fetches updated completions after every key stroke.
|
|
incompleteResults := options.Completion.Deep || options.Completion.FuzzyMatching
|
|
|
|
items := toProtocolCompletionItems(candidates, rng, options)
|
|
|
|
if incompleteResults && len(items) > 1 {
|
|
for i := range items[1:] {
|
|
// Give all the candidaites the same filterText to trick VSCode
|
|
// into not reordering our candidates. All the candidates will
|
|
// appear to be equally good matches, so VSCode's fuzzy
|
|
// matching/ranking just maintains the natural "sortText"
|
|
// ordering. We can only do this in tandem with
|
|
// "incompleteResults" since otherwise client side filtering is
|
|
// important.
|
|
items[i].FilterText = items[0].FilterText
|
|
}
|
|
}
|
|
|
|
return &protocol.CompletionList{
|
|
IsIncomplete: incompleteResults,
|
|
Items: items,
|
|
}, nil
|
|
}
|
|
|
|
func toProtocolCompletionItems(candidates []source.CompletionItem, rng protocol.Range, options source.Options) []protocol.CompletionItem {
|
|
var (
|
|
items = make([]protocol.CompletionItem, 0, len(candidates))
|
|
numDeepCompletionsSeen int
|
|
)
|
|
for i, candidate := range candidates {
|
|
// Limit the number of deep completions to not overwhelm the user in cases
|
|
// with dozens of deep completion matches.
|
|
if candidate.Depth > 0 {
|
|
if !options.Completion.Deep {
|
|
continue
|
|
}
|
|
if numDeepCompletionsSeen >= source.MaxDeepCompletions {
|
|
continue
|
|
}
|
|
numDeepCompletionsSeen++
|
|
}
|
|
insertText := candidate.InsertText
|
|
if options.InsertTextFormat == protocol.SnippetTextFormat {
|
|
insertText = candidate.Snippet()
|
|
}
|
|
|
|
// This can happen if the client has snippets disabled but the
|
|
// candidate only supports snippet insertion.
|
|
if insertText == "" {
|
|
continue
|
|
}
|
|
|
|
item := protocol.CompletionItem{
|
|
Label: candidate.Label,
|
|
Detail: candidate.Detail,
|
|
Kind: candidate.Kind,
|
|
TextEdit: &protocol.TextEdit{
|
|
NewText: insertText,
|
|
Range: rng,
|
|
},
|
|
InsertTextFormat: options.InsertTextFormat,
|
|
AdditionalTextEdits: candidate.AdditionalTextEdits,
|
|
// 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),
|
|
FilterText: candidate.InsertText,
|
|
Preselect: i == 0,
|
|
Documentation: candidate.Documentation,
|
|
}
|
|
// 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",
|
|
}
|
|
}
|
|
items = append(items, item)
|
|
}
|
|
return items
|
|
}
|