on master help sign in
fix: undo/redo selection handling.
d181027Blaž Hrastnik
helix-term/src/commands.rs+20−4
@@ -322,17 +322,23 @@ pub fn select_line(cx: &mut Context) {
322 cx.view.doc.set_selection(Selection::single(start, end));
322 cx.view.doc.set_selection(Selection::single(start, end));
323}
323}
324
324
325+// heuristic: append changes to history after each command, unless we're in insert mode
326+
325−pub fn delete_selection(cx: &mut Context) {
327+fn _delete_selection(cx: &mut Context) {
326 let transaction = Transaction::change_by_selection(&cx.view.doc.state, |range| {
328 let transaction = Transaction::change_by_selection(&cx.view.doc.state, |range| {
327 (range.from(), range.to() + 1, None)
329 (range.from(), range.to() + 1, None)
328 });
330 });
329 cx.view.doc.apply(&transaction);
331 cx.view.doc.apply(&transaction);
332+}
333+
334+pub fn delete_selection(cx: &mut Context) {
335+ _delete_selection(cx);
330
336
331 append_changes_to_history(cx);
337 append_changes_to_history(cx);
332}
338}
333
339
334pub fn change_selection(cx: &mut Context) {
340pub fn change_selection(cx: &mut Context) {
335− delete_selection(cx);
341+ _delete_selection(cx);
336 insert_mode(cx);
342 insert_mode(cx);
337}
343}
338
344
@@ -359,8 +365,9 @@ pub fn flip_selections(cx: &mut Context) {
359fn enter_insert_mode(cx: &mut Context) {
365fn enter_insert_mode(cx: &mut Context) {
360 cx.view.doc.mode = Mode::Insert;
366 cx.view.doc.mode = Mode::Insert;
361
367
362− append_changes_to_history(cx);
368+ // TODO: store selection for undo
363}
369}
370+
364// inserts at the start of each selection
371// inserts at the start of each selection
365pub fn insert_mode(cx: &mut Context) {
372pub fn insert_mode(cx: &mut Context) {
366 enter_insert_mode(cx);
373 enter_insert_mode(cx);
@@ -535,6 +542,8 @@ fn append_changes_to_history(cx: &mut Context) {
535 return;
542 return;
536 }
543 }
537
544
545+ // TODO: change -> change -> undo -> change -> change fails, probably old_state needs reset
546+
538 let new_changeset = ChangeSet::new(cx.view.doc.text());
547 let new_changeset = ChangeSet::new(cx.view.doc.text());
539 let changes = std::mem::replace(&mut cx.view.doc.changes, new_changeset);
548 let changes = std::mem::replace(&mut cx.view.doc.changes, new_changeset);
540 // Instead of doing this messy merge we could always commit, and based on transaction
549 // Instead of doing this messy merge we could always commit, and based on transaction
@@ -548,7 +557,13 @@ fn append_changes_to_history(cx: &mut Context) {
548 // TODO: trigger lsp/documentDidChange with changes
557 // TODO: trigger lsp/documentDidChange with changes
549
558
550 // HAXX: we need to reconstruct the state as it was before the changes..
559 // HAXX: we need to reconstruct the state as it was before the changes..
551− let old_state = std::mem::replace(&mut cx.view.doc.old_state, cx.view.doc.state.clone());
560+ let old_state = cx
561+ .view
562+ .doc
563+ .old_state
564+ .take()
565+ .expect("no old_state available");
566+
552 // TODO: take transaction by value?
567 // TODO: take transaction by value?
553 cx.view
568 cx.view
554 .doc
569 .doc
@@ -649,6 +664,7 @@ pub fn undo(cx: &mut Context) {
649 if let Some(revert) = cx.view.doc.history.undo() {
664 if let Some(revert) = cx.view.doc.history.undo() {
650 cx.view.doc.version += 1;
665 cx.view.doc.version += 1;
651 cx.view.doc.apply(&revert);
666 cx.view.doc.apply(&revert);
667+ // TODO: undo/redo needs to avoid storing in self.changes/self.old_state
652 }
668 }
653
669
654 // TODO: each command could simply return a Option<transaction>, then the higher level handles storing it?
670 // TODO: each command could simply return a Option<transaction>, then the higher level handles storing it?
helix-view/src/document.rs+8−2
@@ -30,7 +30,7 @@ pub struct Document {
30
30
31 /// Pending changes since last history commit.
31 /// Pending changes since last history commit.
32 pub changes: ChangeSet,
32 pub changes: ChangeSet,
33− pub old_state: State,
33+ pub old_state: Option<State>,
34 pub history: History,
34 pub history: History,
35 pub version: i32, // should be usize?
35 pub version: i32, // should be usize?
36
36
@@ -58,7 +58,7 @@ use url::Url;
58impl Document {
58impl Document {
59 fn new(state: State) -> Self {
59 fn new(state: State) -> Self {
60 let changes = ChangeSet::new(&state.doc);
60 let changes = ChangeSet::new(&state.doc);
61− let old_state = state.clone();
61+ let old_state = None;
62
62
63 Self {
63 Self {
64 path: None,
64 path: None,
@@ -156,6 +156,12 @@ impl Document {
156 pub fn apply(&mut self, transaction: &Transaction) -> bool {
156 pub fn apply(&mut self, transaction: &Transaction) -> bool {
157 let old_doc = self.text().clone();
157 let old_doc = self.text().clone();
158
158
159+ // store the state just before any changes are made. This allows us to undo to the
160+ // state just before a transaction was applied.
161+ if self.changes.is_empty() && !transaction.changes().is_empty() {
162+ self.old_state = Some(self.state.clone());
163+ }
164+
159 let success = transaction.apply(&mut self.state);
165 let success = transaction.apply(&mut self.state);
160
166
161 if !transaction.changes().is_empty() {
167 if !transaction.changes().is_empty() {