replacing

This commit is contained in:
alice pellerin
2026-03-18 14:46:04 -05:00
parent bfc67a2671
commit ba3d80f111
7 changed files with 114 additions and 44 deletions
+10 -10
View File
@@ -9,9 +9,9 @@ pub enum Action {
NormalMode, NormalMode,
SelectMode, SelectMode,
GPartial, Goto,
ZPartial, Zview,
RPartial, Replace,
MoveByteUp, MoveByteUp,
MoveByteDown, MoveByteDown,
@@ -61,9 +61,9 @@ impl App {
Action::NormalMode => self.normal_mode(), Action::NormalMode => self.normal_mode(),
Action::SelectMode => self.select_mode(), Action::SelectMode => self.select_mode(),
Action::GPartial => self.g_partial(), Action::Goto => self.goto(),
Action::ZPartial => self.z_partial(), Action::Zview => self.zview(),
Action::RPartial => self.r_partial(), Action::Replace => self.replace(),
Action::MoveByteUp => self.move_byte_up(), Action::MoveByteUp => self.move_byte_up(),
Action::MoveByteDown => self.move_byte_down(), Action::MoveByteDown => self.move_byte_down(),
@@ -118,15 +118,15 @@ impl App {
self.mode = Mode::Select; self.mode = Mode::Select;
} }
const fn g_partial(&mut self) { const fn goto(&mut self) {
self.partial_action = Some(PartialAction::Goto); self.partial_action = Some(PartialAction::Goto);
} }
const fn z_partial(&mut self) { const fn zview(&mut self) {
self.partial_action = Some(PartialAction::Zview); self.partial_action = Some(PartialAction::Zview);
} }
const fn r_partial(&mut self) { const fn replace(&mut self) {
self.partial_action = Some(PartialAction::Replace); self.partial_action = Some(PartialAction::Replace);
} }
@@ -343,7 +343,7 @@ impl App {
self.execute_and_add( self.execute_and_add(
EditAction::Delete { EditAction::Delete {
cursor: self.cursor, cursor: self.cursor,
data: self.contents[self.cursor.range()].into() old_data: self.contents[self.cursor.range()].into()
} }
); );
+68 -14
View File
@@ -20,6 +20,7 @@ pub struct App {
pub mode: Mode, pub mode: Mode,
pub partial_action: Option<PartialAction>, pub partial_action: Option<PartialAction>,
pub partial_replace: Option<u8>,
pub edit_history: Vec<EditAction>, pub edit_history: Vec<EditAction>,
// some index to keep track of where we are? edit_prophecy? // some index to keep track of where we are? edit_prophecy?
@@ -27,18 +28,18 @@ pub struct App {
pub logs: Vec<String>, pub logs: Vec<String>,
} }
#[derive(Hash, PartialEq, Eq)] #[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub enum Mode { pub enum Mode {
Normal, Select, Insert Normal, Select, Insert
} }
#[derive(Hash, PartialEq, Eq)] #[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub enum PartialAction { pub enum PartialAction {
Goto, Zview, Replace Goto, Zview, Replace
} }
impl Mode { impl Mode {
pub const fn label(&self) -> &'static str { pub const fn label(self) -> &'static str {
match self { match self {
Self::Normal => " NORMAL ", Self::Normal => " NORMAL ",
Self::Select => " SELECT ", Self::Select => " SELECT ",
@@ -46,7 +47,7 @@ impl Mode {
} }
} }
pub const fn color(&self) -> Color { pub const fn color(self) -> Color {
match self { match self {
Self::Normal => Color::Blue, Self::Normal => Color::Blue,
Self::Select => Color::Yellow, Self::Select => Color::Yellow,
@@ -56,7 +57,7 @@ impl Mode {
} }
impl PartialAction { impl PartialAction {
pub const fn label(&self) -> &'static str { pub const fn label(self) -> &'static str {
match self { match self {
Self::Goto => "g", Self::Goto => "g",
Self::Zview => "z", Self::Zview => "z",
@@ -98,6 +99,7 @@ impl App {
mode: Mode::Normal, mode: Mode::Normal,
partial_action: None, partial_action: None,
partial_replace: None,
edit_history: Vec::new(), edit_history: Vec::new(),
@@ -122,17 +124,69 @@ impl App {
} }
fn handle_key(&mut self, event: KeyEvent) { fn handle_key(&mut self, event: KeyEvent) {
let should_reset_partial = self.partial_action.is_some(); if self.partial_action == Some(PartialAction::Replace) {
if let Some(hex_character) = event.code.as_char() &&
let Some(nybble) = nybble_from_hex(hex_character)
{
if let Some(partial_replace) = self.partial_replace.take() {
self.execute_and_add(
EditAction::Replace {
cursor: self.cursor,
old_data: self.contents[self.cursor.range()].into(),
new_byte: partial_replace << 4 | nybble
}
);
self.partial_action = None;
} else {
self.partial_replace = Some(nybble);
}
} else {
self.partial_action = None;
self.partial_replace = None;
}
} else {
let should_reset_partial = self.partial_action.is_some();
if let Some(mode_config) = self.config.0.get(&self.mode) && if let Some(mode_config) = self.config.0.get(&self.mode) &&
let Some(keybinds) = mode_config.0.get(&self.partial_action) && let Some(keybinds) = mode_config.0.get(&self.partial_action) &&
let Some(action) = keybinds.0.get(&event.into()) let Some(action) = keybinds.0.get(&event.into())
{ {
self.execute(*action); self.execute(*action);
} }
if should_reset_partial { if should_reset_partial {
self.partial_action = None; self.partial_action = None;
}
}
}
}
fn nybble_from_hex(hex: char) -> Option<u8> {
if !hex.is_ascii() { return None }
match hex {
'0'..='9' => Some(u8::try_from(hex).unwrap() - u8::try_from('0').unwrap()),
'a'..='f' => Some(u8::try_from(hex).unwrap() - u8::try_from('a').unwrap() + 10),
'A'..='F' => Some(u8::try_from(hex).unwrap() - u8::try_from('A').unwrap() + 10),
_ => None
}
}
mod tests {
#[allow(unused_imports)]
use crate::app::nybble_from_hex;
#[test]
fn nybble_from_hex_case_doesnt_matter() {
for character in 'a'..='f' {
assert_eq!(nybble_from_hex(character), nybble_from_hex(character.to_ascii_uppercase()));
}
}
#[test]
fn nybble_from_hex_digits_are_correct() {
for (index, character) in ('0'..='9').enumerate() {
assert_eq!(nybble_from_hex(character), Some(index as u8));
} }
} }
} }
+2 -2
View File
@@ -394,7 +394,7 @@ mod status_line {
} }
mod extra_statuses { mod extra_statuses {
use crate::app::{App, PartialAction}; use crate::app::App;
use ratatui::text::Line; use ratatui::text::Line;
impl App { impl App {
@@ -404,7 +404,7 @@ mod extra_statuses {
let partial_action = self.partial_action let partial_action = self.partial_action
.as_ref() .as_ref()
.map_or("", PartialAction::label); .map_or("", |partial_action| partial_action.label());
format!("{partial_action} {percentage:.0}% ").into() format!("{partial_action} {percentage:.0}% ").into()
} }
+6 -6
View File
@@ -103,9 +103,9 @@ impl Default for Config {
("v".try_into().unwrap(), Action::SelectMode), ("v".try_into().unwrap(), Action::SelectMode),
("g".try_into().unwrap(), Action::GPartial), ("g".try_into().unwrap(), Action::Goto),
("z".try_into().unwrap(), Action::ZPartial), ("z".try_into().unwrap(), Action::Zview),
("r".try_into().unwrap(), Action::RPartial), ("r".try_into().unwrap(), Action::Replace),
("i".try_into().unwrap(), Action::MoveByteUp), ("i".try_into().unwrap(), Action::MoveByteUp),
("k".try_into().unwrap(), Action::MoveByteDown), ("k".try_into().unwrap(), Action::MoveByteDown),
@@ -147,9 +147,9 @@ impl Default for Config {
("v".try_into().unwrap(), Action::NormalMode), ("v".try_into().unwrap(), Action::NormalMode),
("g".try_into().unwrap(), Action::GPartial), ("g".try_into().unwrap(), Action::Goto),
("z".try_into().unwrap(), Action::ZPartial), ("z".try_into().unwrap(), Action::Zview),
("r".try_into().unwrap(), Action::RPartial), ("r".try_into().unwrap(), Action::Replace),
("i".try_into().unwrap(), Action::ExtendByteUp), ("i".try_into().unwrap(), Action::ExtendByteUp),
("k".try_into().unwrap(), Action::ExtendByteDown), ("k".try_into().unwrap(), Action::ExtendByteDown),
+1 -1
View File
@@ -1,6 +1,6 @@
use std::ops::RangeInclusive; use std::ops::RangeInclusive;
#[derive(Clone, Copy, Default, PartialEq, Eq)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Cursor { pub struct Cursor {
pub head: usize, pub head: usize,
pub tail: usize pub tail: usize
+15 -1
View File
@@ -4,7 +4,12 @@ use crate::{app::App, cursor::Cursor};
pub enum EditAction { pub enum EditAction {
Delete { Delete {
cursor: Cursor, cursor: Cursor,
data: Vec<u8> old_data: Vec<u8>
},
Replace {
cursor: Cursor,
old_data: Vec<u8>,
new_byte: u8
} }
} }
@@ -17,6 +22,9 @@ impl App {
fn execute_edit(&mut self, edit_action: &EditAction) { fn execute_edit(&mut self, edit_action: &EditAction) {
match edit_action { match edit_action {
EditAction::Delete { cursor, .. } => self.delete_at(*cursor), EditAction::Delete { cursor, .. } => self.delete_at(*cursor),
EditAction::Replace {
cursor, old_data: _, new_byte
} => self.replace_at_with(*cursor, *new_byte),
} }
} }
@@ -26,4 +34,10 @@ impl App {
self.cursor.head = min(cursor.head, cursor.tail); self.cursor.head = min(cursor.head, cursor.tail);
self.cursor.collapse(); self.cursor.collapse();
} }
fn replace_at_with(&mut self, cursor: Cursor, new_byte: u8) {
self.contents[self.cursor.range()].fill(new_byte);
self.cursor = cursor;
}
} }
+10 -8
View File
@@ -18,23 +18,23 @@ const CHUNKS_PER_LINE: usize = BYTES_PER_LINE / BYTES_PER_CHUNK;
// TODO: // TODO:
// - undo/redo // - undo/redo
// - modes
// - select
// - insert
// - zz/zt/zb
// - modifications // - modifications
// - replace
// - partial action(s)
// - insert/append // - insert/append
// - mode // - mode
// - replace // - how this works with edit history is strange :/
// - partial action // - add to edit history when *leaving* insert mode
// - replace-and-keep-going // - replace-and-keep-going
// - mode // - mode
// - delete
// - change // - change
// - saving
// - search
// - edit character panel // - edit character panel
// - modifier on existing keys like teehee? or jump to panel? // - modifier on existing keys like teehee? or jump to panel?
// - if jump to panel, space? // - if jump to panel, space?
// - search // - zz/zt/zb
// - visual gg/G
// - jumplist // - jumplist
// - f/t // - f/t
// - ascii? // - ascii?
@@ -53,6 +53,8 @@ const CHUNKS_PER_LINE: usize = BYTES_PER_LINE / BYTES_PER_CHUNK;
// - utf8? // - utf8?
// - diffing // - diffing
// when AsciiChar is stabilized, use it instead of char everywhere
fn main() { fn main() {
let mut app = App::init(); let mut app = App::init();
let mut terminal = ratatui::init(); let mut terminal = ratatui::init();