Abstract Transaction::change_by_selection, working del/backspace.

This commit is contained in:
Blaž Hrastnik 2020-09-13 11:32:37 +09:00
parent f098166571
commit d466882d04
3 changed files with 66 additions and 22 deletions

View file

@ -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
}

View file

@ -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 acc = Vec::with_capacity(2 * ranges.len() + 1);
let changes = ranges.iter().map(f);
// TODO: verify ranges are ordered and not overlapping.
let mut last = 0;
for range in state.selection.ranges() {
let cur = range.head;
changes.push(Change::Retain(cur));
changes.push(Change::Insert(text.clone()));
last = cur;
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())))
}
}

View file

@ -259,7 +259,7 @@ impl Editor {
}
}
pub async fn print_events(&mut self) {
pub async fn event_loop(&mut self) {
let mut reader = EventStream::new();
let keymap = keymap::default();
@ -284,6 +284,14 @@ impl Editor {
KeyEvent {
code: KeyCode::Esc, ..
} => helix_core::commands::normal_mode(state, 1),
KeyEvent {
code: KeyCode::Backspace,
..
} => helix_core::commands::delete_char_backward(state, 1),
KeyEvent {
code: KeyCode::Delete,
..
} => helix_core::commands::delete_char_forward(state, 1),
KeyEvent {
code: KeyCode::Char(c),
..
@ -320,7 +328,7 @@ impl Editor {
execute!(stdout, terminal::EnterAlternateScreen)?;
self.print_events().await;
self.event_loop().await;
// reset cursor shape
write!(stdout, "\x1B[2 q");