diff --git a/internal/lsp/completion.go b/internal/lsp/completion.go index 820e039b12..7edc1844b4 100644 --- a/internal/lsp/completion.go +++ b/internal/lsp/completion.go @@ -24,7 +24,6 @@ func (s *Server) completion(ctx context.Context, params *protocol.CompletionPara return nil, err } snapshot := view.Snapshot() - options := view.Options() fh, err := snapshot.GetFile(uri) if err != nil { return nil, err @@ -33,8 +32,7 @@ func (s *Server) completion(ctx context.Context, params *protocol.CompletionPara var surrounding *source.Selection switch fh.Identity().Kind { case source.Go: - options.Completion.FullDocumentation = options.HoverKind == source.FullDocumentation - candidates, surrounding, err = source.Completion(ctx, snapshot, fh, params.Position, options.Completion) + candidates, surrounding, err = source.Completion(ctx, snapshot, fh, params.Position) case source.Mod: candidates, surrounding = nil, nil } @@ -62,7 +60,8 @@ func (s *Server) completion(ctx context.Context, params *protocol.CompletionPara // When using deep completions/fuzzy matching, report results as incomplete so // client fetches updated completions after every key stroke. - incompleteResults := options.Completion.Deep || options.Completion.FuzzyMatching + options := view.Options() + incompleteResults := options.DeepCompletion || options.Matcher == source.Fuzzy items := toProtocolCompletionItems(candidates, rng, options) @@ -94,7 +93,7 @@ func toProtocolCompletionItems(candidates []source.CompletionItem, rng protocol. // Limit the number of deep completions to not overwhelm the user in cases // with dozens of deep completion matches. if candidate.Depth > 0 { - if !options.Completion.Deep { + if !options.DeepCompletion { continue } if numDeepCompletionsSeen >= source.MaxDeepCompletions { diff --git a/internal/lsp/completion_test.go b/internal/lsp/completion_test.go index 846a54b467..a8955e62b7 100644 --- a/internal/lsp/completion_test.go +++ b/internal/lsp/completion_test.go @@ -11,11 +11,10 @@ import ( ) func (r *runner) Completion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) { - got := r.callCompletion(t, src, source.CompletionOptions{ - Deep: false, - FuzzyMatching: false, - Documentation: true, - Literal: strings.Contains(string(src.URI()), "literal"), + got := r.callCompletion(t, src, func(opts *source.Options) { + opts.DeepCompletion = false + opts.Matcher = source.CaseInsensitive + opts.Literal = strings.Contains(string(src.URI()), "literal") }) if !strings.Contains(string(src.URI()), "builtins") { got = tests.FilterBuiltins(got) @@ -27,11 +26,11 @@ func (r *runner) Completion(t *testing.T, src span.Span, test tests.Completion, } func (r *runner) CompletionSnippet(t *testing.T, src span.Span, expected tests.CompletionSnippet, placeholders bool, items tests.CompletionItems) { - list := r.callCompletion(t, src, source.CompletionOptions{ - Placeholders: placeholders, - Deep: true, - FuzzyMatching: true, - Literal: true, + list := r.callCompletion(t, src, func(opts *source.Options) { + opts.Placeholders = placeholders + opts.DeepCompletion = true + opts.Matcher = source.Fuzzy + opts.Literal = true }) got := tests.FindItem(list, *items[expected.CompletionItem]) want := expected.PlainSnippet @@ -44,8 +43,8 @@ func (r *runner) CompletionSnippet(t *testing.T, src span.Span, expected tests.C } func (r *runner) UnimportedCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) { - got := r.callCompletion(t, src, source.CompletionOptions{ - Unimported: true, + got := r.callCompletion(t, src, func(opts *source.Options) { + opts.UnimportedCompletion = true }) if !strings.Contains(string(src.URI()), "builtins") { got = tests.FilterBuiltins(got) @@ -57,9 +56,9 @@ func (r *runner) UnimportedCompletion(t *testing.T, src span.Span, test tests.Co } func (r *runner) DeepCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) { - got := r.callCompletion(t, src, source.CompletionOptions{ - Deep: true, - Documentation: true, + got := r.callCompletion(t, src, func(opts *source.Options) { + opts.DeepCompletion = true + opts.Matcher = source.CaseInsensitive }) if !strings.Contains(string(src.URI()), "builtins") { got = tests.FilterBuiltins(got) @@ -71,9 +70,9 @@ func (r *runner) DeepCompletion(t *testing.T, src span.Span, test tests.Completi } func (r *runner) FuzzyCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) { - got := r.callCompletion(t, src, source.CompletionOptions{ - FuzzyMatching: true, - Deep: true, + got := r.callCompletion(t, src, func(opts *source.Options) { + opts.DeepCompletion = true + opts.Matcher = source.Fuzzy }) if !strings.Contains(string(src.URI()), "builtins") { got = tests.FilterBuiltins(got) @@ -85,8 +84,8 @@ func (r *runner) FuzzyCompletion(t *testing.T, src span.Span, test tests.Complet } func (r *runner) CaseSensitiveCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) { - got := r.callCompletion(t, src, source.CompletionOptions{ - CaseSensitive: true, + got := r.callCompletion(t, src, func(opts *source.Options) { + opts.Matcher = source.CaseSensitive }) if !strings.Contains(string(src.URI()), "builtins") { got = tests.FilterBuiltins(got) @@ -98,10 +97,10 @@ func (r *runner) CaseSensitiveCompletion(t *testing.T, src span.Span, test tests } func (r *runner) RankCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) { - got := r.callCompletion(t, src, source.CompletionOptions{ - FuzzyMatching: true, - Deep: true, - Literal: true, + got := r.callCompletion(t, src, func(opts *source.Options) { + opts.DeepCompletion = true + opts.Matcher = source.Fuzzy + opts.Literal = true }) want := expected(t, test, items) if msg := tests.CheckCompletionOrder(want, got, true); msg != "" { @@ -120,7 +119,7 @@ func expected(t *testing.T, test tests.Completion, items tests.CompletionItems) return want } -func (r *runner) callCompletion(t *testing.T, src span.Span, options source.CompletionOptions) []protocol.CompletionItem { +func (r *runner) callCompletion(t *testing.T, src span.Span, options func(*source.Options)) []protocol.CompletionItem { t.Helper() view, err := r.server.session.ViewOf(src.URI()) @@ -129,8 +128,7 @@ func (r *runner) callCompletion(t *testing.T, src span.Span, options source.Comp } original := view.Options() modified := original - modified.InsertTextFormat = protocol.SnippetTextFormat - modified.Completion = options + options(&modified) view, err = view.SetOptions(r.ctx, modified) if err != nil { t.Error(err) diff --git a/internal/lsp/source/completion.go b/internal/lsp/source/completion.go index 30f3d6ee5e..4f212a1759 100644 --- a/internal/lsp/source/completion.go +++ b/internal/lsp/source/completion.go @@ -131,9 +131,8 @@ func (ipm insensitivePrefixMatcher) Score(candidateLabel string) float32 { type completer struct { snapshot Snapshot pkg Package - - qf types.Qualifier - opts CompletionOptions + qf types.Qualifier + opts *completionOptions // ctx is the context associated with this completion request. ctx context.Context @@ -247,10 +246,10 @@ func (p Selection) Suffix() string { } func (c *completer) deepCompletionContext() (context.Context, context.CancelFunc) { - if c.opts.Budget == 0 { + if c.opts.budget == 0 { return context.WithCancel(c.ctx) } - return context.WithDeadline(c.ctx, c.startTime.Add(c.opts.Budget)) + return context.WithDeadline(c.ctx, c.startTime.Add(c.opts.budget)) } func (c *completer) setSurrounding(ident *ast.Ident) { @@ -268,11 +267,12 @@ func (c *completer) setSurrounding(ident *ast.Ident) { mappedRange: newMappedRange(c.snapshot.View().Session().Cache().FileSet(), c.mapper, ident.Pos(), ident.End()), } - if c.opts.FuzzyMatching { + switch c.opts.matcher { + case Fuzzy: c.matcher = fuzzy.NewMatcher(c.surrounding.Prefix()) - } else if c.opts.CaseSensitive { + case CaseSensitive: c.matcher = prefixMatcher(c.surrounding.Prefix()) - } else { + default: c.matcher = insensitivePrefixMatcher(strings.ToLower(c.surrounding.Prefix())) } } @@ -405,7 +405,7 @@ func (e ErrIsDefinition) Error() string { // The selection is computed based on the preceding identifier and can be used by // the client to score the quality of the completion. For instance, some clients // may tolerate imperfect matches as valid completion results, since users may make typos. -func Completion(ctx context.Context, snapshot Snapshot, fh FileHandle, pos protocol.Position, opts CompletionOptions) ([]CompletionItem, *Selection, error) { +func Completion(ctx context.Context, snapshot Snapshot, fh FileHandle, pos protocol.Position) ([]CompletionItem, *Selection, error) { ctx, done := trace.StartSpan(ctx, "source.Completion") defer done() @@ -439,6 +439,7 @@ func Completion(ctx context.Context, snapshot Snapshot, fh FileHandle, pos proto return nil, nil, nil } + opts := snapshot.View().Options() c := &completer{ pkg: pkg, snapshot: snapshot, @@ -451,7 +452,16 @@ func Completion(ctx context.Context, snapshot Snapshot, fh FileHandle, pos proto seen: make(map[types.Object]bool), enclosingFunc: enclosingFunction(path, rng.Start, pkg.GetTypesInfo()), enclosingCompositeLiteral: enclosingCompositeLiteral(path, rng.Start, pkg.GetTypesInfo()), - opts: opts, + opts: &completionOptions{ + matcher: opts.Matcher, + deepCompletion: opts.DeepCompletion, + unimported: opts.UnimportedCompletion, + documentation: opts.CompletionDocumentation, + fullDocumentation: opts.HoverKind == FullDocumentation, + placeholders: opts.Placeholders, + literal: opts.Literal, + budget: opts.CompletionBudget, + }, // default to a matcher that always matches matcher: prefixMatcher(""), methodSetCache: make(map[methodSetKey]*types.MethodSet), @@ -459,7 +469,7 @@ func Completion(ctx context.Context, snapshot Snapshot, fh FileHandle, pos proto startTime: startTime, } - if opts.Deep { + if c.opts.deepCompletion { // Initialize max search depth to unlimited. c.deepState.maxDepth = -1 } @@ -629,7 +639,7 @@ func (c *completer) selector(sel *ast.SelectorExpr) error { } // Try unimported packages. - if id, ok := sel.X.(*ast.Ident); ok && c.opts.Unimported && len(c.items) < unimportedTarget { + if id, ok := sel.X.(*ast.Ident); ok && c.opts.unimported && len(c.items) < unimportedTarget { if err := c.unimportedMembers(id); err != nil { return err } @@ -862,7 +872,7 @@ func (c *completer) lexical() error { } } - if c.opts.Unimported && len(c.items) < unimportedTarget { + if c.opts.unimported && len(c.items) < unimportedTarget { ctx, cancel := c.deepCompletionContext() defer cancel() // Suggest packages that have not been imported yet. diff --git a/internal/lsp/source/completion_format.go b/internal/lsp/source/completion_format.go index 77713dddfe..168308f6bd 100644 --- a/internal/lsp/source/completion_format.go +++ b/internal/lsp/source/completion_format.go @@ -168,7 +168,7 @@ func (c *completer) item(cand candidate) (CompletionItem, error) { snippet: snip, } // If the user doesn't want documentation for completion items. - if !c.opts.Documentation { + if !c.opts.documentation { return item, nil } pos := c.snapshot.View().Session().Cache().FileSet().Position(obj.Pos()) @@ -200,7 +200,7 @@ func (c *completer) item(cand candidate) (CompletionItem, error) { return item, nil } item.Documentation = hover.Synopsis - if c.opts.FullDocumentation { + if c.opts.fullDocumentation { item.Documentation = hover.FullDocumentation } return item, nil diff --git a/internal/lsp/source/completion_literal.go b/internal/lsp/source/completion_literal.go index 287d43d046..89ecda6387 100644 --- a/internal/lsp/source/completion_literal.go +++ b/internal/lsp/source/completion_literal.go @@ -20,7 +20,7 @@ import ( // literal generates composite literal, function literal, and make() // completion items. func (c *completer) literal(literalType types.Type, imp *importInfo) { - if !c.opts.Literal { + if !c.opts.literal { return } @@ -213,7 +213,7 @@ func (c *completer) functionLiteral(sig *types.Signature, matchScore float64) { // Our parameter names are guesses, so they must be placeholders // for easy correction. If placeholders are disabled, don't // offer the completion. - if !c.opts.Placeholders { + if !c.opts.placeholders { return } @@ -367,7 +367,7 @@ func (c *completer) makeCall(typeName string, secondArg string, matchScore float if secondArg != "" { snip.WriteText(", ") snip.WritePlaceholder(func(b *snippet.Builder) { - if c.opts.Placeholders { + if c.opts.placeholders { b.WriteText(secondArg) } }) diff --git a/internal/lsp/source/completion_snippet.go b/internal/lsp/source/completion_snippet.go index 567825693f..6aaf28dce4 100644 --- a/internal/lsp/source/completion_snippet.go +++ b/internal/lsp/source/completion_snippet.go @@ -36,7 +36,7 @@ func (c *completer) structFieldSnippet(label, detail string) *snippet.Builder { snip.WriteText(label + ": ") snip.WritePlaceholder(func(b *snippet.Builder) { // A placeholder snippet turns "Foo{Ba<>" into "Foo{Bar: <*int*>". - if c.opts.Placeholders { + if c.opts.placeholders { b.WriteText(detail) } }) @@ -79,7 +79,7 @@ func (c *completer) functionCallSnippet(name string, params []string) *snippet.B snip := &snippet.Builder{} snip.WriteText(name + "(") - if c.opts.Placeholders { + if c.opts.placeholders { // A placeholder snippet turns "someFun<>" into "someFunc(<*i int*>, *s string*)". for i, p := range params { if i > 0 { diff --git a/internal/lsp/source/deep_completion.go b/internal/lsp/source/deep_completion.go index 24643d8722..74f2bbf5a9 100644 --- a/internal/lsp/source/deep_completion.go +++ b/internal/lsp/source/deep_completion.go @@ -107,8 +107,8 @@ func (c *completer) shouldPrune() bool { } // Check our remaining budget every 100 candidates. - if c.opts.Budget > 0 && c.deepState.candidateCount%100 == 0 { - spent := float64(time.Since(c.startTime)) / float64(c.opts.Budget) + if c.opts.budget > 0 && c.deepState.candidateCount%100 == 0 { + spent := float64(time.Since(c.startTime)) / float64(c.opts.budget) switch { case spent >= 0.90: diff --git a/internal/lsp/source/options.go b/internal/lsp/source/options.go index e3398211d8..a2eeb0baea 100644 --- a/internal/lsp/source/options.go +++ b/internal/lsp/source/options.go @@ -74,16 +74,13 @@ var ( }, } DefaultUserOptions = UserOptions{ - Env: os.Environ(), - HoverKind: SynopsisDocumentation, - Completion: CompletionOptions{ - Documentation: true, - Deep: true, - FuzzyMatching: true, - Literal: true, - Budget: 100 * time.Millisecond, - }, - LinkTarget: "pkg.go.dev", + Env: os.Environ(), + HoverKind: SynopsisDocumentation, + LinkTarget: "pkg.go.dev", + Matcher: Fuzzy, + DeepCompletion: true, + CompletionDocumentation: true, + Literal: true, } DefaultHooks = Hooks{ ComputeEdits: myers.ComputeEdits, @@ -94,7 +91,9 @@ var ( DefaultExperimentalOptions = ExperimentalOptions{ TempModfile: false, } - DefaultDebuggingOptions = DebuggingOptions{} + DefaultDebuggingOptions = DebuggingOptions{ + CompletionBudget: 100 * time.Millisecond, + } ) type Options struct { @@ -142,7 +141,37 @@ type UserOptions struct { // LocalPrefix is used to specify goimports's -local behavior. LocalPrefix string - Completion CompletionOptions + // Matcher specifies the type of matcher to use for completion requests. + Matcher Matcher + + // DeepCompletion allows completion to perform nested searches through + // possible candidates. + DeepCompletion bool + + // UnimportedCompletion enables completion for unimported packages. + UnimportedCompletion bool + + // CompletionDocumentation returns additional documentation with completion + // requests. + CompletionDocumentation bool + + // Placeholders adds placeholders to parameters and structs in completion + // results. + Placeholders bool + + // Literal enables completion for map, slice, and function literals. + Literal bool +} + +type completionOptions struct { + deepCompletion bool + unimported bool + documentation bool + fullDocumentation bool + placeholders bool + literal bool + matcher Matcher + budget time.Duration } type Hooks struct { @@ -161,26 +190,23 @@ type ExperimentalOptions struct { type DebuggingOptions struct { VerboseOutput bool -} -type CompletionOptions struct { - Deep bool - FuzzyMatching bool - CaseSensitive bool - Unimported bool - Documentation bool - FullDocumentation bool - Placeholders bool - Literal bool - - // Budget is the soft latency goal for completion requests. Most + // CompletionBudget is the soft latency goal for completion requests. Most // requests finish in a couple milliseconds, but in some cases deep // completions can take much longer. As we use up our budget we // dynamically reduce the search scope to ensure we return timely // results. Zero means unlimited. - Budget time.Duration + CompletionBudget time.Duration } +type Matcher int + +const ( + Fuzzy = Matcher(iota) + CaseInsensitive + CaseSensitive +) + type HoverKind int const ( @@ -280,17 +306,13 @@ func (o *Options) set(name string, value interface{}) OptionResult { o.BuildFlags = flags case "completionDocumentation": - result.setBool(&o.Completion.Documentation) + result.setBool(&o.CompletionDocumentation) case "usePlaceholders": - result.setBool(&o.Completion.Placeholders) + result.setBool(&o.Placeholders) case "deepCompletion": - result.setBool(&o.Completion.Deep) - case "fuzzyMatching": - result.setBool(&o.Completion.FuzzyMatching) - case "caseSensitiveCompletion": - result.setBool(&o.Completion.CaseSensitive) + result.setBool(&o.DeepCompletion) case "completeUnimported": - result.setBool(&o.Completion.Unimported) + result.setBool(&o.UnimportedCompletion) case "completionBudget": if v, ok := result.asString(); ok { d, err := time.ParseDuration(v) @@ -298,13 +320,26 @@ func (o *Options) set(name string, value interface{}) OptionResult { result.errorf("failed to parse duration %q: %v", v, err) break } - o.Completion.Budget = d + o.CompletionBudget = d + } + + case "matcher": + matcher, ok := result.asString() + if !ok { + break + } + switch matcher { + case "fuzzy": + o.Matcher = Fuzzy + case "caseSensitive": + o.Matcher = CaseSensitive + default: + o.Matcher = CaseInsensitive } case "hoverKind": - hoverKind, ok := value.(string) + hoverKind, ok := result.asString() if !ok { - result.errorf("invalid type %T for string option %q", value, name) break } switch hoverKind { @@ -378,6 +413,14 @@ func (o *Options) set(name string, value interface{}) OptionResult { result.State = OptionDeprecated result.Replacement = "completeUnimported" + case "fuzzyMatching": + result.State = OptionDeprecated + result.Replacement = "matcher" + + case "caseSensitiveCompletion": + result.State = OptionDeprecated + result.Replacement = "matcher" + case "noIncrementalSync": result.State = OptionDeprecated diff --git a/internal/lsp/source/source_test.go b/internal/lsp/source/source_test.go index defab5e333..f6d36e9320 100644 --- a/internal/lsp/source/source_test.go +++ b/internal/lsp/source/source_test.go @@ -107,10 +107,10 @@ func (r *runner) Completion(t *testing.T, src span.Span, test tests.Completion, for _, pos := range test.CompletionItems { want = append(want, tests.ToProtocolCompletionItem(*items[pos])) } - prefix, list := r.callCompletion(t, src, source.CompletionOptions{ - Documentation: true, - FuzzyMatching: true, - Literal: strings.Contains(string(src.URI()), "literal"), + prefix, list := r.callCompletion(t, src, func(opts *source.Options) { + opts.Matcher = source.Fuzzy + opts.Literal = strings.Contains(string(src.URI()), "literal") + opts.DeepCompletion = false }) if !strings.Contains(string(src.URI()), "builtins") { list = tests.FilterBuiltins(list) @@ -128,10 +128,10 @@ func (r *runner) Completion(t *testing.T, src span.Span, test tests.Completion, } func (r *runner) CompletionSnippet(t *testing.T, src span.Span, expected tests.CompletionSnippet, placeholders bool, items tests.CompletionItems) { - _, list := r.callCompletion(t, src, source.CompletionOptions{ - Placeholders: placeholders, - Deep: true, - Literal: true, + _, list := r.callCompletion(t, src, func(opts *source.Options) { + opts.Placeholders = placeholders + opts.DeepCompletion = true + opts.Literal = true }) got := tests.FindItem(list, *items[expected.CompletionItem]) want := expected.PlainSnippet @@ -148,8 +148,8 @@ func (r *runner) UnimportedCompletion(t *testing.T, src span.Span, test tests.Co for _, pos := range test.CompletionItems { want = append(want, tests.ToProtocolCompletionItem(*items[pos])) } - _, got := r.callCompletion(t, src, source.CompletionOptions{ - Unimported: true, + _, got := r.callCompletion(t, src, func(opts *source.Options) { + opts.UnimportedCompletion = true }) if !strings.Contains(string(src.URI()), "builtins") { got = tests.FilterBuiltins(got) @@ -164,9 +164,9 @@ func (r *runner) DeepCompletion(t *testing.T, src span.Span, test tests.Completi for _, pos := range test.CompletionItems { want = append(want, tests.ToProtocolCompletionItem(*items[pos])) } - prefix, list := r.callCompletion(t, src, source.CompletionOptions{ - Deep: true, - Documentation: true, + prefix, list := r.callCompletion(t, src, func(opts *source.Options) { + opts.DeepCompletion = true + opts.Matcher = source.CaseInsensitive }) if !strings.Contains(string(src.URI()), "builtins") { list = tests.FilterBuiltins(list) @@ -189,9 +189,9 @@ func (r *runner) FuzzyCompletion(t *testing.T, src span.Span, test tests.Complet for _, pos := range test.CompletionItems { want = append(want, tests.ToProtocolCompletionItem(*items[pos])) } - _, got := r.callCompletion(t, src, source.CompletionOptions{ - FuzzyMatching: true, - Deep: true, + _, got := r.callCompletion(t, src, func(opts *source.Options) { + opts.DeepCompletion = true + opts.Matcher = source.Fuzzy }) if !strings.Contains(string(src.URI()), "builtins") { got = tests.FilterBuiltins(got) @@ -206,8 +206,8 @@ func (r *runner) CaseSensitiveCompletion(t *testing.T, src span.Span, test tests for _, pos := range test.CompletionItems { want = append(want, tests.ToProtocolCompletionItem(*items[pos])) } - _, list := r.callCompletion(t, src, source.CompletionOptions{ - CaseSensitive: true, + _, list := r.callCompletion(t, src, func(opts *source.Options) { + opts.Matcher = source.CaseSensitive }) if !strings.Contains(string(src.URI()), "builtins") { list = tests.FilterBuiltins(list) @@ -222,25 +222,34 @@ func (r *runner) RankCompletion(t *testing.T, src span.Span, test tests.Completi for _, pos := range test.CompletionItems { want = append(want, tests.ToProtocolCompletionItem(*items[pos])) } - _, got := r.callCompletion(t, src, source.CompletionOptions{ - FuzzyMatching: true, - Deep: true, - Literal: true, + _, got := r.callCompletion(t, src, func(opts *source.Options) { + opts.DeepCompletion = true + opts.Matcher = source.Fuzzy + opts.Literal = true }) if msg := tests.CheckCompletionOrder(want, got, true); msg != "" { t.Errorf("%s: %s", src, msg) } } -func (r *runner) callCompletion(t *testing.T, src span.Span, options source.CompletionOptions) (string, []protocol.CompletionItem) { +func (r *runner) callCompletion(t *testing.T, src span.Span, options func(*source.Options)) (string, []protocol.CompletionItem) { fh, err := r.view.Snapshot().GetFile(src.URI()) if err != nil { t.Fatal(err) } - list, surrounding, err := source.Completion(r.ctx, r.view.Snapshot(), fh, protocol.Position{ + original := r.view.Options() + modified := original + options(&modified) + view, err := r.view.SetOptions(r.ctx, modified) + if err != nil { + t.Fatal(err) + } + defer r.view.SetOptions(r.ctx, original) + + list, surrounding, err := source.Completion(r.ctx, view.Snapshot(), fh, protocol.Position{ Line: float64(src.Start().Line() - 1), Character: float64(src.Start().Column() - 1), - }, options) + }) if err != nil && !errors.As(err, &source.ErrIsDefinition{}) { t.Fatalf("failed for %v: %v", src, err) } @@ -261,7 +270,7 @@ func (r *runner) callCompletion(t *testing.T, src span.Span, options source.Comp // Apply deep completion filtering. for _, item := range list { if item.Depth > 0 { - if !options.Deep { + if !modified.DeepCompletion { continue } if numDeepCompletionsSeen >= source.MaxDeepCompletions { diff --git a/internal/lsp/tests/tests.go b/internal/lsp/tests/tests.go index 3b9d129b7c..49608d34c5 100644 --- a/internal/lsp/tests/tests.go +++ b/internal/lsp/tests/tests.go @@ -191,7 +191,7 @@ func DefaultOptions() source.Options { } o.HoverKind = source.SynopsisDocumentation o.InsertTextFormat = protocol.SnippetTextFormat - o.Completion.Budget = time.Minute + o.CompletionBudget = time.Minute return o }