jump relative to mark
This commit is contained in:
@@ -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],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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}");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user