Rename Transaction -> Change.

This commit is contained in:
Nathan Vegdahl 2024-10-19 08:57:19 +02:00
parent 5ad3f0ce8c
commit 2e04233c79
6 changed files with 105 additions and 103 deletions

View File

@ -7,7 +7,7 @@ use std::{
use ropey::{LineType, Rope}; use ropey::{LineType, Rope};
use crate::{history::History, marks::MarkSet, transaction::Transaction}; use crate::{change::Change, history::History, marks::MarkSet};
/// The line type that should be used as the standard line metric in a buffer. /// The line type that should be used as the standard line metric in a buffer.
pub const BUFLINE: LineType = LineType::LF_CR; pub const BUFLINE: LineType = LineType::LF_CR;
@ -25,10 +25,12 @@ pub enum BufferPath {
/// An open text buffer, currently being edited. /// An open text buffer, currently being edited.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Buffer { pub struct Buffer {
pub text: Rope, // The actual text content.
pub mark_sets: Vec<MarkSet>, // MarkSets for cursors.
pub path: BufferPath, pub path: BufferPath,
pub edits_since_saved: i64, // Tracks whether this buffer is out of sync with the last save. pub edits_since_saved: i64, // Tracks whether this buffer is out of sync with the last save.
pub text: Rope, // The actual text content.
pub mark_sets: Vec<MarkSet>, // MarkSets for cursors, view positions, etc.
history: History, history: History,
} }
@ -86,65 +88,65 @@ impl Buffer {
(byte_idx_range.1, byte_idx_range.0) (byte_idx_range.1, byte_idx_range.0)
}; };
// Build the transaction. // Build the change.
let trans = Transaction::from_edit(start, &Cow::from(self.text.slice(start..end)), text); let change = Change::from_edit(start, &Cow::from(self.text.slice(start..end)), text);
// Update mark sets. // Update mark sets.
for mark_set in self.mark_sets.iter_mut() { for mark_set in self.mark_sets.iter_mut() {
trans.apply_to_marks(mark_set); change.apply_to_marks(mark_set);
mark_set.make_consistent(); mark_set.make_consistent();
} }
// Apply the edit to the document. // Apply the edit to the document.
trans.apply(&mut self.text); change.apply(&mut self.text);
// Update undo stack. // Update undo stack.
self.history.push_edit(trans); self.history.push_edit(change);
} }
/// Un-does the last edit if there is one, and returns the /// Un-does the last edit if there is one, and returns the
/// Transaction that was inverse-applied, in case it's needed for /// Change that was inverse-applied, in case it's needed for
/// further processing. /// further processing.
/// ///
/// Returns None if there is no edit to undo. /// Returns None if there is no edit to undo.
pub fn undo(&mut self) -> Option<&Transaction> { pub fn undo(&mut self) -> Option<&Change> {
if let Some(trans) = self.history.undo() { if let Some(change) = self.history.undo() {
self.edits_since_saved = self.edits_since_saved.wrapping_sub(1); self.edits_since_saved = self.edits_since_saved.wrapping_sub(1);
// Update mark sets. // Update mark sets.
for mark_set in self.mark_sets.iter_mut() { for mark_set in self.mark_sets.iter_mut() {
trans.apply_inverse_to_marks(mark_set); change.apply_inverse_to_marks(mark_set);
mark_set.make_consistent(); mark_set.make_consistent();
} }
// Apply the edit to the document. // Apply the edit to the document.
trans.apply_inverse(&mut self.text); change.apply_inverse(&mut self.text);
return Some(trans); return Some(change);
} else { } else {
return None; return None;
} }
} }
/// Re-does the last edit if there is one, and returns the /// Re-does the last edit if there is one, and returns the
/// Transaction that was applied, in case it's needed for further /// Change that was applied, in case it's needed for further
/// processing. /// processing.
/// ///
/// Returns None if there is no edit to redo. /// Returns None if there is no edit to redo.
pub fn redo(&mut self) -> Option<&Transaction> { pub fn redo(&mut self) -> Option<&Change> {
if let Some(trans) = self.history.redo() { if let Some(change) = self.history.redo() {
self.edits_since_saved = self.edits_since_saved.wrapping_add(1); self.edits_since_saved = self.edits_since_saved.wrapping_add(1);
// Update mark sets. // Update mark sets.
for mark_set in self.mark_sets.iter_mut() { for mark_set in self.mark_sets.iter_mut() {
trans.apply_to_marks(mark_set); change.apply_to_marks(mark_set);
mark_set.make_consistent(); mark_set.make_consistent();
} }
// Apply the edit to the document. // Apply the edit to the document.
trans.apply(&mut self.text); change.apply(&mut self.text);
return Some(trans); return Some(change);
} else { } else {
return None; return None;
} }

View File

@ -7,7 +7,7 @@ enum Op {
Retain(usize), // In bytes. Retain(usize), // In bytes.
Replace { Replace {
// These both represent strings, and are byte-index ranges into // These both represent strings, and are byte-index ranges into
// the Transaction's `buffer` where their actual string data is // the Change's `buffer` where their actual string data is
// stored. `(0, 0)` can be used to indicate no string data. // stored. `(0, 0)` can be used to indicate no string data.
old: (usize, usize), old: (usize, usize),
new: (usize, usize), new: (usize, usize),
@ -36,21 +36,21 @@ impl Op {
/// A reversable set of edits treated as an atomic unit. /// A reversable set of edits treated as an atomic unit.
#[derive(Clone)] #[derive(Clone)]
pub struct Transaction { pub struct Change {
ops: Vec<Op>, ops: Vec<Op>,
buffer: String, buffer: String,
} }
impl Transaction { impl Change {
pub fn new() -> Transaction { pub fn new() -> Change {
Transaction { Change {
ops: Vec::new(), ops: Vec::new(),
buffer: String::new(), buffer: String::new(),
} }
} }
/// Creates a Transaction from a single edit. /// Creates a Change from a single edit.
pub fn from_edit(byte_idx: usize, old: &str, new: &str) -> Transaction { pub fn from_edit(byte_idx: usize, old: &str, new: &str) -> Change {
let mut buffer = String::new(); let mut buffer = String::new();
buffer.push_str(old); buffer.push_str(old);
buffer.push_str(new); buffer.push_str(new);
@ -63,13 +63,13 @@ impl Transaction {
}, },
]; ];
Transaction { Change {
ops: ops, ops: ops,
buffer: buffer, buffer: buffer,
} }
} }
/// Creates a Transaction from a sorted, non-overlapping set of /// Creates a Change from a sorted, non-overlapping set of
/// simultaneous edits. /// simultaneous edits.
/// ///
/// Takes an iterator that yields `(byte_index, old_text, replacement_text)` /// Takes an iterator that yields `(byte_index, old_text, replacement_text)`
@ -78,26 +78,26 @@ impl Transaction {
/// the original text--this function does _not_ treat them as a /// the original text--this function does _not_ treat them as a
/// sequence of progressively applied edits, but rather as a set of /// sequence of progressively applied edits, but rather as a set of
/// edits applied simultaneously. /// edits applied simultaneously.
pub fn from_ordered_edit_set<'a, I>(edit_iter: I) -> Transaction pub fn from_ordered_edit_set<'a, I>(edit_iter: I) -> Change
where where
I: Iterator<Item = (usize, &'a str, &'a str)> + 'a, I: Iterator<Item = (usize, &'a str, &'a str)> + 'a,
{ {
let mut trans = Transaction::new(); let mut change = Change::new();
let mut i = 0; let mut i = 0;
let mut len_delta = 0isize; let mut len_delta = 0isize;
for (byte_idx, old, new) in edit_iter { for (byte_idx, old, new) in edit_iter {
let adjusted_byte_idx = (byte_idx as isize + len_delta) as usize; let adjusted_byte_idx = (byte_idx as isize + len_delta) as usize;
let retained = adjusted_byte_idx - i; let retained = adjusted_byte_idx - i;
let old_range = (trans.buffer.len(), trans.buffer.len() + old.len()); let old_range = (change.buffer.len(), change.buffer.len() + old.len());
trans.buffer.push_str(old); change.buffer.push_str(old);
let new_range = (trans.buffer.len(), trans.buffer.len() + new.len()); let new_range = (change.buffer.len(), change.buffer.len() + new.len());
trans.buffer.push_str(new); change.buffer.push_str(new);
if retained > 0 { if retained > 0 {
trans.ops.push(Op::Retain(retained)); change.ops.push(Op::Retain(retained));
} }
trans.ops.push(Op::Replace { change.ops.push(Op::Replace {
old: old_range, old: old_range,
new: new_range, new: new_range,
}); });
@ -105,38 +105,38 @@ impl Transaction {
i += retained + new.len(); i += retained + new.len();
len_delta += new.len() as isize - old.len() as isize; len_delta += new.len() as isize - old.len() as isize;
} }
trans change
} }
/// Build a Transaction that is functionally identical to applying /// Build a Change that is functionally identical to applying
/// first `self` and then `other` sequentially. /// first `self` and then `other` sequentially.
#[must_use] #[must_use]
pub fn compose(&self, other: &Transaction) -> Transaction { pub fn compose(&self, other: &Change) -> Change {
use Op::*; use Op::*;
let mut trans = Transaction::new(); let mut change = Change::new();
let mut ops1 = self.ops.iter().copied(); let mut ops1 = self.ops.iter().copied();
let mut ops2 = other.ops.iter().copied(); let mut ops2 = other.ops.iter().copied();
let mut next_op1 = ops1.next(); let mut next_op1 = ops1.next();
let mut next_op2 = ops2.next(); let mut next_op2 = ops2.next();
// We progress through both transaction's ops such that their // We progress through both change's ops such that their
// heads stay in sync. To do this, we have to split ops // heads stay in sync. To do this, we have to split ops
// sometimes, fragmenting the transaction. // sometimes, fragmenting the change.
loop { loop {
match (next_op1, next_op2) { match (next_op1, next_op2) {
// Done! // Done!
(None, None) => break, (None, None) => break,
// One of the transactions is empty, so just append the // One of the changes is empty, so just append the
// remaining ops from the other one. // remaining ops from the other one.
(Some(op), None) => { (Some(op), None) => {
trans.push_op(op, &self.buffer); change.push_op(op, &self.buffer);
next_op1 = ops1.next(); next_op1 = ops1.next();
} }
(None, Some(op)) => { (None, Some(op)) => {
trans.push_op(op, &other.buffer); change.push_op(op, &other.buffer);
next_op2 = ops2.next(); next_op2 = ops2.next();
} }
@ -144,15 +144,15 @@ impl Transaction {
// Retain, Retain // Retain, Retain
(Some(Retain(bc1)), Some(Retain(bc2))) => { (Some(Retain(bc1)), Some(Retain(bc2))) => {
if bc1 < bc2 { if bc1 < bc2 {
trans.retain(bc1); change.retain(bc1);
next_op1 = ops1.next(); next_op1 = ops1.next();
next_op2 = Some(Retain(bc2 - bc1)); next_op2 = Some(Retain(bc2 - bc1));
} else if bc2 < bc1 { } else if bc2 < bc1 {
trans.retain(bc2); change.retain(bc2);
next_op1 = Some(Retain(bc1 - bc2)); next_op1 = Some(Retain(bc1 - bc2));
next_op2 = ops2.next(); next_op2 = ops2.next();
} else { } else {
trans.retain(bc1); change.retain(bc1);
next_op1 = ops1.next(); next_op1 = ops1.next();
next_op2 = ops2.next(); next_op2 = ops2.next();
} }
@ -170,7 +170,7 @@ impl Transaction {
Some(Retain(bc2)), Some(Retain(bc2)),
) => { ) => {
if op1.len_post() < bc2 { if op1.len_post() < bc2 {
trans.push_op(op1, &self.buffer); change.push_op(op1, &self.buffer);
next_op1 = ops1.next(); next_op1 = ops1.next();
next_op2 = Some(Retain(bc2 - op1.len_post())); next_op2 = Some(Retain(bc2 - op1.len_post()));
} else if op1.len_post() > bc2 { } else if op1.len_post() > bc2 {
@ -182,11 +182,11 @@ impl Transaction {
old: (0, 0), old: (0, 0),
new: (new1.0 + bc2, new1.1), new: (new1.0 + bc2, new1.1),
}; };
trans.push_op(op1a, &self.buffer); change.push_op(op1a, &self.buffer);
next_op1 = Some(op1b); next_op1 = Some(op1b);
next_op2 = ops2.next(); next_op2 = ops2.next();
} else { } else {
trans.push_op(op1, &self.buffer); change.push_op(op1, &self.buffer);
next_op1 = ops1.next(); next_op1 = ops1.next();
next_op2 = ops2.next(); next_op2 = ops2.next();
} }
@ -204,7 +204,7 @@ impl Transaction {
let op2 = next_op2.unwrap(); let op2 = next_op2.unwrap();
if bc1 < op2.len_pre() { if bc1 < op2.len_pre() {
trans.push_op( change.push_op(
Replace { Replace {
old: (old2.0, old2.0 + bc1), old: (old2.0, old2.0 + bc1),
new: (0, 0), new: (0, 0),
@ -217,11 +217,11 @@ impl Transaction {
new: new2, new: new2,
}); });
} else if bc1 > op2.len_pre() { } else if bc1 > op2.len_pre() {
trans.push_op(op2, &other.buffer); change.push_op(op2, &other.buffer);
next_op1 = Some(Retain(bc1 - op2.len_pre())); next_op1 = Some(Retain(bc1 - op2.len_pre()));
next_op2 = ops2.next(); next_op2 = ops2.next();
} else { } else {
trans.push_op(op2, &other.buffer); change.push_op(op2, &other.buffer);
next_op1 = ops1.next(); next_op1 = ops1.next();
next_op2 = ops2.next(); next_op2 = ops2.next();
} }
@ -243,7 +243,7 @@ impl Transaction {
let op2 = next_op2.unwrap(); let op2 = next_op2.unwrap();
if op1.len_post() < op2.len_pre() { if op1.len_post() < op2.len_pre() {
trans.push_op( change.push_op(
Replace { Replace {
old: old1, old: old1,
new: (0, 0), new: (0, 0),
@ -256,14 +256,14 @@ impl Transaction {
new: new2, new: new2,
}); });
} else if op1.len_post() > op2.len_pre() { } else if op1.len_post() > op2.len_pre() {
trans.push_op( change.push_op(
Replace { Replace {
old: old1, old: old1,
new: (0, 0), new: (0, 0),
}, },
&self.buffer, &self.buffer,
); );
trans.push_op( change.push_op(
Replace { Replace {
old: (0, 0), old: (0, 0),
new: new2, new: new2,
@ -276,7 +276,7 @@ impl Transaction {
}); });
next_op2 = ops2.next(); next_op2 = ops2.next();
} else { } else {
trans.push_op_separate_buf( change.push_op_separate_buf(
Replace { Replace {
old: old1, old: old1,
new: new2, new: new2,
@ -291,13 +291,13 @@ impl Transaction {
} }
} }
trans change
} }
/// Creates a transaction that is the inverse of this one (i.e. the "undo" /// Creates a change that is the inverse of this one (i.e. the "undo"
/// of this transaction). /// of this change).
pub fn inverse(&self) -> Transaction { pub fn inverse(&self) -> Change {
let mut inv = Transaction::new(); let mut inv = Change::new();
for op in self.ops.iter() { for op in self.ops.iter() {
match op { match op {
@ -320,7 +320,7 @@ impl Transaction {
inv inv
} }
/// Applies the Transaction to a Rope. /// Applies the Change to a Rope.
pub fn apply(&self, text: &mut Rope) { pub fn apply(&self, text: &mut Rope) {
let mut i = 0; let mut i = 0;
for op in self.ops.iter() { for op in self.ops.iter() {
@ -347,9 +347,9 @@ impl Transaction {
debug_assert!(i <= text.len()); debug_assert!(i <= text.len());
} }
/// Applies the inverse of the Transaction to a Rope. /// Applies the inverse of the Change to a Rope.
/// ///
/// This is an "undo" of the Transaction. /// This is an "undo" of the Change.
pub fn apply_inverse(&self, text: &mut Rope) { pub fn apply_inverse(&self, text: &mut Rope) {
let mut i = 0; let mut i = 0;
for op in self.ops.iter() { for op in self.ops.iter() {
@ -380,7 +380,7 @@ impl Transaction {
debug_assert!(i <= text.len()); debug_assert!(i <= text.len());
} }
/// Applies the Transaction to a set of Marks. /// Applies the Change to a set of Marks.
pub fn apply_to_marks(&self, marks: &mut MarkSet) { pub fn apply_to_marks(&self, marks: &mut MarkSet) {
marks.make_consistent(); marks.make_consistent();
@ -429,9 +429,9 @@ impl Transaction {
marks.make_consistent(); marks.make_consistent();
} }
/// Applies the inverse of the Transaction to a set of Marks. /// Applies the inverse of the Change to a set of Marks.
/// ///
/// This is essentially an "undo" of the Transaction. However, on /// This is essentially an "undo" of the Change. However, on
/// marks this is a lossy operation and is not guaranteed to return /// marks this is a lossy operation and is not guaranteed to return
/// the marks to their original count and position. /// the marks to their original count and position.
pub fn apply_inverse_to_marks(&self, marks: &mut MarkSet) { pub fn apply_inverse_to_marks(&self, marks: &mut MarkSet) {
@ -484,7 +484,7 @@ impl Transaction {
//--------------------------------------------------------- //---------------------------------------------------------
/// Pushes a retain op onto the transaction operations. /// Pushes a retain op onto the change operations.
#[inline(always)] #[inline(always)]
fn retain(&mut self, byte_count: usize) { fn retain(&mut self, byte_count: usize) {
if let Some(Op::Retain(ref mut last_byte_count)) = self.ops.last_mut() { if let Some(Op::Retain(ref mut last_byte_count)) = self.ops.last_mut() {
@ -494,7 +494,7 @@ impl Transaction {
} }
} }
/// Pushes a replace op onto the transaction operations. /// Pushes a replace op onto the change operations.
#[inline(always)] #[inline(always)]
fn replace(&mut self, old: &str, new: &str) { fn replace(&mut self, old: &str, new: &str) {
if !old.is_empty() || !new.is_empty() { if !old.is_empty() || !new.is_empty() {
@ -513,7 +513,7 @@ impl Transaction {
} }
} }
/// Pushes an op onto the transaction operations. /// Pushes an op onto the change operations.
/// ///
/// - op: the operation. /// - op: the operation.
/// - buffer: the string buffer the op points into if it's a `Replace`. /// - buffer: the string buffer the op points into if it's a `Replace`.
@ -522,7 +522,7 @@ impl Transaction {
self.push_op_separate_buf(op, buffer, buffer); self.push_op_separate_buf(op, buffer, buffer);
} }
/// Pushes an op onto the transaction operations. /// Pushes an op onto the change operations.
/// ///
/// - op: the operation. /// - op: the operation.
/// - buf_old: the string buffer that `Replace { old }` points into. /// - buf_old: the string buffer that `Replace { old }` points into.
@ -538,9 +538,9 @@ impl Transaction {
} }
} }
impl std::fmt::Debug for Transaction { impl std::fmt::Debug for Change {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
f.write_str("Transaction {\n")?; f.write_str("Change {\n")?;
f.write_str(" ops: [\n")?; f.write_str(" ops: [\n")?;
for op in self.ops.iter() { for op in self.ops.iter() {
match op { match op {
@ -573,14 +573,14 @@ mod tests {
#[test] #[test]
fn apply_to_marks_01() { fn apply_to_marks_01() {
let trans = Transaction::from_edit(5, "", "hi"); let change = Change::from_edit(5, "", "hi");
let mut mark_set = MarkSet::new(); let mut mark_set = MarkSet::new();
mark_set.add_mark(Mark::new(4, 4)); mark_set.add_mark(Mark::new(4, 4));
mark_set.add_mark(Mark::new(5, 5)); mark_set.add_mark(Mark::new(5, 5));
mark_set.add_mark(Mark::new(6, 6)); mark_set.add_mark(Mark::new(6, 6));
trans.apply_to_marks(&mut mark_set); change.apply_to_marks(&mut mark_set);
assert_eq!(mark_set.marks[0].head, 4); assert_eq!(mark_set.marks[0].head, 4);
assert_eq!(mark_set.marks[0].tail, 4); assert_eq!(mark_set.marks[0].tail, 4);
assert_eq!(mark_set.marks[1].head, 7); assert_eq!(mark_set.marks[1].head, 7);
@ -591,7 +591,7 @@ mod tests {
#[test] #[test]
fn apply_to_marks_02() { fn apply_to_marks_02() {
let trans = Transaction { let change = Change {
ops: vec![ ops: vec![
Op::Retain(5), Op::Retain(5),
Op::Replace { Op::Replace {
@ -611,7 +611,7 @@ mod tests {
mark_set.add_mark(Mark::new(5, 5)); mark_set.add_mark(Mark::new(5, 5));
mark_set.add_mark(Mark::new(6, 6)); mark_set.add_mark(Mark::new(6, 6));
trans.apply_to_marks(&mut mark_set); change.apply_to_marks(&mut mark_set);
assert_eq!(mark_set.marks[0].head, 4); assert_eq!(mark_set.marks[0].head, 4);
assert_eq!(mark_set.marks[0].tail, 4); assert_eq!(mark_set.marks[0].tail, 4);
assert_eq!(mark_set.marks[1].head, 7); assert_eq!(mark_set.marks[1].head, 7);
@ -622,14 +622,14 @@ mod tests {
#[test] #[test]
fn apply_to_marks_03() { fn apply_to_marks_03() {
let trans = Transaction::from_edit(5, "hi", ""); let change = Change::from_edit(5, "hi", "");
let mut mark_set = MarkSet::new(); let mut mark_set = MarkSet::new();
mark_set.add_mark(Mark::new(4, 4)); mark_set.add_mark(Mark::new(4, 4));
mark_set.add_mark(Mark::new(6, 6)); mark_set.add_mark(Mark::new(6, 6));
mark_set.add_mark(Mark::new(8, 8)); mark_set.add_mark(Mark::new(8, 8));
trans.apply_to_marks(&mut mark_set); change.apply_to_marks(&mut mark_set);
assert_eq!(mark_set.marks[0].head, 4); assert_eq!(mark_set.marks[0].head, 4);
assert_eq!(mark_set.marks[0].tail, 4); assert_eq!(mark_set.marks[0].tail, 4);
assert_eq!(mark_set.marks[1].head, 5); assert_eq!(mark_set.marks[1].head, 5);
@ -640,7 +640,7 @@ mod tests {
#[test] #[test]
fn apply_to_marks_04() { fn apply_to_marks_04() {
let trans = Transaction { let change = Change {
ops: vec![ ops: vec![
Op::Retain(5), Op::Retain(5),
Op::Replace { Op::Replace {
@ -660,7 +660,7 @@ mod tests {
mark_set.add_mark(Mark::new(6, 6)); mark_set.add_mark(Mark::new(6, 6));
mark_set.add_mark(Mark::new(8, 8)); mark_set.add_mark(Mark::new(8, 8));
trans.apply_to_marks(&mut mark_set); change.apply_to_marks(&mut mark_set);
assert_eq!(mark_set.marks[0].head, 4); assert_eq!(mark_set.marks[0].head, 4);
assert_eq!(mark_set.marks[0].tail, 4); assert_eq!(mark_set.marks[0].tail, 4);
assert_eq!(mark_set.marks[1].head, 5); assert_eq!(mark_set.marks[1].head, 5);
@ -671,14 +671,14 @@ mod tests {
#[test] #[test]
fn apply_inverse_to_marks_01() { fn apply_inverse_to_marks_01() {
let trans = Transaction::from_edit(5, "hi", ""); let change = Change::from_edit(5, "hi", "");
let mut mark_set = MarkSet::new(); let mut mark_set = MarkSet::new();
mark_set.add_mark(Mark::new(4, 4)); mark_set.add_mark(Mark::new(4, 4));
mark_set.add_mark(Mark::new(5, 5)); mark_set.add_mark(Mark::new(5, 5));
mark_set.add_mark(Mark::new(6, 6)); mark_set.add_mark(Mark::new(6, 6));
trans.apply_inverse_to_marks(&mut mark_set); change.apply_inverse_to_marks(&mut mark_set);
assert_eq!(mark_set.marks[0].head, 4); assert_eq!(mark_set.marks[0].head, 4);
assert_eq!(mark_set.marks[0].tail, 4); assert_eq!(mark_set.marks[0].tail, 4);
assert_eq!(mark_set.marks[1].head, 7); assert_eq!(mark_set.marks[1].head, 7);
@ -689,14 +689,14 @@ mod tests {
#[test] #[test]
fn apply_inverse_to_marks_02() { fn apply_inverse_to_marks_02() {
let trans = Transaction::from_edit(5, "", "hi"); let change = Change::from_edit(5, "", "hi");
let mut mark_set = MarkSet::new(); let mut mark_set = MarkSet::new();
mark_set.add_mark(Mark::new(4, 4)); mark_set.add_mark(Mark::new(4, 4));
mark_set.add_mark(Mark::new(6, 6)); mark_set.add_mark(Mark::new(6, 6));
mark_set.add_mark(Mark::new(8, 8)); mark_set.add_mark(Mark::new(8, 8));
trans.apply_inverse_to_marks(&mut mark_set); change.apply_inverse_to_marks(&mut mark_set);
assert_eq!(mark_set.marks[0].head, 4); assert_eq!(mark_set.marks[0].head, 4);
assert_eq!(mark_set.marks[0].tail, 4); assert_eq!(mark_set.marks[0].tail, 4);
assert_eq!(mark_set.marks[1].head, 5); assert_eq!(mark_set.marks[1].head, 5);

View File

@ -1,8 +1,8 @@
use crate::transaction::Transaction; use crate::change::Change;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct History { pub struct History {
edits: Vec<Transaction>, edits: Vec<Change>,
position: usize, // Where we are in the history. position: usize, // Where we are in the history.
} }
@ -14,13 +14,13 @@ impl History {
} }
} }
pub fn push_edit(&mut self, edit: Transaction) { pub fn push_edit(&mut self, edit: Change) {
self.commit(); self.commit();
self.edits.push(edit); self.edits.push(edit);
self.position += 1; self.position += 1;
} }
pub fn undo(&mut self) -> Option<&Transaction> { pub fn undo(&mut self) -> Option<&Change> {
if self.position > 0 { if self.position > 0 {
self.position -= 1; self.position -= 1;
Some(&self.edits[self.position]) Some(&self.edits[self.position])
@ -29,7 +29,7 @@ impl History {
} }
} }
pub fn redo(&mut self) -> Option<&Transaction> { pub fn redo(&mut self) -> Option<&Change> {
if self.position < self.edits.len() { if self.position < self.edits.len() {
let edit = &self.edits[self.position]; let edit = &self.edits[self.position];
self.position += 1; self.position += 1;
@ -39,14 +39,14 @@ impl History {
} }
} }
/// Takes the current undo state, and pushes that as another transaction on /// Takes the current undo state, and pushes that as another change on
/// top of the existing undo stack, without deleting any existing items. /// top of the existing undo stack, without deleting any existing items.
pub fn commit(&mut self) { pub fn commit(&mut self) {
if self.position == self.edits.len() { if self.position == self.edits.len() {
return; return;
} }
let mut undo_trans = Transaction::new(); let mut undo_trans = Change::new();
for trans in self.edits[self.position..].iter().rev() { for trans in self.edits[self.position..].iter().rev() {
undo_trans = undo_trans.compose(&trans.inverse()); undo_trans = undo_trans.compose(&trans.inverse());
} }

View File

@ -2,8 +2,8 @@ extern crate ropey;
extern crate unicode_segmentation; extern crate unicode_segmentation;
pub mod buffer; pub mod buffer;
pub mod change;
pub mod editor; pub mod editor;
pub mod history; pub mod history;
pub mod marks; pub mod marks;
pub mod project; pub mod project;
pub mod transaction;

View File

@ -6,7 +6,7 @@ use proptest::test_runner::Config;
use ropey::Rope; use ropey::Rope;
use backend::transaction::Transaction; use backend::change::Change;
fn make_edit_list_valid(edits: &mut Vec<(usize, usize, String)>, start_text: &str) { fn make_edit_list_valid(edits: &mut Vec<(usize, usize, String)>, start_text: &str) {
edits.sort_by_key(|e| e.0); edits.sort_by_key(|e| e.0);
@ -41,24 +41,24 @@ proptest! {
) { ) {
// Make edits1 valid and compute the post-edits1 string. // Make edits1 valid and compute the post-edits1 string.
make_edit_list_valid(&mut edits1, start_text); make_edit_list_valid(&mut edits1, start_text);
let trans1 = Transaction::from_ordered_edit_set(edits1.iter().map(|e| { let change1 = Change::from_ordered_edit_set(edits1.iter().map(|e| {
(e.0, &start_text[e.0..(e.0+e.1)], &e.2[..]) (e.0, &start_text[e.0..(e.0+e.1)], &e.2[..])
})); }));
let mut r1 = Rope::from_str(start_text); let mut r1 = Rope::from_str(start_text);
trans1.apply(&mut r1); change1.apply(&mut r1);
let text1: String = r1.clone().into(); let text1: String = r1.clone().into();
// Make edits2 valid and compute the post-edits2 string. // Make edits2 valid and compute the post-edits2 string.
make_edit_list_valid(&mut edits2, &text1); make_edit_list_valid(&mut edits2, &text1);
let trans2 = Transaction::from_ordered_edit_set(edits2.iter().map(|e| { let change2 = Change::from_ordered_edit_set(edits2.iter().map(|e| {
(e.0, &text1[e.0..(e.0+e.1)], &e.2[..]) (e.0, &text1[e.0..(e.0+e.1)], &e.2[..])
})); }));
trans2.apply(&mut r1); change2.apply(&mut r1);
// Do the test! // Do the test!
let trans3 = trans1.compose(&trans2); let change3 = change1.compose(&change2);
let mut r2 = Rope::from_str(start_text); let mut r2 = Rope::from_str(start_text);
trans3.apply(&mut r2); change3.apply(&mut r2);
assert_eq!(r1, r2); assert_eq!(r1, r2);
} }