summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortummychow <tummychow@users.noreply.github.com>2018-02-16 16:53:15 -0800
committertummychow <tummychow@users.noreply.github.com>2018-02-16 16:56:21 -0800
commit45ab83a57d5ffed8f58854c131a132d508341ace (patch)
treedba773d35ba154e6a7fd8f19835cdb20f0ed9348
parent9c9bc3e4bcd712ed93db05f34f28717496ca9a20 (diff)
implement hunk commutation
-rw-r--r--src/lib.rs40
1 files changed, 40 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 3507732..9157b9c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -66,3 +66,43 @@ pub fn run(config: &Config) -> Result<(), failure::Error> {
Ok(())
}
+
+fn overlap(above: &owned::Block, below: &owned::Block) -> bool {
+ !above.lines.is_empty() && !below.lines.is_empty()
+ && below.start - above.start - above.lines.len() == 0
+}
+
+fn commute(
+ first: owned::Hunk,
+ second: owned::Hunk,
+) -> Result<(bool, owned::Hunk, owned::Hunk), failure::Error> {
+ // represent hunks in content order rather than application order
+ let (first_above, above, mut below) = match (
+ first.added.start <= second.added.start,
+ first.removed.start <= second.removed.start,
+ ) {
+ (true, true) => (true, first, second),
+ (false, false) => (false, second, first),
+ _ => return Err(failure::err_msg("nonsensical hunk ordering")),
+ };
+
+ // if the hunks overlap on either side, they can't commute, so return them in original order
+ if overlap(&above.added, &below.added) || overlap(&above.removed, &below.removed) {
+ return Ok(if first_above {
+ (false, above, below)
+ } else {
+ (false, below, above)
+ });
+ }
+
+ let above_change_offset = (above.added.lines.len() as i64 - above.removed.lines.len() as i64)
+ * if first_above { -1 } else { 1 };
+ below.added.start = (below.added.start as i64 + above_change_offset) as usize;
+ below.removed.start = (below.removed.start as i64 + above_change_offset) as usize;
+
+ Ok(if first_above {
+ (true, below, above)
+ } else {
+ (true, above, below)
+ })
+}