on master help sign in
store multiple snapshots on the document at once
e8898fdPascal Kuthe
Cargo.lock+1
@@ -1241,6 +1241,7 @@ dependencies = [
1241 "libc",
1241 "libc",
1242 "log",
1242 "log",
1243 "once_cell",
1243 "once_cell",
1244+ "parking_lot",
1244 "serde",
1245 "serde",
1245 "serde_json",
1246 "serde_json",
1246 "slotmap",
1247 "slotmap",
helix-term/src/commands.rs+2−1
@@ -4181,7 +4181,7 @@ pub fn completion(cx: &mut Context) {
4181 iter.reverse();
4181 iter.reverse();
4182 let offset = iter.take_while(|ch| chars::char_is_word(*ch)).count();
4182 let offset = iter.take_while(|ch| chars::char_is_word(*ch)).count();
4183 let start_offset = cursor.saturating_sub(offset);
4183 let start_offset = cursor.saturating_sub(offset);
4184− doc.savepoint(&view);
4184+ let savepoint = doc.savepoint(view);
4185
4185
4186 cx.callback(
4186 cx.callback(
4187 future,
4187 future,
@@ -4209,6 +4209,7 @@ pub fn completion(cx: &mut Context) {
4209 let ui = compositor.find::<ui::EditorView>().unwrap();
4209 let ui = compositor.find::<ui::EditorView>().unwrap();
4210 ui.set_completion(
4210 ui.set_completion(
4211 editor,
4211 editor,
4212+ savepoint,
4212 items,
4213 items,
4213 offset_encoding,
4214 offset_encoding,
4214 start_offset,
4215 start_offset,
helix-term/src/ui/completion.rs+4−4
@@ -1,12 +1,13 @@
1use crate::compositor::{Component, Context, Event, EventResult};
1use crate::compositor::{Component, Context, Event, EventResult};
2use helix_view::{
2use helix_view::{
3+ document::SavePoint,
3 editor::CompleteAction,
4 editor::CompleteAction,
4 theme::{Modifier, Style},
5 theme::{Modifier, Style},
5 ViewId,
6 ViewId,
6};
7};
7use tui::{buffer::Buffer as Surface, text::Span};
8use tui::{buffer::Buffer as Surface, text::Span};
8
9
9−use std::borrow::Cow;
10+use std::{borrow::Cow, sync::Arc};
10
11
11use helix_core::{Change, Transaction};
12use helix_core::{Change, Transaction};
12use helix_view::{graphics::Rect, Document, Editor};
13use helix_view::{graphics::Rect, Document, Editor};
@@ -101,6 +102,7 @@ impl Completion {
101
102
102 pub fn new(
103 pub fn new(
103 editor: &Editor,
104 editor: &Editor,
105+ savepoint: Arc<SavePoint>,
104 mut items: Vec<CompletionItem>,
106 mut items: Vec<CompletionItem>,
105 offset_encoding: helix_lsp::OffsetEncoding,
107 offset_encoding: helix_lsp::OffsetEncoding,
106 start_offset: usize,
108 start_offset: usize,
@@ -213,11 +215,10 @@ impl Completion {
213 let (view, doc) = current!(editor);
215 let (view, doc) = current!(editor);
214
216
215 // if more text was entered, remove it
217 // if more text was entered, remove it
216− doc.restore(view);
218+ doc.restore(view, &savepoint);
217
219
218 match event {
220 match event {
219 PromptEvent::Abort => {
221 PromptEvent::Abort => {
220− doc.restore(view);
221 editor.last_completion = None;
222 editor.last_completion = None;
222 }
223 }
223 PromptEvent::Update => {
224 PromptEvent::Update => {
@@ -235,7 +236,6 @@ impl Completion {
235 );
236 );
236
237
237 // initialize a savepoint
238 // initialize a savepoint
238− doc.savepoint(&view);
239 doc.apply(&transaction, view.id);
239 doc.apply(&transaction, view.id);
240
240
241 editor.last_completion = Some(CompleteAction {
241 editor.last_completion = Some(CompleteAction {
helix-term/src/ui/editor.rs+10−4
@@ -940,17 +940,25 @@ impl EditorView {
940 }
940 }
941 }
941 }
942
942
943+ #[allow(clippy::too_many_arguments)]
943 pub fn set_completion(
944 pub fn set_completion(
944 &mut self,
945 &mut self,
945 editor: &mut Editor,
946 editor: &mut Editor,
947+ savepoint: Arc<SavePoint>,
946 items: Vec<helix_lsp::lsp::CompletionItem>,
948 items: Vec<helix_lsp::lsp::CompletionItem>,
947 offset_encoding: helix_lsp::OffsetEncoding,
949 offset_encoding: helix_lsp::OffsetEncoding,
948 start_offset: usize,
950 start_offset: usize,
949 trigger_offset: usize,
951 trigger_offset: usize,
950 size: Rect,
952 size: Rect,
951 ) {
953 ) {
952− let mut completion =
954+ let mut completion = Completion::new(
953− Completion::new(editor, items, offset_encoding, start_offset, trigger_offset);
955+ editor,
956+ savepoint,
957+ items,
958+ offset_encoding,
959+ start_offset,
960+ trigger_offset,
961+ );
954
962
955 if completion.is_empty() {
963 if completion.is_empty() {
956 // skip if we got no completion results
964 // skip if we got no completion results
@@ -969,8 +977,6 @@ impl EditorView {
969 self.completion = None;
977 self.completion = None;
970
978
971 // Clear any savepoints
979 // Clear any savepoints
972− let doc = doc_mut!(editor);
973− doc.savepoint = None;
974 editor.clear_idle_timer(); // don't retrigger
980 editor.clear_idle_timer(); // don't retrigger
975 }
981 }
976
982
helix-view/Cargo.toml+1
@@ -43,6 +43,7 @@ toml = "0.7"
43log = "~0.4"
43log = "~0.4"
44
44
45which = "4.4"
45which = "4.4"
46+parking_lot = "0.12.1"
46
47
47
48
48[target.'cfg(windows)'.dependencies]
49[target.'cfg(windows)'.dependencies]
helix-view/src/document.rs+54−15
@@ -9,6 +9,7 @@ use helix_core::text_annotations::TextAnnotations;
9use helix_core::Range;
9use helix_core::Range;
10use helix_vcs::{DiffHandle, DiffProviderRegistry};
10use helix_vcs::{DiffHandle, DiffProviderRegistry};
11
11
12+use ::parking_lot::Mutex;
12use serde::de::{self, Deserialize, Deserializer};
13use serde::de::{self, Deserialize, Deserializer};
13use serde::Serialize;
14use serde::Serialize;
14use std::borrow::Cow;
15use std::borrow::Cow;
@@ -18,7 +19,7 @@ use std::fmt::Display;
18use std::future::Future;
19use std::future::Future;
19use std::path::{Path, PathBuf};
20use std::path::{Path, PathBuf};
20use std::str::FromStr;
21use std::str::FromStr;
21−use std::sync::Arc;
22+use std::sync::{Arc, Weak};
22use std::time::SystemTime;
23use std::time::SystemTime;
23
24
24use helix_core::{
25use helix_core::{
@@ -105,6 +106,13 @@ pub struct DocumentSavedEvent {
105pub type DocumentSavedEventResult = Result<DocumentSavedEvent, anyhow::Error>;
106pub type DocumentSavedEventResult = Result<DocumentSavedEvent, anyhow::Error>;
106pub type DocumentSavedEventFuture = BoxFuture<'static, DocumentSavedEventResult>;
107pub type DocumentSavedEventFuture = BoxFuture<'static, DocumentSavedEventResult>;
107
108
109+#[derive(Debug)]
110+pub struct SavePoint {
111+ /// The view this savepoint is associated with
112+ pub view: ViewId,
113+ revert: Mutex<Transaction>,
114+}
115+
108pub struct Document {
116pub struct Document {
109 pub(crate) id: DocumentId,
117 pub(crate) id: DocumentId,
110 text: Rope,
118 text: Rope,
@@ -136,7 +144,7 @@ pub struct Document {
136 pub history: Cell<History>,
144 pub history: Cell<History>,
137 pub config: Arc<dyn DynAccess<Config>>,
145 pub config: Arc<dyn DynAccess<Config>>,
138
146
139− pub savepoint: Option<Transaction>,
147+ savepoints: Vec<Weak<SavePoint>>,
140
148
141 // Last time we wrote to the file. This will carry the time the file was last opened if there
149 // Last time we wrote to the file. This will carry the time the file was last opened if there
142 // were no saves.
150 // were no saves.
@@ -389,7 +397,7 @@ impl Document {
389 diagnostics: Vec::new(),
397 diagnostics: Vec::new(),
390 version: 0,
398 version: 0,
391 history: Cell::new(History::default()),
399 history: Cell::new(History::default()),
392− savepoint: None,
400+ savepoints: Vec::new(),
393 last_saved_time: SystemTime::now(),
401 last_saved_time: SystemTime::now(),
394 last_saved_revision: 0,
402 last_saved_revision: 0,
395 modified_since_accessed: false,
403 modified_since_accessed: false,
@@ -846,11 +854,18 @@ impl Document {
846 }
854 }
847
855
848 // generate revert to savepoint
856 // generate revert to savepoint
849− if self.savepoint.is_some() {
857+ if !self.savepoints.is_empty() {
850− take_with(&mut self.savepoint, |prev_revert| {
851− let revert = transaction.invert(&old_doc);
858+ let revert = transaction.invert(&old_doc);
852− Some(revert.compose(prev_revert.unwrap()))
859+ self.savepoints
860+ .retain_mut(|save_point| match save_point.upgrade() {
861+ Some(savepoint) => {
862+ let mut revert_to_savepoint = savepoint.revert.lock();
863+ *revert_to_savepoint =
864+ revert.clone().compose(mem::take(&mut revert_to_savepoint));
865+ true
866+ }
867+ None => false,
853− });
868+ })
854 }
869 }
855
870
856 // update tree-sitter syntax tree
871 // update tree-sitter syntax tree
@@ -940,15 +955,39 @@ impl Document {
940 self.undo_redo_impl(view, false)
955 self.undo_redo_impl(view, false)
941 }
956 }
942
957
958+ /// Creates a reference counted snapshot (called savpepoint) of the document.
959+ ///
960+ /// The snapshot will remain valid (and updated) idenfinitly as long as ereferences to it exist.
961+ /// Restoring the snapshot will restore the selection and the contents of the document to
962+ /// the state it had when this function was called.
943− pub fn savepoint(&mut self) {
963+ pub fn savepoint(&mut self, view: &View) -> Arc<SavePoint> {
944− self.savepoint =
945− Some(Transaction::new(self.text()).with_selection(self.selection(view.id).clone()));
964+ let revert = Transaction::new(self.text()).with_selection(self.selection(view.id).clone());
965+ let savepoint = Arc::new(SavePoint {
966+ view: view.id,
967+ revert: Mutex::new(revert),
968+ });
969+ self.savepoints.push(Arc::downgrade(&savepoint));
970+ savepoint
946 }
971 }
947
972
948− pub fn restore(&mut self, view: &mut View) {
973+ pub fn restore(&mut self, view: &mut View, savepoint: &SavePoint) {
949− if let Some(revert) = self.savepoint.take() {
974+ assert_eq!(
975+ savepoint.view, view.id,
976+ "Savepoint must not be used with a different view!"
977+ );
978+ // search and remove savepoint using a ptr comparison
979+ // this avoids a deadlock as we need to lock the mutex
980+ let savepoint_idx = self
981+ .savepoints
982+ .iter()
983+ .position(|savepoint_ref| savepoint_ref.as_ptr() == savepoint as *const _)
984+ .expect("Savepoint must belong to this document");
985+
986+ let savepoint_ref = self.savepoints.remove(savepoint_idx);
987+ let mut revert = savepoint.revert.lock();
950− self.apply(&revert, view.id);
988+ self.apply(&revert, view.id);
951− }
989+ *revert = Transaction::new(self.text()).with_selection(self.selection(view.id).clone());
990+ self.savepoints.push(savepoint_ref)
952 }
991 }
953
992
954 fn earlier_later_impl(&mut self, view: &mut View, uk: UndoKind, earlier: bool) -> bool {
993 fn earlier_later_impl(&mut self, view: &mut View, uk: UndoKind, earlier: bool) -> bool {