2018-11-07 13:21:31 -07:00
// 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.
2018-11-05 19:23:02 -07:00
package lsp
import (
2019-04-04 17:33:08 -06:00
"context"
2018-11-20 14:05:10 -07:00
"fmt"
2018-11-07 18:57:08 -07:00
"sort"
2018-11-20 14:05:10 -07:00
"strings"
2018-11-05 19:23:02 -07:00
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/lsp/source"
2019-04-04 17:33:08 -06:00
"golang.org/x/tools/internal/span"
2018-11-05 19:23:02 -07:00
)
2019-04-04 17:33:08 -06:00
func ( s * Server ) completion ( ctx context . Context , params * protocol . CompletionParams ) ( * protocol . CompletionList , error ) {
uri := span . NewURI ( params . TextDocument . URI )
2019-05-15 10:24:49 -06:00
view := s . session . ViewOf ( uri )
2019-05-03 22:04:18 -06:00
f , m , err := getGoFile ( ctx , view , uri )
2019-04-04 17:33:08 -06:00
if err != nil {
return nil , err
}
spn , err := m . PointSpan ( params . Position )
if err != nil {
return nil , err
}
rng , err := spn . Range ( m . Converter )
if err != nil {
return nil , err
}
2019-06-27 11:50:01 -06:00
items , surrounding , err := source . Completion ( ctx , view , f , rng . Start , source . CompletionOptions {
DeepComplete : s . useDeepCompletions ,
} )
2019-04-04 17:33:08 -06:00
if err != nil {
2019-05-15 10:24:49 -06:00
s . session . Logger ( ) . Infof ( ctx , "no completions found for %s:%v:%v: %v" , uri , int ( params . Position . Line ) , int ( params . Position . Character ) , err )
2019-05-13 14:49:29 -06:00
}
// We might need to adjust the position to account for the prefix.
2019-05-17 11:45:59 -06:00
insertionRng := protocol . Range {
2019-05-16 13:57:51 -06:00
Start : params . Position ,
End : params . Position ,
}
2019-05-17 11:45:59 -06:00
var prefix string
if surrounding != nil {
prefix = surrounding . Prefix ( )
spn , err := surrounding . Range . Span ( )
2019-05-13 14:49:29 -06:00
if err != nil {
2019-05-17 11:45:59 -06:00
s . session . Logger ( ) . Infof ( ctx , "failed to get span for surrounding position: %s:%v:%v: %v" , uri , int ( params . Position . Line ) , int ( params . Position . Character ) , err )
2019-05-13 14:49:29 -06:00
} else {
2019-05-17 11:45:59 -06:00
rng , err := m . Range ( spn )
if err != nil {
s . session . Logger ( ) . Infof ( ctx , "failed to convert surrounding position: %s:%v:%v: %v" , uri , int ( params . Position . Line ) , int ( params . Position . Character ) , err )
} else {
insertionRng = rng
}
2019-05-13 14:49:29 -06:00
}
2019-04-04 17:33:08 -06:00
}
return & protocol . CompletionList {
IsIncomplete : false ,
2019-06-27 11:50:01 -06:00
Items : toProtocolCompletionItems ( items , prefix , insertionRng , s . insertTextFormat , s . usePlaceholders , s . useDeepCompletions ) ,
2019-04-04 17:33:08 -06:00
} , nil
}
2019-06-27 11:50:01 -06:00
// Limit deep completion results because in some cases there are too many
// to be useful.
const maxDeepCompletions = 3
func toProtocolCompletionItems ( candidates [ ] source . CompletionItem , prefix string , rng protocol . Range , insertTextFormat protocol . InsertTextFormat , usePlaceholders bool , useDeepCompletions bool ) [ ] protocol . CompletionItem {
2018-12-10 22:52:31 -07:00
sort . SliceStable ( candidates , func ( i , j int ) bool {
return candidates [ i ] . Score > candidates [ j ] . Score
} )
2019-06-27 11:50:01 -06:00
var (
items = make ( [ ] protocol . CompletionItem , 0 , len ( candidates ) )
numDeepCompletionsSeen int
)
2018-12-10 22:52:31 -07:00
for i , candidate := range candidates {
2019-04-04 17:33:08 -06:00
// Match against the label.
2018-12-10 22:52:31 -07:00
if ! strings . HasPrefix ( candidate . Label , prefix ) {
2018-11-21 21:51:01 -07:00
continue
}
2019-06-27 11:50:01 -06:00
// Limit the number of deep completions to not overwhelm the user in cases
// with dozens of deep completion matches.
if candidate . Depth > 0 {
if ! useDeepCompletions {
continue
}
if numDeepCompletionsSeen >= maxDeepCompletions {
continue
}
numDeepCompletionsSeen ++
}
2019-04-29 17:47:54 -06:00
insertText := candidate . InsertText
2019-04-28 21:19:54 -06:00
if insertTextFormat == protocol . SnippetTextFormat {
2019-05-14 12:15:18 -06:00
insertText = candidate . Snippet ( usePlaceholders )
2019-04-28 21:19:54 -06:00
}
2018-12-10 22:52:31 -07:00
item := protocol . CompletionItem {
2019-03-13 12:25:56 -06:00
Label : candidate . Label ,
Detail : candidate . Detail ,
Kind : toProtocolCompletionItemKind ( candidate . Kind ) ,
2018-11-21 21:51:01 -07:00
TextEdit : & protocol . TextEdit {
NewText : insertText ,
2019-05-16 13:57:51 -06:00
Range : rng ,
2018-11-21 21:51:01 -07:00
} ,
2019-03-29 16:16:43 -06:00
InsertTextFormat : insertTextFormat ,
2018-11-21 21:51:01 -07:00
// 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.
2019-03-13 18:59:59 -06:00
SortText : fmt . Sprintf ( "%05d" , i ) ,
2019-05-13 14:49:29 -06:00
FilterText : candidate . InsertText ,
2019-03-13 19:41:54 -06:00
Preselect : i == 0 ,
2018-11-20 16:18:33 -07:00
}
2019-04-17 12:31:17 -06:00
// 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" ,
}
}
2018-12-10 22:52:31 -07:00
items = append ( items , item )
2018-11-07 18:57:08 -07:00
}
2018-12-10 22:52:31 -07:00
return items
2018-11-07 18:57:08 -07:00
}
func toProtocolCompletionItemKind ( kind source . CompletionItemKind ) protocol . CompletionItemKind {
switch kind {
case source . InterfaceCompletionItem :
return protocol . InterfaceCompletion
case source . StructCompletionItem :
return protocol . StructCompletion
case source . TypeCompletionItem :
return protocol . TypeParameterCompletion // ??
case source . ConstantCompletionItem :
return protocol . ConstantCompletion
case source . FieldCompletionItem :
return protocol . FieldCompletion
case source . ParameterCompletionItem , source . VariableCompletionItem :
return protocol . VariableCompletion
case source . FunctionCompletionItem :
return protocol . FunctionCompletion
case source . MethodCompletionItem :
return protocol . MethodCompletion
case source . PackageCompletionItem :
return protocol . ModuleCompletion // ??
2018-11-05 19:23:02 -07:00
default :
2018-11-07 18:57:08 -07:00
return protocol . TextCompletion
2018-11-05 19:23:02 -07:00
}
2018-11-20 14:05:10 -07:00
}