Rename Transaction
-> Change
.
This commit is contained in:
parent
5ad3f0ce8c
commit
2e04233c79
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user