add extend motions, color cursor in select mode

This commit is contained in:
alice pellerin
2026-03-18 04:12:41 -05:00
parent 11075723d8
commit bfc67a2671
4 changed files with 109 additions and 9 deletions
+65
View File
@@ -18,6 +18,11 @@ pub enum Action {
MoveByteLeft,
MoveByteRight,
ExtendByteUp,
ExtendByteDown,
ExtendByteLeft,
ExtendByteRight,
GotoLineStart,
GotoLineEnd,
GotoFileStart,
@@ -36,6 +41,10 @@ pub enum Action {
MoveNextWordEnd,
MovePreviousWordStart,
ExtendNextWordStart,
ExtendNextWordEnd,
ExtendPreviousWordStart,
CollapseSelection,
ExtendLineBelow,
@@ -61,6 +70,11 @@ impl App {
Action::MoveByteLeft => self.move_byte_left(),
Action::MoveByteRight => self.move_byte_right(),
Action::ExtendByteUp => self.extend_byte_up(),
Action::ExtendByteDown => self.extend_byte_down(),
Action::ExtendByteLeft => self.extend_byte_left(),
Action::ExtendByteRight => self.extend_byte_right(),
Action::GotoLineStart => self.goto_line_start(),
Action::GotoLineEnd => self.goto_line_end(),
Action::GotoFileStart => self.goto_file_start(),
@@ -79,6 +93,10 @@ impl App {
Action::MoveNextWordEnd => self.move_next_word_end(),
Action::MovePreviousWordStart => self.move_previous_word_start(),
Action::ExtendNextWordStart => self.extend_next_word_start(),
Action::ExtendNextWordEnd => self.extend_next_word_end(),
Action::ExtendPreviousWordStart => self.extend_previous_word_start(),
Action::CollapseSelection => self.collapse_selection(),
Action::ExtendLineBelow => self.extend_line_below(),
@@ -148,6 +166,34 @@ impl App {
}
}
const fn extend_byte_up(&mut self) {
if self.cursor.head >= BYTES_PER_LINE {
self.cursor.head -= BYTES_PER_LINE;
self.clamp_screen_to_cursor();
}
}
const fn extend_byte_down(&mut self) {
if self.contents.len() - 1 - self.cursor.head >= BYTES_PER_LINE {
self.cursor.head += BYTES_PER_LINE;
self.clamp_screen_to_cursor();
}
}
const fn extend_byte_left(&mut self) {
if self.cursor.head >= 1 {
self.cursor.head -= 1;
self.clamp_screen_to_cursor();
}
}
const fn extend_byte_right(&mut self) {
if self.contents.len() - 1 - self.cursor.head >= 1 {
self.cursor.head += 1;
self.clamp_screen_to_cursor();
}
}
const fn goto_line_start(&mut self) {
self.cursor.head -= self.cursor.head % BYTES_PER_LINE;
self.cursor.collapse();
@@ -240,6 +286,21 @@ impl App {
self.clamp_screen_to_cursor();
}
fn extend_next_word_start(&mut self) {
self.cursor.extend_to_next_word(self.contents.len() - 1);
self.clamp_screen_to_cursor();
}
fn extend_next_word_end(&mut self) {
self.cursor.extend_to_next_end(self.contents.len() - 1);
self.clamp_screen_to_cursor();
}
const fn extend_previous_word_start(&mut self) {
self.cursor.extend_to_previous_beginning();
self.clamp_screen_to_cursor();
}
const fn collapse_selection(&mut self) {
self.cursor.collapse();
}
@@ -285,6 +346,10 @@ impl App {
data: self.contents[self.cursor.range()].into()
}
);
if self.mode == Mode::Select {
self.mode = Mode::Normal;
}
}
}
+7 -2
View File
@@ -78,7 +78,7 @@ mod hex {
use itertools::{Itertools, repeat_n};
use ratatui::{style::{Color, Style, Stylize}, text::Span};
use crate::{BYTES_PER_CHUNK, BYTES_PER_LINE, CHUNKS_PER_LINE, app::App, cardinality::HasCardinality, cursor::InCursor, empty_span::empty_span, custom_greys::CustomGreys};
use crate::{BYTES_PER_CHUNK, BYTES_PER_LINE, CHUNKS_PER_LINE, app::{App, Mode}, cardinality::HasCardinality, cursor::InCursor, custom_greys::CustomGreys, empty_span::empty_span};
impl App {
pub fn render_chunks(
@@ -194,8 +194,13 @@ mod hex {
let span = SPAN_FOR_BYTE[byte as usize].clone();
let head_color = match self.mode {
Mode::Select => Color::Yellow,
_ => Color::Gray
};
match self.cursor.contains(address) {
Some(InCursor::Head) => span.bg(Color::Gray),
Some(InCursor::Head) => span.bg(head_color),
Some(InCursor::Rest) => span.bg(Color::select_grey()),
None => span,
}
+7 -7
View File
@@ -151,10 +151,10 @@ impl Default for Config {
("z".try_into().unwrap(), Action::ZPartial),
("r".try_into().unwrap(), Action::RPartial),
// ("i".try_into().unwrap(), Action::ExtendByteUp),
// ("k".try_into().unwrap(), Action::ExtendByteDown),
// ("j".try_into().unwrap(), Action::ExtendByteLeft),
// ("l".try_into().unwrap(), Action::ExtendByteRight),
("i".try_into().unwrap(), Action::ExtendByteUp),
("k".try_into().unwrap(), Action::ExtendByteDown),
("j".try_into().unwrap(), Action::ExtendByteLeft),
("l".try_into().unwrap(), Action::ExtendByteRight),
("C-e".try_into().unwrap(), Action::ScrollDown),
("C-y".try_into().unwrap(), Action::ScrollUp),
@@ -165,9 +165,9 @@ impl Default for Config {
("C-f".try_into().unwrap(), Action::PageDown),
("C-b".try_into().unwrap(), Action::PageUp),
// ("w".try_into().unwrap(), Action::ExtendNextWordStart),
// ("e".try_into().unwrap(), Action::ExtendNextWordEnd),
// ("b".try_into().unwrap(), Action::ExtendPreviousWordStart),
("w".try_into().unwrap(), Action::ExtendNextWordStart),
("e".try_into().unwrap(), Action::ExtendNextWordEnd),
("b".try_into().unwrap(), Action::ExtendPreviousWordStart),
(";".try_into().unwrap(), Action::CollapseSelection),
+30
View File
@@ -83,6 +83,36 @@ impl Cursor {
self.head -= self.head % 4;
}
}
pub fn extend_to_next_word(&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_to_next_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_to_previous_beginning(&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;
}
}
}
mod tests {