|
| 1 | +use std::cmp::Ordering; |
| 2 | + |
1 | 3 | use egui::{Id, Layout, RichText, ScrollArea, TextEdit, Ui, Widget, text::LayoutJob}; |
2 | 4 | use objdiff_core::{ |
3 | 5 | build::BuildStatus, |
4 | 6 | diff::{ |
5 | 7 | DiffObjConfig, ObjectDiff, SymbolDiff, |
6 | 8 | data::BYTES_PER_ROW, |
7 | | - display::{ContextItem, HoverItem, HoverItemColor, SymbolFilter, SymbolNavigationKind}, |
| 9 | + display::{ |
| 10 | + ContextItem, DiffText, HoverItem, HoverItemColor, SymbolFilter, SymbolNavigationKind, |
| 11 | + display_row, |
| 12 | + }, |
8 | 13 | }, |
9 | | - obj::{Object, Symbol}, |
| 14 | + obj::{InstructionArgValue, Object, Symbol}, |
| 15 | + util::ReallySigned, |
10 | 16 | }; |
11 | 17 | use time::format_description; |
12 | 18 |
|
@@ -64,6 +70,51 @@ impl<'a> DiffColumnContext<'a> { |
64 | 70 | pub fn id(&self) -> Option<&str> { self.symbol.map(|(symbol, _, _)| symbol.name.as_str()) } |
65 | 71 | } |
66 | 72 |
|
| 73 | +/// Obtains the assembly text for a given symbol diff, suitable for copying to clipboard. |
| 74 | +fn get_asm_text( |
| 75 | + obj: &Object, |
| 76 | + symbol_diff: &SymbolDiff, |
| 77 | + symbol_idx: usize, |
| 78 | + diff_config: &DiffObjConfig, |
| 79 | +) -> String { |
| 80 | + let mut asm_text = String::new(); |
| 81 | + |
| 82 | + for ins_row in &symbol_diff.instruction_rows { |
| 83 | + let mut line = String::new(); |
| 84 | + let result = display_row(obj, symbol_idx, ins_row, diff_config, |segment| { |
| 85 | + let text = match segment.text { |
| 86 | + DiffText::Basic(text) => text.to_string(), |
| 87 | + DiffText::Line(num) => format!("{num} "), |
| 88 | + DiffText::Address(addr) => format!("{addr:x}:"), |
| 89 | + DiffText::Opcode(mnemonic, _op) => format!("{mnemonic} "), |
| 90 | + DiffText::Argument(arg) => match arg { |
| 91 | + InstructionArgValue::Signed(v) => format!("{:#x}", ReallySigned(v)), |
| 92 | + InstructionArgValue::Unsigned(v) => format!("{v:#x}"), |
| 93 | + InstructionArgValue::Opaque(v) => v.into_owned(), |
| 94 | + }, |
| 95 | + DiffText::BranchDest(addr) => format!("{addr:x}"), |
| 96 | + DiffText::Symbol(sym) => sym.demangled_name.as_ref().unwrap_or(&sym.name).clone(), |
| 97 | + DiffText::Addend(addend) => match addend.cmp(&0i64) { |
| 98 | + Ordering::Greater => format!("+{addend:#x}"), |
| 99 | + Ordering::Less => format!("-{:#x}", -addend), |
| 100 | + _ => String::new(), |
| 101 | + }, |
| 102 | + DiffText::Spacing(n) => " ".repeat(n.into()), |
| 103 | + DiffText::Eol => "\n".to_string(), |
| 104 | + }; |
| 105 | + line.push_str(&text); |
| 106 | + Ok(()) |
| 107 | + }); |
| 108 | + |
| 109 | + if result.is_ok() { |
| 110 | + asm_text.push_str(line.trim_end()); |
| 111 | + asm_text.push('\n'); |
| 112 | + } |
| 113 | + } |
| 114 | + |
| 115 | + asm_text |
| 116 | +} |
| 117 | + |
67 | 118 | #[must_use] |
68 | 119 | pub fn diff_view_ui( |
69 | 120 | ui: &mut Ui, |
@@ -208,16 +259,33 @@ pub fn diff_view_ui( |
208 | 259 |
|
209 | 260 | // Third row |
210 | 261 | if left_ctx.has_symbol() && right_ctx.has_symbol() { |
211 | | - if (state.current_view == View::FunctionDiff |
212 | | - && ui |
213 | | - .button("Change target") |
214 | | - .on_hover_text_at_pointer("Choose a different symbol to use as the target") |
215 | | - .clicked() |
216 | | - || hotkeys::consume_change_target_shortcut(ui.ctx())) |
217 | | - && let Some(symbol_ref) = state.symbol_state.right_symbol.as_ref() |
218 | | - { |
219 | | - ret = Some(DiffViewAction::SelectingLeft(symbol_ref.clone())); |
220 | | - } |
| 262 | + ui.horizontal(|ui| { |
| 263 | + if (state.current_view == View::FunctionDiff |
| 264 | + && ui |
| 265 | + .button("Change target") |
| 266 | + .on_hover_text_at_pointer( |
| 267 | + "Choose a different symbol to use as the target", |
| 268 | + ) |
| 269 | + .clicked() |
| 270 | + || hotkeys::consume_change_target_shortcut(ui.ctx())) |
| 271 | + && let Some(symbol_ref) = state.symbol_state.right_symbol.as_ref() |
| 272 | + { |
| 273 | + ret = Some(DiffViewAction::SelectingLeft(symbol_ref.clone())); |
| 274 | + } |
| 275 | + |
| 276 | + // Copy target ASM button. |
| 277 | + if state.current_view == View::FunctionDiff |
| 278 | + && let Some((_, symbol_diff, symbol_idx)) = left_ctx.symbol |
| 279 | + && let Some((obj, _)) = left_ctx.obj |
| 280 | + && ui |
| 281 | + .button("📋 Copy ASM") |
| 282 | + .on_hover_text_at_pointer("Copy assembly to clipboard") |
| 283 | + .clicked() |
| 284 | + { |
| 285 | + let asm_text = get_asm_text(obj, symbol_diff, symbol_idx, diff_config); |
| 286 | + ui.ctx().copy_text(asm_text); |
| 287 | + } |
| 288 | + }); |
221 | 289 | } else if left_ctx.status.success && !left_ctx.has_symbol() { |
222 | 290 | ui.horizontal(|ui| { |
223 | 291 | let mut search = state.search.clone(); |
@@ -374,6 +442,18 @@ pub fn diff_view_ui( |
374 | 442 | { |
375 | 443 | ret = Some(DiffViewAction::SelectingRight(symbol_ref.clone())); |
376 | 444 | } |
| 445 | + |
| 446 | + // Copy base ASM button. |
| 447 | + if let Some((_, symbol_diff, symbol_idx)) = right_ctx.symbol |
| 448 | + && let Some((obj, _)) = right_ctx.obj |
| 449 | + && ui |
| 450 | + .button("📋 Copy ASM") |
| 451 | + .on_hover_text_at_pointer("Copy assembly to clipboard") |
| 452 | + .clicked() |
| 453 | + { |
| 454 | + let asm_text = get_asm_text(obj, symbol_diff, symbol_idx, diff_config); |
| 455 | + ui.ctx().copy_text(asm_text); |
| 456 | + } |
377 | 457 | } |
378 | 458 | } else if right_ctx.status.success && !right_ctx.has_symbol() { |
379 | 459 | let mut search = state.search.clone(); |
|
0 commit comments