key events, address column
This commit is contained in:
+42
-13
@@ -1,5 +1,8 @@
|
|||||||
use std::{borrow::Cow, env, fs::File, io::Read, mem, process::exit};
|
#![warn(clippy::pedantic, clippy::nursery)]
|
||||||
use crossterm::event::{self, Event};
|
#![allow(clippy::cast_possible_truncation)]
|
||||||
|
|
||||||
|
use std::{borrow::Cow, cmp::min, env, fs::File, io::Read, mem, process::exit};
|
||||||
|
use crossterm::event::{self, Event, KeyCode, KeyModifiers};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use ratatui::{style::{Color, Style}, text::{Line, Span, Text}, widgets::Widget};
|
use ratatui::{style::{Color, Style}, text::{Line, Span, Text}, widgets::Widget};
|
||||||
|
|
||||||
@@ -9,7 +12,7 @@ fn main() {
|
|||||||
|
|
||||||
while !app.should_quit {
|
while !app.should_quit {
|
||||||
terminal.draw(|frame| {
|
terminal.draw(|frame| {
|
||||||
frame.render_widget(&app, frame.area())
|
frame.render_widget(&app, frame.area());
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
|
|
||||||
app.handle_events();
|
app.handle_events();
|
||||||
@@ -27,7 +30,7 @@ struct App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
fn init() -> App {
|
fn init() -> Self {
|
||||||
let input_files: Vec<_> = env::args().skip(1).collect();
|
let input_files: Vec<_> = env::args().skip(1).collect();
|
||||||
|
|
||||||
if input_files.is_empty() {
|
if input_files.is_empty() {
|
||||||
@@ -43,7 +46,7 @@ impl App {
|
|||||||
let mut contents = Vec::new();
|
let mut contents = Vec::new();
|
||||||
file.unwrap().read_to_end(&mut contents).unwrap();
|
file.unwrap().read_to_end(&mut contents).unwrap();
|
||||||
|
|
||||||
App {
|
Self {
|
||||||
contents,
|
contents,
|
||||||
scroll_position: 0,
|
scroll_position: 0,
|
||||||
// cursor_position: 0,
|
// cursor_position: 0,
|
||||||
@@ -52,8 +55,20 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_events(&mut self) {
|
fn handle_events(&mut self) {
|
||||||
if matches!(event::read().unwrap(), Event::Key(_)) {
|
match event::read().unwrap() {
|
||||||
self.should_quit = true;
|
Event::Key(key_event) if key_event.code == KeyCode::Char('q') => {
|
||||||
|
self.should_quit = true;
|
||||||
|
}
|
||||||
|
Event::Key(key_event) if key_event.code == KeyCode::Char('e') &&
|
||||||
|
key_event.modifiers.contains(KeyModifiers::CONTROL) => {
|
||||||
|
let max_scroll_position = self.contents.len() - 0x50;
|
||||||
|
self.scroll_position = min(self.scroll_position + BYTES_PER_LINE, max_scroll_position);
|
||||||
|
}
|
||||||
|
Event::Key(key_event) if key_event.code == KeyCode::Char('y') &&
|
||||||
|
key_event.modifiers.contains(KeyModifiers::CONTROL) => {
|
||||||
|
self.scroll_position = self.scroll_position.saturating_sub(BYTES_PER_LINE);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,8 +78,10 @@ const BYTES_PER_LINE: usize = 0x10;
|
|||||||
impl Widget for &App {
|
impl Widget for &App {
|
||||||
fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer) {
|
fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer) {
|
||||||
let screen_end = self.scroll_position + BYTES_PER_LINE * (area.height as usize);
|
let screen_end = self.scroll_position + BYTES_PER_LINE * (area.height as usize);
|
||||||
|
let bytes_end = min(screen_end, self.contents.len());
|
||||||
|
|
||||||
let bytes_to_render = &self.contents[self.scroll_position..screen_end];
|
// TODO: bounds check this
|
||||||
|
let bytes_to_render = &self.contents[self.scroll_position..bytes_end];
|
||||||
|
|
||||||
let (chunks, remainder) = bytes_to_render
|
let (chunks, remainder) = bytes_to_render
|
||||||
.as_chunks::<BYTES_PER_LINE>();
|
.as_chunks::<BYTES_PER_LINE>();
|
||||||
@@ -73,7 +90,8 @@ impl Widget for &App {
|
|||||||
|
|
||||||
let lines: Vec<_> = chunks
|
let lines: Vec<_> = chunks
|
||||||
.iter()
|
.iter()
|
||||||
.map(render_line)
|
.zip((self.scroll_position..).step_by(BYTES_PER_LINE))
|
||||||
|
.map(|(bytes, address)| render_line(address, bytes))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let text = Text::from(lines);
|
let text = Text::from(lines);
|
||||||
@@ -82,25 +100,36 @@ impl Widget for &App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_line(bytes: &[u8; BYTES_PER_LINE]) -> Line {
|
#[allow(mismatched_lifetime_syntaxes)]
|
||||||
|
fn render_line(address: usize, bytes: &[u8; BYTES_PER_LINE]) -> Line {
|
||||||
let (chunks, remainder) = bytes.as_chunks::<BYTES_PER_CHUNK>();
|
let (chunks, remainder) = bytes.as_chunks::<BYTES_PER_CHUNK>();
|
||||||
|
|
||||||
assert!(remainder.is_empty());
|
assert!(remainder.is_empty());
|
||||||
|
|
||||||
#[allow(unstable_name_collisions)]
|
#[allow(unstable_name_collisions)]
|
||||||
let spans: Vec<_> = chunks
|
let mut spans: Vec<Span<'_>> = chunks
|
||||||
.iter()
|
.iter()
|
||||||
|
.copied()
|
||||||
.map(render_chunk)
|
.map(render_chunk)
|
||||||
.intersperse(vec![" ".into()])
|
.intersperse(vec![" ".into()])
|
||||||
.flatten()
|
.flatten()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
spans.insert(0, render_address(address));
|
||||||
|
|
||||||
Line::from(spans)
|
Line::from(spans)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_address(address: usize) -> Span<'static> {
|
||||||
|
Span {
|
||||||
|
style: Style::new().fg(Color::Rgb(138, 187, 195)),
|
||||||
|
content: format!("{:08x} ", address).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const BYTES_PER_CHUNK: usize = 4;
|
const BYTES_PER_CHUNK: usize = 4;
|
||||||
|
|
||||||
fn render_chunk(bytes: &[u8; BYTES_PER_CHUNK]) -> Vec<Span<'static>> {
|
fn render_chunk(bytes: [u8; BYTES_PER_CHUNK]) -> Vec<Span<'static>> {
|
||||||
#[allow(unstable_name_collisions)]
|
#[allow(unstable_name_collisions)]
|
||||||
bytes
|
bytes
|
||||||
.iter()
|
.iter()
|
||||||
@@ -115,7 +144,7 @@ trait HasCardinality {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl HasCardinality for u8 {
|
impl HasCardinality for u8 {
|
||||||
const CARDINALITY: usize = 2usize.pow(u8::BITS);
|
const CARDINALITY: usize = 2usize.pow(Self::BITS);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn byte_as_span(byte: u8) -> Span<'static> {
|
fn byte_as_span(byte: u8) -> Span<'static> {
|
||||||
|
|||||||
Reference in New Issue
Block a user