internal/lsp: add text edits for unkeyed literals

Add text edits that a user can accept to make the unkeyed composite
literals keyed from the inlay hints. The text edits modify all of
the unkeyed fields in a composite literal, since a mixture of keyed
and unkeyed fields are not allowed.

Change-Id: I0683fbaa5e22bc004b91c98fc09e495e797826ee
Reviewed-on: https://go-review.googlesource.com/c/tools/+/414855
TryBot-Result: Gopher Robot <gobot@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Jamal Carvalho <jamal@golang.org>
Run-TryBot: Suzy Mueller <suzmue@golang.org>
diff --git a/internal/lsp/protocol/tsprotocol.go b/internal/lsp/protocol/tsprotocol.go
index 5dd3d09..3a284bf 100644
--- a/internal/lsp/protocol/tsprotocol.go
+++ b/internal/lsp/protocol/tsprotocol.go
@@ -2745,6 +2745,14 @@
 	 */
 	Kind InlayHintKind `json:"kind,omitempty"`
 	/**
+	 * Optional text edits that are performed when accepting this inlay hint.
+	 *
+	 * *Note* that edits are expected to change the document so that the inlay
+	 * hint (or its nearest variant) is now part of the document and the inlay
+	 * hint itself is now obsolete.
+	 */
+	TextEdits []TextEdit `json:"textEdits,omitempty"`
+	/**
 	 * The tooltip text when you hover over this item.
 	 */
 	Tooltip string/*string | MarkupContent*/ `json:"tooltip,omitempty"`
diff --git a/internal/lsp/source/inlay_hint.go b/internal/lsp/source/inlay_hint.go
index 009cc52..967752b 100644
--- a/internal/lsp/source/inlay_hint.go
+++ b/internal/lsp/source/inlay_hint.go
@@ -344,7 +344,7 @@
 	}
 
 	var hints []protocol.InlayHint
-
+	var allEdits []protocol.TextEdit
 	for i, v := range compLit.Elts {
 		if _, ok := v.(*ast.KeyValueExpr); !ok {
 			start, ok := tmap.Position(v.Pos())
@@ -360,8 +360,17 @@
 				Kind:         protocol.Parameter,
 				PaddingRight: true,
 			})
+			allEdits = append(allEdits, protocol.TextEdit{
+				Range:   protocol.Range{Start: start, End: start},
+				NewText: strct.Field(i).Name() + ": ",
+			})
 		}
 	}
+	// It is not allowed to have a mix of keyed and unkeyed fields, so
+	// have the text edits add keys to all fields.
+	for i := range hints {
+		hints[i].TextEdits = allEdits
+	}
 	return hints
 }