@@ -390,6 +390,10 @@ impl ChangeSet {
394+ pub fn changes_iter(&self) -> ChangeIterator {
395+ ChangeIterator::new(self)
395/// Transaction represents a single undoable unit of changes. Several changes can be grouped into
399/// Transaction represents a single undoable unit of changes. Several changes can be grouped into
@@ -502,6 +506,10 @@ impl Transaction {
502 (range.head, range.head, Some(text.clone()))
506 (range.head, range.head, Some(text.clone()))
510+ pub fn changes_iter(&self) -> ChangeIterator {
511+ self.changes.changes_iter()
507impl From<ChangeSet> for Transaction {
515impl From<ChangeSet> for Transaction {
@@ -513,6 +521,51 @@ impl From<ChangeSet> for Transaction {
524+pub struct ChangeIterator<'a> {
525+ iter: std::iter::Peekable<std::slice::Iter<'a, Operation>>,
529+impl<'a> ChangeIterator<'a> {
530+ fn new(changeset: &'a ChangeSet) -> Self {
531+ let iter = changeset.changes.iter().peekable();
532+ Self { iter, pos: 0 }
536+impl<'a> Iterator for ChangeIterator<'a> {
539+ fn next(&mut self) -> Option<Self::Item> {
543+ match self.iter.next()? {
548+ let start = self.pos;
550+ return Some((start, self.pos, None));
553+ let start = self.pos;
554+ // a subsequent delete means a replace, consume it
555+ if let Some(Delete(len)) = self.iter.peek() {
559+ return Some((start, self.pos, Some(s.clone())));
561+ return Some((start, start, Some(s.clone())));
516#[cfg(test)]
569#[cfg(test)]
517mod test {
570mod test {
518 use super::*;
571 use super::*;
@@ -626,6 +679,14 @@ mod test {
626 assert_eq!(state.doc, Rope::from_str("hello void! 123"));
679 assert_eq!(state.doc, Rope::from_str("hello void! 123"));
684+ let mut state = State::new("hello world!\ntest 123".into());
685+ let changes = vec![(6, 11, Some("void".into())), (12, 17, None)];
686+ let transaction = Transaction::change(&state.doc, changes.clone().into_iter());
687+ assert_eq!(transaction.changes_iter().collect::<Vec<_>>(), changes);
630 fn optimized_composition() {
691 fn optimized_composition() {
631 let mut state = State::new("".into());
692 let mut state = State::new("".into());