gopls/internal/regtest: follow-ups to review comments from earlier CLs

For convenience, this CL addresses review comments from earlier CLs in
the stack.

Change-Id: I28581c77170aa5e3978b28c4c7c7c85e6cbf506f
Reviewed-on: https://go-review.googlesource.com/c/tools/+/461943
Run-TryBot: Robert Findley <rfindley@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
diff --git a/gopls/internal/lsp/fake/edit.go b/gopls/internal/lsp/fake/edit.go
index 0b688e7..5fd65b0 100644
--- a/gopls/internal/lsp/fake/edit.go
+++ b/gopls/internal/lsp/fake/edit.go
@@ -9,8 +9,10 @@
 	"golang.org/x/tools/internal/diff"
 )
 
-// NewEdit creates an edit replacing all content between
+// NewEdit creates an edit replacing all content between the 0-based
 // (startLine, startColumn) and (endLine, endColumn) with text.
+//
+// Columns measure UTF-16 codes.
 func NewEdit(startLine, startColumn, endLine, endColumn uint32, text string) protocol.TextEdit {
 	return protocol.TextEdit{
 		Range: protocol.Range{
diff --git a/gopls/internal/lsp/fake/editor.go b/gopls/internal/lsp/fake/editor.go
index 7a61785..c9214a8 100644
--- a/gopls/internal/lsp/fake/editor.go
+++ b/gopls/internal/lsp/fake/editor.go
@@ -816,9 +816,7 @@
 
 // Symbol performs a workspace symbol search using query
 func (e *Editor) Symbol(ctx context.Context, query string) ([]protocol.SymbolInformation, error) {
-	params := &protocol.WorkspaceSymbolParams{}
-	params.Query = query
-
+	params := &protocol.WorkspaceSymbolParams{Query: query}
 	return e.Server.Symbol(ctx, params)
 }
 
diff --git a/gopls/internal/lsp/regtest/env.go b/gopls/internal/lsp/regtest/env.go
index ad60658..67e7207 100644
--- a/gopls/internal/lsp/regtest/env.go
+++ b/gopls/internal/lsp/regtest/env.go
@@ -297,17 +297,17 @@
 		if v > finalVerdict {
 			finalVerdict = v
 		}
-		summary.WriteString(fmt.Sprintf("%v: %s\n", v, e.Description))
+		fmt.Fprintf(&summary, "%v: %s\n", v, e.Description)
 	}
 	return finalVerdict, summary.String()
 }
 
 // Await blocks until the given expectations are all simultaneously met.
 //
-// Generally speaking Await should be avoided because it can block indefinitely
-// if gopls ends up in a state where the expectations are never going to be
-// met. Use AfterChange or OnceMet instead, so that the runner knows when to
-// stop waiting.
+// Generally speaking Await should be avoided because it blocks indefinitely if
+// gopls ends up in a state where the expectations are never going to be met.
+// Use AfterChange or OnceMet instead, so that the runner knows when to stop
+// waiting.
 func (e *Env) Await(expectations ...Expectation) {
 	e.T.Helper()
 	if err := e.Awaiter.Await(e.Ctx, expectations...); err != nil {
@@ -315,8 +315,9 @@
 	}
 }
 
-// OnceMet blocks until precondition is met or unmeetable; if the precondition
-// is met, it atomically checks that all expectations in mustMeets are met.
+// OnceMet blocks until the precondition is met by the state or becomes
+// unmeetable. If it was met, OnceMet checks that the state meets all
+// expectations in mustMeets.
 func (e *Env) OnceMet(precondition Expectation, mustMeets ...Expectation) {
 	e.Await(OnceMet(precondition, mustMeets...))
 }
diff --git a/gopls/internal/lsp/regtest/expectation.go b/gopls/internal/lsp/regtest/expectation.go
index d0896ff..6d3f334 100644
--- a/gopls/internal/lsp/regtest/expectation.go
+++ b/gopls/internal/lsp/regtest/expectation.go
@@ -49,9 +49,25 @@
 	return fmt.Sprintf("unrecognized verdict %d", v)
 }
 
-// Expectation holds an arbitrary check func, and implements the Expectation interface.
+// An Expectation is an expected property of the state of the LSP client.
+// The Check function reports whether the property is met.
+//
+// Expectations are combinators. By composing them, tests may express
+// complex expectations in terms of simpler ones.
+//
+// TODO(rfindley): as expectations are combined, it becomes harder to identify
+// why they failed. A better signature for Check would be
+//
+//	func(State) (Verdict, string)
+//
+// returning a reason for the verdict that can be composed similarly to
+// descriptions.
 type Expectation struct {
-	Check       func(State) Verdict
+	Check func(State) Verdict
+
+	// Description holds a noun-phrase identifying what the expectation checks.
+	//
+	// TODO(rfindley): revisit existing descriptions to ensure they compose nicely.
 	Description string
 }
 
@@ -135,8 +151,11 @@
 	}
 }
 
-// ReadDiagnostics is an 'expectation' that is used to read diagnostics
-// atomically. It is intended to be used with 'OnceMet'.
+// ReadDiagnostics is an Expectation that stores the current diagnostics for
+// fileName in into, whenever it is evaluated.
+//
+// It can be used in combination with OnceMet or AfterChange to capture the
+// state of diagnostics when other expectations are satisfied.
 func ReadDiagnostics(fileName string, into *protocol.PublishDiagnosticsParams) Expectation {
 	check := func(s State) Verdict {
 		diags, ok := s.diagnostics[fileName]
@@ -695,7 +714,7 @@
 func (e *Env) AtRegexp(name, pattern string) DiagnosticFilter {
 	pos := e.RegexpSearch(name, pattern)
 	return DiagnosticFilter{
-		desc: fmt.Sprintf("at the first position matching %q in %q", pattern, name),
+		desc: fmt.Sprintf("at the first position matching %#q in %q", pattern, name),
 		check: func(diagName string, d protocol.Diagnostic) bool {
 			return diagName == name && d.Range.Start == pos
 		},
@@ -704,6 +723,10 @@
 
 // AtPosition filters to diagnostics at location name:line:character, for a
 // sandbox-relative path name.
+//
+// Line and character are 0-based, and character measures UTF-16 codes.
+//
+// Note: prefer the more readable AtRegexp.
 func AtPosition(name string, line, character uint32) DiagnosticFilter {
 	pos := protocol.Position{Line: line, Character: character}
 	return DiagnosticFilter{
diff --git a/gopls/internal/regtest/misc/workspace_symbol_test.go b/gopls/internal/regtest/misc/workspace_symbol_test.go
index 33b8147..a492e1d 100644
--- a/gopls/internal/regtest/misc/workspace_symbol_test.go
+++ b/gopls/internal/regtest/misc/workspace_symbol_test.go
@@ -79,7 +79,7 @@
 			"Fooest",
 		}
 		got := env.Symbol("Foo")
-		compareSymbols(t, got, want)
+		compareSymbols(t, got, want...)
 	})
 }
 
@@ -102,15 +102,15 @@
 	WithOptions(
 		Settings{"symbolMatcher": symbolMatcher},
 	).Run(t, files, func(t *testing.T, env *Env) {
-		compareSymbols(t, env.Symbol("ABC"), []string{"ABC", "AxxBxxCxx"})
-		compareSymbols(t, env.Symbol("'ABC"), []string{"ABC"})
-		compareSymbols(t, env.Symbol("^mod.com"), []string{"mod.com/a.ABC", "mod.com/a.AxxBxxCxx"})
-		compareSymbols(t, env.Symbol("^mod.com Axx"), []string{"mod.com/a.AxxBxxCxx"})
-		compareSymbols(t, env.Symbol("C$"), []string{"ABC"})
+		compareSymbols(t, env.Symbol("ABC"), "ABC", "AxxBxxCxx")
+		compareSymbols(t, env.Symbol("'ABC"), "ABC")
+		compareSymbols(t, env.Symbol("^mod.com"), "mod.com/a.ABC", "mod.com/a.AxxBxxCxx")
+		compareSymbols(t, env.Symbol("^mod.com Axx"), "mod.com/a.AxxBxxCxx")
+		compareSymbols(t, env.Symbol("C$"), "ABC")
 	})
 }
 
-func compareSymbols(t *testing.T, got []protocol.SymbolInformation, want []string) {
+func compareSymbols(t *testing.T, got []protocol.SymbolInformation, want ...string) {
 	t.Helper()
 	if len(got) != len(want) {
 		t.Errorf("got %d symbols, want %d", len(got), len(want))