gopls/internal/cmd: absorb tool package harness into cmd Move the CLI execution harness from gopls/internal/tool into gopls/internal/cmd, removing package tool entirely. This addresses review feedback to consolidate command interfaces, profiling flags, and execution runners into a single cohesive package before restructuring CLI flag parsing. And unexport internal structs and fields in package cmd, root application embeds unexported 'serve serve'. This helps isolate server instance flags (-listen, -logfile, -debug, -mcp.listen) so they no longer pollute root 'gopls --help' usage output. For golang/go#79906 Change-Id: I62689ff6ff7fbf345c64d3c90e14c3510b0729b8 Reviewed-on: https://go-review.googlesource.com/c/tools/+/795680 Reviewed-by: Alan Donovan <adonovan@google.com> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/gopls/internal/cmd/call_hierarchy.go b/gopls/internal/cmd/call_hierarchy.go index 890b21f..906e0ba 100644 --- a/gopls/internal/cmd/call_hierarchy.go +++ b/gopls/internal/cmd/call_hierarchy.go
@@ -11,12 +11,11 @@ "strings" "golang.org/x/tools/gopls/internal/protocol" - "golang.org/x/tools/gopls/internal/tool" ) // callHierarchy implements the callHierarchy verb for gopls. type callHierarchy struct { - app *Application + app *application } func (c *callHierarchy) Name() string { return "call_hierarchy" } @@ -36,7 +35,7 @@ func (c *callHierarchy) Run(ctx context.Context, args ...string) error { if len(args) != 1 { - return tool.CommandLineErrorf("call_hierarchy expects 1 argument (position)") + return commandLineErrorf("call_hierarchy expects 1 argument (position)") } cli, _, err := c.app.connect(ctx)
diff --git a/gopls/internal/cmd/capabilities_test.go b/gopls/internal/cmd/capabilities_test.go index a3a438f..0bac958 100644 --- a/gopls/internal/cmd/capabilities_test.go +++ b/gopls/internal/cmd/capabilities_test.go
@@ -37,7 +37,7 @@ } defer os.RemoveAll(tmpDir) - app := New() + app := newApplication() ctx := context.Background() // Initialize the client.
diff --git a/gopls/internal/cmd/check.go b/gopls/internal/cmd/check.go index a73d1dc..83e7291 100644 --- a/gopls/internal/cmd/check.go +++ b/gopls/internal/cmd/check.go
@@ -16,7 +16,7 @@ // check implements the check verb for gopls. type check struct { - app *Application + app *application Severity string `flag:"severity" help:"minimum diagnostic severity (hint, info, warning, or error)"` }
diff --git a/gopls/internal/cmd/cmd.go b/gopls/internal/cmd/cmd.go index fb38663..b0d80d0 100644 --- a/gopls/internal/cmd/cmd.go +++ b/gopls/internal/cmd/cmd.go
@@ -26,11 +26,10 @@ "golang.org/x/tools/gopls/internal/filecache" "golang.org/x/tools/gopls/internal/lsprpc" "golang.org/x/tools/gopls/internal/protocol" - "golang.org/x/tools/gopls/internal/protocol/command" + protocolcommand "golang.org/x/tools/gopls/internal/protocol/command" "golang.org/x/tools/gopls/internal/protocol/semtok" "golang.org/x/tools/gopls/internal/server" "golang.org/x/tools/gopls/internal/settings" - "golang.org/x/tools/gopls/internal/tool" "golang.org/x/tools/gopls/internal/util/browser" "golang.org/x/tools/gopls/internal/util/bug" "golang.org/x/tools/gopls/internal/util/moreslices" @@ -38,18 +37,17 @@ "golang.org/x/tools/internal/jsonrpc2" ) -// Application is the main application as passed to tool.Main -// It handles the main command line parsing and dispatch to the sub commands. -type Application struct { +// application represents the root gopls command and coordinates subcommand dispatch. +type application struct { // Core application flags // Embed the basic profiling flags supported by the tool package - tool.Profile + ProfileFlags // We include the server configuration directly for now, so the flags work // even without the verb. // TODO: Remove this when we stop allowing the serve verb by default. - serve Serve + serve serve // the options configuring function to invoke when building a server options func(*settings.Options) @@ -121,13 +119,13 @@ return args } -func (app *Application) verbose() bool { +func (app *application) verbose() bool { return app.Verbose || app.VeryVerbose } -// New returns a new Application ready to run. -func New() *Application { - app := &Application{ +// newApplication returns a new application ready to run. +func newApplication() *application { + app := &application{ RemoteFlags: RemoteFlags{ RemoteListenTimeout: 1 * time.Minute, }, @@ -136,20 +134,20 @@ return app } -// Name implements tool.Command returning the binary name. -func (app *Application) Name() string { return "gopls" } +// Name implements command returning the binary name. +func (app *application) Name() string { return "gopls" } -// Usage implements tool.Command returning empty extra argument usage. -func (app *Application) Usage() string { return "" } +// Usage implements command returning empty extra argument usage. +func (app *application) Usage() string { return "" } -// ShortHelp implements tool.Command returning the main binary help. -func (app *Application) ShortHelp() string { +// ShortHelp implements command returning the main binary help. +func (app *application) ShortHelp() string { return "" } -// DetailedHelp implements tool.Command returning the main binary help. +// DetailedHelp implements command returning the main binary help. // This includes the short help for all the sub commands. -func (app *Application) DetailedHelp(f *flag.FlagSet) { +func (app *application) DetailedHelp(f *flag.FlagSet) { w := tabwriter.NewWriter(f.Output(), 0, 0, 2, ' ', 0) defer w.Flush() @@ -263,7 +261,7 @@ // sub command as specified by the first argument. // If no arguments are passed it will invoke the server sub command, as a // temporary measure for compatibility. -func (app *Application) Run(ctx context.Context, args ...string) error { +func (app *application) Run(ctx context.Context, args ...string) error { // In the category of "things we can do while waiting for the Go command": // Pre-initialize the filecache, which takes ~50ms to hash the gopls // executable, and immediately runs a gc. @@ -272,31 +270,31 @@ ctx = debug.WithInstance(ctx, app.OTel) if len(args) == 0 { s := flag.NewFlagSet(app.Name(), flag.ExitOnError) - return tool.Run(ctx, s, &app.serve, args) + return runCommand(ctx, s, &app.serve, args) } command, args := args[0], args[1:] for _, c := range app.Commands() { if c.Name() == command { s := flag.NewFlagSet(app.Name(), flag.ExitOnError) - return tool.Run(ctx, s, c, args) + return runCommand(ctx, s, c, args) } } - return tool.CommandLineErrorf("Unknown command %v", command) + return commandLineErrorf("Unknown command %v", command) } // Commands returns the set of commands supported by the gopls tool on the // command line. // The command is specified by the first non flag argument. -func (app *Application) Commands() []tool.Command { - var commands []tool.Command +func (app *application) Commands() []command { + var commands []command commands = append(commands, app.mainCommands()...) commands = append(commands, app.featureCommands()...) commands = append(commands, app.internalCommands()...) return commands } -func (app *Application) mainCommands() []tool.Command { - return []tool.Command{ +func (app *application) mainCommands() []command { + return []command{ &app.serve, &version{app: app}, &help{app: app}, @@ -305,14 +303,14 @@ } } -func (app *Application) internalCommands() []tool.Command { - return []tool.Command{ +func (app *application) internalCommands() []command { + return []command{ &vulncheck{app: app}, } } -func (app *Application) featureCommands() []tool.Command { - return []tool.Command{ +func (app *application) featureCommands() []command { + return []command{ &callHierarchy{app: app}, &check{app: app, Severity: "warning"}, &codeaction{app: app}, @@ -340,7 +338,8 @@ } // connect creates and initializes a new in-process gopls LSP session. -func (app *Application) connect(ctx context.Context) (*client, *cache.Session, error) { +func (app *application) connect(ctx context.Context) (*client, *cache.Session, error) { + root, err := os.Getwd() if err != nil { return nil, nil, fmt.Errorf("finding workdir: %v", err) @@ -432,7 +431,7 @@ // connection; it conceptually corresponds to a single call to // connect(2). type client struct { - app *Application + app *application server protocol.Server initializeResult *protocol.InitializeResult // includes server capabilities @@ -454,7 +453,7 @@ diagnostics []protocol.Diagnostic } -func newClient(app *Application) *client { +func newClient(app *application) *client { return &client{ app: app, files: make(map[protocol.DocumentURI]*cmdFile), @@ -850,7 +849,7 @@ } func diagnoseFiles(ctx context.Context, server protocol.Server, files []protocol.DocumentURI) error { - cmd := command.NewDiagnoseFilesCommand("Diagnose files", command.DiagnoseFilesArgs{ + cmd := protocolcommand.NewDiagnoseFilesCommand("Diagnose files", protocolcommand.DiagnoseFilesArgs{ Files: files, }) _, err := executeCommand(ctx, server, cmd)
diff --git a/gopls/internal/cmd/codeaction.go b/gopls/internal/cmd/codeaction.go index 974b499..f15ccdf 100644 --- a/gopls/internal/cmd/codeaction.go +++ b/gopls/internal/cmd/codeaction.go
@@ -13,7 +13,6 @@ "strings" "golang.org/x/tools/gopls/internal/protocol" - "golang.org/x/tools/gopls/internal/tool" ) // codeaction implements the codeaction verb for gopls. @@ -23,7 +22,7 @@ Title string `flag:"title" help:"regular expression to match title"` Exec bool `flag:"exec" help:"execute the first matching code action"` - app *Application + app *application } func (cmd *codeaction) Name() string { return "codeaction" } @@ -105,7 +104,7 @@ func (cmd *codeaction) Run(ctx context.Context, args ...string) error { if len(args) < 1 { - return tool.CommandLineErrorf("codeaction expects at least 1 argument") + return commandLineErrorf("codeaction expects at least 1 argument") } cmd.app.editFlags = &cmd.EditFlags cli, _, err := cmd.app.connect(ctx)
diff --git a/gopls/internal/cmd/codelens.go b/gopls/internal/cmd/codelens.go index 1d32493..e48462a 100644 --- a/gopls/internal/cmd/codelens.go +++ b/gopls/internal/cmd/codelens.go
@@ -11,13 +11,12 @@ "golang.org/x/tools/gopls/internal/protocol" "golang.org/x/tools/gopls/internal/settings" - "golang.org/x/tools/gopls/internal/tool" ) // codelens implements the codelens verb for gopls. type codelens struct { EditFlags - app *Application + app *application Exec bool `flag:"exec" help:"execute the first matching code lens"` } @@ -56,14 +55,14 @@ var filename, title string switch len(args) { case 0: - return tool.CommandLineErrorf("codelens requires a file name") + return commandLineErrorf("codelens requires a file name") case 2: title = args[1] fallthrough case 1: filename = args[0] default: - return tool.CommandLineErrorf("codelens expects at most two arguments") + return commandLineErrorf("codelens expects at most two arguments") } r.app.editFlags = &r.EditFlags // in case a codelens perform an edit
diff --git a/gopls/internal/cmd/definition.go b/gopls/internal/cmd/definition.go index 26dd86f..99fe5dc 100644 --- a/gopls/internal/cmd/definition.go +++ b/gopls/internal/cmd/definition.go
@@ -14,11 +14,10 @@ "golang.org/x/tools/gopls/internal/protocol" "golang.org/x/tools/gopls/internal/settings" - "golang.org/x/tools/gopls/internal/tool" ) -// A Definition is the result of a 'definition' query. -type Definition struct { +// A definitionJSON is the result of a 'definition' query. +type definitionJSON struct { Span span `json:"span"` // span of the definition Description string `json:"description"` // description of the denoted object } @@ -34,7 +33,7 @@ // definition implements the definition verb for gopls. type definition struct { - app *Application + app *application JSON bool `flag:"json" help:"emit output in JSON format"` MarkdownSupported bool `flag:"markdown" help:"support markdown in responses"` @@ -60,7 +59,7 @@ // results to stdout. func (d *definition) Run(ctx context.Context, args ...string) error { if len(args) != 1 { - return tool.CommandLineErrorf("definition expects 1 argument") + return commandLineErrorf("definition expects 1 argument") } // Plaintext makes more sense for the command line. opts := d.app.options @@ -119,7 +118,7 @@ description = strings.TrimSpace(hover.Contents.Value) } - result := &Definition{ + result := &definitionJSON{ Span: definition, Description: description, }
diff --git a/gopls/internal/cmd/execute.go b/gopls/internal/cmd/execute.go index 0c40e59..c139635 100644 --- a/gopls/internal/cmd/execute.go +++ b/gopls/internal/cmd/execute.go
@@ -15,15 +15,14 @@ "golang.org/x/tools/gopls/internal/filecache" "golang.org/x/tools/gopls/internal/protocol" - "golang.org/x/tools/gopls/internal/protocol/command" - "golang.org/x/tools/gopls/internal/tool" + protocolcommand "golang.org/x/tools/gopls/internal/protocol/command" "golang.org/x/tools/gopls/internal/util/bug" ) // execute implements the LSP ExecuteCommand verb for gopls. type execute struct { EditFlags - app *Application + app *application } func (e *execute) Name() string { return "execute" } @@ -48,6 +47,7 @@ execute-flags: `) + printFlagDefaults(f) } @@ -62,11 +62,11 @@ } if len(args) == 0 { - return tool.CommandLineErrorf("execute requires a command name") + return commandLineErrorf("execute requires a command name") } cmd := args[0] - if !slices.Contains(command.Commands, command.Command(cmd)) { - return tool.CommandLineErrorf("unrecognized command: %s", cmd) + if !slices.Contains(protocolcommand.Commands, protocolcommand.Command(cmd)) { + return commandLineErrorf("unrecognized command: %s", cmd) } // A command may have multiple arguments, though the only one
diff --git a/gopls/internal/cmd/export_test.go b/gopls/internal/cmd/export_test.go new file mode 100644 index 0000000..800a9d7 --- /dev/null +++ b/gopls/internal/cmd/export_test.go
@@ -0,0 +1,24 @@ +// Copyright 2026 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. + +// This file exports unexported internal symbols exclusively +// for use in package cmd_test. + +package cmd + +type ( + DefinitionJSON = definitionJSON + StatsJSON = statsJSON +) + +// CommandNames returns the names of all commands, including the root command. +func CommandNames() []string { + var names []string + app := newApplication() + for _, c := range app.Commands() { + names = append(names, c.Name()) + } + names = append(names, app.Name()) + return names +}
diff --git a/gopls/internal/cmd/folding_range.go b/gopls/internal/cmd/folding_range.go index e2c2f4b..37c9617 100644 --- a/gopls/internal/cmd/folding_range.go +++ b/gopls/internal/cmd/folding_range.go
@@ -10,12 +10,11 @@ "fmt" "golang.org/x/tools/gopls/internal/protocol" - "golang.org/x/tools/gopls/internal/tool" ) // foldingRanges implements the folding_ranges verb for gopls type foldingRanges struct { - app *Application + app *application } func (r *foldingRanges) Name() string { return "folding_ranges" } @@ -33,7 +32,7 @@ func (r *foldingRanges) Run(ctx context.Context, args ...string) error { if len(args) != 1 { - return tool.CommandLineErrorf("folding_ranges expects 1 argument (file)") + return commandLineErrorf("folding_ranges expects 1 argument (file)") } cli, _, err := r.app.connect(ctx)
diff --git a/gopls/internal/cmd/format.go b/gopls/internal/cmd/format.go index 8766d9c..16da4ff 100644 --- a/gopls/internal/cmd/format.go +++ b/gopls/internal/cmd/format.go
@@ -15,7 +15,7 @@ // format implements the format verb for gopls. type format struct { EditFlags - app *Application + app *application } func (c *format) Name() string { return "format" }
diff --git a/gopls/internal/tool/tool.go b/gopls/internal/cmd/harness.go similarity index 74% rename from gopls/internal/tool/tool.go rename to gopls/internal/cmd/harness.go index 53031ea..3873bbf 100644 --- a/gopls/internal/tool/tool.go +++ b/gopls/internal/cmd/harness.go
@@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package tool is a harness for writing Go tools. -package tool +package cmd import ( "context" @@ -19,21 +18,12 @@ "time" ) -// This file is a harness for writing your main function. -// -// It recursively scans the command object for fields with a tag containing -// `flag:"flagnames" help:"short help text"` -// uses all those fields to build command line flags. It will split flagnames on -// commas and add a flag per name. -// It expects the Command type to have a method -// Run(context.Context, args...string) error -// which it invokes only after all command line flag processing has been finished. -// If Run returns an error, the error will be printed to stderr and the -// application will quit with a non zero exit status. +// This file defines common flags and helper functions +// that coordinate flag registration via reflection. -// Profile can be embedded in your application struct to automatically -// add command line arguments and handling for the common profiling methods. -type Profile struct { +// ProfileFlags can be embedded in your application struct to automatically +// add command line arguments and handling for common profiling methods. +type ProfileFlags struct { CPU string `flag:"profile.cpu" help:"write CPU profile to this file"` Memory string `flag:"profile.mem" help:"write memory profile to this file"` Alloc string `flag:"profile.alloc" help:"write alloc profile to this file"` @@ -41,8 +31,8 @@ Block string `flag:"profile.block" help:"write block profile to this file"` } -// Command is the interface that must be satisfied by an object passed to Main. -type Command interface { +// command represents an executable CLI command or subcommand within gopls. +type command interface { // Name returns the command's name. It is used in help and error messages. Name() string // Most of the help usage is automatically generated, this string should only @@ -61,21 +51,24 @@ Run(ctx context.Context, args ...string) error } -type Subcommand interface { - Command +type subcommand interface { + // TODO(hyangah): merge with command. It is unclear why we need + // to keep command and subcommand separate. + + command Parent() string } -// This is the type returned by CommandLineErrorf, which causes the outer main +// This is the type returned by commandLineErrorf, which causes the outer main // to trigger printing of the command line help. type commandLineError string func (e commandLineError) Error() string { return string(e) } -// CommandLineErrorf is like fmt.Errorf except that it returns a value that +// commandLineErrorf is like fmt.Errorf except that it returns a value that // triggers printing of the command line help. // In general you should use this when generating command line validation errors. -func CommandLineErrorf(message string, args ...any) error { +func commandLineErrorf(message string, args ...any) error { return commandLineError(fmt.Sprintf(message, args...)) } @@ -83,9 +76,12 @@ // It will only return if there was no error. If an error // was encountered it is printed to standard error and the // application exits with an exit code of 2. -func Main(ctx context.Context, app Command, args []string) { +func Main() { + ctx := context.Background() + args := os.Args[1:] + app := newApplication() s := flag.NewFlagSet(app.Name(), flag.ExitOnError) - if err := Run(ctx, s, app, args); err != nil { + if err := runCommand(ctx, s, app, args); err != nil { fmt.Fprintf(s.Output(), "%s: %v\n", app.Name(), err) if _, printHelp := err.(commandLineError); printHelp { // TODO(adonovan): refine this. It causes @@ -98,27 +94,30 @@ } } -// Run is the inner loop for Main; invoked by Main, recursively by -// Run, and by various tests. It runs the application and returns an -// error. -func Run(ctx context.Context, s *flag.FlagSet, app Command, args []string) (resultErr error) { - s.Usage = func() { - if app.ShortHelp() != "" { - fmt.Fprintf(s.Output(), "%s\n\nUsage:\n ", app.ShortHelp()) - if sub, ok := app.(Subcommand); ok && sub.Parent() != "" { - fmt.Fprintf(s.Output(), "%s [flags] %s", sub.Parent(), app.Name()) - } else { - fmt.Fprintf(s.Output(), "%s [flags]", app.Name()) - } - if usage := app.Usage(); usage != "" { - fmt.Fprintf(s.Output(), " %s", usage) - } - fmt.Fprint(s.Output(), "\n") +// printHelp prints the usage and detailed help for any command to s.Output(). +func printHelp(s *flag.FlagSet, app command) { + if app.ShortHelp() != "" { + fmt.Fprintf(s.Output(), "%s\n\nUsage:\n ", app.ShortHelp()) + if sub, ok := app.(subcommand); ok && sub.Parent() != "" { + fmt.Fprintf(s.Output(), "%s [flags] %s", sub.Parent(), app.Name()) + } else { + fmt.Fprintf(s.Output(), "%s [flags]", app.Name()) } - app.DetailedHelp(s) + if usage := app.Usage(); usage != "" { + fmt.Fprintf(s.Output(), " %s", usage) + } + fmt.Fprintln(s.Output()) } - p := addFlags(s, reflect.StructField{}, reflect.ValueOf(app)) - if err := s.Parse(args); err != nil { + app.DetailedHelp(s) +} + +// runCommand executes cmd with the provided flagset and arguments. +func runCommand(ctx context.Context, flags *flag.FlagSet, cmd command, args []string) (resultErr error) { + flags.Usage = func() { printHelp(flags, cmd) } + // addFlags returns non-nil *ProfileFlags only if cmd embeds ProfileFlags. + // Only 'application' meets this criteria. + p := addFlags(flags, reflect.StructField{}, reflect.ValueOf(cmd)) + if err := flags.Parse(args); err != nil { return err } @@ -204,12 +203,12 @@ }() } - return app.Run(ctx, s.Args()...) + return cmd.Run(ctx, flags.Args()...) } // addFlags scans fields of structs recursively to find things with flag tags // and add them to the flag set. -func addFlags(f *flag.FlagSet, field reflect.StructField, value reflect.Value) *Profile { +func addFlags(f *flag.FlagSet, field reflect.StructField, value reflect.Value) *ProfileFlags { // is it a field we are allowed to reflect on? if field.PkgPath != "" { return nil @@ -238,7 +237,7 @@ // TODO(adonovan): there's no need for this special treatment of Profile: // The caller can use f.Lookup("profile.cpu") etc instead. - p, _ := value.Addr().Interface().(*Profile) + p, _ := value.Addr().Interface().(*ProfileFlags) // go through all the fields of the struct for i := 0; i < value.Type().NumField(); i++ { child := value.Type().Field(i)
diff --git a/gopls/internal/cmd/help_test.go b/gopls/internal/cmd/help_test.go index 43f9daf..3518dce 100644 --- a/gopls/internal/cmd/help_test.go +++ b/gopls/internal/cmd/help_test.go
@@ -29,15 +29,13 @@ func TestHelpFiles(t *testing.T) { testenv.NeedsGoBuild(t) // This is a lie. We actually need the source code. t.Parallel() - app := cmd.New() tree := writeTree(t, "") - for _, cmd := range append(app.Commands(), app) { - name := cmd.Name() + for _, name := range cmd.CommandNames() { t.Run(name, func(t *testing.T) { t.Parallel() args := []string{name, "-h"} // The output of 'gopls -h' is in usage.hlp - if cmd == app { + if name == "gopls" { args = args[1:] name = "usage" }
diff --git a/gopls/internal/cmd/highlight.go b/gopls/internal/cmd/highlight.go index 26c7fe6..6675336 100644 --- a/gopls/internal/cmd/highlight.go +++ b/gopls/internal/cmd/highlight.go
@@ -10,12 +10,11 @@ "fmt" "golang.org/x/tools/gopls/internal/protocol" - "golang.org/x/tools/gopls/internal/tool" ) // highlight implements the highlight verb for gopls. type highlight struct { - app *Application + app *application } func (r *highlight) Name() string { return "highlight" } @@ -35,7 +34,7 @@ func (r *highlight) Run(ctx context.Context, args ...string) error { if len(args) != 1 { - return tool.CommandLineErrorf("highlight expects 1 argument (position)") + return commandLineErrorf("highlight expects 1 argument (position)") } cli, _, err := r.app.connect(ctx)
diff --git a/gopls/internal/cmd/implementation.go b/gopls/internal/cmd/implementation.go index 0c845f4..8332603 100644 --- a/gopls/internal/cmd/implementation.go +++ b/gopls/internal/cmd/implementation.go
@@ -11,12 +11,11 @@ "sort" "golang.org/x/tools/gopls/internal/protocol" - "golang.org/x/tools/gopls/internal/tool" ) // implementation implements the implementation verb for gopls type implementation struct { - app *Application + app *application } func (i *implementation) Name() string { return "implementation" } @@ -36,7 +35,7 @@ func (i *implementation) Run(ctx context.Context, args ...string) error { if len(args) != 1 { - return tool.CommandLineErrorf("implementation expects 1 argument (position)") + return commandLineErrorf("implementation expects 1 argument (position)") } cli, _, err := i.app.connect(ctx)
diff --git a/gopls/internal/cmd/imports.go b/gopls/internal/cmd/imports.go index 0b8f143..8a29976 100644 --- a/gopls/internal/cmd/imports.go +++ b/gopls/internal/cmd/imports.go
@@ -10,13 +10,12 @@ "fmt" "golang.org/x/tools/gopls/internal/protocol" - "golang.org/x/tools/gopls/internal/tool" ) // imports implements the import verb for gopls. type imports struct { EditFlags - app *Application + app *application } func (t *imports) Name() string { return "imports" } @@ -40,7 +39,7 @@ // - otherwise, prints the new versions to stdout. func (t *imports) Run(ctx context.Context, args ...string) error { if len(args) != 1 { - return tool.CommandLineErrorf("imports expects 1 argument") + return commandLineErrorf("imports expects 1 argument") } t.app.editFlags = &t.EditFlags cli, _, err := t.app.connect(ctx)
diff --git a/gopls/internal/cmd/info.go b/gopls/internal/cmd/info.go index dd4f991..7dd6237 100644 --- a/gopls/internal/cmd/info.go +++ b/gopls/internal/cmd/info.go
@@ -18,12 +18,11 @@ "golang.org/x/tools/gopls/internal/debug" "golang.org/x/tools/gopls/internal/doc" licensespkg "golang.org/x/tools/gopls/internal/licenses" - "golang.org/x/tools/gopls/internal/tool" ) // help implements the help command. type help struct { - app *Application + app *application } func (h *help) Name() string { return "help" } @@ -43,7 +42,7 @@ // Run prints help information about a subcommand. func (h *help) Run(ctx context.Context, args ...string) error { - find := func(cmds []tool.Command, name string) tool.Command { + find := func(cmds []command, name string) command { for _, cmd := range cmds { if cmd.Name() == name { return cmd @@ -53,27 +52,27 @@ } // Find the subcommand denoted by args (empty => h.app). - var cmd tool.Command = h.app + var cmd command = h.app for i, arg := range args { cmd = find(getSubcommands(cmd), arg) if cmd == nil { - return tool.CommandLineErrorf( + return commandLineErrorf( "no such subcommand: %s", strings.Join(args[:i+1], " ")) } } // 'gopls help cmd subcmd' is equivalent to 'gopls cmd subcmd -h'. - // The flag package prints the usage information (defined by tool.Run) + // The flag package prints the usage information (defined by Run) // when it sees the -h flag. fs := flag.NewFlagSet(cmd.Name(), flag.ExitOnError) - return tool.Run(ctx, fs, h.app, append(args[:len(args):len(args)], "-h")) + return runCommand(ctx, fs, h.app, append(args[:len(args):len(args)], "-h")) } // version implements the version command. type version struct { JSON bool `flag:"json" help:"outputs in json format."` - app *Application + app *application } func (v *version) Name() string { return "version" } @@ -98,7 +97,7 @@ } type apiJSON struct { - app *Application + app *application } func (j *apiJSON) Name() string { return "api-json" } @@ -121,7 +120,7 @@ } type licenses struct { - app *Application + app *application } func (l *licenses) Name() string { return "licenses" }
diff --git a/gopls/internal/cmd/integration_test.go b/gopls/internal/cmd/integration_test.go index 170629c..ff4b353 100644 --- a/gopls/internal/cmd/integration_test.go +++ b/gopls/internal/cmd/integration_test.go
@@ -27,7 +27,6 @@ import ( "bytes" - "context" "encoding/json" "fmt" "math/rand" @@ -41,7 +40,6 @@ "golang.org/x/tools/gopls/internal/cmd" "golang.org/x/tools/gopls/internal/debug" "golang.org/x/tools/gopls/internal/protocol" - "golang.org/x/tools/gopls/internal/tool" "golang.org/x/tools/gopls/internal/util/bug" "golang.org/x/tools/gopls/internal/version" "golang.org/x/tools/internal/testenv" @@ -84,6 +82,17 @@ } } +func TestProfileFlags(t *testing.T) { + t.Parallel() + tree := writeTree(t, "") + cpuProfile := filepath.Join(t.TempDir(), "cpu.prof") + res := gopls(t, tree, "-profile.cpu="+cpuProfile, "version") + res.checkExit(true) + if info, err := os.Stat(cpuProfile); err != nil || info.Size() == 0 { + t.Errorf("expected non-empty profile file %s, got err=%v", cpuProfile, err) + } +} + // TestCheck tests the 'check' subcommand (check.go). func TestCheck(t *testing.T) { t.Parallel() @@ -302,7 +311,7 @@ { res := gopls(t, tree, "definition", "-json", "-markdown", "a.go:4:7") res.checkExit(true) - var defn cmd.Definition + var defn cmd.DefinitionJSON if res.toJSON(&defn) { if !strings.HasPrefix(defn.Description, "```go\nfunc fmt.Println") { t.Errorf("Description does not start with markdown code block. Got: %s", defn.Description) @@ -878,7 +887,7 @@ res := gopls(t, tree, "stats") res.checkExit(true) - var stats cmd.GoplsStats + var stats cmd.StatsJSON if err := json.Unmarshal([]byte(res.stdout), &stats); err != nil { t.Fatalf("failed to unmarshal JSON output of stats command: %v", err) } @@ -930,7 +939,7 @@ res2 := gopls(t, tree, "stats", "-anon") res2.checkExit(true) - var stats2 cmd.GoplsStats + var stats2 cmd.StatsJSON if err := json.Unmarshal([]byte(res2.stdout), &stats2); err != nil { t.Fatalf("failed to unmarshal JSON output of stats command: %v", err) } @@ -1116,7 +1125,7 @@ version.VersionOverride = v } - tool.Main(context.Background(), cmd.New(), os.Args[1:]) + cmd.Main() } // writeTree extracts a txtar archive into a new directory and returns its path.
diff --git a/gopls/internal/cmd/links.go b/gopls/internal/cmd/links.go index 3b08868..4681793 100644 --- a/gopls/internal/cmd/links.go +++ b/gopls/internal/cmd/links.go
@@ -12,14 +12,13 @@ "os" "golang.org/x/tools/gopls/internal/protocol" - "golang.org/x/tools/gopls/internal/tool" ) // links implements the links verb for gopls. type links struct { JSON bool `flag:"json" help:"emit document links in JSON format"` - app *Application + app *application } func (l *links) Name() string { return "links" } @@ -42,7 +41,7 @@ // - otherwise, prints the a list of unique links func (l *links) Run(ctx context.Context, args ...string) error { if len(args) != 1 { - return tool.CommandLineErrorf("links expects 1 argument") + return commandLineErrorf("links expects 1 argument") } cli, _, err := l.app.connect(ctx) if err != nil {
diff --git a/gopls/internal/cmd/mcp.go b/gopls/internal/cmd/mcp.go index a35f049..1175ae0 100644 --- a/gopls/internal/cmd/mcp.go +++ b/gopls/internal/cmd/mcp.go
@@ -21,7 +21,7 @@ ) type headlessMCP struct { - app *Application + app *application Address string `flag:"listen" help:"the address on which to run the mcp server"` Logfile string `flag:"logfile" help:"filename to log to; if unset, logs to stderr"` @@ -47,7 +47,12 @@ } func (m *headlessMCP) Run(ctx context.Context, args ...string) error { + // TODO(hxjiang): properly support remote mode (https://github.com/golang/go/issues/78668). + if m.app.Remote != "" { + return commandLineErrorf("mcp does not currently support remote mode") + } if m.Instructions { + fmt.Println(internalmcp.Instructions) return nil }
diff --git a/gopls/internal/cmd/prepare_rename.go b/gopls/internal/cmd/prepare_rename.go index 72f42af..715f1fd 100644 --- a/gopls/internal/cmd/prepare_rename.go +++ b/gopls/internal/cmd/prepare_rename.go
@@ -11,12 +11,11 @@ "fmt" "golang.org/x/tools/gopls/internal/protocol" - "golang.org/x/tools/gopls/internal/tool" ) // prepareRename implements the prepare_rename verb for gopls. type prepareRename struct { - app *Application + app *application } func (r *prepareRename) Name() string { return "prepare_rename" } @@ -34,13 +33,13 @@ printFlagDefaults(f) } -// ErrInvalidRenamePosition is returned when prepareRename is run at a position that +// errInvalidRenamePosition is returned when prepareRename is run at a position that // is not a candidate for renaming. -var ErrInvalidRenamePosition = errors.New("request is not valid at the given position") +var errInvalidRenamePosition = errors.New("request is not valid at the given position") func (r *prepareRename) Run(ctx context.Context, args ...string) error { if len(args) != 1 { - return tool.CommandLineErrorf("prepare_rename expects 1 argument (file)") + return commandLineErrorf("prepare_rename expects 1 argument (file)") } cli, _, err := r.app.connect(ctx) @@ -66,7 +65,7 @@ return fmt.Errorf("prepare_rename failed: %w", err) } if result == nil { - return ErrInvalidRenamePosition + return errInvalidRenamePosition } s, err := file.rangeSpan(result.Range)
diff --git a/gopls/internal/cmd/references.go b/gopls/internal/cmd/references.go index a91925f..def3f0b 100644 --- a/gopls/internal/cmd/references.go +++ b/gopls/internal/cmd/references.go
@@ -11,14 +11,13 @@ "sort" "golang.org/x/tools/gopls/internal/protocol" - "golang.org/x/tools/gopls/internal/tool" ) // references implements the references verb for gopls type references struct { IncludeDeclaration bool `flag:"d,declaration" help:"include the declaration of the specified identifier in the results"` - app *Application + app *application } func (r *references) Name() string { return "references" } @@ -40,7 +39,7 @@ func (r *references) Run(ctx context.Context, args ...string) error { if len(args) != 1 { - return tool.CommandLineErrorf("references expects 1 argument (position)") + return commandLineErrorf("references expects 1 argument (position)") } cli, _, err := r.app.connect(ctx)
diff --git a/gopls/internal/cmd/remote.go b/gopls/internal/cmd/remote.go index c100164..65614d3 100644 --- a/gopls/internal/cmd/remote.go +++ b/gopls/internal/cmd/remote.go
@@ -14,15 +14,15 @@ "os" "golang.org/x/tools/gopls/internal/lsprpc" - "golang.org/x/tools/gopls/internal/protocol/command" + protocolcommand "golang.org/x/tools/gopls/internal/protocol/command" ) type remote struct { - app *Application + app *application subcommands } -func newRemote(app *Application) *remote { +func newRemote(app *application) *remote { return &remote{ app: app, subcommands: subcommands{ @@ -44,7 +44,7 @@ // listSessions is an inspect subcommand to list current sessions. type listSessions struct { - app *Application + app *application } func (c *listSessions) Name() string { return "sessions" } @@ -91,7 +91,7 @@ } type startDebugging struct { - app *Application + app *application } func (c *startDebugging) Name() string { return "debug" } @@ -132,11 +132,11 @@ if len(args) > 0 { debugAddr = args[0] } - debugArgs := command.DebuggingArgs{ + debugArgs := protocolcommand.DebuggingArgs{ Addr: debugAddr, } - var result command.DebuggingResult - if err := lsprpc.ExecuteCommand(ctx, remote, command.StartDebugging.String(), debugArgs, &result); err != nil { + var result protocolcommand.DebuggingResult + if err := lsprpc.ExecuteCommand(ctx, remote, protocolcommand.StartDebugging.String(), debugArgs, &result); err != nil { return err } if len(result.URLs) == 0 {
diff --git a/gopls/internal/cmd/rename.go b/gopls/internal/cmd/rename.go index 1922f40..6afdedd 100644 --- a/gopls/internal/cmd/rename.go +++ b/gopls/internal/cmd/rename.go
@@ -10,13 +10,12 @@ "fmt" "golang.org/x/tools/gopls/internal/protocol" - "golang.org/x/tools/gopls/internal/tool" ) // rename implements the rename verb for gopls. type rename struct { EditFlags - app *Application + app *application } func (r *rename) Name() string { return "rename" } @@ -42,7 +41,7 @@ // - otherwise, prints the new versions to stdout. func (r *rename) Run(ctx context.Context, args ...string) error { if len(args) != 2 { - return tool.CommandLineErrorf("rename expects 2 arguments (position, new name)") + return commandLineErrorf("rename expects 2 arguments (position, new name)") } r.app.editFlags = &r.EditFlags cli, _, err := r.app.connect(ctx)
diff --git a/gopls/internal/cmd/semantictokens.go b/gopls/internal/cmd/semantictokens.go index 6dd0052..c971945 100644 --- a/gopls/internal/cmd/semantictokens.go +++ b/gopls/internal/cmd/semantictokens.go
@@ -43,7 +43,7 @@ // the gopls coordinate system type semanticToken struct { - app *Application + app *application } func (c *semanticToken) Name() string { return "semtok" } @@ -114,8 +114,8 @@ // prefixes for semantic token comments const ( - SemanticLeft = "/*⇐" - SemanticRight = "/*⇒" + semanticLeft = "/*⇐" + semanticRight = "/*⇒" ) func markLine(m mark, lines [][]byte) { @@ -126,7 +126,7 @@ if m.typ == "namespace" && m.offset-1+m.len < len(l) && l[m.offset-1+m.len] == '"' { // it is the last component of an import spec // cannot put a comment inside a string - insert = fmt.Sprintf("%s%d,namespace,[]*/", SemanticLeft, length) + insert = fmt.Sprintf("%s%d,namespace,[]*/", semanticLeft, length) splitAt = m.offset + m.len } else { // be careful not to generate //* @@ -134,7 +134,7 @@ if splitAt-1 >= 0 && l[splitAt-1] == '/' { spacer = " " } - insert = fmt.Sprintf("%s%s%d,%s,%v*/", spacer, SemanticRight, length, m.typ, m.mods) + insert = fmt.Sprintf("%s%s%d,%s,%v*/", spacer, semanticRight, length, m.typ, m.mods) } x := append([]byte(insert), l[splitAt:]...) l = append(l[:splitAt], x...)
diff --git a/gopls/internal/cmd/serve.go b/gopls/internal/cmd/serve.go index 3861d62..7df303b 100644 --- a/gopls/internal/cmd/serve.go +++ b/gopls/internal/cmd/serve.go
@@ -21,14 +21,13 @@ "golang.org/x/tools/gopls/internal/lsprpc" "golang.org/x/tools/gopls/internal/mcp" "golang.org/x/tools/gopls/internal/protocol" - "golang.org/x/tools/gopls/internal/tool" "golang.org/x/tools/gopls/internal/util/fakenet" "golang.org/x/tools/internal/jsonrpc2" ) -// Serve is a struct that exposes the configurable parts of the LSP and MCP +// serve is a struct that exposes the configurable parts of the LSP and MCP // server as flags, in the right form for tool.Main to consume. -type Serve struct { +type serve struct { Logfile string `flag:"logfile" help:"filename to log to. if value is \"auto\", then logging to a default output file is enabled"` Mode string `flag:"mode" help:"no effect"` Address string `flag:"listen" help:"address on which to listen for remote connections. If prefixed by 'unix;', the subsequent address is assumed to be a unix domain socket. Otherwise, TCP is used."` @@ -39,16 +38,16 @@ // MCP Server related configurations. MCPAddress string `flag:"mcp.listen" help:"experimental: address on which to listen for model context protocol connections. If port is localhost:0, pick a random port in localhost instead."` - app *Application + app *application } -func (s *Serve) Name() string { return "serve" } -func (s *Serve) Parent() string { return s.app.Name() } -func (s *Serve) Usage() string { return "[server-flags]" } -func (s *Serve) ShortHelp() string { +func (s *serve) Name() string { return "serve" } +func (s *serve) Parent() string { return s.app.Name() } +func (s *serve) Usage() string { return "[server-flags]" } +func (s *serve) ShortHelp() string { return "run a server for Go code using the Language Server Protocol" } -func (s *Serve) DetailedHelp(f *flag.FlagSet) { +func (s *serve) DetailedHelp(f *flag.FlagSet) { fmt.Fprint(f.Output(), ` gopls [flags] [server-flags] The server communicates using JSONRPC2 on stdin and stdout, and is intended to be run directly as @@ -61,9 +60,9 @@ // Run configures a server based on the flags, and then runs it. // It blocks until the server shuts down. -func (s *Serve) Run(ctx context.Context, args ...string) error { +func (s *serve) Run(ctx context.Context, args ...string) error { if len(args) > 0 { - return tool.CommandLineErrorf("server does not take arguments, got %v", args) + return commandLineErrorf("server does not take arguments, got %v", args) } di := debug.GetInstance(ctx)
diff --git a/gopls/internal/cmd/signature.go b/gopls/internal/cmd/signature.go index e1c5372..9f62afd 100644 --- a/gopls/internal/cmd/signature.go +++ b/gopls/internal/cmd/signature.go
@@ -10,12 +10,11 @@ "fmt" "golang.org/x/tools/gopls/internal/protocol" - "golang.org/x/tools/gopls/internal/tool" ) // signature implements the signature verb for gopls type signature struct { - app *Application + app *application } func (r *signature) Name() string { return "signature" } @@ -35,7 +34,7 @@ func (r *signature) Run(ctx context.Context, args ...string) error { if len(args) != 1 { - return tool.CommandLineErrorf("signature expects 1 argument (position)") + return commandLineErrorf("signature expects 1 argument (position)") } cli, _, err := r.app.connect(ctx) @@ -65,7 +64,7 @@ } if s == nil || len(s.Signatures) == 0 { - return tool.CommandLineErrorf("%v: not a function", from) + return commandLineErrorf("%v: not a function", from) } // there is only ever one possible signature,
diff --git a/gopls/internal/cmd/stats.go b/gopls/internal/cmd/stats.go index 2fad301..af8c77a 100644 --- a/gopls/internal/cmd/stats.go +++ b/gopls/internal/cmd/stats.go
@@ -20,7 +20,7 @@ "golang.org/x/tools/gopls/internal/filecache" "golang.org/x/tools/gopls/internal/protocol" - "golang.org/x/tools/gopls/internal/protocol/command" + protocolcommand "golang.org/x/tools/gopls/internal/protocol/command" "golang.org/x/tools/gopls/internal/settings" "golang.org/x/tools/gopls/internal/util/bug" versionpkg "golang.org/x/tools/gopls/internal/version" @@ -28,7 +28,7 @@ ) type stats struct { - app *Application + app *application Anon bool `flag:"anon" help:"hide any fields that may contain user names, file names, or source code"` } @@ -55,19 +55,21 @@ } func (s *stats) Run(ctx context.Context, args ...string) error { + // stats does not work with -remote yet. + // Other sessions on the daemon may interfere with results. + // Additionally, the type assertions in below only work if progress + // notifications bypass jsonrpc2 serialization. + // TODO(hyangah): support remote mode by fetching server-side + // statistics and avoiding hangs during InitialWorkspaceLoad. if s.app.Remote != "" { - // stats does not work with -remote. - // Other sessions on the daemon may interfere with results. - // Additionally, the type assertions in below only work if progress - // notifications bypass jsonrpc2 serialization. - return fmt.Errorf("the stats subcommand does not work with -remote") + return commandLineErrorf("stats does not currently support remote mode") } if !s.app.Verbose { event.SetExporter(nil) // don't log errors to stderr } - stats := GoplsStats{ + stats := statsJSON{ GOOS: runtime.GOOS, GOARCH: runtime.GOARCH, GOPLSCACHE: os.Getenv("GOPLSCACHE"), @@ -85,6 +87,7 @@ } // do executes a timed section of the stats command. + do := func(name string, f func() error) (time.Duration, error) { start := time.Now() fmt.Fprintf(os.Stderr, "%-30s", name+"...") @@ -128,12 +131,12 @@ if _, err := do("Querying memstats", func() error { memStats, err := executeCommand(ctx, cli.server, &protocol.Command{ - Command: command.MemStats.String(), + Command: protocolcommand.MemStats.String(), }) if err != nil { return err } - stats.MemStats = memStats.(command.MemStatsResult) + stats.MemStats = memStats.(protocolcommand.MemStatsResult) return nil }); err != nil { return err @@ -141,12 +144,12 @@ if _, err := do("Querying workspace stats", func() error { wsStats, err := executeCommand(ctx, cli.server, &protocol.Command{ - Command: command.WorkspaceStats.String(), + Command: protocolcommand.WorkspaceStats.String(), }) if err != nil { return err } - stats.WorkspaceStats = wsStats.(command.WorkspaceStatsResult) + stats.WorkspaceStats = wsStats.(protocolcommand.WorkspaceStatsResult) return nil }); err != nil { return err @@ -192,13 +195,13 @@ return nil } -// GoplsStats holds information extracted from a gopls session in the current +// statsJSON holds information extracted from a gopls session in the current // workspace. // // Fields that should be printed with the -anon flag should be explicitly // marked as `anon:"ok"`. Only fields that cannot refer to user files or code // should be marked as such. -type GoplsStats struct { +type statsJSON struct { GOOS, GOARCH string `anon:"ok"` GOPLSCACHE string GoVersion string `anon:"ok"` @@ -207,9 +210,9 @@ InitialWorkspaceLoadDuration string `anon:"ok"` // in time.Duration string form CacheDir string BugReports []bug.Bug - MemStats command.MemStatsResult `anon:"ok"` - WorkspaceStats command.WorkspaceStatsResult `anon:"ok"` - DirStats dirStats `anon:"ok"` + MemStats protocolcommand.MemStatsResult `anon:"ok"` + WorkspaceStats protocolcommand.WorkspaceStatsResult `anon:"ok"` + DirStats dirStats `anon:"ok"` } type dirStats struct {
diff --git a/gopls/internal/cmd/subcommands.go b/gopls/internal/cmd/subcommands.go index 22034b9..ee5eaad 100644 --- a/gopls/internal/cmd/subcommands.go +++ b/gopls/internal/cmd/subcommands.go
@@ -9,13 +9,11 @@ "flag" "fmt" "text/tabwriter" - - "golang.org/x/tools/gopls/internal/tool" ) // subcommands is a helper that may be embedded for commands that delegate to // subcommands. -type subcommands []tool.Command +type subcommands []command func (s subcommands) DetailedHelp(f *flag.FlagSet) { w := tabwriter.NewWriter(f.Output(), 0, 0, 2, ' ', 0) @@ -31,26 +29,26 @@ func (s subcommands) Run(ctx context.Context, args ...string) error { if len(args) == 0 { - return tool.CommandLineErrorf("must provide subcommand") + return commandLineErrorf("must provide subcommand") } command, args := args[0], args[1:] for _, c := range s { if c.Name() == command { s := flag.NewFlagSet(c.Name(), flag.ExitOnError) - return tool.Run(ctx, s, c, args) + return runCommand(ctx, s, c, args) } } - return tool.CommandLineErrorf("unknown subcommand %v", command) + return commandLineErrorf("unknown subcommand %v", command) } -func (s subcommands) Commands() []tool.Command { return s } +func (s subcommands) Commands() []command { return s } -// getSubcommands returns the subcommands of a given Command. -func getSubcommands(a tool.Command) []tool.Command { - // This interface is satisfied both by tool.Commands - // that embed subcommands, and by *cmd.Application. +// getSubcommands returns the subcommands of a given command. +func getSubcommands(a command) []command { + // This interface is satisfied both by commands + // that embed subcommands, and by *cmd.application. type hasCommands interface { - Commands() []tool.Command + Commands() []command } if sub, ok := a.(hasCommands); ok { return sub.Commands()
diff --git a/gopls/internal/cmd/symbols.go b/gopls/internal/cmd/symbols.go index 4641b6a..36fc208 100644 --- a/gopls/internal/cmd/symbols.go +++ b/gopls/internal/cmd/symbols.go
@@ -12,12 +12,11 @@ "sort" "golang.org/x/tools/gopls/internal/protocol" - "golang.org/x/tools/gopls/internal/tool" ) // symbols implements the symbols verb for gopls type symbols struct { - app *Application + app *application } func (r *symbols) Name() string { return "symbols" } @@ -33,7 +32,7 @@ } func (r *symbols) Run(ctx context.Context, args ...string) error { if len(args) != 1 { - return tool.CommandLineErrorf("symbols expects 1 argument (position)") + return commandLineErrorf("symbols expects 1 argument (position)") } cli, _, err := r.app.connect(ctx)
diff --git a/gopls/internal/cmd/vulncheck.go b/gopls/internal/cmd/vulncheck.go index 7babf0d..97d1aa2 100644 --- a/gopls/internal/cmd/vulncheck.go +++ b/gopls/internal/cmd/vulncheck.go
@@ -16,7 +16,7 @@ // vulncheck implements the vulncheck command. // TODO(hakim): hide from the public. type vulncheck struct { - app *Application + app *application } func (v *vulncheck) Name() string { return "vulncheck" }
diff --git a/gopls/internal/cmd/workspace_symbol.go b/gopls/internal/cmd/workspace_symbol.go index cd4ebc6..07cac18 100644 --- a/gopls/internal/cmd/workspace_symbol.go +++ b/gopls/internal/cmd/workspace_symbol.go
@@ -12,14 +12,13 @@ "golang.org/x/tools/gopls/internal/protocol" "golang.org/x/tools/gopls/internal/settings" - "golang.org/x/tools/gopls/internal/tool" ) // workspaceSymbol implements the workspace_symbol verb for gopls. type workspaceSymbol struct { Matcher string `flag:"matcher" help:"specifies the type of matcher: fuzzy, fastfuzzy, casesensitive, or caseinsensitive.\nThe default is caseinsensitive."` - app *Application + app *application } func (r *workspaceSymbol) Name() string { return "workspace_symbol" } @@ -39,7 +38,7 @@ func (r *workspaceSymbol) Run(ctx context.Context, args ...string) error { if len(args) != 1 { - return tool.CommandLineErrorf("workspace_symbol expects 1 argument") + return commandLineErrorf("workspace_symbol expects 1 argument") } opts := r.app.options
diff --git a/gopls/internal/test/integration/bench/bench_test.go b/gopls/internal/test/integration/bench/bench_test.go index f42cf47..4286f48 100644 --- a/gopls/internal/test/integration/bench/bench_test.go +++ b/gopls/internal/test/integration/bench/bench_test.go
@@ -24,7 +24,6 @@ "golang.org/x/tools/gopls/internal/protocol/command" "golang.org/x/tools/gopls/internal/test/integration" "golang.org/x/tools/gopls/internal/test/integration/fake" - "golang.org/x/tools/gopls/internal/tool" "golang.org/x/tools/gopls/internal/util/bug" "golang.org/x/tools/gopls/internal/util/fakenet" "golang.org/x/tools/internal/event" @@ -57,7 +56,7 @@ func TestMain(m *testing.M) { bug.PanicOnBugs = true if os.Getenv(runAsGopls) == "true" { - tool.Main(context.Background(), cmd.New(), os.Args[1:]) + cmd.Main() os.Exit(0) } event.SetExporter(nil) // don't log to stderr
diff --git a/gopls/internal/test/integration/regtest.go b/gopls/internal/test/integration/regtest.go index 25ea415..baea9df 100644 --- a/gopls/internal/test/integration/regtest.go +++ b/gopls/internal/test/integration/regtest.go
@@ -5,7 +5,6 @@ package integration import ( - "context" "flag" "fmt" "os" @@ -18,7 +17,6 @@ "golang.org/x/tools/gopls/internal/cache" "golang.org/x/tools/gopls/internal/cmd" - "golang.org/x/tools/gopls/internal/tool" "golang.org/x/tools/gopls/internal/util/memoize" "golang.org/x/tools/internal/drivertest" "golang.org/x/tools/internal/gocommand" @@ -144,7 +142,7 @@ // If this magic environment variable is set, run gopls instead of the test // suite. See the documentation for runTestAsGoplsEnvvar for more details. if os.Getenv(runTestAsGoplsEnvvar) == "true" { - tool.Main(context.Background(), cmd.New(), os.Args[1:]) + cmd.Main() return 0 }
diff --git a/gopls/main.go b/gopls/main.go index 0a79d27..75e4ca2 100644 --- a/gopls/main.go +++ b/gopls/main.go
@@ -11,15 +11,12 @@ package main import ( - "context" "log" - "os" "golang.org/x/telemetry" "golang.org/x/telemetry/counter" "golang.org/x/tools/gopls/internal/cmd" "golang.org/x/tools/gopls/internal/filecache" - "golang.org/x/tools/gopls/internal/tool" versionpkg "golang.org/x/tools/gopls/internal/version" ) @@ -51,6 +48,5 @@ log.Fatalf("gopls cannot access its persistent index (disk full?): %v", err) } - ctx := context.Background() - tool.Main(ctx, cmd.New(), os.Args[1:]) + cmd.Main() }