Skip to content

Commit 4646dbf

Browse files
committed
gopls/internal/protocol: customize InsertReplaceEdit JSON unmarshal
InsertReplaceEdit is used instead of TextEdit in CompletionItem in editors that support it. These two types are alike in appearance but can be differentiated by the presence or absence of certain properties. UnmarshalJSON of the sum type tries to unmarshal as TextEdit only if unmarshal as InsertReplaceEdit fails. Due to this similarity, unmarshal with the different type never fails. Add a custom JSON unmarshaller for InsertReplaceEdit, so it fails when the required fields are missing. That makes Or_CompletionItem_textEdit decode TextEdit type correctly. For golang/go#40871 For golang/go#61215 Change-Id: I62471fa973fa376cad5eb3934522ff21c14e3647 Reviewed-on: https://go-review.googlesource.com/c/tools/+/587135 Reviewed-by: Peter Weinberger <pjw@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
1 parent bc5e086 commit 4646dbf

File tree

2 files changed

+84
-0
lines changed

2 files changed

+84
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2024 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 protocol
6+
7+
import (
8+
"encoding/json"
9+
"fmt"
10+
)
11+
12+
// InsertReplaceEdit is used instead of TextEdit in CompletionItem
13+
// in editors that support it. These two types are alike in appearance
14+
// but can be differentiated by the presence or absence of
15+
// certain properties. UnmarshalJSON of the sum type tries to
16+
// unmarshal as TextEdit only if unmarshal as InsertReplaceEdit fails.
17+
// However, due to this similarity, unmarshal with the other type
18+
// never fails. This file has a custom JSON unmarshaller for
19+
// InsertReplaceEdit, that fails if the required fields are missing.
20+
21+
// UnmarshalJSON unmarshals InsertReplaceEdit with extra
22+
// checks on the presence of "insert" and "replace" properties.
23+
func (e *InsertReplaceEdit) UnmarshalJSON(data []byte) error {
24+
var required struct {
25+
NewText string
26+
Insert *Range `json:"insert,omitempty"`
27+
Replace *Range `json:"replace,omitempty"`
28+
}
29+
30+
if err := json.Unmarshal(data, &required); err != nil {
31+
return err
32+
}
33+
if required.Insert == nil && required.Replace == nil {
34+
return fmt.Errorf("not InsertReplaceEdit")
35+
}
36+
e.NewText = required.NewText
37+
e.Insert = *required.Insert
38+
e.Replace = *required.Replace
39+
return nil
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright 2024 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 protocol
6+
7+
import (
8+
"encoding/json"
9+
"testing"
10+
11+
"github.com/google/go-cmp/cmp"
12+
)
13+
14+
func TestInsertReplaceEdit_UnmarshalJSON(t *testing.T) {
15+
tests := []struct {
16+
name string
17+
in any
18+
wantErr bool
19+
}{
20+
{
21+
name: "TextEdit",
22+
in: TextEdit{NewText: "new text", Range: Range{Start: Position{Line: 1}}},
23+
},
24+
{
25+
name: "InsertReplaceEdit",
26+
in: InsertReplaceEdit{NewText: "new text", Insert: Range{Start: Position{Line: 100}}, Replace: Range{End: Position{Line: 200}}},
27+
},
28+
}
29+
for _, tt := range tests {
30+
t.Run(tt.name, func(t *testing.T) {
31+
data, err := json.MarshalIndent(Or_CompletionItem_textEdit{Value: tt.in}, "", " ")
32+
if err != nil {
33+
t.Fatalf("failed to marshal: %v", err)
34+
}
35+
var decoded Or_CompletionItem_textEdit
36+
if err := json.Unmarshal(data, &decoded); err != nil {
37+
t.Fatalf("failed to unmarshal: %v", err)
38+
}
39+
if diff := cmp.Diff(tt.in, decoded.Value); diff != "" {
40+
t.Errorf("unmarshal returns unexpected result: (-want +got):\n%s", diff)
41+
}
42+
})
43+
}
44+
}

0 commit comments

Comments
 (0)