diff --git a/helix-core/src/commands.rs b/helix-core/src/commands.rs index fdb3fab6a..860ec1cb6 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 2f40be76a..130f77b8e 100644 --- a/helix-core/src/transaction.rs +++ b/helix-core/src/transaction.rs @@ -342,31 +342,37 @@ pub fn apply(&self, state: &mut State) -> bool { true } - pub fn insert(state: &State, text: Tendril) -> Self { + pub fn change_by_selection(state: &State, f: F) -> Self + where + F: Fn(&SelectionRange) -> (usize, usize, Option), + { 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(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()))) } } diff --git a/helix-term/src/editor.rs b/helix-term/src/editor.rs index 8e5c0042d..a6ee999c8 100644 --- a/helix-term/src/editor.rs +++ b/helix-term/src/editor.rs @@ -259,7 +259,7 @@ fn render(&mut self) { } } - 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 @@ pub async fn print_events(&mut self) { 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 @@ pub async fn run(&mut self) -> Result<(), Error> { execute!(stdout, terminal::EnterAlternateScreen)?; - self.print_events().await; + self.event_loop().await; // reset cursor shape write!(stdout, "\x1B[2 q");