diff options
Diffstat (limited to 'helix-core')
-rw-r--r-- | helix-core/src/commands.rs | 34 | ||||
-rw-r--r-- | helix-core/src/transaction.rs | 42 |
2 files changed, 56 insertions, 20 deletions
diff --git a/helix-core/src/commands.rs b/helix-core/src/commands.rs index fdb3fab6..860ec1cb 100644 --- a/helix-core/src/commands.rs +++ b/helix-core/src/commands.rs @@ -1,4 +1,4 @@ -use crate::graphemes::next_grapheme_boundary; +use crate::graphemes; use crate::selection::Range; use crate::state::{Direction, Granularity, Mode, State}; use crate::transaction::{ChangeSet, Transaction}; @@ -53,7 +53,10 @@ pub fn append_mode(state: &mut State, _count: usize) { let text = &state.doc.slice(..); state.selection = state.selection.transform(|range| { // TODO: to() + next char - Range::new(range.from(), next_grapheme_boundary(text, range.to())) + Range::new( + range.from(), + graphemes::next_grapheme_boundary(text, range.to()), + ) }) } @@ -75,3 +78,30 @@ pub fn insert_char(state: &mut State, c: char) { transaction.apply(state); // TODO: need to store into history if successful } + +// TODO: handle indent-aware delete +pub fn delete_char_backward(state: &mut State, count: usize) { + let text = &state.doc.slice(..); + let transaction = Transaction::change_by_selection(state, |range| { + ( + graphemes::nth_prev_grapheme_boundary(text, range.head, count), + range.head, + None, + ) + }); + transaction.apply(state); + // TODO: need to store into history if successful +} + +pub fn delete_char_forward(state: &mut State, count: usize) { + let text = &state.doc.slice(..); + let transaction = Transaction::change_by_selection(state, |range| { + ( + graphemes::nth_next_grapheme_boundary(text, range.head, count), + range.head, + None, + ) + }); + transaction.apply(state); + // TODO: need to store into history if successful +} diff --git a/helix-core/src/transaction.rs b/helix-core/src/transaction.rs index 2f40be76..130f77b8 100644 --- a/helix-core/src/transaction.rs +++ b/helix-core/src/transaction.rs @@ -342,31 +342,37 @@ impl Transaction { true } - pub fn insert(state: &State, text: Tendril) -> Self { + pub fn change_by_selection<F>(state: &State, f: F) -> Self + where + F: Fn(&SelectionRange) -> (usize, usize, Option<Tendril>), + { let len = state.doc.len_chars(); let ranges = state.selection.ranges(); - let mut changes = Vec::with_capacity(2 * ranges.len() + 1); - let mut last = 0; + let mut acc = Vec::with_capacity(2 * ranges.len() + 1); + + let changes = ranges.iter().map(f); + + // TODO: verify ranges are ordered and not overlapping. - for range in state.selection.ranges() { - let cur = range.head; - changes.push(Change::Retain(cur)); - changes.push(Change::Insert(text.clone())); - last = cur; + let mut last = 0; + for (from, to, tendril) in changes { + // TODO: need to fill the in-between ranges too + // Retain from last "to" to current "from" + acc.push(Change::Retain(from - last)); + match tendril { + Some(text) => acc.push(Change::Insert(text)), + None => acc.push(Change::Delete(to - from)), + } + last = to; } - changes.push(Change::Retain(len - last)); + acc.push(Change::Retain(len - last)); - Self::from(ChangeSet { changes, len }) + Self::from(ChangeSet { changes: acc, len }) } - pub fn change_selection<F>(selection: Selection, f: F) -> Self - where - F: Fn(SelectionRange) -> ChangeSet, - { - selection.ranges().iter().map(|range| true); - // TODO: most idiomatic would be to return a - // Change { from: x, to: y, insert: "str" } - unimplemented!() + /// Insert text at each selection head. + pub fn insert(state: &State, text: Tendril) -> Self { + Self::change_by_selection(state, |range| (range.head, range.head, Some(text.clone()))) } } |