diff --git a/src/buffer/actions.rs b/src/buffer/actions.rs index fa41ed8..dc4a5f9 100644 --- a/src/buffer/actions.rs +++ b/src/buffer/actions.rs @@ -121,11 +121,13 @@ impl Buffer { } else { self.primary_cursor.clamp(self.scroll_position, window_size.visible_byte_count()); } + self.combine_cursors_if_overlapping(); } pub fn scroll_up(&mut self, window_size: WindowSize) { self.scroll_position = self.scroll_position.saturating_sub(BYTES_PER_LINE); + if window_size.hex_rows() > LINES_OF_PADDING * 2 { self.primary_cursor.clamp( self.scroll_position + BYTES_OF_PADDING, @@ -134,6 +136,7 @@ impl Buffer { } else { self.primary_cursor.clamp(self.scroll_position, window_size.visible_byte_count()); } + self.combine_cursors_if_overlapping(); } @@ -210,6 +213,7 @@ impl Buffer { } else { self.primary_cursor.clamp(self.scroll_position, window_size.visible_byte_count()); } + self.combine_cursors_if_overlapping(); } @@ -226,6 +230,7 @@ impl Buffer { } else { self.primary_cursor.clamp(self.scroll_position, window_size.visible_byte_count()); } + self.combine_cursors_if_overlapping(); } @@ -340,7 +345,6 @@ impl Buffer { self.cursors.sort_by_key(|cursor| cursor.head); self.combine_cursors_if_overlapping(); - self.rotate_selections_forward(window_size); } @@ -576,6 +580,7 @@ impl Buffer { cursor.head = mark_after_cursor - 1; } + self.combine_cursors_if_overlapping(); self.clamp_screen_to_primary_cursor(window_size); } @@ -600,6 +605,7 @@ impl Buffer { } } + self.combine_cursors_if_overlapping(); self.clamp_screen_to_primary_cursor(window_size); } @@ -625,6 +631,7 @@ impl Buffer { } } + self.combine_cursors_if_overlapping(); self.clamp_screen_to_primary_cursor(window_size); } @@ -696,7 +703,7 @@ fn inspect(selection: &[u8]) -> Vec> { let int = nat.and_then(|nat| nat_to_int_if_different(nat, selection.len())); let utf8 = str::from_utf8(selection).ok() - .filter(|_| selection.len() == 1) + .filter(|_| selection.len() != 1) .map(|utf8| utf8.trim_suffix('\0')) .filter(|utf8| !utf8.contains(is_illegal_control_character)) .map(|utf8| Span::from(format!("\"{utf8}\"")).red()); diff --git a/src/config/default.rs b/src/config/default.rs index 7e7f7df..9db01d7 100644 --- a/src/config/default.rs +++ b/src/config/default.rs @@ -1,4 +1,8 @@ -use crate::{action::{AppAction, BufferAction, CursorAction}, buffer::{Mode, PartialAction}, config::Config}; +use crate::{action::{AppAction, BufferAction, CursorAction}, buffer::{Mode, PartialAction}, config::{Config, Keypress}}; + +fn keypress(string: &str) -> Keypress { + string.try_into().unwrap() +} impl Default for Config { #[allow(clippy::too_many_lines)] @@ -10,255 +14,255 @@ impl Default for Config { [ (Mode::Normal, [ (None, [ - ("q".try_into().unwrap(), QuitIfSaved.into()), - ("Q".try_into().unwrap(), Quit.into()), + (keypress("q"), QuitIfSaved.into()), + (keypress("Q"), Quit.into()), - ("v".try_into().unwrap(), SelectMode.into()), + (keypress("v"), SelectMode.into()), - ("g".try_into().unwrap(), Goto.into()), - ("z".try_into().unwrap(), View.into()), - ("r".try_into().unwrap(), Replace.into()), - (" ".try_into().unwrap(), Space.into()), - ("*".try_into().unwrap(), Repeat.into()), - ("t".try_into().unwrap(), To.into()), + (keypress("g"), Goto.into()), + (keypress("z"), View.into()), + (keypress("r"), Replace.into()), + (keypress(" "), Space.into()), + (keypress("*"), Repeat.into()), + (keypress("t"), To.into()), - ("i".try_into().unwrap(), MoveByteUp.into()), - ("k".try_into().unwrap(), MoveByteDown.into()), - ("j".try_into().unwrap(), MoveByteLeft.into()), - ("l".try_into().unwrap(), MoveByteRight.into()), + (keypress("i"), MoveByteUp.into()), + (keypress("k"), MoveByteDown.into()), + (keypress("j"), MoveByteLeft.into()), + (keypress("l"), MoveByteRight.into()), - ("up".try_into().unwrap(), MoveByteUp.into()), - ("down".try_into().unwrap(), MoveByteDown.into()), - ("left".try_into().unwrap(), MoveByteLeft.into()), - ("right".try_into().unwrap(), MoveByteRight.into()), + (keypress("up"), MoveByteUp.into()), + (keypress("down"), MoveByteDown.into()), + (keypress("left"), MoveByteLeft.into()), + (keypress("right"), MoveByteRight.into()), - ("G".try_into().unwrap(), GotoFileEnd.into()), + (keypress("G"), GotoFileEnd.into()), - ("C-e".try_into().unwrap(), ScrollDown.into()), - ("C-y".try_into().unwrap(), ScrollUp.into()), + (keypress("C-e"), ScrollDown.into()), + (keypress("C-y"), ScrollUp.into()), - ("C-d".try_into().unwrap(), PageCursorHalfDown.into()), - ("C-u".try_into().unwrap(), PageCursorHalfUp.into()), + (keypress("C-d"), PageCursorHalfDown.into()), + (keypress("C-u"), PageCursorHalfUp.into()), - ("C-f".try_into().unwrap(), PageDown.into()), - ("C-b".try_into().unwrap(), PageUp.into()), + (keypress("C-f"), PageDown.into()), + (keypress("C-b"), PageUp.into()), - ("w".try_into().unwrap(), MoveNextWordStart.into()), - ("e".try_into().unwrap(), MoveNextWordEnd.into()), - ("b".try_into().unwrap(), MovePreviousWordStart.into()), + (keypress("w"), MoveNextWordStart.into()), + (keypress("e"), MoveNextWordEnd.into()), + (keypress("b"), MovePreviousWordStart.into()), - (";".try_into().unwrap(), CollapseSelection.into()), - ("A-;".try_into().unwrap(), FlipSelections.into()), + (keypress(";"), CollapseSelection.into()), + (keypress("A-;"), FlipSelections.into()), - ("x".try_into().unwrap(), ExtendLineBelow.into()), - ("X".try_into().unwrap(), ExtendLineAbove.into()), + (keypress("x"), ExtendLineBelow.into()), + (keypress("X"), ExtendLineAbove.into()), - ("d".try_into().unwrap(), Delete.into()), + (keypress("d"), Delete.into()), - ("u".try_into().unwrap(), Undo.into()), - ("U".try_into().unwrap(), Redo.into()), + (keypress("u"), Undo.into()), + (keypress("U"), Redo.into()), - ("C-j".try_into().unwrap(), PreviousBuffer.into()), - ("C-l".try_into().unwrap(), NextBuffer.into()), + (keypress("C-j"), PreviousBuffer.into()), + (keypress("C-l"), NextBuffer.into()), - ("C".try_into().unwrap(), CopySelectionOnNextLine.into()), + (keypress("C"), CopySelectionOnNextLine.into()), - ("(".try_into().unwrap(), RotateSelectionsBackward.into()), - (")".try_into().unwrap(), RotateSelectionsForward.into()), + (keypress("("), RotateSelectionsBackward.into()), + (keypress(")"), RotateSelectionsForward.into()), - (",".try_into().unwrap(), KeepPrimarySelection.into()), - ("A-,".try_into().unwrap(), RemovePrimarySelection.into()), + (keypress(","), KeepPrimarySelection.into()), + (keypress("A-,"), RemovePrimarySelection.into()), - ("1".try_into().unwrap(), SplitSelectionsInto1s.into()), - ("2".try_into().unwrap(), SplitSelectionsInto2s.into()), - ("3".try_into().unwrap(), SplitSelectionsInto3s.into()), - ("4".try_into().unwrap(), SplitSelectionsInto4s.into()), - ("5".try_into().unwrap(), SplitSelectionsInto5s.into()), - ("6".try_into().unwrap(), SplitSelectionsInto6s.into()), - ("7".try_into().unwrap(), SplitSelectionsInto7s.into()), - ("8".try_into().unwrap(), SplitSelectionsInto8s.into()), - ("9".try_into().unwrap(), SplitSelectionsInto9s.into()), + (keypress("1"), SplitSelectionsInto1s.into()), + (keypress("2"), SplitSelectionsInto2s.into()), + (keypress("3"), SplitSelectionsInto3s.into()), + (keypress("4"), SplitSelectionsInto4s.into()), + (keypress("5"), SplitSelectionsInto5s.into()), + (keypress("6"), SplitSelectionsInto6s.into()), + (keypress("7"), SplitSelectionsInto7s.into()), + (keypress("8"), SplitSelectionsInto8s.into()), + (keypress("9"), SplitSelectionsInto9s.into()), - ("J".try_into().unwrap(), JumpToSelectedOffsetRelativeToMark.into()), - ("A-J".try_into().unwrap(), JumpToSelectedOffset.into()), + (keypress("J"), JumpToSelectedOffsetRelativeToMark.into()), + (keypress("A-J"), JumpToSelectedOffset.into()), - ("m".try_into().unwrap(), ToggleMark.into()), + (keypress("m"), ToggleMark.into()), - ("y".try_into().unwrap(), Yank.into()), + (keypress("y"), Yank.into()), - ("C- ".try_into().unwrap(), InspectSelection.into()), - ("A- ".try_into().unwrap(), InspectSelectionColor.into()), + (keypress("C- "), InspectSelection.into()), + (keypress("A- "), InspectSelectionColor.into()), ].into()), (Some(PartialAction::Goto), [ - ("j".try_into().unwrap(), GotoLineStart.into()), - ("l".try_into().unwrap(), GotoLineEnd.into()), + (keypress("j"), GotoLineStart.into()), + (keypress("l"), GotoLineEnd.into()), - ("g".try_into().unwrap(), GotoFileStart.into()), + (keypress("g"), GotoFileStart.into()), ].into()), (Some(PartialAction::View), [ - ("z".try_into().unwrap(), AlignViewCenter.into()), - ("b".try_into().unwrap(), AlignViewBottom.into()), - ("t".try_into().unwrap(), AlignViewTop.into()), + (keypress("z"), AlignViewCenter.into()), + (keypress("b"), AlignViewBottom.into()), + (keypress("t"), AlignViewTop.into()), ].into()), (Some(PartialAction::Space), [ - ("w".try_into().unwrap(), Save.into()), + (keypress("w"), Save.into()), ].into()), (Some(PartialAction::Repeat), [ - ("i".try_into().unwrap(), MoveByteUp.into()), - ("k".try_into().unwrap(), MoveByteDown.into()), - ("j".try_into().unwrap(), MoveByteLeft.into()), - ("l".try_into().unwrap(), MoveByteRight.into()), + (keypress("i"), MoveByteUp.into()), + (keypress("k"), MoveByteDown.into()), + (keypress("j"), MoveByteLeft.into()), + (keypress("l"), MoveByteRight.into()), - ("up".try_into().unwrap(), MoveByteUp.into()), - ("down".try_into().unwrap(), MoveByteDown.into()), - ("left".try_into().unwrap(), MoveByteLeft.into()), - ("right".try_into().unwrap(), MoveByteRight.into()), + (keypress("up"), MoveByteUp.into()), + (keypress("down"), MoveByteDown.into()), + (keypress("left"), MoveByteLeft.into()), + (keypress("right"), MoveByteRight.into()), - ("C-e".try_into().unwrap(), ScrollDown.into()), - ("C-y".try_into().unwrap(), ScrollUp.into()), + (keypress("C-e"), ScrollDown.into()), + (keypress("C-y"), ScrollUp.into()), - ("C-d".try_into().unwrap(), PageCursorHalfDown.into()), - ("C-u".try_into().unwrap(), PageCursorHalfUp.into()), + (keypress("C-d"), PageCursorHalfDown.into()), + (keypress("C-u"), PageCursorHalfUp.into()), - ("C-f".try_into().unwrap(), PageDown.into()), - ("C-b".try_into().unwrap(), PageUp.into()), + (keypress("C-f"), PageDown.into()), + (keypress("C-b"), PageUp.into()), - ("w".try_into().unwrap(), MoveNextWordStart.into()), - ("e".try_into().unwrap(), MoveNextWordEnd.into()), - ("b".try_into().unwrap(), MovePreviousWordStart.into()), + (keypress("w"), MoveNextWordStart.into()), + (keypress("e"), MoveNextWordEnd.into()), + (keypress("b"), MovePreviousWordStart.into()), - ("x".try_into().unwrap(), ExtendLineBelow.into()), - ("X".try_into().unwrap(), ExtendLineAbove.into()), + (keypress("x"), ExtendLineBelow.into()), + (keypress("X"), ExtendLineAbove.into()), - ("d".try_into().unwrap(), Delete.into()), + (keypress("d"), Delete.into()), - ("C".try_into().unwrap(), CopySelectionOnNextLine.into()), + (keypress("C"), CopySelectionOnNextLine.into()), ].into()), (Some(PartialAction::To), [ - ("m".try_into().unwrap(), ExtendToMark.into()), - ("0".try_into().unwrap(), ExtendToNull.into()), - ("f".try_into().unwrap(), ExtendToFF.into()), + (keypress("m"), ExtendToMark.into()), + (keypress("0"), ExtendToNull.into()), + (keypress("f"), ExtendToFF.into()), ].into()), ].into()), (Mode::Select, [ (None, [ - ("q".try_into().unwrap(), QuitIfSaved.into()), - ("Q".try_into().unwrap(), Quit.into()), + (keypress("q"), QuitIfSaved.into()), + (keypress("Q"), Quit.into()), - ("v".try_into().unwrap(), NormalMode.into()), + (keypress("v"), NormalMode.into()), - ("g".try_into().unwrap(), Goto.into()), - ("z".try_into().unwrap(), View.into()), - ("r".try_into().unwrap(), Replace.into()), - (" ".try_into().unwrap(), Space.into()), - ("*".try_into().unwrap(), Repeat.into()), - ("t".try_into().unwrap(), To.into()), + (keypress("g"), Goto.into()), + (keypress("z"), View.into()), + (keypress("r"), Replace.into()), + (keypress(" "), Space.into()), + (keypress("*"), Repeat.into()), + (keypress("t"), To.into()), - ("i".try_into().unwrap(), ExtendByteUp.into()), - ("k".try_into().unwrap(), ExtendByteDown.into()), - ("j".try_into().unwrap(), ExtendByteLeft.into()), - ("l".try_into().unwrap(), ExtendByteRight.into()), + (keypress("i"), ExtendByteUp.into()), + (keypress("k"), ExtendByteDown.into()), + (keypress("j"), ExtendByteLeft.into()), + (keypress("l"), ExtendByteRight.into()), - ("up".try_into().unwrap(), ExtendByteUp.into()), - ("down".try_into().unwrap(), ExtendByteDown.into()), - ("left".try_into().unwrap(), ExtendByteLeft.into()), - ("right".try_into().unwrap(), ExtendByteRight.into()), + (keypress("up"), ExtendByteUp.into()), + (keypress("down"), ExtendByteDown.into()), + (keypress("left"), ExtendByteLeft.into()), + (keypress("right"), ExtendByteRight.into()), - ("C-e".try_into().unwrap(), ScrollDown.into()), - ("C-y".try_into().unwrap(), ScrollUp.into()), + (keypress("C-e"), ScrollDown.into()), + (keypress("C-y"), ScrollUp.into()), - ("C-d".try_into().unwrap(), PageCursorHalfDown.into()), - ("C-u".try_into().unwrap(), PageCursorHalfUp.into()), + (keypress("C-d"), PageCursorHalfDown.into()), + (keypress("C-u"), PageCursorHalfUp.into()), - ("C-f".try_into().unwrap(), PageDown.into()), - ("C-b".try_into().unwrap(), PageUp.into()), + (keypress("C-f"), PageDown.into()), + (keypress("C-b"), PageUp.into()), - ("w".try_into().unwrap(), ExtendNextWordStart.into()), - ("e".try_into().unwrap(), ExtendNextWordEnd.into()), - ("b".try_into().unwrap(), ExtendPreviousWordStart.into()), + (keypress("w"), ExtendNextWordStart.into()), + (keypress("e"), ExtendNextWordEnd.into()), + (keypress("b"), ExtendPreviousWordStart.into()), - (";".try_into().unwrap(), CollapseSelection.into()), - ("A-;".try_into().unwrap(), FlipSelections.into()), + (keypress(";"), CollapseSelection.into()), + (keypress("A-;"), FlipSelections.into()), - ("x".try_into().unwrap(), ExtendLineBelow.into()), - ("X".try_into().unwrap(), ExtendLineAbove.into()), + (keypress("x"), ExtendLineBelow.into()), + (keypress("X"), ExtendLineAbove.into()), - ("d".try_into().unwrap(), Delete.into()), + (keypress("d"), Delete.into()), - ("u".try_into().unwrap(), Undo.into()), - ("U".try_into().unwrap(), Redo.into()), + (keypress("u"), Undo.into()), + (keypress("U"), Redo.into()), - ("C".try_into().unwrap(), CopySelectionOnNextLine.into()), + (keypress("C"), CopySelectionOnNextLine.into()), - ("(".try_into().unwrap(), RotateSelectionsBackward.into()), - (")".try_into().unwrap(), RotateSelectionsForward.into()), + (keypress("("), RotateSelectionsBackward.into()), + (keypress(")"), RotateSelectionsForward.into()), - (",".try_into().unwrap(), KeepPrimarySelection.into()), - ("A-,".try_into().unwrap(), RemovePrimarySelection.into()), + (keypress(","), KeepPrimarySelection.into()), + (keypress("A-,"), RemovePrimarySelection.into()), - ("1".try_into().unwrap(), SplitSelectionsInto1s.into()), - ("2".try_into().unwrap(), SplitSelectionsInto2s.into()), - ("3".try_into().unwrap(), SplitSelectionsInto3s.into()), - ("4".try_into().unwrap(), SplitSelectionsInto4s.into()), - ("5".try_into().unwrap(), SplitSelectionsInto5s.into()), - ("6".try_into().unwrap(), SplitSelectionsInto6s.into()), - ("7".try_into().unwrap(), SplitSelectionsInto7s.into()), - ("8".try_into().unwrap(), SplitSelectionsInto8s.into()), - ("9".try_into().unwrap(), SplitSelectionsInto9s.into()), + (keypress("1"), SplitSelectionsInto1s.into()), + (keypress("2"), SplitSelectionsInto2s.into()), + (keypress("3"), SplitSelectionsInto3s.into()), + (keypress("4"), SplitSelectionsInto4s.into()), + (keypress("5"), SplitSelectionsInto5s.into()), + (keypress("6"), SplitSelectionsInto6s.into()), + (keypress("7"), SplitSelectionsInto7s.into()), + (keypress("8"), SplitSelectionsInto8s.into()), + (keypress("9"), SplitSelectionsInto9s.into()), - ("J".try_into().unwrap(), JumpToSelectedOffsetRelativeToMark.into()), - ("A-J".try_into().unwrap(), JumpToSelectedOffset.into()), + (keypress("J"), JumpToSelectedOffsetRelativeToMark.into()), + (keypress("A-J"), JumpToSelectedOffset.into()), - ("m".try_into().unwrap(), ToggleMark.into()), + (keypress("m"), ToggleMark.into()), - ("y".try_into().unwrap(), Yank.into()), + (keypress("y"), Yank.into()), - ("C- ".try_into().unwrap(), InspectSelection.into()), - ("A- ".try_into().unwrap(), InspectSelectionColor.into()), + (keypress("C- "), InspectSelection.into()), + (keypress("A- "), InspectSelectionColor.into()), ].into()), (Some(PartialAction::View), [ - ("z".try_into().unwrap(), AlignViewCenter.into()), - ("b".try_into().unwrap(), AlignViewBottom.into()), - ("t".try_into().unwrap(), AlignViewTop.into()), + (keypress("z"), AlignViewCenter.into()), + (keypress("b"), AlignViewBottom.into()), + (keypress("t"), AlignViewTop.into()), ].into()), (Some(PartialAction::Space), [ - ("w".try_into().unwrap(), Save.into()), + (keypress("w"), Save.into()), ].into()), (Some(PartialAction::Repeat), [ - ("i".try_into().unwrap(), ExtendByteUp.into()), - ("k".try_into().unwrap(), ExtendByteDown.into()), - ("j".try_into().unwrap(), ExtendByteLeft.into()), - ("l".try_into().unwrap(), ExtendByteRight.into()), + (keypress("i"), ExtendByteUp.into()), + (keypress("k"), ExtendByteDown.into()), + (keypress("j"), ExtendByteLeft.into()), + (keypress("l"), ExtendByteRight.into()), - ("up".try_into().unwrap(), ExtendByteUp.into()), - ("down".try_into().unwrap(), ExtendByteDown.into()), - ("left".try_into().unwrap(), ExtendByteLeft.into()), - ("right".try_into().unwrap(), ExtendByteRight.into()), + (keypress("up"), ExtendByteUp.into()), + (keypress("down"), ExtendByteDown.into()), + (keypress("left"), ExtendByteLeft.into()), + (keypress("right"), ExtendByteRight.into()), - ("C-e".try_into().unwrap(), ScrollDown.into()), - ("C-y".try_into().unwrap(), ScrollUp.into()), + (keypress("C-e"), ScrollDown.into()), + (keypress("C-y"), ScrollUp.into()), - ("C-d".try_into().unwrap(), PageCursorHalfDown.into()), - ("C-u".try_into().unwrap(), PageCursorHalfUp.into()), + (keypress("C-d"), PageCursorHalfDown.into()), + (keypress("C-u"), PageCursorHalfUp.into()), - ("C-f".try_into().unwrap(), PageDown.into()), - ("C-b".try_into().unwrap(), PageUp.into()), + (keypress("C-f"), PageDown.into()), + (keypress("C-b"), PageUp.into()), - ("w".try_into().unwrap(), ExtendNextWordStart.into()), - ("e".try_into().unwrap(), ExtendNextWordEnd.into()), - ("b".try_into().unwrap(), ExtendPreviousWordStart.into()), + (keypress("w"), ExtendNextWordStart.into()), + (keypress("e"), ExtendNextWordEnd.into()), + (keypress("b"), ExtendPreviousWordStart.into()), - ("x".try_into().unwrap(), ExtendLineBelow.into()), - ("X".try_into().unwrap(), ExtendLineAbove.into()), + (keypress("x"), ExtendLineBelow.into()), + (keypress("X"), ExtendLineAbove.into()), - ("d".try_into().unwrap(), Delete.into()), + (keypress("d"), Delete.into()), - ("C".try_into().unwrap(), CopySelectionOnNextLine.into()), + (keypress("C"), CopySelectionOnNextLine.into()), ].into()), (Some(PartialAction::To), [ - ("m".try_into().unwrap(), ExtendToMark.into()), - ("0".try_into().unwrap(), ExtendToNull.into()), - ("f".try_into().unwrap(), ExtendToFF.into()), + (keypress("m"), ExtendToMark.into()), + (keypress("0"), ExtendToNull.into()), + (keypress("f"), ExtendToFF.into()), ].into()), ].into()) ].into() diff --git a/src/cursor.rs b/src/cursor.rs index ee1c9b5..9e81d13 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -1,5 +1,6 @@ use std::{cmp::{max, min}, mem::swap, ops::RangeInclusive}; -use crate::{BYTES_PER_LINE, action::CursorAction}; + +mod actions; #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub struct Cursor { @@ -75,443 +76,4 @@ impl Cursor { self.tail = min(self.tail, other.tail); } } - - pub fn execute( - &mut self, - action: CursorAction, - max_contents_index: usize - ) { - match action { - CursorAction::MoveByteUp => self.move_byte_up(), - CursorAction::MoveByteDown => self.move_byte_down(max_contents_index), - CursorAction::MoveByteLeft => self.move_byte_left(), - CursorAction::MoveByteRight => self.move_byte_right(max_contents_index), - - CursorAction::ExtendByteUp => self.extend_byte_up(), - CursorAction::ExtendByteDown => self.extend_byte_down(max_contents_index), - CursorAction::ExtendByteLeft => self.extend_byte_left(), - CursorAction::ExtendByteRight => self.extend_byte_right(max_contents_index), - - CursorAction::GotoLineStart => self.goto_line_start(), - CursorAction::GotoLineEnd => self.goto_line_end(max_contents_index), - CursorAction::GotoFileStart => self.goto_file_start(), - CursorAction::GotoFileEnd => self.goto_file_end(max_contents_index), - - CursorAction::MoveNextWordStart => self.move_next_word_start(max_contents_index), - CursorAction::MoveNextWordEnd => self.move_next_word_end(max_contents_index), - CursorAction::MovePreviousWordStart => self.move_previous_word_start(), - - CursorAction::ExtendNextWordStart => self.extend_next_word_start(max_contents_index), - CursorAction::ExtendNextWordEnd => self.extend_next_word_end(max_contents_index), - CursorAction::ExtendPreviousWordStart => self.extend_previous_word_start(), - - CursorAction::ExtendLineBelow => self.extend_line_below(max_contents_index), - CursorAction::ExtendLineAbove => self.extend_line_above(max_contents_index), - } - } - - pub const fn move_byte_up(&mut self) { - if self.head >= BYTES_PER_LINE { - self.head -= BYTES_PER_LINE; - self.collapse(); - } - } - - pub const fn move_byte_down(&mut self, max: usize) { - if max - self.head >= BYTES_PER_LINE { - self.head += BYTES_PER_LINE; - self.collapse(); - } - } - - pub const fn move_byte_left(&mut self) { - if self.head >= 1 { - self.head -= 1; - self.collapse(); - } - } - - pub const fn move_byte_right(&mut self, max: usize) { - if max - self.head >= 1 { - self.head += 1; - self.collapse(); - } - } - - pub const fn extend_byte_up(&mut self) { - if self.head >= BYTES_PER_LINE { - self.head -= BYTES_PER_LINE; - } - } - - pub const fn extend_byte_down(&mut self, max: usize) { - if max - self.head >= BYTES_PER_LINE { - self.head += BYTES_PER_LINE; - } - } - - pub const fn extend_byte_left(&mut self) { - if self.head >= 1 { - self.head -= 1; - } - } - - pub const fn extend_byte_right(&mut self, max: usize) { - if max - self.head >= 1 { - self.head += 1; - } - } - - pub const fn goto_line_start(&mut self) { - self.head -= self.head % BYTES_PER_LINE; - self.collapse(); - } - - pub fn goto_line_end(&mut self, max: usize) { - self.head = min( - self.head + BYTES_PER_LINE - 1 - (self.head % BYTES_PER_LINE), - max - ); - self.collapse(); - } - - pub const fn goto_file_start(&mut self) { - self.head %= BYTES_PER_LINE; - self.collapse(); - } - - pub const fn goto_file_end(&mut self, max: usize) { - self.head += previous_multiple_of(BYTES_PER_LINE, max + 1 - self.head); - - self.collapse(); - } - - pub fn move_next_word_start(&mut self, max: usize) { - if self.head == max { return; } - - if self.head.is_multiple_of(4) { // at the beginning of a word - self.head = (self.head + 4).min(max); - } else { - self.head = self.head.next_multiple_of(4).min(max); - } - self.collapse(); - } - - pub fn move_next_word_end(&mut self, max: usize) { - if self.head == max { return; } - - self.collapse(); - if self.head % 4 == 3 { // at the end of a word - self.tail = self.head + 1; - self.head = (self.head + 4).min(max); - } else { - self.head = ((self.head + 1).next_multiple_of(4) - 1).min(max); - } - } - - pub const fn move_previous_word_start(&mut self) { - if self.head == 0 { return; } - - self.collapse(); - if self.head.is_multiple_of(4) { // at the beginning of a word - self.tail = self.head - 1; - self.head -= 4; - } else { - self.head -= self.head % 4; - } - } - - pub fn extend_next_word_start(&mut self, max: usize) { - if self.head == max { return; } - - if self.head.is_multiple_of(4) { // at the beginning of a word - self.head = (self.head + 4).min(max); - } else { - self.head = self.head.next_multiple_of(4).min(max); - } - } - - pub fn extend_next_word_end(&mut self, max: usize) { - if self.head == max { return; } - - if self.head % 4 == 3 { // at the end of a word - self.head = (self.head + 4).min(max); - } else { - self.head = ((self.head + 1).next_multiple_of(4) - 1).min(max); - } - } - - pub const fn extend_previous_word_start(&mut self) { - if self.head == 0 { return; } - - if self.head.is_multiple_of(4) { // at the beginning of a word - self.head -= 4; - } else { - self.head -= self.head % 4; - } - } - - pub fn extend_line_below(&mut self, max: usize) { - if self.tail > self.head { - swap(&mut self.head, &mut self.tail); - } - - if self.tail.is_multiple_of(BYTES_PER_LINE) && - self.head % BYTES_PER_LINE == BYTES_PER_LINE - 1 - { - self.head = min(self.head + BYTES_PER_LINE, max); - } else { - self.tail -= self.tail % BYTES_PER_LINE; - self.head = min( - self.head + BYTES_PER_LINE - 1 - (self.head % BYTES_PER_LINE), - max - ); - } - } - - pub fn extend_line_above(&mut self, max: usize) { - if self.head > self.tail { - swap(&mut self.head, &mut self.tail); - } - - if self.head.is_multiple_of(BYTES_PER_LINE) && - (self.tail % BYTES_PER_LINE == BYTES_PER_LINE - 1 || - self.tail == max) - { - self.head = self.head.saturating_sub(BYTES_PER_LINE); - } else { - self.head -= self.head % BYTES_PER_LINE; - self.tail = min( - self.tail + BYTES_PER_LINE - 1 - (self.tail % BYTES_PER_LINE), - max - ); - } - } -} - -const fn previous_multiple_of(multiple: usize, number: usize) -> usize { - if number == 0 { - 0 - } else { - (number - 1) - ((number - 1) % multiple) - } -} - -mod tests { - #[allow(unused_imports)] - use crate::cursor::Cursor; - - #[test] - fn next_word() { - // [a]bcd efgh -> abcd [e]fgh - let mut cursor = Cursor::at(0); - cursor.move_next_word_start(99); - assert_eq!(cursor, Cursor::at(4)); - - // a[b]cd efgh -> abcd [e]fgh - let mut cursor = Cursor::at(1); - cursor.move_next_word_start(99); - assert_eq!(cursor, Cursor::at(4)); - - // ab[c]d efgh -> abcd [e]fgh - let mut cursor = Cursor::at(2); - cursor.move_next_word_start(99); - assert_eq!(cursor, Cursor::at(4)); - - // abc[d] efgh -> abcd [e]fgh - let mut cursor = Cursor::at(3); - cursor.move_next_word_start(99); - assert_eq!(cursor, Cursor::at(4)); - - // [a]bcd -> abc[d] - let mut cursor = Cursor::at(0); - cursor.move_next_word_start(3); - assert_eq!(cursor, Cursor::at(3)); - - // [a]bc -> ab[c] - let mut cursor = Cursor::at(0); - cursor.move_next_word_start(2); - assert_eq!(cursor, Cursor::at(2)); - - // [a]b -> a[b] - let mut cursor = Cursor::at(0); - cursor.move_next_word_start(1); - assert_eq!(cursor, Cursor::at(1)); - - // [a] -> [a] - let mut cursor = Cursor::at(0); - cursor.move_next_word_start(0); - assert_eq!(cursor, Cursor::at(0)); - - // ab[c]d -> abc[d] - let mut cursor = Cursor::at(2); - cursor.move_next_word_start(3); - assert_eq!(cursor, Cursor::at(3)); - - // abc[d] -> abc[d] - let mut cursor = Cursor::at(3); - cursor.move_next_word_start(3); - assert_eq!(cursor, Cursor::at(3)); - - // ab[c[d] -> ab[c[d] - let mut cursor = Cursor { tail: 2, head: 3 }; - cursor.move_next_word_start(3); - assert_eq!(cursor, Cursor { tail: 2, head: 3 }); - } - - #[test] - fn next_end() { - // [a]bcd -> [abcd] - let mut cursor = Cursor::at(0); - cursor.move_next_word_end(99); - assert_eq!(cursor, Cursor { tail: 0, head: 3 }); - - // a[b]cd -> [abcd] - let mut cursor = Cursor::at(1); - cursor.move_next_word_end(99); - assert_eq!(cursor, Cursor { tail: 1, head: 3 }); - - // ab[c]d -> [abcd] - let mut cursor = Cursor::at(2); - cursor.move_next_word_end(99); - assert_eq!(cursor, Cursor { tail: 2, head: 3 }); - - // abc[d] efgh -> abcd [efgh] - let mut cursor = Cursor::at(3); - cursor.move_next_word_end(99); - assert_eq!(cursor, Cursor { tail: 4, head: 7 }); - - // abcd [e]fgh -> abcd [efgh] - let mut cursor = Cursor::at(4); - cursor.move_next_word_end(99); - assert_eq!(cursor, Cursor { tail: 4, head: 7 }); - - // abcd e[f]gh -> abcd e[fgh] - let mut cursor = Cursor::at(5); - cursor.move_next_word_end(99); - assert_eq!(cursor, Cursor { tail: 5, head: 7 }); - - // abcd ef[g]h -> abcd ef[gh] - let mut cursor = Cursor::at(6); - cursor.move_next_word_end(99); - assert_eq!(cursor, Cursor { tail: 6, head: 7 }); - - // abcd efg[h] ijkl -> abcd efgh [ijkl] - let mut cursor = Cursor::at(7); - cursor.move_next_word_end(99); - assert_eq!(cursor, Cursor { tail: 8, head: 11 }); - - // abcd efg[h] -> abcd efg[h] - let mut cursor = Cursor::at(7); - cursor.move_next_word_end(7); - assert_eq!(cursor, Cursor { tail: 7, head: 7 }); - - // abcd e[fgh] -> abcd e[fgh] - let mut cursor = Cursor { tail: 5, head: 7 }; - cursor.move_next_word_end(7); - assert_eq!(cursor, Cursor { tail: 5, head: 7 }); - - // a[b]c -> a[bc] - let mut cursor = Cursor::at(1); - cursor.move_next_word_end(2); - assert_eq!(cursor, Cursor { tail: 1, head: 2 }); - - // a[bc] -> a[bc] - let mut cursor = Cursor { tail: 1, head: 2}; - cursor.move_next_word_end(2); - assert_eq!(cursor, Cursor { tail: 1, head: 2 }); - - // a[b] -> a[b] - let mut cursor = Cursor::at(1); - cursor.move_next_word_end(1); - assert_eq!(cursor, Cursor::at(1)); - - // [a]b -> [ab] - let mut cursor = Cursor::at(0); - cursor.move_next_word_end(1); - assert_eq!(cursor, Cursor { tail: 0, head: 1 }); - - // [ab] -> [ab] - let mut cursor = Cursor { tail: 0, head: 1}; - cursor.move_next_word_end(1); - assert_eq!(cursor, Cursor { tail: 0, head: 1 }); - - // [a] -> [a] - let mut cursor = Cursor::at(0); - cursor.move_next_word_end(0); - assert_eq!(cursor, Cursor::at(0)); - - // [a]bcd] -> [abc[d] - let mut cursor = Cursor { head: 0, tail: 3 }; - cursor.move_next_word_end(99); - assert_eq!(cursor, Cursor { tail: 0, head: 3 }); - - // [a[b]cd -> a[bc[d] - let mut cursor = Cursor { tail: 0, head: 1 }; - cursor.move_next_word_end(99); - assert_eq!(cursor, Cursor { tail: 1, head: 3 }); - - // abc[d] ef -> abcd [ef] - let mut cursor = Cursor::at(3); - cursor.move_next_word_end(5); - assert_eq!(cursor, Cursor { tail: 4, head: 5 }); - } - - #[test] - fn previous_beginning() { - // abcd efgh [i]jkl -> abcd [efgh] ijkl - let mut cursor = Cursor::at(8); - cursor.move_previous_word_start(); - assert_eq!(cursor, Cursor { head: 4, tail: 7 }); - - // abcd efg[h] -> abcd [efgh] - let mut cursor = Cursor::at(7); - cursor.move_previous_word_start(); - assert_eq!(cursor, Cursor { head: 4, tail: 7 }); - - // abcd ef[g]h -> abcd [efg]h - let mut cursor = Cursor::at(6); - cursor.move_previous_word_start(); - assert_eq!(cursor, Cursor { head: 4, tail: 6 }); - - // abcd e[f]gh -> abcd [ef]gh - let mut cursor = Cursor::at(5); - cursor.move_previous_word_start(); - assert_eq!(cursor, Cursor { head: 4, tail: 5 }); - - // abcd [e]fgh -> [abcd] efgh - let mut cursor = Cursor::at(4); - cursor.move_previous_word_start(); - assert_eq!(cursor, Cursor { head: 0, tail: 3 }); - - // abc[d] -> [abcd] - let mut cursor = Cursor::at(3); - cursor.move_previous_word_start(); - assert_eq!(cursor, Cursor { head: 0, tail: 3 }); - - // ab[c]d -> [abc]d - let mut cursor = Cursor::at(2); - cursor.move_previous_word_start(); - assert_eq!(cursor, Cursor { head: 0, tail: 2 }); - - // a[b]cd -> [ab]cd - let mut cursor = Cursor::at(1); - cursor.move_previous_word_start(); - assert_eq!(cursor, Cursor { head: 0, tail: 1 }); - - // [a]bcd -> [a]bcd - let mut cursor = Cursor::at(0); - cursor.move_previous_word_start(); - assert_eq!(cursor, Cursor { head: 0, tail: 0 }); - - // [abc[d] -> [a]bcd] - let mut cursor = Cursor { tail: 0, head: 3 }; - cursor.move_previous_word_start(); - assert_eq!(cursor, Cursor { head: 0, tail: 3 }); - - // ab[c]d] -> [a]bc]d - let mut cursor = Cursor { head: 2, tail: 3 }; - cursor.move_previous_word_start(); - assert_eq!(cursor, Cursor { head: 0, tail: 2 }); - } } diff --git a/src/cursor/actions.rs b/src/cursor/actions.rs index bca5e47..68fb3e7 100644 --- a/src/cursor/actions.rs +++ b/src/cursor/actions.rs @@ -1,3 +1,6 @@ +use std::{cmp::min, mem::swap}; +use crate::{BYTES_PER_LINE, action::CursorAction, cursor::Cursor}; + impl Cursor { pub fn execute( &mut self, @@ -180,7 +183,7 @@ impl Cursor { } if self.tail.is_multiple_of(BYTES_PER_LINE) && - self.head % BYTES_PER_LINE == BYTES_PER_LINE - 1 + self.head % BYTES_PER_LINE == BYTES_PER_LINE - 1 { self.head = min(self.head + BYTES_PER_LINE, max); } else {