golang.org/x/tools/gopls@v0.15.3/internal/server/completion.go (about) 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 server 6 7 import ( 8 "context" 9 "fmt" 10 "strings" 11 12 "golang.org/x/tools/gopls/internal/file" 13 "golang.org/x/tools/gopls/internal/golang" 14 "golang.org/x/tools/gopls/internal/golang/completion" 15 "golang.org/x/tools/gopls/internal/protocol" 16 "golang.org/x/tools/gopls/internal/settings" 17 "golang.org/x/tools/gopls/internal/telemetry" 18 "golang.org/x/tools/gopls/internal/template" 19 "golang.org/x/tools/gopls/internal/work" 20 "golang.org/x/tools/internal/event" 21 "golang.org/x/tools/internal/event/tag" 22 ) 23 24 func (s *server) Completion(ctx context.Context, params *protocol.CompletionParams) (_ *protocol.CompletionList, rerr error) { 25 recordLatency := telemetry.StartLatencyTimer("completion") 26 defer func() { 27 recordLatency(ctx, rerr) 28 }() 29 30 ctx, done := event.Start(ctx, "lsp.Server.completion", tag.URI.Of(params.TextDocument.URI)) 31 defer done() 32 33 fh, snapshot, release, err := s.fileOf(ctx, params.TextDocument.URI) 34 if err != nil { 35 return nil, err 36 } 37 defer release() 38 39 var candidates []completion.CompletionItem 40 var surrounding *completion.Selection 41 switch snapshot.FileKind(fh) { 42 case file.Go: 43 candidates, surrounding, err = completion.Completion(ctx, snapshot, fh, params.Position, params.Context) 44 case file.Mod: 45 candidates, surrounding = nil, nil 46 case file.Work: 47 cl, err := work.Completion(ctx, snapshot, fh, params.Position) 48 if err != nil { 49 break 50 } 51 return cl, nil 52 case file.Tmpl: 53 var cl *protocol.CompletionList 54 cl, err = template.Completion(ctx, snapshot, fh, params.Position, params.Context) 55 if err != nil { 56 break // use common error handling, candidates==nil 57 } 58 return cl, nil 59 } 60 if err != nil { 61 event.Error(ctx, "no completions found", err, tag.Position.Of(params.Position)) 62 } 63 if candidates == nil { 64 return &protocol.CompletionList{ 65 IsIncomplete: true, 66 Items: []protocol.CompletionItem{}, 67 }, nil 68 } 69 70 rng, err := surrounding.Range() 71 if err != nil { 72 return nil, err 73 } 74 75 // When using deep completions/fuzzy matching, report results as incomplete so 76 // client fetches updated completions after every key stroke. 77 options := snapshot.Options() 78 incompleteResults := options.DeepCompletion || options.Matcher == settings.Fuzzy 79 80 items := toProtocolCompletionItems(candidates, rng, options) 81 82 return &protocol.CompletionList{ 83 IsIncomplete: incompleteResults, 84 Items: items, 85 }, nil 86 } 87 88 func toProtocolCompletionItems(candidates []completion.CompletionItem, rng protocol.Range, options *settings.Options) []protocol.CompletionItem { 89 var ( 90 items = make([]protocol.CompletionItem, 0, len(candidates)) 91 numDeepCompletionsSeen int 92 ) 93 for i, candidate := range candidates { 94 // Limit the number of deep completions to not overwhelm the user in cases 95 // with dozens of deep completion matches. 96 if candidate.Depth > 0 { 97 if !options.DeepCompletion { 98 continue 99 } 100 if numDeepCompletionsSeen >= completion.MaxDeepCompletions { 101 continue 102 } 103 numDeepCompletionsSeen++ 104 } 105 insertText := candidate.InsertText 106 if options.InsertTextFormat == protocol.SnippetTextFormat { 107 insertText = candidate.Snippet() 108 } 109 110 // This can happen if the client has snippets disabled but the 111 // candidate only supports snippet insertion. 112 if insertText == "" { 113 continue 114 } 115 116 doc := &protocol.Or_CompletionItem_documentation{ 117 Value: protocol.MarkupContent{ 118 Kind: protocol.Markdown, 119 Value: golang.CommentToMarkdown(candidate.Documentation, options), 120 }, 121 } 122 if options.PreferredContentFormat != protocol.Markdown { 123 doc.Value = candidate.Documentation 124 } 125 item := protocol.CompletionItem{ 126 Label: candidate.Label, 127 Detail: candidate.Detail, 128 Kind: candidate.Kind, 129 TextEdit: &protocol.TextEdit{ 130 NewText: insertText, 131 Range: rng, 132 }, 133 InsertTextFormat: &options.InsertTextFormat, 134 AdditionalTextEdits: candidate.AdditionalTextEdits, 135 // This is a hack so that the client sorts completion results in the order 136 // according to their score. This can be removed upon the resolution of 137 // https://github.com/Microsoft/language-server-protocol/issues/348. 138 SortText: fmt.Sprintf("%05d", i), 139 140 // Trim operators (VSCode doesn't like weird characters in 141 // filterText). 142 FilterText: strings.TrimLeft(candidate.InsertText, "&*"), 143 144 Preselect: i == 0, 145 Documentation: doc, 146 Tags: protocol.NonNilSlice(candidate.Tags), 147 Deprecated: candidate.Deprecated, 148 } 149 items = append(items, item) 150 } 151 return items 152 }