Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Allow clicking on branch arrows to scroll target into view
  • Loading branch information
LagoLunatic committed Dec 18, 2025
commit f139e7425d1b20c077516d7988395ae1b23d73db
1 change: 1 addition & 0 deletions objdiff-cli/src/views/function_diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,7 @@ impl FunctionDiffUi {
DiffText::Opcode(mnemonic, _op) => format!("{mnemonic} "),
DiffText::Argument(arg) => arg.to_string(),
DiffText::BranchDest(addr) => format!("{addr:x}"),
DiffText::BranchArrow(_) => " ~> ".to_string(),
DiffText::Symbol(sym) => {
sym.demangled_name.as_ref().unwrap_or(&sym.name).clone()
}
Expand Down
16 changes: 14 additions & 2 deletions objdiff-core/src/diff/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ pub enum DiffText<'a> {
Argument(InstructionArgValue<'a>),
/// Branch destination
BranchDest(u64),
/// Branch source/destination arrow, scrolls to a specific row when clicked
BranchArrow(u32),
/// Symbol name
Symbol(&'a Symbol),
/// Relocation addend
Expand Down Expand Up @@ -185,7 +187,13 @@ pub fn display_row(
pad_to: 5,
})?;
if let Some(branch) = &ins_row.branch_from {
cb(DiffTextSegment::basic(" ~> ", DiffTextColor::Rotating(branch.branch_idx as u8)))?;
// Pick the first branch source if there are multiple.
let ins_idx = branch.ins_idx[0];
cb(DiffTextSegment {
text: DiffText::BranchArrow(ins_idx),
color: DiffTextColor::Rotating(branch.branch_idx as u8),
pad_to: 0,
})?;
} else {
cb(DiffTextSegment::spacing(4))?;
}
Expand Down Expand Up @@ -307,7 +315,11 @@ pub fn display_row(
cb(DiffTextSegment::basic(">", base_color))?;
}
if let Some(branch) = &ins_row.branch_to {
cb(DiffTextSegment::basic(" ~>", DiffTextColor::Rotating(branch.branch_idx as u8)))?;
cb(DiffTextSegment {
text: DiffText::BranchArrow(branch.ins_idx),
color: DiffTextColor::Rotating(branch.branch_idx as u8),
pad_to: 0,
})?;
}
cb(EOL_SEGMENT)?;
Ok(())
Expand Down
6 changes: 5 additions & 1 deletion objdiff-gui/src/views/column_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,23 @@ pub fn render_table(
num_columns: usize,
row_height: f32,
total_rows: usize,
scroll_to_row: Option<usize>,
mut add_contents: impl FnMut(&mut TableRow, usize),
) {
ui.style_mut().interaction.selectable_labels = false;
let column_width = available_width / num_columns as f32;
let available_height = ui.available_height();
let table = TableBuilder::new(ui)
let mut table = TableBuilder::new(ui)
.striped(false)
.cell_layout(Layout::left_to_right(Align::Min))
.columns(Column::exact(column_width).clip(true), num_columns)
.resizable(false)
.auto_shrink([false, false])
.min_scrolled_height(available_height)
.sense(Sense::click());
if let Some(row) = scroll_to_row {
table = table.scroll_to_row(row, Some(Align::Center));
}
table.body(|body| {
body.rows(row_height, total_rows, |mut row| {
row.set_hovered(false); // Disable hover effect
Expand Down
5 changes: 5 additions & 0 deletions objdiff-gui/src/views/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ fn get_asm_text(
InstructionArgValue::Opaque(v) => v.into_owned(),
},
DiffText::BranchDest(addr) => format!("{addr:x}"),
DiffText::BranchArrow(_) => " ~> ".to_string(),
DiffText::Symbol(sym) => sym.demangled_name.as_ref().unwrap_or(&sym.name).clone(),
DiffText::Addend(addend) => match addend.cmp(&0i64) {
Ordering::Greater => format!("+{addend:#x}"),
Expand Down Expand Up @@ -498,6 +499,7 @@ pub fn diff_view_ui(
2,
appearance.code_font.size,
instructions_len,
state.function_state.scroll_to_row,
|row, column| {
if column == 0 {
if let Some(action) = asm_col_ui(
Expand Down Expand Up @@ -560,6 +562,7 @@ pub fn diff_view_ui(
2,
appearance.code_font.size,
total_rows,
None,
|row, column| {
let i = row.index();
let row_offset = i as u64 * BYTES_PER_ROW as u64;
Expand Down Expand Up @@ -690,6 +693,7 @@ fn diff_col_ui(
1,
appearance.code_font.size,
total_rows,
None,
|row, _column| {
let i = row.index();
let row_offset = i as u64 * BYTES_PER_ROW as u64;
Expand All @@ -713,6 +717,7 @@ fn diff_col_ui(
1,
appearance.code_font.size,
symbol_diff.instruction_rows.len(),
state.function_state.scroll_to_row,
|row, column| {
if let Some(action) = asm_col_ui(
row,
Expand Down
12 changes: 11 additions & 1 deletion objdiff-gui/src/views/function_diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use crate::views::{
pub struct FunctionViewState {
left_highlight: HighlightKind,
right_highlight: HighlightKind,
pub scroll_to_row: Option<usize>,
}

impl FunctionViewState {
Expand Down Expand Up @@ -143,6 +144,7 @@ fn diff_text_ui(
response_cb: impl Fn(Response) -> Response,
) -> Option<DiffViewAction> {
let highlight_kind = HighlightKind::from(&segment.text);
let mut branch_to_ins_idx = None;
let label_text = match segment.text {
DiffText::Basic(text) => text.to_string(),
DiffText::Line(num) => format!("{num} "),
Expand All @@ -154,6 +156,10 @@ fn diff_text_ui(
InstructionArgValue::Opaque(v) => v.into_owned(),
},
DiffText::BranchDest(addr) => format!("{addr:x}"),
DiffText::BranchArrow(ins_idx) => {
branch_to_ins_idx = Some(ins_idx);
" ~> ".to_string()
}
DiffText::Symbol(sym) => sym.demangled_name.as_ref().unwrap_or(&sym.name).clone(),
DiffText::Addend(addend) => match addend.cmp(&0i64) {
Ordering::Greater => format!("+{addend:#x}"),
Expand Down Expand Up @@ -191,7 +197,11 @@ fn diff_text_ui(
response = response_cb(response);
let mut ret = None;
if response.clicked() {
ret = Some(DiffViewAction::SetDiffHighlight(column, highlight_kind));
if let Some(ins_idx) = branch_to_ins_idx {
ret = Some(DiffViewAction::ScrollToRow(ins_idx as usize));
} else {
ret = Some(DiffViewAction::SetDiffHighlight(column, highlight_kind));
}
}
if len < segment.pad_to as usize {
ui.add_space((segment.pad_to as usize - len) as f32 * space_width);
Expand Down
8 changes: 7 additions & 1 deletion objdiff-gui/src/views/symbol_diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ pub enum DiffViewAction {
SetShowMappedSymbols(bool),
/// Set the show_data_flow flag
SetShowDataFlow(bool),
// Scrolls a row of the function view table into view.
ScrollToRow(usize),
}

#[derive(Debug, Clone, Default, Eq, PartialEq)]
Expand Down Expand Up @@ -193,8 +195,9 @@ impl DiffViewState {
ctx.open_url(OpenUrl::new_tab(result.scratch_url));
}

// Clear the autoscroll flag so that it doesn't scroll continuously.
// Clear the scroll flags to prevent it from scrolling continuously.
self.symbol_state.autoscroll_to_highlighted_symbols = false;
self.function_state.scroll_to_row = None;

let Some(action) = action else {
return;
Expand Down Expand Up @@ -362,6 +365,9 @@ impl DiffViewState {
};
state.config.diff_obj_config.show_data_flow = value;
}
DiffViewAction::ScrollToRow(row) => {
self.function_state.scroll_to_row = Some(row);
}
}
}

Expand Down