on master help sign in
Hacky undo/redo integration.
b765c17Blaž Hrastnik
helix-core/src/state.rs+5−1
@@ -1,6 +1,6 @@
1use crate::graphemes::{nth_next_grapheme_boundary, nth_prev_grapheme_boundary, RopeGraphemes};
1use crate::graphemes::{nth_next_grapheme_boundary, nth_prev_grapheme_boundary, RopeGraphemes};
2use crate::syntax::LOADER;
2use crate::syntax::LOADER;
3−use crate::{Position, Range, Rope, RopeSlice, Selection, Syntax};
3+use crate::{ChangeSet, Position, Range, Rope, RopeSlice, Selection, Syntax};
4use anyhow::Error;
4use anyhow::Error;
5
5
6use std::path::PathBuf;
6use std::path::PathBuf;
@@ -25,6 +25,8 @@ pub struct State {
25
25
26 //
26 //
27 pub syntax: Option<Syntax>,
27 pub syntax: Option<Syntax>,
28+ pub changes: Option<ChangeSet>,
29+ pub old_state: Option<(Rope, Selection)>,
28}
30}
29
31
30#[derive(Copy, Clone, PartialEq, Eq)]
32#[derive(Copy, Clone, PartialEq, Eq)]
@@ -50,6 +52,8 @@ impl State {
50 mode: Mode::Normal,
52 mode: Mode::Normal,
51 restore_cursor: false,
53 restore_cursor: false,
52 syntax: None,
54 syntax: None,
55+ changes: None,
56+ old_state: None,
53 }
57 }
54 }
58 }
55
59
helix-core/src/transaction.rs+7
@@ -369,6 +369,13 @@ impl Transaction {
369 return false;
369 return false;
370 }
370 }
371
371
372+ // Compose this transaction with the previous one
373+ let old_changes = state.changes.take();
374+ state.changes = Some(old_changes.map_or_else(
375+ || self.changes.clone(),
376+ |changes| changes.compose(self.changes.clone()).unwrap(),
377+ ));
378+
372 if let Some(syntax) = &mut state.syntax {
379 if let Some(syntax) = &mut state.syntax {
373 // TODO: no unwrap
380 // TODO: no unwrap
374 syntax.update(&old_doc, &state.doc, &self.changes).unwrap();
381 syntax.update(&old_doc, &state.doc, &self.changes).unwrap();
helix-term/src/editor.rs−28
@@ -426,31 +426,3 @@ impl Editor {
426// TODO: scope matching: biggest union match? [string] & [html, string], [string, html] & [ string, html]
426// TODO: scope matching: biggest union match? [string] & [html, string], [string, html] & [ string, html]
427// can do this by sorting our theme matches based on array len (longest first) then stopping at the
427// can do this by sorting our theme matches based on array len (longest first) then stopping at the
428// first rule that matches (rule.all(|scope| scopes.contains(scope)))
428// first rule that matches (rule.all(|scope| scopes.contains(scope)))
429−//
430−// let visual_x = 0;
431−// let line = ?;
432−// for span in spans {
433−// start(scope) => scopes.push(scope)
434−// span =>
435−// let text = rope.slice(span.start..span.end);
436−// let style = calculate_style(scopes);
437−// for each grapheme in text.graphemes() {
438−// // if newline += lines, continue
439−//
440−// if state.selection.ranges().any(|range| range.contains(char_index)) {
441−// if exactly on cursor {
442−// }
443−// if on primary cursor? {
444−// }
445−// modify style temporarily
446−// }
447−//
448−// // if in bounds
449−//
450−// // if tab, draw tab width
451−// // draw(visual_x, line, grapheme, style)
452−// // increment visual_x by grapheme_width(grapheme)
453−// // increment char_index by grapheme.len_chars()
454−// }
455−// end => scopes.pop()
456−// }
helix-view/src/commands.rs+24−10
@@ -3,7 +3,7 @@ use helix_core::{
3 regex::Regex,
3 regex::Regex,
4 selection,
4 selection,
5 state::{Direction, Granularity, Mode, State},
5 state::{Direction, Granularity, Mode, State},
6− Range, Selection, Tendril, Transaction,
6+ ChangeSet, Range, Selection, Tendril, Transaction,
7};
7};
8use once_cell::sync::Lazy;
8use once_cell::sync::Lazy;
9
9
@@ -253,14 +253,16 @@ pub fn collapse_selection(view: &mut View, _count: usize) {
253 .transform(|range| Range::new(range.head, range.head))
253 .transform(|range| Range::new(range.head, range.head))
254}
254}
255
255
256−// insert mode:
256+fn enter_insert_mode(view: &mut View) {
257−// first we calculate the correct cursors/selections
257+ view.state.mode = Mode::Insert;
258−// then we just append at each cursor
259−// lastly, if it was append mode we shift cursor by 1?
260
258
259+ // HAXX
260+ view.state.changes = Some(ChangeSet::new(view.state.doc()));
261+ view.state.old_state = Some((view.state.doc().clone(), view.state.selection.clone()));
262+}
261// inserts at the start of each selection
263// inserts at the start of each selection
262pub fn insert_mode(view: &mut View, _count: usize) {
264pub fn insert_mode(view: &mut View, _count: usize) {
263− view.state.mode = Mode::Insert;
265+ enter_insert_mode(view);
264
266
265 view.state.selection = view
267 view.state.selection = view
266 .state
268 .state
@@ -270,7 +272,7 @@ pub fn insert_mode(view: &mut View, _count: usize) {
270
272
271// inserts at the end of each selection
273// inserts at the end of each selection
272pub fn append_mode(view: &mut View, _count: usize) {
274pub fn append_mode(view: &mut View, _count: usize) {
273− view.state.mode = Mode::Insert;
275+ enter_insert_mode(view);
274 view.state.restore_cursor = true;
276 view.state.restore_cursor = true;
275
277
276 // TODO: as transaction
278 // TODO: as transaction
@@ -303,21 +305,21 @@ fn selection_lines(state: &State) -> Vec<usize> {
303
305
304// I inserts at the start of each line with a selection
306// I inserts at the start of each line with a selection
305pub fn prepend_to_line(view: &mut View, count: usize) {
307pub fn prepend_to_line(view: &mut View, count: usize) {
306− view.state.mode = Mode::Insert;
308+ enter_insert_mode(view);
307
309
308 move_line_start(view, count);
310 move_line_start(view, count);
309}
311}
310
312
311// A inserts at the end of each line with a selection
313// A inserts at the end of each line with a selection
312pub fn append_to_line(view: &mut View, count: usize) {
314pub fn append_to_line(view: &mut View, count: usize) {
313− view.state.mode = Mode::Insert;
315+ enter_insert_mode(view);
314
316
315 move_line_end(view, count);
317 move_line_end(view, count);
316}
318}
317
319
318// o inserts a new line after each line with a selection
320// o inserts a new line after each line with a selection
319pub fn open_below(view: &mut View, _count: usize) {
321pub fn open_below(view: &mut View, _count: usize) {
320− view.state.mode = Mode::Insert;
322+ enter_insert_mode(view);
321
323
322 let lines = selection_lines(&view.state);
324 let lines = selection_lines(&view.state);
323
325
@@ -356,6 +358,18 @@ pub fn open_below(view: &mut View, _count: usize) {
356pub fn normal_mode(view: &mut View, _count: usize) {
358pub fn normal_mode(view: &mut View, _count: usize) {
357 view.state.mode = Mode::Normal;
359 view.state.mode = Mode::Normal;
358
360
361+ if let Some(changes) = view.state.changes.take() {
362+ // Instead of doing this messy merge we could always commit, and based on transaction
363+ // annotations either add a new layer or compose into the previous one.
364+ let transaction = Transaction::from(changes).with_selection(view.state.selection().clone());
365+ let (doc, selection) = view.state.old_state.take().unwrap();
366+ let mut old_state = State::new(doc);
367+ old_state.selection = selection;
368+
369+ // TODO: take transaction by value?
370+ view.history.commit_revision(&transaction, &old_state);
371+ }
372+
359 // if leaving append mode, move cursor back by 1
373 // if leaving append mode, move cursor back by 1
360 if view.state.restore_cursor {
374 if view.state.restore_cursor {
361 let text = &view.state.doc.slice(..);
375 let text = &view.state.doc.slice(..);