From fc8982679440995d4609f67ee7e01bada2eb9027 Mon Sep 17 00:00:00 2001 From: shadow cat Date: Sat, 22 Nov 2025 15:01:22 -0500 Subject: [PATCH] ctrl a & word movement --- src/core/text/edit.rs | 139 ++++++++++++++++++++++++++++-------------- 1 file changed, 94 insertions(+), 45 deletions(-) diff --git a/src/core/text/edit.rs b/src/core/text/edit.rs index dc25e0b..600c7df 100644 --- a/src/core/text/edit.rs +++ b/src/core/text/edit.rs @@ -169,12 +169,20 @@ impl<'a> TextEditCtx<'a> { .cursor_motion(self.font_system, cursor, None, motion) { self.text.selection = TextSelection::Pos(cursor); + } else if let TextSelection::Span { start, end } = self.text.selection { + let (start, end) = sort_cursors(start, end); + let sel = &mut self.text.selection; + match motion { + Motion::Left | Motion::LeftWord => *sel = TextSelection::Pos(start), + Motion::Right | Motion::RightWord => *sel = TextSelection::Pos(end), + _ => (), + } } } pub fn replace(&mut self, len: usize, text: &str) { for _ in 0..len { - self.delete(); + self.delete(false); } self.insert_inner(text, false); } @@ -193,25 +201,8 @@ impl<'a> TextEditCtx<'a> { pub fn clear_span(&mut self) -> bool { if let TextSelection::Span { start, end } = self.text.selection { - let lines = &mut self.text.view.buf.lines; - let (start, end) = sort_cursors(start, end); - if start.line == end.line { - let line = &mut lines[start.line]; - let text = line.text(); - let text = text[..start.index].to_string() + &text[end.index..]; - edit_line(line, text); - } else { - // start - let start_text = lines[start.line].text()[..start.index].to_string(); - let end_text = &lines[end.line].text()[end.index..]; - let text = start_text + end_text; - edit_line(&mut lines[start.line], text); - } - // between - let range = (start.line + 1)..=end.line; - if !range.is_empty() { - lines.splice(range, None); - } + self.delete_between(start, end); + let (start, _) = sort_cursors(start, end); self.text.selection = TextSelection::Pos(start); true } else { @@ -219,6 +210,28 @@ impl<'a> TextEditCtx<'a> { } } + pub fn delete_between(&mut self, start: Cursor, end: Cursor) { + let lines = &mut self.text.view.buf.lines; + let (start, end) = sort_cursors(start, end); + if start.line == end.line { + let line = &mut lines[start.line]; + let text = line.text(); + let text = text[..start.index].to_string() + &text[end.index..]; + edit_line(line, text); + } else { + // start + let start_text = lines[start.line].text()[..start.index].to_string(); + let end_text = &lines[end.line].text()[end.index..]; + let text = start_text + end_text; + edit_line(&mut lines[start.line], text); + } + // between + let range = (start.line + 1)..=end.line; + if !range.is_empty() { + lines.splice(range, None); + } + } + fn insert_inner(&mut self, text: &str, mov: bool) { self.clear_span(); if let TextSelection::Pos(cursor) = &mut self.text.selection { @@ -246,35 +259,46 @@ impl<'a> TextEditCtx<'a> { } } - pub fn backspace(&mut self) { + pub fn backspace(&mut self, word: bool) { if !self.clear_span() && let TextSelection::Pos(cursor) = &mut self.text.selection && (cursor.index != 0 || cursor.line != 0) { - self.motion(Motion::Left); - self.delete(); + self.motion(if word { Motion::LeftWord } else { Motion::Left }); + self.delete(word); } } - pub fn delete(&mut self) { + pub fn delete(&mut self, word: bool) { if !self.clear_span() && let TextSelection::Pos(cursor) = &mut self.text.selection { - let lines = &mut self.text.view.buf.lines; - let line = &mut lines[cursor.line]; - if cursor.index == line.text().len() { - if cursor.line == lines.len() - 1 { - return; + if word { + let start = *cursor; + if let Some((end, _)) = + self.text + .buf + .cursor_motion(self.font_system, start, None, Motion::RightWord) + { + self.delete_between(start, end); } - let add = lines.remove(cursor.line + 1).into_text(); - let line = &mut lines[cursor.line]; - let mut cur = line.text().to_string(); - cur.push_str(&add); - edit_line(line, cur); } else { - let mut text = line.text().to_string(); - text.remove(cursor.index); - edit_line(line, text); + let lines = &mut self.text.view.buf.lines; + let line = &mut lines[cursor.line]; + if cursor.index == line.text().len() { + if cursor.line == lines.len() - 1 { + return; + } + let add = lines.remove(cursor.line + 1).into_text(); + let line = &mut lines[cursor.line]; + let mut cur = line.text().to_string(); + cur.push_str(&add); + edit_line(line, cur); + } else { + let mut text = line.text().to_string(); + text.remove(cursor.index); + edit_line(line, text); + } } } } @@ -316,8 +340,8 @@ impl<'a> TextEditCtx<'a> { pub fn apply_event(&mut self, event: &KeyEvent, modifiers: &Modifiers) -> TextInputResult { match &event.logical_key { Key::Named(named) => match named { - NamedKey::Backspace => self.backspace(), - NamedKey::Delete => self.delete(), + NamedKey::Backspace => self.backspace(modifiers.control), + NamedKey::Delete => self.delete(modifiers.control), NamedKey::Space => self.insert(" "), NamedKey::Enter => { if modifiers.shift { @@ -326,8 +350,20 @@ impl<'a> TextEditCtx<'a> { return TextInputResult::Submit; } } - NamedKey::ArrowRight => self.motion(Motion::Right), - NamedKey::ArrowLeft => self.motion(Motion::Left), + NamedKey::ArrowRight => { + if modifiers.control { + self.motion(Motion::RightWord) + } else { + self.motion(Motion::Right) + } + } + NamedKey::ArrowLeft => { + if modifiers.control { + self.motion(Motion::LeftWord) + } else { + self.motion(Motion::Left) + } + } NamedKey::ArrowUp => self.motion(Motion::Up), NamedKey::ArrowDown => self.motion(Motion::Down), NamedKey::Escape => { @@ -345,12 +381,25 @@ impl<'a> TextEditCtx<'a> { let content = self.text.select_content(start, end); return TextInputResult::Copy(content); } - return TextInputResult::Used; } - _ => (), + "a" => { + if !self.text.buf.lines[0].text().is_empty() + || self.text.buf.lines.len() > 1 + { + let lines = &self.text.buf.lines; + let last_line = lines.len() - 1; + let last_idx = lines[last_line].text().len(); + self.text.selection = TextSelection::Span { + start: Cursor::new(0, 0), + end: Cursor::new(last_line, last_idx), + }; + } + } + _ => self.insert(text), } + } else { + self.insert(text); } - self.insert(text) } _ => return TextInputResult::Unused, } @@ -380,7 +429,7 @@ pub enum TextInputResult { Paste, } -#[derive(Default)] +#[derive(Debug, Default)] pub enum TextSelection { #[default] None,