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