.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.1-0.20210319172145-bda8f5cee399 / internal / lsp / completion.go
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.
4
5 package lsp
6
7 import (
8         "bytes"
9         "context"
10         "fmt"
11         "strings"
12
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"
19 )
20
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)
23         defer release()
24         if !ok {
25                 return nil, err
26         }
27         var candidates []completion.CompletionItem
28         var surrounding *completion.Selection
29         switch fh.Kind() {
30         case source.Go:
31                 candidates, surrounding, err = completion.Completion(ctx, snapshot, fh, params.Position, params.Context)
32         case source.Mod:
33                 candidates, surrounding = nil, nil
34         }
35         if err != nil {
36                 event.Error(ctx, "no completions found", err, tag.Position.Of(params.Position))
37         }
38         if candidates == nil {
39                 return &protocol.CompletionList{
40                         IsIncomplete: true,
41                         Items:        []protocol.CompletionItem{},
42                 }, nil
43         }
44         // We might need to adjust the position to account for the prefix.
45         rng, err := surrounding.Range()
46         if err != nil {
47                 return nil, err
48         }
49
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.
53         src, err := fh.Read()
54         if err != nil {
55                 return nil, err
56         }
57         numLines := len(bytes.Split(src, []byte("\n")))
58         tok := snapshot.FileSet().File(surrounding.Start())
59         eof := tok.Pos(tok.Size())
60
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
67                 // line.
68                 spn, err := span.NewRange(snapshot.FileSet(), eof-1, eof-1).Span()
69                 if err != nil {
70                         return nil, err
71                 }
72                 m := &protocol.ColumnMapper{
73                         URI:       fh.URI(),
74                         Converter: span.NewContentConverter(fh.URI().Filename(), src),
75                         Content:   src,
76                 }
77                 eofRng, err := m.Range(spn)
78                 if err != nil {
79                         return nil, err
80                 }
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,
86                 }
87                 if surrounding.Start() >= eof {
88                         rng.Start = pos
89                 }
90                 if surrounding.End() >= eof {
91                         rng.End = pos
92                 }
93         }
94
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
99
100         items := toProtocolCompletionItems(candidates, rng, options)
101
102         return &protocol.CompletionList{
103                 IsIncomplete: incompleteResults,
104                 Items:        items,
105         }, nil
106 }
107
108 func toProtocolCompletionItems(candidates []completion.CompletionItem, rng protocol.Range, options *source.Options) []protocol.CompletionItem {
109         var (
110                 items                  = make([]protocol.CompletionItem, 0, len(candidates))
111                 numDeepCompletionsSeen int
112         )
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 {
118                                 continue
119                         }
120                         if numDeepCompletionsSeen >= completion.MaxDeepCompletions {
121                                 continue
122                         }
123                         numDeepCompletionsSeen++
124                 }
125                 insertText := candidate.InsertText
126                 if options.InsertTextFormat == protocol.SnippetTextFormat {
127                         insertText = candidate.Snippet()
128                 }
129
130                 // This can happen if the client has snippets disabled but the
131                 // candidate only supports snippet insertion.
132                 if insertText == "" {
133                         continue
134                 }
135
136                 item := protocol.CompletionItem{
137                         Label:  candidate.Label,
138                         Detail: candidate.Detail,
139                         Kind:   candidate.Kind,
140                         TextEdit: &protocol.TextEdit{
141                                 NewText: insertText,
142                                 Range:   rng,
143                         },
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),
150
151                         // Trim operators (VSCode doesn't like weird characters in
152                         // filterText).
153                         FilterText: strings.TrimLeft(candidate.InsertText, "&*"),
154
155                         Preselect:     i == 0,
156                         Documentation: candidate.Documentation,
157                 }
158                 items = append(items, item)
159         }
160         return items
161 }