use super::diff::*;
use super::replace::ConflictContexts;
use super::vertex_buffer::{ConflictMarker, ConflictType, Diff};
use super::{bytes_len, bytes_pos, Line};
use crate::change;
use crate::change::{Atom, EdgeMap, Hunk, Local, NewVertex};
use crate::pristine::*;
use crate::record::Builder;
impl Builder {
pub(super) fn delete<T: GraphTxnT>(
&mut self,
txn: &T,
channel: &T::Graph,
diff: &Diff,
d: &super::diff::D,
conflict_contexts: &mut ConflictContexts,
lines_a: &[Line],
lines_b: &[Line],
r: usize,
) -> Result<(), TxnErr<T::GraphError>> {
debug!("delete {:?}", r);
self.delete_lines(txn, channel, diff, d, lines_a, r)?;
let old = d[r].old;
let len = d[r].old_len;
self.order_conflict_sides(
diff,
d,
conflict_contexts,
lines_a,
lines_b,
old,
len,
d[r].new,
d[r].new_len > 0,
);
Ok(())
}
}
struct Deletion {
edges: Vec<crate::change::NewEdge<Option<ChangeId>>>,
resurrect: Vec<crate::change::NewEdge<Option<ChangeId>>>,
}
impl Builder {
fn delete_lines<T: GraphTxnT>(
&mut self,
txn: &T,
channel: &T::Graph,
diff: &Diff,
d: &super::diff::D,
lines_a: &[Line],
r: usize,
) -> Result<(), TxnErr<T::GraphError>> {
let deletion = delete_lines(txn, channel, diff, d, lines_a, r)?;
if !deletion.edges.is_empty() {
self.rec.actions.push(Hunk::Edit {
change: Atom::EdgeMap(EdgeMap {
edges: deletion.edges,
inode: diff.inode,
}),
local: Local {
line: d[r].new + 1,
path: diff.path.clone(),
},
})
}
if !deletion.resurrect.is_empty() {
self.rec.actions.push(Hunk::ResurrectZombies {
change: Atom::EdgeMap(EdgeMap {
edges: deletion.resurrect,
inode: diff.inode,
}),
local: Local {
line: d[r].new + 1,
path: diff.path.clone(),
},
})
}
Ok(())
}
}
fn delete_lines<T: GraphTxnT>(
txn: &T,
channel: &T::Graph,
diff: &Diff,
d: &super::diff::D,
lines_a: &[Line],
r: usize,
) -> Result<Deletion, TxnErr<T::GraphError>> {
let old = d[r].old;
let len = d[r].old_len;
let mut deletion = Deletion {
edges: Vec::new(),
resurrect: Vec::new(),
};
let mut pos = bytes_pos(lines_a, old);
let end_pos = pos + bytes_len(lines_a, old, len);
let first_vertex = diff.last_vertex_containing(pos);
let mut solved_conflict_end = 0;
let mut i = first_vertex;
while pos < end_pos {
debug!("pos = {:?} {:?}", diff.pos_a[i].pos, diff.pos_a[i].vertex);
let marker = diff.marker.get(&diff.pos_a[i].pos);
if marker.is_none() || (!diff.pos_a[i].vertex.is_root() && diff.pos_a[i].vertex.is_empty())
{
debug!("{:?}", diff.vertex(i, pos, end_pos));
delete_parents(
txn,
channel,
diff.pos_a[i].vertex,
diff.vertex(i, pos, end_pos),
&mut deletion,
)?
} else if let Some(ConflictMarker::Begin) = marker {
debug!(
"conflict type = {:#?}",
diff.conflict_ends[diff.pos_a[i].conflict].conflict_type
);
if let ConflictType::Zombie = diff.conflict_ends[diff.pos_a[i].conflict].conflict_type {
solved_conflict_end =
solved_conflict_end.max(diff.conflict_ends[diff.pos_a[i].conflict].end_pos)
}
} else {
debug!("conflict: {:?}", marker);
}
i += 1;
if i < diff.pos_a.len() {
pos = diff.pos_a[i].pos
} else {
break;
}
}
if solved_conflict_end > 0 && i < diff.pos_a.len() {
resurrect_zombies(
txn,
channel,
diff,
d,
lines_a,
r,
i,
end_pos,
solved_conflict_end,
&mut deletion,
)?
}
Ok(deletion)
}
fn delete_parents<T: GraphTxnT>(
txn: &T,
channel: &T::Graph,
graph_key: Vertex<ChangeId>,
del_key: Vertex<ChangeId>,
deletion: &mut Deletion,
) -> Result<(), TxnErr<T::GraphError>> {
for e in iter_adjacent(
txn,
&channel,
graph_key,
EdgeFlags::PARENT,
EdgeFlags::all(),
)? {
let e = e?;
if !e.flag.contains(EdgeFlags::PARENT) || e.flag.contains(EdgeFlags::PSEUDO) {
continue;
}
let previous = e.flag - EdgeFlags::PARENT;
if graph_key.start != del_key.start
&& !graph_key.is_empty()
&& !e.flag.contains(EdgeFlags::BLOCK)
{
continue;
}
deletion.edges.push(change::NewEdge {
previous,
flag: previous | EdgeFlags::DELETED,
from: if graph_key.start == del_key.start {
e.dest.to_option()
} else {
del_key.start_pos().to_option()
},
to: del_key.to_option(),
introduced_by: Some(e.introduced_by),
})
}
Ok(())
}
fn is_conflict_reordering(diff: &Diff, old_bytes: usize, len_bytes: usize) -> bool {
let mut result = false;
debug!("conflict reordering {:?} {:?}", old_bytes, len_bytes);
trace!("markers: {:#?}", diff.marker);
for i in old_bytes..old_bytes + len_bytes {
match diff.marker.get(&i) {
Some(&ConflictMarker::Next) => result = true,
Some(_) => return false,
_ => {}
}
}
debug!("is_conflict_reordering: {:?}", result);
result
}
impl Builder {
fn order_conflict_sides(
&mut self,
diff: &Diff,
dd: &D,
conflict_contexts: &mut ConflictContexts,
lines_a: &[Line],
lines_b: &[Line],
old: usize,
len: usize,
new: usize,
is_replaced: bool,
) {
let old_bytes = bytes_pos(lines_a, old);
let len_bytes = bytes_len(lines_a, old, len);
if !is_conflict_reordering(diff, old_bytes, len_bytes) {
return;
}
let up_context = super::replace::get_up_context(diff, conflict_contexts, lines_a, old);
self.rec.contents.push(0);
let pos = ChangePosition(self.rec.contents.len() as u64);
self.rec.contents.push(0);
let down_context = if is_replaced {
conflict_contexts.reorderings.insert(old, pos);
Vec::new()
} else {
super::replace::get_down_context(
diff,
conflict_contexts,
dd,
lines_a,
lines_b,
old,
len,
0,
0,
self.rec.contents.len(),
)
};
debug!("Conflict reordering {:?} {:?}", up_context, down_context);
self.rec.actions.push(Hunk::SolveOrderConflict {
change: Atom::NewVertex(NewVertex {
up_context,
down_context,
flag: EdgeFlags::empty(),
start: pos,
end: pos,
inode: diff.inode,
}),
local: Local {
line: new + 1,
path: diff.path.clone(),
},
});
}
}
fn resurrect_zombies<T: GraphTxnT>(
txn: &T,
channel: &T::Graph,
diff: &Diff,
d: &super::diff::D,
lines_a: &[Line],
mut r: usize,
mut i: usize,
end_pos: usize,
solved_conflict_end: usize,
deletion: &mut Deletion,
) -> Result<(), TxnErr<T::GraphError>> {
debug!(
"resurrect_zombies {:?} {:?} {:?} {:?}",
r, i, end_pos, solved_conflict_end
);
let mut pos = end_pos;
if diff.pos_a[i].pos > pos {
i -= 1;
}
while pos < solved_conflict_end {
r += 1;
while r < d.len() && d[r].old_len == 0 && bytes_pos(lines_a, d[r].old) < solved_conflict_end
{
r += 1
}
let next_pos = if r >= d.len() {
solved_conflict_end
} else {
bytes_pos(lines_a, d[r].old).min(solved_conflict_end)
};
while i < diff.pos_a.len() {
if diff.pos_a[i].pos > next_pos {
break;
}
if diff.pos_a[i].vertex.is_root()
|| (i + 1 < diff.pos_a.len() && diff.pos_a[i + 1].pos <= pos)
{
i += 1;
continue;
}
resurrect_zombie(
txn,
channel,
diff.pos_a[i].vertex,
diff.vertex(i, pos, next_pos),
deletion,
)?;
i += 1
}
if r >= d.len() {
break;
} else {
pos = bytes_pos(lines_a, d[r].old) + bytes_len(lines_a, d[r].old, d[r].old_len)
}
}
Ok(())
}
fn resurrect_zombie<T: GraphTxnT>(
txn: &T,
channel: &T::Graph,
v: Vertex<ChangeId>,
target: Vertex<ChangeId>,
deletion: &mut Deletion,
) -> Result<(), TxnErr<T::GraphError>> {
debug!("resurrect zombie {:?} {:?}", v, target);
for e in iter_adjacent(
txn,
&channel,
v,
EdgeFlags::PARENT,
EdgeFlags::PARENT | EdgeFlags::DELETED | EdgeFlags::BLOCK,
)? {
let e = e?;
if e.flag.contains(EdgeFlags::PSEUDO) || !e.flag.contains(EdgeFlags::PARENT) {
continue;
}
let previous = e.flag - EdgeFlags::PARENT;
let newedge = change::NewEdge {
previous,
flag: previous - EdgeFlags::DELETED,
from: if target.start_pos() == v.start_pos() {
e.dest.to_option()
} else {
target.start_pos().to_option()
},
to: target.to_option(),
introduced_by: Some(e.introduced_by),
};
deletion.resurrect.push(newedge)
}
Ok(())
}