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}