diff --git a/src/app/mod.rs b/src/app/mod.rs index f61905e..0656628 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -98,13 +98,13 @@ impl App { } } Event::Key(key_event) if key_event.code == KeyCode::Char('w') => { - self.cursor.to_next_word(self.contents.len() - 1); + self.cursor.move_to_next_word(self.contents.len() - 1); } Event::Key(key_event) if key_event.code == KeyCode::Char('e') => { - self.cursor.to_next_end(self.contents.len() - 1); + self.cursor.move_to_next_end(self.contents.len() - 1); } Event::Key(key_event) if key_event.code == KeyCode::Char('b') => { - self.cursor.to_previous_beginning(); + self.cursor.move_to_previous_beginning(); } Event::Key(key_event) if key_event.code == KeyCode::Char(';') => { self.cursor.collapse(); diff --git a/src/app/widget.rs b/src/app/widget.rs index 9eb0019..cd44183 100644 --- a/src/app/widget.rs +++ b/src/app/widget.rs @@ -53,11 +53,11 @@ mod address { } mod hex { - use std::{borrow::Cow, mem}; + use std::{borrow::Cow, iter, mem}; use itertools::Itertools; use ratatui::{style::{Color, Style, Stylize}, text::Span}; - use crate::{BYTES_PER_LINE, BYTES_PER_CHUNK, app::App, cardinality::HasCardinality, empty_span::empty_span, cursor::InCursor}; + use crate::{BYTES_PER_CHUNK, BYTES_PER_LINE, CHUNKS_PER_LINE, app::App, cardinality::HasCardinality, cursor::InCursor, empty_span::empty_span, select_grey::SelectGrey}; impl App { pub fn render_chunks( @@ -75,7 +75,13 @@ mod hex { .copied() .zip((address..).step_by(BYTES_PER_CHUNK)) .map(|(chunk, address)| self.render_chunk(address, chunk)) - .intersperse(vec![" ".into()]) + .interleave( + (address..) + .step_by(BYTES_PER_CHUNK) + .take(CHUNKS_PER_LINE) + .skip(1) + .map(|address| vec![self.render_large_space_before(address)]) + ) .flatten() } @@ -90,7 +96,12 @@ mod hex { .copied() .zip(address..) .map(|(byte, address)| self.render_byte_at(address, byte)) - .intersperse(" ".into()) // TODO: highlight if selected + .interleave( + (address..) + .take(BYTES_PER_CHUNK) + .skip(1) + .map(|address| self.render_space_before(address)) + ) .collect() } @@ -105,10 +116,32 @@ mod hex { match self.cursor.contains(address) { Some(InCursor::Head) => span.bg(Color::Gray), - Some(InCursor::Rest) => span.bg(Color::Indexed(242)), + Some(InCursor::Rest) => span.bg(Color::select_grey()), None => span, } } + + fn render_large_space_before(&self, address: usize) -> Span<'static> { + if self.cursor.contains_space_before(address) { + Span { + style: Style::new().bg(Color::select_grey()), + content: " ".into() + } + } else { + " ".into() + } + } + + fn render_space_before(&self, address: usize) -> Span<'static> { + if self.cursor.contains_space_before(address) { + Span { + style: Style::new().bg(Color::select_grey()), + content: " ".into() + } + } else { + " ".into() + } + } } const fn create_byte_lookup_table() -> [Span<'static>; u8::CARDINALITY] { diff --git a/src/cursor.rs b/src/cursor.rs index f67678b..93e2f1d 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -22,22 +22,27 @@ impl Cursor { } } + pub const fn contains_space_before(&self, index: usize) -> bool { + (self.head < index && index <= self.tail) || + (self.tail < index && index <= self.head) + } + pub const fn collapse(&mut self) { self.tail = self.head; } - pub fn to_next_word(&mut self, max: usize) { + pub fn move_to_next_word(&mut self, max: usize) { if self.head == max { return } - if self.head % 4 == 0 { // at the beginning of a word + 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 to_next_end(&mut self, max: usize) { + pub fn move_to_next_end(&mut self, max: usize) { if self.head == max { return } self.collapse(); @@ -49,11 +54,11 @@ impl Cursor { } } - pub const fn to_previous_beginning(&mut self) { + pub const fn move_to_previous_beginning(&mut self) { if self.head == 0 { return } self.collapse(); - if self.head % 4 == 0 { // at the beginning of a word + if self.head.is_multiple_of(4) { // at the beginning of a word self.tail = self.head - 1; self.head -= 4; } else { @@ -76,57 +81,57 @@ mod tests { fn next_word() { // [a]bcd efgh -> abcd [e]fgh let mut cursor = Cursor::at(0); - cursor.to_next_word(99); + cursor.move_to_next_word(99); assert_eq!(cursor, Cursor::at(4)); // a[b]cd efgh -> abcd [e]fgh let mut cursor = Cursor::at(1); - cursor.to_next_word(99); + cursor.move_to_next_word(99); assert_eq!(cursor, Cursor::at(4)); // ab[c]d efgh -> abcd [e]fgh let mut cursor = Cursor::at(2); - cursor.to_next_word(99); + cursor.move_to_next_word(99); assert_eq!(cursor, Cursor::at(4)); // abc[d] efgh -> abcd [e]fgh let mut cursor = Cursor::at(3); - cursor.to_next_word(99); + cursor.move_to_next_word(99); assert_eq!(cursor, Cursor::at(4)); // [a]bcd -> abc[d] let mut cursor = Cursor::at(0); - cursor.to_next_word(3); + cursor.move_to_next_word(3); assert_eq!(cursor, Cursor::at(3)); // [a]bc -> ab[c] let mut cursor = Cursor::at(0); - cursor.to_next_word(2); + cursor.move_to_next_word(2); assert_eq!(cursor, Cursor::at(2)); // [a]b -> a[b] let mut cursor = Cursor::at(0); - cursor.to_next_word(1); + cursor.move_to_next_word(1); assert_eq!(cursor, Cursor::at(1)); // [a] -> [a] let mut cursor = Cursor::at(0); - cursor.to_next_word(0); + cursor.move_to_next_word(0); assert_eq!(cursor, Cursor::at(0)); // ab[c]d -> abc[d] let mut cursor = Cursor::at(2); - cursor.to_next_word(3); + cursor.move_to_next_word(3); assert_eq!(cursor, Cursor::at(3)); // abc[d] -> abc[d] let mut cursor = Cursor::at(3); - cursor.to_next_word(3); + cursor.move_to_next_word(3); assert_eq!(cursor, Cursor::at(3)); // ab[c[d] -> ab[c[d] let mut cursor = Cursor { tail: 2, head: 3 }; - cursor.to_next_word(3); + cursor.move_to_next_word(3); assert_eq!(cursor, Cursor { tail: 2, head: 3 }); } @@ -134,97 +139,97 @@ mod tests { fn next_end() { // [a]bcd -> [abcd] let mut cursor = Cursor::at(0); - cursor.to_next_end(99); + cursor.move_to_next_end(99); assert_eq!(cursor, Cursor { tail: 0, head: 3 }); // a[b]cd -> [abcd] let mut cursor = Cursor::at(1); - cursor.to_next_end(99); + cursor.move_to_next_end(99); assert_eq!(cursor, Cursor { tail: 1, head: 3 }); // ab[c]d -> [abcd] let mut cursor = Cursor::at(2); - cursor.to_next_end(99); + cursor.move_to_next_end(99); assert_eq!(cursor, Cursor { tail: 2, head: 3 }); // abc[d] efgh -> abcd [efgh] let mut cursor = Cursor::at(3); - cursor.to_next_end(99); + cursor.move_to_next_end(99); assert_eq!(cursor, Cursor { tail: 4, head: 7 }); // abcd [e]fgh -> abcd [efgh] let mut cursor = Cursor::at(4); - cursor.to_next_end(99); + cursor.move_to_next_end(99); assert_eq!(cursor, Cursor { tail: 4, head: 7 }); // abcd e[f]gh -> abcd e[fgh] let mut cursor = Cursor::at(5); - cursor.to_next_end(99); + cursor.move_to_next_end(99); assert_eq!(cursor, Cursor { tail: 5, head: 7 }); // abcd ef[g]h -> abcd ef[gh] let mut cursor = Cursor::at(6); - cursor.to_next_end(99); + cursor.move_to_next_end(99); assert_eq!(cursor, Cursor { tail: 6, head: 7 }); // abcd efg[h] ijkl -> abcd efgh [ijkl] let mut cursor = Cursor::at(7); - cursor.to_next_end(99); + cursor.move_to_next_end(99); assert_eq!(cursor, Cursor { tail: 8, head: 11 }); // abcd efg[h] -> abcd efg[h] let mut cursor = Cursor::at(7); - cursor.to_next_end(7); + cursor.move_to_next_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.to_next_end(7); + cursor.move_to_next_end(7); assert_eq!(cursor, Cursor { tail: 5, head: 7 }); // a[b]c -> a[bc] let mut cursor = Cursor::at(1); - cursor.to_next_end(2); + cursor.move_to_next_end(2); assert_eq!(cursor, Cursor { tail: 1, head: 2 }); // a[bc] -> a[bc] let mut cursor = Cursor { tail: 1, head: 2}; - cursor.to_next_end(2); + cursor.move_to_next_end(2); assert_eq!(cursor, Cursor { tail: 1, head: 2 }); // a[b] -> a[b] let mut cursor = Cursor::at(1); - cursor.to_next_end(1); + cursor.move_to_next_end(1); assert_eq!(cursor, Cursor::at(1)); // [a]b -> [ab] let mut cursor = Cursor::at(0); - cursor.to_next_end(1); + cursor.move_to_next_end(1); assert_eq!(cursor, Cursor { tail: 0, head: 1 }); // [ab] -> [ab] let mut cursor = Cursor { tail: 0, head: 1}; - cursor.to_next_end(1); + cursor.move_to_next_end(1); assert_eq!(cursor, Cursor { tail: 0, head: 1 }); // [a] -> [a] let mut cursor = Cursor::at(0); - cursor.to_next_end(0); + cursor.move_to_next_end(0); assert_eq!(cursor, Cursor::at(0)); // [a]bcd] -> [abc[d] let mut cursor = Cursor { head: 0, tail: 3 }; - cursor.to_next_end(99); + cursor.move_to_next_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.to_next_end(99); + cursor.move_to_next_end(99); assert_eq!(cursor, Cursor { tail: 1, head: 3 }); // abc[d] ef -> abcd [ef] let mut cursor = Cursor::at(3); - cursor.to_next_end(5); + cursor.move_to_next_end(5); assert_eq!(cursor, Cursor { tail: 4, head: 5 }); } @@ -232,57 +237,57 @@ mod tests { fn previous_beginning() { // abcd efgh [i]jkl -> abcd [efgh] ijkl let mut cursor = Cursor::at(8); - cursor.to_previous_beginning(); + cursor.move_to_previous_beginning(); assert_eq!(cursor, Cursor { head: 4, tail: 7 }); // abcd efg[h] -> abcd [efgh] let mut cursor = Cursor::at(7); - cursor.to_previous_beginning(); + cursor.move_to_previous_beginning(); assert_eq!(cursor, Cursor { head: 4, tail: 7 }); // abcd ef[g]h -> abcd [efg]h let mut cursor = Cursor::at(6); - cursor.to_previous_beginning(); + cursor.move_to_previous_beginning(); assert_eq!(cursor, Cursor { head: 4, tail: 6 }); // abcd e[f]gh -> abcd [ef]gh let mut cursor = Cursor::at(5); - cursor.to_previous_beginning(); + cursor.move_to_previous_beginning(); assert_eq!(cursor, Cursor { head: 4, tail: 5 }); // abcd [e]fgh -> [abcd] efgh let mut cursor = Cursor::at(4); - cursor.to_previous_beginning(); + cursor.move_to_previous_beginning(); assert_eq!(cursor, Cursor { head: 0, tail: 3 }); // abc[d] -> [abcd] let mut cursor = Cursor::at(3); - cursor.to_previous_beginning(); + cursor.move_to_previous_beginning(); assert_eq!(cursor, Cursor { head: 0, tail: 3 }); // ab[c]d -> [abc]d let mut cursor = Cursor::at(2); - cursor.to_previous_beginning(); + cursor.move_to_previous_beginning(); assert_eq!(cursor, Cursor { head: 0, tail: 2 }); // a[b]cd -> [ab]cd let mut cursor = Cursor::at(1); - cursor.to_previous_beginning(); + cursor.move_to_previous_beginning(); assert_eq!(cursor, Cursor { head: 0, tail: 1 }); // [a]bcd -> [a]bcd let mut cursor = Cursor::at(0); - cursor.to_previous_beginning(); + cursor.move_to_previous_beginning(); assert_eq!(cursor, Cursor { head: 0, tail: 0 }); // [abc[d] -> [a]bcd] let mut cursor = Cursor { tail: 0, head: 3 }; - cursor.to_previous_beginning(); + cursor.move_to_previous_beginning(); assert_eq!(cursor, Cursor { head: 0, tail: 3 }); // ab[c]d] -> [a]bc]d let mut cursor = Cursor { head: 2, tail: 3 }; - cursor.to_previous_beginning(); + cursor.move_to_previous_beginning(); assert_eq!(cursor, Cursor { head: 0, tail: 2 }); } } diff --git a/src/main.rs b/src/main.rs index 7e6ddff..9dd127d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,11 +5,13 @@ use app::App; mod cardinality; mod empty_span; +mod select_grey; mod app; mod cursor; const BYTES_PER_LINE: usize = 0x10; const BYTES_PER_CHUNK: usize = 4; +const CHUNKS_PER_LINE: usize = BYTES_PER_LINE / BYTES_PER_CHUNK; fn main() { let mut app = App::init(); diff --git a/src/select_grey.rs b/src/select_grey.rs new file mode 100644 index 0000000..9e7d425 --- /dev/null +++ b/src/select_grey.rs @@ -0,0 +1,11 @@ +use ratatui::style::Color; + +pub trait SelectGrey { + fn select_grey() -> Self; +} + +impl SelectGrey for Color { + fn select_grey() -> Self { + Self::Indexed(242) + } +}