1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
13 "golang.org/x/tools/internal/event"
14 "golang.org/x/tools/internal/lsp/debug/tag"
15 "golang.org/x/tools/internal/lsp/protocol"
16 "golang.org/x/tools/internal/lsp/source"
17 "golang.org/x/tools/internal/lsp/source/completion"
18 "golang.org/x/tools/internal/span"
21 func (s *Server) completion(ctx context.Context, params *protocol.CompletionParams) (*protocol.CompletionList, error) {
22 snapshot, fh, ok, release, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.UnknownKind)
27 var candidates []completion.CompletionItem
28 var surrounding *completion.Selection
31 candidates, surrounding, err = completion.Completion(ctx, snapshot, fh, params.Position, params.Context)
33 candidates, surrounding = nil, nil
36 event.Error(ctx, "no completions found", err, tag.Position.Of(params.Position))
38 if candidates == nil {
39 return &protocol.CompletionList{
41 Items: []protocol.CompletionItem{},
44 // We might need to adjust the position to account for the prefix.
45 rng, err := surrounding.Range()
50 // internal/span treats end of file as the beginning of the next line, even
51 // when it's not newline-terminated. We correct for that behaviour here if
52 // end of file is not newline-terminated. See golang/go#41029.
57 numLines := len(bytes.Split(src, []byte("\n")))
58 tok := snapshot.FileSet().File(surrounding.Start())
59 eof := tok.Pos(tok.Size())
61 // For newline-terminated files, the line count reported by go/token should
62 // be lower than the actual number of lines we see when splitting by \n. If
63 // they're the same, the file isn't newline-terminated.
64 if tok.Size() > 0 && tok.LineCount() == numLines {
65 // Get the span for the last character in the file-1. This is
66 // technically incorrect, but will get span to point to the previous
68 spn, err := span.NewRange(snapshot.FileSet(), eof-1, eof-1).Span()
72 m := &protocol.ColumnMapper{
74 Converter: span.NewContentConverter(fh.URI().Filename(), src),
77 eofRng, err := m.Range(spn)
81 // Instead of using the computed range, correct for our earlier
82 // position adjustment by adding 1 to the column, not the line number.
83 pos := protocol.Position{
84 Line: eofRng.Start.Line,
85 Character: eofRng.Start.Character + 1,
87 if surrounding.Start() >= eof {
90 if surrounding.End() >= eof {
95 // When using deep completions/fuzzy matching, report results as incomplete so
96 // client fetches updated completions after every key stroke.
97 options := snapshot.View().Options()
98 incompleteResults := options.DeepCompletion || options.Matcher == source.Fuzzy
100 items := toProtocolCompletionItems(candidates, rng, options)
102 return &protocol.CompletionList{
103 IsIncomplete: incompleteResults,
108 func toProtocolCompletionItems(candidates []completion.CompletionItem, rng protocol.Range, options *source.Options) []protocol.CompletionItem {
110 items = make([]protocol.CompletionItem, 0, len(candidates))
111 numDeepCompletionsSeen int
113 for i, candidate := range candidates {
114 // Limit the number of deep completions to not overwhelm the user in cases
115 // with dozens of deep completion matches.
116 if candidate.Depth > 0 {
117 if !options.DeepCompletion {
120 if numDeepCompletionsSeen >= completion.MaxDeepCompletions {
123 numDeepCompletionsSeen++
125 insertText := candidate.InsertText
126 if options.InsertTextFormat == protocol.SnippetTextFormat {
127 insertText = candidate.Snippet()
130 // This can happen if the client has snippets disabled but the
131 // candidate only supports snippet insertion.
132 if insertText == "" {
136 item := protocol.CompletionItem{
137 Label: candidate.Label,
138 Detail: candidate.Detail,
139 Kind: candidate.Kind,
140 TextEdit: &protocol.TextEdit{
144 InsertTextFormat: options.InsertTextFormat,
145 AdditionalTextEdits: candidate.AdditionalTextEdits,
146 // This is a hack so that the client sorts completion results in the order
147 // according to their score. This can be removed upon the resolution of
148 // https://github.com/Microsoft/language-server-protocol/issues/348.
149 SortText: fmt.Sprintf("%05d", i),
151 // Trim operators (VSCode doesn't like weird characters in
153 FilterText: strings.TrimLeft(candidate.InsertText, "&*"),
156 Documentation: candidate.Documentation,
158 items = append(items, item)