mirror of
https://github.com/golang/go
synced 2024-11-18 11:04:42 -07:00
acefd226e2
Completion is slowly becoming a large part of internal/lsp/source and it makes sense to move to its own seperate package inside source to make future refactors easier. As a part of this change, any unexported members from source required by completion are now exported. Util functions only required by completion are moved from internal/lsp/source/util.go to internal/lsp/source/completion/util.go. Change-Id: I6b7405ec598c910545e649bb0e6aa02ffa653b38 Reviewed-on: https://go-review.googlesource.com/c/tools/+/253178 Run-TryBot: Danish Dua <danishdua@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Heschi Kreinick <heschi@google.com>
161 lines
5.0 KiB
Go
161 lines
5.0 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"
|
|
"strings"
|
|
|
|
"golang.org/x/tools/internal/event"
|
|
"golang.org/x/tools/internal/lsp/debug/tag"
|
|
"golang.org/x/tools/internal/lsp/protocol"
|
|
"golang.org/x/tools/internal/lsp/source"
|
|
"golang.org/x/tools/internal/lsp/source/completion"
|
|
"golang.org/x/tools/internal/span"
|
|
)
|
|
|
|
func (s *Server) completion(ctx context.Context, params *protocol.CompletionParams) (*protocol.CompletionList, error) {
|
|
snapshot, fh, ok, release, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.UnknownKind)
|
|
defer release()
|
|
if !ok {
|
|
return nil, err
|
|
}
|
|
var candidates []completion.CompletionItem
|
|
var surrounding *completion.Selection
|
|
switch fh.Kind() {
|
|
case source.Go:
|
|
candidates, surrounding, err = completion.Completion(ctx, snapshot, fh, params.Position, params.Context.TriggerCharacter)
|
|
case source.Mod:
|
|
candidates, surrounding = nil, nil
|
|
}
|
|
if err != nil {
|
|
event.Error(ctx, "no completions found", err, tag.Position.Of(params.Position))
|
|
}
|
|
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
|
|
}
|
|
|
|
// internal/span treats end of file as the beginning of the next line, even
|
|
// when it's not newline-terminated. We correct for that behaviour here if
|
|
// end of file is not newline-terminated. See golang/go#41029.
|
|
src, err := fh.Read()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Get the actual number of lines in source.
|
|
numLines := len(strings.Split(string(src), "\n"))
|
|
|
|
tok := snapshot.FileSet().File(surrounding.Start())
|
|
endOfFile := tok.Pos(tok.Size())
|
|
|
|
// For newline-terminated files, the line count reported by go/token should
|
|
// be lower than the actual number of lines we see when splitting by \n. If
|
|
// they're the same, the file isn't newline-terminated.
|
|
if numLines == tok.LineCount() && tok.Size() != 0 {
|
|
// Get span for character before end of file to bypass span's treatment of end
|
|
// of file. We correct for this later.
|
|
spn, err := span.NewRange(snapshot.FileSet(), endOfFile-1, endOfFile-1).Span()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
m := &protocol.ColumnMapper{
|
|
URI: fh.URI(),
|
|
Converter: span.NewContentConverter(fh.URI().Filename(), []byte(src)),
|
|
Content: []byte(src),
|
|
}
|
|
eofRng, err := m.Range(spn)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
eofPosition := protocol.Position{
|
|
Line: eofRng.Start.Line,
|
|
// Correct for using endOfFile - 1 earlier.
|
|
Character: eofRng.Start.Character + 1,
|
|
}
|
|
if surrounding.Start() == endOfFile {
|
|
rng.Start = eofPosition
|
|
}
|
|
if surrounding.End() == endOfFile {
|
|
rng.End = eofPosition
|
|
}
|
|
}
|
|
|
|
// When using deep completions/fuzzy matching, report results as incomplete so
|
|
// client fetches updated completions after every key stroke.
|
|
options := snapshot.View().Options()
|
|
incompleteResults := options.DeepCompletion || options.Matcher == source.Fuzzy
|
|
|
|
items := toProtocolCompletionItems(candidates, rng, options)
|
|
|
|
return &protocol.CompletionList{
|
|
IsIncomplete: incompleteResults,
|
|
Items: items,
|
|
}, nil
|
|
}
|
|
|
|
func toProtocolCompletionItems(candidates []completion.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.DeepCompletion {
|
|
continue
|
|
}
|
|
if numDeepCompletionsSeen >= completion.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),
|
|
|
|
// Trim operators (VSCode doesn't like weird characters in
|
|
// filterText).
|
|
FilterText: strings.TrimLeft(candidate.InsertText, "&*"),
|
|
|
|
Preselect: i == 0,
|
|
Documentation: candidate.Documentation,
|
|
}
|
|
items = append(items, item)
|
|
}
|
|
return items
|
|
}
|