From 84d789b8acbeeb0624f49b9936cbdb22f7b5f572 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 11 Nov 2021 23:28:45 -0700 Subject: [PATCH] WIP --- crates/editor/src/display_map/patch.rs | 172 ++++++++++++++++++++++++- 1 file changed, 170 insertions(+), 2 deletions(-) diff --git a/crates/editor/src/display_map/patch.rs b/crates/editor/src/display_map/patch.rs index 97af438d45..5167c537c6 100644 --- a/crates/editor/src/display_map/patch.rs +++ b/crates/editor/src/display_map/patch.rs @@ -1,12 +1,151 @@ -use std::mem; +use std::{iter::Peekable, mem, ops::Range}; type Edit = buffer::Edit; #[derive(Default, Debug, PartialEq, Eq)] struct Patch(Vec); +fn compose_edits(old_edit: &Edit, new_edit: &Edit) -> Edit { + let mut composed = old_edit.clone(); + if old_edit.new.start > new_edit.old.start { + composed.old.start -= old_edit.new.start - new_edit.old.start; + composed.new.start = new_edit.old.start; + } + if new_edit.old.end > old_edit.new.end { + composed.old.end += new_edit.old.end - old_edit.new.end; + composed.new.end = new_edit.old.end; + } + let new_edit_delta = new_edit.new.len() as i32 - new_edit.old.len() as i32; + composed.new.end = (composed.new.end as i32 + new_edit_delta) as u32; + composed +} + impl Patch { fn compose(&self, new: &Self) -> Patch { + enum EditSource { + Old, + New, + } + + let mut composed_edits = Vec::new(); + let mut old_delta = 0; + let mut new_delta = 0; + let mut old_edits = self.0.iter().cloned().peekable(); + let mut new_edits = new.0.iter().cloned().peekable(); + + fn peek_next( + old_edits: &mut Peekable>, + new_edits: &mut Peekable>, + ) -> Option { + match (old_edits.peek(), new_edits.peek()) { + (Some(old_edit), Some(new_edit)) => { + if old_edit.new.start <= new_edit.old.start { + Some(EditSource::Old) + } else { + Some(EditSource::New) + } + } + (Some(_), None) => Some(EditSource::Old), + (None, Some(_)) => Some(EditSource::New), + (None, None) => None, + } + } + + while let Some(source) = peek_next(&mut old_edits, &mut new_edits) { + let mut intermediate_end; + let mut composed_edit; + match source { + EditSource::Old => { + let edit = old_edits.next().unwrap(); + println!("pulling an old edit {:?}", edit); + old_delta += edit_delta(&edit); + intermediate_end = edit.new.end; + composed_edit = edit.clone(); + apply_delta(&mut composed_edit.new, new_delta); + println!(" composed edit: {:?}", composed_edit); + } + EditSource::New => { + let edit = new_edits.next().unwrap(); + println!("pulling a new edit {:?}", edit); + new_delta += edit_delta(&edit); + intermediate_end = edit.old.end; + composed_edit = edit.clone(); + apply_delta(&mut composed_edit.old, -old_delta); + println!(" composed edit: {:?}", composed_edit); + } + } + + while let Some(source) = peek_next(&mut old_edits, &mut new_edits) { + match source { + EditSource::Old => { + if let Some(old_edit) = old_edits.peek() { + if old_edit.new.start <= intermediate_end { + let old_edit = old_edits.next().unwrap(); + println!(" merging with an old edit {:?}", old_edit); + + if old_edit.old.start < composed_edit.old.start { + composed_edit.new.start = + composed_edit.old.start - old_edit.old.start; + composed_edit.old.start = old_edit.old.start; + } + + if old_edit.old.end > composed_edit.old.end { + println!( + " old edit end exceeds composed edit by {:?}", + old_edit.old.end - composed_edit.old.end + ); + composed_edit.new.end += + old_edit.old.end - composed_edit.old.end; + composed_edit.old.end = old_edit.old.end; + intermediate_end = old_edit.new.end; + + println!( + " composed edit after expansion, before delta {:?}", + composed_edit, + ); + } + let edit_delta = edit_delta(&old_edit); + println!(" edit delta is {}", edit_delta); + composed_edit.old.end = + (composed_edit.old.end as i32 - edit_delta) as u32; + println!(" composed edit is now {:?}", composed_edit); + old_delta += edit_delta; + continue; + } + } + break; + } + EditSource::New => { + if let Some(new_edit) = new_edits.peek() { + if new_edit.old.start <= intermediate_end { + let new_edit = new_edits.next().unwrap(); + println!(" merging with a new edit {:?}", new_edit); + if new_edit.old.end > intermediate_end { + let expansion = new_edit.old.end - intermediate_end; + composed_edit.old.end += expansion; + composed_edit.new.end += expansion; + intermediate_end = new_edit.old.end; + } + let edit_delta = edit_delta(&new_edit); + composed_edit.new.end = + (composed_edit.new.end as i32 + edit_delta) as u32; + println!(" composed edit is now {:?}", composed_edit); + new_delta += edit_delta; + continue; + } + } + break; + } + } + } + + composed_edits.push(composed_edit); + } + + Patch(composed_edits) + } + + fn compose1(&self, new: &Self) -> Patch { let mut composed = Vec::::new(); let mut old_edits = self.0.iter().cloned().peekable(); let mut old_delta = 0; @@ -106,6 +245,15 @@ impl Patch { } } +fn edit_delta(edit: &Edit) -> i32 { + edit.new.len() as i32 - edit.old.len() as i32 +} + +fn apply_delta(range: &mut Range, delta: i32) { + range.start = (range.start as i32 + delta) as u32; + range.end = (range.end as i32 + delta) as u32; +} + #[cfg(test)] mod tests { use super::*; @@ -277,6 +425,26 @@ mod tests { ); } + #[test] + fn test_compose_edits() { + assert_eq!( + compose_edits( + &Edit { + old: 3..3, + new: 3..6, + }, + &Edit { + old: 2..7, + new: 2..4, + }, + ), + Edit { + old: 2..4, + new: 2..4 + } + ); + } + #[gpui::test] fn test_two_new_edits_touching_one_old_edit() { assert_patch_composition( @@ -314,7 +482,7 @@ mod tests { } #[gpui::test(iterations = 1000)] - fn test_random(mut rng: StdRng) { + fn test_random_patch_compositions(mut rng: StdRng) { let operations = env::var("OPERATIONS") .map(|i| i.parse().expect("invalid `OPERATIONS` variable")) .unwrap_or(2);