From 3476716e7913589483d105020db2936e10c295a7 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Sun, 13 Nov 2022 11:59:23 -0600 Subject: [PATCH] Apply transactions to all views Previously, transactions were only applied to the currently focused view. This could lead to jumplist selections not being updated or panics if a jumplist selection pointed past the end of the document. This change moves the application of transactions to the Editor which can apply the transaction to the document and all views, ensuring that jumplist entries are updated even if a document is open in multiple windows. This complicates most callers of the removed `helix_view::apply_transaction` helper function. Previously, callers could take mutable references of the view and doc at the beginning of a function and then pass them to the helper. Now they must take immutable references or refresh the mutable references after calling `Editor::apply_transaction` to avoid taking multiple mutable references of Editor. A new macro `current_ids` has been added to support a new common case where we only care about the currently focused document's and view's IDs. --- helix-term/src/commands.rs | 211 +++++++++++++++++-------------- helix-term/src/commands/lsp.rs | 7 +- helix-term/src/commands/typed.rs | 67 +++++----- helix-term/src/ui/completion.rs | 23 ++-- helix-term/src/ui/editor.rs | 9 +- helix-view/src/document.rs | 72 ++++++----- helix-view/src/editor.rs | 90 ++++++++++++- helix-view/src/lib.rs | 13 -- helix-view/src/macros.rs | 12 +- 9 files changed, 302 insertions(+), 202 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index c74dfb39cefcb..ea8299455b189 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -1,3 +1,8 @@ +// TODO: remove this when the MSRV is 1.62.0. +// This warning is benign and is removed in 1.62.0. +// See https://github.com/rust-lang/rust/pull/96268 +#![allow(mutable_borrow_reservation_conflict)] + pub(crate) mod dap; pub(crate) mod lsp; pub(crate) mod typed; @@ -27,7 +32,6 @@ use helix_core::{ SmallVec, Tendril, Transaction, }; use helix_view::{ - apply_transaction, clipboard::ClipboardType, document::{FormatterError, Mode, SCRATCH_BUFFER_NAME}, editor::{Action, Motion}, @@ -697,7 +701,7 @@ fn extend_to_line_start(cx: &mut Context) { } fn kill_to_line_start(cx: &mut Context) { - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); let text = doc.text().slice(..); let selection = doc.selection(view.id).clone().transform(|range| { @@ -721,13 +725,13 @@ fn kill_to_line_start(cx: &mut Context) { }; Range::new(head, anchor) }); - delete_selection_insert_mode(doc, view, &selection); + delete_selection_insert_mode(cx.editor, &selection); lsp::signature_help_impl(cx, SignatureHelpInvoked::Automatic); } fn kill_to_line_end(cx: &mut Context) { - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); let text = doc.text().slice(..); let selection = doc.selection(view.id).clone().transform(|range| { @@ -742,7 +746,7 @@ fn kill_to_line_end(cx: &mut Context) { } new_range }); - delete_selection_insert_mode(doc, view, &selection); + delete_selection_insert_mode(cx.editor, &selection); lsp::signature_help_impl(cx, SignatureHelpInvoked::Automatic); } @@ -802,9 +806,10 @@ fn trim_selections(cx: &mut Context) { // align text in selection fn align_selections(cx: &mut Context) { - let (view, doc) = current!(cx.editor); + let (view_id, doc_id) = current_ids!(cx.editor); + let doc = doc!(cx.editor, &doc_id); let text = doc.text().slice(..); - let selection = doc.selection(view.id); + let selection = doc.selection(view_id); let tab_width = doc.tab_width(); let mut column_widths: Vec> = Vec::new(); @@ -862,7 +867,7 @@ fn align_selections(cx: &mut Context) { changes.sort_unstable_by_key(|(from, _, _)| *from); let transaction = Transaction::change(doc.text(), changes.into_iter()); - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc_id, view_id); } fn goto_window(cx: &mut Context, align: Align) { @@ -1270,7 +1275,7 @@ fn replace(cx: &mut Context) { // need to wait for next key cx.on_next_key(move |cx, event| { - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); let ch: Option<&str> = match event { KeyEvent { code: KeyCode::Char(ch), @@ -1307,7 +1312,7 @@ fn replace(cx: &mut Context) { } }); - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); exit_select_mode(cx); } }) @@ -1317,7 +1322,7 @@ fn switch_case_impl(cx: &mut Context, change_fn: F) where F: Fn(RopeSlice) -> Tendril, { - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); let selection = doc.selection(view.id); let transaction = Transaction::change_by_selection(doc.text(), selection, |range| { let text: Tendril = change_fn(range.slice(doc.text().slice(..))); @@ -1325,7 +1330,7 @@ where (range.from(), range.to(), Some(text)) }); - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); } fn switch_case(cx: &mut Context) { @@ -2119,7 +2124,7 @@ enum Operation { } fn delete_selection_impl(cx: &mut Context, op: Operation) { - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); let text = doc.text().slice(..); let selection = doc.selection(view.id); @@ -2137,7 +2142,7 @@ fn delete_selection_impl(cx: &mut Context, op: Operation) { let transaction = Transaction::change_by_selection(doc.text(), selection, |range| { (range.from(), range.to(), None) }); - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); match op { Operation::Delete => { @@ -2151,11 +2156,12 @@ fn delete_selection_impl(cx: &mut Context, op: Operation) { } #[inline] -fn delete_selection_insert_mode(doc: &mut Document, view: &mut View, selection: &Selection) { +fn delete_selection_insert_mode(editor: &mut Editor, selection: &Selection) { + let (view, doc) = current_ref!(editor); let transaction = Transaction::change_by_selection(doc.text(), selection, |range| { (range.from(), range.to(), None) }); - apply_transaction(&transaction, doc, view); + editor.apply_transaction(&transaction, doc.id(), view.id); } fn delete_selection(cx: &mut Context) { @@ -2234,8 +2240,8 @@ fn insert_mode(cx: &mut Context) { // inserts at the end of each selection fn append_mode(cx: &mut Context) { enter_insert_mode(cx); - let (view, doc) = current!(cx.editor); - doc.restore_cursor = true; + doc_mut!(cx.editor).restore_cursor = true; + let (view, doc) = current_ref!(cx.editor); let text = doc.text().slice(..); // Make sure there's room at the end of the document if the last @@ -2251,9 +2257,10 @@ fn append_mode(cx: &mut Context) { doc.text(), [(end, end, Some(doc.line_ending.as_str().into()))].into_iter(), ); - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); } + let (view, doc) = current!(cx.editor); let selection = doc.selection(view.id).clone().transform(|range| { Range::new( range.from(), @@ -2542,12 +2549,11 @@ async fn make_format_callback( } let scrolloff = editor.config().scrolloff; - let doc = doc_mut!(editor, &doc_id); - let view = view_mut!(editor, view_id); if let Ok(format) = format { - if doc.version() == doc_version { - apply_transaction(&format, doc, view); + if doc!(editor, &doc_id).version() == doc_version { + editor.apply_transaction(&format, doc_id, view_id); + let (view, doc) = current!(editor); doc.append_changes_to_history(view.id); doc.detect_indent_and_line_ending(); view.ensure_cursor_in_view(doc, scrolloff); @@ -2557,8 +2563,7 @@ async fn make_format_callback( } if let Some((path, force)) = write { - let id = doc.id(); - if let Err(err) = editor.save(id, path, force) { + if let Err(err) = editor.save(doc_id, path, force) { editor.set_error(format!("Error saving: {}", err)); } } @@ -2576,7 +2581,7 @@ pub enum Open { fn open(cx: &mut Context, open: Open) { let count = cx.count(); enter_insert_mode(cx); - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); let text = doc.text().slice(..); let contents = doc.text(); @@ -2640,7 +2645,7 @@ fn open(cx: &mut Context, open: Open) { transaction = transaction.with_selection(Selection::new(ranges, selection.primary_index())); - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); } // o inserts a new line after each line with a selection @@ -2659,9 +2664,10 @@ fn normal_mode(cx: &mut Context) { } cx.editor.mode = Mode::Normal; - let (view, doc) = current!(cx.editor); - try_restore_indent(doc, view); + try_restore_indent(cx.editor); + + let (view, doc) = current!(cx.editor); // if leaving append mode, move cursor back by 1 if doc.restore_cursor { @@ -2678,7 +2684,7 @@ fn normal_mode(cx: &mut Context) { } } -fn try_restore_indent(doc: &mut Document, view: &mut View) { +fn try_restore_indent(editor: &mut Editor) { use helix_core::chars::char_is_whitespace; use helix_core::Operation; @@ -2695,6 +2701,8 @@ fn try_restore_indent(doc: &mut Document, view: &mut View) { } } + let (view, doc) = current_ref!(editor); + let doc_changes = doc.changes().changes(); let text = doc.text().slice(..); let range = doc.selection(view.id).primary(); @@ -2708,7 +2716,7 @@ fn try_restore_indent(doc: &mut Document, view: &mut View) { let line_start_pos = text.line_to_char(range.cursor_line(text)); (line_start_pos, pos, None) }); - apply_transaction(&transaction, doc, view); + editor.apply_transaction(&transaction, doc.id(), view.id); } } @@ -3020,9 +3028,8 @@ pub mod insert { .and_then(|ap| auto_pairs::hook(text, selection, c, ap)) .or_else(|| insert(text, selection, c)); - let (view, doc) = current!(cx.editor); - if let Some(t) = transaction { - apply_transaction(&t, doc, view); + if let Some(tx) = transaction { + cx.editor.apply_transaction(&tx, doc.id(), view.id); } // TODO: need a post insert hook too for certain triggers (autocomplete, signature help, etc) @@ -3034,7 +3041,7 @@ pub mod insert { } pub fn insert_tab(cx: &mut Context) { - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); // TODO: round out to nearest indentation level (for example a line with 3 spaces should // indent by one to reach 4 spaces). @@ -3044,7 +3051,7 @@ pub mod insert { &doc.selection(view.id).clone().cursors(doc.text().slice(..)), indent, ); - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); } pub fn insert_newline(cx: &mut Context) { @@ -3130,8 +3137,7 @@ pub mod insert { transaction = transaction.with_selection(Selection::new(ranges, selection.primary_index())); - let (view, doc) = current!(cx.editor); - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); } pub fn delete_char_backward(cx: &mut Context) { @@ -3225,15 +3231,15 @@ pub mod insert { } } }); - let (view, doc) = current!(cx.editor); - apply_transaction(&transaction, doc, view); + let (view, doc) = current_ref!(cx.editor); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); lsp::signature_help_impl(cx, SignatureHelpInvoked::Automatic); } pub fn delete_char_forward(cx: &mut Context) { let count = cx.count(); - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); let text = doc.text().slice(..); let transaction = Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| { @@ -3244,14 +3250,14 @@ pub mod insert { None, ) }); - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); lsp::signature_help_impl(cx, SignatureHelpInvoked::Automatic); } pub fn delete_word_backward(cx: &mut Context) { let count = cx.count(); - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); let text = doc.text().slice(..); let selection = doc.selection(view.id).clone().transform(|range| { @@ -3259,14 +3265,14 @@ pub mod insert { let next = Range::new(anchor, range.cursor(text)); exclude_cursor(text, next, range) }); - delete_selection_insert_mode(doc, view, &selection); + delete_selection_insert_mode(cx.editor, &selection); lsp::signature_help_impl(cx, SignatureHelpInvoked::Automatic); } pub fn delete_word_forward(cx: &mut Context) { let count = cx.count(); - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); let text = doc.text().slice(..); let selection = doc.selection(view.id).clone().transform(|range| { @@ -3274,7 +3280,7 @@ pub mod insert { Range::new(range.cursor(text), head) }); - delete_selection_insert_mode(doc, view, &selection); + delete_selection_insert_mode(cx.editor, &selection); lsp::signature_help_impl(cx, SignatureHelpInvoked::Automatic); } @@ -3284,9 +3290,9 @@ pub mod insert { fn undo(cx: &mut Context) { let count = cx.count(); - let (view, doc) = current!(cx.editor); + let (view_id, doc_id) = current_ids!(cx.editor); for _ in 0..count { - if !doc.undo(view) { + if !cx.editor.undo(doc_id, view_id) { cx.editor.set_status("Already at oldest change"); break; } @@ -3295,9 +3301,9 @@ fn undo(cx: &mut Context) { fn redo(cx: &mut Context) { let count = cx.count(); - let (view, doc) = current!(cx.editor); + let (view_id, doc_id) = current_ids!(cx.editor); for _ in 0..count { - if !doc.redo(view) { + if !cx.editor.redo(doc_id, view_id) { cx.editor.set_status("Already at newest change"); break; } @@ -3306,10 +3312,10 @@ fn redo(cx: &mut Context) { fn earlier(cx: &mut Context) { let count = cx.count(); - let (view, doc) = current!(cx.editor); + let (view_id, doc_id) = current_ids!(cx.editor); for _ in 0..count { // rather than doing in batch we do this so get error halfway - if !doc.earlier(view, UndoKind::Steps(1)) { + if !cx.editor.earlier(doc_id, view_id, UndoKind::Steps(1)) { cx.editor.set_status("Already at oldest change"); break; } @@ -3318,10 +3324,10 @@ fn earlier(cx: &mut Context) { fn later(cx: &mut Context) { let count = cx.count(); - let (view, doc) = current!(cx.editor); + let (view_id, doc_id) = current_ids!(cx.editor); for _ in 0..count { // rather than doing in batch we do this so get error halfway - if !doc.later(view, UndoKind::Steps(1)) { + if !cx.editor.later(doc_id, view_id, UndoKind::Steps(1)) { cx.editor.set_status("Already at newest change"); break; } @@ -3450,9 +3456,15 @@ enum Paste { Cursor, } -fn paste_impl(values: &[String], doc: &mut Document, view: &mut View, action: Paste, count: usize) { +fn paste_impl( + values: &[String], + doc: &Document, + view: &View, + action: Paste, + count: usize, +) -> Transaction { if values.is_empty() { - return; + return Transaction::new(doc.text()); } let repeat = std::iter::repeat( @@ -3514,9 +3526,7 @@ fn paste_impl(values: &[String], doc: &mut Document, view: &mut View, action: Pa (pos, pos, value) }); - let transaction = transaction.with_selection(Selection::new(ranges, selection.primary_index())); - - apply_transaction(&transaction, doc, view); + transaction.with_selection(Selection::new(ranges, selection.primary_index())) } pub(crate) fn paste_bracketed_value(cx: &mut Context, contents: String) { @@ -3525,8 +3535,9 @@ pub(crate) fn paste_bracketed_value(cx: &mut Context, contents: String) { Mode::Insert | Mode::Select => Paste::Cursor, Mode::Normal => Paste::Before, }; - let (view, doc) = current!(cx.editor); - paste_impl(&[contents], doc, view, paste, count); + let (view, doc) = current_ref!(cx.editor); + let transaction = paste_impl(&[contents], doc, view, paste, count); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); } fn paste_clipboard_impl( @@ -3535,10 +3546,11 @@ fn paste_clipboard_impl( clipboard_type: ClipboardType, count: usize, ) -> anyhow::Result<()> { - let (view, doc) = current!(editor); match editor.clipboard_provider.get_contents(clipboard_type) { Ok(contents) => { - paste_impl(&[contents], doc, view, action, count); + let (view, doc) = current_ref!(editor); + let transaction = paste_impl(&[contents], doc, view, action, count); + editor.apply_transaction(&transaction, doc.id(), view.id); Ok(()) } Err(e) => Err(e.context("Couldn't get system clipboard contents")), @@ -3584,7 +3596,7 @@ fn paste_primary_clipboard_before(cx: &mut Context) { fn replace_with_yanked(cx: &mut Context) { let count = cx.count(); let reg_name = cx.register.unwrap_or('"'); - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); let registers = &mut cx.editor.registers; if let Some(values) = registers.read(reg_name) { @@ -3608,7 +3620,7 @@ fn replace_with_yanked(cx: &mut Context) { } }); - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); exit_select_mode(cx); } } @@ -3619,7 +3631,7 @@ fn replace_selections_with_clipboard_impl( clipboard_type: ClipboardType, ) -> anyhow::Result<()> { let count = cx.count(); - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); match cx.editor.clipboard_provider.get_contents(clipboard_type) { Ok(contents) => { @@ -3632,7 +3644,8 @@ fn replace_selections_with_clipboard_impl( ) }); - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); + let (view, doc) = current!(cx.editor); doc.append_changes_to_history(view.id); } Err(e) => return Err(e.context("Couldn't get system clipboard contents")), @@ -3653,12 +3666,14 @@ fn replace_selections_with_primary_clipboard(cx: &mut Context) { fn paste(cx: &mut Context, pos: Paste) { let count = cx.count(); let reg_name = cx.register.unwrap_or('"'); - let (view, doc) = current!(cx.editor); - let registers = &mut cx.editor.registers; + let (view, doc) = current_ref!(cx.editor); + let values = match cx.editor.registers.read(reg_name) { + Some(values) => values, + None => return, + }; - if let Some(values) = registers.read(reg_name) { - paste_impl(values, doc, view, pos, count); - } + let transaction = paste_impl(values, doc, view, pos, count); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); } fn paste_after(cx: &mut Context) { @@ -3687,7 +3702,7 @@ fn get_lines(doc: &Document, view_id: ViewId) -> Vec { fn indent(cx: &mut Context) { let count = cx.count(); - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); let lines = get_lines(doc, view.id); // Indent by one level @@ -3704,12 +3719,12 @@ fn indent(cx: &mut Context) { Some((pos, pos, Some(indent.clone()))) }), ); - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); } fn unindent(cx: &mut Context) { let count = cx.count(); - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); let lines = get_lines(doc, view.id); let mut changes = Vec::with_capacity(lines.len()); let tab_width = doc.tab_width(); @@ -3743,13 +3758,13 @@ fn unindent(cx: &mut Context) { let transaction = Transaction::change(doc.text(), changes.into_iter()); - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); } fn format_selections(cx: &mut Context) { use helix_lsp::{lsp, util::range_to_lsp_range}; - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); // via lsp if available // TODO: else via tree-sitter indentation calculations @@ -3792,12 +3807,12 @@ fn format_selections(cx: &mut Context) { language_server.offset_encoding(), ); - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); } fn join_selections_inner(cx: &mut Context, select_space: bool) { use movement::skip_while; - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); let text = doc.text(); let slice = doc.text().slice(..); @@ -3846,7 +3861,7 @@ fn join_selections_inner(cx: &mut Context, select_space: bool) { Transaction::change(doc.text(), changes.into_iter()) }; - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); } fn keep_or_remove_selections_impl(cx: &mut Context, remove: bool) { @@ -3989,14 +4004,14 @@ pub fn completion(cx: &mut Context) { // comments fn toggle_comments(cx: &mut Context) { - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); let token = doc .language_config() .and_then(|lc| lc.comment_token.as_ref()) .map(|tc| tc.as_ref()); let transaction = comment::toggle_line_comments(doc.text(), doc.selection(view.id), token); - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); exit_select_mode(cx); } @@ -4021,7 +4036,7 @@ fn rotate_selections_backward(cx: &mut Context) { fn rotate_selection_contents(cx: &mut Context, direction: Direction) { let count = cx.count; - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); let text = doc.text().slice(..); let selection = doc.selection(view.id); @@ -4052,7 +4067,7 @@ fn rotate_selection_contents(cx: &mut Context, direction: Direction) { .map(|(range, fragment)| (range.from(), range.to(), Some(fragment))), ); - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); } fn rotate_selection_contents_forward(cx: &mut Context) { @@ -4527,7 +4542,7 @@ fn surround_add(cx: &mut Context) { Some(ch) => ch, None => return, }; - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); let selection = doc.selection(view.id); let (open, close) = surround::get_pair(ch); @@ -4542,7 +4557,7 @@ fn surround_add(cx: &mut Context) { } let transaction = Transaction::change(doc.text(), changes.into_iter()); - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); }) } @@ -4554,7 +4569,7 @@ fn surround_replace(cx: &mut Context) { Some(ch) => Some(ch), None => return, }; - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); let text = doc.text().slice(..); let selection = doc.selection(view.id); @@ -4567,7 +4582,7 @@ fn surround_replace(cx: &mut Context) { }; cx.on_next_key(move |cx, event| { - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); let to = match event.char() { Some(to) => to, None => return, @@ -4581,7 +4596,7 @@ fn surround_replace(cx: &mut Context) { (pos, pos + 1, Some(t)) }), ); - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); }); }) } @@ -4594,7 +4609,7 @@ fn surround_delete(cx: &mut Context) { Some(ch) => Some(ch), None => return, }; - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); let text = doc.text().slice(..); let selection = doc.selection(view.id); @@ -4608,7 +4623,7 @@ fn surround_delete(cx: &mut Context) { let transaction = Transaction::change(doc.text(), change_pos.into_iter().map(|p| (p, p + 1, None))); - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); }) } @@ -4766,7 +4781,7 @@ fn shell(cx: &mut compositor::Context, cmd: &str, behavior: &ShellBehavior) { let config = cx.editor.config(); let shell = &config.shell; - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); let selection = doc.selection(view.id); let mut changes = Vec::with_capacity(selection.len()); @@ -4801,12 +4816,14 @@ fn shell(cx: &mut compositor::Context, cmd: &str, behavior: &ShellBehavior) { if behavior != &ShellBehavior::Ignore { let transaction = Transaction::change(doc.text(), changes.into_iter()) .with_selection(Selection::new(ranges, selection.primary_index())); - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); + let (view, doc) = current!(cx.editor); doc.append_changes_to_history(view.id); } // after replace cursor may be out of bounds, do this to // make sure cursor is in view and update scroll as well + let (view, doc) = current!(cx.editor); view.ensure_cursor_in_view(doc, config.scrolloff); } @@ -4844,7 +4861,7 @@ fn add_newline_below(cx: &mut Context) { fn add_newline_impl(cx: &mut Context, open: Open) { let count = cx.count(); - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); let selection = doc.selection(view.id); let text = doc.text(); let slice = text.slice(..); @@ -4864,7 +4881,7 @@ fn add_newline_impl(cx: &mut Context, open: Open) { }); let transaction = Transaction::change(text, changes); - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); } enum IncrementDirection { @@ -4925,7 +4942,7 @@ fn increment_impl(cx: &mut Context, increment_direction: IncrementDirection) { // If the register is `#` then increase or decrease the `amount` by 1 per element let increase_by = if cx.register == Some('#') { sign } else { 0 }; - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); let selection = doc.selection(view.id); let text = doc.text().slice(..); @@ -4974,7 +4991,7 @@ fn increment_impl(cx: &mut Context, increment_direction: IncrementDirection) { let transaction = Transaction::change(doc.text(), changes); let transaction = transaction.with_selection(selection.clone()); - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); } } diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs index c149e62b40bbd..7b3361fbd0d59 100644 --- a/helix-term/src/commands/lsp.rs +++ b/helix-term/src/commands/lsp.rs @@ -9,7 +9,7 @@ use tui::text::{Span, Spans}; use super::{align_view, push_jump, Align, Context, Editor, Open}; use helix_core::{path, Selection}; -use helix_view::{apply_transaction, document::Mode, editor::Action, theme::Style}; +use helix_view::{document::Mode, editor::Action, theme::Style}; use crate::{ compositor::{self, Compositor}, @@ -711,7 +711,7 @@ pub fn apply_workspace_edit( } }; - let doc = doc_mut!(editor, &doc_id); + let doc = doc!(editor, &doc_id); // Need to determine a view for apply/append_changes_to_history let selections = doc.selections(); @@ -732,7 +732,8 @@ pub fn apply_workspace_edit( text_edits, offset_encoding, ); - apply_transaction(&transaction, doc, view_mut!(editor, view_id)); + editor.apply_transaction(&transaction, doc_id, view_id); + let doc = doc_mut!(editor, &doc_id); doc.append_changes_to_history(view_id); }; diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index c6810f05390f7..440b48a4378d5 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -4,10 +4,7 @@ use crate::job::Job; use super::*; -use helix_view::{ - apply_transaction, - editor::{Action, CloseError, ConfigEvent}, -}; +use helix_view::editor::{Action, CloseError, ConfigEvent}; use ui::completers::{self, Completer}; #[derive(Clone)] @@ -445,8 +442,8 @@ fn set_line_ending( arg if arg.starts_with("nel") => Nel, _ => bail!("invalid line ending"), }; - let (view, doc) = current!(cx.editor); - doc.line_ending = line_ending; + doc_mut!(cx.editor).line_ending = line_ending; + let (view, doc) = current_ref!(cx.editor); let mut pos = 0; let transaction = Transaction::change( @@ -463,7 +460,8 @@ fn set_line_ending( } }), ); - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); + let (view, doc) = current!(cx.editor); doc.append_changes_to_history(view.id); Ok(()) @@ -480,9 +478,8 @@ fn earlier( let uk = args.join(" ").parse::().map_err(|s| anyhow!(s))?; - let (view, doc) = current!(cx.editor); - let success = doc.earlier(view, uk); - if !success { + let (view_id, doc_id) = current_ids!(cx.editor); + if !cx.editor.earlier(doc_id, view_id, uk) { cx.editor.set_status("Already at oldest change"); } @@ -499,9 +496,9 @@ fn later( } let uk = args.join(" ").parse::().map_err(|s| anyhow!(s))?; - let (view, doc) = current!(cx.editor); - let success = doc.later(view, uk); - if !success { + + let (view_id, doc_id) = current_ids!(cx.editor); + if !cx.editor.later(doc_id, view_id, uk) { cx.editor.set_status("Already at newest change"); } @@ -899,7 +896,7 @@ fn replace_selections_with_clipboard_impl( cx: &mut compositor::Context, clipboard_type: ClipboardType, ) -> anyhow::Result<()> { - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); match cx.editor.clipboard_provider.get_contents(clipboard_type) { Ok(contents) => { @@ -908,7 +905,8 @@ fn replace_selections_with_clipboard_impl( (range.from(), range.to(), Some(contents.as_str().into())) }); - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); + let (view, doc) = current!(cx.editor); doc.append_changes_to_history(view.id); Ok(()) } @@ -1027,11 +1025,8 @@ fn reload( return Ok(()); } - let scrolloff = cx.editor.config().scrolloff; - let (view, doc) = current!(cx.editor); - doc.reload(view).map(|_| { - view.ensure_cursor_in_view(doc, scrolloff); - }) + let (view_id, doc_id) = current_ids!(cx.editor); + cx.editor.reload(doc_id, view_id) } /// Update the [`Document`] if it has been modified. @@ -1044,8 +1039,7 @@ fn update( return Ok(()); } - let (_view, doc) = current!(cx.editor); - if doc.is_modified() { + if doc!(cx.editor).is_modified() { write(cx, args, event) } else { Ok(()) @@ -1061,9 +1055,7 @@ fn lsp_workspace_command( return Ok(()); } - let (_, doc) = current!(cx.editor); - - let language_server = match doc.language_server() { + let language_server = match doc!(cx.editor).language_server() { Some(language_server) => language_server, None => { cx.editor @@ -1132,7 +1124,8 @@ fn lsp_restart( return Ok(()); } - let (_view, doc) = current!(cx.editor); + let doc = doc!(cx.editor); + let config = doc .language_config() .context("LSP not defined for the current document")?; @@ -1166,7 +1159,7 @@ fn tree_sitter_scopes( return Ok(()); } - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); let text = doc.text().slice(..); let pos = doc.selection(view.id).primary().cursor(text); @@ -1199,10 +1192,10 @@ fn vsplit( return Ok(()); } - let id = view!(cx.editor).doc; + let (_, doc_id) = current_ids!(cx.editor); if args.is_empty() { - cx.editor.switch(id, Action::VerticalSplit); + cx.editor.switch(doc_id, Action::VerticalSplit); } else { for arg in args { cx.editor @@ -1222,10 +1215,10 @@ fn hsplit( return Ok(()); } - let id = view!(cx.editor).doc; + let (_, doc_id) = current_ids!(cx.editor); if args.is_empty() { - cx.editor.switch(id, Action::HorizontalSplit); + cx.editor.switch(doc_id, Action::HorizontalSplit); } else { for arg in args { cx.editor @@ -1504,7 +1497,7 @@ fn sort_impl( _args: &[Cow], reverse: bool, ) -> anyhow::Result<()> { - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); let text = doc.text().slice(..); let selection = doc.selection(view.id); @@ -1527,7 +1520,8 @@ fn sort_impl( .map(|(s, fragment)| (s.from(), s.to(), Some(fragment))), ); - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); + let (view, doc) = current!(cx.editor); doc.append_changes_to_history(view.id); Ok(()) @@ -1543,7 +1537,7 @@ fn reflow( } let scrolloff = cx.editor.config().scrolloff; - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); const DEFAULT_MAX_LEN: usize = 79; @@ -1571,7 +1565,8 @@ fn reflow( (range.from(), range.to(), Some(reflowed_text)) }); - apply_transaction(&transaction, doc, view); + cx.editor.apply_transaction(&transaction, doc.id(), view.id); + let (view, doc) = current!(cx.editor); doc.append_changes_to_history(view.id); view.ensure_cursor_in_view(doc, scrolloff); @@ -1587,7 +1582,7 @@ fn tree_sitter_subtree( return Ok(()); } - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(cx.editor); if let Some(syntax) = doc.syntax() { let primary_selection = doc.selection(view.id).primary(); diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs index 545b19d8968fc..f799f0b6bdc86 100644 --- a/helix-term/src/ui/completion.rs +++ b/helix-term/src/ui/completion.rs @@ -1,5 +1,5 @@ use crate::compositor::{Component, Context, Event, EventResult}; -use helix_view::{apply_transaction, editor::CompleteAction}; +use helix_view::editor::CompleteAction; use tui::buffer::Buffer as Surface; use tui::text::Spans; @@ -148,14 +148,14 @@ impl Completion { .collect() } - let (view, doc) = current!(editor); + let (view_id, doc_id) = current_ids!(editor); // if more text was entered, remove it - doc.restore(view); + editor.restore(doc_id, view_id); match event { PromptEvent::Abort => { - doc.restore(view); + editor.restore(doc_id, view_id); editor.last_completion = None; } PromptEvent::Update => { @@ -163,7 +163,7 @@ impl Completion { let item = item.unwrap(); let transaction = item_to_transaction( - doc, + doc!(editor, &doc_id), item, offset_encoding, start_offset, @@ -171,8 +171,8 @@ impl Completion { ); // initialize a savepoint - doc.savepoint(); - apply_transaction(&transaction, doc, view); + doc_mut!(editor, &doc_id).savepoint(); + editor.apply_transaction(&transaction, doc_id, view_id); editor.last_completion = Some(CompleteAction { trigger_offset, @@ -184,14 +184,14 @@ impl Completion { let item = item.unwrap(); let transaction = item_to_transaction( - doc, + doc!(editor, &doc_id), item, offset_encoding, start_offset, trigger_offset, ); - apply_transaction(&transaction, doc, view); + editor.apply_transaction(&transaction, doc_id, view_id); editor.last_completion = Some(CompleteAction { trigger_offset, @@ -207,7 +207,7 @@ impl Completion { { None } else { - Self::resolve_completion_item(doc, item.clone()) + Self::resolve_completion_item(doc!(editor, &doc_id), item.clone()) }; if let Some(additional_edits) = resolved_item @@ -216,12 +216,13 @@ impl Completion { .or(item.additional_text_edits.as_ref()) { if !additional_edits.is_empty() { + let doc = doc!(editor); let transaction = util::generate_transaction_from_edits( doc.text(), additional_edits.clone(), offset_encoding, // TODO: should probably transcode in Client ); - apply_transaction(&transaction, doc, view); + editor.apply_transaction(&transaction, doc_id, view_id); } } } diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index f2a588e338f22..28e3c8e678494 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -17,7 +17,6 @@ use helix_core::{ visual_coords_at_pos, LineEnding, Position, Range, Selection, Transaction, }; use helix_view::{ - apply_transaction, document::{Mode, SCRATCH_BUFFER_NAME}, editor::{CompleteAction, CursorShapeConfig}, graphics::{Color, CursorKind, Modifier, Rect, Style}, @@ -1018,10 +1017,10 @@ impl EditorView { match key { InsertEvent::Key(key) => self.insert_mode(cxt, key), InsertEvent::CompletionApply(compl) => { - let (view, doc) = current!(cxt.editor); - - doc.restore(view); + let (view_id, doc_id) = current_ids!(cxt.editor); + cxt.editor.restore(doc_id, view_id); + let (view, doc) = current_ref!(cxt.editor); let text = doc.text().slice(..); let cursor = doc.selection(view.id).primary().cursor(text); @@ -1034,7 +1033,7 @@ impl EditorView { (shift_position(start), shift_position(end), t) }), ); - apply_transaction(&tx, doc, view); + cxt.editor.apply_transaction(&tx, doc_id, view_id); } InsertEvent::TriggerCompletion => { let (_, doc) = current!(cxt.editor); diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 0870852833918..3e759cb719908 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -24,7 +24,7 @@ use helix_core::{ DEFAULT_LINE_ENDING, }; -use crate::{apply_transaction, DocumentId, Editor, View, ViewId}; +use crate::{DocumentId, Editor, ViewId}; /// 8kB of buffer space for encoding and decoding `Rope`s. const BUF_SIZE: usize = 8192; @@ -624,7 +624,7 @@ impl Document { } /// Reload the document from its path. - pub fn reload(&mut self, view: &mut View) -> Result<(), Error> { + pub fn reload(&self) -> Result { let encoding = &self.encoding; let path = self.path().filter(|path| path.exists()); @@ -639,14 +639,7 @@ impl Document { // Calculate the difference between the buffer and source text, and apply it. // This is not considered a modification of the contents of the file regardless // of the encoding. - let transaction = helix_core::diff::compare_ropes(self.text(), &rope); - apply_transaction(&transaction, self, view); - self.append_changes_to_history(view.id); - self.reset_modified(); - - self.detect_indent_and_line_ending(); - - Ok(()) + Ok(helix_core::diff::compare_ropes(self.text(), &rope)) } /// Sets the [`Document`]'s encoding with the encoding correspondent to `label`. @@ -833,7 +826,7 @@ impl Document { } /// Apply a [`Transaction`] to the [`Document`] to change its text. - /// Instead of calling this function directly, use [crate::apply_transaction] + /// Instead of calling this function directly, use [Editor::apply_transaction] /// to ensure that the transaction is applied to the appropriate [`View`] as /// well. pub fn apply(&mut self, transaction: &Transaction, view_id: ViewId) -> bool { @@ -857,11 +850,11 @@ impl Document { success } - fn undo_redo_impl(&mut self, view: &mut View, undo: bool) -> bool { + fn undo_redo_impl(&mut self, view_id: ViewId, undo: bool) -> Option { let mut history = self.history.take(); - let txn = if undo { history.undo() } else { history.redo() }; - let success = if let Some(txn) = txn { - self.apply_impl(txn, view.id) && view.apply(txn, self) + let transaction = if undo { history.undo() } else { history.redo() }.cloned(); + let success = if let Some(ref txn) = transaction { + self.apply_impl(txn, view_id) } else { false }; @@ -870,57 +863,68 @@ impl Document { if success { // reset changeset to fix len self.changes = ChangeSet::new(self.text()); + transaction + } else { + None } - success } - /// Undo the last modification to the [`Document`]. Returns whether the undo was successful. - pub fn undo(&mut self, view: &mut View) -> bool { - self.undo_redo_impl(view, true) + /// Undo the last modification to the [`Document`]. + /// Use [Editor::undo] instead of calling this method directly. + pub fn undo(&mut self, view_id: ViewId) -> Option { + self.undo_redo_impl(view_id, true) } - /// Redo the last modification to the [`Document`]. Returns whether the redo was successful. - pub fn redo(&mut self, view: &mut View) -> bool { - self.undo_redo_impl(view, false) + /// Redo the last modification to the [`Document`]. + /// Use [Editor::redo] instead of calling this method directly. + pub fn redo(&mut self, view_id: ViewId) -> Option { + self.undo_redo_impl(view_id, false) } pub fn savepoint(&mut self) { self.savepoint = Some(Transaction::new(self.text())); } - pub fn restore(&mut self, view: &mut View) { - if let Some(revert) = self.savepoint.take() { - apply_transaction(&revert, self, view); - } + pub fn restore(&mut self) -> Option { + self.savepoint.take() } - fn earlier_later_impl(&mut self, view: &mut View, uk: UndoKind, earlier: bool) -> bool { + fn earlier_later_impl( + &mut self, + view_id: ViewId, + uk: UndoKind, + earlier: bool, + ) -> Vec { let txns = if earlier { self.history.get_mut().earlier(uk) } else { self.history.get_mut().later(uk) }; let mut success = false; - for txn in txns { - if self.apply_impl(&txn, view.id) && view.apply(&txn, self) { + for txn in txns.clone() { + if self.apply_impl(&txn, view_id) { success = true; } } if success { // reset changeset to fix len self.changes = ChangeSet::new(self.text()); + txns.clone() + } else { + Vec::new() } - success } /// Undo modifications to the [`Document`] according to `uk`. - pub fn earlier(&mut self, view: &mut View, uk: UndoKind) -> bool { - self.earlier_later_impl(view, uk, true) + /// Use [Editor::earlier] instead of calling this method directly. + pub fn earlier(&mut self, view_id: ViewId, uk: UndoKind) -> Vec { + self.earlier_later_impl(view_id, uk, true) } /// Redo modifications to the [`Document`] according to `uk`. - pub fn later(&mut self, view: &mut View, uk: UndoKind) -> bool { - self.earlier_later_impl(view, uk, false) + /// Use [Editor::later] instead of calling this method directly. + pub fn later(&mut self, view_id: ViewId, uk: UndoKind) -> Vec { + self.earlier_later_impl(view_id, uk, false) } /// Commit pending changes to history diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 213141ffc40b7..473495233c539 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -34,11 +34,11 @@ use anyhow::{anyhow, bail, Error}; pub use helix_core::diagnostic::Severity; pub use helix_core::register::Registers; -use helix_core::Position; use helix_core::{ auto_pairs::AutoPairs, + history::UndoKind, syntax::{self, AutoPairConfig}, - Change, + Change, Position, }; use helix_dap as dap; use helix_lsp::lsp; @@ -1222,6 +1222,92 @@ impl Editor { Ok(()) } + pub fn reload(&mut self, doc_id: DocumentId, view_id: ViewId) -> anyhow::Result<()> { + let scrolloff = self.config().scrolloff; + let doc = doc_mut!(self, &doc_id); + let transaction = doc.reload()?; + self.apply_transaction(&transaction, doc_id, view_id); + let doc = doc_mut!(self, &doc_id); + doc.append_changes_to_history(view_id); + doc.reset_modified(); + doc.detect_indent_and_line_ending(); + view_mut!(self, view_id).ensure_cursor_in_view(doc, scrolloff); + Ok(()) + } + + pub fn restore(&mut self, doc_id: DocumentId, view_id: ViewId) { + let doc = doc_mut!(self, &doc_id); + if let Some(transaction) = doc.restore() { + self.apply_transaction(&transaction, doc_id, view_id); + } + } + + pub fn undo(&mut self, doc_id: DocumentId, view_id: ViewId) -> bool { + let doc = doc_mut!(self, &doc_id); + let transaction = match doc.undo(view_id) { + Some(transaction) => transaction, + None => return false, + }; + self.apply_transaction_to_views(&transaction, doc_id) + } + + pub fn redo(&mut self, doc_id: DocumentId, view_id: ViewId) -> bool { + let doc = doc_mut!(self, &doc_id); + let transaction = match doc.redo(view_id) { + Some(transaction) => transaction, + None => return false, + }; + self.apply_transaction_to_views(&transaction, doc_id) + } + + pub fn earlier(&mut self, doc_id: DocumentId, view_id: ViewId, kind: UndoKind) -> bool { + let doc = doc_mut!(self, &doc_id); + let transactions = doc.earlier(view_id, kind); + if transactions.is_empty() { + return false; + } + transactions.iter().fold(true, |acc, transaction| { + acc && self.apply_transaction_to_views(&transaction, doc_id) + }) + } + + pub fn later(&mut self, doc_id: DocumentId, view_id: ViewId, kind: UndoKind) -> bool { + let doc = doc_mut!(self, &doc_id); + let transactions = doc.later(view_id, kind); + if transactions.is_empty() { + return false; + } + transactions.iter().fold(true, |acc, transaction| { + acc && self.apply_transaction_to_views(&transaction, doc_id) + }) + } + + /// Applies a given [helix_core::Transaction] to the given Document and the focused + /// ViewId. + /// + /// The transaction is also applied to all views which contain the open document so + /// that all jumplist selections are updated by the transaction. + pub fn apply_transaction( + &mut self, + transaction: &helix_core::Transaction, + doc_id: DocumentId, + view_id: ViewId, + ) -> bool { + let doc = doc_mut!(self, &doc_id); + doc.apply(&transaction, view_id) && self.apply_transaction_to_views(&transaction, doc_id) + } + + fn apply_transaction_to_views( + &mut self, + transaction: &helix_core::Transaction, + doc_id: DocumentId, + ) -> bool { + let doc = doc!(self, &doc_id); + self.tree.views_mut().fold(true, |acc, (view, _focused)| { + acc && view.apply(&transaction, doc) + }) + } + pub fn resize(&mut self, area: Rect) { if self.tree.resize(area) { self._refresh(); diff --git a/helix-view/src/lib.rs b/helix-view/src/lib.rs index 4c32b356b9eaa..9a98044639531 100644 --- a/helix-view/src/lib.rs +++ b/helix-view/src/lib.rs @@ -66,19 +66,6 @@ pub fn align_view(doc: &Document, view: &mut View, align: Align) { view.offset.row = line.saturating_sub(relative); } -/// Applies a [`helix_core::Transaction`] to the given [`Document`] -/// and [`View`]. -pub fn apply_transaction( - transaction: &helix_core::Transaction, - doc: &mut Document, - view: &mut View, -) -> bool { - // This is a short function but it's easy to call `Document::apply` - // without calling `View::apply` or in the wrong order. The transaction - // must be applied to the document before the view. - doc.apply(transaction, view.id) && view.apply(transaction, doc) -} - pub use document::Document; pub use editor::Editor; pub use theme::Theme; diff --git a/helix-view/src/macros.rs b/helix-view/src/macros.rs index 53ab434622d44..311e65d07777f 100644 --- a/helix-view/src/macros.rs +++ b/helix-view/src/macros.rs @@ -28,6 +28,16 @@ macro_rules! current_ref { }}; } +/// Get the IDs of the current View and Document immutably. +/// Returns `(ViewId, DocumentId)` +#[macro_export] +macro_rules! current_ids { + ($editor:expr) => {{ + let (view, doc) = $crate::current_ref!($editor); + (view.id, doc.id()) + }}; +} + /// Get the current document mutably. /// Returns `&mut Document` #[macro_export] @@ -67,7 +77,7 @@ macro_rules! view { #[macro_export] macro_rules! doc { ($editor:expr, $id:expr) => {{ - $editor.documents[$id] + &$editor.documents[$id] }}; ($editor:expr) => {{ $crate::current_ref!($editor).1