@@ -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