diff --git a/src/action.rs b/src/action.rs index 48b9185..23dde22 100644 --- a/src/action.rs +++ b/src/action.rs @@ -1,7 +1,7 @@ use std::{cmp::min, collections::hash_set::Entry, convert::identity, fs::File, io::Write, iter, mem::{replace, swap}}; use ratatui::{style::Stylize, text::Span}; -use crate::{BYTES_PER_LINE, app::WindowSize, buffer::{Buffer, Mode, PartialAction}, cursor::Cursor, edit_action::EditAction}; +use crate::{BYTES_OF_PADDING, BYTES_PER_LINE, LINES_OF_PADDING, app::WindowSize, buffer::{Buffer, Mode, PartialAction}, cursor::Cursor, edit_action::EditAction}; #[derive(Clone, Copy)] pub enum Action { @@ -85,6 +85,10 @@ pub enum Action { JumpToSelectedOffset, ToggleMark, + + AlignViewCenter, + AlignViewBottom, + AlignViewTop, } // actions that act on the app as a whole, not just one buffer @@ -148,10 +152,10 @@ impl Buffer { Action::ExtendLineBelow => self.extend_line_below(window_size), Action::ExtendLineAbove => self.extend_line_above(window_size), - Action::Delete => self.delete(), + Action::Delete => self.delete(window_size), - Action::Undo => self.undo(), - Action::Redo => self.redo(), + Action::Undo => self.undo(window_size), + Action::Redo => self.redo(window_size), Action::Save => self.save(), @@ -179,6 +183,10 @@ impl Buffer { Action::JumpToSelectedOffset => self.jump_to_selected_offset(window_size), Action::ToggleMark => self.toggle_mark(), + + Action::AlignViewCenter => self.align_view_center(window_size), + Action::AlignViewBottom => self.align_view_bottom(window_size), + Action::AlignViewTop => self.align_view_top(), } None @@ -286,31 +294,45 @@ impl Buffer { } pub fn scroll_down(&mut self, window_size: WindowSize) { - if self.contents.len() <= 5 * BYTES_PER_LINE { return; } + if self.contents.len() <= BYTES_OF_PADDING { return; } self.scroll_position = min( self.scroll_position + BYTES_PER_LINE, - self.contents.len() - (5 * BYTES_PER_LINE) + self.contents.len() - BYTES_OF_PADDING - self.contents.len() % BYTES_PER_LINE ); - self.primary_cursor.clamp(self.scroll_position, window_size.visible_byte_count()); + if window_size.hex_rows() > LINES_OF_PADDING * 2 { + self.primary_cursor.clamp( + self.scroll_position + BYTES_OF_PADDING, + window_size.visible_byte_count() - BYTES_OF_PADDING * 2 + ); + } 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); - self.primary_cursor.clamp(self.scroll_position, window_size.visible_byte_count()); + if window_size.hex_rows() > LINES_OF_PADDING * 2 { + self.primary_cursor.clamp( + self.scroll_position + BYTES_OF_PADDING, + window_size.visible_byte_count() - BYTES_OF_PADDING * 2 + ); + } else { + self.primary_cursor.clamp(self.scroll_position, window_size.visible_byte_count()); + } self.combine_cursors_if_overlapping(); } fn page_cursor_half_down(&mut self, window_size: WindowSize) { - if self.contents.len() <= 5 * BYTES_PER_LINE { return; } + if self.contents.len() <= BYTES_OF_PADDING { return; } let old_scroll_position = self.scroll_position; self.scroll_position = min( self.scroll_position + (window_size.visible_byte_count() / 2).next_multiple_of(BYTES_PER_LINE), - self.contents.len() - (5 * BYTES_PER_LINE) + self.contents.len() - BYTES_OF_PADDING - self.contents.len() % BYTES_PER_LINE ); let scroll_position_change = self.scroll_position - old_scroll_position; @@ -361,14 +383,21 @@ impl Buffer { } fn page_down(&mut self, window_size: WindowSize) { - if self.contents.len() <= 5 * BYTES_PER_LINE { return; } + if self.contents.len() <= BYTES_OF_PADDING { return; } self.scroll_position = min( self.scroll_position + window_size.visible_byte_count(), - self.contents.len() - (5 * BYTES_PER_LINE) + self.contents.len() - BYTES_OF_PADDING - self.contents.len() % BYTES_PER_LINE ); - self.primary_cursor.clamp(self.scroll_position, window_size.visible_byte_count()); + if window_size.hex_rows() > LINES_OF_PADDING * 2 { + self.primary_cursor.clamp( + self.scroll_position + BYTES_OF_PADDING, + window_size.visible_byte_count() - BYTES_OF_PADDING * 2 + ); + } else { + self.primary_cursor.clamp(self.scroll_position, window_size.visible_byte_count()); + } self.combine_cursors_if_overlapping(); } @@ -377,7 +406,14 @@ impl Buffer { window_size.visible_byte_count() ); - self.primary_cursor.clamp(self.scroll_position, window_size.visible_byte_count()); + if window_size.hex_rows() > LINES_OF_PADDING * 2 { + self.primary_cursor.clamp( + self.scroll_position + BYTES_OF_PADDING, + window_size.visible_byte_count() - BYTES_OF_PADDING * 2 + ); + } else { + self.primary_cursor.clamp(self.scroll_position, window_size.visible_byte_count()); + } self.combine_cursors_if_overlapping(); } @@ -443,7 +479,7 @@ impl Buffer { self.clamp_screen_to_primary_cursor(window_size); } - fn delete(&mut self) { + fn delete(&mut self, window_size: WindowSize) { if !self.contents.is_empty() { self.execute_and_add( EditAction::Delete { @@ -454,7 +490,8 @@ impl Buffer { .iter() .map(|cursor| self.contents[cursor.range()].to_vec()) .collect(), - } + }, + window_size ); } @@ -463,7 +500,7 @@ impl Buffer { } } - fn undo(&mut self) { + fn undo(&mut self, window_size: WindowSize) { if self.time_traveling == Some(0) || self.edit_history.is_empty() { return; } let current_date = self.time_traveling @@ -476,12 +513,12 @@ impl Buffer { EditAction::Placeholder ); - self.undo_edit(&edit_action); + self.undo_edit(&edit_action, window_size); self.edit_history[current_date] = edit_action; } - fn redo(&mut self) { + fn redo(&mut self, window_size: WindowSize) { let Some(previous_date) = self.time_traveling else { return; }; let current_date = previous_date + 1; @@ -497,7 +534,7 @@ impl Buffer { EditAction::Placeholder ); - self.execute_edit(&edit_action); + self.execute_edit(&edit_action, window_size); self.edit_history[previous_date] = edit_action; } @@ -664,19 +701,40 @@ impl Buffer { } } } + + const fn align_view_center(&mut self, window_size: WindowSize) { + let half_a_screen = window_size.visible_byte_count() / 2; + + self.scroll_position = self.primary_cursor.head + .saturating_sub(self.primary_cursor.head % BYTES_PER_LINE) + .saturating_sub(half_a_screen - (half_a_screen % BYTES_PER_LINE)); + } + + fn align_view_bottom(&mut self, window_size: WindowSize) { + self.scroll_position = self.primary_cursor.head + .saturating_sub(self.primary_cursor.head % BYTES_PER_LINE) + .saturating_sub( + window_size + .visible_byte_count() + .saturating_sub(BYTES_PER_LINE + BYTES_OF_PADDING) + ) + .min(self.max_contents_index() - self.max_contents_index() % BYTES_PER_LINE); + } + + const fn align_view_top(&mut self) { + self.scroll_position = self.primary_cursor.head + .saturating_sub(self.primary_cursor.head % BYTES_PER_LINE) + .saturating_sub(BYTES_OF_PADDING); + } } // helpers impl Buffer { - const fn clamp_screen_to_primary_cursor(&mut self, window_size: WindowSize) { - if self.primary_cursor.head < self.scroll_position { - self.scroll_position -= (self.scroll_position - self.primary_cursor.head) - .next_multiple_of(BYTES_PER_LINE); - } else if self.primary_cursor.head > self.scroll_position + window_size.visible_byte_count() - 1 { - let screen_edge_offset_to_cursor = self.primary_cursor.head - ( - self.scroll_position + window_size.visible_byte_count() - 1 - ); - self.scroll_position += screen_edge_offset_to_cursor.next_multiple_of(BYTES_PER_LINE); + pub fn clamp_screen_to_primary_cursor(&mut self, window_size: WindowSize) { + if self.primary_cursor.head < self.scroll_position + BYTES_OF_PADDING { + self.align_view_top(); + } else if self.primary_cursor.head > self.scroll_position + (window_size.visible_byte_count() - 1).saturating_sub(BYTES_OF_PADDING) { + self.align_view_bottom(window_size); } } } diff --git a/src/buffer/mod.rs b/src/buffer/mod.rs index 0dfce9e..738c599 100644 --- a/src/buffer/mod.rs +++ b/src/buffer/mod.rs @@ -129,7 +129,8 @@ impl Buffer { .map(|cursor| self.contents[cursor.range()].to_vec()) .collect(), new_byte: partial_replace << 4 | nybble - } + }, + window_size ); self.partial_action = None; } else { diff --git a/src/buffer/widget.rs b/src/buffer/widget.rs index 2c8c0d4..a30dec9 100644 --- a/src/buffer/widget.rs +++ b/src/buffer/widget.rs @@ -125,7 +125,7 @@ mod hex { let chunks_rendered = chunks.len() + remainder_chunks.iter().len(); let chunks_not_rendered = CHUNKS_PER_LINE - chunks_rendered; - let spaces_per_chunk = BYTES_PER_CHUNK - 1; + let spaces_per_chunk = BYTES_PER_CHUNK - 1 + 2; let bytes_not_rendered = BYTES_PER_LINE - bytes.len(); let padding_width = 2 * bytes_not_rendered + @@ -138,13 +138,6 @@ mod hex { .zip((address..).step_by(BYTES_PER_CHUNK)) .map(|(chunk, address)| self.render_chunk(address, &chunk).collect()) .chain(remainder_chunks) - .interleave( - (address..) - .step_by(BYTES_PER_CHUNK) - .take(CHUNKS_PER_LINE) - .skip(1) - .map(|address| vec![self.render_large_space_before(address)]) - ) .flatten() .chain(repeat_n(" ".into(), padding_width)) } @@ -247,9 +240,10 @@ mod hex { " ".into() }; - if iter::once(&self.primary_cursor) - .chain(&self.cursors) - .any(|cursor| cursor.contains_space_before(address)) + if !address.is_multiple_of(BYTES_PER_LINE) && + iter::once(&self.primary_cursor) + .chain(&self.cursors) + .any(|cursor| cursor.contains_space_before(address)) { span.bg(Color::select_grey()) } else { diff --git a/src/config.rs b/src/config.rs index 994a49f..dbbaebd 100644 --- a/src/config.rs +++ b/src/config.rs @@ -172,6 +172,11 @@ impl Default for Config { ("g".try_into().unwrap(), Action::GotoFileStart), ].into()), + (Some(PartialAction::View), [ + ("z".try_into().unwrap(), Action::AlignViewCenter), + ("b".try_into().unwrap(), Action::AlignViewBottom), + ("t".try_into().unwrap(), Action::AlignViewTop), + ].into()), (Some(PartialAction::Space), [ ("w".try_into().unwrap(), Action::Save), ].into()), @@ -239,6 +244,11 @@ impl Default for Config { ("m".try_into().unwrap(), Action::ToggleMark), ].into()), + (Some(PartialAction::View), [ + ("z".try_into().unwrap(), Action::AlignViewCenter), + ("b".try_into().unwrap(), Action::AlignViewBottom), + ("t".try_into().unwrap(), Action::AlignViewTop), + ].into()), (Some(PartialAction::Space), [ ("w".try_into().unwrap(), Action::Save), ].into()), diff --git a/src/cursor.rs b/src/cursor.rs index 352590c..746da15 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -148,8 +148,7 @@ impl Cursor { } pub const fn goto_file_end(&mut self, max: usize) { - self.head = previous_multiple_of(BYTES_PER_LINE, max + 1) + - (self.head % BYTES_PER_LINE); + self.head += previous_multiple_of(BYTES_PER_LINE, max + 1 - self.head); self.collapse(); } @@ -230,11 +229,14 @@ impl Cursor { self.head = min(self.head + BYTES_PER_LINE, max); } else { self.tail -= self.tail % BYTES_PER_LINE; - self.head += BYTES_PER_LINE - 1 - (self.head % BYTES_PER_LINE); + self.head = min( + self.head + BYTES_PER_LINE - 1 - (self.head % BYTES_PER_LINE), + max + ); } } - pub const fn extend_line_above(&mut self, max: usize) { + pub fn extend_line_above(&mut self, max: usize) { if self.head > self.tail { swap(&mut self.head, &mut self.tail); } @@ -246,7 +248,10 @@ impl Cursor { self.head = self.head.saturating_sub(BYTES_PER_LINE); } else { self.head -= self.head % BYTES_PER_LINE; - self.tail += BYTES_PER_LINE - 1 - (self.tail % BYTES_PER_LINE); + self.tail = min( + self.tail + BYTES_PER_LINE - 1 - (self.tail % BYTES_PER_LINE), + max + ); } } } @@ -267,57 +272,57 @@ mod tests { fn next_word() { // [a]bcd efgh -> abcd [e]fgh let mut cursor = Cursor::at(0); - cursor.move_to_next_word(99); + 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_to_next_word(99); + 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_to_next_word(99); + 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_to_next_word(99); + cursor.move_next_word_start(99); assert_eq!(cursor, Cursor::at(4)); // [a]bcd -> abc[d] let mut cursor = Cursor::at(0); - cursor.move_to_next_word(3); + cursor.move_next_word_start(3); assert_eq!(cursor, Cursor::at(3)); // [a]bc -> ab[c] let mut cursor = Cursor::at(0); - cursor.move_to_next_word(2); + cursor.move_next_word_start(2); assert_eq!(cursor, Cursor::at(2)); // [a]b -> a[b] let mut cursor = Cursor::at(0); - cursor.move_to_next_word(1); + cursor.move_next_word_start(1); assert_eq!(cursor, Cursor::at(1)); // [a] -> [a] let mut cursor = Cursor::at(0); - cursor.move_to_next_word(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_to_next_word(3); + cursor.move_next_word_start(3); assert_eq!(cursor, Cursor::at(3)); // abc[d] -> abc[d] let mut cursor = Cursor::at(3); - cursor.move_to_next_word(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_to_next_word(3); + cursor.move_next_word_start(3); assert_eq!(cursor, Cursor { tail: 2, head: 3 }); } @@ -325,97 +330,97 @@ mod tests { fn next_end() { // [a]bcd -> [abcd] let mut cursor = Cursor::at(0); - cursor.move_to_next_end(99); + 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_to_next_end(99); + 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_to_next_end(99); + 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_to_next_end(99); + 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_to_next_end(99); + 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_to_next_end(99); + 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_to_next_end(99); + 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_to_next_end(99); + 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_to_next_end(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_to_next_end(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_to_next_end(2); + 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_to_next_end(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_to_next_end(1); + cursor.move_next_word_end(1); assert_eq!(cursor, Cursor::at(1)); // [a]b -> [ab] let mut cursor = Cursor::at(0); - cursor.move_to_next_end(1); + 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_to_next_end(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_to_next_end(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_to_next_end(99); + 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_to_next_end(99); + 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_to_next_end(5); + cursor.move_next_word_end(5); assert_eq!(cursor, Cursor { tail: 4, head: 5 }); } @@ -423,57 +428,57 @@ mod tests { fn previous_beginning() { // abcd efgh [i]jkl -> abcd [efgh] ijkl let mut cursor = Cursor::at(8); - cursor.move_to_previous_beginning(); + 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_to_previous_beginning(); + 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_to_previous_beginning(); + 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_to_previous_beginning(); + 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_to_previous_beginning(); + cursor.move_previous_word_start(); assert_eq!(cursor, Cursor { head: 0, tail: 3 }); // abc[d] -> [abcd] let mut cursor = Cursor::at(3); - cursor.move_to_previous_beginning(); + 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_to_previous_beginning(); + 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_to_previous_beginning(); + 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_to_previous_beginning(); + 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_to_previous_beginning(); + 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_to_previous_beginning(); + cursor.move_previous_word_start(); assert_eq!(cursor, Cursor { head: 0, tail: 2 }); } } diff --git a/src/edit_action.rs b/src/edit_action.rs index 29494ec..4645780 100644 --- a/src/edit_action.rs +++ b/src/edit_action.rs @@ -1,5 +1,5 @@ use std::{cmp::min, convert::identity, iter}; -use crate::{buffer::Buffer, cursor::Cursor}; +use crate::{app::WindowSize, buffer::Buffer, cursor::Cursor}; #[derive(Debug)] pub enum EditAction { @@ -34,10 +34,10 @@ pub enum EditAction { } impl Buffer { - pub fn execute_and_add(&mut self, edit_action: EditAction) { + pub fn execute_and_add(&mut self, edit_action: EditAction, window_size: WindowSize) { assert!(!matches!(edit_action, EditAction::Placeholder)); - self.execute_edit(&edit_action); + self.execute_edit(&edit_action, window_size); if let Some(date) = self.time_traveling { self.edit_history.truncate(date); @@ -51,44 +51,57 @@ impl Buffer { self.edit_history.push(edit_action); } - pub fn execute_edit(&mut self, edit_action: &EditAction) { + pub fn execute_edit(&mut self, edit_action: &EditAction, window_size: WindowSize) { match edit_action { EditAction::Placeholder => unreachable!(), - EditAction::Delete { primary_cursor, cursors, .. } => self.delete_at(*primary_cursor, cursors), + EditAction::Delete { primary_cursor, cursors, .. } => { + self.delete_at(*primary_cursor, cursors, window_size); + }, EditAction::Replace { primary_cursor, cursors, primary_old_data: _, old_data: _, new_byte - } => self.replace_at_with(*primary_cursor, cursors, *new_byte), + } => self.replace_at_with(*primary_cursor, cursors, *new_byte, window_size), } } - pub fn undo_edit(&mut self, edit_action: &EditAction) { + pub fn undo_edit(&mut self, edit_action: &EditAction, window_size: WindowSize) { match edit_action { EditAction::Placeholder => unreachable!(), EditAction::Delete { primary_cursor, cursors, primary_old_data, old_data - } => self.undo_delete_at(*primary_cursor, cursors, primary_old_data, old_data), + } => self.undo_delete_at( + *primary_cursor, + cursors, + primary_old_data, + old_data, + window_size + ), EditAction::Replace { primary_cursor, cursors, primary_old_data, old_data, .. - } => self.undo_replace_at_with(*primary_cursor, cursors, primary_old_data, old_data), + } => self.undo_replace_at_with( + *primary_cursor, + cursors, + primary_old_data, + old_data, + window_size + ), } } fn delete_at( &mut self, primary_cursor: Cursor, - cursors: &[Cursor] + cursors: &[Cursor], + window_size: WindowSize ) { let mut bytes_deleted_so_far = 0; for cursor in cursors_in_order(primary_cursor, cursors) { - let range = cursor.range(); - self.contents.drain( - (range.start() - bytes_deleted_so_far)..=(range.end() - bytes_deleted_so_far) + (cursor.lower_bound() - bytes_deleted_so_far)..= + (cursor.upper_bound() - bytes_deleted_so_far) ); - // RangeInclusive::len() is unstable/nonexistant :/ - bytes_deleted_so_far += range.end() - range.start() + 1; + bytes_deleted_so_far += cursor.len(); } self.primary_cursor.head = min( @@ -103,6 +116,7 @@ impl Buffer { .collect(); self.combine_cursors_if_overlapping(); + self.clamp_screen_to_primary_cursor(window_size); } fn undo_delete_at( @@ -110,7 +124,8 @@ impl Buffer { primary_cursor: Cursor, cursors: &[Cursor], primary_old_data: &[u8], - old_data: &[Vec] + old_data: &[Vec], + window_size: WindowSize ) { let primary_cursor_start = primary_cursor.lower_bound(); @@ -130,13 +145,15 @@ impl Buffer { self.primary_cursor = primary_cursor; self.cursors = cursors.to_vec(); + self.clamp_screen_to_primary_cursor(window_size); } fn replace_at_with( &mut self, primary_cursor: Cursor, cursors: &[Cursor], - new_byte: u8 + new_byte: u8, + window_size: WindowSize ) { self.contents[primary_cursor.range()].fill(new_byte); @@ -146,6 +163,7 @@ impl Buffer { self.primary_cursor = primary_cursor; self.cursors = cursors.to_vec(); + self.clamp_screen_to_primary_cursor(window_size); } fn undo_replace_at_with( @@ -153,7 +171,8 @@ impl Buffer { primary_cursor: Cursor, cursors: &[Cursor], primary_old_data: &[u8], - old_data: &[Vec] + old_data: &[Vec], + window_size: WindowSize ) { self.contents.splice( primary_cursor.range(), @@ -169,6 +188,7 @@ impl Buffer { self.primary_cursor = primary_cursor; self.cursors = cursors.to_vec(); + self.clamp_screen_to_primary_cursor(window_size); } } diff --git a/src/main.rs b/src/main.rs index 9bd1e09..cd1bf5e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,7 +22,14 @@ const BYTES_PER_LINE: usize = 0x10; const BYTES_PER_CHUNK: usize = 4; const CHUNKS_PER_LINE: usize = BYTES_PER_LINE / BYTES_PER_CHUNK; +const LINES_OF_PADDING: usize = 5; +const BYTES_OF_PADDING: usize = LINES_OF_PADDING * BYTES_PER_LINE; + // TODO: +// - zz/zt/zb +// - resizing can move the cursor off the screen +// - constant for 5 lines of padding +// - pad in clamp function, always // - search // - s/A-k/A-K // - C-a/C-x @@ -36,7 +43,6 @@ const CHUNKS_PER_LINE: usize = BYTES_PER_LINE / BYTES_PER_CHUNK; // - edit character panel // - modifier on existing keys like teehee? or jump to panel? // - if jump to panel, space? -// - zz/zt/zb // - visual gg/G // - jumplist // - y/p