@@ -4,7 +4,7 @@ use egui::{Id, Layout, RichText, ScrollArea, TextEdit, Ui, Widget, text::LayoutJ
44use objdiff_core:: {
55 build:: BuildStatus ,
66 diff:: {
7- DiffObjConfig , ObjectDiff , SymbolDiff ,
7+ DataDiffKind , DiffObjConfig , InstructionDiffKind , ObjectDiff , SymbolDiff ,
88 data:: BYTES_PER_ROW ,
99 display:: {
1010 ContextItem , DiffText , HoverItem , HoverItemColor , SymbolFilter , SymbolNavigationKind ,
@@ -185,6 +185,9 @@ pub fn diff_view_ui(
185185 let available_width = ui. available_width ( ) ;
186186 let mut open_sections = ( None , None ) ;
187187
188+ let mut scroll_to_prev_diff = false ;
189+ let mut scroll_to_next_diff = false ;
190+
188191 render_header ( ui, available_width, 2 , |ui, column| {
189192 if column == 0 {
190193 // Left column
@@ -450,6 +453,31 @@ pub fn diff_view_ui(
450453 {
451454 ret = Some ( DiffViewAction :: SelectingRight ( symbol_ref. clone ( ) ) ) ;
452455 }
456+ needs_separator = true ;
457+ }
458+
459+ if state. current_view == View :: FunctionDiff
460+ || state. current_view == View :: DataDiff
461+ {
462+ if needs_separator {
463+ ui. separator ( ) ;
464+ }
465+ if ui
466+ . button ( "⏴ Prev diff" )
467+ . on_hover_text_at_pointer ( "Scroll to the previous difference (Ctrl+Up)" )
468+ . clicked ( )
469+ || hotkeys:: consume_prev_diff_shortcut ( ui. ctx ( ) )
470+ {
471+ scroll_to_prev_diff = true ;
472+ }
473+ if ui
474+ . button ( "Next diff ⏵" )
475+ . on_hover_text_at_pointer ( "Scroll to the next difference (Ctrl+Down)" )
476+ . clicked ( )
477+ || hotkeys:: consume_next_diff_shortcut ( ui. ctx ( ) )
478+ {
479+ scroll_to_next_diff = true ;
480+ }
453481 }
454482 } else if right_ctx. status . success && !right_ctx. has_symbol ( ) {
455483 let mut search = state. search . clone ( ) ;
@@ -493,14 +521,21 @@ pub fn diff_view_ui(
493521 return ;
494522 }
495523 let instructions_len = left_symbol_diff. instruction_rows . len ( ) ;
524+ let mut min_row = None ;
525+ let mut max_row = None ;
496526 render_table (
497527 ui,
498528 available_width,
499529 2 ,
500530 appearance. code_font . size ,
501531 instructions_len,
502- state. function_state . scroll_to_row ,
532+ state. scroll_to_diff_row ,
503533 |row, column| {
534+ if min_row. is_none ( ) {
535+ min_row = Some ( row. index ( ) ) ;
536+ }
537+ max_row = Some ( row. index ( ) ) ;
538+
504539 if column == 0 {
505540 if let Some ( action) = asm_col_ui (
506541 row,
@@ -537,6 +572,27 @@ pub fn diff_view_ui(
537572 }
538573 } ,
539574 ) ;
575+
576+ if scroll_to_prev_diff && let Some ( min_row) = min_row {
577+ for ( ins_idx, ins_diff) in
578+ right_diff. symbols [ right_symbol_idx] . instruction_rows . iter ( ) . enumerate ( ) . rev ( )
579+ {
580+ if ins_idx <= min_row && ins_diff. kind != InstructionDiffKind :: None {
581+ ret = Some ( DiffViewAction :: ScrollToRow ( ins_idx) ) ;
582+ break ;
583+ }
584+ }
585+ }
586+ if scroll_to_next_diff && let Some ( max_row) = max_row {
587+ for ( ins_idx, ins_diff) in
588+ right_diff. symbols [ right_symbol_idx] . instruction_rows . iter ( ) . enumerate ( )
589+ {
590+ if ins_idx >= max_row - 1 && ins_diff. kind != InstructionDiffKind :: None {
591+ ret = Some ( DiffViewAction :: ScrollToRow ( ins_idx) ) ;
592+ break ;
593+ }
594+ }
595+ }
540596 } else if let (
541597 View :: DataDiff ,
542598 Some ( ( left_obj, _left_diff) ) ,
@@ -556,14 +612,21 @@ pub fn diff_view_ui(
556612 if total_rows == 0 {
557613 return ;
558614 }
615+ let mut min_row = None ;
616+ let mut max_row = None ;
559617 render_table (
560618 ui,
561619 available_width,
562620 2 ,
563621 appearance. code_font . size ,
564622 total_rows,
565- None ,
623+ state . scroll_to_diff_row ,
566624 |row, column| {
625+ if min_row. is_none ( ) {
626+ min_row = Some ( row. index ( ) ) ;
627+ }
628+ max_row = Some ( row. index ( ) ) ;
629+
567630 let i = row. index ( ) ;
568631 let row_offset = i as u64 * BYTES_PER_ROW as u64 ;
569632 row. col ( |ui| {
@@ -591,6 +654,29 @@ pub fn diff_view_ui(
591654 } ) ;
592655 } ,
593656 ) ;
657+
658+ if scroll_to_prev_diff && let Some ( min_row) = min_row {
659+ for ( row_idx, diff_row) in right_symbol_diff. data_rows . iter ( ) . enumerate ( ) . rev ( ) {
660+ if row_idx <= min_row
661+ && ( diff_row. segments . iter ( ) . any ( |dd| dd. kind != DataDiffKind :: None )
662+ || diff_row. relocations . iter ( ) . any ( |rd| rd. kind != DataDiffKind :: None ) )
663+ {
664+ ret = Some ( DiffViewAction :: ScrollToRow ( row_idx) ) ;
665+ break ;
666+ }
667+ }
668+ }
669+ if scroll_to_next_diff && let Some ( max_row) = max_row {
670+ for ( row_idx, diff_row) in right_symbol_diff. data_rows . iter ( ) . enumerate ( ) {
671+ if row_idx >= max_row - 1
672+ && ( diff_row. segments . iter ( ) . any ( |dd| dd. kind != DataDiffKind :: None )
673+ || diff_row. relocations . iter ( ) . any ( |rd| rd. kind != DataDiffKind :: None ) )
674+ {
675+ ret = Some ( DiffViewAction :: ScrollToRow ( row_idx) ) ;
676+ break ;
677+ }
678+ }
679+ }
594680 } else {
595681 // Split view
596682 render_strips ( ui, available_width, 2 , |ui, column| {
@@ -693,7 +779,7 @@ fn diff_col_ui(
693779 1 ,
694780 appearance. code_font . size ,
695781 total_rows,
696- None ,
782+ state . scroll_to_diff_row ,
697783 |row, _column| {
698784 let i = row. index ( ) ;
699785 let row_offset = i as u64 * BYTES_PER_ROW as u64 ;
@@ -717,7 +803,7 @@ fn diff_col_ui(
717803 1 ,
718804 appearance. code_font . size ,
719805 symbol_diff. instruction_rows . len ( ) ,
720- state. function_state . scroll_to_row ,
806+ state. scroll_to_diff_row ,
721807 |row, column| {
722808 if let Some ( action) = asm_col_ui (
723809 row,
0 commit comments