1use super::Line;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
4pub enum Algorithm {
6 #[default]
7 Myers,
8 Patience,
9 ImaraHistogram,
10}
11
12pub(super) fn diff(
13 lines_a: &[Line],
14 lines_b: &[Line],
15 algorithm: Algorithm,
16 stop_early: bool,
17) -> D {
18 let result = D {
19 r: Vec::with_capacity(lines_a.len() + lines_b.len()),
20 stop_early,
21 };
22 match algorithm {
23 Algorithm::Patience => {
24 let mut dd = diffs::Replace::new(result);
25 diffs::patience::diff(
26 &mut dd,
27 lines_a,
28 0,
29 lines_a.len(),
30 lines_b,
31 0,
32 lines_b.len(),
33 )
34 .unwrap_or(());
35 dd.into_inner()
36 }
37 Algorithm::Myers => {
38 let mut dd = diffs::Replace::new(result);
39 diffs::myers::diff(
40 &mut dd,
41 lines_a,
42 0,
43 lines_a.len(),
44 lines_b,
45 0,
46 lines_b.len(),
47 )
48 .unwrap_or(());
49 dd.into_inner()
50 }
51 Algorithm::ImaraHistogram => {
52 let source = imara_diff::intern::InternedInput::new(&Lines(lines_a), &Lines(lines_b));
53 imara_diff::diff(imara_diff::Algorithm::Histogram, &source, result)
54 }
55 }
56}
57#[derive(Debug)]
58pub struct D {
59 pub r: Vec<Replacement>,
60 pub stop_early: bool,
61}
62
63impl D {
64 pub fn len(&self) -> usize {
65 self.r.len()
66 }
67}
68
69impl std::ops::Index<usize> for D {
70 type Output = Replacement;
71 fn index(&self, i: usize) -> &Replacement {
72 self.r.index(i)
73 }
74}
75
76impl std::ops::IndexMut<usize> for D {
77 fn index_mut(&mut self, i: usize) -> &mut Replacement {
78 self.r.index_mut(i)
79 }
80}
81
82#[derive(Debug)]
83pub struct Replacement {
84 pub old: usize,
85 pub old_len: usize,
86 pub new: usize,
87 pub new_len: usize,
88 }
90
91impl diffs::Diff for D {
92 type Error = ();
93 fn delete(&mut self, old: usize, old_len: usize, new: usize) -> std::result::Result<(), ()> {
94 debug!("Diff::delete {:?} {:?} {:?}", old, old_len, new);
95 self.r.push(Replacement {
96 old,
97 old_len,
98 new,
99 new_len: 0,
100 });
102 if self.stop_early { Err(()) } else { Ok(()) }
103 }
104 fn insert(&mut self, old: usize, new: usize, new_len: usize) -> std::result::Result<(), ()> {
105 debug!("Diff::insert {:?} {:?} {:?}", old, new, new_len);
106 self.r.push(Replacement {
107 old,
108 old_len: 0,
109 new,
110 new_len,
111 });
113 if self.stop_early { Err(()) } else { Ok(()) }
114 }
115 fn replace(
116 &mut self,
117 old: usize,
118 old_len: usize,
119 new: usize,
120 new_len: usize,
121 ) -> std::result::Result<(), ()> {
122 debug!(
123 "Diff::replace {:?} {:?} {:?} {:?}",
124 old, old_len, new, new_len
125 );
126 self.r.push(Replacement {
127 old,
128 old_len,
129 new,
130 new_len,
131 });
133 if self.stop_early { Err(()) } else { Ok(()) }
134 }
135}
136
137struct Lines<'a>(&'a [Line<'a>]);
139
140impl<'a> imara_diff::intern::TokenSource for &Lines<'a> {
141 type Token = &'a Line<'a>;
142 type Tokenizer = core::slice::Iter<'a, Line<'a>>;
143
144 fn tokenize(&self) -> Self::Tokenizer {
145 self.0.iter()
146 }
147 fn estimate_tokens(&self) -> u32 {
148 self.0.len() as u32
149 }
150}
151
152impl imara_diff::Sink for D {
153 type Out = D;
154
155 fn process_change(&mut self, before: std::ops::Range<u32>, after: std::ops::Range<u32>) {
156 debug!(
157 "Process change: old:{:?}-{:?} new:{:?}-{:?}",
158 before.start, before.end, after.start, after.end
159 );
160 self.r.push(Replacement {
161 old: before.start as usize,
162 old_len: before.len(),
163 new: after.start as usize,
164 new_len: after.len(),
165 });
167 }
168
169 fn finish(self) -> Self::Out {
170 self
171 }
172}
173
174fn line_index(lines_a: &[Line], pos_bytes: usize) -> usize {
175 match lines_a.binary_search_by(|line| {
181 (line.l.as_ptr() as usize - lines_a[0].l.as_ptr() as usize).cmp(&pos_bytes)
182 }) {
183 Ok(i) => i,
184 Err(i) => i.saturating_sub(1),
185 }
186}
187
188pub struct Deleted {
189 pub replaced: bool,
190 pub next: usize,
191}
192
193impl D {
194 pub(super) fn is_deleted(&self, lines_a: &[Line], pos: usize) -> Option<Deleted> {
195 let line = line_index(lines_a, pos);
196 match self.r.binary_search_by(|repl| repl.old.cmp(&line)) {
197 Ok(i) if self.r[i].old_len > 0 => Some(Deleted {
198 replaced: self.r[i].new_len > 0,
199 next: pos + lines_a[line].l.len(),
200 }),
201 Err(0) => None,
202 Err(i) if line < self.r[i - 1].old + self.r[i - 1].old_len => Some(Deleted {
203 replaced: self.r[i - 1].new_len > 0,
204 next: pos + lines_a[line].l.len(),
205 }),
206 _ => None,
207 }
208 }
209}