Apply transactions to Views

This change adds View::apply calls for all Document::apply call-sites,
ensuring that changes to a document do not leave invalid entries in
the View's jumplist.
This commit is contained in:
Michael Davis 2022-10-10 14:56:26 -05:00 committed by Blaž Hrastnik
parent d418f0795d
commit 0aedef0333
6 changed files with 58 additions and 20 deletions

View file

@ -860,6 +860,7 @@ fn align_selections(cx: &mut Context) {
let transaction = Transaction::change(doc.text(), changes.into_iter()); let transaction = Transaction::change(doc.text(), changes.into_iter());
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
} }
fn goto_window(cx: &mut Context, align: Align) { fn goto_window(cx: &mut Context, align: Align) {
@ -1290,6 +1291,7 @@ fn replace(cx: &mut Context) {
}); });
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
} }
}) })
} }
@ -1307,6 +1309,7 @@ where
}); });
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
} }
fn switch_case(cx: &mut Context) { fn switch_case(cx: &mut Context) {
@ -2113,6 +2116,7 @@ fn delete_selection_impl(cx: &mut Context, op: Operation) {
(range.from(), range.to(), None) (range.from(), range.to(), None)
}); });
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
match op { match op {
Operation::Delete => { Operation::Delete => {
@ -2126,7 +2130,7 @@ fn delete_selection_impl(cx: &mut Context, op: Operation) {
} }
#[inline] #[inline]
fn delete_selection_insert_mode(doc: &mut Document, view: &View, selection: &Selection) { fn delete_selection_insert_mode(doc: &mut Document, view: &mut View, selection: &Selection) {
let view_id = view.id; let view_id = view.id;
// then delete // then delete
@ -2134,6 +2138,7 @@ fn delete_selection_insert_mode(doc: &mut Document, view: &View, selection: &Sel
(range.from(), range.to(), None) (range.from(), range.to(), None)
}); });
doc.apply(&transaction, view_id); doc.apply(&transaction, view_id);
view.apply(&transaction, doc);
} }
fn delete_selection(cx: &mut Context) { fn delete_selection(cx: &mut Context) {
@ -2230,6 +2235,7 @@ fn append_mode(cx: &mut Context) {
[(end, end, Some(doc.line_ending.as_str().into()))].into_iter(), [(end, end, Some(doc.line_ending.as_str().into()))].into_iter(),
); );
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
} }
let selection = doc.selection(view.id).clone().transform(|range| { let selection = doc.selection(view.id).clone().transform(|range| {
@ -2530,6 +2536,7 @@ async fn make_format_callback(
let view = view_mut!(editor); let view = view_mut!(editor);
if doc.version() == doc_version { if doc.version() == doc_version {
doc.apply(&format, view.id); doc.apply(&format, view.id);
view.apply(&format, doc);
doc.append_changes_to_history(view.id); doc.append_changes_to_history(view.id);
doc.detect_indent_and_line_ending(); doc.detect_indent_and_line_ending();
view.ensure_cursor_in_view(doc, scrolloff); view.ensure_cursor_in_view(doc, scrolloff);
@ -2617,6 +2624,7 @@ fn open(cx: &mut Context, open: Open) {
transaction = transaction.with_selection(Selection::new(ranges, selection.primary_index())); transaction = transaction.with_selection(Selection::new(ranges, selection.primary_index()));
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
} }
// o inserts a new line after each line with a selection // o inserts a new line after each line with a selection
@ -2637,7 +2645,7 @@ fn normal_mode(cx: &mut Context) {
cx.editor.mode = Mode::Normal; cx.editor.mode = Mode::Normal;
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
try_restore_indent(doc, view.id); try_restore_indent(doc, view);
// if leaving append mode, move cursor back by 1 // if leaving append mode, move cursor back by 1
if doc.restore_cursor { if doc.restore_cursor {
@ -2654,7 +2662,7 @@ fn normal_mode(cx: &mut Context) {
} }
} }
fn try_restore_indent(doc: &mut Document, view_id: ViewId) { fn try_restore_indent(doc: &mut Document, view: &mut View) {
use helix_core::chars::char_is_whitespace; use helix_core::chars::char_is_whitespace;
use helix_core::Operation; use helix_core::Operation;
@ -2673,18 +2681,19 @@ fn try_restore_indent(doc: &mut Document, view_id: ViewId) {
let doc_changes = doc.changes().changes(); let doc_changes = doc.changes().changes();
let text = doc.text().slice(..); let text = doc.text().slice(..);
let range = doc.selection(view_id).primary(); let range = doc.selection(view.id).primary();
let pos = range.cursor(text); let pos = range.cursor(text);
let line_end_pos = line_end_char_index(&text, range.cursor_line(text)); let line_end_pos = line_end_char_index(&text, range.cursor_line(text));
if inserted_a_new_blank_line(doc_changes, pos, line_end_pos) { if inserted_a_new_blank_line(doc_changes, pos, line_end_pos) {
// Removes tailing whitespaces. // Removes tailing whitespaces.
let transaction = let transaction =
Transaction::change_by_selection(doc.text(), doc.selection(view_id), |range| { Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| {
let line_start_pos = text.line_to_char(range.cursor_line(text)); let line_start_pos = text.line_to_char(range.cursor_line(text));
(line_start_pos, pos, None) (line_start_pos, pos, None)
}); });
doc.apply(&transaction, view_id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
} }
} }
@ -2999,6 +3008,7 @@ pub mod insert {
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
if let Some(t) = transaction { if let Some(t) = transaction {
doc.apply(&t, view.id); doc.apply(&t, view.id);
view.apply(&t, doc);
} }
// TODO: need a post insert hook too for certain triggers (autocomplete, signature help, etc) // TODO: need a post insert hook too for certain triggers (autocomplete, signature help, etc)
@ -3021,6 +3031,7 @@ pub mod insert {
indent, indent,
); );
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
} }
pub fn insert_newline(cx: &mut Context) { pub fn insert_newline(cx: &mut Context) {
@ -3108,6 +3119,7 @@ pub mod insert {
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
} }
pub fn delete_char_backward(cx: &mut Context) { pub fn delete_char_backward(cx: &mut Context) {
@ -3202,6 +3214,7 @@ pub mod insert {
}); });
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
lsp::signature_help_impl(cx, SignatureHelpInvoked::Automatic); lsp::signature_help_impl(cx, SignatureHelpInvoked::Automatic);
} }
@ -3220,6 +3233,7 @@ pub mod insert {
) )
}); });
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
lsp::signature_help_impl(cx, SignatureHelpInvoked::Automatic); lsp::signature_help_impl(cx, SignatureHelpInvoked::Automatic);
} }
@ -3413,7 +3427,7 @@ enum Paste {
Cursor, Cursor,
} }
fn paste_impl(values: &[String], doc: &mut Document, view: &View, action: Paste, count: usize) { fn paste_impl(values: &[String], doc: &mut Document, view: &mut View, action: Paste, count: usize) {
let repeat = std::iter::repeat( let repeat = std::iter::repeat(
values values
.last() .last()
@ -3457,6 +3471,7 @@ fn paste_impl(values: &[String], doc: &mut Document, view: &View, action: Paste,
(pos, pos, values.next()) (pos, pos, values.next())
}); });
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
} }
pub(crate) fn paste_bracketed_value(cx: &mut Context, contents: String) { pub(crate) fn paste_bracketed_value(cx: &mut Context, contents: String) {
@ -3549,6 +3564,7 @@ fn replace_with_yanked(cx: &mut Context) {
}); });
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
} }
} }
} }
@ -3572,6 +3588,7 @@ fn replace_selections_with_clipboard_impl(
}); });
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
doc.append_changes_to_history(view.id); doc.append_changes_to_history(view.id);
Ok(()) Ok(())
} }
@ -3642,6 +3659,7 @@ fn indent(cx: &mut Context) {
}), }),
); );
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
} }
fn unindent(cx: &mut Context) { fn unindent(cx: &mut Context) {
@ -3681,6 +3699,7 @@ fn unindent(cx: &mut Context) {
let transaction = Transaction::change(doc.text(), changes.into_iter()); let transaction = Transaction::change(doc.text(), changes.into_iter());
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
} }
fn format_selections(cx: &mut Context) { fn format_selections(cx: &mut Context) {
@ -3728,6 +3747,7 @@ fn format_selections(cx: &mut Context) {
// ); // );
// doc.apply(&transaction, view.id); // doc.apply(&transaction, view.id);
// view.apply(&transaction, doc);
} }
} }
@ -3783,6 +3803,7 @@ fn join_selections_inner(cx: &mut Context, select_space: bool) {
}; };
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
} }
fn keep_or_remove_selections_impl(cx: &mut Context, remove: bool) { fn keep_or_remove_selections_impl(cx: &mut Context, remove: bool) {
@ -3936,6 +3957,7 @@ fn toggle_comments(cx: &mut Context) {
let transaction = comment::toggle_line_comments(doc.text(), doc.selection(view.id), token); let transaction = comment::toggle_line_comments(doc.text(), doc.selection(view.id), token);
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
exit_select_mode(cx); exit_select_mode(cx);
} }
@ -3992,6 +4014,7 @@ fn rotate_selection_contents(cx: &mut Context, direction: Direction) {
); );
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
} }
fn rotate_selection_contents_forward(cx: &mut Context) { fn rotate_selection_contents_forward(cx: &mut Context) {
@ -4488,6 +4511,7 @@ fn surround_add(cx: &mut Context) {
let transaction = Transaction::change(doc.text(), changes.into_iter()); let transaction = Transaction::change(doc.text(), changes.into_iter());
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
}) })
} }
@ -4527,6 +4551,7 @@ fn surround_replace(cx: &mut Context) {
}), }),
); );
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
}); });
}) })
} }
@ -4554,6 +4579,7 @@ fn surround_delete(cx: &mut Context) {
let transaction = let transaction =
Transaction::change(doc.text(), change_pos.into_iter().map(|p| (p, p + 1, None))); Transaction::change(doc.text(), change_pos.into_iter().map(|p| (p, p + 1, None)));
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
}) })
} }
@ -4729,6 +4755,7 @@ fn shell(cx: &mut compositor::Context, cmd: &str, behavior: &ShellBehavior) {
if behavior != &ShellBehavior::Ignore { if behavior != &ShellBehavior::Ignore {
let transaction = Transaction::change(doc.text(), changes.into_iter()); let transaction = Transaction::change(doc.text(), changes.into_iter());
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
doc.append_changes_to_history(view.id); doc.append_changes_to_history(view.id);
} }
@ -4792,6 +4819,7 @@ fn add_newline_impl(cx: &mut Context, open: Open) {
let transaction = Transaction::change(text, changes); let transaction = Transaction::change(text, changes);
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
} }
/// Increment object under cursor by count. /// Increment object under cursor by count.
@ -4885,6 +4913,7 @@ fn increment_impl(cx: &mut Context, amount: i64) {
let transaction = transaction.with_selection(selection.clone()); let transaction = transaction.with_selection(selection.clone());
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
} }
} }

View file

@ -596,9 +596,7 @@ pub fn apply_workspace_edit(
} }
}; };
let doc = editor let doc = doc_mut!(editor, &doc_id);
.document_mut(doc_id)
.expect("Document for document_changes not found");
// Need to determine a view for apply/append_changes_to_history // Need to determine a view for apply/append_changes_to_history
let selections = doc.selections(); let selections = doc.selections();
@ -620,6 +618,7 @@ pub fn apply_workspace_edit(
offset_encoding, offset_encoding,
); );
doc.apply(&transaction, view_id); doc.apply(&transaction, view_id);
view_mut!(editor, view_id).apply(&transaction, doc);
doc.append_changes_to_history(view_id); doc.append_changes_to_history(view_id);
}; };

View file

@ -463,6 +463,7 @@ fn set_line_ending(
}), }),
); );
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
doc.append_changes_to_history(view.id); doc.append_changes_to_history(view.id);
Ok(()) Ok(())
@ -884,6 +885,7 @@ fn replace_selections_with_clipboard_impl(
}); });
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
doc.append_changes_to_history(view.id); doc.append_changes_to_history(view.id);
Ok(()) Ok(())
} }
@ -1004,7 +1006,7 @@ fn reload(
let scrolloff = cx.editor.config().scrolloff; let scrolloff = cx.editor.config().scrolloff;
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);
doc.reload(view.id).map(|_| { doc.reload(view).map(|_| {
view.ensure_cursor_in_view(doc, scrolloff); view.ensure_cursor_in_view(doc, scrolloff);
}) })
} }
@ -1399,6 +1401,7 @@ fn sort_impl(
); );
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
doc.append_changes_to_history(view.id); doc.append_changes_to_history(view.id);
Ok(()) Ok(())
@ -1443,6 +1446,7 @@ fn reflow(
}); });
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
doc.append_changes_to_history(view.id); doc.append_changes_to_history(view.id);
view.ensure_cursor_in_view(doc, scrolloff); view.ensure_cursor_in_view(doc, scrolloff);

View file

@ -143,11 +143,11 @@ impl Completion {
let (view, doc) = current!(editor); let (view, doc) = current!(editor);
// if more text was entered, remove it // if more text was entered, remove it
doc.restore(view.id); doc.restore(view);
match event { match event {
PromptEvent::Abort => { PromptEvent::Abort => {
doc.restore(view.id); doc.restore(view);
editor.last_completion = None; editor.last_completion = None;
} }
PromptEvent::Update => { PromptEvent::Update => {
@ -165,6 +165,7 @@ impl Completion {
// initialize a savepoint // initialize a savepoint
doc.savepoint(); doc.savepoint();
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
editor.last_completion = Some(CompleteAction { editor.last_completion = Some(CompleteAction {
trigger_offset, trigger_offset,
@ -184,6 +185,7 @@ impl Completion {
); );
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
editor.last_completion = Some(CompleteAction { editor.last_completion = Some(CompleteAction {
trigger_offset, trigger_offset,
@ -214,6 +216,7 @@ impl Completion {
offset_encoding, // TODO: should probably transcode in Client offset_encoding, // TODO: should probably transcode in Client
); );
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
view.apply(&transaction, doc);
} }
} }
} }

View file

@ -988,7 +988,7 @@ impl EditorView {
InsertEvent::CompletionApply(compl) => { InsertEvent::CompletionApply(compl) => {
let (view, doc) = current!(cxt.editor); let (view, doc) = current!(cxt.editor);
doc.restore(view.id); doc.restore(view);
let text = doc.text().slice(..); let text = doc.text().slice(..);
let cursor = doc.selection(view.id).primary().cursor(text); let cursor = doc.selection(view.id).primary().cursor(text);
@ -1003,6 +1003,7 @@ impl EditorView {
}), }),
); );
doc.apply(&tx, view.id); doc.apply(&tx, view.id);
view.apply(&tx, doc);
} }
InsertEvent::TriggerCompletion => { InsertEvent::TriggerCompletion => {
let (_, doc) = current!(cxt.editor); let (_, doc) = current!(cxt.editor);

View file

@ -24,7 +24,7 @@ use helix_core::{
DEFAULT_LINE_ENDING, DEFAULT_LINE_ENDING,
}; };
use crate::{DocumentId, Editor, ViewId}; use crate::{DocumentId, Editor, View, ViewId};
/// 8kB of buffer space for encoding and decoding `Rope`s. /// 8kB of buffer space for encoding and decoding `Rope`s.
const BUF_SIZE: usize = 8192; const BUF_SIZE: usize = 8192;
@ -601,7 +601,7 @@ impl Document {
} }
/// Reload the document from its path. /// Reload the document from its path.
pub fn reload(&mut self, view_id: ViewId) -> Result<(), Error> { pub fn reload(&mut self, view: &mut View) -> Result<(), Error> {
let encoding = &self.encoding; let encoding = &self.encoding;
let path = self.path().filter(|path| path.exists()); let path = self.path().filter(|path| path.exists());
@ -617,8 +617,9 @@ impl Document {
// This is not considered a modification of the contents of the file regardless // This is not considered a modification of the contents of the file regardless
// of the encoding. // of the encoding.
let transaction = helix_core::diff::compare_ropes(self.text(), &rope); let transaction = helix_core::diff::compare_ropes(self.text(), &rope);
self.apply(&transaction, view_id); self.apply(&transaction, view.id);
self.append_changes_to_history(view_id); view.apply(&transaction, self);
self.append_changes_to_history(view.id);
self.reset_modified(); self.reset_modified();
self.detect_indent_and_line_ending(); self.detect_indent_and_line_ending();
@ -862,9 +863,10 @@ impl Document {
self.savepoint = Some(Transaction::new(self.text())); self.savepoint = Some(Transaction::new(self.text()));
} }
pub fn restore(&mut self, view_id: ViewId) { pub fn restore(&mut self, view: &mut View) {
if let Some(revert) = self.savepoint.take() { if let Some(revert) = self.savepoint.take() {
self.apply(&revert, view_id); self.apply(&revert, view.id);
view.apply(&revert, self);
} }
} }