Skip to content

Commit ad0b65b

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 0b2bb06 commit ad0b65b

File tree

11 files changed

+325
-218
lines changed

11 files changed

+325
-218
lines changed

helix-term/src/commands.rs

Lines changed: 113 additions & 100 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: 34 additions & 40 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
fn reload_all(
@@ -1062,11 +1057,10 @@ fn reload_all(
10621057
.collect();
10631058

10641059
for (doc_id, view_ids) in docs_view_ids {
1065-
let doc = doc_mut!(cx.editor, &doc_id);
1066-
10671060
// Every doc is guaranteed to have at least 1 view at this point.
1068-
let view = view_mut!(cx.editor, view_ids[0]);
1069-
doc.reload(view)?;
1061+
cx.editor.reload(doc_id, view_ids[0])?;
1062+
1063+
let doc = doc!(cx.editor, &doc_id);
10701064

10711065
for view_id in view_ids {
10721066
let view = view_mut!(cx.editor, view_id);
@@ -1088,8 +1082,7 @@ fn update(
10881082
return Ok(());
10891083
}
10901084

1091-
let (_view, doc) = current!(cx.editor);
1092-
if doc.is_modified() {
1085+
if doc!(cx.editor).is_modified() {
10931086
write(cx, args, event)
10941087
} else {
10951088
Ok(())
@@ -1105,9 +1098,7 @@ fn lsp_workspace_command(
11051098
return Ok(());
11061099
}
11071100

1108-
let (_, doc) = current!(cx.editor);
1109-
1110-
let language_server = match doc.language_server() {
1101+
let language_server = match doc!(cx.editor).language_server() {
11111102
Some(language_server) => language_server,
11121103
None => {
11131104
cx.editor
@@ -1176,7 +1167,8 @@ fn lsp_restart(
11761167
return Ok(());
11771168
}
11781169

1179-
let (_view, doc) = current!(cx.editor);
1170+
let doc = doc!(cx.editor);
1171+
11801172
let config = doc
11811173
.language_config()
11821174
.context("LSP not defined for the current document")?;
@@ -1210,7 +1202,7 @@ fn tree_sitter_scopes(
12101202
return Ok(());
12111203
}
12121204

1213-
let (view, doc) = current!(cx.editor);
1205+
let (view, doc) = current_ref!(cx.editor);
12141206
let text = doc.text().slice(..);
12151207

12161208
let pos = doc.selection(view.id).primary().cursor(text);
@@ -1243,10 +1235,10 @@ fn vsplit(
12431235
return Ok(());
12441236
}
12451237

1246-
let id = view!(cx.editor).doc;
1238+
let (_, doc_id) = current_ids!(cx.editor);
12471239

12481240
if args.is_empty() {
1249-
cx.editor.switch(id, Action::VerticalSplit);
1241+
cx.editor.switch(doc_id, Action::VerticalSplit);
12501242
} else {
12511243
for arg in args {
12521244
cx.editor
@@ -1266,10 +1258,10 @@ fn hsplit(
12661258
return Ok(());
12671259
}
12681260

1269-
let id = view!(cx.editor).doc;
1261+
let (_, doc_id) = current_ids!(cx.editor);
12701262

12711263
if args.is_empty() {
1272-
cx.editor.switch(id, Action::HorizontalSplit);
1264+
cx.editor.switch(doc_id, Action::HorizontalSplit);
12731265
} else {
12741266
for arg in args {
12751267
cx.editor
@@ -1548,7 +1540,7 @@ fn sort_impl(
15481540
_args: &[Cow<str>],
15491541
reverse: bool,
15501542
) -> anyhow::Result<()> {
1551-
let (view, doc) = current!(cx.editor);
1543+
let (view, doc) = current_ref!(cx.editor);
15521544
let text = doc.text().slice(..);
15531545

15541546
let selection = doc.selection(view.id);
@@ -1571,7 +1563,8 @@ fn sort_impl(
15711563
.map(|(s, fragment)| (s.from(), s.to(), Some(fragment))),
15721564
);
15731565

1574-
apply_transaction(&transaction, doc, view);
1566+
cx.editor.apply_transaction(&transaction, doc.id(), view.id);
1567+
let (view, doc) = current!(cx.editor);
15751568
doc.append_changes_to_history(view.id);
15761569

15771570
Ok(())
@@ -1587,7 +1580,7 @@ fn reflow(
15871580
}
15881581

15891582
let scrolloff = cx.editor.config().scrolloff;
1590-
let (view, doc) = current!(cx.editor);
1583+
let (view, doc) = current_ref!(cx.editor);
15911584

15921585
const DEFAULT_MAX_LEN: usize = 79;
15931586

@@ -1615,7 +1608,8 @@ fn reflow(
16151608
(range.from(), range.to(), Some(reflowed_text))
16161609
});
16171610

1618-
apply_transaction(&transaction, doc, view);
1611+
cx.editor.apply_transaction(&transaction, doc.id(), view.id);
1612+
let (view, doc) = current!(cx.editor);
16191613
doc.append_changes_to_history(view.id);
16201614
view.ensure_cursor_in_view(doc, scrolloff);
16211615

@@ -1631,7 +1625,7 @@ fn tree_sitter_subtree(
16311625
return Ok(());
16321626
}
16331627

1634-
let (view, doc) = current!(cx.editor);
1628+
let (view, doc) = current_ref!(cx.editor);
16351629

16361630
if let Some(syntax) = doc.syntax() {
16371631
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},
@@ -1010,10 +1009,10 @@ impl EditorView {
10101009
match key {
10111010
InsertEvent::Key(key) => self.insert_mode(cxt, key),
10121011
InsertEvent::CompletionApply(compl) => {
1013-
let (view, doc) = current!(cxt.editor);
1014-
1015-
doc.restore(view);
1012+
let (view_id, doc_id) = current_ids!(cxt.editor);
1013+
cxt.editor.restore(doc_id, view_id);
10161014

1015+
let (view, doc) = current_ref!(cxt.editor);
10171016
let text = doc.text().slice(..);
10181017
let cursor = doc.selection(view.id).primary().cursor(text);
10191018

@@ -1026,7 +1025,7 @@ impl EditorView {
10261025
(shift_position(start), shift_position(end), t)
10271026
}),
10281027
);
1029-
apply_transaction(&tx, doc, view);
1028+
cxt.editor.apply_transaction(&tx, doc_id, view_id);
10301029
}
10311030
InsertEvent::TriggerCompletion => {
10321031
let (_, doc) = current!(cxt.editor);

0 commit comments

Comments
 (0)