@@ -77,6 +77,10 @@ pub(super) mod _impl {
77
77
78
78
use super :: { ConsumeHunk , ContextSize , NewlineSeparator } ;
79
79
80
+ const CONTEXT : char = ' ' ;
81
+ const ADDITION : char = '+' ;
82
+ const REMOVAL : char = '-' ;
83
+
80
84
/// A [`Sink`] that creates a textual diff in the format typically output by git or `gnu-diff` if the `-u` option is used,
81
85
/// and passes it in full to a consumer.
82
86
pub struct UnifiedDiff < ' a , T , D >
@@ -88,18 +92,25 @@ pub(super) mod _impl {
88
92
after : & ' a [ Token ] ,
89
93
interner : & ' a Interner < T > ,
90
94
91
- pos : u32 ,
95
+ /// The 0-based start position in the 'before' tokens for the accumulated hunk for display in the header.
92
96
before_hunk_start : u32 ,
93
- after_hunk_start : u32 ,
97
+ /// The size of the accumulated 'before' hunk in lines for display in the header.
94
98
before_hunk_len : u32 ,
99
+ /// The 0-based start position in the 'after' tokens for the accumulated hunk for display in the header.
100
+ after_hunk_start : u32 ,
101
+ /// The size of the accumulated 'after' hunk in lines.
95
102
after_hunk_len : u32 ,
103
+ // An index into `before` and the context line to print next,
104
+ // or `None` if this value was never computed to be the correct starting point for an accumulated hunk.
105
+ ctx_pos : Option < u32 > ,
106
+
96
107
/// Symmetrical context before and after the changed hunk.
97
108
ctx_size : u32 ,
109
+ newline : NewlineSeparator < ' a > ,
98
110
99
111
buffer : Vec < u8 > ,
100
112
header_buf : String ,
101
113
delegate : D ,
102
- newline : NewlineSeparator < ' a > ,
103
114
104
115
err : Option < std:: io:: Error > ,
105
116
}
@@ -122,19 +133,22 @@ pub(super) mod _impl {
122
133
context_size : ContextSize ,
123
134
) -> Self {
124
135
Self {
136
+ interner : & input. interner ,
137
+ before : & input. before ,
138
+ after : & input. after ,
139
+
125
140
before_hunk_start : 0 ,
126
- after_hunk_start : 0 ,
127
141
before_hunk_len : 0 ,
128
142
after_hunk_len : 0 ,
143
+ after_hunk_start : 0 ,
144
+ ctx_pos : None ,
145
+
146
+ ctx_size : context_size. symmetrical ,
147
+ newline : newline_separator,
148
+
129
149
buffer : Vec :: with_capacity ( 8 ) ,
130
150
header_buf : String :: new ( ) ,
131
151
delegate : consume_hunk,
132
- interner : & input. interner ,
133
- before : & input. before ,
134
- after : & input. after ,
135
- pos : 0 ,
136
- ctx_size : context_size. symmetrical ,
137
- newline : newline_separator,
138
152
139
153
err : None ,
140
154
}
@@ -158,23 +172,25 @@ pub(super) mod _impl {
158
172
}
159
173
}
160
174
161
- fn flush ( & mut self ) -> std:: io:: Result < ( ) > {
162
- if self . before_hunk_len == 0 && self . after_hunk_len == 0 {
175
+ fn flush_accumulated_hunk ( & mut self ) -> std:: io:: Result < ( ) > {
176
+ if self . nothing_to_flush ( ) {
163
177
return Ok ( ( ) ) ;
164
178
}
165
179
166
- let end = ( self . pos + self . ctx_size ) . min ( self . before . len ( ) as u32 ) ;
167
- self . update_pos ( end, end) ;
180
+ let ctx_pos = self . ctx_pos . expect ( "has been set if we started a hunk" ) ;
181
+ let end = ( ctx_pos + self . ctx_size ) . min ( self . before . len ( ) as u32 ) ;
182
+ self . print_context_and_update_pos ( ctx_pos..end, end) ;
168
183
184
+ let hunk_start = self . before_hunk_start + 1 ;
185
+ let hunk_end = self . after_hunk_start + 1 ;
169
186
self . header_buf . clear ( ) ;
170
-
171
187
std:: fmt:: Write :: write_fmt (
172
188
& mut self . header_buf ,
173
189
format_args ! (
174
190
"@@ -{},{} +{},{} @@{nl}" ,
175
- self . before_hunk_start + 1 ,
191
+ hunk_start ,
176
192
self . before_hunk_len,
177
- self . after_hunk_start + 1 ,
193
+ hunk_end ,
178
194
self . after_hunk_len,
179
195
nl = match self . newline {
180
196
NewlineSeparator :: AfterHeaderAndLine ( nl) | NewlineSeparator :: AfterHeaderAndWhenNeeded ( nl) => {
@@ -185,26 +201,35 @@ pub(super) mod _impl {
185
201
)
186
202
. map_err ( |err| std:: io:: Error :: new ( ErrorKind :: Other , err) ) ?;
187
203
self . delegate . consume_hunk (
188
- self . before_hunk_start + 1 ,
204
+ hunk_start ,
189
205
self . before_hunk_len ,
190
- self . after_hunk_start + 1 ,
206
+ hunk_end ,
191
207
self . after_hunk_len ,
192
208
& self . header_buf ,
193
209
& self . buffer ,
194
210
) ?;
195
- self . buffer . clear ( ) ;
196
- self . before_hunk_len = 0 ;
197
- self . after_hunk_len = 0 ;
211
+
212
+ self . reset_hunks ( ) ;
198
213
Ok ( ( ) )
199
214
}
200
215
201
- fn update_pos ( & mut self , print_to : u32 , move_to : u32 ) {
202
- self . print_tokens ( & self . before [ self . pos as usize ..print_to as usize ] , ' ' ) ;
203
- let len = print_to - self . pos ;
204
- self . pos = move_to;
216
+ fn print_context_and_update_pos ( & mut self , print : Range < u32 > , move_to : u32 ) {
217
+ self . print_tokens ( & self . before [ print . start as usize ..print . end as usize ] , CONTEXT ) ;
218
+ let len = print . end - print . start ;
219
+ self . ctx_pos = Some ( move_to) ;
205
220
self . before_hunk_len += len;
206
221
self . after_hunk_len += len;
207
222
}
223
+
224
+ fn reset_hunks ( & mut self ) {
225
+ self . buffer . clear ( ) ;
226
+ self . before_hunk_len = 0 ;
227
+ self . after_hunk_len = 0 ;
228
+ }
229
+
230
+ fn nothing_to_flush ( & self ) -> bool {
231
+ self . before_hunk_len == 0 && self . after_hunk_len == 0
232
+ }
208
233
}
209
234
210
235
impl < T , D > Sink for UnifiedDiff < ' _ , T , D >
@@ -218,24 +243,39 @@ pub(super) mod _impl {
218
243
if self . err . is_some ( ) {
219
244
return ;
220
245
}
221
- if before. start - self . pos > 2 * self . ctx_size {
222
- if let Err ( err) = self . flush ( ) {
246
+ let start_next_hunk = self
247
+ . ctx_pos
248
+ . is_some_and ( |ctx_pos| before. start - ctx_pos > 2 * self . ctx_size ) ;
249
+ if start_next_hunk {
250
+ if let Err ( err) = self . flush_accumulated_hunk ( ) {
223
251
self . err = Some ( err) ;
224
252
return ;
225
253
}
226
- self . pos = before. start - self . ctx_size ;
227
- self . before_hunk_start = self . pos ;
254
+ let ctx_pos = before. start - self . ctx_size ;
255
+ self . ctx_pos = Some ( ctx_pos) ;
256
+ self . before_hunk_start = ctx_pos;
228
257
self . after_hunk_start = after. start - self . ctx_size ;
229
258
}
230
- self . update_pos ( before. start , before. end ) ;
259
+ let ctx_pos = match self . ctx_pos {
260
+ None => {
261
+ // TODO: can this be made so the code above does the job?
262
+ let ctx_pos = before. start . saturating_sub ( self . ctx_size ) ;
263
+ self . before_hunk_start = ctx_pos;
264
+ self . after_hunk_start = after. start . saturating_sub ( self . ctx_size ) ;
265
+ ctx_pos
266
+ }
267
+ Some ( pos) => pos,
268
+ } ;
269
+ self . print_context_and_update_pos ( ctx_pos..before. start , before. end ) ;
231
270
self . before_hunk_len += before. end - before. start ;
232
271
self . after_hunk_len += after. end - after. start ;
233
- self . print_tokens ( & self . before [ before. start as usize ..before. end as usize ] , '-' ) ;
234
- self . print_tokens ( & self . after [ after. start as usize ..after. end as usize ] , '+' ) ;
272
+
273
+ self . print_tokens ( & self . before [ before. start as usize ..before. end as usize ] , REMOVAL ) ;
274
+ self . print_tokens ( & self . after [ after. start as usize ..after. end as usize ] , ADDITION ) ;
235
275
}
236
276
237
277
fn finish ( mut self ) -> Self :: Out {
238
- if let Err ( err) = self . flush ( ) {
278
+ if let Err ( err) = self . flush_accumulated_hunk ( ) {
239
279
self . err = Some ( err) ;
240
280
}
241
281
if let Some ( err) = self . err {
0 commit comments