From 2c970250090fbe742d33862acb3e37447e6e6999 Mon Sep 17 00:00:00 2001 From: LagoLunatic Date: Tue, 30 Dec 2025 17:34:40 -0500 Subject: [PATCH] Function diff: Implement "Go to line number" --- objdiff-gui/src/hotkeys.rs | 6 +++ objdiff-gui/src/views/diff.rs | 55 ++++++++++++++++++++++++++ objdiff-gui/src/views/function_diff.rs | 2 + objdiff-gui/src/views/symbol_diff.rs | 9 +++++ 4 files changed, 72 insertions(+) diff --git a/objdiff-gui/src/hotkeys.rs b/objdiff-gui/src/hotkeys.rs index 80d8d5dc..b2a192fc 100644 --- a/objdiff-gui/src/hotkeys.rs +++ b/objdiff-gui/src/hotkeys.rs @@ -106,3 +106,9 @@ const CHANGE_BASE_SHORTCUT: KeyboardShortcut = KeyboardShortcut::new(Modifiers:: pub fn consume_change_base_shortcut(ctx: &Context) -> bool { ctx.input_mut(|i| i.consume_shortcut(&CHANGE_BASE_SHORTCUT)) } + +const GO_TO_SHORTCUT: KeyboardShortcut = KeyboardShortcut::new(Modifiers::CTRL, Key::G); + +pub fn consume_go_to_shortcut(ctx: &Context) -> bool { + ctx.input_mut(|i| i.consume_shortcut(&GO_TO_SHORTCUT)) +} diff --git a/objdiff-gui/src/views/diff.rs b/objdiff-gui/src/views/diff.rs index f7c4a678..2322ecc8 100644 --- a/objdiff-gui/src/views/diff.rs +++ b/objdiff-gui/src/views/diff.rs @@ -116,6 +116,28 @@ fn get_asm_text( asm_text } +fn try_scroll_to_line_number( + scroll_to_line_number: Option, + obj: &Object, + diff: &ObjectDiff, + symbol_idx: usize, +) -> Option { + let target_line = scroll_to_line_number?; + let symbol = obj.symbols.get(symbol_idx)?; + let section_index = symbol.section?; + let section = &obj.sections[section_index]; + for (ins_idx, ins_row) in diff.symbols[symbol_idx].instruction_rows.iter().enumerate() { + if let Some(ins_ref) = ins_row.ins_ref + && let Some(current_line) = + section.line_info.range(..=ins_ref.address).last().map(|(_, &b)| b) + && current_line == target_line + { + return Some(DiffViewAction::ScrollToRow(ins_idx)); + } + } + None +} + #[must_use] pub fn diff_view_ui( ui: &mut Ui, @@ -450,6 +472,23 @@ pub fn diff_view_ui( { ret = Some(DiffViewAction::SelectingRight(symbol_ref.clone())); } + needs_separator = true; + } + if state.current_view == View::FunctionDiff { + if needs_separator { + ui.separator(); + } + let mut goto_line_text = state.function_state.go_to_line_text.clone(); + let response = TextEdit::singleline(&mut goto_line_text) + .hint_text("Go to line number") + .desired_width(100.0) + .ui(ui); + if hotkeys::consume_go_to_shortcut(ui.ctx()) { + response.request_focus(); + } + if response.changed() { + ret = Some(DiffViewAction::SetGoToText(goto_line_text)); + } } } else if right_ctx.status.success && !right_ctx.has_symbol() { let mut search = state.search.clone(); @@ -492,6 +531,14 @@ pub fn diff_view_ui( ui.label("Instruction count mismatch"); return; } + if let Some(action) = try_scroll_to_line_number( + state.function_state.scroll_to_line_number, + right_obj, + right_diff, + right_symbol_idx, + ) { + ret = Some(action); + } let instructions_len = left_symbol_diff.instruction_rows.len(); render_table( ui, @@ -711,6 +758,14 @@ fn diff_col_ui( }, ); } else { + if let Some(action) = try_scroll_to_line_number( + state.function_state.scroll_to_line_number, + obj, + diff, + symbol_idx, + ) { + ret = Some(action); + } render_table( ui, available_width / 2.0, diff --git a/objdiff-gui/src/views/function_diff.rs b/objdiff-gui/src/views/function_diff.rs index 2ee8a147..2507b1c9 100644 --- a/objdiff-gui/src/views/function_diff.rs +++ b/objdiff-gui/src/views/function_diff.rs @@ -25,6 +25,8 @@ pub struct FunctionViewState { left_highlight: HighlightKind, right_highlight: HighlightKind, pub scroll_to_row: Option, + pub scroll_to_line_number: Option, + pub go_to_line_text: String, } impl FunctionViewState { diff --git a/objdiff-gui/src/views/symbol_diff.rs b/objdiff-gui/src/views/symbol_diff.rs index bb40f21b..606870dc 100644 --- a/objdiff-gui/src/views/symbol_diff.rs +++ b/objdiff-gui/src/views/symbol_diff.rs @@ -83,6 +83,8 @@ pub enum DiffViewAction { SetShowDataFlow(bool), // Scrolls a row of the function view table into view. ScrollToRow(usize), + /// Sets the text of the line number jump field and try to scroll that line into view. + SetGoToText(String), } #[derive(Debug, Clone, Default, Eq, PartialEq)] @@ -198,6 +200,7 @@ impl DiffViewState { // 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; + self.function_state.scroll_to_line_number = None; let Some(action) = action else { return; @@ -368,6 +371,12 @@ impl DiffViewState { DiffViewAction::ScrollToRow(row) => { self.function_state.scroll_to_row = Some(row); } + DiffViewAction::SetGoToText(text) => { + if let Ok(line_num) = text.trim().parse::() { + self.function_state.scroll_to_line_number = Some(line_num); + } + self.function_state.go_to_line_text = text; + } } }