Skip to content

Commit

Permalink
Track document changes in the jumplist
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
the-mikedavis committed Aug 28, 2022
1 parent e066782 commit c186c3f
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 18 deletions.
11 changes: 5 additions & 6 deletions helix-term/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2346,7 +2346,7 @@ fn jumplist_picker(cx: &mut Context) {
view.jumps
.get()
.iter()
.map(|(doc_id, selection)| new_meta(view, *doc_id, selection.clone()))
.map(|(doc_id, selection)| new_meta(view, *doc_id, selection.borrow().clone()))
})
.collect(),
(),
Expand Down Expand Up @@ -2644,9 +2644,8 @@ fn try_restore_indent(doc: &mut Document, view_id: ViewId) {
}

// Store a jump on the jumplist.
fn push_jump(view: &mut View, doc: &Document) {
let jump = (doc.id(), doc.selection(view.id).clone());
view.jumps.push(jump);
fn push_jump(view: &mut View, doc: &mut Document) {
view.jumps.push(doc, doc.selection(view.id).clone());
}

fn goto_line(cx: &mut Context) {
Expand Down Expand Up @@ -4048,7 +4047,7 @@ fn jump_forward(cx: &mut Context) {

if let Some((id, selection)) = view.jumps.forward(count) {
view.doc = *id;
let selection = selection.clone();
let selection = selection.borrow().clone();
let (view, doc) = current!(cx.editor); // refetch doc
doc.set_selection(view.id, selection);

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

if let Some((id, selection)) = view.jumps.backward(view.id, doc, count) {
view.doc = *id;
let selection = selection.clone();
let selection = selection.borrow().clone();
let (view, doc) = current!(cx.editor); // refetch doc
doc.set_selection(view.id, selection);

Expand Down
4 changes: 1 addition & 3 deletions helix-term/src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ pub fn regex_prompt(
fun: impl Fn(&mut View, &mut Document, Regex, PromptEvent) + 'static,
) {
let (view, doc) = current!(cx.editor);
let doc_id = view.doc;
let snapshot = doc.selection(view.id).clone();
let offset_snapshot = view.offset;
let config = cx.editor.config();
Expand Down Expand Up @@ -109,8 +108,7 @@ pub fn regex_prompt(
doc.set_selection(view.id, snapshot.clone());

if event == PromptEvent::Validate {
// Equivalent to push_jump to store selection just before jump
view.jumps.push((doc_id, snapshot.clone()));
view.jumps.push(doc, snapshot.clone());
}

fun(view, doc, regex, event);
Expand Down
17 changes: 16 additions & 1 deletion helix-view/src/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ use helix_core::auto_pairs::AutoPairs;
use helix_core::Range;
use serde::de::{self, Deserialize, Deserializer};
use serde::Serialize;
use std::cell::Cell;
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::fmt::Display;
use std::future::Future;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::str::FromStr;
use std::sync::Arc;

Expand Down Expand Up @@ -86,6 +87,7 @@ pub struct Document {
pub(crate) id: DocumentId,
text: Rope,
selections: HashMap<ViewId, Selection>,
jump_selections: Vec<Rc<RefCell<Selection>>>,

path: Option<PathBuf>,
encoding: &'static encoding::Encoding,
Expand Down Expand Up @@ -347,6 +349,7 @@ impl Document {
encoding,
text,
selections: HashMap::default(),
jump_selections: vec![Rc::new(RefCell::new(Selection::point(0)))],
indent_style: DEFAULT_INDENT,
line_ending: DEFAULT_LINE_ENDING,
mode: Mode::Normal,
Expand Down Expand Up @@ -744,6 +747,14 @@ impl Document {
}

self.modified_since_accessed = true;

for selection in &self.jump_selections {
let mut selection = selection.borrow_mut();
*selection = selection
.clone()
.map(transaction.changes())
.ensure_invariants(self.text.slice(..));
}
}

if !transaction.changes().is_empty() {
Expand Down Expand Up @@ -1075,6 +1086,10 @@ impl Document {
None => global_config,
}
}

pub fn push_jump_selection(&mut self, selection: Rc<RefCell<Selection>>) {
self.jump_selections.push(selection)
}
}

impl Default for Document {
Expand Down
3 changes: 1 addition & 2 deletions helix-view/src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -905,8 +905,7 @@ impl Editor {
view.remove_document(&id);
}
} else {
let jump = (view.doc, doc.selection(view.id).clone());
view.jumps.push(jump);
view.jumps.push(doc, doc.selection(view.id).clone());
// Set last accessed doc if it is a different document
if doc.id != id {
view.add_to_history(view.doc);
Expand Down
15 changes: 9 additions & 6 deletions helix-view/src/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ use crate::{
};
use helix_core::{pos_at_visual_coords, visual_coords_at_pos, Position, RopeSlice, Selection};

use std::fmt;
use std::{cell::RefCell, fmt, rc::Rc};

type Jump = (DocumentId, Selection);
type Jump = (DocumentId, Rc<RefCell<Selection>>);

#[derive(Debug, Clone)]
pub struct JumpList {
Expand All @@ -23,7 +23,11 @@ impl JumpList {
}
}

pub fn push(&mut self, jump: Jump) {
pub fn push(&mut self, doc: &mut Document, selection: Selection) {
let selection = Rc::new(RefCell::new(selection));
let jump = (doc.id, selection.clone());
doc.push_jump_selection(selection);

self.jumps.truncate(self.current);
// don't push duplicates
if self.jumps.last() != Some(&jump) {
Expand All @@ -45,8 +49,7 @@ impl JumpList {
pub fn backward(&mut self, view_id: ViewId, doc: &mut Document, count: usize) -> Option<&Jump> {
if let Some(current) = self.current.checked_sub(count) {
if self.current == self.jumps.len() {
let jump = (doc.id(), doc.selection(view_id).clone());
self.push(jump);
self.push(doc, doc.selection(view_id).clone());
}
self.current = current;
self.jumps.get(self.current)
Expand Down Expand Up @@ -126,7 +129,7 @@ impl View {
doc,
offset: Position::new(0, 0),
area: Rect::default(), // will get calculated upon inserting into tree
jumps: JumpList::new((doc, Selection::point(0))), // TODO: use actual sel
jumps: JumpList::new((doc, Rc::new(RefCell::new(Selection::point(0))))),
docs_access_history: Vec::new(),
last_modified_docs: [None, None],
object_selections: Vec::new(),
Expand Down

0 comments on commit c186c3f

Please sign in to comment.