1 use super::diff::*;
2 use super::vertex_buffer::{ConflictMarker, Diff};
3 use super::{bytes_len, bytes_pos, Line};
4 use crate::change::{Atom, Hunk, LocalByte, NewVertex};
5 use crate::pristine::{ChangeId, ChangePosition, EdgeFlags, Inode, Position};
6 use crate::record::Recorded;
7 use crate::text_encoding::Encoding;
8 use crate::{HashMap, HashSet};
9 
10 pub struct ConflictContexts {
11     pub up: HashMap<usize, ChangePosition>,
12     pub side_ends: HashMap<usize, Vec<ChangePosition>>,
13     pub active: HashSet<usize>,
14     pub reorderings: HashMap<usize, ChangePosition>,
15 }
16 
17 impl ConflictContexts {
new() -> Self18     pub fn new() -> Self {
19         ConflictContexts {
20             side_ends: HashMap::default(),
21             up: HashMap::default(),
22             active: HashSet::default(),
23             reorderings: HashMap::default(),
24         }
25     }
26 }
27 
28 impl Recorded {
replace( &mut self, diff: &Diff, conflict_contexts: &mut ConflictContexts, lines_a: &[Line], lines_b: &[Line], inode: Inode, dd: &D, r: usize, encoding: &Option<Encoding>, )29     pub(super) fn replace(
30         &mut self,
31         diff: &Diff,
32         conflict_contexts: &mut ConflictContexts,
33         lines_a: &[Line],
34         lines_b: &[Line],
35         inode: Inode,
36         dd: &D,
37         r: usize,
38         encoding: &Option<Encoding>,
39     ) {
40         let old = dd[r].old;
41         let old_len = dd[r].old_len;
42         let from_new = dd[r].new;
43         let len = dd[r].new_len;
44         let up_context = get_up_context(diff, conflict_contexts, lines_a, old);
45 
46         let start = self.contents.lock().len();
47 
48         let down_context = get_down_context(
49             diff,
50             conflict_contexts,
51             dd,
52             lines_a,
53             lines_b,
54             old,
55             old_len,
56             from_new,
57             len,
58             start,
59         );
60 
61         debug!("old {:?}..{:?}", old, old + old_len);
62         trace!("old {:?}", &lines_a[old..(old + old_len)]);
63         debug!("new {:?}..{:?}", from_new, from_new + len);
64         trace!("new {:?}", &lines_b[from_new..(from_new + len)]);
65 
66         let mut contents = self.contents.lock();
67         for &line in &lines_b[from_new..(from_new + len)] {
68             contents.extend(line.l);
69         }
70         let end = contents.len();
71         if start >= end {
72             return;
73         }
74         contents.push(0);
75         std::mem::drop(contents);
76 
77         let change = NewVertex {
78             up_context,
79             down_context,
80             flag: EdgeFlags::BLOCK,
81             start: ChangePosition(start.into()),
82             end: ChangePosition(end.into()),
83             inode: diff.inode,
84         };
85         if old_len > 0 {
86             match self.actions.pop() {
87                 Some(Hunk::Edit {
88                     change: c, local, ..
89                 }) => {
90                     if local.line == from_new + 1 {
91                         self.actions.push(Hunk::Replacement {
92                             change: c,
93                             local,
94                             replacement: Atom::NewVertex(change),
95                             encoding: encoding.clone(),
96                         });
97                         return;
98                     } else {
99                         self.actions.push(Hunk::Edit {
100                             change: c,
101                             local,
102                             encoding: encoding.clone(),
103                         })
104                     }
105                 }
106                 Some(c) => self.actions.push(c),
107                 None => {}
108             }
109         }
110         self.actions.push(Hunk::Edit {
111             local: LocalByte {
112                 line: from_new + 1,
113                 path: diff.path.clone(),
114                 inode,
115                 byte: Some(bytes_pos(lines_b, from_new)),
116             },
117             change: Atom::NewVertex(change),
118             encoding: encoding.clone(),
119         });
120     }
121 }
122 
get_up_context( diff: &Diff, conflict_contexts: &mut ConflictContexts, lines_a: &[Line], old: usize, ) -> Vec<Position<Option<ChangeId>>>123 pub(super) fn get_up_context(
124     diff: &Diff,
125     conflict_contexts: &mut ConflictContexts,
126     lines_a: &[Line],
127     old: usize,
128 ) -> Vec<Position<Option<ChangeId>>> {
129     if let Some(&pos) = conflict_contexts.reorderings.get(&old) {
130         return vec![Position { change: None, pos }];
131     }
132     let old_bytes = if old == 0 {
133         return vec![diff.pos_a[0].vertex.end_pos().to_option()];
134     } else if old < lines_a.len() {
135         bytes_pos(lines_a, old)
136     } else {
137         diff.contents_a.len()
138     };
139     debug!("old_bytes {:?}", old_bytes);
140     let mut up_context_idx = diff.last_vertex_containing(old_bytes - 1);
141     let mut seen_conflict_markers = false;
142     loop {
143         debug!("up_context_idx = {:?}", up_context_idx);
144         debug!("{:?}", diff.marker.get(&diff.pos_a[up_context_idx].pos));
145         match diff.marker.get(&diff.pos_a[up_context_idx].pos) {
146             None if seen_conflict_markers => {
147                 return vec![diff.pos_a[up_context_idx].vertex.end_pos().to_option()]
148             }
149             None => {
150                 let change = diff.pos_a[up_context_idx].vertex.change;
151                 let pos = diff.pos_a[up_context_idx].vertex.start;
152                 let offset = old_bytes - diff.pos_a[up_context_idx].pos;
153                 debug!("offset {:?} {:?}", pos.0, offset);
154                 return vec![Position {
155                     change: Some(change),
156                     pos: ChangePosition(pos.0 + offset),
157                 }];
158             }
159             Some(ConflictMarker::End) => {
160                 debug!("get_up_context_conflict");
161                 return get_up_context_conflict(diff, conflict_contexts, up_context_idx);
162             }
163             _ => {
164                 let conflict = diff.pos_a[up_context_idx].conflict;
165                 debug!(
166                     "conflict = {:?} {:?}",
167                     conflict, diff.conflict_ends[conflict]
168                 );
169                 if let Some(&pos) = conflict_contexts.up.get(&conflict) {
170                     return vec![Position { change: None, pos }];
171                 }
172                 seen_conflict_markers = true;
173                 if diff.conflict_ends[conflict].start > 0 {
174                     up_context_idx = diff.conflict_ends[conflict].start - 1
175                 } else {
176                     return vec![diff.pos_a[0].vertex.end_pos().to_option()];
177                 }
178             }
179         }
180     }
181 }
get_up_context_conflict( diff: &Diff, conflict_contexts: &mut ConflictContexts, mut up_context_idx: usize, ) -> Vec<Position<Option<ChangeId>>>182 fn get_up_context_conflict(
183     diff: &Diff,
184     conflict_contexts: &mut ConflictContexts,
185     mut up_context_idx: usize,
186 ) -> Vec<Position<Option<ChangeId>>> {
187     let conflict = diff.pos_a[up_context_idx].conflict;
188     let conflict_start = diff.conflict_ends[conflict].start;
189     let mut up_context = Vec::new();
190     if let Some(ref up) = conflict_contexts.side_ends.get(&up_context_idx) {
191         up_context.extend(up.iter().map(|&pos| Position { change: None, pos }));
192     }
193     let mut on = true;
194     conflict_contexts.active.clear();
195     conflict_contexts.active.insert(conflict);
196     while up_context_idx > conflict_start {
197         match diff.marker.get(&diff.pos_a[up_context_idx].pos) {
198             None if on => {
199                 let change = diff.pos_a[up_context_idx].vertex.change;
200                 let pos = diff.pos_a[up_context_idx].vertex.end;
201                 up_context.push(Position {
202                     change: Some(change),
203                     pos,
204                 });
205                 on = false
206             }
207             Some(ConflictMarker::End) if on => {
208                 conflict_contexts
209                     .active
210                     .insert(diff.pos_a[up_context_idx].conflict);
211             }
212             Some(ConflictMarker::Next)
213                 if conflict_contexts
214                     .active
215                     .contains(&diff.pos_a[up_context_idx].conflict) =>
216             {
217                 on = true
218             }
219             _ => {}
220         }
221         up_context_idx -= 1;
222     }
223     assert!(!up_context.is_empty());
224     up_context
225 }
get_down_context( diff: &Diff, conflict_contexts: &mut ConflictContexts, dd: &D, lines_a: &[Line], lines_b: &[Line], old: usize, old_len: usize, from_new: usize, new_len: usize, contents_len: usize, ) -> Vec<Position<Option<ChangeId>>>226 pub(super) fn get_down_context(
227     diff: &Diff,
228     conflict_contexts: &mut ConflictContexts,
229     dd: &D,
230     lines_a: &[Line],
231     lines_b: &[Line],
232     old: usize,
233     old_len: usize,
234     from_new: usize,
235     new_len: usize,
236     contents_len: usize,
237 ) -> Vec<Position<Option<ChangeId>>> {
238     if old + old_len >= lines_a.len() {
239         return Vec::new();
240     }
241     let mut down_context_idx = 1;
242     let mut pos_bytes = if old + old_len == 0 {
243         0
244     } else {
245         let pos_bytes = bytes_pos(lines_a, old) + bytes_len(lines_a, old, old_len);
246         down_context_idx = diff.first_vertex_containing(pos_bytes);
247         pos_bytes
248     };
249     while down_context_idx < diff.pos_a.len() {
250         match diff.marker.get(&(diff.pos_a[down_context_idx].pos)) {
251             Some(ConflictMarker::Begin) => {
252                 return get_down_context_conflict(
253                     diff,
254                     dd,
255                     conflict_contexts,
256                     lines_a,
257                     lines_b,
258                     from_new,
259                     new_len,
260                     down_context_idx,
261                 )
262             }
263             Some(marker) => {
264                 if let ConflictMarker::Next = marker {
265                     let conflict = diff.pos_a[down_context_idx].conflict;
266                     down_context_idx = diff.conflict_ends[conflict].end;
267                 }
268                 let e = conflict_contexts
269                     .side_ends
270                     .entry(down_context_idx)
271                     .or_default();
272                 let b_len_bytes = bytes_len(lines_b, from_new, new_len);
273                 e.push(ChangePosition((contents_len + b_len_bytes).into()));
274                 down_context_idx += 1
275             }
276             None => {
277                 pos_bytes = pos_bytes.max(diff.pos_a[down_context_idx].pos);
278                 let next_vertex_pos = if down_context_idx + 1 >= diff.pos_a.len() {
279                     diff.contents_a.len()
280                 } else {
281                     diff.pos_a[down_context_idx + 1].pos
282                 };
283                 while pos_bytes < next_vertex_pos {
284                     match dd.is_deleted(lines_a, pos_bytes) {
285                         Some(Deleted { replaced: true, .. }) => return Vec::new(),
286                         Some(Deleted {
287                             replaced: false,
288                             next,
289                         }) => pos_bytes = next,
290                         None => {
291                             return vec![diff.position(down_context_idx, pos_bytes).to_option()]
292                         }
293                     }
294                 }
295                 down_context_idx += 1;
296             }
297         }
298     }
299     Vec::new()
300 }
get_down_context_conflict( diff: &Diff, dd: &D, conflict_contexts: &mut ConflictContexts, lines_a: &[Line], lines_b: &[Line], from_new: usize, new_len: usize, mut down_context_idx: usize, ) -> Vec<Position<Option<ChangeId>>>301 fn get_down_context_conflict(
302     diff: &Diff,
303     dd: &D,
304     conflict_contexts: &mut ConflictContexts,
305     lines_a: &[Line],
306     lines_b: &[Line],
307     from_new: usize,
308     new_len: usize,
309     mut down_context_idx: usize,
310 ) -> Vec<Position<Option<ChangeId>>> {
311     let conflict = diff.pos_a[down_context_idx].conflict;
312     let len_bytes = bytes_len(lines_b, from_new, new_len);
313     conflict_contexts
314         .up
315         .insert(conflict, ChangePosition(len_bytes.into()));
316     conflict_contexts.active.clear();
317     conflict_contexts.active.insert(conflict);
318     assert!(!diff.pos_a.is_empty());
319     let conflict_end = diff.conflict_ends[conflict].end.min(diff.pos_a.len() - 1);
320     let mut down_context = Vec::new();
321     let mut on = true;
322     let mut pos = diff.pos_a[down_context_idx].pos;
323     loop {
324         match diff.marker.get(&pos) {
325             None if on => match dd.is_deleted(lines_a, pos) {
326                 Some(Deleted { replaced: true, .. }) => on = false,
327                 Some(Deleted { next, .. }) => {
328                     pos = next;
329                     let next_pos = if down_context_idx + 1 < diff.pos_a.len() {
330                         diff.pos_a[down_context_idx + 1].pos
331                     } else {
332                         diff.contents_a.len()
333                     };
334                     if pos < next_pos {
335                         continue;
336                     }
337                 }
338                 None => {
339                     down_context.push(diff.position(down_context_idx, pos).to_option());
340                     on = false;
341                 }
342             },
343             Some(ConflictMarker::Begin) if on => {
344                 conflict_contexts
345                     .active
346                     .insert(diff.pos_a[down_context_idx].conflict);
347             }
348             Some(ConflictMarker::Next)
349                 if conflict_contexts
350                     .active
351                     .contains(&diff.pos_a[down_context_idx].conflict) =>
352             {
353                 on = true
354             }
355             _ => {}
356         }
357         down_context_idx += 1;
358         if down_context_idx > conflict_end {
359             break;
360         } else {
361             pos = diff.pos_a[down_context_idx].pos
362         }
363     }
364     down_context
365 }
366