libpijul_compat/optimal_diff/add.rs
1use super::*;
2use backend::*;
3
4impl<A: Transaction, R: rand::Rng> GenericTxn<A, R> {
5 fn add_lines_up_context(&self, line_index: usize, diff: &Diff<A>) -> Vec<Key<Option<PatchId>>> {
6 if diff.lines_a[line_index].is_root() {
7 let mut up_context = Vec::new();
8 let status = *diff.status.get(&line_index).unwrap();
9 if status < Status::End {
10 // If the up context is the beginning of a conflict,
11 // or a separator, the actual up context is the last
12 // line before the conflict (this conflict might be
13 // nested, which is why we need a loop to get up to
14 // that line).
15 let i = diff.conflicts_ancestors.get(&line_index).unwrap();
16
17 // Now, maybe that line was inserted by the patch
18 // we're preparing.
19 if let Some(&(line, _)) = diff.conflicts_down_contexts.get(i) {
20 // If this is case, use that line as the up context.
21 up_context.push(Key { patch: None, line })
22 } else {
23 // Else, the up context is the first non-marker
24 // line in `diff.lines_a` before `i`. What if it's
25 // been deleted?
26 let mut i = *i;
27 while i > 0 && diff.lines_a[i].is_root() {
28 i -= 1
29 }
30 let up = diff.lines_a[i].to_owned();
31 up_context.push(Key {
32 patch: Some(up.patch),
33 line: up.line,
34 })
35 }
36 } else {
37 // End of a conflict.
38
39 let i0 = *diff.conflicts_ancestors.get(&line_index).unwrap();
40 // In case we're in an embedded conflict, get up to the
41 // line before the first conflict marker.
42 let mut i = line_index;
43 debug!("add_lines_up_context, i0 = {:?}", i0);
44
45 while i > i0 {
46 debug!(
47 "i = {:?} a[i] = {:?}, a[i-1] = {:?}",
48 i,
49 diff.lines_a[i],
50 diff.lines_a[i - 1]
51 );
52 if diff.lines_a[i].is_root() && !diff.lines_a[i - 1].is_root() {
53 // If we're still at a conflict marker
54 let up = diff.lines_a[i - 1].to_owned();
55 up_context.push(Key {
56 patch: Some(up.patch),
57 line: up.line,
58 })
59 }
60 if let Some(&(line, _)) = diff.conflicts_down_contexts.get(&i) {
61 up_context.push(Key { patch: None, line })
62 }
63 i -= 1
64 }
65 }
66 up_context
67 } else {
68 // Here, since additions can only "pause" if there is a
69 // conflict, and this is not a conflict, there's no need
70 // to check for down contexts.
71 //
72 // Note: maybe some future diff algorithms might require
73 // this case to check `diff.conflicts_down_contexts`.
74 let up_context = diff.lines_a[line_index];
75 vec![Key {
76 patch: Some(up_context.patch),
77 line: up_context.line,
78 }]
79 }
80 }
81
82 fn add_lines_down_context_trailing_equals(
83 &self,
84 line_index: usize,
85 line_id: LineId,
86 diff: &mut Diff<A>,
87 trailing_equals: usize,
88 ) -> Rc<RefCell<Vec<Key<Option<PatchId>>>>> {
89 let status = *diff.status.get(&line_index).unwrap();
90 let ancestor = *diff.conflicts_ancestors.get(&line_index).unwrap();
91 match status {
92 Status::Begin if line_index >= diff.lines_a.len() - trailing_equals => {
93 // If the first line of the "trailing equals" is a
94 // conflict marker, we already know that nothing
95 // below will change. The down context lines can
96 // already be produced, and moreover will not be
97 // produced later on, as the algorithm stops here.
98 //
99 // down_context = each side of the conflict. We
100 // need the `ConflictSidesIter` iterator because
101 // this conflict might be nested.
102 Rc::new(RefCell::new(
103 ConflictSidesIter {
104 diff,
105 started: false,
106 current: ancestor,
107 level: 0,
108 }.filter_map(|side| {
109 let k = diff.lines_a[side + 1];
110 if k.is_root() {
111 None
112 } else {
113 Some(Key {
114 patch: Some(k.patch),
115 line: k.line,
116 })
117 }
118 })
119 .collect(),
120 ))
121 }
122 _ => {
123 // Here, we're inserting a line just before a conflict
124 // marker or separator, where the end conflict marker
125 // (which might or might not be the current line) is
126 // in the trailing equals part of the file.
127 //
128 // Therefore, nothing will happen after this function
129 // returns, and this is our last chance to add the
130 // down context for this addition.
131 let mut descendant = *diff.conflicts_descendants.get(&ancestor).unwrap();
132 let down = if descendant >= diff.lines_a.len() - trailing_equals {
133 // down_context = next line after the conflict
134 if descendant + 1 < diff.lines_a.len() {
135 if diff.lines_a[descendant + 1].is_root() {
136 // If the next line after the conflict is
137 // itself a new conflict.
138 Rc::new(RefCell::new(
139 ConflictSidesIter {
140 diff,
141 started: false,
142 current: descendant + 1,
143 level: 0,
144 }.filter_map(|side| {
145 let k = diff.lines_a[side + 1];
146 if k.is_root() {
147 None
148 } else {
149 Some(Key {
150 patch: Some(k.patch),
151 line: k.line,
152 })
153 }
154 })
155 .collect(),
156 ))
157 } else {
158 // If there is a line after the conflict, but
159 // it is not a new conflict.
160 let k = diff.lines_a[descendant + 1];
161 Rc::new(RefCell::new(vec![Key {
162 patch: Some(k.patch),
163 line: k.line,
164 }]))
165 }
166 } else {
167 // If there is no line after the conflict.
168 Rc::new(RefCell::new(Vec::new()))
169 }
170 } else {
171 Rc::new(RefCell::new(Vec::new()))
172 };
173 debug!("inserting down contexts {:?} {:?}", line_index, line_id);
174 diff.conflicts_down_contexts
175 .insert(line_index, (line_id, down.clone()));
176 down
177 }
178 }
179 }
180
181 fn add_lines_down_context(
182 &self,
183 line_index: usize,
184 line_id: LineId,
185 diff: &mut Diff<A>,
186 trailing_equals: usize,
187 ) -> Rc<RefCell<Vec<Key<Option<PatchId>>>>> {
188 if diff.lines_a[line_index].is_root() {
189 debug!(
190 "line_index {:?}, len {:?}, trailing_equals {:?}",
191 line_index,
192 diff.lines_a.len(),
193 trailing_equals
194 );
195 self.add_lines_down_context_trailing_equals(line_index, line_id, diff, trailing_equals)
196 } else {
197 let down_context = diff.lines_a[line_index];
198 Rc::new(RefCell::new(vec![Key {
199 patch: Some(down_context.patch),
200 line: down_context.line.clone(),
201 }]))
202 }
203 }
204
205 pub(crate) fn add_lines(
206 &self,
207 up_line_index: usize,
208 down_line_index: Option<usize>,
209 diff: &mut Diff<A>,
210 lines: &[&[u8]],
211 trailing_equals: usize,
212 ) -> patch::Change<Rc<RefCell<ChangeContext<PatchId>>>> {
213 debug!("add_lines: {:?}", lines);
214 let up_context = self.add_lines_up_context(up_line_index, diff);
215 let down_context = if let Some(down) = down_line_index {
216 self.add_lines_down_context(
217 down,
218 *diff.line_num + (lines.len() - 1),
219 diff,
220 trailing_equals,
221 )
222 } else {
223 Rc::new(RefCell::new(Vec::new()))
224 };
225 debug!("adding lines {}", lines.len());
226 let changes = Change::NewNodes {
227 up_context: Rc::new(RefCell::new(up_context)),
228 down_context,
229 line_num: diff.line_num.clone(),
230 flag: EdgeFlags::empty(),
231 nodes: lines.iter().map(|x| x.to_vec()).collect(),
232 inode: diff.inode.clone(),
233 };
234 *diff.line_num += lines.len();
235 changes
236 }
237}
238
239/// Iterator over the first lines of sides of a conflict. This is
240/// non-trivial because conflicts can be nested. In such a case, this
241/// iterator returns the first lines of all sides of nested conflicts.
242struct ConflictSidesIter<'c, 'a: 'c, 'b: 'c, T: 'a> {
243 level: usize,
244 started: bool,
245 current: usize,
246 diff: &'c Diff<'a, 'b, T>,
247}
248
249impl<'c, 'a: 'c, 'b: 'c, T: 'a> Iterator for ConflictSidesIter<'c, 'a, 'b, T> {
250 type Item = usize;
251 fn next(&mut self) -> Option<Self::Item> {
252 loop {
253 if (self.level == 0 && self.started) || self.current >= self.diff.lines_a.len() {
254 return None;
255 } else {
256 self.started = true;
257 if self.diff.lines_a[self.current].is_root() {
258 let status = *self.diff.status.get(&self.current).unwrap();
259 if status == Status::Begin {
260 self.level += 1
261 }
262 self.current += 1;
263 if status == Status::End {
264 self.level -= 1;
265 } else {
266 return Some(self.current - 1);
267 }
268 } else {
269 self.current += 1;
270 }
271 }
272 }
273 }
274}