mirror of https://github.com/golang/go.git
internal/lsp/completion: move postfix completions behind option
Move postfix completion functionality behind an experimental option flag. For now users can enable it by setting "experimentalPostfixCompletions" or "allExperiments". I added a RunnerOption so regtest tests can tweak *source.Options. I didn't refactor the "Experimental" mode to use the new RunnerOption because I didn't fully understand its purpose. Change-Id: I75ed748710cae7fa99f4ea6ea117ce245a4e9749 Reviewed-on: https://go-review.googlesource.com/c/tools/+/296109 Run-TryBot: Muir Manders <muir@mnd.rs> gopls-CI: kokoro <noreply+kokoro@google.com> Trust: Heschi Kreinick <heschi@google.com> Trust: Rebecca Stambler <rstambler@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
parent
09058ab085
commit
94a19427f1
|
|
@ -209,6 +209,15 @@ Must be one of:
|
|||
* `"Fuzzy"`
|
||||
Default: `"Fuzzy"`.
|
||||
|
||||
##### **experimentalPostfixCompletions** *bool*
|
||||
|
||||
**This setting is experimental and may be deleted.**
|
||||
|
||||
experimentalPostfixCompletions enables artifical method snippets
|
||||
such as "someSlice.sort!".
|
||||
|
||||
Default: `false`.
|
||||
|
||||
#### Diagnostic
|
||||
|
||||
##### **analyses** *map[string]bool*
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"testing"
|
||||
|
||||
. "golang.org/x/tools/gopls/internal/regtest"
|
||||
"golang.org/x/tools/internal/lsp/source"
|
||||
)
|
||||
|
||||
func TestPostfixSnippetCompletion(t *testing.T) {
|
||||
|
|
@ -373,7 +374,10 @@ func _() {
|
|||
},
|
||||
}
|
||||
|
||||
Run(t, mod, func(t *testing.T, env *Env) {
|
||||
r := WithOptions(Options(func(o *source.Options) {
|
||||
o.ExperimentalPostfixCompletions = true
|
||||
}))
|
||||
r.Run(t, mod, func(t *testing.T, env *Env) {
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
c.before = strings.Trim(c.before, "\n")
|
||||
|
|
|
|||
|
|
@ -70,19 +70,21 @@ type Runner struct {
|
|||
}
|
||||
|
||||
type runConfig struct {
|
||||
editor fake.EditorConfig
|
||||
sandbox fake.SandboxConfig
|
||||
modes Mode
|
||||
timeout time.Duration
|
||||
debugAddr string
|
||||
skipLogs bool
|
||||
skipHooks bool
|
||||
editor fake.EditorConfig
|
||||
sandbox fake.SandboxConfig
|
||||
modes Mode
|
||||
timeout time.Duration
|
||||
debugAddr string
|
||||
skipLogs bool
|
||||
skipHooks bool
|
||||
optionsHook func(*source.Options)
|
||||
}
|
||||
|
||||
func (r *Runner) defaultConfig() *runConfig {
|
||||
return &runConfig{
|
||||
modes: r.DefaultModes,
|
||||
timeout: r.Timeout,
|
||||
modes: r.DefaultModes,
|
||||
timeout: r.Timeout,
|
||||
optionsHook: hooks.Options,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -118,6 +120,19 @@ func Modes(modes Mode) RunOption {
|
|||
})
|
||||
}
|
||||
|
||||
// Options configures the various server and user options.
|
||||
func Options(hook func(*source.Options)) RunOption {
|
||||
return optionSetter(func(opts *runConfig) {
|
||||
old := opts.optionsHook
|
||||
opts.optionsHook = func(o *source.Options) {
|
||||
if old != nil {
|
||||
old(o)
|
||||
}
|
||||
hook(o)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func SendPID() RunOption {
|
||||
return optionSetter(func(opts *runConfig) {
|
||||
opts.editor.SendPID = true
|
||||
|
|
@ -216,7 +231,7 @@ func (r *Runner) Run(t *testing.T, files string, test TestFunc, opts ...RunOptio
|
|||
tests := []struct {
|
||||
name string
|
||||
mode Mode
|
||||
getServer func(context.Context, *testing.T) jsonrpc2.StreamServer
|
||||
getServer func(context.Context, *testing.T, func(*source.Options)) jsonrpc2.StreamServer
|
||||
}{
|
||||
{"singleton", Singleton, singletonServer},
|
||||
{"forwarded", Forwarded, r.forwardedServer},
|
||||
|
|
@ -268,7 +283,7 @@ func (r *Runner) Run(t *testing.T, files string, test TestFunc, opts ...RunOptio
|
|||
// better solution to ensure that all Go processes started by gopls have
|
||||
// exited before we clean up.
|
||||
r.AddCloser(sandbox)
|
||||
ss := tc.getServer(ctx, t)
|
||||
ss := tc.getServer(ctx, t, config.optionsHook)
|
||||
framer := jsonrpc2.NewRawStream
|
||||
ls := &loggingFramer{}
|
||||
if !config.skipLogs {
|
||||
|
|
@ -367,38 +382,38 @@ func (s *loggingFramer) printBuffers(testname string, w io.Writer) {
|
|||
fmt.Fprintf(os.Stderr, "#### End Gopls Test Logs for %q\n", testname)
|
||||
}
|
||||
|
||||
func singletonServer(ctx context.Context, t *testing.T) jsonrpc2.StreamServer {
|
||||
return lsprpc.NewStreamServer(cache.New(hooks.Options), false)
|
||||
func singletonServer(ctx context.Context, t *testing.T, optsHook func(*source.Options)) jsonrpc2.StreamServer {
|
||||
return lsprpc.NewStreamServer(cache.New(optsHook), false)
|
||||
}
|
||||
|
||||
func experimentalWorkspaceModule(_ context.Context, t *testing.T) jsonrpc2.StreamServer {
|
||||
func experimentalWorkspaceModule(_ context.Context, t *testing.T, optsHook func(*source.Options)) jsonrpc2.StreamServer {
|
||||
options := func(o *source.Options) {
|
||||
hooks.Options(o)
|
||||
optsHook(o)
|
||||
o.ExperimentalWorkspaceModule = true
|
||||
}
|
||||
return lsprpc.NewStreamServer(cache.New(options), false)
|
||||
}
|
||||
|
||||
func (r *Runner) forwardedServer(ctx context.Context, t *testing.T) jsonrpc2.StreamServer {
|
||||
ts := r.getTestServer()
|
||||
func (r *Runner) forwardedServer(ctx context.Context, t *testing.T, optsHook func(*source.Options)) jsonrpc2.StreamServer {
|
||||
ts := r.getTestServer(optsHook)
|
||||
return lsprpc.NewForwarder("tcp", ts.Addr)
|
||||
}
|
||||
|
||||
// getTestServer gets the shared test server instance to connect to, or creates
|
||||
// one if it doesn't exist.
|
||||
func (r *Runner) getTestServer() *servertest.TCPServer {
|
||||
func (r *Runner) getTestServer(optsHook func(*source.Options)) *servertest.TCPServer {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
if r.ts == nil {
|
||||
ctx := context.Background()
|
||||
ctx = debug.WithInstance(ctx, "", "off")
|
||||
ss := lsprpc.NewStreamServer(cache.New(hooks.Options), false)
|
||||
ss := lsprpc.NewStreamServer(cache.New(optsHook), false)
|
||||
r.ts = servertest.NewTCPServer(ctx, ss, nil)
|
||||
}
|
||||
return r.ts
|
||||
}
|
||||
|
||||
func (r *Runner) separateProcessServer(ctx context.Context, t *testing.T) jsonrpc2.StreamServer {
|
||||
func (r *Runner) separateProcessServer(ctx context.Context, t *testing.T, optsHook func(*source.Options)) jsonrpc2.StreamServer {
|
||||
// TODO(rfindley): can we use the autostart behavior here, instead of
|
||||
// pre-starting the remote?
|
||||
socket := r.getRemoteSocket(t)
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ func (r *runner) Completion(t *testing.T, src span.Span, test tests.Completion,
|
|||
opts.CompleteUnimported = false
|
||||
opts.InsertTextFormat = protocol.SnippetTextFormat
|
||||
opts.LiteralCompletions = strings.Contains(string(src.URI()), "literal")
|
||||
opts.PostfixCompletions = strings.Contains(string(src.URI()), "postfix")
|
||||
opts.ExperimentalPostfixCompletions = strings.Contains(string(src.URI()), "postfix")
|
||||
})
|
||||
got = tests.FilterBuiltins(src, got)
|
||||
want := expected(t, test, items)
|
||||
|
|
@ -100,7 +100,7 @@ func (r *runner) RankCompletion(t *testing.T, src span.Span, test tests.Completi
|
|||
opts.Matcher = source.Fuzzy
|
||||
opts.CompleteUnimported = false
|
||||
opts.LiteralCompletions = true
|
||||
opts.PostfixCompletions = true
|
||||
opts.ExperimentalPostfixCompletions = true
|
||||
})
|
||||
want := expected(t, test, items)
|
||||
if msg := tests.CheckCompletionOrder(want, got, true); msg != "" {
|
||||
|
|
|
|||
|
|
@ -221,6 +221,19 @@ var GeneratedAPIJSON = &APIJSON{
|
|||
Status: "advanced",
|
||||
Hierarchy: "ui.completion",
|
||||
},
|
||||
{
|
||||
Name: "experimentalPostfixCompletions",
|
||||
Type: "bool",
|
||||
Doc: "experimentalPostfixCompletions enables artifical method snippets\nsuch as \"someSlice.sort!\".\n",
|
||||
EnumKeys: EnumKeys{
|
||||
ValueType: "",
|
||||
Keys: nil,
|
||||
},
|
||||
EnumValues: nil,
|
||||
Default: "false",
|
||||
Status: "experimental",
|
||||
Hierarchy: "ui.completion",
|
||||
},
|
||||
{
|
||||
Name: "importShortcut",
|
||||
Type: "enum",
|
||||
|
|
|
|||
|
|
@ -522,7 +522,7 @@ func Completion(ctx context.Context, snapshot source.Snapshot, fh source.FileHan
|
|||
literal: opts.LiteralCompletions && opts.InsertTextFormat == protocol.SnippetTextFormat,
|
||||
budget: opts.CompletionBudget,
|
||||
snippets: opts.InsertTextFormat == protocol.SnippetTextFormat,
|
||||
postfix: opts.PostfixCompletions,
|
||||
postfix: opts.ExperimentalPostfixCompletions,
|
||||
},
|
||||
// default to a matcher that always matches
|
||||
matcher: prefixMatcher(""),
|
||||
|
|
|
|||
|
|
@ -129,8 +129,9 @@ func DefaultOptions() *Options {
|
|||
SymbolStyle: DynamicSymbols,
|
||||
},
|
||||
CompletionOptions: CompletionOptions{
|
||||
Matcher: Fuzzy,
|
||||
CompletionBudget: 100 * time.Millisecond,
|
||||
Matcher: Fuzzy,
|
||||
CompletionBudget: 100 * time.Millisecond,
|
||||
ExperimentalPostfixCompletions: false,
|
||||
},
|
||||
Codelenses: map[string]bool{
|
||||
string(command.Generate): true,
|
||||
|
|
@ -144,7 +145,6 @@ func DefaultOptions() *Options {
|
|||
},
|
||||
InternalOptions: InternalOptions{
|
||||
LiteralCompletions: true,
|
||||
PostfixCompletions: true,
|
||||
TempModfile: true,
|
||||
CompleteUnimported: true,
|
||||
CompletionDocumentation: true,
|
||||
|
|
@ -294,6 +294,10 @@ type CompletionOptions struct {
|
|||
// Matcher sets the algorithm that is used when calculating completion
|
||||
// candidates.
|
||||
Matcher Matcher `status:"advanced"`
|
||||
|
||||
// ExperimentalPostfixCompletions enables artifical method snippets
|
||||
// such as "someSlice.sort!".
|
||||
ExperimentalPostfixCompletions bool `status:"experimental"`
|
||||
}
|
||||
|
||||
type DocumentationOptions struct {
|
||||
|
|
@ -437,11 +441,6 @@ type InternalOptions struct {
|
|||
// their expected values.
|
||||
LiteralCompletions bool
|
||||
|
||||
// PostfixCompletions enables pseudo method snippets such as
|
||||
// "someSlice.sort!". Tests disable this flag to simplify their
|
||||
// expected values.
|
||||
PostfixCompletions bool
|
||||
|
||||
// VerboseWorkDoneProgress controls whether the LSP server should send
|
||||
// progress reports for all work done outside the scope of an RPC.
|
||||
// Used by the regression tests.
|
||||
|
|
@ -687,6 +686,7 @@ func (o *Options) AddStaticcheckAnalyzer(a *analysis.Analyzer) {
|
|||
// should be enabled in enableAllExperimentMaps.
|
||||
func (o *Options) enableAllExperiments() {
|
||||
o.SemanticTokens = true
|
||||
o.ExperimentalPostfixCompletions = true
|
||||
}
|
||||
|
||||
func (o *Options) enableAllExperimentMaps() {
|
||||
|
|
@ -858,6 +858,9 @@ func (o *Options) set(name string, value interface{}, seen map[string]struct{})
|
|||
case "expandWorkspaceToModule":
|
||||
result.setBool(&o.ExpandWorkspaceToModule)
|
||||
|
||||
case "experimentalPostfixCompletions":
|
||||
result.setBool(&o.ExperimentalPostfixCompletions)
|
||||
|
||||
case "experimentalWorkspaceModule":
|
||||
result.setBool(&o.ExperimentalWorkspaceModule)
|
||||
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ func (r *runner) Completion(t *testing.T, src span.Span, test tests.Completion,
|
|||
opts.CompleteUnimported = false
|
||||
opts.InsertTextFormat = protocol.SnippetTextFormat
|
||||
opts.LiteralCompletions = strings.Contains(string(src.URI()), "literal")
|
||||
opts.PostfixCompletions = strings.Contains(string(src.URI()), "postfix")
|
||||
opts.ExperimentalPostfixCompletions = strings.Contains(string(src.URI()), "postfix")
|
||||
})
|
||||
got = tests.FilterBuiltins(src, got)
|
||||
if diff := tests.DiffCompletionItems(want, got); diff != "" {
|
||||
|
|
@ -277,7 +277,7 @@ func (r *runner) RankCompletion(t *testing.T, src span.Span, test tests.Completi
|
|||
_, got := r.callCompletion(t, src, func(opts *source.Options) {
|
||||
opts.DeepCompletion = true
|
||||
opts.Matcher = source.Fuzzy
|
||||
opts.PostfixCompletions = true
|
||||
opts.ExperimentalPostfixCompletions = true
|
||||
})
|
||||
if msg := tests.CheckCompletionOrder(want, got, true); msg != "" {
|
||||
t.Errorf("%s: %s", src, msg)
|
||||
|
|
@ -931,6 +931,7 @@ func (r *runner) SignatureHelp(t *testing.T, spn span.Span, want *protocol.Signa
|
|||
|
||||
// These are pure LSP features, no source level functionality to be tested.
|
||||
func (r *runner) Link(t *testing.T, uri span.URI, wantLinks []tests.Link) {}
|
||||
|
||||
func (r *runner) SuggestedFix(t *testing.T, spn span.Span, actionKinds []string, expectedActions int) {
|
||||
}
|
||||
func (r *runner) FunctionExtraction(t *testing.T, start span.Span, end span.Span) {}
|
||||
|
|
|
|||
Loading…
Reference in New Issue