mirror of
https://github.com/golang/go
synced 2024-11-18 17:44:47 -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:
parent
ea99b82c7b
commit
a8d5d34286
@ -53,6 +53,10 @@ func (r *runner) FuzzyCompletions(t *testing.T, data tests.FuzzyCompletions, ite
|
|||||||
//TODO: add command line completions tests when it works
|
//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) {
|
func (r *runner) RankCompletions(t *testing.T, data tests.RankCompletions, items tests.CompletionItems) {
|
||||||
//TODO: add command line completions tests when it works
|
//TODO: add command line completions tests when it works
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
func (r *runner) RankCompletions(t *testing.T, data tests.RankCompletions, items tests.CompletionItems) {
|
||||||
for src, test := range data {
|
for src, test := range data {
|
||||||
got := r.callCompletion(t, src, source.CompletionOptions{
|
got := r.callCompletion(t, src, source.CompletionOptions{
|
||||||
|
@ -104,11 +104,21 @@ type matcher interface {
|
|||||||
Score(candidateLabel string) (score float32)
|
Score(candidateLabel string) (score float32)
|
||||||
}
|
}
|
||||||
|
|
||||||
// prefixMatcher implements case insensitive prefix matching.
|
// prefixMatcher implements case sensitive prefix matching.
|
||||||
type prefixMatcher string
|
type prefixMatcher string
|
||||||
|
|
||||||
func (pm prefixMatcher) Score(candidateLabel string) float32 {
|
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
|
||||||
}
|
}
|
||||||
return -1
|
return -1
|
||||||
@ -233,8 +243,10 @@ func (c *completer) setSurrounding(ident *ast.Ident) {
|
|||||||
|
|
||||||
if c.opts.FuzzyMatching {
|
if c.opts.FuzzyMatching {
|
||||||
c.matcher = fuzzy.NewMatcher(c.surrounding.Prefix(), fuzzy.Symbol)
|
c.matcher = fuzzy.NewMatcher(c.surrounding.Prefix(), fuzzy.Symbol)
|
||||||
|
} else if c.opts.CaseSensitive {
|
||||||
|
c.matcher = prefixMatcher(c.surrounding.Prefix())
|
||||||
} else {
|
} else {
|
||||||
c.matcher = prefixMatcher(strings.ToLower(c.surrounding.Prefix()))
|
c.matcher = insensitivePrefixMatcher(strings.ToLower(c.surrounding.Prefix()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,6 +76,7 @@ type Options struct {
|
|||||||
type CompletionOptions struct {
|
type CompletionOptions struct {
|
||||||
Deep bool
|
Deep bool
|
||||||
FuzzyMatching bool
|
FuzzyMatching bool
|
||||||
|
CaseSensitive bool
|
||||||
Unimported bool
|
Unimported bool
|
||||||
Documentation bool
|
Documentation bool
|
||||||
FullDocumentation bool
|
FullDocumentation bool
|
||||||
@ -200,6 +201,8 @@ func (o *Options) set(name string, value interface{}) OptionResult {
|
|||||||
result.setBool(&o.Completion.Deep)
|
result.setBool(&o.Completion.Deep)
|
||||||
case "fuzzyMatching":
|
case "fuzzyMatching":
|
||||||
result.setBool(&o.Completion.FuzzyMatching)
|
result.setBool(&o.Completion.FuzzyMatching)
|
||||||
|
case "caseSensitiveCompletion":
|
||||||
|
result.setBool(&o.Completion.CaseSensitive)
|
||||||
case "completeUnimported":
|
case "completeUnimported":
|
||||||
result.setBool(&o.Completion.Unimported)
|
result.setBool(&o.Completion.Unimported)
|
||||||
|
|
||||||
|
@ -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) {
|
func (r *runner) RankCompletions(t *testing.T, data tests.RankCompletions, items tests.CompletionItems) {
|
||||||
for src, test := range data {
|
for src, test := range data {
|
||||||
var want []protocol.CompletionItem
|
var want []protocol.CompletionItem
|
||||||
|
16
internal/lsp/testdata/casesensitive/casesensitive.go
vendored
Normal file
16
internal/lsp/testdata/casesensitive/casesensitive.go
vendored
Normal 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(" //")
|
||||||
|
}
|
@ -29,26 +29,27 @@ import (
|
|||||||
// We hardcode the expected number of test cases to ensure that all tests
|
// 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.
|
// are being executed. If a test is added, this number must be changed.
|
||||||
const (
|
const (
|
||||||
ExpectedCompletionsCount = 169
|
ExpectedCompletionsCount = 169
|
||||||
ExpectedCompletionSnippetCount = 36
|
ExpectedCompletionSnippetCount = 36
|
||||||
ExpectedUnimportedCompletionsCount = 1
|
ExpectedUnimportedCompletionsCount = 1
|
||||||
ExpectedDeepCompletionsCount = 5
|
ExpectedDeepCompletionsCount = 5
|
||||||
ExpectedFuzzyCompletionsCount = 6
|
ExpectedFuzzyCompletionsCount = 6
|
||||||
ExpectedRankedCompletionsCount = 1
|
ExpectedCaseSensitiveCompletionsCount = 4
|
||||||
ExpectedDiagnosticsCount = 21
|
ExpectedRankedCompletionsCount = 1
|
||||||
ExpectedFormatCount = 6
|
ExpectedDiagnosticsCount = 21
|
||||||
ExpectedImportCount = 2
|
ExpectedFormatCount = 6
|
||||||
ExpectedSuggestedFixCount = 1
|
ExpectedImportCount = 2
|
||||||
ExpectedDefinitionsCount = 39
|
ExpectedSuggestedFixCount = 1
|
||||||
ExpectedTypeDefinitionsCount = 2
|
ExpectedDefinitionsCount = 39
|
||||||
ExpectedFoldingRangesCount = 2
|
ExpectedTypeDefinitionsCount = 2
|
||||||
ExpectedHighlightsCount = 2
|
ExpectedFoldingRangesCount = 2
|
||||||
ExpectedReferencesCount = 6
|
ExpectedHighlightsCount = 2
|
||||||
ExpectedRenamesCount = 20
|
ExpectedReferencesCount = 6
|
||||||
ExpectedPrepareRenamesCount = 8
|
ExpectedRenamesCount = 20
|
||||||
ExpectedSymbolsCount = 1
|
ExpectedPrepareRenamesCount = 8
|
||||||
ExpectedSignaturesCount = 21
|
ExpectedSymbolsCount = 1
|
||||||
ExpectedLinksCount = 4
|
ExpectedSignaturesCount = 21
|
||||||
|
ExpectedLinksCount = 4
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -67,6 +68,7 @@ type CompletionSnippets map[span.Span]CompletionSnippet
|
|||||||
type UnimportedCompletions map[span.Span]Completion
|
type UnimportedCompletions map[span.Span]Completion
|
||||||
type DeepCompletions map[span.Span]Completion
|
type DeepCompletions map[span.Span]Completion
|
||||||
type FuzzyCompletions map[span.Span]Completion
|
type FuzzyCompletions map[span.Span]Completion
|
||||||
|
type CaseSensitiveCompletions map[span.Span]Completion
|
||||||
type RankCompletions map[span.Span]Completion
|
type RankCompletions map[span.Span]Completion
|
||||||
type FoldingRanges []span.Span
|
type FoldingRanges []span.Span
|
||||||
type Formats []span.Span
|
type Formats []span.Span
|
||||||
@ -83,29 +85,30 @@ type Signatures map[span.Span]*source.SignatureInformation
|
|||||||
type Links map[span.URI][]Link
|
type Links map[span.URI][]Link
|
||||||
|
|
||||||
type Data struct {
|
type Data struct {
|
||||||
Config packages.Config
|
Config packages.Config
|
||||||
Exported *packagestest.Exported
|
Exported *packagestest.Exported
|
||||||
Diagnostics Diagnostics
|
Diagnostics Diagnostics
|
||||||
CompletionItems CompletionItems
|
CompletionItems CompletionItems
|
||||||
Completions Completions
|
Completions Completions
|
||||||
CompletionSnippets CompletionSnippets
|
CompletionSnippets CompletionSnippets
|
||||||
UnimportedCompletions UnimportedCompletions
|
UnimportedCompletions UnimportedCompletions
|
||||||
DeepCompletions DeepCompletions
|
DeepCompletions DeepCompletions
|
||||||
FuzzyCompletions FuzzyCompletions
|
FuzzyCompletions FuzzyCompletions
|
||||||
RankCompletions RankCompletions
|
CaseSensitiveCompletions CaseSensitiveCompletions
|
||||||
FoldingRanges FoldingRanges
|
RankCompletions RankCompletions
|
||||||
Formats Formats
|
FoldingRanges FoldingRanges
|
||||||
Imports Imports
|
Formats Formats
|
||||||
SuggestedFixes SuggestedFixes
|
Imports Imports
|
||||||
Definitions Definitions
|
SuggestedFixes SuggestedFixes
|
||||||
Highlights Highlights
|
Definitions Definitions
|
||||||
References References
|
Highlights Highlights
|
||||||
Renames Renames
|
References References
|
||||||
PrepareRenames PrepareRenames
|
Renames Renames
|
||||||
Symbols Symbols
|
PrepareRenames PrepareRenames
|
||||||
symbolsChildren SymbolsChildren
|
Symbols Symbols
|
||||||
Signatures Signatures
|
symbolsChildren SymbolsChildren
|
||||||
Links Links
|
Signatures Signatures
|
||||||
|
Links Links
|
||||||
|
|
||||||
t testing.TB
|
t testing.TB
|
||||||
fragments map[string]string
|
fragments map[string]string
|
||||||
@ -123,6 +126,7 @@ type Tests interface {
|
|||||||
UnimportedCompletions(*testing.T, UnimportedCompletions, CompletionItems)
|
UnimportedCompletions(*testing.T, UnimportedCompletions, CompletionItems)
|
||||||
DeepCompletions(*testing.T, DeepCompletions, CompletionItems)
|
DeepCompletions(*testing.T, DeepCompletions, CompletionItems)
|
||||||
FuzzyCompletions(*testing.T, FuzzyCompletions, CompletionItems)
|
FuzzyCompletions(*testing.T, FuzzyCompletions, CompletionItems)
|
||||||
|
CaseSensitiveCompletions(*testing.T, CaseSensitiveCompletions, CompletionItems)
|
||||||
RankCompletions(*testing.T, RankCompletions, CompletionItems)
|
RankCompletions(*testing.T, RankCompletions, CompletionItems)
|
||||||
FoldingRange(*testing.T, FoldingRanges)
|
FoldingRange(*testing.T, FoldingRanges)
|
||||||
Format(*testing.T, Formats)
|
Format(*testing.T, Formats)
|
||||||
@ -160,6 +164,9 @@ const (
|
|||||||
// Fuzzy tests deep completion and fuzzy matching.
|
// Fuzzy tests deep completion and fuzzy matching.
|
||||||
CompletionFuzzy
|
CompletionFuzzy
|
||||||
|
|
||||||
|
// CaseSensitive tests case sensitive completion
|
||||||
|
CompletionCaseSensitve
|
||||||
|
|
||||||
// CompletionRank candidates in test must be valid and in the right relative order.
|
// CompletionRank candidates in test must be valid and in the right relative order.
|
||||||
CompletionRank
|
CompletionRank
|
||||||
)
|
)
|
||||||
@ -209,23 +216,24 @@ func Load(t testing.TB, exporter packagestest.Exporter, dir string) *Data {
|
|||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
data := &Data{
|
data := &Data{
|
||||||
Diagnostics: make(Diagnostics),
|
Diagnostics: make(Diagnostics),
|
||||||
CompletionItems: make(CompletionItems),
|
CompletionItems: make(CompletionItems),
|
||||||
Completions: make(Completions),
|
Completions: make(Completions),
|
||||||
CompletionSnippets: make(CompletionSnippets),
|
CompletionSnippets: make(CompletionSnippets),
|
||||||
UnimportedCompletions: make(UnimportedCompletions),
|
UnimportedCompletions: make(UnimportedCompletions),
|
||||||
DeepCompletions: make(DeepCompletions),
|
DeepCompletions: make(DeepCompletions),
|
||||||
FuzzyCompletions: make(FuzzyCompletions),
|
FuzzyCompletions: make(FuzzyCompletions),
|
||||||
RankCompletions: make(RankCompletions),
|
RankCompletions: make(RankCompletions),
|
||||||
Definitions: make(Definitions),
|
CaseSensitiveCompletions: make(CaseSensitiveCompletions),
|
||||||
Highlights: make(Highlights),
|
Definitions: make(Definitions),
|
||||||
References: make(References),
|
Highlights: make(Highlights),
|
||||||
Renames: make(Renames),
|
References: make(References),
|
||||||
PrepareRenames: make(PrepareRenames),
|
Renames: make(Renames),
|
||||||
Symbols: make(Symbols),
|
PrepareRenames: make(PrepareRenames),
|
||||||
symbolsChildren: make(SymbolsChildren),
|
Symbols: make(Symbols),
|
||||||
Signatures: make(Signatures),
|
symbolsChildren: make(SymbolsChildren),
|
||||||
Links: make(Links),
|
Signatures: make(Signatures),
|
||||||
|
Links: make(Links),
|
||||||
|
|
||||||
t: t,
|
t: t,
|
||||||
dir: dir,
|
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.
|
// Collect any data that needs to be used by subsequent tests.
|
||||||
if err := data.Exported.Expect(map[string]interface{}{
|
if err := data.Exported.Expect(map[string]interface{}{
|
||||||
"diag": data.collectDiagnostics,
|
"diag": data.collectDiagnostics,
|
||||||
"item": data.collectCompletionItems,
|
"item": data.collectCompletionItems,
|
||||||
"complete": data.collectCompletions(CompletionDefault),
|
"complete": data.collectCompletions(CompletionDefault),
|
||||||
"unimported": data.collectCompletions(CompletionUnimported),
|
"unimported": data.collectCompletions(CompletionUnimported),
|
||||||
"deep": data.collectCompletions(CompletionDeep),
|
"deep": data.collectCompletions(CompletionDeep),
|
||||||
"fuzzy": data.collectCompletions(CompletionFuzzy),
|
"fuzzy": data.collectCompletions(CompletionFuzzy),
|
||||||
"rank": data.collectCompletions(CompletionRank),
|
"casesensitive": data.collectCompletions(CompletionCaseSensitve),
|
||||||
"snippet": data.collectCompletionSnippets,
|
"rank": data.collectCompletions(CompletionRank),
|
||||||
"fold": data.collectFoldingRanges,
|
"snippet": data.collectCompletionSnippets,
|
||||||
"format": data.collectFormats,
|
"fold": data.collectFoldingRanges,
|
||||||
"import": data.collectImports,
|
"format": data.collectFormats,
|
||||||
"godef": data.collectDefinitions,
|
"import": data.collectImports,
|
||||||
"typdef": data.collectTypeDefinitions,
|
"godef": data.collectDefinitions,
|
||||||
"hover": data.collectHoverDefinitions,
|
"typdef": data.collectTypeDefinitions,
|
||||||
"highlight": data.collectHighlights,
|
"hover": data.collectHoverDefinitions,
|
||||||
"refs": data.collectReferences,
|
"highlight": data.collectHighlights,
|
||||||
"rename": data.collectRenames,
|
"refs": data.collectReferences,
|
||||||
"prepare": data.collectPrepareRenames,
|
"rename": data.collectRenames,
|
||||||
"symbol": data.collectSymbols,
|
"prepare": data.collectPrepareRenames,
|
||||||
"signature": data.collectSignatures,
|
"symbol": data.collectSymbols,
|
||||||
"link": data.collectLinks,
|
"signature": data.collectSignatures,
|
||||||
"suggestedfix": data.collectSuggestedFixes,
|
"link": data.collectLinks,
|
||||||
|
"suggestedfix": data.collectSuggestedFixes,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -382,6 +391,14 @@ func Run(t *testing.T, tests Tests, data *Data) {
|
|||||||
tests.FuzzyCompletions(t, data.FuzzyCompletions, data.CompletionItems)
|
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.Run("RankCompletions", func(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
if len(data.RankCompletions) != ExpectedRankedCompletionsCount {
|
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) {
|
return func(src span.Span, expected []token.Pos) {
|
||||||
result(data.RankCompletions, src, expected)
|
result(data.RankCompletions, src, expected)
|
||||||
}
|
}
|
||||||
|
case CompletionCaseSensitve:
|
||||||
|
return func(src span.Span, expected []token.Pos) {
|
||||||
|
result(data.CaseSensitiveCompletions, src, expected)
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return func(src span.Span, expected []token.Pos) {
|
return func(src span.Span, expected []token.Pos) {
|
||||||
result(data.Completions, src, expected)
|
result(data.Completions, src, expected)
|
||||||
|
Loading…
Reference in New Issue
Block a user