store cursor position for undo/redo

This commit is contained in:
Dongdong Zhou 2022-06-11 10:44:50 +01:00
parent ed55108bcd
commit 3aa2180890
4 changed files with 128 additions and 28 deletions

View File

@ -17,6 +17,7 @@
};
use crate::{
cursor::CursorMode,
editor::EditType,
indent::{auto_detect_indent_style, IndentStyle},
mode::Mode,
@ -54,6 +55,8 @@ struct Revision {
num: u64,
max_undo_so_far: usize,
edit: Contents,
cursor_before: Option<CursorMode>,
cursor_after: Option<CursorMode>,
}
#[derive(Debug, Clone)]
@ -103,6 +106,8 @@ pub fn new(text: &str) -> Self {
toggled_groups: BTreeSet::new(),
deletes_bitxor: Subset::new(0),
},
cursor_before: None,
cursor_after: None,
}],
cur_undo: 1,
undos: BTreeSet::new(),
@ -133,6 +138,18 @@ pub fn is_pristine(&self) -> bool {
self.is_equivalent_revision(self.pristine_rev_id, self.rev())
}
pub fn set_cursor_before(&mut self, cursor: CursorMode) {
self.revs
.last_mut()
.map(|rev| rev.cursor_before = Some(cursor));
}
pub fn set_cursor_after(&mut self, cursor: CursorMode) {
self.revs
.last_mut()
.map(|rev| rev.cursor_after = Some(cursor));
}
fn is_equivalent_revision(&self, base_rev: u64, other_rev: u64) -> bool {
let base_subset = self
.find_rev(base_rev)
@ -411,6 +428,8 @@ fn mk_new_rev(
inserts: new_inserts,
deletes: new_deletes,
},
cursor_before: None,
cursor_after: None,
},
new_text,
new_tombstones,
@ -535,6 +554,32 @@ fn compute_undo(&self, groups: &BTreeSet<usize>) -> (Revision, Subset) {
}
}
let cursor_before = self
.revs
.get(first_candidate)
.and_then(|rev| rev.cursor_before.clone());
let cursor_after = self
.revs
.get(first_candidate)
.and_then(|rev| match &rev.edit {
Contents::Edit { undo_group, .. } => Some(undo_group),
Contents::Undo { .. } => None,
})
.and_then(|group| {
let mut cursor: Option<&CursorMode> = None;
for rev in &self.revs[first_candidate..] {
if let Contents::Edit { ref undo_group, .. } = rev.edit {
if group == undo_group {
cursor = rev.cursor_after.as_ref();
} else {
break;
}
}
}
cursor.cloned()
});
let deletes_bitxor = self.deletes_from_union.bitxor(&deletes_from_union);
let max_undo_so_far = self.revs.last().unwrap().max_undo_so_far;
self.atomic_rev
@ -547,12 +592,22 @@ fn compute_undo(&self, groups: &BTreeSet<usize>) -> (Revision, Subset) {
toggled_groups,
deletes_bitxor,
},
cursor_before,
cursor_after,
},
deletes_from_union,
)
}
fn undo(&mut self, groups: BTreeSet<usize>) -> (RopeDelta, InvalLines) {
fn undo(
&mut self,
groups: BTreeSet<usize>,
) -> (
RopeDelta,
InvalLines,
Option<CursorMode>,
Option<CursorMode>,
) {
let (new_rev, new_deletes_from_union) = self.compute_undo(&groups);
let delta = Delta::synthesize(
&self.tombstones,
@ -568,6 +623,9 @@ fn undo(&mut self, groups: BTreeSet<usize>) -> (RopeDelta, InvalLines) {
);
self.undone_groups = groups;
let cursor_before = new_rev.cursor_before.clone();
let cursor_after = new_rev.cursor_after.clone();
let inval_lines = self.apply_edit(
&delta,
new_rev,
@ -576,26 +634,34 @@ fn undo(&mut self, groups: BTreeSet<usize>) -> (RopeDelta, InvalLines) {
new_deletes_from_union,
);
(delta, inval_lines)
(delta, inval_lines, cursor_before, cursor_after)
}
pub fn do_undo(&mut self) -> Option<(RopeDelta, InvalLines)> {
pub fn do_undo(
&mut self,
) -> Option<(RopeDelta, InvalLines, Option<CursorMode>)> {
if self.cur_undo > 1 {
self.cur_undo -= 1;
self.undos.insert(self.live_undos[self.cur_undo]);
self.last_edit_type = EditType::Undo;
Some(self.undo(self.undos.clone()))
let (delta, inval_lines, cursor_before, _cursor_after) =
self.undo(self.undos.clone());
Some((delta, inval_lines, cursor_before))
} else {
None
}
}
pub fn do_redo(&mut self) -> Option<(RopeDelta, InvalLines)> {
pub fn do_redo(
&mut self,
) -> Option<(RopeDelta, InvalLines, Option<CursorMode>)> {
if self.cur_undo < self.live_undos.len() {
self.undos.remove(&self.live_undos[self.cur_undo]);
self.cur_undo += 1;
self.last_edit_type = EditType::Redo;
Some(self.undo(self.undos.clone()))
let (delta, inval_lines, _cursor_before, cursor_after) =
self.undo(self.undos.clone());
Some((delta, inval_lines, cursor_after))
} else {
None
}

View File

@ -33,6 +33,16 @@ pub enum CursorMode {
Insert(Selection),
}
impl CursorMode {
pub fn offset(&self) -> usize {
match &self {
CursorMode::Normal(offset) => *offset,
CursorMode::Visual { end, .. } => *end,
CursorMode::Insert(selection) => selection.get_cursor_offset(),
}
}
}
impl Cursor {
pub fn new(
mode: CursorMode,
@ -48,11 +58,7 @@ pub fn new(
}
pub fn offset(&self) -> usize {
match &self.mode {
CursorMode::Normal(offset) => *offset,
CursorMode::Visual { end, .. } => *end,
CursorMode::Insert(selection) => selection.get_cursor_offset(),
}
self.mode.offset()
}
pub fn is_normal(&self) -> bool {

View File

@ -136,10 +136,14 @@ pub fn insert(
let (delta, inval_lines) =
buffer.edit(&edits, EditType::InsertChars);
buffer.set_cursor_before(CursorMode::Insert(selection.clone()));
// Update selection
let mut selection =
selection.apply_delta(&delta, true, InsertDrift::Default);
buffer.set_cursor_after(CursorMode::Insert(selection.clone()));
deltas.push((delta, inval_lines));
// Apply late edits
let edits_after = edits_after
@ -158,9 +162,11 @@ pub fn insert(
.map(|(selection, content)| (selection, content.as_str()))
.collect::<Vec<_>>();
let (delta, inval_lines) =
buffer.edit(&edits_after, EditType::InsertChars);
deltas.push((delta, inval_lines));
if !edits_after.is_empty() {
let (delta, inval_lines) =
buffer.edit(&edits_after, EditType::InsertChars);
deltas.push((delta, inval_lines));
}
// Adjust selection according to previous late edits
let mut adjustment = 0;
@ -783,13 +789,21 @@ pub fn do_edit<T: Clipboard>(
vec![(delta, inval_lines)]
}
Undo => {
if let Some((delta, inval_lines)) = buffer.do_undo() {
if let Some(new_cursor) =
get_first_selection_after(cursor, buffer, &delta)
{
*cursor = new_cursor
if let Some((delta, inval_lines, cursor_mode)) = buffer.do_undo() {
if let Some(cursor_mode) = cursor_mode {
if modal {
cursor.mode = CursorMode::Normal(cursor_mode.offset());
} else {
cursor.mode = cursor_mode;
}
} else {
cursor.apply_delta(&delta);
if let Some(new_cursor) =
get_first_selection_after(cursor, buffer, &delta)
{
*cursor = new_cursor
} else {
cursor.apply_delta(&delta);
}
}
vec![(delta, inval_lines)]
} else {
@ -797,13 +811,21 @@ pub fn do_edit<T: Clipboard>(
}
}
Redo => {
if let Some((delta, inval_lines)) = buffer.do_redo() {
if let Some(new_cursor) =
get_first_selection_after(cursor, buffer, &delta)
{
*cursor = new_cursor
if let Some((delta, inval_lines, cursor_mode)) = buffer.do_redo() {
if let Some(cursor_mode) = cursor_mode {
if modal {
cursor.mode = CursorMode::Normal(cursor_mode.offset());
} else {
cursor.mode = cursor_mode;
}
} else {
cursor.apply_delta(&delta);
if let Some(new_cursor) =
get_first_selection_after(cursor, buffer, &delta)
{
*cursor = new_cursor
} else {
cursor.apply_delta(&delta);
}
}
vec![(delta, inval_lines)]
} else {

View File

@ -626,8 +626,11 @@ pub fn do_insert(
cursor: &mut Cursor,
s: &str,
) -> Vec<(RopeDelta, InvalLines)> {
let old_cursor = cursor.mode.clone();
let deltas =
Editor::insert(cursor, &mut self.buffer, s, self.syntax.as_ref());
self.buffer_mut().set_cursor_before(old_cursor);
self.buffer_mut().set_cursor_after(cursor.mode.clone());
self.apply_deltas(&deltas);
deltas
}
@ -644,14 +647,15 @@ pub fn do_raw_edit(
pub fn do_edit(
&mut self,
curosr: &mut Cursor,
cursor: &mut Cursor,
cmd: &EditCommand,
modal: bool,
register: &mut Register,
) -> Vec<(RopeDelta, InvalLines)> {
let mut clipboard = SystemClipboard {};
let old_cursor = cursor.mode.clone();
let deltas = Editor::do_edit(
curosr,
cursor,
&mut self.buffer,
cmd,
self.syntax.as_ref(),
@ -659,6 +663,8 @@ pub fn do_edit(
modal,
register,
);
self.buffer_mut().set_cursor_before(old_cursor);
self.buffer_mut().set_cursor_after(cursor.mode.clone());
self.apply_deltas(&deltas);
deltas
}