internal/lsp: add support for an "enable all experiments" settings

This setting automatically turns on all of the off-by-default gopls
features so that users can opt in to all experiments.

Fixes golang/go#41763

Change-Id: Ia6998128649a081c2a1e8eb08c2db6d795a73143
Reviewed-on: https://go-review.googlesource.com/c/tools/+/260002
Trust: Rebecca Stambler <rstambler@golang.org>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
diff --git a/gopls/doc/settings.md b/gopls/doc/settings.md
index 69bcc90..2c00fed 100644
--- a/gopls/doc/settings.md
+++ b/gopls/doc/settings.md
@@ -17,6 +17,9 @@
 
 Below is the list of settings that are officially supported for `gopls`.
 
+To enable all experimental features, use **allExperiments: `true`**. You will
+still be able to independently override specific experimental features.
+
 <!-- BEGIN User: DO NOT MANUALLY EDIT THIS SECTION -->
 ### **buildFlags** *[]string*
 buildFlags is the set of flags passed on to the build system when invoked.
diff --git a/gopls/internal/regtest/diagnostics_test.go b/gopls/internal/regtest/diagnostics_test.go
index 0291f2c..d9450c0 100644
--- a/gopls/internal/regtest/diagnostics_test.go
+++ b/gopls/internal/regtest/diagnostics_test.go
@@ -1358,3 +1358,27 @@
 		)
 	})
 }
+
+func TestEnableAllExperiments(t *testing.T) {
+	const mod = `
+-- go.mod --
+module mod.com
+
+-- main.go --
+package main
+
+func main() {
+	if true {}
+}`
+	withOptions(
+		EditorConfig{
+			AllExperiments: true,
+		},
+	).run(t, mod, func(t *testing.T, env *Env) {
+		// Confirm that staticcheck is enabled.
+		env.OpenFile("main.go")
+		env.Await(
+			env.DiagnosticAtRegexp("main.go", "if"),
+		)
+	})
+}
diff --git a/internal/lsp/fake/editor.go b/internal/lsp/fake/editor.go
index 73ba921..f983062 100644
--- a/internal/lsp/fake/editor.go
+++ b/internal/lsp/fake/editor.go
@@ -90,6 +90,10 @@
 	// TODO(rstambler): This mode is temporary and should be removed when
 	// golang.org/cl/258518 is merged.
 	WithoutExperimentalWorkspaceModule bool
+
+	// AllExperiments sets the "allExperiments" configuration, which enables
+	// all of gopls's opt-in settings.
+	AllExperiments bool
 }
 
 // NewEditor Creates a new Editor.
@@ -205,6 +209,10 @@
 		config["experimentalWorkspaceModule"] = true
 	}
 
+	if e.Config.AllExperiments {
+		config["allExperiments"] = true
+	}
+
 	// TODO(rFindley): uncomment this if/when diagnostics delay is on by
 	// default... and probably change to the new settings name.
 	// config["experimentalDiagnosticsDelay"] = "10ms"
diff --git a/internal/lsp/source/options.go b/internal/lsp/source/options.go
index 615de2e..a84abe5 100644
--- a/internal/lsp/source/options.go
+++ b/internal/lsp/source/options.go
@@ -452,6 +452,13 @@
 	switch opts := opts.(type) {
 	case nil:
 	case map[string]interface{}:
+		// If the user's settings contains "allExperiments", set that first,
+		// and then let them override individual settings independently.
+		for name, value := range opts {
+			if b, ok := value.(bool); name == "allExperiments" && ok && b {
+				options.enableAllExperiments()
+			}
+		}
 		for name, value := range opts {
 			results = append(results, options.set(name, value))
 		}
@@ -538,6 +545,18 @@
 	o.StaticcheckAnalyzers[a.Name] = Analyzer{Analyzer: a, Enabled: true}
 }
 
+// enableAllExperiments turns on all of the experimental "off-by-default"
+// features offered by gopls.
+func (o *Options) enableAllExperiments() {
+	o.ExperimentalDiagnosticsDelay = 200 * time.Millisecond
+	o.ExperimentalWorkspaceModule = true
+	o.Staticcheck = true
+	o.Gofumpt = true
+	o.SymbolStyle = DynamicSymbols
+	o.Codelens[CommandToggleDetails.Name] = true
+	o.Analyses[unusedparams.Analyzer.Name] = true
+}
+
 func (o *Options) set(name string, value interface{}) OptionResult {
 	result := OptionResult{Name: name, Value: value}
 	switch name {