Skip to content

Commit 71235e8

Browse files
committed
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 borrows of the view and doc at the beginning of a function and then pass them to the helper. Now they must take immutable borrows or refresh the mutable borrows after calling `Editor::apply_transaction` to avoid taking multiple mutable borrows 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.
1 parent 89efb4f commit 71235e8

File tree

11 files changed

+323
-211
lines changed

11 files changed

+323
-211
lines changed

helix-term/src/commands.rs

Lines changed: 114 additions & 97 deletions
Large diffs are not rendered by default.

helix-term/src/commands/lsp.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use tui::text::{Span, Spans};
99
use super::{align_view, push_jump, Align, Context, Editor, Open};
1010

1111
use helix_core::{path, Selection};
12-
use helix_view::{apply_transaction, document::Mode, editor::Action, theme::Style};
12+
use helix_view::{document::Mode, editor::Action, theme::Style};
1313

1414
use crate::{
1515
compositor::{self, Compositor},
@@ -711,7 +711,7 @@ pub fn apply_workspace_edit(
711711
}
712712
};
713713

714-
let doc = doc_mut!(editor, &doc_id);
714+
let doc = doc!(editor, &doc_id);
715715

716716
// Need to determine a view for apply/append_changes_to_history
717717
let selections = doc.selections();
@@ -732,7 +732,8 @@ pub fn apply_workspace_edit(
732732
text_edits,
733733
offset_encoding,
734734
);
735-
apply_transaction(&transaction, doc, view_mut!(editor, view_id));
735+
editor.apply_transaction(&transaction, doc_id, view_id);
736+
let doc = doc_mut!(editor, &doc_id);
736737
doc.append_changes_to_history(view_id);
737738
};
738739

helix-term/src/commands/typed.rs

Lines changed: 31 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@ use crate::job::Job;
44

55
use super::*;
66

7-
use helix_view::{
8-
apply_transaction,
9-
editor::{Action, CloseError, ConfigEvent},
10-
};
7+
use helix_view::editor::{Action, CloseError, ConfigEvent};
118
use ui::completers::{self, Completer};
129

1310
#[derive(Clone)]
@@ -445,8 +442,8 @@ fn set_line_ending(
445442
arg if arg.starts_with("nel") => Nel,
446443
_ => bail!("invalid line ending"),
447444
};
448-
let (view, doc) = current!(cx.editor);
449-
doc.line_ending = line_ending;
445+
doc_mut!(cx.editor).line_ending = line_ending;
446+
let (view, doc) = current_ref!(cx.editor);
450447

451448
let mut pos = 0;
452449
let transaction = Transaction::change(
@@ -463,7 +460,8 @@ fn set_line_ending(
463460
}
464461
}),
465462
);
466-
apply_transaction(&transaction, doc, view);
463+
cx.editor.apply_transaction(&transaction, doc.id(), view.id);
464+
let (view, doc) = current!(cx.editor);
467465
doc.append_changes_to_history(view.id);
468466

469467
Ok(())
@@ -480,9 +478,8 @@ fn earlier(
480478

481479
let uk = args.join(" ").parse::<UndoKind>().map_err(|s| anyhow!(s))?;
482480

483-
let (view, doc) = current!(cx.editor);
484-
let success = doc.earlier(view, uk);
485-
if !success {
481+
let (view_id, doc_id) = current_ids!(cx.editor);
482+
if !cx.editor.earlier(doc_id, view_id, uk) {
486483
cx.editor.set_status("Already at oldest change");
487484
}
488485

@@ -499,9 +496,9 @@ fn later(
499496
}
500497

501498
let uk = args.join(" ").parse::<UndoKind>().map_err(|s| anyhow!(s))?;
502-
let (view, doc) = current!(cx.editor);
503-
let success = doc.later(view, uk);
504-
if !success {
499+
500+
let (view_id, doc_id) = current_ids!(cx.editor);
501+
if !cx.editor.later(doc_id, view_id, uk) {
505502
cx.editor.set_status("Already at newest change");
506503
}
507504

@@ -899,7 +896,7 @@ fn replace_selections_with_clipboard_impl(
899896
cx: &mut compositor::Context,
900897
clipboard_type: ClipboardType,
901898
) -> anyhow::Result<()> {
902-
let (view, doc) = current!(cx.editor);
899+
let (view, doc) = current_ref!(cx.editor);
903900

904901
match cx.editor.clipboard_provider.get_contents(clipboard_type) {
905902
Ok(contents) => {
@@ -908,7 +905,8 @@ fn replace_selections_with_clipboard_impl(
908905
(range.from(), range.to(), Some(contents.as_str().into()))
909906
});
910907

911-
apply_transaction(&transaction, doc, view);
908+
cx.editor.apply_transaction(&transaction, doc.id(), view.id);
909+
let (view, doc) = current!(cx.editor);
912910
doc.append_changes_to_history(view.id);
913911
Ok(())
914912
}
@@ -1027,11 +1025,8 @@ fn reload(
10271025
return Ok(());
10281026
}
10291027

1030-
let scrolloff = cx.editor.config().scrolloff;
1031-
let (view, doc) = current!(cx.editor);
1032-
doc.reload(view).map(|_| {
1033-
view.ensure_cursor_in_view(doc, scrolloff);
1034-
})
1028+
let (view_id, doc_id) = current_ids!(cx.editor);
1029+
cx.editor.reload(doc_id, view_id)
10351030
}
10361031

10371032
/// Update the [`Document`] if it has been modified.
@@ -1044,8 +1039,7 @@ fn update(
10441039
return Ok(());
10451040
}
10461041

1047-
let (_view, doc) = current!(cx.editor);
1048-
if doc.is_modified() {
1042+
if doc!(cx.editor).is_modified() {
10491043
write(cx, args, event)
10501044
} else {
10511045
Ok(())
@@ -1061,9 +1055,7 @@ fn lsp_workspace_command(
10611055
return Ok(());
10621056
}
10631057

1064-
let (_, doc) = current!(cx.editor);
1065-
1066-
let language_server = match doc.language_server() {
1058+
let language_server = match doc!(cx.editor).language_server() {
10671059
Some(language_server) => language_server,
10681060
None => {
10691061
cx.editor
@@ -1132,7 +1124,8 @@ fn lsp_restart(
11321124
return Ok(());
11331125
}
11341126

1135-
let (_view, doc) = current!(cx.editor);
1127+
let doc = doc!(cx.editor);
1128+
11361129
let config = doc
11371130
.language_config()
11381131
.context("LSP not defined for the current document")?;
@@ -1166,7 +1159,7 @@ fn tree_sitter_scopes(
11661159
return Ok(());
11671160
}
11681161

1169-
let (view, doc) = current!(cx.editor);
1162+
let (view, doc) = current_ref!(cx.editor);
11701163
let text = doc.text().slice(..);
11711164

11721165
let pos = doc.selection(view.id).primary().cursor(text);
@@ -1199,10 +1192,10 @@ fn vsplit(
11991192
return Ok(());
12001193
}
12011194

1202-
let id = view!(cx.editor).doc;
1195+
let (_, doc_id) = current_ids!(cx.editor);
12031196

12041197
if args.is_empty() {
1205-
cx.editor.switch(id, Action::VerticalSplit);
1198+
cx.editor.switch(doc_id, Action::VerticalSplit);
12061199
} else {
12071200
for arg in args {
12081201
cx.editor
@@ -1222,10 +1215,10 @@ fn hsplit(
12221215
return Ok(());
12231216
}
12241217

1225-
let id = view!(cx.editor).doc;
1218+
let (_, doc_id) = current_ids!(cx.editor);
12261219

12271220
if args.is_empty() {
1228-
cx.editor.switch(id, Action::HorizontalSplit);
1221+
cx.editor.switch(doc_id, Action::HorizontalSplit);
12291222
} else {
12301223
for arg in args {
12311224
cx.editor
@@ -1504,7 +1497,7 @@ fn sort_impl(
15041497
_args: &[Cow<str>],
15051498
reverse: bool,
15061499
) -> anyhow::Result<()> {
1507-
let (view, doc) = current!(cx.editor);
1500+
let (view, doc) = current_ref!(cx.editor);
15081501
let text = doc.text().slice(..);
15091502

15101503
let selection = doc.selection(view.id);
@@ -1527,7 +1520,8 @@ fn sort_impl(
15271520
.map(|(s, fragment)| (s.from(), s.to(), Some(fragment))),
15281521
);
15291522

1530-
apply_transaction(&transaction, doc, view);
1523+
cx.editor.apply_transaction(&transaction, doc.id(), view.id);
1524+
let (view, doc) = current!(cx.editor);
15311525
doc.append_changes_to_history(view.id);
15321526

15331527
Ok(())
@@ -1543,7 +1537,7 @@ fn reflow(
15431537
}
15441538

15451539
let scrolloff = cx.editor.config().scrolloff;
1546-
let (view, doc) = current!(cx.editor);
1540+
let (view, doc) = current_ref!(cx.editor);
15471541

15481542
const DEFAULT_MAX_LEN: usize = 79;
15491543

@@ -1571,7 +1565,8 @@ fn reflow(
15711565
(range.from(), range.to(), Some(reflowed_text))
15721566
});
15731567

1574-
apply_transaction(&transaction, doc, view);
1568+
cx.editor.apply_transaction(&transaction, doc.id(), view.id);
1569+
let (view, doc) = current!(cx.editor);
15751570
doc.append_changes_to_history(view.id);
15761571
view.ensure_cursor_in_view(doc, scrolloff);
15771572

@@ -1587,7 +1582,7 @@ fn tree_sitter_subtree(
15871582
return Ok(());
15881583
}
15891584

1590-
let (view, doc) = current!(cx.editor);
1585+
let (view, doc) = current_ref!(cx.editor);
15911586

15921587
if let Some(syntax) = doc.syntax() {
15931588
let primary_selection = doc.selection(view.id).primary();

helix-term/src/ui/completion.rs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::compositor::{Component, Context, Event, EventResult};
2-
use helix_view::{apply_transaction, editor::CompleteAction};
2+
use helix_view::editor::CompleteAction;
33
use tui::buffer::Buffer as Surface;
44
use tui::text::Spans;
55

@@ -148,31 +148,31 @@ impl Completion {
148148
.collect()
149149
}
150150

151-
let (view, doc) = current!(editor);
151+
let (view_id, doc_id) = current_ids!(editor);
152152

153153
// if more text was entered, remove it
154-
doc.restore(view);
154+
editor.restore(doc_id, view_id);
155155

156156
match event {
157157
PromptEvent::Abort => {
158-
doc.restore(view);
158+
editor.restore(doc_id, view_id);
159159
editor.last_completion = None;
160160
}
161161
PromptEvent::Update => {
162162
// always present here
163163
let item = item.unwrap();
164164

165165
let transaction = item_to_transaction(
166-
doc,
166+
doc!(editor, &doc_id),
167167
item,
168168
offset_encoding,
169169
start_offset,
170170
trigger_offset,
171171
);
172172

173173
// initialize a savepoint
174-
doc.savepoint();
175-
apply_transaction(&transaction, doc, view);
174+
doc_mut!(editor, &doc_id).savepoint();
175+
editor.apply_transaction(&transaction, doc_id, view_id);
176176

177177
editor.last_completion = Some(CompleteAction {
178178
trigger_offset,
@@ -184,14 +184,14 @@ impl Completion {
184184
let item = item.unwrap();
185185

186186
let transaction = item_to_transaction(
187-
doc,
187+
doc!(editor, &doc_id),
188188
item,
189189
offset_encoding,
190190
start_offset,
191191
trigger_offset,
192192
);
193193

194-
apply_transaction(&transaction, doc, view);
194+
editor.apply_transaction(&transaction, doc_id, view_id);
195195

196196
editor.last_completion = Some(CompleteAction {
197197
trigger_offset,
@@ -207,7 +207,7 @@ impl Completion {
207207
{
208208
None
209209
} else {
210-
Self::resolve_completion_item(doc, item.clone())
210+
Self::resolve_completion_item(doc!(editor, &doc_id), item.clone())
211211
};
212212

213213
if let Some(additional_edits) = resolved_item
@@ -216,12 +216,13 @@ impl Completion {
216216
.or(item.additional_text_edits.as_ref())
217217
{
218218
if !additional_edits.is_empty() {
219+
let doc = doc!(editor);
219220
let transaction = util::generate_transaction_from_edits(
220221
doc.text(),
221222
additional_edits.clone(),
222223
offset_encoding, // TODO: should probably transcode in Client
223224
);
224-
apply_transaction(&transaction, doc, view);
225+
editor.apply_transaction(&transaction, doc_id, view_id);
225226
}
226227
}
227228
}

helix-term/src/ui/editor.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ use helix_core::{
1717
visual_coords_at_pos, LineEnding, Position, Range, Selection, Transaction,
1818
};
1919
use helix_view::{
20-
apply_transaction,
2120
document::{Mode, SCRATCH_BUFFER_NAME},
2221
editor::{CompleteAction, CursorShapeConfig},
2322
graphics::{Color, CursorKind, Modifier, Rect, Style},
@@ -1018,10 +1017,10 @@ impl EditorView {
10181017
match key {
10191018
InsertEvent::Key(key) => self.insert_mode(cxt, key),
10201019
InsertEvent::CompletionApply(compl) => {
1021-
let (view, doc) = current!(cxt.editor);
1022-
1023-
doc.restore(view);
1020+
let (view_id, doc_id) = current_ids!(cxt.editor);
1021+
cxt.editor.restore(doc_id, view_id);
10241022

1023+
let (view, doc) = current_ref!(cxt.editor);
10251024
let text = doc.text().slice(..);
10261025
let cursor = doc.selection(view.id).primary().cursor(text);
10271026

@@ -1034,7 +1033,7 @@ impl EditorView {
10341033
(shift_position(start), shift_position(end), t)
10351034
}),
10361035
);
1037-
apply_transaction(&tx, doc, view);
1036+
cxt.editor.apply_transaction(&tx, doc_id, view_id);
10381037
}
10391038
InsertEvent::TriggerCompletion => {
10401039
let (_, doc) = current!(cxt.editor);

helix-term/tests/test/helpers.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// TODO: remove this when the MSRV is 1.62.0
2+
#![allow(mutable_borrow_reservation_conflict)]
3+
14
use std::{
25
fs::File,
36
io::{Read, Write},
@@ -121,7 +124,7 @@ pub async fn test_key_sequence_with_input_text<T: Into<TestCase>>(
121124
None => Application::new(Args::default(), Config::default(), test_syntax_conf(None))?,
122125
};
123126

124-
let (view, doc) = helix_view::current!(app.editor);
127+
let (view, doc) = helix_view::current_ref!(app.editor);
125128
let sel = doc.selection(view.id).clone();
126129

127130
// replace the initial text with the input text
@@ -130,7 +133,8 @@ pub async fn test_key_sequence_with_input_text<T: Into<TestCase>>(
130133
})
131134
.with_selection(test_case.in_selection.clone());
132135

133-
helix_view::apply_transaction(&transaction, doc, view);
136+
app.editor
137+
.apply_transaction(&transaction, doc.id(), view.id);
134138

135139
test_key_sequence(
136140
&mut app,
@@ -307,15 +311,15 @@ impl AppBuilder {
307311
let mut app = Application::new(self.args, self.config, self.syn_conf)?;
308312

309313
if let Some((text, selection)) = self.input {
310-
let (view, doc) = helix_view::current!(app.editor);
314+
let (view, doc) = helix_view::current_ref!(app.editor);
311315
let sel = doc.selection(view.id).clone();
312316
let trans = Transaction::change_by_selection(doc.text(), &sel, |_| {
313317
(0, doc.text().len_chars(), Some((text.clone()).into()))
314318
})
315319
.with_selection(selection);
316320

317321
// replace the initial text with the input text
318-
helix_view::apply_transaction(&trans, doc, view);
322+
app.editor.apply_transaction(&trans, doc.id(), view.id);
319323
}
320324

321325
Ok(app)

0 commit comments

Comments
 (0)