repeat x times

This commit is contained in:
alice pellerin
2026-03-21 16:41:10 -05:00
parent 823e186acd
commit 9ac66fc074
6 changed files with 483 additions and 383 deletions
+110 -235
View File
@@ -1,13 +1,34 @@
use std::{cmp::min, collections::hash_set::Entry, convert::identity, fs::File, io::Write, iter, mem::{replace, swap}}; 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 ratatui::{style::Stylize, text::Span};
use crate::{BYTES_OF_PADDING, BYTES_PER_LINE, LINES_OF_PADDING, 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)] #[derive(Clone, Copy)]
pub enum Action { pub enum Action {
App(AppAction),
Buffer(BufferAction),
Cursor(CursorAction),
}
// actions that act on the app as a whole, not just one buffer
#[derive(Debug, Clone, Copy)]
pub enum AppAction {
QuitIfSaved, QuitIfSaved,
Quit, Quit,
PreviousBuffer,
NextBuffer,
Yank,
}
impl From<AppAction> for Action {
fn from(app_action: AppAction) -> Self {
Self::App(app_action)
}
}
#[derive(Clone, Copy)]
pub enum BufferAction {
NormalMode, NormalMode,
SelectMode, SelectMode,
@@ -15,21 +36,7 @@ pub enum Action {
View, View,
Replace, Replace,
Space, Space,
Repeat,
MoveByteUp,
MoveByteDown,
MoveByteLeft,
MoveByteRight,
ExtendByteUp,
ExtendByteDown,
ExtendByteLeft,
ExtendByteRight,
GotoLineStart,
GotoLineEnd,
GotoFileStart,
GotoFileEnd,
ScrollDown, ScrollDown,
ScrollUp, ScrollUp,
@@ -40,20 +47,9 @@ pub enum Action {
PageDown, PageDown,
PageUp, PageUp,
MoveNextWordStart,
MoveNextWordEnd,
MovePreviousWordStart,
ExtendNextWordStart,
ExtendNextWordEnd,
ExtendPreviousWordStart,
CollapseSelection, CollapseSelection,
FlipSelections, FlipSelections,
ExtendLineBelow,
ExtendLineAbove,
Delete, Delete,
Undo, Undo,
@@ -61,9 +57,6 @@ pub enum Action {
Save, Save,
PreviousBuffer,
NextBuffer,
CopySelectionOnNextLine, CopySelectionOnNextLine,
RotateSelectionsBackward, RotateSelectionsBackward,
@@ -92,106 +85,105 @@ pub enum Action {
AlignViewTop, AlignViewTop,
} }
// actions that act on the app as a whole, not just one buffer impl From<BufferAction> for Action {
pub enum AppAction { fn from(buffer_action: BufferAction) -> Self {
QuitIfSaved, Self::Buffer(buffer_action)
Quit, }
}
#[derive(Clone, Copy)]
pub enum CursorAction {
MoveByteUp,
MoveByteDown,
MoveByteLeft,
MoveByteRight,
PreviousBuffer, ExtendByteUp,
NextBuffer, ExtendByteDown,
ExtendByteLeft,
ExtendByteRight,
GotoLineStart,
GotoLineEnd,
GotoFileStart,
GotoFileEnd,
MoveNextWordStart,
MoveNextWordEnd,
MovePreviousWordStart,
ExtendNextWordStart,
ExtendNextWordEnd,
ExtendPreviousWordStart,
ExtendLineBelow,
ExtendLineAbove,
}
impl From<CursorAction> for Action {
fn from(cursor_action: CursorAction) -> Self {
Self::Cursor(cursor_action)
}
} }
impl Buffer { impl Buffer {
pub fn execute(&mut self, action: Action, window_size: WindowSize) -> Option<AppAction> { pub fn execute(&mut self, action: BufferAction, window_size: WindowSize) {
match action { match action {
Action::QuitIfSaved => return Some(AppAction::QuitIfSaved), BufferAction::NormalMode => self.normal_mode(),
Action::Quit => return Some(AppAction::Quit), BufferAction::SelectMode => self.select_mode(),
Action::NormalMode => self.normal_mode(), BufferAction::Goto => self.goto(),
Action::SelectMode => self.select_mode(), BufferAction::View => self.view(),
BufferAction::Replace => self.replace(),
BufferAction::Space => self.space(),
BufferAction::Repeat => self.repeat(),
Action::Goto => self.goto(), BufferAction::ScrollDown => self.scroll_down(window_size),
Action::View => self.view(), BufferAction::ScrollUp => self.scroll_up(window_size),
Action::Replace => self.replace(),
Action::Space => self.space(),
Action::MoveByteUp => self.move_byte_up(window_size), BufferAction::PageCursorHalfDown => self.page_cursor_half_down(window_size),
Action::MoveByteDown => self.move_byte_down(window_size), BufferAction::PageCursorHalfUp => self.page_cursor_half_up(window_size),
Action::MoveByteLeft => self.move_byte_left(window_size),
Action::MoveByteRight => self.move_byte_right(window_size),
Action::ExtendByteUp => self.extend_byte_up(window_size), BufferAction::PageDown => self.page_down(window_size),
Action::ExtendByteDown => self.extend_byte_down(window_size), BufferAction::PageUp => self.page_up(window_size),
Action::ExtendByteLeft => self.extend_byte_left(window_size),
Action::ExtendByteRight => self.extend_byte_right(window_size),
Action::GotoLineStart => self.goto_line_start(), BufferAction::CollapseSelection => self.collapse_selection(),
Action::GotoLineEnd => self.goto_line_end(), BufferAction::FlipSelections => self.flip_selection(),
Action::GotoFileStart => self.goto_file_start(window_size),
Action::GotoFileEnd => self.goto_file_end(window_size),
Action::ScrollDown => self.scroll_down(window_size), BufferAction::Delete => self.delete(window_size),
Action::ScrollUp => self.scroll_up(window_size),
Action::PageCursorHalfDown => self.page_cursor_half_down(window_size), BufferAction::Undo => self.undo(window_size),
Action::PageCursorHalfUp => self.page_cursor_half_up(window_size), BufferAction::Redo => self.redo(window_size),
Action::PageDown => self.page_down(window_size), BufferAction::Save => self.save(),
Action::PageUp => self.page_up(window_size),
Action::MoveNextWordStart => self.move_next_word_start(window_size), BufferAction::CopySelectionOnNextLine => self.copy_selection_on_next_line(),
Action::MoveNextWordEnd => self.move_next_word_end(window_size),
Action::MovePreviousWordStart => self.move_previous_word_start(window_size),
Action::ExtendNextWordStart => self.extend_next_word_start(window_size), BufferAction::RotateSelectionsBackward => self.rotate_selections_backward(),
Action::ExtendNextWordEnd => self.extend_next_word_end(window_size), BufferAction::RotateSelectionsForward => self.rotate_selections_forward(),
Action::ExtendPreviousWordStart => self.extend_previous_word_start(window_size),
Action::CollapseSelection => self.collapse_selection(), BufferAction::KeepPrimarySelection => self.keep_primary_selection(),
Action::FlipSelections => self.flip_selection(), BufferAction::RemovePrimarySelection => self.remove_primary_selection(),
Action::ExtendLineBelow => self.extend_line_below(window_size), BufferAction::SplitSelectionsInto1s => self.split_selections_into_size(1),
Action::ExtendLineAbove => self.extend_line_above(window_size), BufferAction::SplitSelectionsInto2s => self.split_selections_into_size(2),
BufferAction::SplitSelectionsInto3s => self.split_selections_into_size(3),
BufferAction::SplitSelectionsInto4s => self.split_selections_into_size(4),
BufferAction::SplitSelectionsInto5s => self.split_selections_into_size(5),
BufferAction::SplitSelectionsInto6s => self.split_selections_into_size(6),
BufferAction::SplitSelectionsInto7s => self.split_selections_into_size(7),
BufferAction::SplitSelectionsInto8s => self.split_selections_into_size(8),
BufferAction::SplitSelectionsInto9s => self.split_selections_into_size(9),
Action::Delete => self.delete(window_size), BufferAction::JumpToSelectedOffset => self.jump_to_selected_offset(window_size),
BufferAction::JumpToSelectedOffsetRelativeToMark => self.jump_to_selected_offset_relative_to_mark(window_size),
Action::Undo => self.undo(window_size), BufferAction::ToggleMark => self.toggle_mark(),
Action::Redo => self.redo(window_size),
Action::Save => self.save(), BufferAction::AlignViewCenter => self.align_view_center(window_size),
BufferAction::AlignViewBottom => self.align_view_bottom(window_size),
Action::PreviousBuffer => return Some(AppAction::PreviousBuffer), BufferAction::AlignViewTop => self.align_view_top(),
Action::NextBuffer => return Some(AppAction::NextBuffer),
Action::CopySelectionOnNextLine => self.copy_selection_on_next_line(),
Action::RotateSelectionsBackward => self.rotate_selections_backward(),
Action::RotateSelectionsForward => self.rotate_selections_forward(),
Action::KeepPrimarySelection => self.keep_primary_selection(),
Action::RemovePrimarySelection => self.remove_primary_selection(),
Action::SplitSelectionsInto1s => self.split_selections_into_size(1),
Action::SplitSelectionsInto2s => self.split_selections_into_size(2),
Action::SplitSelectionsInto3s => self.split_selections_into_size(3),
Action::SplitSelectionsInto4s => self.split_selections_into_size(4),
Action::SplitSelectionsInto5s => self.split_selections_into_size(5),
Action::SplitSelectionsInto6s => self.split_selections_into_size(6),
Action::SplitSelectionsInto7s => self.split_selections_into_size(7),
Action::SplitSelectionsInto8s => self.split_selections_into_size(8),
Action::SplitSelectionsInto9s => self.split_selections_into_size(9),
Action::JumpToSelectedOffset => self.jump_to_selected_offset(window_size),
Action::JumpToSelectedOffsetRelativeToMark => self.jump_to_selected_offset_relative_to_mark(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
} }
const fn normal_mode(&mut self) { const fn normal_mode(&mut self) {
@@ -220,79 +212,8 @@ impl Buffer {
self.partial_action = Some(PartialAction::Space); self.partial_action = Some(PartialAction::Space);
} }
fn change_all_cursors(&mut self, transform: impl Fn(&mut Cursor)) { const fn repeat(&mut self) {
transform(&mut self.primary_cursor); self.partial_action = Some(PartialAction::Repeat);
for cursor in &mut self.cursors {
transform(cursor);
}
self.cursors.sort_by_key(|cursor| cursor.head);
self.combine_cursors_if_overlapping();
}
fn move_byte_up(&mut self, window_size: WindowSize) {
self.change_all_cursors(Cursor::move_byte_up);
self.clamp_screen_to_primary_cursor(window_size);
}
fn move_byte_down(&mut self, window_size: WindowSize) {
let max_contents_index = self.max_contents_index();
self.change_all_cursors(|cursor| cursor.move_byte_down(max_contents_index));
self.clamp_screen_to_primary_cursor(window_size);
}
fn move_byte_left(&mut self, window_size: WindowSize) {
self.change_all_cursors(Cursor::move_byte_left);
self.clamp_screen_to_primary_cursor(window_size);
}
fn move_byte_right(&mut self, window_size: WindowSize) {
let max_contents_index = self.max_contents_index();
self.change_all_cursors(|cursor| cursor.move_byte_right(max_contents_index));
self.clamp_screen_to_primary_cursor(window_size);
}
fn extend_byte_up(&mut self, window_size: WindowSize) {
self.change_all_cursors(Cursor::extend_byte_up);
self.clamp_screen_to_primary_cursor(window_size);
}
fn extend_byte_down(&mut self, window_size: WindowSize) {
let max_contents_index = self.max_contents_index();
self.change_all_cursors(|cursor| cursor.extend_byte_down(max_contents_index));
self.clamp_screen_to_primary_cursor(window_size);
}
fn extend_byte_left(&mut self, window_size: WindowSize) {
self.change_all_cursors(Cursor::extend_byte_left);
self.clamp_screen_to_primary_cursor(window_size);
}
fn extend_byte_right(&mut self, window_size: WindowSize) {
let max_contents_index = self.max_contents_index();
self.change_all_cursors(|cursor| cursor.extend_byte_right(max_contents_index));
self.clamp_screen_to_primary_cursor(window_size);
}
fn goto_line_start(&mut self) {
self.change_all_cursors(Cursor::goto_line_start);
}
fn goto_line_end(&mut self) {
let max_contents_index = self.max_contents_index();
self.change_all_cursors(|cursor| cursor.goto_line_end(max_contents_index));
}
fn goto_file_start(&mut self, window_size: WindowSize) {
self.change_all_cursors(Cursor::goto_file_start);
self.clamp_screen_to_primary_cursor(window_size);
}
fn goto_file_end(&mut self, window_size: WindowSize) {
let max_contents_index = self.max_contents_index();
self.change_all_cursors(|cursor| cursor.goto_file_end(max_contents_index));
self.clamp_screen_to_primary_cursor(window_size);
} }
pub fn scroll_down(&mut self, window_size: WindowSize) { pub fn scroll_down(&mut self, window_size: WindowSize) {
@@ -419,40 +340,6 @@ impl Buffer {
self.combine_cursors_if_overlapping(); self.combine_cursors_if_overlapping();
} }
fn move_next_word_start(&mut self, window_size: WindowSize) {
let max_contents_index = self.max_contents_index();
self.change_all_cursors(|cursor| cursor.move_next_word_start(max_contents_index));
self.clamp_screen_to_primary_cursor(window_size);
}
fn move_next_word_end(&mut self, window_size: WindowSize) {
let max_contents_index = self.max_contents_index();
self.change_all_cursors(|cursor| cursor.move_next_word_end(max_contents_index));
self.clamp_screen_to_primary_cursor(window_size);
}
fn move_previous_word_start(&mut self, window_size: WindowSize) {
self.change_all_cursors(Cursor::move_previous_word_start);
self.clamp_screen_to_primary_cursor(window_size);
}
fn extend_next_word_start(&mut self, window_size: WindowSize) {
let max_contents_index = self.max_contents_index();
self.change_all_cursors(|cursor| cursor.extend_next_word_start(max_contents_index));
self.clamp_screen_to_primary_cursor(window_size);
}
fn extend_next_word_end(&mut self, window_size: WindowSize) {
let max_contents_index = self.max_contents_index();
self.change_all_cursors(|cursor| cursor.extend_next_word_end(max_contents_index));
self.clamp_screen_to_primary_cursor(window_size);
}
fn extend_previous_word_start(&mut self, window_size: WindowSize) {
self.change_all_cursors(Cursor::extend_previous_word_start);
self.clamp_screen_to_primary_cursor(window_size);
}
fn collapse_selection(&mut self) { fn collapse_selection(&mut self) {
self.primary_cursor.collapse(); self.primary_cursor.collapse();
@@ -469,18 +356,6 @@ impl Buffer {
} }
} }
fn extend_line_below(&mut self, window_size: WindowSize) {
let max_contents_index = self.max_contents_index();
self.change_all_cursors(|cursor| cursor.extend_line_below(max_contents_index));
self.clamp_screen_to_primary_cursor(window_size);
}
fn extend_line_above(&mut self, window_size: WindowSize) {
let max_contents_index = self.max_contents_index();
self.change_all_cursors(|cursor| cursor.extend_line_above(max_contents_index));
self.clamp_screen_to_primary_cursor(window_size);
}
fn delete(&mut self, window_size: WindowSize) { fn delete(&mut self, window_size: WindowSize) {
if !self.contents.is_empty() { if !self.contents.is_empty() {
self.execute_and_add( self.execute_and_add(
@@ -658,7 +533,7 @@ impl Buffer {
if !iter::once(&self.primary_cursor) if !iter::once(&self.primary_cursor)
.chain(&self.cursors) .chain(&self.cursors)
.all(|cursor| { .all(|cursor| {
bytes_as_nat(&self.contents[cursor.range()]) bytes_to_nat(&self.contents[cursor.range()])
.is_some_and(|offset| offset < self.contents.len()) .is_some_and(|offset| offset < self.contents.len())
}) })
{ {
@@ -675,12 +550,12 @@ impl Buffer {
} }
self.primary_cursor = Cursor::at( self.primary_cursor = Cursor::at(
bytes_as_nat(&self.contents[self.primary_cursor.range()]).unwrap() bytes_to_nat(&self.contents[self.primary_cursor.range()]).unwrap()
); );
for cursor in &mut self.cursors { for cursor in &mut self.cursors {
*cursor = Cursor::at( *cursor = Cursor::at(
bytes_as_nat(&self.contents[cursor.range()]).unwrap() bytes_to_nat(&self.contents[cursor.range()]).unwrap()
); );
} }
@@ -697,7 +572,7 @@ impl Buffer {
if !iter::once(&self.primary_cursor) if !iter::once(&self.primary_cursor)
.chain(&self.cursors) .chain(&self.cursors)
.all(|cursor| { .all(|cursor| {
bytes_as_nat(&self.contents[cursor.range()]) bytes_to_nat(&self.contents[cursor.range()])
.map(|offset| mark_before(cursor.lower_bound(), &sorted_marks) + offset) .map(|offset| mark_before(cursor.lower_bound(), &sorted_marks) + offset)
.is_some_and(|offset| offset < self.contents.len()) .is_some_and(|offset| offset < self.contents.len())
}) })
@@ -715,7 +590,7 @@ impl Buffer {
} }
self.primary_cursor = Cursor::at( self.primary_cursor = Cursor::at(
bytes_as_nat(&self.contents[self.primary_cursor.range()]) bytes_to_nat(&self.contents[self.primary_cursor.range()])
.map(|offset| { .map(|offset| {
mark_before(self.primary_cursor.lower_bound(), &sorted_marks) + offset mark_before(self.primary_cursor.lower_bound(), &sorted_marks) + offset
}) })
@@ -724,7 +599,7 @@ impl Buffer {
for cursor in &mut self.cursors { for cursor in &mut self.cursors {
*cursor = Cursor::at( *cursor = Cursor::at(
bytes_as_nat(&self.contents[cursor.range()]) bytes_to_nat(&self.contents[cursor.range()])
.map(|offset| { .map(|offset| {
mark_before(cursor.lower_bound(), &sorted_marks) + offset mark_before(cursor.lower_bound(), &sorted_marks) + offset
}) })
@@ -789,7 +664,7 @@ impl Buffer {
} }
} }
fn bytes_as_nat(bytes: &[u8]) -> Option<usize> { pub fn bytes_to_nat(bytes: &[u8]) -> Option<usize> {
bytes bytes
.iter() .iter()
.rev() // little-endian .rev() // little-endian
+23
View File
@@ -11,6 +11,9 @@ pub struct App {
pub buffers: Vec<Buffer>, pub buffers: Vec<Buffer>,
pub current_buffer_index: usize, pub current_buffer_index: usize,
pub primary_cursor_register: Vec<u8>,
pub other_cursor_registers: Vec<Vec<u8>>,
pub window_size: WindowSize, pub window_size: WindowSize,
pub should_quit: bool, pub should_quit: bool,
@@ -52,6 +55,9 @@ impl App {
buffers, buffers,
current_buffer_index: 0, current_buffer_index: 0,
primary_cursor_register: Vec::new(),
other_cursor_registers: Vec::new(),
window_size, window_size,
should_quit: false, should_quit: false,
@@ -84,6 +90,8 @@ impl App {
let maybe_app_action = self.buffers[self.current_buffer_index].handle_key( let maybe_app_action = self.buffers[self.current_buffer_index].handle_key(
key_event, key_event,
&self.config, &self.config,
&self.primary_cursor_register,
&self.other_cursor_registers,
self.window_size self.window_size
); );
@@ -94,6 +102,8 @@ impl App {
AppAction::PreviousBuffer => self.previous_buffer(), AppAction::PreviousBuffer => self.previous_buffer(),
AppAction::NextBuffer => self.next_buffer(), AppAction::NextBuffer => self.next_buffer(),
AppAction::Yank => self.yank(),
} }
} }
} }
@@ -184,6 +194,19 @@ impl App {
} }
} }
fn yank(&mut self) {
self.primary_cursor_register = self.current_buffer()
.contents[self.current_buffer().primary_cursor.range()]
.to_vec();
self.other_cursor_registers = self.current_buffer().cursors
.iter()
.map(|cursor| {
self.current_buffer().contents[cursor.range()].to_vec()
})
.collect();
}
pub fn current_buffer(&self) -> &Buffer { pub fn current_buffer(&self) -> &Buffer {
&self.buffers[self.current_buffer_index] &self.buffers[self.current_buffer_index]
} }
+147 -40
View File
@@ -1,8 +1,8 @@
use core::slice::GetDisjointMutIndex; use core::slice::GetDisjointMutIndex;
use std::{collections::HashSet, fs::File, io::Read, path::PathBuf}; use std::{collections::HashSet, fs::File, io::Read, path::PathBuf};
use crossterm::event::KeyEvent; use crossterm::event::KeyEvent;
use ratatui::{style::Color, text::Span}; use ratatui::{style::{Color, Stylize}, text::Span};
use crate::{action::AppAction, app::WindowSize, config::Config, cursor::Cursor, edit_action::EditAction}; use crate::{BYTES_PER_LINE, action::{Action, AppAction, bytes_to_nat}, app::WindowSize, config::Config, cursor::Cursor, edit_action::EditAction};
mod widget; mod widget;
@@ -40,7 +40,7 @@ pub enum Mode {
#[derive(Clone, Copy, Hash, PartialEq, Eq)] #[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub enum PartialAction { pub enum PartialAction {
Goto, View, Replace, Space Goto, View, Replace, Space, Repeat
} }
impl Mode { impl Mode {
@@ -68,6 +68,7 @@ impl PartialAction {
Self::View => "z", Self::View => "z",
Self::Replace => "r", Self::Replace => "r",
Self::Space => "", Self::Space => "",
Self::Repeat => "×",
} }
} }
} }
@@ -108,54 +109,160 @@ impl Buffer {
&mut self, &mut self,
event: KeyEvent, event: KeyEvent,
config: &Config, config: &Config,
primary_cursor_register: &[u8],
other_cursor_registers: &[Vec<u8>],
window_size: WindowSize window_size: WindowSize
) -> Option<AppAction> { ) -> Option<AppAction> {
self.alert_message = "".into(); self.alert_message = "".into();
let mut app_action = None; let app_action = match self.partial_action {
Some(PartialAction::Replace) => {
self.handle_replace(event, window_size);
None
},
Some(PartialAction::Repeat) => {
self.handle_repeat(
event,
config,
primary_cursor_register,
other_cursor_registers,
window_size
);
None
},
_ => self.handle_other_modes(event, config, window_size),
};
if self.partial_action == Some(PartialAction::Replace) { assert!(self.scroll_position.is_multiple_of(BYTES_PER_LINE));
if let Some(hex_character) = event.code.as_char() && assert!(self.scroll_position < self.contents.len());
let Some(nybble) = nybble_from_hex(hex_character) assert!(self.primary_cursor.head < self.contents.len());
{ assert!(self.primary_cursor.tail < self.contents.len());
if let Some(partial_replace) = self.partial_replace.take() { assert!(self.scroll_position <= self.primary_cursor.head);
self.execute_and_add( assert!(self.primary_cursor.head < self.scroll_position + window_size.visible_byte_count());
EditAction::Replace {
primary_cursor: self.primary_cursor, app_action
cursors: self.cursors.clone(), }
primary_old_data: self.contents[self.primary_cursor.range()].to_vec(),
old_data: self.cursors fn handle_replace(&mut self, event: KeyEvent, window_size: WindowSize) {
.iter() if let Some(hex_character) = event.code.as_char() &&
.map(|cursor| self.contents[cursor.range()].to_vec()) let Some(nybble) = nybble_from_hex(hex_character)
.collect(), {
new_byte: partial_replace << 4 | nybble if let Some(partial_replace) = self.partial_replace.take() {
}, self.execute_and_add(
window_size EditAction::Replace {
); primary_cursor: self.primary_cursor,
self.partial_action = None; cursors: self.cursors.clone(),
} else { primary_old_data: self.contents[self.primary_cursor.range()].to_vec(),
self.partial_replace = Some(nybble); old_data: self.cursors
} .iter()
} else { .map(|cursor| self.contents[cursor.range()].to_vec())
.collect(),
new_byte: partial_replace << 4 | nybble
},
window_size
);
self.partial_action = None; self.partial_action = None;
self.partial_replace = None; } else {
self.partial_replace = Some(nybble);
} }
} else { } else {
let should_reset_partial = self.partial_action.is_some(); self.partial_action = None;
self.partial_replace = None;
if let Some(mode_config) = config.0.get(&self.mode) && }
let Some(keybinds) = mode_config.0.get(&self.partial_action) && }
let Some(action) = keybinds.0.get(&event.into())
{ fn handle_other_modes(
app_action = self.execute(*action, window_size); &mut self,
} event: KeyEvent,
config: &Config,
if should_reset_partial { window_size: WindowSize
self.partial_action = None; ) -> Option<AppAction> {
let mut result = None;
let should_reset_partial = self.partial_action.is_some();
if let Some(mode_config) = config.0.get(&self.mode) &&
let Some(keybinds) = mode_config.0.get(&self.partial_action) &&
let Some(action) = keybinds.0.get(&event.into())
{
match action {
Action::App(app_action) => result = Some(*app_action),
Action::Buffer(buffer_action) => self.execute(*buffer_action, window_size),
Action::Cursor(cursor_action) => {
let max_contents_index = self.max_contents_index();
self.primary_cursor.execute(*cursor_action, max_contents_index);
for cursor in &mut self.cursors {
cursor.execute(*cursor_action, max_contents_index);
}
self.cursors.sort_by_key(|cursor| cursor.head);
self.combine_cursors_if_overlapping();
self.clamp_screen_to_primary_cursor(window_size);
},
} }
} }
app_action if should_reset_partial {
self.partial_action = None;
}
result
}
fn handle_repeat(
&mut self,
event: KeyEvent,
config: &Config,
primary_cursor_register: &[u8],
other_cursor_registers: &[Vec<u8>],
window_size: WindowSize
) {
self.partial_action = None;
if let Some(mode_config) = config.0.get(&self.mode) &&
let Some(keybinds) = mode_config.0.get(&Some(PartialAction::Repeat)) &&
let Some(action) = keybinds.0.get(&event.into())
{
match action {
Action::Cursor(cursor_action) => {
let Some(primary_repeat_count) = bytes_to_nat(primary_cursor_register) else {
self.alert_message = Span::from(
"repeat count is too large"
).red();
return;
};
let other_repeat_counts = other_cursor_registers
.iter()
.map(|register| bytes_to_nat(register));
if other_repeat_counts.clone().any(|count| count.is_none()) {
self.alert_message = Span::from(
"repeat count is too large"
).red();
return;
}
let max_contents_index = self.max_contents_index();
for _ in 0..primary_repeat_count {
self.primary_cursor.execute(*cursor_action, max_contents_index);
}
for (cursor, repeat_count) in self.cursors.iter_mut().zip(other_repeat_counts) {
for _ in 0..repeat_count.unwrap() {
cursor.execute(*cursor_action, max_contents_index);
}
}
self.cursors.sort_by_key(|cursor| cursor.head);
self.combine_cursors_if_overlapping();
self.clamp_screen_to_primary_cursor(window_size);
},
_ => panic!("repeated actions may only be cursor actions"),
}
}
} }
pub const fn has_unsaved_changes(&self) -> bool { pub const fn has_unsaved_changes(&self) -> bool {
+161 -103
View File
@@ -1,6 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use crate::{action::Action, buffer::{Mode, PartialAction}}; use crate::{action::{Action, AppAction, BufferAction, CursorAction}, buffer::{Mode, PartialAction}};
pub struct Config( pub struct Config(
pub HashMap<Mode, ModeConfig> pub HashMap<Mode, ModeConfig>
@@ -97,159 +97,217 @@ impl Default for Config {
[ [
(Mode::Normal, [ (Mode::Normal, [
(None, [ (None, [
("q".try_into().unwrap(), Action::QuitIfSaved), ("q".try_into().unwrap(), AppAction::QuitIfSaved.into()),
("Q".try_into().unwrap(), Action::Quit), ("Q".try_into().unwrap(), AppAction::Quit.into()),
("v".try_into().unwrap(), Action::SelectMode), ("v".try_into().unwrap(), BufferAction::SelectMode.into()),
("g".try_into().unwrap(), Action::Goto), ("g".try_into().unwrap(), BufferAction::Goto.into()),
("z".try_into().unwrap(), Action::View), ("z".try_into().unwrap(), BufferAction::View.into()),
("r".try_into().unwrap(), Action::Replace), ("r".try_into().unwrap(), BufferAction::Replace.into()),
(" ".try_into().unwrap(), Action::Space), (" ".try_into().unwrap(), BufferAction::Space.into()),
("*".try_into().unwrap(), BufferAction::Repeat.into()),
("i".try_into().unwrap(), Action::MoveByteUp), ("i".try_into().unwrap(), CursorAction::MoveByteUp.into()),
("k".try_into().unwrap(), Action::MoveByteDown), ("k".try_into().unwrap(), CursorAction::MoveByteDown.into()),
("j".try_into().unwrap(), Action::MoveByteLeft), ("j".try_into().unwrap(), CursorAction::MoveByteLeft.into()),
("l".try_into().unwrap(), Action::MoveByteRight), ("l".try_into().unwrap(), CursorAction::MoveByteRight.into()),
("G".try_into().unwrap(), Action::GotoFileEnd), ("G".try_into().unwrap(), CursorAction::GotoFileEnd.into()),
("C-e".try_into().unwrap(), Action::ScrollDown), ("C-e".try_into().unwrap(), BufferAction::ScrollDown.into()),
("C-y".try_into().unwrap(), Action::ScrollUp), ("C-y".try_into().unwrap(), BufferAction::ScrollUp.into()),
("C-d".try_into().unwrap(), Action::PageCursorHalfDown), ("C-d".try_into().unwrap(), BufferAction::PageCursorHalfDown.into()),
("C-u".try_into().unwrap(), Action::PageCursorHalfUp), ("C-u".try_into().unwrap(), BufferAction::PageCursorHalfUp.into()),
("C-f".try_into().unwrap(), Action::PageDown), ("C-f".try_into().unwrap(), BufferAction::PageDown.into()),
("C-b".try_into().unwrap(), Action::PageUp), ("C-b".try_into().unwrap(), BufferAction::PageUp.into()),
("w".try_into().unwrap(), Action::MoveNextWordStart), ("w".try_into().unwrap(), CursorAction::MoveNextWordStart.into()),
("e".try_into().unwrap(), Action::MoveNextWordEnd), ("e".try_into().unwrap(), CursorAction::MoveNextWordEnd.into()),
("b".try_into().unwrap(), Action::MovePreviousWordStart), ("b".try_into().unwrap(), CursorAction::MovePreviousWordStart.into()),
(";".try_into().unwrap(), Action::CollapseSelection), (";".try_into().unwrap(), BufferAction::CollapseSelection.into()),
("A-;".try_into().unwrap(), Action::FlipSelections), ("A-;".try_into().unwrap(), BufferAction::FlipSelections.into()),
("x".try_into().unwrap(), Action::ExtendLineBelow), ("x".try_into().unwrap(), CursorAction::ExtendLineBelow.into()),
("X".try_into().unwrap(), Action::ExtendLineAbove), ("X".try_into().unwrap(), CursorAction::ExtendLineAbove.into()),
("d".try_into().unwrap(), Action::Delete), ("d".try_into().unwrap(), BufferAction::Delete.into()),
("u".try_into().unwrap(), Action::Undo), ("u".try_into().unwrap(), BufferAction::Undo.into()),
("U".try_into().unwrap(), Action::Redo), ("U".try_into().unwrap(), BufferAction::Redo.into()),
("C-j".try_into().unwrap(), Action::PreviousBuffer), ("C-j".try_into().unwrap(), AppAction::PreviousBuffer.into()),
("C-l".try_into().unwrap(), Action::NextBuffer), ("C-l".try_into().unwrap(), AppAction::NextBuffer.into()),
("C".try_into().unwrap(), Action::CopySelectionOnNextLine), ("C".try_into().unwrap(), BufferAction::CopySelectionOnNextLine.into()),
("(".try_into().unwrap(), Action::RotateSelectionsBackward), ("(".try_into().unwrap(), BufferAction::RotateSelectionsBackward.into()),
(")".try_into().unwrap(), Action::RotateSelectionsForward), (")".try_into().unwrap(), BufferAction::RotateSelectionsForward.into()),
(",".try_into().unwrap(), Action::KeepPrimarySelection), (",".try_into().unwrap(), BufferAction::KeepPrimarySelection.into()),
("A-,".try_into().unwrap(), Action::RemovePrimarySelection), ("A-,".try_into().unwrap(), BufferAction::RemovePrimarySelection.into()),
("1".try_into().unwrap(), Action::SplitSelectionsInto1s), ("1".try_into().unwrap(), BufferAction::SplitSelectionsInto1s.into()),
("2".try_into().unwrap(), Action::SplitSelectionsInto2s), ("2".try_into().unwrap(), BufferAction::SplitSelectionsInto2s.into()),
("3".try_into().unwrap(), Action::SplitSelectionsInto3s), ("3".try_into().unwrap(), BufferAction::SplitSelectionsInto3s.into()),
("4".try_into().unwrap(), Action::SplitSelectionsInto4s), ("4".try_into().unwrap(), BufferAction::SplitSelectionsInto4s.into()),
("5".try_into().unwrap(), Action::SplitSelectionsInto5s), ("5".try_into().unwrap(), BufferAction::SplitSelectionsInto5s.into()),
("6".try_into().unwrap(), Action::SplitSelectionsInto6s), ("6".try_into().unwrap(), BufferAction::SplitSelectionsInto6s.into()),
("7".try_into().unwrap(), Action::SplitSelectionsInto7s), ("7".try_into().unwrap(), BufferAction::SplitSelectionsInto7s.into()),
("8".try_into().unwrap(), Action::SplitSelectionsInto8s), ("8".try_into().unwrap(), BufferAction::SplitSelectionsInto8s.into()),
("9".try_into().unwrap(), Action::SplitSelectionsInto9s), ("9".try_into().unwrap(), BufferAction::SplitSelectionsInto9s.into()),
("J".try_into().unwrap(), Action::JumpToSelectedOffsetRelativeToMark), ("J".try_into().unwrap(), BufferAction::JumpToSelectedOffsetRelativeToMark.into()),
("A-J".try_into().unwrap(), Action::JumpToSelectedOffset), ("A-J".try_into().unwrap(), BufferAction::JumpToSelectedOffset.into()),
("m".try_into().unwrap(), Action::ToggleMark), ("m".try_into().unwrap(), BufferAction::ToggleMark.into()),
("y".try_into().unwrap(), AppAction::Yank.into()),
].into()), ].into()),
(Some(PartialAction::Goto), [ (Some(PartialAction::Goto), [
("j".try_into().unwrap(), Action::GotoLineStart), ("j".try_into().unwrap(), CursorAction::GotoLineStart.into()),
("l".try_into().unwrap(), Action::GotoLineEnd), ("l".try_into().unwrap(), CursorAction::GotoLineEnd.into()),
("g".try_into().unwrap(), Action::GotoFileStart), ("g".try_into().unwrap(), CursorAction::GotoFileStart.into()),
].into()), ].into()),
(Some(PartialAction::View), [ (Some(PartialAction::View), [
("z".try_into().unwrap(), Action::AlignViewCenter), ("z".try_into().unwrap(), BufferAction::AlignViewCenter.into()),
("b".try_into().unwrap(), Action::AlignViewBottom), ("b".try_into().unwrap(), BufferAction::AlignViewBottom.into()),
("t".try_into().unwrap(), Action::AlignViewTop), ("t".try_into().unwrap(), BufferAction::AlignViewTop.into()),
].into()), ].into()),
(Some(PartialAction::Space), [ (Some(PartialAction::Space), [
("w".try_into().unwrap(), Action::Save), ("w".try_into().unwrap(), BufferAction::Save.into()),
].into()),
(Some(PartialAction::Repeat), [
("i".try_into().unwrap(), CursorAction::MoveByteUp.into()),
("k".try_into().unwrap(), CursorAction::MoveByteDown.into()),
("j".try_into().unwrap(), CursorAction::MoveByteLeft.into()),
("l".try_into().unwrap(), CursorAction::MoveByteRight.into()),
("C-e".try_into().unwrap(), BufferAction::ScrollDown.into()),
("C-y".try_into().unwrap(), BufferAction::ScrollUp.into()),
("C-d".try_into().unwrap(), BufferAction::PageCursorHalfDown.into()),
("C-u".try_into().unwrap(), BufferAction::PageCursorHalfUp.into()),
("C-f".try_into().unwrap(), BufferAction::PageDown.into()),
("C-b".try_into().unwrap(), BufferAction::PageUp.into()),
("w".try_into().unwrap(), CursorAction::MoveNextWordStart.into()),
("e".try_into().unwrap(), CursorAction::MoveNextWordEnd.into()),
("b".try_into().unwrap(), CursorAction::MovePreviousWordStart.into()),
("x".try_into().unwrap(), CursorAction::ExtendLineBelow.into()),
("X".try_into().unwrap(), CursorAction::ExtendLineAbove.into()),
("d".try_into().unwrap(), BufferAction::Delete.into()),
("C".try_into().unwrap(), BufferAction::CopySelectionOnNextLine.into()),
].into()), ].into()),
].into()), ].into()),
(Mode::Select, [ (Mode::Select, [
(None, [ (None, [
("q".try_into().unwrap(), Action::QuitIfSaved), ("q".try_into().unwrap(), AppAction::QuitIfSaved.into()),
("Q".try_into().unwrap(), Action::Quit), ("Q".try_into().unwrap(), AppAction::Quit.into()),
("v".try_into().unwrap(), Action::NormalMode), ("v".try_into().unwrap(), BufferAction::NormalMode.into()),
("g".try_into().unwrap(), Action::Goto), ("g".try_into().unwrap(), BufferAction::Goto.into()),
("z".try_into().unwrap(), Action::View), ("z".try_into().unwrap(), BufferAction::View.into()),
("r".try_into().unwrap(), Action::Replace), ("r".try_into().unwrap(), BufferAction::Replace.into()),
(" ".try_into().unwrap(), Action::Space), (" ".try_into().unwrap(), BufferAction::Space.into()),
("*".try_into().unwrap(), BufferAction::Repeat.into()),
("i".try_into().unwrap(), Action::ExtendByteUp), ("i".try_into().unwrap(), CursorAction::ExtendByteUp.into()),
("k".try_into().unwrap(), Action::ExtendByteDown), ("k".try_into().unwrap(), CursorAction::ExtendByteDown.into()),
("j".try_into().unwrap(), Action::ExtendByteLeft), ("j".try_into().unwrap(), CursorAction::ExtendByteLeft.into()),
("l".try_into().unwrap(), Action::ExtendByteRight), ("l".try_into().unwrap(), CursorAction::ExtendByteRight.into()),
("C-e".try_into().unwrap(), Action::ScrollDown), ("C-e".try_into().unwrap(), BufferAction::ScrollDown.into()),
("C-y".try_into().unwrap(), Action::ScrollUp), ("C-y".try_into().unwrap(), BufferAction::ScrollUp.into()),
("C-d".try_into().unwrap(), Action::PageCursorHalfDown), ("C-d".try_into().unwrap(), BufferAction::PageCursorHalfDown.into()),
("C-u".try_into().unwrap(), Action::PageCursorHalfUp), ("C-u".try_into().unwrap(), BufferAction::PageCursorHalfUp.into()),
("C-f".try_into().unwrap(), Action::PageDown), ("C-f".try_into().unwrap(), BufferAction::PageDown.into()),
("C-b".try_into().unwrap(), Action::PageUp), ("C-b".try_into().unwrap(), BufferAction::PageUp.into()),
("w".try_into().unwrap(), Action::ExtendNextWordStart), ("w".try_into().unwrap(), CursorAction::ExtendNextWordStart.into()),
("e".try_into().unwrap(), Action::ExtendNextWordEnd), ("e".try_into().unwrap(), CursorAction::ExtendNextWordEnd.into()),
("b".try_into().unwrap(), Action::ExtendPreviousWordStart), ("b".try_into().unwrap(), CursorAction::ExtendPreviousWordStart.into()),
(";".try_into().unwrap(), Action::CollapseSelection), (";".try_into().unwrap(), BufferAction::CollapseSelection.into()),
("A-;".try_into().unwrap(), Action::FlipSelections), ("A-;".try_into().unwrap(), BufferAction::FlipSelections.into()),
("x".try_into().unwrap(), Action::ExtendLineBelow), ("x".try_into().unwrap(), CursorAction::ExtendLineBelow.into()),
("X".try_into().unwrap(), Action::ExtendLineAbove), ("X".try_into().unwrap(), CursorAction::ExtendLineAbove.into()),
("d".try_into().unwrap(), Action::Delete), ("d".try_into().unwrap(), BufferAction::Delete.into()),
("u".try_into().unwrap(), Action::Undo), ("u".try_into().unwrap(), BufferAction::Undo.into()),
("U".try_into().unwrap(), Action::Redo), ("U".try_into().unwrap(), BufferAction::Redo.into()),
("C".try_into().unwrap(), Action::CopySelectionOnNextLine), ("C".try_into().unwrap(), BufferAction::CopySelectionOnNextLine.into()),
("(".try_into().unwrap(), Action::RotateSelectionsBackward), ("(".try_into().unwrap(), BufferAction::RotateSelectionsBackward.into()),
(")".try_into().unwrap(), Action::RotateSelectionsForward), (")".try_into().unwrap(), BufferAction::RotateSelectionsForward.into()),
(",".try_into().unwrap(), Action::KeepPrimarySelection), (",".try_into().unwrap(), BufferAction::KeepPrimarySelection.into()),
("A-,".try_into().unwrap(), Action::RemovePrimarySelection), ("A-,".try_into().unwrap(), BufferAction::RemovePrimarySelection.into()),
("1".try_into().unwrap(), Action::SplitSelectionsInto1s), ("1".try_into().unwrap(), BufferAction::SplitSelectionsInto1s.into()),
("2".try_into().unwrap(), Action::SplitSelectionsInto2s), ("2".try_into().unwrap(), BufferAction::SplitSelectionsInto2s.into()),
("3".try_into().unwrap(), Action::SplitSelectionsInto3s), ("3".try_into().unwrap(), BufferAction::SplitSelectionsInto3s.into()),
("4".try_into().unwrap(), Action::SplitSelectionsInto4s), ("4".try_into().unwrap(), BufferAction::SplitSelectionsInto4s.into()),
("5".try_into().unwrap(), Action::SplitSelectionsInto5s), ("5".try_into().unwrap(), BufferAction::SplitSelectionsInto5s.into()),
("6".try_into().unwrap(), Action::SplitSelectionsInto6s), ("6".try_into().unwrap(), BufferAction::SplitSelectionsInto6s.into()),
("7".try_into().unwrap(), Action::SplitSelectionsInto7s), ("7".try_into().unwrap(), BufferAction::SplitSelectionsInto7s.into()),
("8".try_into().unwrap(), Action::SplitSelectionsInto8s), ("8".try_into().unwrap(), BufferAction::SplitSelectionsInto8s.into()),
("9".try_into().unwrap(), Action::SplitSelectionsInto9s), ("9".try_into().unwrap(), BufferAction::SplitSelectionsInto9s.into()),
("J".try_into().unwrap(), Action::JumpToSelectedOffsetRelativeToMark), ("J".try_into().unwrap(), BufferAction::JumpToSelectedOffsetRelativeToMark.into()),
("A-J".try_into().unwrap(), Action::JumpToSelectedOffset), ("A-J".try_into().unwrap(), BufferAction::JumpToSelectedOffset.into()),
("m".try_into().unwrap(), Action::ToggleMark), ("m".try_into().unwrap(), BufferAction::ToggleMark.into()),
("y".try_into().unwrap(), AppAction::Yank.into()),
].into()), ].into()),
(Some(PartialAction::View), [ (Some(PartialAction::View), [
("z".try_into().unwrap(), Action::AlignViewCenter), ("z".try_into().unwrap(), BufferAction::AlignViewCenter.into()),
("b".try_into().unwrap(), Action::AlignViewBottom), ("b".try_into().unwrap(), BufferAction::AlignViewBottom.into()),
("t".try_into().unwrap(), Action::AlignViewTop), ("t".try_into().unwrap(), BufferAction::AlignViewTop.into()),
].into()), ].into()),
(Some(PartialAction::Space), [ (Some(PartialAction::Space), [
("w".try_into().unwrap(), Action::Save), ("w".try_into().unwrap(), BufferAction::Save.into()),
].into()),
(Some(PartialAction::Repeat), [
("i".try_into().unwrap(), CursorAction::ExtendByteUp.into()),
("k".try_into().unwrap(), CursorAction::ExtendByteDown.into()),
("j".try_into().unwrap(), CursorAction::ExtendByteLeft.into()),
("l".try_into().unwrap(), CursorAction::ExtendByteRight.into()),
("C-e".try_into().unwrap(), BufferAction::ScrollDown.into()),
("C-y".try_into().unwrap(), BufferAction::ScrollUp.into()),
("C-d".try_into().unwrap(), BufferAction::PageCursorHalfDown.into()),
("C-u".try_into().unwrap(), BufferAction::PageCursorHalfUp.into()),
("C-f".try_into().unwrap(), BufferAction::PageDown.into()),
("C-b".try_into().unwrap(), BufferAction::PageUp.into()),
("w".try_into().unwrap(), CursorAction::ExtendNextWordStart.into()),
("e".try_into().unwrap(), CursorAction::ExtendNextWordEnd.into()),
("b".try_into().unwrap(), CursorAction::ExtendPreviousWordStart.into()),
("x".try_into().unwrap(), CursorAction::ExtendLineBelow.into()),
("X".try_into().unwrap(), CursorAction::ExtendLineAbove.into()),
("d".try_into().unwrap(), BufferAction::Delete.into()),
("C".try_into().unwrap(), BufferAction::CopySelectionOnNextLine.into()),
].into()), ].into()),
].into()) ].into())
].into() ].into()
+35 -2
View File
@@ -1,6 +1,5 @@
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};
use crate::BYTES_PER_LINE;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Cursor { pub struct Cursor {
@@ -77,6 +76,40 @@ impl Cursor {
} }
} }
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) { pub const fn move_byte_up(&mut self) {
if self.head >= BYTES_PER_LINE { if self.head >= BYTES_PER_LINE {
self.head -= BYTES_PER_LINE; self.head -= BYTES_PER_LINE;
+7 -3
View File
@@ -26,6 +26,10 @@ const LINES_OF_PADDING: usize = 5;
const BYTES_OF_PADDING: usize = LINES_OF_PADDING * BYTES_PER_LINE; const BYTES_OF_PADDING: usize = LINES_OF_PADDING * BYTES_PER_LINE;
// TODO: // TODO:
// - extend to mark (tm?)
// - t0 can be to next null
// - tf can be to next FF
// - inspect selection
// - resizing can move the cursor off the screen // - resizing can move the cursor off the screen
// - tab bar overflow // - tab bar overflow
// - search // - search
@@ -46,9 +50,6 @@ const BYTES_OF_PADDING: usize = LINES_OF_PADDING * BYTES_PER_LINE;
// - y/p // - y/p
// - [/] to cycle view offset? // - [/] to cycle view offset?
// - gj jump to entered offset // - gj jump to entered offset
// - repeat X times (dec and hex)
// - from register??
// - extend to mark (tm?)
// future directions // future directions
// - 'views' for bytes (i8/16/etc u8/16/etc 20.12/8.4/etc) // - 'views' for bytes (i8/16/etc u8/16/etc 20.12/8.4/etc)
@@ -79,6 +80,9 @@ fn main() {
// dbg!(app.edit_history); // dbg!(app.edit_history);
// dbg!(app.primary_cursor_register);
// dbg!(app.other_cursor_registers);
for log in app.logs { for log in app.logs {
println!("{log}"); println!("{log}");
} }