@@ -95,51 +95,11 @@ public struct CodeDiff {
9595 let oldLines = oldSnippet. splitByNewLine ( omittingEmptySubsequences: false )
9696 let diffByLine = newLines. difference ( from: oldLines)
9797
98- struct DiffSection : Equatable {
99- var offset : Int
100- var end : Int
101- var lines : [ String ]
102- }
103-
104- func collect(
105- into all: inout [ DiffSection ] ,
106- changes: [ CollectionDifference < Substring > . Change ] ,
107- extract: ( CollectionDifference < Substring > . Change ) -> ( offset: Int , line: Substring ) ?
108- ) {
109- var current : DiffSection ?
110- for change in changes {
111- guard let ( offset, element) = extract ( change) else { continue }
112- if var section = current {
113- if offset == section. end + 1 {
114- section. lines. append ( String ( element) )
115- section. end = offset
116- current = section
117- continue
118- } else {
119- all. append ( section)
120- }
121- }
122-
123- current = DiffSection ( offset: offset, end: offset, lines: [ String ( element) ] )
124- }
125-
126- if let current {
127- all. append ( current)
128- }
129- }
130-
131- var insertions = [ DiffSection] ( )
132- var removals = [ DiffSection] ( )
133-
134- collect ( into: & removals, changes: diffByLine. removals) { change in
135- guard case let . remove( offset, element, _) = change else { return nil }
136- return ( offset, element)
137- }
138-
139- collect ( into: & insertions, changes: diffByLine. insertions) { change in
140- guard case let . insert( offset, element, _) = change else { return nil }
141- return ( offset, element)
142- }
98+ let ( insertions, removals) = generateDiffSections (
99+ oldLines: oldLines,
100+ newLines: newLines,
101+ diffByLine: diffByLine
102+ )
143103
144104 var oldLineIndex = 0
145105 var newLineIndex = 0
@@ -217,6 +177,133 @@ public struct CodeDiff {
217177 }
218178}
219179
180+ extension CodeDiff {
181+ struct DiffSection : Equatable {
182+ var offset : Int
183+ var end : Int
184+ var lines : [ String ]
185+
186+ mutating func appendIfPossible( offset: Int , element: Substring ) -> Bool {
187+ if end + 1 != offset { return false }
188+ end = offset
189+ lines. append ( String ( element) )
190+ return true
191+ }
192+ }
193+
194+ func generateDiffSections(
195+ oldLines: [ Substring ] ,
196+ newLines: [ Substring ] ,
197+ diffByLine: CollectionDifference < Substring >
198+ ) -> ( insertionSections: [ DiffSection ] , removalSections: [ DiffSection ] ) {
199+ let insertionDiffs = diffByLine. insertions
200+ let removalDiffs = diffByLine. removals
201+ var insertions = [ DiffSection] ( )
202+ var removals = [ DiffSection] ( )
203+ var insertionIndex = 0
204+ var removalIndex = 0
205+ var insertionUnchangedGap = 0
206+ var removalUnchangedGap = 0
207+
208+ while insertionIndex < insertionDiffs. endIndex || removalIndex < removalDiffs. endIndex {
209+ let insertion = insertionDiffs [ safe: insertionIndex]
210+ let removal = removalDiffs [ safe: removalIndex]
211+
212+ append (
213+ into: & insertions,
214+ change: insertion,
215+ index: & insertionIndex,
216+ unchangedGap: & insertionUnchangedGap
217+ ) { change in
218+ guard case let . insert( offset, element, _) = change else { return nil }
219+ return ( offset, element)
220+ }
221+
222+ append (
223+ into: & removals,
224+ change: removal,
225+ index: & removalIndex,
226+ unchangedGap: & removalUnchangedGap
227+ ) { change in
228+ guard case let . remove( offset, element, _) = change else { return nil }
229+ return ( offset, element)
230+ }
231+
232+ if insertionUnchangedGap > removalUnchangedGap {
233+ // insert empty sections to insertions
234+ if removalUnchangedGap > 0 {
235+ let count = insertionUnchangedGap - removalUnchangedGap
236+ let index = max ( insertions. endIndex - 1 , 0 )
237+ let offset = ( insertions. last? . offset ?? 0 ) - count
238+ insertions. insert (
239+ . init( offset: offset, end: offset, lines: [ ] ) ,
240+ at: index
241+ )
242+ insertionUnchangedGap -= removalUnchangedGap
243+ removalUnchangedGap = 0
244+ } else if removal == nil {
245+ removalUnchangedGap = 0
246+ insertionUnchangedGap = 0
247+ }
248+ } else if removalUnchangedGap > insertionUnchangedGap {
249+ // insert empty sections to removals
250+ if insertionUnchangedGap > 0 {
251+ let count = removalUnchangedGap - insertionUnchangedGap
252+ let index = max ( removals. endIndex - 1 , 0 )
253+ let offset = ( removals. last? . offset ?? 0 ) - count
254+ removals. insert (
255+ . init( offset: offset, end: offset, lines: [ ] ) ,
256+ at: index
257+ )
258+ removalUnchangedGap -= insertionUnchangedGap
259+ insertionUnchangedGap = 0
260+ } else {
261+ removalUnchangedGap = 0
262+ insertionUnchangedGap = 0
263+ }
264+ } else {
265+ removalUnchangedGap = 0
266+ insertionUnchangedGap = 0
267+ }
268+ }
269+
270+ return ( insertions, removals)
271+ }
272+
273+ func append(
274+ into sections: inout [ DiffSection ] ,
275+ change: CollectionDifference < Substring > . Change ? ,
276+ index: inout Int ,
277+ unchangedGap: inout Int ,
278+ extract: ( CollectionDifference < Substring > . Change ) -> ( offset: Int , line: Substring ) ?
279+ ) {
280+ guard let change, let ( offset, element) = extract ( change) else { return }
281+ if unchangedGap == 0 {
282+ if !sections. isEmpty {
283+ let lastIndex = sections. endIndex - 1
284+ if !sections[ lastIndex]
285+ . appendIfPossible ( offset: offset, element: element)
286+ {
287+ unchangedGap = offset - sections[ lastIndex] . end - 1
288+ sections. append ( . init(
289+ offset: offset,
290+ end: offset,
291+ lines: [ String ( element) ]
292+ ) )
293+ }
294+ } else {
295+ sections. append ( . init(
296+ offset: offset,
297+ end: offset,
298+ lines: [ String ( element) ]
299+ ) )
300+ unchangedGap = offset
301+ }
302+ index += 1
303+ }
304+ }
305+ }
306+
220307extension Array {
221308 subscript( safe index: Int ) -> Element ? {
222309 guard index >= 0 , index < count else { return nil }
0 commit comments