internal/lsp: initial test set up for inlay hints

Set up the tests for inlay hints. We test inlay hints by converting them to text edits
and verifying the output is as we expected it.

This change does not yet deal with making sure the server
settings are correct.

Change-Id: I136f971a87bf9936fd44047d45fe0a3f03c9164e
Reviewed-on: https://go-review.googlesource.com/c/tools/+/411095
Run-TryBot: Suzy Mueller <suzmue@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Jamal Carvalho <jamal@golang.org>
diff --git a/internal/lsp/cmd/test/cmdtest.go b/internal/lsp/cmd/test/cmdtest.go
index 312f7b8..ff0461b 100644
--- a/internal/lsp/cmd/test/cmdtest.go
+++ b/internal/lsp/cmd/test/cmdtest.go
@@ -113,6 +113,10 @@
 	//TODO: hovering not supported on command line
 }
 
+func (r *runner) InlayHints(t *testing.T, spn span.Span) {
+	// TODO: inlayHints not supported on command line
+}
+
 func (r *runner) runGoplsCmd(t testing.TB, args ...string) (string, string) {
 	rStdout, wStdout, err := os.Pipe()
 	if err != nil {
diff --git a/internal/lsp/lsp_test.go b/internal/lsp/lsp_test.go
index ee364b8..e097100 100644
--- a/internal/lsp/lsp_test.go
+++ b/internal/lsp/lsp_test.go
@@ -932,6 +932,48 @@
 	}
 }
 
+func (r *runner) InlayHints(t *testing.T, spn span.Span) {
+	uri := spn.URI()
+	filename := uri.Filename()
+
+	hints, err := r.server.InlayHint(r.ctx, &protocol.InlayHintParams{
+		TextDocument: protocol.TextDocumentIdentifier{
+			URI: protocol.URIFromSpanURI(uri),
+		},
+		// TODO: add ViewPort
+	})
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// Map inlay hints to text edits.
+	edits := make([]protocol.TextEdit, len(hints))
+	for i, hint := range hints {
+		edits[i] = protocol.TextEdit{
+			Range:   protocol.Range{Start: *hint.Position, End: *hint.Position},
+			NewText: fmt.Sprintf("<%s>", hint.Label[0].Value),
+		}
+	}
+
+	m, err := r.data.Mapper(uri)
+	if err != nil {
+		t.Fatal(err)
+	}
+	sedits, err := source.FromProtocolEdits(m, edits)
+	if err != nil {
+		t.Error(err)
+	}
+	got := diff.ApplyEdits(string(m.Content), sedits)
+
+	withinlayHints := string(r.data.Golden("inlayHint", filename, func() ([]byte, error) {
+		return []byte(got), nil
+	}))
+
+	if withinlayHints != got {
+		t.Errorf("format failed for %s, expected:\n%v\ngot:\n%v", filename, withinlayHints, got)
+	}
+}
+
 func (r *runner) Rename(t *testing.T, spn span.Span, newText string) {
 	tag := fmt.Sprintf("%s-rename", newText)
 
diff --git a/internal/lsp/source/source_test.go b/internal/lsp/source/source_test.go
index 426bffc..9218f9d 100644
--- a/internal/lsp/source/source_test.go
+++ b/internal/lsp/source/source_test.go
@@ -685,6 +685,10 @@
 	}
 }
 
+func (r *runner) InlayHints(t *testing.T, src span.Span) {
+	// TODO(golang/go#53315): add source test
+}
+
 func (r *runner) Hover(t *testing.T, src span.Span, text string) {
 	ctx := r.ctx
 	_, srcRng, err := spanToRange(r.data, src)
diff --git a/internal/lsp/testdata/inlayHint/a.go b/internal/lsp/testdata/inlayHint/a.go
new file mode 100644
index 0000000..90ef7c4
--- /dev/null
+++ b/internal/lsp/testdata/inlayHint/a.go
@@ -0,0 +1,9 @@
+package inlayHint //@inlayHint("package")
+
+func hello(name string) string {
+	return "Hello " + name
+}
+
+func helloWorld() string {
+	return hello("World")
+}
diff --git a/internal/lsp/testdata/inlayHint/a.go.golden b/internal/lsp/testdata/inlayHint/a.go.golden
new file mode 100644
index 0000000..e4e6cc0
--- /dev/null
+++ b/internal/lsp/testdata/inlayHint/a.go.golden
@@ -0,0 +1,11 @@
+-- inlayHint --
+package inlayHint //@inlayHint("package")
+
+func hello(name string) string {
+	return "Hello " + name
+}
+
+func helloWorld() string {
+	return hello("World")
+}
+
diff --git a/internal/lsp/tests/tests.go b/internal/lsp/tests/tests.go
index 8265cf2..81a5d39 100644
--- a/internal/lsp/tests/tests.go
+++ b/internal/lsp/tests/tests.go
@@ -81,6 +81,7 @@
 type Symbols map[span.URI][]protocol.DocumentSymbol
 type SymbolsChildren map[string][]protocol.DocumentSymbol
 type SymbolInformation map[span.Span]protocol.SymbolInformation
+type InlayHints []span.Span
 type WorkspaceSymbols map[WorkspaceSymbolsTestType]map[span.URI][]string
 type Signatures map[span.Span]*protocol.SignatureHelp
 type Links map[span.URI][]Link
@@ -113,6 +114,7 @@
 	Highlights               Highlights
 	References               References
 	Renames                  Renames
+	InlayHints               InlayHints
 	PrepareRenames           PrepareRenames
 	Symbols                  Symbols
 	symbolsChildren          SymbolsChildren
@@ -156,6 +158,7 @@
 	Definition(*testing.T, span.Span, Definition)
 	Implementation(*testing.T, span.Span, []span.Span)
 	Highlight(*testing.T, span.Span, []span.Span)
+	InlayHints(*testing.T, span.Span)
 	References(*testing.T, span.Span, []span.Span)
 	Rename(*testing.T, span.Span, string)
 	PrepareRename(*testing.T, span.Span, *source.PrepareItem)
@@ -466,6 +469,7 @@
 		"hoverdef":        datum.collectHoverDefinitions,
 		"hover":           datum.collectHovers,
 		"highlight":       datum.collectHighlights,
+		"inlayHint":       datum.collectInlayHints,
 		"refs":            datum.collectReferences,
 		"rename":          datum.collectRenames,
 		"prepare":         datum.collectPrepareRenames,
@@ -782,6 +786,17 @@
 		}
 	})
 
+	t.Run("InlayHints", func(t *testing.T) {
+		t.Skip("Inlay Hints not yet implemented")
+		t.Helper()
+		for _, src := range data.InlayHints {
+			t.Run(SpanName(src), func(t *testing.T) {
+				t.Helper()
+				tests.InlayHints(t, src)
+			})
+		}
+	})
+
 	t.Run("References", func(t *testing.T) {
 		t.Helper()
 		for src, itemList := range data.References {
@@ -1292,6 +1307,10 @@
 	data.Highlights[src] = append(data.Highlights[src], expected...)
 }
 
+func (data *Data) collectInlayHints(src span.Span) {
+	data.InlayHints = append(data.InlayHints, src)
+}
+
 func (data *Data) collectReferences(src span.Span, expected []span.Span) {
 	data.References[src] = expected
 }