Abstract Transaction::change_by_selection, working del/backspace.
This commit is contained in:
parent
f098166571
commit
d466882d04
3 changed files with 66 additions and 22 deletions
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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())))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
Loading…
Reference in a new issue