diff --git a/helix-core/src/transaction.rs b/helix-core/src/transaction.rs index 7da5b7dd..c0f27abe 100644 --- a/helix-core/src/transaction.rs +++ b/helix-core/src/transaction.rs @@ -390,6 +390,10 @@ impl ChangeSet { } new_pos } + + pub fn changes_iter(&self) -> ChangeIterator { + ChangeIterator::new(self) + } } /// Transaction represents a single undoable unit of changes. Several changes can be grouped into @@ -502,6 +506,10 @@ impl Transaction { (range.head, range.head, Some(text.clone())) }) } + + pub fn changes_iter(&self) -> ChangeIterator { + self.changes.changes_iter() + } } impl From for Transaction { @@ -513,6 +521,51 @@ impl From for Transaction { } } +pub struct ChangeIterator<'a> { + iter: std::iter::Peekable>, + pos: usize, +} + +impl<'a> ChangeIterator<'a> { + fn new(changeset: &'a ChangeSet) -> Self { + let iter = changeset.changes.iter().peekable(); + Self { iter, pos: 0 } + } +} + +impl<'a> Iterator for ChangeIterator<'a> { + type Item = Change; + + fn next(&mut self) -> Option { + use Operation::*; + + loop { + match self.iter.next()? { + Retain(len) => { + self.pos += len; + } + Delete(len) => { + let start = self.pos; + self.pos += len; + return Some((start, self.pos, None)); + } + Insert(s) => { + let start = self.pos; + // a subsequent delete means a replace, consume it + if let Some(Delete(len)) = self.iter.peek() { + self.iter.next(); + + self.pos += len; + return Some((start, self.pos, Some(s.clone()))); + } else { + return Some((start, start, Some(s.clone()))); + } + } + } + } + } +} + #[cfg(test)] mod test { use super::*; @@ -626,6 +679,14 @@ mod test { assert_eq!(state.doc, Rope::from_str("hello void! 123")); } + #[test] + fn changes_iter() { + let mut state = State::new("hello world!\ntest 123".into()); + let changes = vec![(6, 11, Some("void".into())), (12, 17, None)]; + let transaction = Transaction::change(&state.doc, changes.clone().into_iter()); + assert_eq!(transaction.changes_iter().collect::>(), changes); + } + #[test] fn optimized_composition() { let mut state = State::new("".into());