1
0
mirror of https://github.com/golang/go synced 2024-11-18 15:04:44 -07:00

internal/lsp: provide option for case sensitive completion

In CL 192137 deep fuzzy matching was enabled by default. We also have
options independent options "deepCompletion" and "fuzzyMatching" to
control this. When fuzzy matching is disabled, case insensitive prefix
matching is used.

Provide an option, "caseSensitiveCompletion", which allows for case
sensitive prefix matching when fuzzy matching is disabled.

Change-Id: I17c8fa310b2ef79e36cc2f7303e98870690b5903
Reviewed-on: https://go-review.googlesource.com/c/tools/+/194757
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Paul Jolly 2019-09-26 12:59:06 +01:00 committed by Paul Jolly
parent ea99b82c7b
commit a8d5d34286
7 changed files with 174 additions and 85 deletions

View File

@ -53,6 +53,10 @@ func (r *runner) FuzzyCompletions(t *testing.T, data tests.FuzzyCompletions, ite
//TODO: add command line completions tests when it works
}
func (r *runner) CaseSensitiveCompletions(t *testing.T, data tests.CaseSensitiveCompletions, items tests.CompletionItems) {
//TODO: add command line completions tests when it works
}
func (r *runner) RankCompletions(t *testing.T, data tests.RankCompletions, items tests.CompletionItems) {
//TODO: add command line completions tests when it works
}

View File

@ -98,6 +98,21 @@ func (r *runner) FuzzyCompletions(t *testing.T, data tests.FuzzyCompletions, ite
}
}
func (r *runner) CaseSensitiveCompletions(t *testing.T, data tests.CaseSensitiveCompletions, items tests.CompletionItems) {
for src, test := range data {
got := r.callCompletion(t, src, source.CompletionOptions{
CaseSensitive: true,
})
if !strings.Contains(string(src.URI()), "builtins") {
got = tests.FilterBuiltins(got)
}
want := expected(t, test, items)
if msg := tests.DiffCompletionItems(want, got); msg != "" {
t.Errorf("%s: %s", src, msg)
}
}
}
func (r *runner) RankCompletions(t *testing.T, data tests.RankCompletions, items tests.CompletionItems) {
for src, test := range data {
got := r.callCompletion(t, src, source.CompletionOptions{

View File

@ -104,11 +104,21 @@ type matcher interface {
Score(candidateLabel string) (score float32)
}
// prefixMatcher implements case insensitive prefix matching.
// prefixMatcher implements case sensitive prefix matching.
type prefixMatcher string
func (pm prefixMatcher) Score(candidateLabel string) float32 {
if strings.HasPrefix(strings.ToLower(candidateLabel), string(pm)) {
if strings.HasPrefix(candidateLabel, string(pm)) {
return 1
}
return -1
}
// insensitivePrefixMatcher implements case insensitive prefix matching.
type insensitivePrefixMatcher string
func (ipm insensitivePrefixMatcher) Score(candidateLabel string) float32 {
if strings.HasPrefix(strings.ToLower(candidateLabel), string(ipm)) {
return 1
}
return -1
@ -233,8 +243,10 @@ func (c *completer) setSurrounding(ident *ast.Ident) {
if c.opts.FuzzyMatching {
c.matcher = fuzzy.NewMatcher(c.surrounding.Prefix(), fuzzy.Symbol)
} else if c.opts.CaseSensitive {
c.matcher = prefixMatcher(c.surrounding.Prefix())
} else {
c.matcher = prefixMatcher(strings.ToLower(c.surrounding.Prefix()))
c.matcher = insensitivePrefixMatcher(strings.ToLower(c.surrounding.Prefix()))
}
}

View File

@ -76,6 +76,7 @@ type Options struct {
type CompletionOptions struct {
Deep bool
FuzzyMatching bool
CaseSensitive bool
Unimported bool
Documentation bool
FullDocumentation bool
@ -200,6 +201,8 @@ func (o *Options) set(name string, value interface{}) OptionResult {
result.setBool(&o.Completion.Deep)
case "fuzzyMatching":
result.setBool(&o.Completion.FuzzyMatching)
case "caseSensitiveCompletion":
result.setBool(&o.Completion.CaseSensitive)
case "completeUnimported":
result.setBool(&o.Completion.Unimported)

View File

@ -210,6 +210,24 @@ func (r *runner) FuzzyCompletions(t *testing.T, data tests.FuzzyCompletions, ite
}
}
func (r *runner) CaseSensitiveCompletions(t *testing.T, data tests.CaseSensitiveCompletions, items tests.CompletionItems) {
for src, test := range data {
var want []protocol.CompletionItem
for _, pos := range test.CompletionItems {
want = append(want, tests.ToProtocolCompletionItem(*items[pos]))
}
_, list := r.callCompletion(t, src, source.CompletionOptions{
CaseSensitive: true,
})
if !strings.Contains(string(src.URI()), "builtins") {
list = tests.FilterBuiltins(list)
}
if diff := tests.DiffCompletionItems(want, list); diff != "" {
t.Errorf("%s: %s", src, diff)
}
}
}
func (r *runner) RankCompletions(t *testing.T, data tests.RankCompletions, items tests.CompletionItems) {
for src, test := range data {
var want []protocol.CompletionItem

View File

@ -0,0 +1,16 @@
// Copyright 2019 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 casesensitive
func _() {
var lower int //@item(lower, "lower", "int", "var")
var Upper int //@item(upper, "Upper", "int", "var")
l //@casesensitive(" //", lower)
U //@casesensitive(" //", upper)
L //@casesensitive(" //")
u //@casesensitive(" //")
}

View File

@ -29,26 +29,27 @@ import (
// We hardcode the expected number of test cases to ensure that all tests
// are being executed. If a test is added, this number must be changed.
const (
ExpectedCompletionsCount = 169
ExpectedCompletionSnippetCount = 36
ExpectedUnimportedCompletionsCount = 1
ExpectedDeepCompletionsCount = 5
ExpectedFuzzyCompletionsCount = 6
ExpectedRankedCompletionsCount = 1
ExpectedDiagnosticsCount = 21
ExpectedFormatCount = 6
ExpectedImportCount = 2
ExpectedSuggestedFixCount = 1
ExpectedDefinitionsCount = 39
ExpectedTypeDefinitionsCount = 2
ExpectedFoldingRangesCount = 2
ExpectedHighlightsCount = 2
ExpectedReferencesCount = 6
ExpectedRenamesCount = 20
ExpectedPrepareRenamesCount = 8
ExpectedSymbolsCount = 1
ExpectedSignaturesCount = 21
ExpectedLinksCount = 4
ExpectedCompletionsCount = 169
ExpectedCompletionSnippetCount = 36
ExpectedUnimportedCompletionsCount = 1
ExpectedDeepCompletionsCount = 5
ExpectedFuzzyCompletionsCount = 6
ExpectedCaseSensitiveCompletionsCount = 4
ExpectedRankedCompletionsCount = 1
ExpectedDiagnosticsCount = 21
ExpectedFormatCount = 6
ExpectedImportCount = 2
ExpectedSuggestedFixCount = 1
ExpectedDefinitionsCount = 39
ExpectedTypeDefinitionsCount = 2
ExpectedFoldingRangesCount = 2
ExpectedHighlightsCount = 2
ExpectedReferencesCount = 6
ExpectedRenamesCount = 20
ExpectedPrepareRenamesCount = 8
ExpectedSymbolsCount = 1
ExpectedSignaturesCount = 21
ExpectedLinksCount = 4
)
const (
@ -67,6 +68,7 @@ type CompletionSnippets map[span.Span]CompletionSnippet
type UnimportedCompletions map[span.Span]Completion
type DeepCompletions map[span.Span]Completion
type FuzzyCompletions map[span.Span]Completion
type CaseSensitiveCompletions map[span.Span]Completion
type RankCompletions map[span.Span]Completion
type FoldingRanges []span.Span
type Formats []span.Span
@ -83,29 +85,30 @@ type Signatures map[span.Span]*source.SignatureInformation
type Links map[span.URI][]Link
type Data struct {
Config packages.Config
Exported *packagestest.Exported
Diagnostics Diagnostics
CompletionItems CompletionItems
Completions Completions
CompletionSnippets CompletionSnippets
UnimportedCompletions UnimportedCompletions
DeepCompletions DeepCompletions
FuzzyCompletions FuzzyCompletions
RankCompletions RankCompletions
FoldingRanges FoldingRanges
Formats Formats
Imports Imports
SuggestedFixes SuggestedFixes
Definitions Definitions
Highlights Highlights
References References
Renames Renames
PrepareRenames PrepareRenames
Symbols Symbols
symbolsChildren SymbolsChildren
Signatures Signatures
Links Links
Config packages.Config
Exported *packagestest.Exported
Diagnostics Diagnostics
CompletionItems CompletionItems
Completions Completions
CompletionSnippets CompletionSnippets
UnimportedCompletions UnimportedCompletions
DeepCompletions DeepCompletions
FuzzyCompletions FuzzyCompletions
CaseSensitiveCompletions CaseSensitiveCompletions
RankCompletions RankCompletions
FoldingRanges FoldingRanges
Formats Formats
Imports Imports
SuggestedFixes SuggestedFixes
Definitions Definitions
Highlights Highlights
References References
Renames Renames
PrepareRenames PrepareRenames
Symbols Symbols
symbolsChildren SymbolsChildren
Signatures Signatures
Links Links
t testing.TB
fragments map[string]string
@ -123,6 +126,7 @@ type Tests interface {
UnimportedCompletions(*testing.T, UnimportedCompletions, CompletionItems)
DeepCompletions(*testing.T, DeepCompletions, CompletionItems)
FuzzyCompletions(*testing.T, FuzzyCompletions, CompletionItems)
CaseSensitiveCompletions(*testing.T, CaseSensitiveCompletions, CompletionItems)
RankCompletions(*testing.T, RankCompletions, CompletionItems)
FoldingRange(*testing.T, FoldingRanges)
Format(*testing.T, Formats)
@ -160,6 +164,9 @@ const (
// Fuzzy tests deep completion and fuzzy matching.
CompletionFuzzy
// CaseSensitive tests case sensitive completion
CompletionCaseSensitve
// CompletionRank candidates in test must be valid and in the right relative order.
CompletionRank
)
@ -209,23 +216,24 @@ func Load(t testing.TB, exporter packagestest.Exporter, dir string) *Data {
t.Helper()
data := &Data{
Diagnostics: make(Diagnostics),
CompletionItems: make(CompletionItems),
Completions: make(Completions),
CompletionSnippets: make(CompletionSnippets),
UnimportedCompletions: make(UnimportedCompletions),
DeepCompletions: make(DeepCompletions),
FuzzyCompletions: make(FuzzyCompletions),
RankCompletions: make(RankCompletions),
Definitions: make(Definitions),
Highlights: make(Highlights),
References: make(References),
Renames: make(Renames),
PrepareRenames: make(PrepareRenames),
Symbols: make(Symbols),
symbolsChildren: make(SymbolsChildren),
Signatures: make(Signatures),
Links: make(Links),
Diagnostics: make(Diagnostics),
CompletionItems: make(CompletionItems),
Completions: make(Completions),
CompletionSnippets: make(CompletionSnippets),
UnimportedCompletions: make(UnimportedCompletions),
DeepCompletions: make(DeepCompletions),
FuzzyCompletions: make(FuzzyCompletions),
RankCompletions: make(RankCompletions),
CaseSensitiveCompletions: make(CaseSensitiveCompletions),
Definitions: make(Definitions),
Highlights: make(Highlights),
References: make(References),
Renames: make(Renames),
PrepareRenames: make(PrepareRenames),
Symbols: make(Symbols),
symbolsChildren: make(SymbolsChildren),
Signatures: make(Signatures),
Links: make(Links),
t: t,
dir: dir,
@ -295,28 +303,29 @@ func Load(t testing.TB, exporter packagestest.Exporter, dir string) *Data {
// Collect any data that needs to be used by subsequent tests.
if err := data.Exported.Expect(map[string]interface{}{
"diag": data.collectDiagnostics,
"item": data.collectCompletionItems,
"complete": data.collectCompletions(CompletionDefault),
"unimported": data.collectCompletions(CompletionUnimported),
"deep": data.collectCompletions(CompletionDeep),
"fuzzy": data.collectCompletions(CompletionFuzzy),
"rank": data.collectCompletions(CompletionRank),
"snippet": data.collectCompletionSnippets,
"fold": data.collectFoldingRanges,
"format": data.collectFormats,
"import": data.collectImports,
"godef": data.collectDefinitions,
"typdef": data.collectTypeDefinitions,
"hover": data.collectHoverDefinitions,
"highlight": data.collectHighlights,
"refs": data.collectReferences,
"rename": data.collectRenames,
"prepare": data.collectPrepareRenames,
"symbol": data.collectSymbols,
"signature": data.collectSignatures,
"link": data.collectLinks,
"suggestedfix": data.collectSuggestedFixes,
"diag": data.collectDiagnostics,
"item": data.collectCompletionItems,
"complete": data.collectCompletions(CompletionDefault),
"unimported": data.collectCompletions(CompletionUnimported),
"deep": data.collectCompletions(CompletionDeep),
"fuzzy": data.collectCompletions(CompletionFuzzy),
"casesensitive": data.collectCompletions(CompletionCaseSensitve),
"rank": data.collectCompletions(CompletionRank),
"snippet": data.collectCompletionSnippets,
"fold": data.collectFoldingRanges,
"format": data.collectFormats,
"import": data.collectImports,
"godef": data.collectDefinitions,
"typdef": data.collectTypeDefinitions,
"hover": data.collectHoverDefinitions,
"highlight": data.collectHighlights,
"refs": data.collectReferences,
"rename": data.collectRenames,
"prepare": data.collectPrepareRenames,
"symbol": data.collectSymbols,
"signature": data.collectSignatures,
"link": data.collectLinks,
"suggestedfix": data.collectSuggestedFixes,
}); err != nil {
t.Fatal(err)
}
@ -382,6 +391,14 @@ func Run(t *testing.T, tests Tests, data *Data) {
tests.FuzzyCompletions(t, data.FuzzyCompletions, data.CompletionItems)
})
t.Run("CaseSensitiveCompletion", func(t *testing.T) {
t.Helper()
if len(data.CaseSensitiveCompletions) != ExpectedCaseSensitiveCompletionsCount {
t.Errorf("got %v case sensitive completions expected %v", len(data.CaseSensitiveCompletions), ExpectedCaseSensitiveCompletionsCount)
}
tests.CaseSensitiveCompletions(t, data.CaseSensitiveCompletions, data.CompletionItems)
})
t.Run("RankCompletions", func(t *testing.T) {
t.Helper()
if len(data.RankCompletions) != ExpectedRankedCompletionsCount {
@ -638,6 +655,10 @@ func (data *Data) collectCompletions(typ CompletionTestType) func(span.Span, []t
return func(src span.Span, expected []token.Pos) {
result(data.RankCompletions, src, expected)
}
case CompletionCaseSensitve:
return func(src span.Span, expected []token.Pos) {
result(data.CaseSensitiveCompletions, src, expected)
}
default:
return func(src span.Span, expected []token.Pos) {
result(data.Completions, src, expected)