From 44d2fc2ab3217e50fd23f67ba93135d62c9acd76 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Sat, 13 Jul 2024 12:59:47 -0500 Subject: [PATCH] Commit an undo checkpoint before each write (#11062) This fixes the modification indicator when saving from insert mode with a config such as [keys.insert] C-s = ":write" Previously the modification indicator would be stuck showing modified even if the buffer contents matched the disk contents when writing after some changes in insert mode with this binding. In insert mode we do not eagerly write undo checkpoints so that all changes made become one checkpoint as you exit insert mode. When saving, `Document`s `changes` `ChangeSet` would be non-empty and when there are changes we show the buffer as modified. Then switching to normal mode would append the changes to history, bumping the current revision past what it was when last saved. Since the last saved revision and current revision were then unsynced, the modification indicator would always show modified. This matches [Kakoune's behavior]. Kakoune has a different architecture for writes but a very similar system for history, transactions and undo checkpoints (what it calls "undo groups"). Upon saving Kakoune creates an undo checkpoint if there are any uncommitted changes. It does this after the write has gone through since its writing system is different. For our writing system it's cleaner to make the undo checkpoint before performing the save so that the history revision increments before we send the save event. [Kakoune's behavior]: https://github.com/mawww/kakoune/blob/80fcfebca8c62ace6cf2af9487784486af07d2d5/src/buffer.cc#L565-L566 --- helix-term/src/commands/typed.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 032f016f5..ed1547f1f 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -340,9 +340,12 @@ fn write_impl( let path = path.map(AsRef::as_ref); if config.insert_final_newline { - insert_final_newline(doc, view); + insert_final_newline(doc, view.id); } + // Save an undo checkpoint for any outstanding changes. + doc.append_changes_to_history(view); + let fmt = if config.auto_format { doc.auto_format().map(|fmt| { let callback = make_format_callback( @@ -367,13 +370,12 @@ fn write_impl( Ok(()) } -fn insert_final_newline(doc: &mut Document, view: &mut View) { +fn insert_final_newline(doc: &mut Document, view_id: ViewId) { let text = doc.text(); if line_ending::get_line_ending(&text.slice(..)).is_none() { let eof = Selection::point(text.len_chars()); let insert = Transaction::insert(text, &eof, doc.line_ending.as_str().into()); - doc.apply(&insert, view.id); - doc.append_changes_to_history(view); + doc.apply(&insert, view_id); } } @@ -704,11 +706,15 @@ pub fn write_all_impl( for (doc_id, target_view) in saves { let doc = doc_mut!(cx.editor, &doc_id); + let view = view_mut!(cx.editor, target_view); if config.insert_final_newline { - insert_final_newline(doc, view_mut!(cx.editor, target_view)); + insert_final_newline(doc, target_view); } + // Save an undo checkpoint for any outstanding changes. + doc.append_changes_to_history(view); + let fmt = if config.auto_format { doc.auto_format().map(|fmt| { let callback = make_format_callback(