golang.org/x/tools/gopls@v0.15.3/internal/settings/settings.go (about)

     1  // Copyright 2019 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package settings
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"path/filepath"
    11  	"regexp"
    12  	"runtime"
    13  	"strings"
    14  	"time"
    15  
    16  	"golang.org/x/tools/go/analysis"
    17  	"golang.org/x/tools/go/analysis/passes/appends"
    18  	"golang.org/x/tools/go/analysis/passes/asmdecl"
    19  	"golang.org/x/tools/go/analysis/passes/assign"
    20  	"golang.org/x/tools/go/analysis/passes/atomic"
    21  	"golang.org/x/tools/go/analysis/passes/atomicalign"
    22  	"golang.org/x/tools/go/analysis/passes/bools"
    23  	"golang.org/x/tools/go/analysis/passes/buildtag"
    24  	"golang.org/x/tools/go/analysis/passes/cgocall"
    25  	"golang.org/x/tools/go/analysis/passes/composite"
    26  	"golang.org/x/tools/go/analysis/passes/copylock"
    27  	"golang.org/x/tools/go/analysis/passes/deepequalerrors"
    28  	"golang.org/x/tools/go/analysis/passes/defers"
    29  	"golang.org/x/tools/go/analysis/passes/directive"
    30  	"golang.org/x/tools/go/analysis/passes/errorsas"
    31  	"golang.org/x/tools/go/analysis/passes/fieldalignment"
    32  	"golang.org/x/tools/go/analysis/passes/httpresponse"
    33  	"golang.org/x/tools/go/analysis/passes/ifaceassert"
    34  	"golang.org/x/tools/go/analysis/passes/loopclosure"
    35  	"golang.org/x/tools/go/analysis/passes/lostcancel"
    36  	"golang.org/x/tools/go/analysis/passes/nilfunc"
    37  	"golang.org/x/tools/go/analysis/passes/nilness"
    38  	"golang.org/x/tools/go/analysis/passes/printf"
    39  	"golang.org/x/tools/go/analysis/passes/shadow"
    40  	"golang.org/x/tools/go/analysis/passes/shift"
    41  	"golang.org/x/tools/go/analysis/passes/slog"
    42  	"golang.org/x/tools/go/analysis/passes/sortslice"
    43  	"golang.org/x/tools/go/analysis/passes/stdmethods"
    44  	"golang.org/x/tools/go/analysis/passes/stringintconv"
    45  	"golang.org/x/tools/go/analysis/passes/structtag"
    46  	"golang.org/x/tools/go/analysis/passes/testinggoroutine"
    47  	"golang.org/x/tools/go/analysis/passes/tests"
    48  	"golang.org/x/tools/go/analysis/passes/timeformat"
    49  	"golang.org/x/tools/go/analysis/passes/unmarshal"
    50  	"golang.org/x/tools/go/analysis/passes/unreachable"
    51  	"golang.org/x/tools/go/analysis/passes/unsafeptr"
    52  	"golang.org/x/tools/go/analysis/passes/unusedresult"
    53  	"golang.org/x/tools/go/analysis/passes/unusedwrite"
    54  	"golang.org/x/tools/gopls/internal/analysis/deprecated"
    55  	"golang.org/x/tools/gopls/internal/analysis/embeddirective"
    56  	"golang.org/x/tools/gopls/internal/analysis/fillreturns"
    57  	"golang.org/x/tools/gopls/internal/analysis/infertypeargs"
    58  	"golang.org/x/tools/gopls/internal/analysis/nonewvars"
    59  	"golang.org/x/tools/gopls/internal/analysis/noresultvalues"
    60  	"golang.org/x/tools/gopls/internal/analysis/simplifycompositelit"
    61  	"golang.org/x/tools/gopls/internal/analysis/simplifyrange"
    62  	"golang.org/x/tools/gopls/internal/analysis/simplifyslice"
    63  	"golang.org/x/tools/gopls/internal/analysis/stubmethods"
    64  	"golang.org/x/tools/gopls/internal/analysis/undeclaredname"
    65  	"golang.org/x/tools/gopls/internal/analysis/unusedparams"
    66  	"golang.org/x/tools/gopls/internal/analysis/unusedvariable"
    67  	"golang.org/x/tools/gopls/internal/analysis/useany"
    68  	"golang.org/x/tools/gopls/internal/file"
    69  	"golang.org/x/tools/gopls/internal/protocol"
    70  	"golang.org/x/tools/gopls/internal/protocol/command"
    71  )
    72  
    73  type Annotation string
    74  
    75  const (
    76  	// Nil controls nil checks.
    77  	Nil Annotation = "nil"
    78  
    79  	// Escape controls diagnostics about escape choices.
    80  	Escape Annotation = "escape"
    81  
    82  	// Inline controls diagnostics about inlining choices.
    83  	Inline Annotation = "inline"
    84  
    85  	// Bounds controls bounds checking diagnostics.
    86  	Bounds Annotation = "bounds"
    87  )
    88  
    89  // Options holds various configuration that affects Gopls execution, organized
    90  // by the nature or origin of the settings.
    91  type Options struct {
    92  	ClientOptions
    93  	ServerOptions
    94  	UserOptions
    95  	InternalOptions
    96  	Hooks
    97  }
    98  
    99  // IsAnalyzerEnabled reports whether an analyzer with the given name is
   100  // enabled.
   101  //
   102  // TODO(rfindley): refactor to simplify this function. We no longer need the
   103  // different categories of analyzer.
   104  func (opts *Options) IsAnalyzerEnabled(name string) bool {
   105  	for _, amap := range []map[string]*Analyzer{opts.DefaultAnalyzers, opts.StaticcheckAnalyzers} {
   106  		for _, analyzer := range amap {
   107  			if analyzer.Analyzer.Name == name && analyzer.IsEnabled(opts) {
   108  				return true
   109  			}
   110  		}
   111  	}
   112  	return false
   113  }
   114  
   115  // ClientOptions holds LSP-specific configuration that is provided by the
   116  // client.
   117  type ClientOptions struct {
   118  	ClientInfo                                 *protocol.ClientInfo
   119  	InsertTextFormat                           protocol.InsertTextFormat
   120  	ConfigurationSupported                     bool
   121  	DynamicConfigurationSupported              bool
   122  	DynamicRegistrationSemanticTokensSupported bool
   123  	DynamicWatchedFilesSupported               bool
   124  	RelativePatternsSupported                  bool
   125  	PreferredContentFormat                     protocol.MarkupKind
   126  	LineFoldingOnly                            bool
   127  	HierarchicalDocumentSymbolSupport          bool
   128  	SemanticTypes                              []string
   129  	SemanticMods                               []string
   130  	RelatedInformationSupported                bool
   131  	CompletionTags                             bool
   132  	CompletionDeprecated                       bool
   133  	SupportedResourceOperations                []protocol.ResourceOperationKind
   134  	CodeActionResolveOptions                   []string
   135  }
   136  
   137  // ServerOptions holds LSP-specific configuration that is provided by the
   138  // server.
   139  type ServerOptions struct {
   140  	SupportedCodeActions map[file.Kind]map[protocol.CodeActionKind]bool
   141  	SupportedCommands    []string
   142  }
   143  
   144  type BuildOptions struct {
   145  	// BuildFlags is the set of flags passed on to the build system when invoked.
   146  	// It is applied to queries like `go list`, which is used when discovering files.
   147  	// The most common use is to set `-tags`.
   148  	BuildFlags []string
   149  
   150  	// Env adds environment variables to external commands run by `gopls`, most notably `go list`.
   151  	Env map[string]string
   152  
   153  	// DirectoryFilters can be used to exclude unwanted directories from the
   154  	// workspace. By default, all directories are included. Filters are an
   155  	// operator, `+` to include and `-` to exclude, followed by a path prefix
   156  	// relative to the workspace folder. They are evaluated in order, and
   157  	// the last filter that applies to a path controls whether it is included.
   158  	// The path prefix can be empty, so an initial `-` excludes everything.
   159  	//
   160  	// DirectoryFilters also supports the `**` operator to match 0 or more directories.
   161  	//
   162  	// Examples:
   163  	//
   164  	// Exclude node_modules at current depth: `-node_modules`
   165  	//
   166  	// Exclude node_modules at any depth: `-**/node_modules`
   167  	//
   168  	// Include only project_a: `-` (exclude everything), `+project_a`
   169  	//
   170  	// Include only project_a, but not node_modules inside it: `-`, `+project_a`, `-project_a/node_modules`
   171  	DirectoryFilters []string
   172  
   173  	// TemplateExtensions gives the extensions of file names that are treateed
   174  	// as template files. (The extension
   175  	// is the part of the file name after the final dot.)
   176  	TemplateExtensions []string
   177  
   178  	// obsolete, no effect
   179  	MemoryMode string `status:"experimental"`
   180  
   181  	// ExpandWorkspaceToModule determines which packages are considered
   182  	// "workspace packages" when the workspace is using modules.
   183  	//
   184  	// Workspace packages affect the scope of workspace-wide operations. Notably,
   185  	// gopls diagnoses all packages considered to be part of the workspace after
   186  	// every keystroke, so by setting "ExpandWorkspaceToModule" to false, and
   187  	// opening a nested workspace directory, you can reduce the amount of work
   188  	// gopls has to do to keep your workspace up to date.
   189  	ExpandWorkspaceToModule bool `status:"experimental"`
   190  
   191  	// AllowModfileModifications disables -mod=readonly, allowing imports from
   192  	// out-of-scope modules. This option will eventually be removed.
   193  	AllowModfileModifications bool `status:"experimental"`
   194  
   195  	// AllowImplicitNetworkAccess disables GOPROXY=off, allowing implicit module
   196  	// downloads rather than requiring user action. This option will eventually
   197  	// be removed.
   198  	AllowImplicitNetworkAccess bool `status:"experimental"`
   199  
   200  	// StandaloneTags specifies a set of build constraints that identify
   201  	// individual Go source files that make up the entire main package of an
   202  	// executable.
   203  	//
   204  	// A common example of standalone main files is the convention of using the
   205  	// directive `//go:build ignore` to denote files that are not intended to be
   206  	// included in any package, for example because they are invoked directly by
   207  	// the developer using `go run`.
   208  	//
   209  	// Gopls considers a file to be a standalone main file if and only if it has
   210  	// package name "main" and has a build directive of the exact form
   211  	// "//go:build tag" or "// +build tag", where tag is among the list of tags
   212  	// configured by this setting. Notably, if the build constraint is more
   213  	// complicated than a simple tag (such as the composite constraint
   214  	// `//go:build tag && go1.18`), the file is not considered to be a standalone
   215  	// main file.
   216  	//
   217  	// This setting is only supported when gopls is built with Go 1.16 or later.
   218  	StandaloneTags []string
   219  }
   220  
   221  type UIOptions struct {
   222  	DocumentationOptions
   223  	CompletionOptions
   224  	NavigationOptions
   225  	DiagnosticOptions
   226  	InlayHintOptions
   227  
   228  	// Codelenses overrides the enabled/disabled state of code lenses. See the
   229  	// "Code Lenses" section of the
   230  	// [Settings page](https://github.com/golang/tools/blob/master/gopls/doc/settings.md#code-lenses)
   231  	// for the list of supported lenses.
   232  	//
   233  	// Example Usage:
   234  	//
   235  	// ```json5
   236  	// "gopls": {
   237  	// ...
   238  	//   "codelenses": {
   239  	//     "generate": false,  // Don't show the `go generate` lens.
   240  	//     "gc_details": true  // Show a code lens toggling the display of gc's choices.
   241  	//   }
   242  	// ...
   243  	// }
   244  	// ```
   245  	Codelenses map[string]bool
   246  
   247  	// SemanticTokens controls whether the LSP server will send
   248  	// semantic tokens to the client.
   249  	SemanticTokens bool `status:"experimental"`
   250  
   251  	// NoSemanticString turns off the sending of the semantic token 'string'
   252  	NoSemanticString bool `status:"experimental"`
   253  
   254  	// NoSemanticNumber  turns off the sending of the semantic token 'number'
   255  	NoSemanticNumber bool `status:"experimental"`
   256  }
   257  
   258  type CompletionOptions struct {
   259  	// Placeholders enables placeholders for function parameters or struct
   260  	// fields in completion responses.
   261  	UsePlaceholders bool
   262  
   263  	// CompletionBudget is the soft latency goal for completion requests. Most
   264  	// requests finish in a couple milliseconds, but in some cases deep
   265  	// completions can take much longer. As we use up our budget we
   266  	// dynamically reduce the search scope to ensure we return timely
   267  	// results. Zero means unlimited.
   268  	CompletionBudget time.Duration `status:"debug"`
   269  
   270  	// Matcher sets the algorithm that is used when calculating completion
   271  	// candidates.
   272  	Matcher Matcher `status:"advanced"`
   273  
   274  	// ExperimentalPostfixCompletions enables artificial method snippets
   275  	// such as "someSlice.sort!".
   276  	ExperimentalPostfixCompletions bool `status:"experimental"`
   277  
   278  	// CompleteFunctionCalls enables function call completion.
   279  	//
   280  	// When completing a statement, or when a function return type matches the
   281  	// expected of the expression being completed, completion may suggest call
   282  	// expressions (i.e. may include parentheses).
   283  	CompleteFunctionCalls bool
   284  }
   285  
   286  type DocumentationOptions struct {
   287  	// HoverKind controls the information that appears in the hover text.
   288  	// SingleLine and Structured are intended for use only by authors of editor plugins.
   289  	HoverKind HoverKind
   290  
   291  	// LinkTarget controls where documentation links go.
   292  	// It might be one of:
   293  	//
   294  	// * `"godoc.org"`
   295  	// * `"pkg.go.dev"`
   296  	//
   297  	// If company chooses to use its own `godoc.org`, its address can be used as well.
   298  	//
   299  	// Modules matching the GOPRIVATE environment variable will not have
   300  	// documentation links in hover.
   301  	LinkTarget string
   302  
   303  	// LinksInHover toggles the presence of links to documentation in hover.
   304  	LinksInHover bool
   305  }
   306  
   307  type FormattingOptions struct {
   308  	// Local is the equivalent of the `goimports -local` flag, which puts
   309  	// imports beginning with this string after third-party packages. It should
   310  	// be the prefix of the import path whose imports should be grouped
   311  	// separately.
   312  	Local string
   313  
   314  	// Gofumpt indicates if we should run gofumpt formatting.
   315  	Gofumpt bool
   316  }
   317  
   318  type DiagnosticOptions struct {
   319  	// Analyses specify analyses that the user would like to enable or disable.
   320  	// A map of the names of analysis passes that should be enabled/disabled.
   321  	// A full list of analyzers that gopls uses can be found in
   322  	// [analyzers.md](https://github.com/golang/tools/blob/master/gopls/doc/analyzers.md).
   323  	//
   324  	// Example Usage:
   325  	//
   326  	// ```json5
   327  	// ...
   328  	// "analyses": {
   329  	//   "unreachable": false, // Disable the unreachable analyzer.
   330  	//   "unusedvariable": true  // Enable the unusedvariable analyzer.
   331  	// }
   332  	// ...
   333  	// ```
   334  	Analyses map[string]bool
   335  
   336  	// Staticcheck enables additional analyses from staticcheck.io.
   337  	// These analyses are documented on
   338  	// [Staticcheck's website](https://staticcheck.io/docs/checks/).
   339  	Staticcheck bool `status:"experimental"`
   340  
   341  	// Annotations specifies the various kinds of optimization diagnostics
   342  	// that should be reported by the gc_details command.
   343  	Annotations map[Annotation]bool `status:"experimental"`
   344  
   345  	// Vulncheck enables vulnerability scanning.
   346  	Vulncheck VulncheckMode `status:"experimental"`
   347  
   348  	// DiagnosticsDelay controls the amount of time that gopls waits
   349  	// after the most recent file modification before computing deep diagnostics.
   350  	// Simple diagnostics (parsing and type-checking) are always run immediately
   351  	// on recently modified packages.
   352  	//
   353  	// This option must be set to a valid duration string, for example `"250ms"`.
   354  	DiagnosticsDelay time.Duration `status:"advanced"`
   355  
   356  	// DiagnosticsTrigger controls when to run diagnostics.
   357  	DiagnosticsTrigger DiagnosticsTrigger `status:"experimental"`
   358  
   359  	// AnalysisProgressReporting controls whether gopls sends progress
   360  	// notifications when construction of its index of analysis facts is taking a
   361  	// long time. Cancelling these notifications will cancel the indexing task,
   362  	// though it will restart after the next change in the workspace.
   363  	//
   364  	// When a package is opened for the first time and heavyweight analyses such as
   365  	// staticcheck are enabled, it can take a while to construct the index of
   366  	// analysis facts for all its dependencies. The index is cached in the
   367  	// filesystem, so subsequent analysis should be faster.
   368  	AnalysisProgressReporting bool
   369  }
   370  
   371  type InlayHintOptions struct {
   372  	// Hints specify inlay hints that users want to see. A full list of hints
   373  	// that gopls uses can be found in
   374  	// [inlayHints.md](https://github.com/golang/tools/blob/master/gopls/doc/inlayHints.md).
   375  	Hints map[string]bool `status:"experimental"`
   376  }
   377  
   378  type NavigationOptions struct {
   379  	// ImportShortcut specifies whether import statements should link to
   380  	// documentation or go to definitions.
   381  	ImportShortcut ImportShortcut
   382  
   383  	// SymbolMatcher sets the algorithm that is used when finding workspace symbols.
   384  	SymbolMatcher SymbolMatcher `status:"advanced"`
   385  
   386  	// SymbolStyle controls how symbols are qualified in symbol responses.
   387  	//
   388  	// Example Usage:
   389  	//
   390  	// ```json5
   391  	// "gopls": {
   392  	// ...
   393  	//   "symbolStyle": "Dynamic",
   394  	// ...
   395  	// }
   396  	// ```
   397  	SymbolStyle SymbolStyle `status:"advanced"`
   398  
   399  	// SymbolScope controls which packages are searched for workspace/symbol
   400  	// requests. The default value, "workspace", searches only workspace
   401  	// packages. The legacy behavior, "all", causes all loaded packages to be
   402  	// searched, including dependencies; this is more expensive and may return
   403  	// unwanted results.
   404  	SymbolScope SymbolScope
   405  }
   406  
   407  // UserOptions holds custom Gopls configuration (not part of the LSP) that is
   408  // modified by the client.
   409  type UserOptions struct {
   410  	BuildOptions
   411  	UIOptions
   412  	FormattingOptions
   413  
   414  	// VerboseOutput enables additional debug logging.
   415  	VerboseOutput bool `status:"debug"`
   416  }
   417  
   418  // EnvSlice returns Env as a slice of k=v strings.
   419  func (u *UserOptions) EnvSlice() []string {
   420  	var result []string
   421  	for k, v := range u.Env {
   422  		result = append(result, fmt.Sprintf("%v=%v", k, v))
   423  	}
   424  	return result
   425  }
   426  
   427  // SetEnvSlice sets Env from a slice of k=v strings.
   428  func (u *UserOptions) SetEnvSlice(env []string) {
   429  	u.Env = map[string]string{}
   430  	for _, kv := range env {
   431  		split := strings.SplitN(kv, "=", 2)
   432  		if len(split) != 2 {
   433  			continue
   434  		}
   435  		u.Env[split[0]] = split[1]
   436  	}
   437  }
   438  
   439  // Hooks contains configuration that is provided to the Gopls command by the
   440  // main package.
   441  type Hooks struct {
   442  	// LicensesText holds third party licenses for software used by gopls.
   443  	LicensesText string
   444  
   445  	// Whether staticcheck is supported.
   446  	StaticcheckSupported bool
   447  
   448  	// URLRegexp is used to find potential URLs in comments/strings.
   449  	//
   450  	// Not all matches are shown to the user: if the matched URL is not detected
   451  	// as valid, it will be skipped.
   452  	URLRegexp *regexp.Regexp
   453  
   454  	// GofumptFormat allows the gopls module to wire-in a call to
   455  	// gofumpt/format.Source. langVersion and modulePath are used for some
   456  	// Gofumpt formatting rules -- see the Gofumpt documentation for details.
   457  	GofumptFormat func(ctx context.Context, langVersion, modulePath string, src []byte) ([]byte, error)
   458  
   459  	DefaultAnalyzers     map[string]*Analyzer
   460  	StaticcheckAnalyzers map[string]*Analyzer
   461  }
   462  
   463  // InternalOptions contains settings that are not intended for use by the
   464  // average user. These may be settings used by tests or outdated settings that
   465  // will soon be deprecated. Some of these settings may not even be configurable
   466  // by the user.
   467  //
   468  // TODO(rfindley): even though these settings are not intended for
   469  // modification, some of them should be surfaced in our documentation.
   470  type InternalOptions struct {
   471  	// VerboseWorkDoneProgress controls whether the LSP server should send
   472  	// progress reports for all work done outside the scope of an RPC.
   473  	// Used by the regression tests.
   474  	VerboseWorkDoneProgress bool
   475  
   476  	// The following options were previously available to users, but they
   477  	// really shouldn't be configured by anyone other than "power users".
   478  
   479  	// CompletionDocumentation enables documentation with completion results.
   480  	CompletionDocumentation bool
   481  
   482  	// CompleteUnimported enables completion for packages that you do not
   483  	// currently import.
   484  	CompleteUnimported bool
   485  
   486  	// DeepCompletion enables the ability to return completions from deep
   487  	// inside relevant entities, rather than just the locally accessible ones.
   488  	//
   489  	// Consider this example:
   490  	//
   491  	// ```go
   492  	// package main
   493  	//
   494  	// import "fmt"
   495  	//
   496  	// type wrapString struct {
   497  	//     str string
   498  	// }
   499  	//
   500  	// func main() {
   501  	//     x := wrapString{"hello world"}
   502  	//     fmt.Printf(<>)
   503  	// }
   504  	// ```
   505  	//
   506  	// At the location of the `<>` in this program, deep completion would suggest
   507  	// the result `x.str`.
   508  	DeepCompletion bool
   509  
   510  	// ShowBugReports causes a message to be shown when the first bug is reported
   511  	// on the server.
   512  	// This option applies only during initialization.
   513  	ShowBugReports bool
   514  
   515  	// SubdirWatchPatterns configures the file watching glob patterns registered
   516  	// by gopls.
   517  	//
   518  	// Some clients (namely VS Code) do not send workspace/didChangeWatchedFile
   519  	// notifications for files contained in a directory when that directory is
   520  	// deleted:
   521  	// https://github.com/microsoft/vscode/issues/109754
   522  	//
   523  	// In this case, gopls would miss important notifications about deleted
   524  	// packages. To work around this, gopls registers a watch pattern for each
   525  	// directory containing Go files.
   526  	//
   527  	// Unfortunately, other clients experience performance problems with this
   528  	// many watch patterns, so there is no single behavior that works well for
   529  	// all clients.
   530  	//
   531  	// The "subdirWatchPatterns" setting allows configuring this behavior. Its
   532  	// default value of "auto" attempts to guess the correct behavior based on
   533  	// the client name. We'd love to avoid this specialization, but as described
   534  	// above there is no single value that works for all clients.
   535  	//
   536  	// If any LSP client does not behave well with the default value (for
   537  	// example, if like VS Code it drops file notifications), please file an
   538  	// issue.
   539  	SubdirWatchPatterns SubdirWatchPatterns
   540  
   541  	// ReportAnalysisProgressAfter sets the duration for gopls to wait before starting
   542  	// progress reporting for ongoing go/analysis passes.
   543  	//
   544  	// It is intended to be used for testing only.
   545  	ReportAnalysisProgressAfter time.Duration
   546  
   547  	// TelemetryPrompt controls whether gopls prompts about enabling Go telemetry.
   548  	//
   549  	// Once the prompt is answered, gopls doesn't ask again, but TelemetryPrompt
   550  	// can prevent the question from ever being asked in the first place.
   551  	TelemetryPrompt bool
   552  
   553  	// LinkifyShowMessage controls whether the client wants gopls
   554  	// to linkify links in showMessage. e.g. [go.dev](https://go.dev).
   555  	LinkifyShowMessage bool
   556  
   557  	// IncludeReplaceInWorkspace controls whether locally replaced modules in a
   558  	// go.mod file are treated like workspace modules.
   559  	// Or in other words, if a go.mod file with local replaces behaves like a
   560  	// go.work file.
   561  	IncludeReplaceInWorkspace bool
   562  
   563  	// ZeroConfig enables the zero-config algorithm for workspace layout,
   564  	// dynamically creating build configurations for different modules,
   565  	// directories, and GOOS/GOARCH combinations to cover open files.
   566  	ZeroConfig bool
   567  }
   568  
   569  type SubdirWatchPatterns string
   570  
   571  const (
   572  	SubdirWatchPatternsOn   SubdirWatchPatterns = "on"
   573  	SubdirWatchPatternsOff  SubdirWatchPatterns = "off"
   574  	SubdirWatchPatternsAuto SubdirWatchPatterns = "auto"
   575  )
   576  
   577  type ImportShortcut string
   578  
   579  const (
   580  	BothShortcuts      ImportShortcut = "Both"
   581  	LinkShortcut       ImportShortcut = "Link"
   582  	DefinitionShortcut ImportShortcut = "Definition"
   583  )
   584  
   585  func (s ImportShortcut) ShowLinks() bool {
   586  	return s == BothShortcuts || s == LinkShortcut
   587  }
   588  
   589  func (s ImportShortcut) ShowDefinition() bool {
   590  	return s == BothShortcuts || s == DefinitionShortcut
   591  }
   592  
   593  type Matcher string
   594  
   595  const (
   596  	Fuzzy           Matcher = "Fuzzy"
   597  	CaseInsensitive Matcher = "CaseInsensitive"
   598  	CaseSensitive   Matcher = "CaseSensitive"
   599  )
   600  
   601  // A SymbolMatcher controls the matching of symbols for workspace/symbol
   602  // requests.
   603  type SymbolMatcher string
   604  
   605  const (
   606  	SymbolFuzzy           SymbolMatcher = "Fuzzy"
   607  	SymbolFastFuzzy       SymbolMatcher = "FastFuzzy"
   608  	SymbolCaseInsensitive SymbolMatcher = "CaseInsensitive"
   609  	SymbolCaseSensitive   SymbolMatcher = "CaseSensitive"
   610  )
   611  
   612  // A SymbolStyle controls the formatting of symbols in workspace/symbol results.
   613  type SymbolStyle string
   614  
   615  const (
   616  	// PackageQualifiedSymbols is package qualified symbols i.e.
   617  	// "pkg.Foo.Field".
   618  	PackageQualifiedSymbols SymbolStyle = "Package"
   619  	// FullyQualifiedSymbols is fully qualified symbols, i.e.
   620  	// "path/to/pkg.Foo.Field".
   621  	FullyQualifiedSymbols SymbolStyle = "Full"
   622  	// DynamicSymbols uses whichever qualifier results in the highest scoring
   623  	// match for the given symbol query. Here a "qualifier" is any "/" or "."
   624  	// delimited suffix of the fully qualified symbol. i.e. "to/pkg.Foo.Field" or
   625  	// just "Foo.Field".
   626  	DynamicSymbols SymbolStyle = "Dynamic"
   627  )
   628  
   629  // A SymbolScope controls the search scope for workspace/symbol requests.
   630  type SymbolScope string
   631  
   632  const (
   633  	// WorkspaceSymbolScope matches symbols in workspace packages only.
   634  	WorkspaceSymbolScope SymbolScope = "workspace"
   635  	// AllSymbolScope matches symbols in any loaded package, including
   636  	// dependencies.
   637  	AllSymbolScope SymbolScope = "all"
   638  )
   639  
   640  type HoverKind string
   641  
   642  const (
   643  	SingleLine            HoverKind = "SingleLine"
   644  	NoDocumentation       HoverKind = "NoDocumentation"
   645  	SynopsisDocumentation HoverKind = "SynopsisDocumentation"
   646  	FullDocumentation     HoverKind = "FullDocumentation"
   647  
   648  	// Structured is an experimental setting that returns a structured hover format.
   649  	// This format separates the signature from the documentation, so that the client
   650  	// can do more manipulation of these fields.
   651  	//
   652  	// This should only be used by clients that support this behavior.
   653  	Structured HoverKind = "Structured"
   654  )
   655  
   656  type VulncheckMode string
   657  
   658  const (
   659  	// Disable vulnerability analysis.
   660  	ModeVulncheckOff VulncheckMode = "Off"
   661  	// In Imports mode, `gopls` will report vulnerabilities that affect packages
   662  	// directly and indirectly used by the analyzed main module.
   663  	ModeVulncheckImports VulncheckMode = "Imports"
   664  
   665  	// TODO: VulncheckRequire, VulncheckCallgraph
   666  )
   667  
   668  type DiagnosticsTrigger string
   669  
   670  const (
   671  	// Trigger diagnostics on file edit and save. (default)
   672  	DiagnosticsOnEdit DiagnosticsTrigger = "Edit"
   673  	// Trigger diagnostics only on file save. Events like initial workspace load
   674  	// or configuration change will still trigger diagnostics.
   675  	DiagnosticsOnSave DiagnosticsTrigger = "Save"
   676  	// TODO: support "Manual"?
   677  )
   678  
   679  type OptionResults []OptionResult
   680  
   681  type OptionResult struct {
   682  	Name  string
   683  	Value any
   684  	Error error
   685  }
   686  
   687  func SetOptions(options *Options, opts any) OptionResults {
   688  	var results OptionResults
   689  	switch opts := opts.(type) {
   690  	case nil:
   691  	case map[string]any:
   692  		// If the user's settings contains "allExperiments", set that first,
   693  		// and then let them override individual settings independently.
   694  		var enableExperiments bool
   695  		for name, value := range opts {
   696  			if b, ok := value.(bool); name == "allExperiments" && ok && b {
   697  				enableExperiments = true
   698  				options.EnableAllExperiments()
   699  			}
   700  		}
   701  		seen := map[string]struct{}{}
   702  		for name, value := range opts {
   703  			results = append(results, options.set(name, value, seen))
   704  		}
   705  		// Finally, enable any experimental features that are specified in
   706  		// maps, which allows users to individually toggle them on or off.
   707  		if enableExperiments {
   708  			options.enableAllExperimentMaps()
   709  		}
   710  	default:
   711  		results = append(results, OptionResult{
   712  			Value: opts,
   713  			Error: fmt.Errorf("Invalid options type %T", opts),
   714  		})
   715  	}
   716  	return results
   717  }
   718  
   719  func (o *Options) ForClientCapabilities(clientName *protocol.ClientInfo, caps protocol.ClientCapabilities) {
   720  	o.ClientInfo = clientName
   721  	// Check if the client supports snippets in completion items.
   722  	if caps.Workspace.WorkspaceEdit != nil {
   723  		o.SupportedResourceOperations = caps.Workspace.WorkspaceEdit.ResourceOperations
   724  	}
   725  	if c := caps.TextDocument.Completion; c.CompletionItem.SnippetSupport {
   726  		o.InsertTextFormat = protocol.SnippetTextFormat
   727  	}
   728  	// Check if the client supports configuration messages.
   729  	o.ConfigurationSupported = caps.Workspace.Configuration
   730  	o.DynamicConfigurationSupported = caps.Workspace.DidChangeConfiguration.DynamicRegistration
   731  	o.DynamicRegistrationSemanticTokensSupported = caps.TextDocument.SemanticTokens.DynamicRegistration
   732  	o.DynamicWatchedFilesSupported = caps.Workspace.DidChangeWatchedFiles.DynamicRegistration
   733  	o.RelativePatternsSupported = caps.Workspace.DidChangeWatchedFiles.RelativePatternSupport
   734  
   735  	// Check which types of content format are supported by this client.
   736  	if hover := caps.TextDocument.Hover; hover != nil && len(hover.ContentFormat) > 0 {
   737  		o.PreferredContentFormat = hover.ContentFormat[0]
   738  	}
   739  	// Check if the client supports only line folding.
   740  
   741  	if fr := caps.TextDocument.FoldingRange; fr != nil {
   742  		o.LineFoldingOnly = fr.LineFoldingOnly
   743  	}
   744  	// Check if the client supports hierarchical document symbols.
   745  	o.HierarchicalDocumentSymbolSupport = caps.TextDocument.DocumentSymbol.HierarchicalDocumentSymbolSupport
   746  
   747  	// Client's semantic tokens
   748  	o.SemanticTypes = caps.TextDocument.SemanticTokens.TokenTypes
   749  	o.SemanticMods = caps.TextDocument.SemanticTokens.TokenModifiers
   750  	// we don't need Requests, as we support full functionality
   751  	// we don't need Formats, as there is only one, for now
   752  
   753  	// Check if the client supports diagnostic related information.
   754  	o.RelatedInformationSupported = caps.TextDocument.PublishDiagnostics.RelatedInformation
   755  	// Check if the client completion support includes tags (preferred) or deprecation
   756  	if caps.TextDocument.Completion.CompletionItem.TagSupport != nil &&
   757  		caps.TextDocument.Completion.CompletionItem.TagSupport.ValueSet != nil {
   758  		o.CompletionTags = true
   759  	} else if caps.TextDocument.Completion.CompletionItem.DeprecatedSupport {
   760  		o.CompletionDeprecated = true
   761  	}
   762  
   763  	// Check if the client supports code actions resolving.
   764  	if caps.TextDocument.CodeAction.DataSupport && caps.TextDocument.CodeAction.ResolveSupport != nil {
   765  		o.CodeActionResolveOptions = caps.TextDocument.CodeAction.ResolveSupport.Properties
   766  	}
   767  }
   768  
   769  func (o *Options) Clone() *Options {
   770  	// TODO(rfindley): has this function gone stale? It appears that there are
   771  	// settings that are incorrectly cloned here (such as TemplateExtensions).
   772  	result := &Options{
   773  		ClientOptions:   o.ClientOptions,
   774  		InternalOptions: o.InternalOptions,
   775  		Hooks: Hooks{
   776  			StaticcheckSupported: o.StaticcheckSupported,
   777  			GofumptFormat:        o.GofumptFormat,
   778  			URLRegexp:            o.URLRegexp,
   779  		},
   780  		ServerOptions: o.ServerOptions,
   781  		UserOptions:   o.UserOptions,
   782  	}
   783  	// Fully clone any slice or map fields. Only Hooks, ExperimentalOptions,
   784  	// and UserOptions can be modified.
   785  	copyStringMap := func(src map[string]bool) map[string]bool {
   786  		dst := make(map[string]bool)
   787  		for k, v := range src {
   788  			dst[k] = v
   789  		}
   790  		return dst
   791  	}
   792  	result.Analyses = copyStringMap(o.Analyses)
   793  	result.Codelenses = copyStringMap(o.Codelenses)
   794  
   795  	copySlice := func(src []string) []string {
   796  		dst := make([]string, len(src))
   797  		copy(dst, src)
   798  		return dst
   799  	}
   800  	result.SetEnvSlice(o.EnvSlice())
   801  	result.BuildFlags = copySlice(o.BuildFlags)
   802  	result.DirectoryFilters = copySlice(o.DirectoryFilters)
   803  	result.StandaloneTags = copySlice(o.StandaloneTags)
   804  
   805  	copyAnalyzerMap := func(src map[string]*Analyzer) map[string]*Analyzer {
   806  		dst := make(map[string]*Analyzer)
   807  		for k, v := range src {
   808  			dst[k] = v
   809  		}
   810  		return dst
   811  	}
   812  	result.DefaultAnalyzers = copyAnalyzerMap(o.DefaultAnalyzers)
   813  	result.StaticcheckAnalyzers = copyAnalyzerMap(o.StaticcheckAnalyzers)
   814  	return result
   815  }
   816  
   817  func (o *Options) AddStaticcheckAnalyzer(a *analysis.Analyzer, enabled bool, severity protocol.DiagnosticSeverity) {
   818  	o.StaticcheckAnalyzers[a.Name] = &Analyzer{
   819  		Analyzer: a,
   820  		Enabled:  enabled,
   821  		Severity: severity,
   822  	}
   823  }
   824  
   825  // EnableAllExperiments turns on all of the experimental "off-by-default"
   826  // features offered by gopls. Any experimental features specified in maps
   827  // should be enabled in enableAllExperimentMaps.
   828  func (o *Options) EnableAllExperiments() {
   829  	o.SemanticTokens = true
   830  }
   831  
   832  func (o *Options) enableAllExperimentMaps() {
   833  	if _, ok := o.Codelenses[string(command.GCDetails)]; !ok {
   834  		o.Codelenses[string(command.GCDetails)] = true
   835  	}
   836  	if _, ok := o.Codelenses[string(command.RunGovulncheck)]; !ok {
   837  		o.Codelenses[string(command.RunGovulncheck)] = true
   838  	}
   839  	if _, ok := o.Analyses[unusedvariable.Analyzer.Name]; !ok {
   840  		o.Analyses[unusedvariable.Analyzer.Name] = true
   841  	}
   842  }
   843  
   844  // validateDirectoryFilter validates if the filter string
   845  // - is not empty
   846  // - start with either + or -
   847  // - doesn't contain currently unsupported glob operators: *, ?
   848  func validateDirectoryFilter(ifilter string) (string, error) {
   849  	filter := fmt.Sprint(ifilter)
   850  	if filter == "" || (filter[0] != '+' && filter[0] != '-') {
   851  		return "", fmt.Errorf("invalid filter %v, must start with + or -", filter)
   852  	}
   853  	segs := strings.Split(filter[1:], "/")
   854  	unsupportedOps := [...]string{"?", "*"}
   855  	for _, seg := range segs {
   856  		if seg != "**" {
   857  			for _, op := range unsupportedOps {
   858  				if strings.Contains(seg, op) {
   859  					return "", fmt.Errorf("invalid filter %v, operator %v not supported. If you want to have this operator supported, consider filing an issue.", filter, op)
   860  				}
   861  			}
   862  		}
   863  	}
   864  
   865  	return strings.TrimRight(filepath.FromSlash(filter), "/"), nil
   866  }
   867  
   868  func (o *Options) set(name string, value interface{}, seen map[string]struct{}) OptionResult {
   869  	// Flatten the name in case we get options with a hierarchy.
   870  	split := strings.Split(name, ".")
   871  	name = split[len(split)-1]
   872  
   873  	result := OptionResult{Name: name, Value: value}
   874  	if _, ok := seen[name]; ok {
   875  		result.parseErrorf("duplicate configuration for %s", name)
   876  	}
   877  	seen[name] = struct{}{}
   878  
   879  	switch name {
   880  	case "env":
   881  		menv, ok := value.(map[string]interface{})
   882  		if !ok {
   883  			result.parseErrorf("invalid type %T, expect map", value)
   884  			break
   885  		}
   886  		if o.Env == nil {
   887  			o.Env = make(map[string]string)
   888  		}
   889  		for k, v := range menv {
   890  			o.Env[k] = fmt.Sprint(v)
   891  		}
   892  
   893  	case "buildFlags":
   894  		// TODO(rfindley): use asStringSlice.
   895  		iflags, ok := value.([]interface{})
   896  		if !ok {
   897  			result.parseErrorf("invalid type %T, expect list", value)
   898  			break
   899  		}
   900  		flags := make([]string, 0, len(iflags))
   901  		for _, flag := range iflags {
   902  			flags = append(flags, fmt.Sprintf("%s", flag))
   903  		}
   904  		o.BuildFlags = flags
   905  
   906  	case "directoryFilters":
   907  		// TODO(rfindley): use asStringSlice.
   908  		ifilters, ok := value.([]interface{})
   909  		if !ok {
   910  			result.parseErrorf("invalid type %T, expect list", value)
   911  			break
   912  		}
   913  		var filters []string
   914  		for _, ifilter := range ifilters {
   915  			filter, err := validateDirectoryFilter(fmt.Sprintf("%v", ifilter))
   916  			if err != nil {
   917  				result.parseErrorf("%v", err)
   918  				return result
   919  			}
   920  			filters = append(filters, strings.TrimRight(filepath.FromSlash(filter), "/"))
   921  		}
   922  		o.DirectoryFilters = filters
   923  
   924  	case "memoryMode":
   925  		result.deprecated("")
   926  	case "completionDocumentation":
   927  		result.setBool(&o.CompletionDocumentation)
   928  	case "usePlaceholders":
   929  		result.setBool(&o.UsePlaceholders)
   930  	case "deepCompletion":
   931  		result.setBool(&o.DeepCompletion)
   932  	case "completeUnimported":
   933  		result.setBool(&o.CompleteUnimported)
   934  	case "completionBudget":
   935  		result.setDuration(&o.CompletionBudget)
   936  	case "matcher":
   937  		if s, ok := result.asOneOf(
   938  			string(Fuzzy),
   939  			string(CaseSensitive),
   940  			string(CaseInsensitive),
   941  		); ok {
   942  			o.Matcher = Matcher(s)
   943  		}
   944  
   945  	case "symbolMatcher":
   946  		if s, ok := result.asOneOf(
   947  			string(SymbolFuzzy),
   948  			string(SymbolFastFuzzy),
   949  			string(SymbolCaseInsensitive),
   950  			string(SymbolCaseSensitive),
   951  		); ok {
   952  			o.SymbolMatcher = SymbolMatcher(s)
   953  		}
   954  
   955  	case "symbolStyle":
   956  		if s, ok := result.asOneOf(
   957  			string(FullyQualifiedSymbols),
   958  			string(PackageQualifiedSymbols),
   959  			string(DynamicSymbols),
   960  		); ok {
   961  			o.SymbolStyle = SymbolStyle(s)
   962  		}
   963  
   964  	case "symbolScope":
   965  		if s, ok := result.asOneOf(
   966  			string(WorkspaceSymbolScope),
   967  			string(AllSymbolScope),
   968  		); ok {
   969  			o.SymbolScope = SymbolScope(s)
   970  		}
   971  
   972  	case "hoverKind":
   973  		if s, ok := result.asOneOf(
   974  			string(NoDocumentation),
   975  			string(SingleLine),
   976  			string(SynopsisDocumentation),
   977  			string(FullDocumentation),
   978  			string(Structured),
   979  		); ok {
   980  			o.HoverKind = HoverKind(s)
   981  		}
   982  
   983  	case "linkTarget":
   984  		result.setString(&o.LinkTarget)
   985  
   986  	case "linksInHover":
   987  		result.setBool(&o.LinksInHover)
   988  
   989  	case "importShortcut":
   990  		if s, ok := result.asOneOf(string(BothShortcuts), string(LinkShortcut), string(DefinitionShortcut)); ok {
   991  			o.ImportShortcut = ImportShortcut(s)
   992  		}
   993  
   994  	case "analyses":
   995  		result.setBoolMap(&o.Analyses)
   996  
   997  	case "hints":
   998  		result.setBoolMap(&o.Hints)
   999  
  1000  	case "annotations":
  1001  		result.setAnnotationMap(&o.Annotations)
  1002  
  1003  	case "vulncheck":
  1004  		if s, ok := result.asOneOf(
  1005  			string(ModeVulncheckOff),
  1006  			string(ModeVulncheckImports),
  1007  		); ok {
  1008  			o.Vulncheck = VulncheckMode(s)
  1009  		}
  1010  
  1011  	case "codelenses", "codelens":
  1012  		var lensOverrides map[string]bool
  1013  		result.setBoolMap(&lensOverrides)
  1014  		if result.Error == nil {
  1015  			if o.Codelenses == nil {
  1016  				o.Codelenses = make(map[string]bool)
  1017  			}
  1018  			for lens, enabled := range lensOverrides {
  1019  				o.Codelenses[lens] = enabled
  1020  			}
  1021  		}
  1022  
  1023  		// codelens is deprecated, but still works for now.
  1024  		// TODO(rstambler): Remove this for the gopls/v0.7.0 release.
  1025  		if name == "codelens" {
  1026  			result.deprecated("codelenses")
  1027  		}
  1028  
  1029  	case "staticcheck":
  1030  		if v, ok := result.asBool(); ok {
  1031  			o.Staticcheck = v
  1032  			if v && !o.StaticcheckSupported {
  1033  				result.Error = fmt.Errorf("applying setting %q: staticcheck is not supported at %s;"+
  1034  					" rebuild gopls with a more recent version of Go", result.Name, runtime.Version())
  1035  			}
  1036  		}
  1037  
  1038  	case "local":
  1039  		result.setString(&o.Local)
  1040  
  1041  	case "verboseOutput":
  1042  		result.setBool(&o.VerboseOutput)
  1043  
  1044  	case "verboseWorkDoneProgress":
  1045  		result.setBool(&o.VerboseWorkDoneProgress)
  1046  
  1047  	case "tempModFile":
  1048  		result.deprecated("")
  1049  
  1050  	case "showBugReports":
  1051  		result.setBool(&o.ShowBugReports)
  1052  
  1053  	case "gofumpt":
  1054  		if v, ok := result.asBool(); ok {
  1055  			o.Gofumpt = v
  1056  			if v && o.GofumptFormat == nil {
  1057  				result.Error = fmt.Errorf("applying setting %q: gofumpt is not supported at %s;"+
  1058  					" rebuild gopls with a more recent version of Go", result.Name, runtime.Version())
  1059  			}
  1060  		}
  1061  	case "completeFunctionCalls":
  1062  		result.setBool(&o.CompleteFunctionCalls)
  1063  
  1064  	case "semanticTokens":
  1065  		result.setBool(&o.SemanticTokens)
  1066  
  1067  	case "noSemanticString":
  1068  		result.setBool(&o.NoSemanticString)
  1069  
  1070  	case "noSemanticNumber":
  1071  		result.setBool(&o.NoSemanticNumber)
  1072  
  1073  	case "expandWorkspaceToModule":
  1074  		// See golang/go#63536: we can consider deprecating
  1075  		// expandWorkspaceToModule, but probably need to change the default
  1076  		// behavior in that case to *not* expand to the module.
  1077  		result.setBool(&o.ExpandWorkspaceToModule)
  1078  
  1079  	case "experimentalPostfixCompletions":
  1080  		result.setBool(&o.ExperimentalPostfixCompletions)
  1081  
  1082  	case "experimentalWorkspaceModule":
  1083  		result.deprecated("")
  1084  
  1085  	case "experimentalTemplateSupport": // TODO(pjw): remove after June 2022
  1086  		result.deprecated("")
  1087  
  1088  	case "templateExtensions":
  1089  		if iexts, ok := value.([]interface{}); ok {
  1090  			ans := []string{}
  1091  			for _, x := range iexts {
  1092  				ans = append(ans, fmt.Sprint(x))
  1093  			}
  1094  			o.TemplateExtensions = ans
  1095  			break
  1096  		}
  1097  		if value == nil {
  1098  			o.TemplateExtensions = nil
  1099  			break
  1100  		}
  1101  		result.parseErrorf("unexpected type %T not []string", value)
  1102  
  1103  	case "experimentalDiagnosticsDelay":
  1104  		result.deprecated("diagnosticsDelay")
  1105  
  1106  	case "diagnosticsDelay":
  1107  		result.setDuration(&o.DiagnosticsDelay)
  1108  
  1109  	case "diagnosticsTrigger":
  1110  		if s, ok := result.asOneOf(
  1111  			string(DiagnosticsOnEdit),
  1112  			string(DiagnosticsOnSave),
  1113  		); ok {
  1114  			o.DiagnosticsTrigger = DiagnosticsTrigger(s)
  1115  		}
  1116  
  1117  	case "analysisProgressReporting":
  1118  		result.setBool(&o.AnalysisProgressReporting)
  1119  
  1120  	case "experimentalWatchedFileDelay":
  1121  		result.deprecated("")
  1122  
  1123  	case "experimentalPackageCacheKey":
  1124  		result.deprecated("")
  1125  
  1126  	case "allowModfileModifications":
  1127  		result.softErrorf("gopls setting \"allowModfileModifications\" is deprecated.\nPlease comment on https://go.dev/issue/65546 if this impacts your workflow.")
  1128  		result.setBool(&o.AllowModfileModifications)
  1129  
  1130  	case "allowImplicitNetworkAccess":
  1131  		result.setBool(&o.AllowImplicitNetworkAccess)
  1132  
  1133  	case "experimentalUseInvalidMetadata":
  1134  		result.deprecated("")
  1135  
  1136  	case "standaloneTags":
  1137  		result.setStringSlice(&o.StandaloneTags)
  1138  
  1139  	case "allExperiments":
  1140  		// This setting should be handled before all of the other options are
  1141  		// processed, so do nothing here.
  1142  
  1143  	case "newDiff":
  1144  		result.deprecated("")
  1145  
  1146  	case "subdirWatchPatterns":
  1147  		if s, ok := result.asOneOf(
  1148  			string(SubdirWatchPatternsOn),
  1149  			string(SubdirWatchPatternsOff),
  1150  			string(SubdirWatchPatternsAuto),
  1151  		); ok {
  1152  			o.SubdirWatchPatterns = SubdirWatchPatterns(s)
  1153  		}
  1154  
  1155  	case "reportAnalysisProgressAfter":
  1156  		result.setDuration(&o.ReportAnalysisProgressAfter)
  1157  
  1158  	case "telemetryPrompt":
  1159  		result.setBool(&o.TelemetryPrompt)
  1160  
  1161  	case "linkifyShowMessage":
  1162  		result.setBool(&o.LinkifyShowMessage)
  1163  
  1164  	case "includeReplaceInWorkspace":
  1165  		result.setBool(&o.IncludeReplaceInWorkspace)
  1166  
  1167  	case "zeroConfig":
  1168  		result.setBool(&o.ZeroConfig)
  1169  
  1170  	// Replaced settings.
  1171  	case "experimentalDisabledAnalyses":
  1172  		result.deprecated("analyses")
  1173  
  1174  	case "disableDeepCompletion":
  1175  		result.deprecated("deepCompletion")
  1176  
  1177  	case "disableFuzzyMatching":
  1178  		result.deprecated("fuzzyMatching")
  1179  
  1180  	case "wantCompletionDocumentation":
  1181  		result.deprecated("completionDocumentation")
  1182  
  1183  	case "wantUnimportedCompletions":
  1184  		result.deprecated("completeUnimported")
  1185  
  1186  	case "fuzzyMatching":
  1187  		result.deprecated("matcher")
  1188  
  1189  	case "caseSensitiveCompletion":
  1190  		result.deprecated("matcher")
  1191  
  1192  	// Deprecated settings.
  1193  	case "wantSuggestedFixes":
  1194  		result.deprecated("")
  1195  
  1196  	case "noIncrementalSync":
  1197  		result.deprecated("")
  1198  
  1199  	case "watchFileChanges":
  1200  		result.deprecated("")
  1201  
  1202  	case "go-diff":
  1203  		result.deprecated("")
  1204  
  1205  	default:
  1206  		result.unexpected()
  1207  	}
  1208  	return result
  1209  }
  1210  
  1211  // parseErrorf reports an error parsing the current configuration value.
  1212  func (r *OptionResult) parseErrorf(msg string, values ...interface{}) {
  1213  	if false {
  1214  		_ = fmt.Sprintf(msg, values...) // this causes vet to check this like printf
  1215  	}
  1216  	prefix := fmt.Sprintf("parsing setting %q: ", r.Name)
  1217  	r.Error = fmt.Errorf(prefix+msg, values...)
  1218  }
  1219  
  1220  // A SoftError is an error that does not affect the functionality of gopls.
  1221  type SoftError struct {
  1222  	msg string
  1223  }
  1224  
  1225  func (e *SoftError) Error() string {
  1226  	return e.msg
  1227  }
  1228  
  1229  // deprecated reports the current setting as deprecated. If 'replacement' is
  1230  // non-nil, it is suggested to the user.
  1231  func (r *OptionResult) deprecated(replacement string) {
  1232  	msg := fmt.Sprintf("gopls setting %q is deprecated", r.Name)
  1233  	if replacement != "" {
  1234  		msg = fmt.Sprintf("%s, use %q instead", msg, replacement)
  1235  	}
  1236  	r.Error = &SoftError{msg}
  1237  }
  1238  
  1239  // softErrorf reports a soft error related to the current option.
  1240  func (r *OptionResult) softErrorf(format string, args ...any) {
  1241  	r.Error = &SoftError{fmt.Sprintf(format, args...)}
  1242  }
  1243  
  1244  // unexpected reports that the current setting is not known to gopls.
  1245  func (r *OptionResult) unexpected() {
  1246  	r.Error = fmt.Errorf("unexpected gopls setting %q", r.Name)
  1247  }
  1248  
  1249  func (r *OptionResult) asBool() (bool, bool) {
  1250  	b, ok := r.Value.(bool)
  1251  	if !ok {
  1252  		r.parseErrorf("invalid type %T, expect bool", r.Value)
  1253  		return false, false
  1254  	}
  1255  	return b, true
  1256  }
  1257  
  1258  func (r *OptionResult) setBool(b *bool) {
  1259  	if v, ok := r.asBool(); ok {
  1260  		*b = v
  1261  	}
  1262  }
  1263  
  1264  func (r *OptionResult) setDuration(d *time.Duration) {
  1265  	if v, ok := r.asString(); ok {
  1266  		parsed, err := time.ParseDuration(v)
  1267  		if err != nil {
  1268  			r.parseErrorf("failed to parse duration %q: %v", v, err)
  1269  			return
  1270  		}
  1271  		*d = parsed
  1272  	}
  1273  }
  1274  
  1275  func (r *OptionResult) setBoolMap(bm *map[string]bool) {
  1276  	m := r.asBoolMap()
  1277  	*bm = m
  1278  }
  1279  
  1280  func (r *OptionResult) setAnnotationMap(bm *map[Annotation]bool) {
  1281  	all := r.asBoolMap()
  1282  	if all == nil {
  1283  		return
  1284  	}
  1285  	// Default to everything enabled by default.
  1286  	m := make(map[Annotation]bool)
  1287  	for k, enabled := range all {
  1288  		a, err := asOneOf(
  1289  			k,
  1290  			string(Nil),
  1291  			string(Escape),
  1292  			string(Inline),
  1293  			string(Bounds),
  1294  		)
  1295  		if err != nil {
  1296  			// In case of an error, process any legacy values.
  1297  			switch k {
  1298  			case "noEscape":
  1299  				m[Escape] = false
  1300  				r.parseErrorf(`"noEscape" is deprecated, set "Escape: false" instead`)
  1301  			case "noNilcheck":
  1302  				m[Nil] = false
  1303  				r.parseErrorf(`"noNilcheck" is deprecated, set "Nil: false" instead`)
  1304  			case "noInline":
  1305  				m[Inline] = false
  1306  				r.parseErrorf(`"noInline" is deprecated, set "Inline: false" instead`)
  1307  			case "noBounds":
  1308  				m[Bounds] = false
  1309  				r.parseErrorf(`"noBounds" is deprecated, set "Bounds: false" instead`)
  1310  			default:
  1311  				r.parseErrorf("%v", err)
  1312  			}
  1313  			continue
  1314  		}
  1315  		m[Annotation(a)] = enabled
  1316  	}
  1317  	*bm = m
  1318  }
  1319  
  1320  func (r *OptionResult) asBoolMap() map[string]bool {
  1321  	all, ok := r.Value.(map[string]interface{})
  1322  	if !ok {
  1323  		r.parseErrorf("invalid type %T for map[string]bool option", r.Value)
  1324  		return nil
  1325  	}
  1326  	m := make(map[string]bool)
  1327  	for a, enabled := range all {
  1328  		if e, ok := enabled.(bool); ok {
  1329  			m[a] = e
  1330  		} else {
  1331  			r.parseErrorf("invalid type %T for map key %q", enabled, a)
  1332  			return m
  1333  		}
  1334  	}
  1335  	return m
  1336  }
  1337  
  1338  func (r *OptionResult) asString() (string, bool) {
  1339  	b, ok := r.Value.(string)
  1340  	if !ok {
  1341  		r.parseErrorf("invalid type %T, expect string", r.Value)
  1342  		return "", false
  1343  	}
  1344  	return b, true
  1345  }
  1346  
  1347  func (r *OptionResult) asStringSlice() ([]string, bool) {
  1348  	iList, ok := r.Value.([]interface{})
  1349  	if !ok {
  1350  		r.parseErrorf("invalid type %T, expect list", r.Value)
  1351  		return nil, false
  1352  	}
  1353  	var list []string
  1354  	for _, elem := range iList {
  1355  		s, ok := elem.(string)
  1356  		if !ok {
  1357  			r.parseErrorf("invalid element type %T, expect string", elem)
  1358  			return nil, false
  1359  		}
  1360  		list = append(list, s)
  1361  	}
  1362  	return list, true
  1363  }
  1364  
  1365  func (r *OptionResult) asOneOf(options ...string) (string, bool) {
  1366  	s, ok := r.asString()
  1367  	if !ok {
  1368  		return "", false
  1369  	}
  1370  	s, err := asOneOf(s, options...)
  1371  	if err != nil {
  1372  		r.parseErrorf("%v", err)
  1373  	}
  1374  	return s, err == nil
  1375  }
  1376  
  1377  func asOneOf(str string, options ...string) (string, error) {
  1378  	lower := strings.ToLower(str)
  1379  	for _, opt := range options {
  1380  		if strings.ToLower(opt) == lower {
  1381  			return opt, nil
  1382  		}
  1383  	}
  1384  	return "", fmt.Errorf("invalid option %q for enum", str)
  1385  }
  1386  
  1387  func (r *OptionResult) setString(s *string) {
  1388  	if v, ok := r.asString(); ok {
  1389  		*s = v
  1390  	}
  1391  }
  1392  
  1393  func (r *OptionResult) setStringSlice(s *[]string) {
  1394  	if v, ok := r.asStringSlice(); ok {
  1395  		*s = v
  1396  	}
  1397  }
  1398  
  1399  func analyzers() map[string]*Analyzer {
  1400  	return map[string]*Analyzer{
  1401  		// The traditional vet suite:
  1402  		appends.Analyzer.Name:   {Analyzer: appends.Analyzer, Enabled: true},
  1403  		asmdecl.Analyzer.Name:   {Analyzer: asmdecl.Analyzer, Enabled: true},
  1404  		assign.Analyzer.Name:    {Analyzer: assign.Analyzer, Enabled: true},
  1405  		atomic.Analyzer.Name:    {Analyzer: atomic.Analyzer, Enabled: true},
  1406  		bools.Analyzer.Name:     {Analyzer: bools.Analyzer, Enabled: true},
  1407  		buildtag.Analyzer.Name:  {Analyzer: buildtag.Analyzer, Enabled: true},
  1408  		cgocall.Analyzer.Name:   {Analyzer: cgocall.Analyzer, Enabled: true},
  1409  		composite.Analyzer.Name: {Analyzer: composite.Analyzer, Enabled: true},
  1410  		copylock.Analyzer.Name:  {Analyzer: copylock.Analyzer, Enabled: true},
  1411  		defers.Analyzer.Name:    {Analyzer: defers.Analyzer, Enabled: true},
  1412  		deprecated.Analyzer.Name: {
  1413  			Analyzer: deprecated.Analyzer,
  1414  			Enabled:  true,
  1415  			Severity: protocol.SeverityHint,
  1416  			Tag:      []protocol.DiagnosticTag{protocol.Deprecated},
  1417  		},
  1418  		directive.Analyzer.Name:     {Analyzer: directive.Analyzer, Enabled: true},
  1419  		errorsas.Analyzer.Name:      {Analyzer: errorsas.Analyzer, Enabled: true},
  1420  		httpresponse.Analyzer.Name:  {Analyzer: httpresponse.Analyzer, Enabled: true},
  1421  		ifaceassert.Analyzer.Name:   {Analyzer: ifaceassert.Analyzer, Enabled: true},
  1422  		loopclosure.Analyzer.Name:   {Analyzer: loopclosure.Analyzer, Enabled: true},
  1423  		lostcancel.Analyzer.Name:    {Analyzer: lostcancel.Analyzer, Enabled: true},
  1424  		nilfunc.Analyzer.Name:       {Analyzer: nilfunc.Analyzer, Enabled: true},
  1425  		printf.Analyzer.Name:        {Analyzer: printf.Analyzer, Enabled: true},
  1426  		shift.Analyzer.Name:         {Analyzer: shift.Analyzer, Enabled: true},
  1427  		slog.Analyzer.Name:          {Analyzer: slog.Analyzer, Enabled: true},
  1428  		stdmethods.Analyzer.Name:    {Analyzer: stdmethods.Analyzer, Enabled: true},
  1429  		stringintconv.Analyzer.Name: {Analyzer: stringintconv.Analyzer, Enabled: true},
  1430  		structtag.Analyzer.Name:     {Analyzer: structtag.Analyzer, Enabled: true},
  1431  		tests.Analyzer.Name:         {Analyzer: tests.Analyzer, Enabled: true},
  1432  		unmarshal.Analyzer.Name:     {Analyzer: unmarshal.Analyzer, Enabled: true},
  1433  		unreachable.Analyzer.Name:   {Analyzer: unreachable.Analyzer, Enabled: true},
  1434  		unsafeptr.Analyzer.Name:     {Analyzer: unsafeptr.Analyzer, Enabled: true},
  1435  		unusedresult.Analyzer.Name:  {Analyzer: unusedresult.Analyzer, Enabled: true},
  1436  
  1437  		// Non-vet analyzers:
  1438  		atomicalign.Analyzer.Name:      {Analyzer: atomicalign.Analyzer, Enabled: true},
  1439  		deepequalerrors.Analyzer.Name:  {Analyzer: deepequalerrors.Analyzer, Enabled: true},
  1440  		fieldalignment.Analyzer.Name:   {Analyzer: fieldalignment.Analyzer, Enabled: false},
  1441  		nilness.Analyzer.Name:          {Analyzer: nilness.Analyzer, Enabled: true},
  1442  		shadow.Analyzer.Name:           {Analyzer: shadow.Analyzer, Enabled: false},
  1443  		sortslice.Analyzer.Name:        {Analyzer: sortslice.Analyzer, Enabled: true},
  1444  		testinggoroutine.Analyzer.Name: {Analyzer: testinggoroutine.Analyzer, Enabled: true},
  1445  		unusedparams.Analyzer.Name:     {Analyzer: unusedparams.Analyzer, Enabled: true},
  1446  		unusedwrite.Analyzer.Name:      {Analyzer: unusedwrite.Analyzer, Enabled: false},
  1447  		useany.Analyzer.Name:           {Analyzer: useany.Analyzer, Enabled: false},
  1448  		infertypeargs.Analyzer.Name: {
  1449  			Analyzer: infertypeargs.Analyzer,
  1450  			Enabled:  true,
  1451  			Severity: protocol.SeverityHint,
  1452  		},
  1453  		timeformat.Analyzer.Name:     {Analyzer: timeformat.Analyzer, Enabled: true},
  1454  		embeddirective.Analyzer.Name: {Analyzer: embeddirective.Analyzer, Enabled: true},
  1455  
  1456  		// gofmt -s suite:
  1457  		simplifycompositelit.Analyzer.Name: {
  1458  			Analyzer:    simplifycompositelit.Analyzer,
  1459  			Enabled:     true,
  1460  			ActionKinds: []protocol.CodeActionKind{protocol.SourceFixAll, protocol.QuickFix},
  1461  		},
  1462  		simplifyrange.Analyzer.Name: {
  1463  			Analyzer:    simplifyrange.Analyzer,
  1464  			Enabled:     true,
  1465  			ActionKinds: []protocol.CodeActionKind{protocol.SourceFixAll, protocol.QuickFix},
  1466  		},
  1467  		simplifyslice.Analyzer.Name: {
  1468  			Analyzer:    simplifyslice.Analyzer,
  1469  			Enabled:     true,
  1470  			ActionKinds: []protocol.CodeActionKind{protocol.SourceFixAll, protocol.QuickFix},
  1471  		},
  1472  
  1473  		// Type error analyzers.
  1474  		// These analyzers enrich go/types errors with suggested fixes.
  1475  		fillreturns.Analyzer.Name:    {Analyzer: fillreturns.Analyzer, Enabled: true},
  1476  		nonewvars.Analyzer.Name:      {Analyzer: nonewvars.Analyzer, Enabled: true},
  1477  		noresultvalues.Analyzer.Name: {Analyzer: noresultvalues.Analyzer, Enabled: true},
  1478  		stubmethods.Analyzer.Name:    {Analyzer: stubmethods.Analyzer, Enabled: true},
  1479  		undeclaredname.Analyzer.Name: {Analyzer: undeclaredname.Analyzer, Enabled: true},
  1480  		// TODO(rfindley): why isn't the 'unusedvariable' analyzer enabled, if it
  1481  		// is only enhancing type errors with suggested fixes?
  1482  		//
  1483  		// In particular, enabling this analyzer could cause unused variables to be
  1484  		// greyed out, (due to the 'deletions only' fix). That seems like a nice UI
  1485  		// feature.
  1486  		unusedvariable.Analyzer.Name: {Analyzer: unusedvariable.Analyzer, Enabled: false},
  1487  	}
  1488  }
  1489  
  1490  func urlRegexp() *regexp.Regexp {
  1491  	// Ensure links are matched as full words, not anywhere.
  1492  	re := regexp.MustCompile(`\b(http|ftp|https)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?\b`)
  1493  	re.Longest()
  1494  	return re
  1495  }