From 0b4b0726170884bd3c26ed6f8858c81e5e208230 Mon Sep 17 00:00:00 2001 From: Yifan Song Date: Tue, 19 Sep 2023 12:30:04 +0200 Subject: [PATCH] Back delete a word --- core/src/input/input.rs | 95 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/core/src/input/input.rs b/core/src/input/input.rs index 58964e62..bc049aef 100644 --- a/core/src/input/input.rs +++ b/core/src/input/input.rs @@ -195,6 +195,9 @@ impl Input { match key { Key { code: KeyCode::Backspace, shift: false, ctrl: false, alt: false } => self.backspace(), + Key { code: KeyCode::Char('w'), shift: false, ctrl: true, alt: false } => { + self.backspace_word() + } _ => false, } } @@ -217,6 +220,98 @@ impl Input { self.move_(s.chars().count() as isize) } + /// Borrowed this function from Gitui + /// + /// Get the position of the first character of the next word, or, if there + /// isn't a next word, the `msg.len()`. + /// Returns None when the cursor is already at `msg.len()`. + /// + /// A Word is continuous sequence of alphanumeric characters. + fn next_word_position(&self) -> Option { + let snap = self.snaps.current(); + if snap.cursor >= snap.value.len() { + return None; + } + + let mut was_in_word = self.at_alphanumeric(snap.cursor); + + let mut index = snap.cursor.saturating_add(1); + while index < snap.value.len() { + if !snap.value.is_char_boundary(index) { + index += 1; + continue; + } + + let is_in_word = self.at_alphanumeric(index); + if !was_in_word && is_in_word { + break; + } + was_in_word = is_in_word; + index += 1; + } + Some(index) + } + + /// Borrowed this function from Gitui + /// + /// Get the position of the first character of the previous word, or, if there + /// isn't a previous word, returns `0`. + /// Returns None when the cursor is already at `0`. + /// + /// A Word is continuous sequence of alphanumeric characters. + fn previous_word_position(&self) -> Option { + let snap = self.snaps.current(); + if snap.cursor < 1 { + return None; + } + + let mut was_in_word = false; + + let mut last_pos = snap.cursor; + let mut index = snap.cursor; + while index > 0 { + index -= 1; + if !snap.value.is_char_boundary(index) { + continue; + } + + let is_in_word = self.at_alphanumeric(index); + if was_in_word && !is_in_word { + return Some(last_pos); + } + + last_pos = index; + was_in_word = is_in_word; + } + Some(0) + } + + /// Helper for `next/previous_word_position`. + fn at_alphanumeric(&self, i: usize) -> bool { + self.snaps.current().value[i..].chars().next().map_or(false, char::is_alphanumeric) + } + + fn move_to(&mut self, index: usize) -> bool { + // For now, just reuse move + if let Ok(ind) = isize::try_from(index) { + if let Ok(cur) = isize::try_from(self.snaps.current().cursor) { + let offset = ind - cur; + return self.move_(offset); + } + } + false + } + + pub fn backspace_word(&mut self) -> bool { + if let Some(pos) = self.previous_word_position() { + let snap = self.snaps.current_mut(); + snap.value.replace_range(pos..snap.cursor, ""); + self.flush_value(); + return self.move_to(pos); + } + true + } + pub fn backspace(&mut self) -> bool { let snap = self.snaps.current_mut(); if snap.cursor < 1 {