jump relative to mark

This commit is contained in:
alice pellerin
2026-03-21 04:48:17 -05:00
parent 90090bab24
commit 823e186acd
4 changed files with 76 additions and 10 deletions
+59
View File
@@ -83,6 +83,7 @@ pub enum Action {
SplitSelectionsInto9s, SplitSelectionsInto9s,
JumpToSelectedOffset, JumpToSelectedOffset,
JumpToSelectedOffsetRelativeToMark,
ToggleMark, ToggleMark,
@@ -181,6 +182,7 @@ impl Buffer {
Action::SplitSelectionsInto9s => self.split_selections_into_size(9), Action::SplitSelectionsInto9s => self.split_selections_into_size(9),
Action::JumpToSelectedOffset => self.jump_to_selected_offset(window_size), 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::ToggleMark => self.toggle_mark(),
@@ -688,6 +690,54 @@ impl Buffer {
self.clamp_screen_to_primary_cursor(window_size); self.clamp_screen_to_primary_cursor(window_size);
} }
fn jump_to_selected_offset_relative_to_mark(&mut self, window_size: WindowSize) {
let mut sorted_marks: Vec<_> = self.marks.iter().copied().collect();
sorted_marks.sort_unstable();
if !iter::once(&self.primary_cursor)
.chain(&self.cursors)
.all(|cursor| {
bytes_as_nat(&self.contents[cursor.range()])
.map(|offset| mark_before(cursor.lower_bound(), &sorted_marks) + offset)
.is_some_and(|offset| offset < self.contents.len())
})
{
if self.cursors.is_empty() {
self.alert_message = Span::from(
"selection is not a valid offset"
).red();
} else {
self.alert_message = Span::from(
"not all selections are valid offsets"
).red();
}
return;
}
self.primary_cursor = Cursor::at(
bytes_as_nat(&self.contents[self.primary_cursor.range()])
.map(|offset| {
mark_before(self.primary_cursor.lower_bound(), &sorted_marks) + offset
})
.unwrap()
);
for cursor in &mut self.cursors {
*cursor = Cursor::at(
bytes_as_nat(&self.contents[cursor.range()])
.map(|offset| {
mark_before(cursor.lower_bound(), &sorted_marks) + offset
})
.unwrap()
);
}
self.cursors.sort_by_key(|cursor| cursor.head);
self.combine_cursors_if_overlapping();
self.clamp_screen_to_primary_cursor(window_size);
}
fn toggle_mark(&mut self) { fn toggle_mark(&mut self) {
match self.marks.entry(self.primary_cursor.lower_bound()) { match self.marks.entry(self.primary_cursor.lower_bound()) {
Entry::Occupied(occupied_entry) => { occupied_entry.remove(); }, Entry::Occupied(occupied_entry) => { occupied_entry.remove(); },
@@ -748,3 +798,12 @@ fn bytes_as_nat(bytes: &[u8]) -> Option<usize> {
Some(result.shl_exact(8)? | (byte as usize)) Some(result.shl_exact(8)? | (byte as usize))
}) })
} }
// or 0 if no mark is before
fn mark_before(offset: usize, sorted_marks: &[usize]) -> usize {
match sorted_marks.binary_search(&offset) {
Ok(_) => offset,
Err(0) => 0,
Err(mark_after_index) => sorted_marks[mark_after_index - 1],
}
}
+4
View File
@@ -14,6 +14,8 @@ pub struct App {
pub window_size: WindowSize, pub window_size: WindowSize,
pub should_quit: bool, pub should_quit: bool,
pub logs: Vec<String>,
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@@ -53,6 +55,8 @@ impl App {
window_size, window_size,
should_quit: false, should_quit: false,
logs: Vec::new(),
} }
} }
+5 -6
View File
@@ -86,10 +86,7 @@ impl From<KeyEvent> for Keypress {
fn from(event: KeyEvent) -> Self { fn from(event: KeyEvent) -> Self {
Self { Self {
code: event.code, code: event.code,
modifiers: match event.modifiers { modifiers: event.modifiers.difference(KeyModifiers::SHIFT),
KeyModifiers::SHIFT => KeyModifiers::NONE,
x => x,
},
} }
} }
} }
@@ -162,7 +159,8 @@ impl Default for Config {
("8".try_into().unwrap(), Action::SplitSelectionsInto8s), ("8".try_into().unwrap(), Action::SplitSelectionsInto8s),
("9".try_into().unwrap(), Action::SplitSelectionsInto9s), ("9".try_into().unwrap(), Action::SplitSelectionsInto9s),
("J".try_into().unwrap(), Action::JumpToSelectedOffset), ("J".try_into().unwrap(), Action::JumpToSelectedOffsetRelativeToMark),
("A-J".try_into().unwrap(), Action::JumpToSelectedOffset),
("m".try_into().unwrap(), Action::ToggleMark), ("m".try_into().unwrap(), Action::ToggleMark),
].into()), ].into()),
@@ -240,7 +238,8 @@ impl Default for Config {
("8".try_into().unwrap(), Action::SplitSelectionsInto8s), ("8".try_into().unwrap(), Action::SplitSelectionsInto8s),
("9".try_into().unwrap(), Action::SplitSelectionsInto9s), ("9".try_into().unwrap(), Action::SplitSelectionsInto9s),
("J".try_into().unwrap(), Action::JumpToSelectedOffset), ("J".try_into().unwrap(), Action::JumpToSelectedOffsetRelativeToMark),
("A-J".try_into().unwrap(), Action::JumpToSelectedOffset),
("m".try_into().unwrap(), Action::ToggleMark), ("m".try_into().unwrap(), Action::ToggleMark),
].into()), ].into()),
+8 -4
View File
@@ -26,10 +26,8 @@ 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:
// - zz/zt/zb
// - resizing can move the cursor off the screen // - resizing can move the cursor off the screen
// - constant for 5 lines of padding // - tab bar overflow
// - pad in clamp function, always
// - search // - search
// - s/A-k/A-K // - s/A-k/A-K
// - C-a/C-x // - C-a/C-x
@@ -49,11 +47,13 @@ const BYTES_OF_PADDING: usize = LINES_OF_PADDING * BYTES_PER_LINE;
// - [/] to cycle view offset? // - [/] to cycle view offset?
// - gj jump to entered offset // - gj jump to entered offset
// - repeat X times (dec and hex) // - repeat X times (dec and hex)
// - jump relative to last marker? // - 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)
// - how to fit??! `-128` longer than `80` // - how to fit??! `-128` longer than `80`
// - popup for different readings for the selected bytes
// - utf8? // - utf8?
// - diffing // - diffing
@@ -79,6 +79,10 @@ fn main() {
// dbg!(app.edit_history); // dbg!(app.edit_history);
for log in app.logs {
println!("{log}");
}
for log in app.buffers.iter().flat_map(|buffer| &buffer.logs) { for log in app.buffers.iter().flat_map(|buffer| &buffer.logs) {
println!("{log}"); println!("{log}");
} }