aboutsummaryrefslogtreecommitdiff
path: root/helix-core
diff options
context:
space:
mode:
Diffstat (limited to 'helix-core')
-rw-r--r--helix-core/src/commands.rs34
-rw-r--r--helix-core/src/transaction.rs42
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())))
}
}