fix extend-to commands not combining cursors

This commit is contained in:
alice pellerin
2026-04-13 01:09:23 -05:00
parent a689949044
commit 901350508d
4 changed files with 188 additions and 612 deletions
+9 -2
View File
@@ -121,11 +121,13 @@ impl Buffer {
} else { } else {
self.primary_cursor.clamp(self.scroll_position, window_size.visible_byte_count()); self.primary_cursor.clamp(self.scroll_position, window_size.visible_byte_count());
} }
self.combine_cursors_if_overlapping(); self.combine_cursors_if_overlapping();
} }
pub fn scroll_up(&mut self, window_size: WindowSize) { pub fn scroll_up(&mut self, window_size: WindowSize) {
self.scroll_position = self.scroll_position.saturating_sub(BYTES_PER_LINE); self.scroll_position = self.scroll_position.saturating_sub(BYTES_PER_LINE);
if window_size.hex_rows() > LINES_OF_PADDING * 2 { if window_size.hex_rows() > LINES_OF_PADDING * 2 {
self.primary_cursor.clamp( self.primary_cursor.clamp(
self.scroll_position + BYTES_OF_PADDING, self.scroll_position + BYTES_OF_PADDING,
@@ -134,6 +136,7 @@ impl Buffer {
} else { } else {
self.primary_cursor.clamp(self.scroll_position, window_size.visible_byte_count()); self.primary_cursor.clamp(self.scroll_position, window_size.visible_byte_count());
} }
self.combine_cursors_if_overlapping(); self.combine_cursors_if_overlapping();
} }
@@ -210,6 +213,7 @@ impl Buffer {
} else { } else {
self.primary_cursor.clamp(self.scroll_position, window_size.visible_byte_count()); self.primary_cursor.clamp(self.scroll_position, window_size.visible_byte_count());
} }
self.combine_cursors_if_overlapping(); self.combine_cursors_if_overlapping();
} }
@@ -226,6 +230,7 @@ impl Buffer {
} else { } else {
self.primary_cursor.clamp(self.scroll_position, window_size.visible_byte_count()); self.primary_cursor.clamp(self.scroll_position, window_size.visible_byte_count());
} }
self.combine_cursors_if_overlapping(); self.combine_cursors_if_overlapping();
} }
@@ -340,7 +345,6 @@ impl Buffer {
self.cursors.sort_by_key(|cursor| cursor.head); self.cursors.sort_by_key(|cursor| cursor.head);
self.combine_cursors_if_overlapping(); self.combine_cursors_if_overlapping();
self.rotate_selections_forward(window_size); self.rotate_selections_forward(window_size);
} }
@@ -576,6 +580,7 @@ impl Buffer {
cursor.head = mark_after_cursor - 1; cursor.head = mark_after_cursor - 1;
} }
self.combine_cursors_if_overlapping();
self.clamp_screen_to_primary_cursor(window_size); 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); 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); self.clamp_screen_to_primary_cursor(window_size);
} }
@@ -696,7 +703,7 @@ fn inspect(selection: &[u8]) -> Vec<Span<'static>> {
let int = nat.and_then(|nat| nat_to_int_if_different(nat, selection.len())); let int = nat.and_then(|nat| nat_to_int_if_different(nat, selection.len()));
let utf8 = str::from_utf8(selection).ok() let utf8 = str::from_utf8(selection).ok()
.filter(|_| selection.len() == 1) .filter(|_| selection.len() != 1)
.map(|utf8| utf8.trim_suffix('\0')) .map(|utf8| utf8.trim_suffix('\0'))
.filter(|utf8| !utf8.contains(is_illegal_control_character)) .filter(|utf8| !utf8.contains(is_illegal_control_character))
.map(|utf8| Span::from(format!("\"{utf8}\"")).red()); .map(|utf8| Span::from(format!("\"{utf8}\"")).red());
+173 -169
View File
@@ -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 { impl Default for Config {
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
@@ -10,255 +14,255 @@ impl Default for Config {
[ [
(Mode::Normal, [ (Mode::Normal, [
(None, [ (None, [
("q".try_into().unwrap(), QuitIfSaved.into()), (keypress("q"), QuitIfSaved.into()),
("Q".try_into().unwrap(), Quit.into()), (keypress("Q"), Quit.into()),
("v".try_into().unwrap(), SelectMode.into()), (keypress("v"), SelectMode.into()),
("g".try_into().unwrap(), Goto.into()), (keypress("g"), Goto.into()),
("z".try_into().unwrap(), View.into()), (keypress("z"), View.into()),
("r".try_into().unwrap(), Replace.into()), (keypress("r"), Replace.into()),
(" ".try_into().unwrap(), Space.into()), (keypress(" "), Space.into()),
("*".try_into().unwrap(), Repeat.into()), (keypress("*"), Repeat.into()),
("t".try_into().unwrap(), To.into()), (keypress("t"), To.into()),
("i".try_into().unwrap(), MoveByteUp.into()), (keypress("i"), MoveByteUp.into()),
("k".try_into().unwrap(), MoveByteDown.into()), (keypress("k"), MoveByteDown.into()),
("j".try_into().unwrap(), MoveByteLeft.into()), (keypress("j"), MoveByteLeft.into()),
("l".try_into().unwrap(), MoveByteRight.into()), (keypress("l"), MoveByteRight.into()),
("up".try_into().unwrap(), MoveByteUp.into()), (keypress("up"), MoveByteUp.into()),
("down".try_into().unwrap(), MoveByteDown.into()), (keypress("down"), MoveByteDown.into()),
("left".try_into().unwrap(), MoveByteLeft.into()), (keypress("left"), MoveByteLeft.into()),
("right".try_into().unwrap(), MoveByteRight.into()), (keypress("right"), MoveByteRight.into()),
("G".try_into().unwrap(), GotoFileEnd.into()), (keypress("G"), GotoFileEnd.into()),
("C-e".try_into().unwrap(), ScrollDown.into()), (keypress("C-e"), ScrollDown.into()),
("C-y".try_into().unwrap(), ScrollUp.into()), (keypress("C-y"), ScrollUp.into()),
("C-d".try_into().unwrap(), PageCursorHalfDown.into()), (keypress("C-d"), PageCursorHalfDown.into()),
("C-u".try_into().unwrap(), PageCursorHalfUp.into()), (keypress("C-u"), PageCursorHalfUp.into()),
("C-f".try_into().unwrap(), PageDown.into()), (keypress("C-f"), PageDown.into()),
("C-b".try_into().unwrap(), PageUp.into()), (keypress("C-b"), PageUp.into()),
("w".try_into().unwrap(), MoveNextWordStart.into()), (keypress("w"), MoveNextWordStart.into()),
("e".try_into().unwrap(), MoveNextWordEnd.into()), (keypress("e"), MoveNextWordEnd.into()),
("b".try_into().unwrap(), MovePreviousWordStart.into()), (keypress("b"), MovePreviousWordStart.into()),
(";".try_into().unwrap(), CollapseSelection.into()), (keypress(";"), CollapseSelection.into()),
("A-;".try_into().unwrap(), FlipSelections.into()), (keypress("A-;"), FlipSelections.into()),
("x".try_into().unwrap(), ExtendLineBelow.into()), (keypress("x"), ExtendLineBelow.into()),
("X".try_into().unwrap(), ExtendLineAbove.into()), (keypress("X"), ExtendLineAbove.into()),
("d".try_into().unwrap(), Delete.into()), (keypress("d"), Delete.into()),
("u".try_into().unwrap(), Undo.into()), (keypress("u"), Undo.into()),
("U".try_into().unwrap(), Redo.into()), (keypress("U"), Redo.into()),
("C-j".try_into().unwrap(), PreviousBuffer.into()), (keypress("C-j"), PreviousBuffer.into()),
("C-l".try_into().unwrap(), NextBuffer.into()), (keypress("C-l"), NextBuffer.into()),
("C".try_into().unwrap(), CopySelectionOnNextLine.into()), (keypress("C"), CopySelectionOnNextLine.into()),
("(".try_into().unwrap(), RotateSelectionsBackward.into()), (keypress("("), RotateSelectionsBackward.into()),
(")".try_into().unwrap(), RotateSelectionsForward.into()), (keypress(")"), RotateSelectionsForward.into()),
(",".try_into().unwrap(), KeepPrimarySelection.into()), (keypress(","), KeepPrimarySelection.into()),
("A-,".try_into().unwrap(), RemovePrimarySelection.into()), (keypress("A-,"), RemovePrimarySelection.into()),
("1".try_into().unwrap(), SplitSelectionsInto1s.into()), (keypress("1"), SplitSelectionsInto1s.into()),
("2".try_into().unwrap(), SplitSelectionsInto2s.into()), (keypress("2"), SplitSelectionsInto2s.into()),
("3".try_into().unwrap(), SplitSelectionsInto3s.into()), (keypress("3"), SplitSelectionsInto3s.into()),
("4".try_into().unwrap(), SplitSelectionsInto4s.into()), (keypress("4"), SplitSelectionsInto4s.into()),
("5".try_into().unwrap(), SplitSelectionsInto5s.into()), (keypress("5"), SplitSelectionsInto5s.into()),
("6".try_into().unwrap(), SplitSelectionsInto6s.into()), (keypress("6"), SplitSelectionsInto6s.into()),
("7".try_into().unwrap(), SplitSelectionsInto7s.into()), (keypress("7"), SplitSelectionsInto7s.into()),
("8".try_into().unwrap(), SplitSelectionsInto8s.into()), (keypress("8"), SplitSelectionsInto8s.into()),
("9".try_into().unwrap(), SplitSelectionsInto9s.into()), (keypress("9"), SplitSelectionsInto9s.into()),
("J".try_into().unwrap(), JumpToSelectedOffsetRelativeToMark.into()), (keypress("J"), JumpToSelectedOffsetRelativeToMark.into()),
("A-J".try_into().unwrap(), JumpToSelectedOffset.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()), (keypress("C- "), InspectSelection.into()),
("A- ".try_into().unwrap(), InspectSelectionColor.into()), (keypress("A- "), InspectSelectionColor.into()),
].into()), ].into()),
(Some(PartialAction::Goto), [ (Some(PartialAction::Goto), [
("j".try_into().unwrap(), GotoLineStart.into()), (keypress("j"), GotoLineStart.into()),
("l".try_into().unwrap(), GotoLineEnd.into()), (keypress("l"), GotoLineEnd.into()),
("g".try_into().unwrap(), GotoFileStart.into()), (keypress("g"), GotoFileStart.into()),
].into()), ].into()),
(Some(PartialAction::View), [ (Some(PartialAction::View), [
("z".try_into().unwrap(), AlignViewCenter.into()), (keypress("z"), AlignViewCenter.into()),
("b".try_into().unwrap(), AlignViewBottom.into()), (keypress("b"), AlignViewBottom.into()),
("t".try_into().unwrap(), AlignViewTop.into()), (keypress("t"), AlignViewTop.into()),
].into()), ].into()),
(Some(PartialAction::Space), [ (Some(PartialAction::Space), [
("w".try_into().unwrap(), Save.into()), (keypress("w"), Save.into()),
].into()), ].into()),
(Some(PartialAction::Repeat), [ (Some(PartialAction::Repeat), [
("i".try_into().unwrap(), MoveByteUp.into()), (keypress("i"), MoveByteUp.into()),
("k".try_into().unwrap(), MoveByteDown.into()), (keypress("k"), MoveByteDown.into()),
("j".try_into().unwrap(), MoveByteLeft.into()), (keypress("j"), MoveByteLeft.into()),
("l".try_into().unwrap(), MoveByteRight.into()), (keypress("l"), MoveByteRight.into()),
("up".try_into().unwrap(), MoveByteUp.into()), (keypress("up"), MoveByteUp.into()),
("down".try_into().unwrap(), MoveByteDown.into()), (keypress("down"), MoveByteDown.into()),
("left".try_into().unwrap(), MoveByteLeft.into()), (keypress("left"), MoveByteLeft.into()),
("right".try_into().unwrap(), MoveByteRight.into()), (keypress("right"), MoveByteRight.into()),
("C-e".try_into().unwrap(), ScrollDown.into()), (keypress("C-e"), ScrollDown.into()),
("C-y".try_into().unwrap(), ScrollUp.into()), (keypress("C-y"), ScrollUp.into()),
("C-d".try_into().unwrap(), PageCursorHalfDown.into()), (keypress("C-d"), PageCursorHalfDown.into()),
("C-u".try_into().unwrap(), PageCursorHalfUp.into()), (keypress("C-u"), PageCursorHalfUp.into()),
("C-f".try_into().unwrap(), PageDown.into()), (keypress("C-f"), PageDown.into()),
("C-b".try_into().unwrap(), PageUp.into()), (keypress("C-b"), PageUp.into()),
("w".try_into().unwrap(), MoveNextWordStart.into()), (keypress("w"), MoveNextWordStart.into()),
("e".try_into().unwrap(), MoveNextWordEnd.into()), (keypress("e"), MoveNextWordEnd.into()),
("b".try_into().unwrap(), MovePreviousWordStart.into()), (keypress("b"), MovePreviousWordStart.into()),
("x".try_into().unwrap(), ExtendLineBelow.into()), (keypress("x"), ExtendLineBelow.into()),
("X".try_into().unwrap(), ExtendLineAbove.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()), ].into()),
(Some(PartialAction::To), [ (Some(PartialAction::To), [
("m".try_into().unwrap(), ExtendToMark.into()), (keypress("m"), ExtendToMark.into()),
("0".try_into().unwrap(), ExtendToNull.into()), (keypress("0"), ExtendToNull.into()),
("f".try_into().unwrap(), ExtendToFF.into()), (keypress("f"), ExtendToFF.into()),
].into()), ].into()),
].into()), ].into()),
(Mode::Select, [ (Mode::Select, [
(None, [ (None, [
("q".try_into().unwrap(), QuitIfSaved.into()), (keypress("q"), QuitIfSaved.into()),
("Q".try_into().unwrap(), Quit.into()), (keypress("Q"), Quit.into()),
("v".try_into().unwrap(), NormalMode.into()), (keypress("v"), NormalMode.into()),
("g".try_into().unwrap(), Goto.into()), (keypress("g"), Goto.into()),
("z".try_into().unwrap(), View.into()), (keypress("z"), View.into()),
("r".try_into().unwrap(), Replace.into()), (keypress("r"), Replace.into()),
(" ".try_into().unwrap(), Space.into()), (keypress(" "), Space.into()),
("*".try_into().unwrap(), Repeat.into()), (keypress("*"), Repeat.into()),
("t".try_into().unwrap(), To.into()), (keypress("t"), To.into()),
("i".try_into().unwrap(), ExtendByteUp.into()), (keypress("i"), ExtendByteUp.into()),
("k".try_into().unwrap(), ExtendByteDown.into()), (keypress("k"), ExtendByteDown.into()),
("j".try_into().unwrap(), ExtendByteLeft.into()), (keypress("j"), ExtendByteLeft.into()),
("l".try_into().unwrap(), ExtendByteRight.into()), (keypress("l"), ExtendByteRight.into()),
("up".try_into().unwrap(), ExtendByteUp.into()), (keypress("up"), ExtendByteUp.into()),
("down".try_into().unwrap(), ExtendByteDown.into()), (keypress("down"), ExtendByteDown.into()),
("left".try_into().unwrap(), ExtendByteLeft.into()), (keypress("left"), ExtendByteLeft.into()),
("right".try_into().unwrap(), ExtendByteRight.into()), (keypress("right"), ExtendByteRight.into()),
("C-e".try_into().unwrap(), ScrollDown.into()), (keypress("C-e"), ScrollDown.into()),
("C-y".try_into().unwrap(), ScrollUp.into()), (keypress("C-y"), ScrollUp.into()),
("C-d".try_into().unwrap(), PageCursorHalfDown.into()), (keypress("C-d"), PageCursorHalfDown.into()),
("C-u".try_into().unwrap(), PageCursorHalfUp.into()), (keypress("C-u"), PageCursorHalfUp.into()),
("C-f".try_into().unwrap(), PageDown.into()), (keypress("C-f"), PageDown.into()),
("C-b".try_into().unwrap(), PageUp.into()), (keypress("C-b"), PageUp.into()),
("w".try_into().unwrap(), ExtendNextWordStart.into()), (keypress("w"), ExtendNextWordStart.into()),
("e".try_into().unwrap(), ExtendNextWordEnd.into()), (keypress("e"), ExtendNextWordEnd.into()),
("b".try_into().unwrap(), ExtendPreviousWordStart.into()), (keypress("b"), ExtendPreviousWordStart.into()),
(";".try_into().unwrap(), CollapseSelection.into()), (keypress(";"), CollapseSelection.into()),
("A-;".try_into().unwrap(), FlipSelections.into()), (keypress("A-;"), FlipSelections.into()),
("x".try_into().unwrap(), ExtendLineBelow.into()), (keypress("x"), ExtendLineBelow.into()),
("X".try_into().unwrap(), ExtendLineAbove.into()), (keypress("X"), ExtendLineAbove.into()),
("d".try_into().unwrap(), Delete.into()), (keypress("d"), Delete.into()),
("u".try_into().unwrap(), Undo.into()), (keypress("u"), Undo.into()),
("U".try_into().unwrap(), Redo.into()), (keypress("U"), Redo.into()),
("C".try_into().unwrap(), CopySelectionOnNextLine.into()), (keypress("C"), CopySelectionOnNextLine.into()),
("(".try_into().unwrap(), RotateSelectionsBackward.into()), (keypress("("), RotateSelectionsBackward.into()),
(")".try_into().unwrap(), RotateSelectionsForward.into()), (keypress(")"), RotateSelectionsForward.into()),
(",".try_into().unwrap(), KeepPrimarySelection.into()), (keypress(","), KeepPrimarySelection.into()),
("A-,".try_into().unwrap(), RemovePrimarySelection.into()), (keypress("A-,"), RemovePrimarySelection.into()),
("1".try_into().unwrap(), SplitSelectionsInto1s.into()), (keypress("1"), SplitSelectionsInto1s.into()),
("2".try_into().unwrap(), SplitSelectionsInto2s.into()), (keypress("2"), SplitSelectionsInto2s.into()),
("3".try_into().unwrap(), SplitSelectionsInto3s.into()), (keypress("3"), SplitSelectionsInto3s.into()),
("4".try_into().unwrap(), SplitSelectionsInto4s.into()), (keypress("4"), SplitSelectionsInto4s.into()),
("5".try_into().unwrap(), SplitSelectionsInto5s.into()), (keypress("5"), SplitSelectionsInto5s.into()),
("6".try_into().unwrap(), SplitSelectionsInto6s.into()), (keypress("6"), SplitSelectionsInto6s.into()),
("7".try_into().unwrap(), SplitSelectionsInto7s.into()), (keypress("7"), SplitSelectionsInto7s.into()),
("8".try_into().unwrap(), SplitSelectionsInto8s.into()), (keypress("8"), SplitSelectionsInto8s.into()),
("9".try_into().unwrap(), SplitSelectionsInto9s.into()), (keypress("9"), SplitSelectionsInto9s.into()),
("J".try_into().unwrap(), JumpToSelectedOffsetRelativeToMark.into()), (keypress("J"), JumpToSelectedOffsetRelativeToMark.into()),
("A-J".try_into().unwrap(), JumpToSelectedOffset.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()), (keypress("C- "), InspectSelection.into()),
("A- ".try_into().unwrap(), InspectSelectionColor.into()), (keypress("A- "), InspectSelectionColor.into()),
].into()), ].into()),
(Some(PartialAction::View), [ (Some(PartialAction::View), [
("z".try_into().unwrap(), AlignViewCenter.into()), (keypress("z"), AlignViewCenter.into()),
("b".try_into().unwrap(), AlignViewBottom.into()), (keypress("b"), AlignViewBottom.into()),
("t".try_into().unwrap(), AlignViewTop.into()), (keypress("t"), AlignViewTop.into()),
].into()), ].into()),
(Some(PartialAction::Space), [ (Some(PartialAction::Space), [
("w".try_into().unwrap(), Save.into()), (keypress("w"), Save.into()),
].into()), ].into()),
(Some(PartialAction::Repeat), [ (Some(PartialAction::Repeat), [
("i".try_into().unwrap(), ExtendByteUp.into()), (keypress("i"), ExtendByteUp.into()),
("k".try_into().unwrap(), ExtendByteDown.into()), (keypress("k"), ExtendByteDown.into()),
("j".try_into().unwrap(), ExtendByteLeft.into()), (keypress("j"), ExtendByteLeft.into()),
("l".try_into().unwrap(), ExtendByteRight.into()), (keypress("l"), ExtendByteRight.into()),
("up".try_into().unwrap(), ExtendByteUp.into()), (keypress("up"), ExtendByteUp.into()),
("down".try_into().unwrap(), ExtendByteDown.into()), (keypress("down"), ExtendByteDown.into()),
("left".try_into().unwrap(), ExtendByteLeft.into()), (keypress("left"), ExtendByteLeft.into()),
("right".try_into().unwrap(), ExtendByteRight.into()), (keypress("right"), ExtendByteRight.into()),
("C-e".try_into().unwrap(), ScrollDown.into()), (keypress("C-e"), ScrollDown.into()),
("C-y".try_into().unwrap(), ScrollUp.into()), (keypress("C-y"), ScrollUp.into()),
("C-d".try_into().unwrap(), PageCursorHalfDown.into()), (keypress("C-d"), PageCursorHalfDown.into()),
("C-u".try_into().unwrap(), PageCursorHalfUp.into()), (keypress("C-u"), PageCursorHalfUp.into()),
("C-f".try_into().unwrap(), PageDown.into()), (keypress("C-f"), PageDown.into()),
("C-b".try_into().unwrap(), PageUp.into()), (keypress("C-b"), PageUp.into()),
("w".try_into().unwrap(), ExtendNextWordStart.into()), (keypress("w"), ExtendNextWordStart.into()),
("e".try_into().unwrap(), ExtendNextWordEnd.into()), (keypress("e"), ExtendNextWordEnd.into()),
("b".try_into().unwrap(), ExtendPreviousWordStart.into()), (keypress("b"), ExtendPreviousWordStart.into()),
("x".try_into().unwrap(), ExtendLineBelow.into()), (keypress("x"), ExtendLineBelow.into()),
("X".try_into().unwrap(), ExtendLineAbove.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()), ].into()),
(Some(PartialAction::To), [ (Some(PartialAction::To), [
("m".try_into().unwrap(), ExtendToMark.into()), (keypress("m"), ExtendToMark.into()),
("0".try_into().unwrap(), ExtendToNull.into()), (keypress("0"), ExtendToNull.into()),
("f".try_into().unwrap(), ExtendToFF.into()), (keypress("f"), ExtendToFF.into()),
].into()), ].into()),
].into()) ].into())
].into() ].into()
+2 -440
View File
@@ -1,5 +1,6 @@
use std::{cmp::{max, min}, mem::swap, ops::RangeInclusive}; 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)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Cursor { pub struct Cursor {
@@ -75,443 +76,4 @@ impl Cursor {
self.tail = min(self.tail, other.tail); 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 });
}
} }
+4 -1
View File
@@ -1,3 +1,6 @@
use std::{cmp::min, mem::swap};
use crate::{BYTES_PER_LINE, action::CursorAction, cursor::Cursor};
impl Cursor { impl Cursor {
pub fn execute( pub fn execute(
&mut self, &mut self,
@@ -180,7 +183,7 @@ impl Cursor {
} }
if self.tail.is_multiple_of(BYTES_PER_LINE) && 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); self.head = min(self.head + BYTES_PER_LINE, max);
} else { } else {