Merge UndoHistory and History, storing also operations in the latter

This commit is contained in:
Antonio Scandurra 2021-04-09 17:17:36 +02:00
parent d0b06a2a1d
commit 472ff1621f
3 changed files with 94 additions and 88 deletions

View file

@ -63,12 +63,11 @@ pub struct Buffer {
file: Option<FileHandle>,
fragments: SumTree<Fragment>,
insertion_splits: HashMap<time::Local, SumTree<InsertionSplit>>,
edit_ops: HashMap<time::Local, EditOperation>,
pub version: time::Global,
saved_version: time::Global,
last_edit: time::Local,
undo_map: UndoMap,
undo_history: UndoHistory,
history: History,
selections: HashMap<SelectionSetId, Vec<Selection>>,
pub selections_last_update: SelectionsVersion,
deferred_ops: OperationQueue<Operation>,
@ -82,9 +81,72 @@ pub struct Snapshot {
fragments: SumTree<Fragment>,
}
#[derive(Clone)]
struct EditGroup {
edits: Vec<time::Local>,
last_edit_at: Instant,
}
#[derive(Clone)]
pub struct History {
pub base_text: String,
pub base_text: Arc<str>,
ops: HashMap<time::Local, EditOperation>,
undo_stack: Vec<EditGroup>,
redo_stack: Vec<EditGroup>,
group_interval: Duration,
}
impl History {
pub fn new(base_text: Arc<str>) -> Self {
Self {
base_text,
ops: Default::default(),
undo_stack: Vec::new(),
redo_stack: Vec::new(),
group_interval: UNDO_GROUP_INTERVAL,
}
}
fn push(&mut self, op: EditOperation) {
self.ops.insert(op.id, op);
}
fn push_undo(&mut self, edit_id: time::Local, now: Instant) {
if let Some(edit_group) = self.undo_stack.last_mut() {
if now - edit_group.last_edit_at <= self.group_interval {
edit_group.edits.push(edit_id);
edit_group.last_edit_at = now;
} else {
self.undo_stack.push(EditGroup {
edits: vec![edit_id],
last_edit_at: now,
});
}
} else {
self.undo_stack.push(EditGroup {
edits: vec![edit_id],
last_edit_at: now,
});
}
}
fn pop_undo(&mut self) -> Option<&EditGroup> {
if let Some(edit_group) = self.undo_stack.pop() {
self.redo_stack.push(edit_group);
self.redo_stack.last()
} else {
None
}
}
fn pop_redo(&mut self) -> Option<&EditGroup> {
if let Some(edit_group) = self.redo_stack.pop() {
self.undo_stack.push(edit_group);
self.undo_stack.last()
} else {
None
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
@ -130,66 +192,6 @@ impl UndoMap {
}
}
#[derive(Clone)]
struct EditGroup {
edits: Vec<time::Local>,
last_edit_at: Instant,
}
#[derive(Clone)]
struct UndoHistory {
group_interval: Duration,
undo_stack: Vec<EditGroup>,
redo_stack: Vec<EditGroup>,
}
impl UndoHistory {
fn new(group_interval: Duration) -> Self {
Self {
group_interval,
undo_stack: Vec::new(),
redo_stack: Vec::new(),
}
}
fn push(&mut self, edit_id: time::Local, now: Instant) {
if let Some(edit_group) = self.undo_stack.last_mut() {
if now - edit_group.last_edit_at <= self.group_interval {
edit_group.edits.push(edit_id);
edit_group.last_edit_at = now;
} else {
self.undo_stack.push(EditGroup {
edits: vec![edit_id],
last_edit_at: now,
});
}
} else {
self.undo_stack.push(EditGroup {
edits: vec![edit_id],
last_edit_at: now,
});
}
}
fn pop_undo(&mut self) -> Option<&EditGroup> {
if let Some(edit_group) = self.undo_stack.pop() {
self.redo_stack.push(edit_group);
self.redo_stack.last()
} else {
None
}
}
fn pop_redo(&mut self) -> Option<&EditGroup> {
if let Some(edit_group) = self.redo_stack.pop() {
self.undo_stack.push(edit_group);
self.undo_stack.last()
} else {
None
}
}
}
#[derive(Clone)]
pub struct CharIter<'a> {
fragments_cursor: Cursor<'a, Fragment, usize, usize>,
@ -305,15 +307,15 @@ pub struct UndoOperation {
}
impl Buffer {
pub fn new<T: Into<String>>(replica_id: ReplicaId, base_text: T) -> Self {
Self::build(replica_id, None, base_text.into())
pub fn new<T: Into<Arc<str>>>(replica_id: ReplicaId, base_text: T) -> Self {
Self::build(replica_id, None, History::new(base_text.into()))
}
pub fn from_history(replica_id: ReplicaId, file: FileHandle, history: History) -> Self {
Self::build(replica_id, Some(file), history.base_text)
Self::build(replica_id, Some(file), history)
}
fn build(replica_id: ReplicaId, file: Option<FileHandle>, base_text: String) -> Self {
fn build(replica_id: ReplicaId, file: Option<FileHandle>, history: History) -> Self {
let mut insertion_splits = HashMap::default();
let mut fragments = SumTree::new();
@ -321,7 +323,7 @@ impl Buffer {
id: time::Local::default(),
parent_id: time::Local::default(),
offset_in_parent: 0,
text: base_text.into(),
text: history.base_text.clone().into(),
lamport_timestamp: time::Lamport::default(),
};
@ -366,12 +368,11 @@ impl Buffer {
file,
fragments,
insertion_splits,
edit_ops: HashMap::default(),
version: time::Global::new(),
saved_version: time::Global::new(),
last_edit: time::Local::default(),
undo_map: Default::default(),
undo_history: UndoHistory::new(UNDO_GROUP_INTERVAL),
history,
selections: HashMap::default(),
selections_last_update: 0,
deferred_ops: OperationQueue::new(),
@ -602,8 +603,8 @@ impl Buffer {
for op in &ops {
if let Operation::Edit { edit, .. } = op {
self.edit_ops.insert(edit.id, edit.clone());
self.undo_history.push(edit.id, now);
self.history.push(edit.clone());
self.history.push_undo(edit.id, now);
}
}
@ -864,7 +865,7 @@ impl Buffer {
lamport_timestamp,
)?;
self.version.observe(edit.id);
self.edit_ops.insert(edit.id, edit);
self.history.push(edit);
}
}
Operation::Undo {
@ -1017,7 +1018,7 @@ impl Buffer {
let old_version = self.version.clone();
let mut ops = Vec::new();
if let Some(edit_group) = self.undo_history.pop_undo() {
if let Some(edit_group) = self.history.pop_undo() {
for edit_id in edit_group.edits.clone() {
ops.push(self.undo_or_redo(edit_id).unwrap());
}
@ -1039,7 +1040,7 @@ impl Buffer {
let old_version = self.version.clone();
let mut ops = Vec::new();
if let Some(edit_group) = self.undo_history.pop_redo() {
if let Some(edit_group) = self.history.pop_redo() {
for edit_id in edit_group.edits.clone() {
ops.push(self.undo_or_redo(edit_id).unwrap());
}
@ -1075,7 +1076,7 @@ impl Buffer {
let mut new_fragments;
self.undo_map.insert(undo);
let edit = &self.edit_ops[&undo.edit_id];
let edit = &self.history.ops[&undo.edit_id];
let start_fragment_id = self.resolve_fragment_id(edit.start_id, edit.start_offset)?;
let end_fragment_id = self.resolve_fragment_id(edit.end_id, edit.end_offset)?;
let mut cursor = self.fragments.cursor::<FragmentIdRef, ()>();
@ -1700,12 +1701,11 @@ impl Clone for Buffer {
file: self.file.clone(),
fragments: self.fragments.clone(),
insertion_splits: self.insertion_splits.clone(),
edit_ops: self.edit_ops.clone(),
version: self.version.clone(),
saved_version: self.saved_version.clone(),
last_edit: self.last_edit.clone(),
undo_map: self.undo_map.clone(),
undo_history: self.undo_history.clone(),
history: self.history.clone(),
selections: self.selections.clone(),
selections_last_update: self.selections_last_update.clone(),
deferred_ops: self.deferred_ops.clone(),
@ -3076,7 +3076,7 @@ mod tests {
pub fn randomly_undo_redo(&mut self, rng: &mut impl Rng) -> Vec<Operation> {
let mut ops = Vec::new();
for _ in 0..rng.gen_range(1..5) {
if let Some(edit_id) = self.edit_ops.keys().choose(rng).copied() {
if let Some(edit_id) = self.history.ops.keys().choose(rng).copied() {
ops.push(self.undo_or_redo(edit_id).unwrap());
}
}

View file

@ -117,6 +117,18 @@ pub struct Text {
impl From<String> for Text {
fn from(text: String) -> Self {
Self::from(Arc::from(text))
}
}
impl<'a> From<&'a str> for Text {
fn from(text: &'a str) -> Self {
Self::from(Arc::from(text))
}
}
impl From<Arc<str>> for Text {
fn from(text: Arc<str>) -> Self {
let mut runs = Vec::new();
let mut chars_len = 0;
@ -147,19 +159,13 @@ impl From<String> for Text {
let mut tree = SumTree::new();
tree.extend(runs);
Text {
text: text.into(),
text,
runs: tree,
range: 0..chars_len,
}
}
}
impl<'a> From<&'a str> for Text {
fn from(text: &'a str) -> Self {
Self::from(String::from(text))
}
}
impl Debug for Text {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Text").field(&self.as_str()).finish()

View file

@ -345,7 +345,7 @@ impl Worktree {
let mut file = smol::fs::File::open(&path).await?;
let mut base_text = String::new();
file.read_to_string(&mut base_text).await?;
let history = History { base_text };
let history = History::new(Arc::from(base_text));
tree.0.write().histories.insert(entry_id, history.clone());
Ok(history)
}
@ -717,7 +717,7 @@ mod test {
.await
.unwrap();
assert_eq!(history.base_text, buffer.text());
assert_eq!(history.base_text.as_ref(), buffer.text());
})
}
}