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 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.
pub const BUFLINE: LineType = LineType::LF_CR;
@ -25,10 +25,12 @@ pub enum BufferPath {
/// An open text buffer, currently being edited.
#[derive(Debug, Clone)]
pub struct Buffer {
pub text: Rope, // The actual text content.
pub mark_sets: Vec<MarkSet>, // MarkSets for cursors.
pub path: BufferPath,
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,
}
@ -86,65 +88,65 @@ impl Buffer {
(byte_idx_range.1, byte_idx_range.0)
};
// Build the transaction.
let trans = Transaction::from_edit(start, &Cow::from(self.text.slice(start..end)), text);
// Build the change.
let change = Change::from_edit(start, &Cow::from(self.text.slice(start..end)), text);
// Update mark sets.
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();
}
// Apply the edit to the document.
trans.apply(&mut self.text);
change.apply(&mut self.text);
// 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
/// Transaction that was inverse-applied, in case it's needed for
/// Change that was inverse-applied, in case it's needed for
/// further processing.
///
/// Returns None if there is no edit to undo.
pub fn undo(&mut self) -> Option<&Transaction> {
if let Some(trans) = self.history.undo() {
pub fn undo(&mut self) -> Option<&Change> {
if let Some(change) = self.history.undo() {
self.edits_since_saved = self.edits_since_saved.wrapping_sub(1);
// Update mark sets.
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();
}
// 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 {
return None;
}
}
/// 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.
///
/// Returns None if there is no edit to redo.
pub fn redo(&mut self) -> Option<&Transaction> {
if let Some(trans) = self.history.redo() {
pub fn redo(&mut self) -> Option<&Change> {
if let Some(change) = self.history.redo() {
self.edits_since_saved = self.edits_since_saved.wrapping_add(1);
// Update mark sets.
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();
}
// Apply the edit to the document.
trans.apply(&mut self.text);
change.apply(&mut self.text);
return Some(trans);
return Some(change);
} else {
return None;
}

View File

@ -7,7 +7,7 @@ enum Op {
Retain(usize), // In bytes.
Replace {
// 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.
old: (usize, usize),
new: (usize, usize),
@ -36,21 +36,21 @@ impl Op {
/// A reversable set of edits treated as an atomic unit.
#[derive(Clone)]
pub struct Transaction {
pub struct Change {
ops: Vec<Op>,
buffer: String,
}
impl Transaction {
pub fn new() -> Transaction {
Transaction {
impl Change {
pub fn new() -> Change {
Change {
ops: Vec::new(),
buffer: String::new(),
}
}
/// Creates a Transaction from a single edit.
pub fn from_edit(byte_idx: usize, old: &str, new: &str) -> Transaction {
/// Creates a Change from a single edit.
pub fn from_edit(byte_idx: usize, old: &str, new: &str) -> Change {
let mut buffer = String::new();
buffer.push_str(old);
buffer.push_str(new);
@ -63,13 +63,13 @@ impl Transaction {
},
];
Transaction {
Change {
ops: ops,
buffer: buffer,
}
}
/// Creates a Transaction from a sorted, non-overlapping set of
/// Creates a Change from a sorted, non-overlapping set of
/// simultaneous edits.
///
/// 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
/// sequence of progressively applied edits, but rather as a set of
/// 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
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 len_delta = 0isize;
for (byte_idx, old, new) in edit_iter {
let adjusted_byte_idx = (byte_idx as isize + len_delta) as usize;
let retained = adjusted_byte_idx - i;
let old_range = (trans.buffer.len(), trans.buffer.len() + old.len());
trans.buffer.push_str(old);
let new_range = (trans.buffer.len(), trans.buffer.len() + new.len());
trans.buffer.push_str(new);
let old_range = (change.buffer.len(), change.buffer.len() + old.len());
change.buffer.push_str(old);
let new_range = (change.buffer.len(), change.buffer.len() + new.len());
change.buffer.push_str(new);
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,
new: new_range,
});
@ -105,38 +105,38 @@ impl Transaction {
i += retained + new.len();
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.
#[must_use]
pub fn compose(&self, other: &Transaction) -> Transaction {
pub fn compose(&self, other: &Change) -> Change {
use Op::*;
let mut trans = Transaction::new();
let mut change = Change::new();
let mut ops1 = self.ops.iter().copied();
let mut ops2 = other.ops.iter().copied();
let mut next_op1 = ops1.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
// sometimes, fragmenting the transaction.
// sometimes, fragmenting the change.
loop {
match (next_op1, next_op2) {
// Done!
(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.
(Some(op), None) => {
trans.push_op(op, &self.buffer);
change.push_op(op, &self.buffer);
next_op1 = ops1.next();
}
(None, Some(op)) => {
trans.push_op(op, &other.buffer);
change.push_op(op, &other.buffer);
next_op2 = ops2.next();
}
@ -144,15 +144,15 @@ impl Transaction {
// Retain, Retain
(Some(Retain(bc1)), Some(Retain(bc2))) => {
if bc1 < bc2 {
trans.retain(bc1);
change.retain(bc1);
next_op1 = ops1.next();
next_op2 = Some(Retain(bc2 - bc1));
} else if bc2 < bc1 {
trans.retain(bc2);
change.retain(bc2);
next_op1 = Some(Retain(bc1 - bc2));
next_op2 = ops2.next();
} else {
trans.retain(bc1);
change.retain(bc1);
next_op1 = ops1.next();
next_op2 = ops2.next();
}
@ -170,7 +170,7 @@ impl Transaction {
Some(Retain(bc2)),
) => {
if op1.len_post() < bc2 {
trans.push_op(op1, &self.buffer);
change.push_op(op1, &self.buffer);
next_op1 = ops1.next();
next_op2 = Some(Retain(bc2 - op1.len_post()));
} else if op1.len_post() > bc2 {
@ -182,11 +182,11 @@ impl Transaction {
old: (0, 0),
new: (new1.0 + bc2, new1.1),
};
trans.push_op(op1a, &self.buffer);
change.push_op(op1a, &self.buffer);
next_op1 = Some(op1b);
next_op2 = ops2.next();
} else {
trans.push_op(op1, &self.buffer);
change.push_op(op1, &self.buffer);
next_op1 = ops1.next();
next_op2 = ops2.next();
}
@ -204,7 +204,7 @@ impl Transaction {
let op2 = next_op2.unwrap();
if bc1 < op2.len_pre() {
trans.push_op(
change.push_op(
Replace {
old: (old2.0, old2.0 + bc1),
new: (0, 0),
@ -217,11 +217,11 @@ impl Transaction {
new: new2,
});
} 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_op2 = ops2.next();
} else {
trans.push_op(op2, &other.buffer);
change.push_op(op2, &other.buffer);
next_op1 = ops1.next();
next_op2 = ops2.next();
}
@ -243,7 +243,7 @@ impl Transaction {
let op2 = next_op2.unwrap();
if op1.len_post() < op2.len_pre() {
trans.push_op(
change.push_op(
Replace {
old: old1,
new: (0, 0),
@ -256,14 +256,14 @@ impl Transaction {
new: new2,
});
} else if op1.len_post() > op2.len_pre() {
trans.push_op(
change.push_op(
Replace {
old: old1,
new: (0, 0),
},
&self.buffer,
);
trans.push_op(
change.push_op(
Replace {
old: (0, 0),
new: new2,
@ -276,7 +276,7 @@ impl Transaction {
});
next_op2 = ops2.next();
} else {
trans.push_op_separate_buf(
change.push_op_separate_buf(
Replace {
old: old1,
new: new2,
@ -291,13 +291,13 @@ impl Transaction {
}
}
trans
change
}
/// Creates a transaction that is the inverse of this one (i.e. the "undo"
/// of this transaction).
pub fn inverse(&self) -> Transaction {
let mut inv = Transaction::new();
/// Creates a change that is the inverse of this one (i.e. the "undo"
/// of this change).
pub fn inverse(&self) -> Change {
let mut inv = Change::new();
for op in self.ops.iter() {
match op {
@ -320,7 +320,7 @@ impl Transaction {
inv
}
/// Applies the Transaction to a Rope.
/// Applies the Change to a Rope.
pub fn apply(&self, text: &mut Rope) {
let mut i = 0;
for op in self.ops.iter() {
@ -347,9 +347,9 @@ impl Transaction {
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) {
let mut i = 0;
for op in self.ops.iter() {
@ -380,7 +380,7 @@ impl Transaction {
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) {
marks.make_consistent();
@ -429,9 +429,9 @@ impl Transaction {
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
/// the marks to their original count and position.
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)]
fn retain(&mut self, byte_count: usize) {
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)]
fn replace(&mut self, old: &str, new: &str) {
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.
/// - 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);
}
/// Pushes an op onto the transaction operations.
/// Pushes an op onto the change operations.
///
/// - op: the operation.
/// - 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> {
f.write_str("Transaction {\n")?;
f.write_str("Change {\n")?;
f.write_str(" ops: [\n")?;
for op in self.ops.iter() {
match op {
@ -573,14 +573,14 @@ mod tests {
#[test]
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();
mark_set.add_mark(Mark::new(4, 4));
mark_set.add_mark(Mark::new(5, 5));
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].tail, 4);
assert_eq!(mark_set.marks[1].head, 7);
@ -591,7 +591,7 @@ mod tests {
#[test]
fn apply_to_marks_02() {
let trans = Transaction {
let change = Change {
ops: vec![
Op::Retain(5),
Op::Replace {
@ -611,7 +611,7 @@ mod tests {
mark_set.add_mark(Mark::new(5, 5));
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].tail, 4);
assert_eq!(mark_set.marks[1].head, 7);
@ -622,14 +622,14 @@ mod tests {
#[test]
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();
mark_set.add_mark(Mark::new(4, 4));
mark_set.add_mark(Mark::new(6, 6));
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].tail, 4);
assert_eq!(mark_set.marks[1].head, 5);
@ -640,7 +640,7 @@ mod tests {
#[test]
fn apply_to_marks_04() {
let trans = Transaction {
let change = Change {
ops: vec![
Op::Retain(5),
Op::Replace {
@ -660,7 +660,7 @@ mod tests {
mark_set.add_mark(Mark::new(6, 6));
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].tail, 4);
assert_eq!(mark_set.marks[1].head, 5);
@ -671,14 +671,14 @@ mod tests {
#[test]
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();
mark_set.add_mark(Mark::new(4, 4));
mark_set.add_mark(Mark::new(5, 5));
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].tail, 4);
assert_eq!(mark_set.marks[1].head, 7);
@ -689,14 +689,14 @@ mod tests {
#[test]
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();
mark_set.add_mark(Mark::new(4, 4));
mark_set.add_mark(Mark::new(6, 6));
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].tail, 4);
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)]
pub struct History {
edits: Vec<Transaction>,
edits: Vec<Change>,
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.edits.push(edit);
self.position += 1;
}
pub fn undo(&mut self) -> Option<&Transaction> {
pub fn undo(&mut self) -> Option<&Change> {
if self.position > 0 {
self.position -= 1;
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() {
let edit = &self.edits[self.position];
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.
pub fn commit(&mut self) {
if self.position == self.edits.len() {
return;
}
let mut undo_trans = Transaction::new();
let mut undo_trans = Change::new();
for trans in self.edits[self.position..].iter().rev() {
undo_trans = undo_trans.compose(&trans.inverse());
}

View File

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

View File

@ -6,7 +6,7 @@ use proptest::test_runner::Config;
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) {
edits.sort_by_key(|e| e.0);
@ -41,24 +41,24 @@ proptest! {
) {
// Make edits1 valid and compute the post-edits1 string.
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[..])
}));
let mut r1 = Rope::from_str(start_text);
trans1.apply(&mut r1);
change1.apply(&mut r1);
let text1: String = r1.clone().into();
// Make edits2 valid and compute the post-edits2 string.
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[..])
}));
trans2.apply(&mut r1);
change2.apply(&mut r1);
// Do the test!
let trans3 = trans1.compose(&trans2);
let change3 = change1.compose(&change2);
let mut r2 = Rope::from_str(start_text);
trans3.apply(&mut r2);
change3.apply(&mut r2);
assert_eq!(r1, r2);
}