Skip to content

Commit

Permalink
add hexdump and summary label cursors and better selection rendering …
Browse files Browse the repository at this point in the history
…for summaries
  • Loading branch information
misson20000 committed Sep 25, 2024
1 parent ae17064 commit 998e4b8
Show file tree
Hide file tree
Showing 10 changed files with 412 additions and 27 deletions.
9 changes: 9 additions & 0 deletions src/model/listing/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ pub mod key;

pub mod title;
pub mod hexdump;
pub mod hexstring;
pub mod punctuation;
pub mod summary_label;

#[derive(Debug)]
pub enum MovementResult {
Expand Down Expand Up @@ -80,7 +82,9 @@ pub trait CursorClassExt {
pub enum CursorClass {
Title(title::Cursor),
Hexdump(hexdump::Cursor),
Hexstring(hexstring::Cursor),
Punctuation(punctuation::Cursor),
SummaryLabel(summary_label::Cursor),
}

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -488,8 +492,10 @@ impl CursorClass {
match token {
token::Token::Title(token) => title::Cursor::new_placement(token, hint).map(CursorClass::Title).map_err(TokenKind::into_token),
token::Token::Hexdump(token) => hexdump::Cursor::new_placement(token, offset, hint).map(CursorClass::Hexdump).map_err(TokenKind::into_token),
token::Token::Hexstring(token) => hexstring::Cursor::new_placement(token, offset, hint).map(CursorClass::Hexstring).map_err(TokenKind::into_token),
token::Token::SummaryPunctuation(token) if token.kind.accepts_cursor() => punctuation::Cursor::new_placement(token.into_token(), hint).map(CursorClass::Punctuation),
token::Token::BlankLine(token) if token.accepts_cursor => punctuation::Cursor::new_placement(token.into_token(), hint).map(CursorClass::Punctuation),
token::Token::SummaryLabel(token) => summary_label::Cursor::new_placement(token, hint).map(CursorClass::SummaryLabel).map_err(TokenKind::into_token),
_ => Err(token)
}
}
Expand All @@ -500,8 +506,10 @@ impl CursorClass {
match token {
token::Token::Title(token) => title::Cursor::new_transition(token, hint).map(CursorClass::Title).map_err(TokenKind::into_token),
token::Token::Hexdump(token) => hexdump::Cursor::new_transition(token, hint).map(CursorClass::Hexdump).map_err(TokenKind::into_token),
token::Token::Hexstring(token) => hexstring::Cursor::new_transition(token, hint).map(CursorClass::Hexstring).map_err(TokenKind::into_token),
token::Token::SummaryPunctuation(token) if token.kind.accepts_cursor() => punctuation::Cursor::new_transition(token.into_token(), hint).map(CursorClass::Punctuation),
token::Token::BlankLine(token) if token.accepts_cursor => punctuation::Cursor::new_transition(token.into_token(), hint).map(CursorClass::Punctuation),
token::Token::SummaryLabel(token) => summary_label::Cursor::new_transition(token, hint).map(CursorClass::SummaryLabel).map_err(TokenKind::into_token),
_ => Err(token)
}
}
Expand Down Expand Up @@ -554,6 +562,7 @@ impl CursorClass {
#[derive(Debug, Clone)]
pub enum PlacementHint {
Hexdump(hexdump::HexdumpPlacementHint),
Hexstring(hexstring::HexstringPlacementHint),
Title,
Punctuation,
Unused,
Expand Down
1 change: 1 addition & 0 deletions src/model/listing/cursor/hexdump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ impl Cursor {
},
low_nybble: match &hint {
cursor::PlacementHint::Hexdump(hph) => hph.low_nybble,
cursor::PlacementHint::Hexstring(hph) => hph.low_nybble,
_ => false,
},

Expand Down
248 changes: 248 additions & 0 deletions src/model/listing/cursor/hexstring.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
use std::task;
use std::vec;

use crate::model::addr;
use crate::model::datapath;
use crate::model::datapath::DataPathExt;
use crate::model::document;
use crate::model::listing::cursor;
use crate::model::listing::line;
use crate::model::listing::token;
use crate::model::listing::token::TokenKind;

#[derive(Debug)]
pub struct Cursor {
pub token: token::HexstringToken,
pub offset: addr::Size,
pub low_nybble: bool,

data_cache: vec::Vec<datapath::ByteRecord>,
data_pending: bool,
}

impl Cursor {
pub fn new_transition(token: token::HexstringToken, hint: &cursor::TransitionHint) -> Result<Cursor, token::Token> {
let extent = token.extent;
let limit = (extent.length() - addr::unit::BIT).floor();

let (offset, low_nybble) = match hint {
cursor::TransitionHint::MoveLeftLarge => (addr::Size::from(limit.bytes & !7), false),

/*
cursor::TransitionHint::MoveVertical {
horizontal_position: cursor::HorizontalPosition::Hexdump(offset_in_line, low_nybble),
line,
..
} => {
let line_extent = match line.ty {
line::LineType::Hexdump { line_extent, .. } => line_extent,
_ => return Err(token::Token::Hexdump(token)),
};
let offset = line_extent.begin + *offset_in_line;
if offset >= extent.end {
return Err(token::Token::Hexdump(token));
} else if offset < extent.begin {
/* the first line after a child can have a first token that
* starts in the middle of the line, and the horizontal
* position can point before that token starts. */
(addr::unit::ZERO, false)
} else {
(offset - extent.begin, *low_nybble)
}
},
*/

op if op.is_left() => (limit, true),
op if op.is_right() => (addr::unit::ZERO, false),
_ => (addr::unit::ZERO, false)
};

Ok(Cursor {
token,
offset,
low_nybble,

data_cache: vec::Vec::new(),
data_pending: true,
})
}

pub fn new_placement(token: token::HexstringToken, offset: addr::Address, hint: &cursor::PlacementHint) -> Result<Cursor, token::Token> {
let extent = token.extent;
let limit = (extent.length() - addr::unit::BIT).floor();

Ok(Cursor {
token,
offset: match offset {
offset if offset < extent.begin => addr::unit::ZERO,
offset if offset >= extent.begin + limit => limit,
offset => offset - extent.begin,
},
low_nybble: match &hint {
cursor::PlacementHint::Hexdump(hph) => hph.low_nybble,
cursor::PlacementHint::Hexstring(hph) => hph.low_nybble,
_ => false,
},

data_cache: vec::Vec::new(),
data_pending: true,
})
}

pub fn extent(&self) -> addr::Extent {
self.token.extent
}
}

impl cursor::CursorClassExt for Cursor {
fn is_over(&self, token: token::TokenRef<'_>) -> bool {
self.token.as_ref() == token
}

fn get_addr(&self) -> addr::Address {
self.token.absolute_extent().begin + self.offset
}

fn get_offset(&self) -> addr::Size {
self.offset
}

fn get_token(&self) -> token::TokenRef<'_> {
self.token.as_ref()
}

fn get_placement_hint(&self) -> cursor::PlacementHint {
cursor::PlacementHint::Hexstring(HexstringPlacementHint {
low_nybble: self.low_nybble
})
}

fn get_horizontal_position_in_line(&self, _line: &line::Line) -> cursor::HorizontalPosition {
cursor::HorizontalPosition::Unspecified
}

fn move_left(&mut self) -> cursor::MovementResult {
if self.low_nybble {
self.low_nybble = false;
cursor::MovementResult::Ok
} else if self.offset >= addr::unit::BYTE {
self.offset-= addr::unit::BYTE;
self.low_nybble = true;
cursor::MovementResult::Ok
} else {
cursor::MovementResult::HitStart
}
}

fn move_right(&mut self) -> cursor::MovementResult {
if self.low_nybble {
let offset = self.offset + addr::unit::BYTE;
if offset >= self.extent().length() {
cursor::MovementResult::HitEnd
} else {
self.offset = offset;
self.low_nybble = false;
cursor::MovementResult::Ok
}
} else {
self.low_nybble = true;
cursor::MovementResult::Ok
}
}

fn move_left_large(&mut self) -> cursor::MovementResult {
if self.offset == addr::unit::ZERO && !self.low_nybble {
cursor::MovementResult::HitStart
} else if self.low_nybble {
self.offset = addr::Size::from(self.offset.bytes & !7);
self.low_nybble = false;
cursor::MovementResult::Ok
} else {
self.offset-= addr::unit::BIT;
self.offset = addr::Size::from(self.offset.bytes & !7);
cursor::MovementResult::Ok
}
}

fn move_right_large(&mut self) -> cursor::MovementResult {
let offset = addr::Size::from(self.offset.bytes & !7);
let length = self.extent().length();

if offset + addr::unit::QWORD >= length {
cursor::MovementResult::HitEnd
} else {
self.low_nybble = false;
self.offset = offset + addr::unit::QWORD;
cursor::MovementResult::Ok
}
}

fn enter_hex(&mut self, document_host: &document::DocumentHost, document: &document::Document, nybble: u8) -> Result<cursor::MovementResult, cursor::EntryError> {
let i = self.offset.bytes as usize;
let loc = self.token.absolute_extent().begin.byte + self.offset.bytes;
let shift = self.token.absolute_extent().begin.bit;
let insert = false; // TODO

/*
* +-----------------+-----------------+
* | 0 1 2 3 4 5 6 7 | 0 1 2 3 4 5 6 7 |
* +-----------------+-----------------+
*
*/

let br = self.data_cache[i];
if br.pending {
return Err(cursor::EntryError::DataPending);
}
let raw = br.value;

let change = if self.low_nybble && shift <= 4 {
let mask = 0xF << shift;

document.patch_byte(loc, (raw & !mask) | (nybble << shift))
} else if !self.low_nybble && shift == 0 {
let mask = 0xF << 4;

if insert {
document.insert_byte(loc, (raw & !mask) | (nybble << 4))
} else {
document.patch_byte(loc, (raw & !mask) | (nybble << 4))
}
} else {
todo!(); // TODO
};

document_host.change(change)?;

Ok(self.move_right())
}

fn invalidate_data(&mut self) {
self.data_cache.clear();
self.data_pending = true;
}

fn work(&mut self, document: &document::Document, cx: &mut task::Context) -> bool {
if self.data_pending {
let (begin_byte, size) = self.token.absolute_extent().round_out();

self.data_cache.resize(size as usize, datapath::ByteRecord::default());
document.datapath.fetch(datapath::ByteRecordRange::new(begin_byte, &mut self.data_cache), cx);

self.data_pending = self.data_cache.iter().any(|b| b.pending);
}

self.data_pending
}
}

#[derive(Debug, Clone)]
pub struct HexstringPlacementHint {
pub low_nybble: bool,
}

#[derive(Debug, Clone)]
pub struct HexstringTransitionHint {
offset: addr::Size,
low_nybble: bool,
}
83 changes: 83 additions & 0 deletions src/model/listing/cursor/summary_label.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use crate::model::addr;
use crate::model::listing::cursor;
use crate::model::listing::line;
use crate::model::listing::token;
use crate::model::listing::token::TokenKind;

use tracing::instrument;

#[derive(Debug)]
pub struct Cursor {
token: token::SummaryLabelToken,
}

impl Cursor {
pub fn new_transition(token: token::SummaryLabelToken, hint: &cursor::TransitionHint) -> Result<Cursor, token::SummaryLabelToken> {
if match hint {
hint if hint.is_entry() => false,
cursor::TransitionHint::MoveVertical { horizontal_position: cursor::HorizontalPosition::Title, .. } => true,
cursor::TransitionHint::MoveVertical { horizontal_position: cursor::HorizontalPosition::Unspecified, .. } => true,
cursor::TransitionHint::MoveVertical { .. } => false,
_ => true,
} {
Ok(Cursor { token })
} else {
Err(token)
}
}

pub fn new_placement(token: token::SummaryLabelToken, hint: &cursor::PlacementHint) -> Result<Cursor, token::SummaryLabelToken> {
match hint {
/* we only place the cursor on a summary label if explicitly requested or this is a last-ditch effort;
* otherwise, we prefer to place it on a content token. */
cursor::PlacementHint::Title | cursor::PlacementHint::LastDitch => Ok(Cursor {
token
}),
_ => Err(token)
}
}
}

impl cursor::CursorClassExt for Cursor {
fn is_over(&self, token: token::TokenRef<'_>) -> bool {
self.token.as_ref() == token
}

fn get_addr(&self) -> addr::Address {
self.token.node_addr()
}

fn get_offset(&self) -> addr::Size {
addr::unit::ZERO
}

fn get_token(&self) -> token::TokenRef<'_> {
self.token.as_ref()
}

fn get_placement_hint(&self) -> cursor::PlacementHint {
cursor::PlacementHint::Title
}

fn get_horizontal_position_in_line(&self, _line: &line::Line) -> cursor::HorizontalPosition {
cursor::HorizontalPosition::Title
}

#[instrument]
fn move_left(&mut self) -> cursor::MovementResult {
cursor::MovementResult::HitStart
}

#[instrument]
fn move_right(&mut self) -> cursor::MovementResult {
cursor::MovementResult::HitEnd
}

fn move_left_large(&mut self) -> cursor::MovementResult {
cursor::MovementResult::HitStart
}

fn move_right_large(&mut self) -> cursor::MovementResult {
cursor::MovementResult::HitEnd
}
}
2 changes: 1 addition & 1 deletion src/model/listing/cursor/title.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ impl Cursor {

pub fn new_placement(token: token::TitleToken, hint: &cursor::PlacementHint) -> Result<Cursor, token::TitleToken> {
match hint {
/* we only place the cursor on a break header if explicitly requested or this is a last-ditch effort;
/* we only place the cursor on a title if explicitly requested or this is a last-ditch effort;
* otherwise, we prefer to place it on a content token. */
cursor::PlacementHint::Title | cursor::PlacementHint::LastDitch => Ok(Cursor {
token
Expand Down
Loading

0 comments on commit 998e4b8

Please sign in to comment.