Skip to content

Fix several major undo/redo issues - #979

Merged
Leonard Hecker (lhecker) merged 3 commits into
mainfrom
dev/lhecker/undo-fixes
Oct 6, 2026
Merged

Leonard Hecker (lhecker) merged 3 commits into
mainfrom
dev/lhecker/undo-fixes

Conversation

@lhecker

Copy link
Copy Markdown
Member
  • Fixed Bug in undo/redo with additional new lines #834: Undo/redo corrupting text when the automatic final
    newline is inserted. This caused a non-adjacent edit_write call in
    an edit session, which was forbidden. We now check for non-adjacency.
  • Documents will not appear as saved after undoing an edit and typing
    something different anymore. E.g. "a", save, undo, "b" used to appear
    as saved, because the generation got reset on undo. Fixed by never
    decrementing the GapBuffer generation anymore.
  • Saving now ends the current typing group, so you can undo back to
    exactly what you saved.
  • Grouped commands no longer merge with other (non-adjacent) edits.
  • Removed the undo history limit, which could discard parts of a large
    grouped operation (e.g. replace-all).
  • Typing after an undo now always discards the redo history instead of
    occasionally merging into an older edit.
  • Fixed grapheme clustering during editing and undo/redo. E.g. "\rX\n
    and deleting "X" would previously get the entire editor permanently
    out of sync with reality.
  • Previously typing with word-wrap enabled, resizing the window,
    and then undoing would restore the layout before the resize and get
    the entire editor out of sync until the next resize. That's fixed.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok I think I understand it well enough. I don't exactly see how it solves all the issues, but I trust it to an extent and have looked at every line

Comment thread crates/edit/src/buffer/gap_buffer.rs Outdated
/// Gap length.
gap_len: usize,
/// Increments every time the buffer is modified.
/// The current text revision. Technically, this belongs into `TextBuffer`,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: belongs to

pub fn mark_as_dirty(&mut self) {
self.last_save_generation = self.buffer.generation().wrapping_sub(1);
// NOTE: This technically may collide after 2^32 edits. Is that a realistic concern?
self.last_save_generation = u32::MAX;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this so much cheaper than an Option<u32> that it's worth the minimal risk?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, rather it's just a PITA to do this way. GapBuffer counts in u32 generations, right? (FWIW I did this to better support 32-bit archs, but I wouldn't mind just going with u64 on all archs. That makes wraparound effectively impossible.)

So what is a new, clean buffer? The gap buffer generation is 0. What is last_save_generation? None? None is not the same as gap buffer's Some(0)! You see where I'm getting at? PITA.

/// It records the change in the undo stack.
fn edit_write(&mut self, text: &[u8]) {
let logical_y_before = self.cursor.logical_pos.y;
if text.is_empty() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this indicate programmer error? Or is there a chance we get in here? Maybe an empty bracketed paste?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a purely defensive addition. The function explodes with empty strings.

@lhecker
Leonard Hecker (lhecker) merged commit 6118429 into main Oct 6, 2026
6 checks passed
@lhecker
Leonard Hecker (lhecker) deleted the dev/lhecker/undo-fixes branch October 6, 2026 21:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug in undo/redo with additional new lines

2 participants