7
7
package langserver
8
8
9
9
import (
10
+ "slices"
10
11
"strings"
11
12
)
12
13
13
- // OpKind is used to denote the type of operation a line represents.
14
- type OpKind int
15
-
16
- const (
17
- // Delete is the operation kind for a line that is present in the input
18
- // but not in the output.
19
- Delete OpKind = iota
20
- // Insert is the operation kind for a line that is new in the output.
21
- Insert
22
- // Equal is the operation kind for a line that is the same in the input and
23
- // output, often used to provide context around edited lines.
24
- Equal
25
- )
26
-
27
14
// Sources:
28
15
// https://blog.jcoglan.com/2017/02/17/the-myers-diff-algorithm-part-3/
29
16
// https://www.codeproject.com/Articles/42279/%2FArticles%2F42279%2FInvestigating-Myers-diff-algorithm-Part-1-of-2
30
17
31
- // ComputeEdits computes diff edits from 2 string inputs
18
+ // ComputeEdits returns the diffs of two strings using a simple
19
+ // line-based implementation, like [diff.Strings].
32
20
func ComputeEdits (_ DocumentURI , before , after string ) []TextEdit {
33
21
ops := operations (splitLines (before ), splitLines (after ))
34
22
edits := make ([]TextEdit , 0 , len (ops ))
23
+
24
+ // If there're some insertions on the same line, they must be reversed to be applied in order.
25
+ // So memorize the last insertion line and its index.
26
+ var insHankLine int
27
+ var insHankIndex int
35
28
for _ , op := range ops {
36
29
switch op .Kind {
37
- case Delete :
30
+ case opDelete :
38
31
// Delete: unformatted[i1:i2] is deleted.
39
32
edits = append (edits , TextEdit {Range : Range {
40
33
Start : Position {Line : op .I1 , Character : 0 },
41
34
End : Position {Line : op .I2 , Character : 0 },
42
35
}})
43
- case Insert :
36
+ insHankLine = - 1 // Reset insertion hanking
37
+ case opInsert :
44
38
// Insert: formatted[j1:j2] is inserted at unformatted[i1:i1].
45
39
if content := strings .Join (op .Content , "" ); content != "" {
46
- edits = append ( edits , TextEdit {
40
+ newEdit := TextEdit {
47
41
Range : Range {
48
42
Start : Position {Line : op .I1 , Character : 0 },
49
43
End : Position {Line : op .I2 , Character : 0 },
50
44
},
51
45
NewText : content ,
52
- })
46
+ }
47
+
48
+ if insHankLine == op .I1 {
49
+ // If there're some insertion on the same line, insert it before the hank of the last insertions.
50
+ edits = slices .Insert (edits , insHankIndex , newEdit )
51
+ } else {
52
+ insHankLine = op .I1
53
+ insHankIndex = len (edits )
54
+ edits = append (edits , newEdit )
55
+ }
53
56
}
54
57
}
55
58
}
56
59
return edits
57
60
}
58
61
62
+ // opKind is used to denote the type of operation a line represents.
63
+ type opKind int
64
+
65
+ const (
66
+ opDelete opKind = iota // line deleted from input (-)
67
+ opInsert // line inserted into output (+)
68
+ opEqual // line present in input and output
69
+ )
70
+
71
+ func (kind opKind ) String () string {
72
+ switch kind {
73
+ case opDelete :
74
+ return "delete"
75
+ case opInsert :
76
+ return "insert"
77
+ case opEqual :
78
+ return "equal"
79
+ default :
80
+ panic ("unknown opKind" )
81
+ }
82
+ }
83
+
59
84
type operation struct {
60
- Kind OpKind
85
+ Kind opKind
61
86
Content []string // content from b
62
87
I1 , I2 int // indices of the line in a
63
88
J1 int // indices of the line in b, J2 implied by len(Content)
@@ -83,7 +108,7 @@ func operations(a, b []string) []*operation {
83
108
return
84
109
}
85
110
op .I2 = i2
86
- if op .Kind == Insert {
111
+ if op .Kind == opInsert {
87
112
op .Content = b [op .J1 :j2 ]
88
113
}
89
114
solution [i ] = op
@@ -99,7 +124,7 @@ func operations(a, b []string) []*operation {
99
124
for snake [0 ]- snake [1 ] > x - y {
100
125
if op == nil {
101
126
op = & operation {
102
- Kind : Delete ,
127
+ Kind : opDelete ,
103
128
I1 : x ,
104
129
J1 : y ,
105
130
}
@@ -115,7 +140,7 @@ func operations(a, b []string) []*operation {
115
140
for snake [0 ]- snake [1 ] < x - y {
116
141
if op == nil {
117
142
op = & operation {
118
- Kind : Insert ,
143
+ Kind : opInsert ,
119
144
I1 : x ,
120
145
J1 : y ,
121
146
}
0 commit comments