use super::delete::{
delete_selection, DeleteBigWordEnd, DeleteFindForward, DeleteTillForward, DeleteWordEnd,
};
use super::motion::find_char_forward;
use super::select::{DeleteInnerBetween, DeleteInnerBigWord, DeleteInnerWord};
use super::Execute;
use crate::clipboard::ClipboardTrait;
use crate::{EditorMode, EditorState};
#[derive(Clone, Debug, Copy)]
pub struct ChangeWord(pub usize);
impl Execute for ChangeWord {
fn execute(&mut self, state: &mut EditorState) {
DeleteWordEnd(self.0).execute(state);
state.mode = EditorMode::Insert;
}
fn is_repeatable(&self) -> bool {
true
}
}
#[derive(Clone, Debug, Copy)]
pub struct ChangeBigWord(pub usize);
impl Execute for ChangeBigWord {
fn execute(&mut self, state: &mut EditorState) {
DeleteBigWordEnd(self.0).execute(state);
state.mode = EditorMode::Insert;
}
fn is_repeatable(&self) -> bool {
true
}
}
#[derive(Clone, Debug, Copy)]
pub struct ChangeInnerWord;
impl Execute for ChangeInnerWord {
fn execute(&mut self, state: &mut EditorState) {
DeleteInnerWord.execute(state);
state.mode = EditorMode::Insert;
}
fn is_repeatable(&self) -> bool {
true
}
}
#[derive(Clone, Debug, Copy)]
pub struct ChangeFindForward(pub Option<char>);
impl Execute for ChangeFindForward {
fn execute(&mut self, state: &mut EditorState) {
let Some(target) = self.0 else {
return;
};
if find_char_forward(state, target).is_none() {
return;
}
DeleteFindForward(self.0).execute(state);
state.mode = EditorMode::Insert;
}
fn is_repeatable(&self) -> bool {
true
}
fn char_arg(&mut self) -> Option<&mut Option<char>> {
Some(&mut self.0)
}
}
#[derive(Clone, Debug, Copy)]
pub struct ChangeTillForward(pub Option<char>);
impl Execute for ChangeTillForward {
fn execute(&mut self, state: &mut EditorState) {
let Some(target) = self.0 else {
return;
};
if find_char_forward(state, target).is_none() {
return;
}
DeleteTillForward(self.0).execute(state);
state.mode = EditorMode::Insert;
}
fn is_repeatable(&self) -> bool {
true
}
fn char_arg(&mut self) -> Option<&mut Option<char>> {
Some(&mut self.0)
}
}
#[derive(Clone, Debug, Copy)]
pub struct ChangeInnerBigWord;
impl Execute for ChangeInnerBigWord {
fn execute(&mut self, state: &mut EditorState) {
DeleteInnerBigWord.execute(state);
state.mode = EditorMode::Insert;
}
fn is_repeatable(&self) -> bool {
true
}
}
#[derive(Clone, Debug, Copy)]
pub struct ChangeInnerBetween {
opening: char,
closing: char,
}
impl ChangeInnerBetween {
#[must_use]
pub fn new(opening: char, closing: char) -> Self {
Self { opening, closing }
}
}
impl Execute for ChangeInnerBetween {
fn execute(&mut self, state: &mut EditorState) {
DeleteInnerBetween::new(self.opening, self.closing).execute(state);
state.mode = EditorMode::Insert;
}
fn is_repeatable(&self) -> bool {
true
}
}
#[derive(Clone, Debug, Copy)]
pub struct ChangeSelection;
impl Execute for ChangeSelection {
fn execute(&mut self, state: &mut EditorState) {
if let Some(selection) = state.selection.take() {
state.capture();
let deleted = delete_selection(state, &selection);
state.clip.set_text(deleted.into());
}
}
fn is_repeatable(&self) -> bool {
true
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Index2, Lines};
#[test]
fn test_change_word_enters_insert_mode() {
let mut state = EditorState::new(Lines::from("Hello World"));
state.cursor = Index2::new(0, 0);
ChangeWord(1).execute(&mut state);
assert_eq!(state.lines.to_string(), " World");
assert_eq!(state.cursor, Index2::new(0, 0));
assert_eq!(state.mode, EditorMode::Insert);
}
#[test]
fn test_change_big_word_enters_insert_mode() {
let mut state = EditorState::new(Lines::from("foo.bar baz"));
state.cursor = Index2::new(0, 0);
ChangeBigWord(1).execute(&mut state);
assert_eq!(state.lines.to_string(), " baz");
assert_eq!(state.mode, EditorMode::Insert);
}
#[test]
fn test_change_inner_word() {
let mut state = EditorState::new(Lines::from("Hello World"));
state.cursor = Index2::new(0, 1);
ChangeInnerWord.execute(&mut state);
assert_eq!(state.lines.to_string(), " World");
assert_eq!(state.cursor, Index2::new(0, 0));
assert_eq!(state.mode, EditorMode::Insert);
}
#[test]
fn test_change_inner_word_with_punctuation() {
let mut state = EditorState::new(Lines::from("foo.bar baz"));
state.cursor = Index2::new(0, 0);
ChangeInnerWord.execute(&mut state);
assert_eq!(state.lines.to_string(), ".bar baz");
assert_eq!(state.cursor, Index2::new(0, 0));
}
#[test]
fn test_change_inner_big_word() {
let mut state = EditorState::new(Lines::from("foo.bar baz"));
state.cursor = Index2::new(0, 1);
ChangeInnerBigWord.execute(&mut state);
assert_eq!(state.lines.to_string(), " baz");
assert_eq!(state.cursor, Index2::new(0, 0));
}
#[test]
fn test_change_inner_big_word_on_punctuation() {
let mut state = EditorState::new(Lines::from("foo.bar baz"));
state.cursor = Index2::new(0, 3);
ChangeInnerBigWord.execute(&mut state);
assert_eq!(state.lines.to_string(), " baz");
assert_eq!(state.cursor, Index2::new(0, 0));
}
#[test]
fn test_change_inner_between_enters_insert_mode() {
let mut state = EditorState::new(Lines::from("\"Hello\" World"));
state.cursor = Index2::new(0, 1);
ChangeInnerBetween::new('"', '"').execute(&mut state);
assert_eq!(state.lines.to_string(), "\"\" World");
assert_eq!(state.cursor, Index2::new(0, 1));
assert_eq!(state.mode, EditorMode::Insert);
}
}