1515package report
1616
1717import (
18+ "slices"
1819 "sort"
1920 "strings"
2021
@@ -56,40 +57,44 @@ func (h hunk) bold(ss *styleSheet) string {
5657}
5758
5859// hunkDiff computes edit hunks for a diff.
59- func hunkDiff (span Span , edits []Edit ) []hunk {
60+ func hunkDiff (span Span , edits []Edit ) ( Span , []hunk ) {
6061 out := make ([]hunk , 0 , len (edits )* 3 + 1 )
6162 var prev int
62- src , offsets := offsetsForDiffing (span , edits )
63- for i , edit := range edits {
64- start := offsets [i ][0 ]
65- end := offsets [i ][1 ]
66-
63+ span , edits = offsetsForDiffing (span , edits )
64+ src := span .Text ()
65+ for _ , edit := range edits {
6766 out = append (out ,
68- hunk {hunkUnchanged , src [prev :start ]},
69- hunk {hunkDelete , src [start : end ]},
67+ hunk {hunkUnchanged , src [prev :edit . Start ]},
68+ hunk {hunkDelete , src [edit . Start : edit . End ]},
7069 hunk {hunkAdd , edit .Replace },
7170 )
72- prev = end
71+ prev = edit . End
7372 }
74- return append (out , hunk {hunkUnchanged , src [prev :]})
73+ return span , append (out , hunk {hunkUnchanged , src [prev :]})
7574}
7675
7776// unifiedDiff computes whole-line hunks for this diff, for producing a unified
7877// edit.
7978//
8079// Each slice will contain one or more lines that should be displayed together.
81- func unifiedDiff (span Span , edits []Edit ) []hunk {
80+ func unifiedDiff (span Span , edits []Edit ) ( Span , []hunk ) {
8281 // Sort the edits such that they are ordered by starting offset.
83- src , offsets := offsetsForDiffing (span , edits )
82+ span , edits = offsetsForDiffing (span , edits )
83+ src := span .Text ()
8484 sort .Slice (edits , func (i , j int ) bool {
85- return offsets [i ][ 0 ] < offsets [j ][ 0 ]
85+ return edits [i ]. Start < edits [j ]. End
8686 })
8787
8888 // Partition offsets into overlapping lines. That is, this connects together
8989 // all edit spans whose end and start are not separated by a newline.
90- prev := & offsets [0 ]
91- parts := slicesx .Partition (offsets , func (_ , next * [2 ]int ) bool {
92- if next == prev || ! strings .Contains (src [prev [1 ]:next [0 ]], "\n " ) {
90+ prev := & edits [0 ]
91+ parts := slicesx .Partition (edits , func (_ , next * Edit ) bool {
92+ if next == prev {
93+ return false
94+ }
95+
96+ chunk := src [prev .End :next .Start ]
97+ if ! strings .Contains (chunk , "\n " ) {
9398 return false
9499 }
95100
@@ -99,12 +104,12 @@ func unifiedDiff(span Span, edits []Edit) []hunk {
99104
100105 var out []hunk
101106 var prevHunk int
102- parts (func (i int , offsets [][ 2 ] int ) bool {
107+ parts (func (_ int , edits []Edit ) bool {
103108 // First, figure out the start and end of the modified region.
104- start , end := offsets [0 ][ 0 ], offsets [0 ][ 1 ]
105- for _ , offset := range offsets [1 :] {
106- start = min (start , offset [ 0 ] )
107- end = max (end , offset [ 1 ] )
109+ start , end := edits [0 ]. Start , edits [0 ]. End
110+ for _ , edit := range edits [1 :] {
111+ start = min (start , edit . Start )
112+ end = max (end , edit . End )
108113 }
109114 // Then, snap the region to be newline delimited. This is the unedited
110115 // lines.
@@ -114,10 +119,10 @@ func unifiedDiff(span Span, edits []Edit) []hunk {
114119 // Now, apply the edits to original to produce the modified result.
115120 var buf strings.Builder
116121 prev := 0
117- for j , offset := range offsets {
118- buf .WriteString (original [prev : offset [ 0 ] ])
119- buf .WriteString (edits [ i + j ] .Replace )
120- prev = offset [ 1 ]
122+ for _ , edit := range edits {
123+ buf .WriteString (original [prev : edit . Start - start ])
124+ buf .WriteString (edit .Replace )
125+ prev = edit . End - start
121126 }
122127 buf .WriteString (original [prev :])
123128
@@ -131,20 +136,31 @@ func unifiedDiff(span Span, edits []Edit) []hunk {
131136 prevHunk = end
132137 return true
133138 })
134- return append (out , hunk {hunkUnchanged , src [prevHunk :]})
139+ return span , append (out , hunk {hunkUnchanged , src [prevHunk :]})
135140}
136141
137142// offsetsForDiffing pre-calculates information needed for diffing:
138- // the line-snapped span, and the offsetsForDiffing of each edit as indices into
139- // that span.
140- func offsetsForDiffing (span Span , edits []Edit ) (string , [][2 ]int ) {
141- start , end := adjustLineOffsets (span .File .Text (), span .Start , span .End )
142- delta := span .Start - start
143-
144- offsets := make ([][2 ]int , len (edits ))
145- for i , edit := range edits {
146- offsets [i ] = [2 ]int {edit .Start + delta , edit .End + delta }
143+ // the line-snapped span, and edits which are adjusted to conform to that
144+ // span.
145+ func offsetsForDiffing (span Span , edits []Edit ) (Span , []Edit ) {
146+ edits = slices .Clone (edits )
147+ var start , end int
148+ for i := range edits {
149+ e := & edits [i ]
150+ e .Start += span .Start
151+ e .End += span .Start
152+ if i == 0 {
153+ start , end = e .Start , e .End
154+ } else {
155+ start , end = min (e .Start , start ), max (e .End , end )
156+ }
157+ }
158+
159+ start , end = adjustLineOffsets (span .File .Text (), start , end )
160+ for i := range edits {
161+ edits [i ].Start -= start
162+ edits [i ].End -= start
147163 }
148164
149- return span .File .Text ()[ start : end ], offsets
165+ return span .File .Span ( start , end ), edits
150166}
0 commit comments