1use crate::{ChangedSpan, Hunk, LineOrigin};
2
3impl Hunk {
4 pub fn changed_spans(&self) -> Vec<Option<ChangedSpan>> {
7 let mut spans = vec![None; self.lines.len()];
8 let mut position = 0;
9 while position < self.lines.len() {
10 if self.lines[position].origin != LineOrigin::Deletion {
11 position += 1;
12 continue;
13 }
14 let deleted_start = position;
15 while position < self.lines.len() && self.lines[position].origin == LineOrigin::Deletion
16 {
17 position += 1;
18 }
19 let added_start = position;
20 while position < self.lines.len() && self.lines[position].origin == LineOrigin::Addition
21 {
22 position += 1;
23 }
24 let pairs = (added_start - deleted_start).min(position - added_start);
25 for offset in 0..pairs {
26 let old_index = deleted_start + offset;
27 let new_index = added_start + offset;
28 let (old, new) =
29 changed_pair(&self.lines[old_index].text, &self.lines[new_index].text);
30 spans[old_index] = (!old.is_empty()).then_some(ChangedSpan(old));
31 spans[new_index] = (!new.is_empty()).then_some(ChangedSpan(new));
32 }
33 }
34 spans
35 }
36}
37
38fn changed_pair(old: &str, new: &str) -> (std::ops::Range<usize>, std::ops::Range<usize>) {
39 let prefix: usize = old
40 .chars()
41 .zip(new.chars())
42 .take_while(|(a, b)| a == b)
43 .map(|(character, _)| character.len_utf8())
44 .sum();
45 let suffix: usize = old[prefix..]
48 .chars()
49 .rev()
50 .zip(new[prefix..].chars().rev())
51 .take_while(|(a, b)| a == b)
52 .map(|(character, _)| character.len_utf8())
53 .sum();
54 (prefix..old.len() - suffix, prefix..new.len() - suffix)
55}