Skip to content

Commit a0e0863

Browse files
committed
Track document changes in the jumplist
Previously, the jumplist was not updated by changes to the document. This meant that the jumplist entries were prone to being incorrect after some edits and that usages of the jumplist were prone to panics if, for example, some edits truncated the document. This change applies `Transaction`s (document edits) to the selections stored in any jumplists.
1 parent e066782 commit a0e0863

File tree

5 files changed

+33
-18
lines changed

5 files changed

+33
-18
lines changed

helix-term/src/commands.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2346,7 +2346,7 @@ fn jumplist_picker(cx: &mut Context) {
23462346
view.jumps
23472347
.get()
23482348
.iter()
2349-
.map(|(doc_id, selection)| new_meta(view, *doc_id, selection.clone()))
2349+
.map(|(doc_id, selection)| new_meta(view, *doc_id, selection.borrow().clone()))
23502350
})
23512351
.collect(),
23522352
(),
@@ -2644,9 +2644,8 @@ fn try_restore_indent(doc: &mut Document, view_id: ViewId) {
26442644
}
26452645

26462646
// Store a jump on the jumplist.
2647-
fn push_jump(view: &mut View, doc: &Document) {
2648-
let jump = (doc.id(), doc.selection(view.id).clone());
2649-
view.jumps.push(jump);
2647+
fn push_jump(view: &mut View, doc: &mut Document) {
2648+
view.jumps.push(doc, doc.selection(view.id).clone());
26502649
}
26512650

26522651
fn goto_line(cx: &mut Context) {
@@ -4048,7 +4047,7 @@ fn jump_forward(cx: &mut Context) {
40484047

40494048
if let Some((id, selection)) = view.jumps.forward(count) {
40504049
view.doc = *id;
4051-
let selection = selection.clone();
4050+
let selection = selection.borrow().clone();
40524051
let (view, doc) = current!(cx.editor); // refetch doc
40534052
doc.set_selection(view.id, selection);
40544053

@@ -4062,7 +4061,7 @@ fn jump_backward(cx: &mut Context) {
40624061

40634062
if let Some((id, selection)) = view.jumps.backward(view.id, doc, count) {
40644063
view.doc = *id;
4065-
let selection = selection.clone();
4064+
let selection = selection.borrow().clone();
40664065
let (view, doc) = current!(cx.editor); // refetch doc
40674066
doc.set_selection(view.id, selection);
40684067

helix-term/src/ui/mod.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ pub fn regex_prompt(
6969
fun: impl Fn(&mut View, &mut Document, Regex, PromptEvent) + 'static,
7070
) {
7171
let (view, doc) = current!(cx.editor);
72-
let doc_id = view.doc;
7372
let snapshot = doc.selection(view.id).clone();
7473
let offset_snapshot = view.offset;
7574
let config = cx.editor.config();
@@ -109,8 +108,7 @@ pub fn regex_prompt(
109108
doc.set_selection(view.id, snapshot.clone());
110109

111110
if event == PromptEvent::Validate {
112-
// Equivalent to push_jump to store selection just before jump
113-
view.jumps.push((doc_id, snapshot.clone()));
111+
view.jumps.push(doc, snapshot.clone());
114112
}
115113

116114
fun(view, doc, regex, event);

helix-view/src/document.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ use helix_core::auto_pairs::AutoPairs;
55
use helix_core::Range;
66
use serde::de::{self, Deserialize, Deserializer};
77
use serde::Serialize;
8-
use std::cell::Cell;
8+
use std::cell::{Cell, RefCell};
99
use std::collections::HashMap;
1010
use std::fmt::Display;
1111
use std::future::Future;
1212
use std::path::{Path, PathBuf};
13+
use std::rc::Rc;
1314
use std::str::FromStr;
1415
use std::sync::Arc;
1516

@@ -86,6 +87,7 @@ pub struct Document {
8687
pub(crate) id: DocumentId,
8788
text: Rope,
8889
selections: HashMap<ViewId, Selection>,
90+
jump_selections: Vec<Rc<RefCell<Selection>>>,
8991

9092
path: Option<PathBuf>,
9193
encoding: &'static encoding::Encoding,
@@ -347,6 +349,7 @@ impl Document {
347349
encoding,
348350
text,
349351
selections: HashMap::default(),
352+
jump_selections: vec![Rc::new(RefCell::new(Selection::point(0)))],
350353
indent_style: DEFAULT_INDENT,
351354
line_ending: DEFAULT_LINE_ENDING,
352355
mode: Mode::Normal,
@@ -744,6 +747,14 @@ impl Document {
744747
}
745748

746749
self.modified_since_accessed = true;
750+
751+
for selection in &self.jump_selections {
752+
let mut selection = selection.borrow_mut();
753+
*selection = selection
754+
.clone()
755+
.map(transaction.changes())
756+
.ensure_invariants(self.text.slice(..));
757+
}
747758
}
748759

749760
if !transaction.changes().is_empty() {
@@ -1075,6 +1086,11 @@ impl Document {
10751086
None => global_config,
10761087
}
10771088
}
1089+
1090+
#[inline]
1091+
pub fn push_jump_selection(&mut self, selection: Rc<RefCell<Selection>>) {
1092+
self.jump_selections.push(selection)
1093+
}
10781094
}
10791095

10801096
impl Default for Document {

helix-view/src/editor.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -905,8 +905,7 @@ impl Editor {
905905
view.remove_document(&id);
906906
}
907907
} else {
908-
let jump = (view.doc, doc.selection(view.id).clone());
909-
view.jumps.push(jump);
908+
view.jumps.push(doc, doc.selection(view.id).clone());
910909
// Set last accessed doc if it is a different document
911910
if doc.id != id {
912911
view.add_to_history(view.doc);

helix-view/src/view.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ use crate::{
55
};
66
use helix_core::{pos_at_visual_coords, visual_coords_at_pos, Position, RopeSlice, Selection};
77

8-
use std::fmt;
8+
use std::{cell::RefCell, fmt, rc::Rc};
99

10-
type Jump = (DocumentId, Selection);
10+
type Jump = (DocumentId, Rc<RefCell<Selection>>);
1111

1212
#[derive(Debug, Clone)]
1313
pub struct JumpList {
@@ -23,7 +23,11 @@ impl JumpList {
2323
}
2424
}
2525

26-
pub fn push(&mut self, jump: Jump) {
26+
pub fn push(&mut self, doc: &mut Document, selection: Selection) {
27+
let selection = Rc::new(RefCell::new(selection));
28+
let jump = (doc.id, selection.clone());
29+
doc.push_jump_selection(selection);
30+
2731
self.jumps.truncate(self.current);
2832
// don't push duplicates
2933
if self.jumps.last() != Some(&jump) {
@@ -45,8 +49,7 @@ impl JumpList {
4549
pub fn backward(&mut self, view_id: ViewId, doc: &mut Document, count: usize) -> Option<&Jump> {
4650
if let Some(current) = self.current.checked_sub(count) {
4751
if self.current == self.jumps.len() {
48-
let jump = (doc.id(), doc.selection(view_id).clone());
49-
self.push(jump);
52+
self.push(doc, doc.selection(view_id).clone());
5053
}
5154
self.current = current;
5255
self.jumps.get(self.current)
@@ -126,7 +129,7 @@ impl View {
126129
doc,
127130
offset: Position::new(0, 0),
128131
area: Rect::default(), // will get calculated upon inserting into tree
129-
jumps: JumpList::new((doc, Selection::point(0))), // TODO: use actual sel
132+
jumps: JumpList::new((doc, Rc::new(RefCell::new(Selection::point(0))))),
130133
docs_access_history: Vec::new(),
131134
last_modified_docs: [None, None],
132135
object_selections: Vec::new(),

0 commit comments

Comments
 (0)