Diff view refactor

This commit is contained in:
Luke Street
2025-02-09 22:27:42 -07:00
parent 3e6efb7736
commit d938988d43
8 changed files with 814 additions and 1118 deletions
+1 -1
View File
@@ -18,4 +18,4 @@ authors = ["Luke Street <luke@street.dev>"]
edition = "2021"
license = "MIT OR Apache-2.0"
repository = "https://github.com/encounter/objdiff"
rust-version = "1.81"
rust-version = "1.82"
+3 -14
View File
@@ -33,16 +33,14 @@ use crate::{
arch_config_window, config_ui, general_config_ui, project_window, ConfigViewState,
CONFIG_DISABLED_TEXT,
},
data_diff::data_diff_ui,
debug::debug_window,
demangle::{demangle_window, DemangleViewState},
extab_diff::extab_diff_ui,
diff::diff_view_ui,
frame_history::FrameHistory,
function_diff::function_diff_ui,
graphics::{graphics_window, GraphicsConfig, GraphicsViewState},
jobs::{jobs_menu_ui, jobs_window},
rlwinm::{rlwinm_decode_window, RlwinmDecodeViewState},
symbol_diff::{symbol_diff_ui, DiffViewAction, DiffViewNavigation, DiffViewState, View},
symbol_diff::{DiffViewAction, DiffViewNavigation, DiffViewState, View},
},
};
@@ -753,16 +751,7 @@ impl eframe::App for App {
let mut action = None;
egui::CentralPanel::default().show(ctx, |ui| {
let build_success = matches!(&diff_state.build, Some(b) if b.first_status.success && b.second_status.success);
action = if diff_state.current_view == View::FunctionDiff && build_success {
function_diff_ui(ui, diff_state, appearance)
} else if diff_state.current_view == View::DataDiff && build_success {
data_diff_ui(ui, diff_state, appearance)
} else if diff_state.current_view == View::ExtabDiff && build_success {
extab_diff_ui(ui, diff_state, appearance)
} else {
symbol_diff_ui(ui, diff_state, appearance)
};
action = diff_view_ui(ui, diff_state, appearance);
});
project_window(ctx, state, show_project_config, config_state, appearance);
+6 -185
View File
@@ -4,28 +4,15 @@ use std::{
mem::take,
};
use egui::{text::LayoutJob, Id, Label, RichText, Sense, Widget};
use egui::{text::LayoutJob, Label, Sense, Widget};
use objdiff_core::{
diff::{ObjDataDiff, ObjDataDiffKind, ObjDataRelocDiff, ObjDiff},
diff::{ObjDataDiff, ObjDataDiffKind, ObjDataRelocDiff},
obj::ObjInfo,
};
use time::format_description;
use crate::{
hotkeys,
views::{
appearance::Appearance,
column_layout::{render_header, render_table},
symbol_diff::{DiffViewAction, DiffViewNavigation, DiffViewState},
write_text,
},
};
use crate::views::{appearance::Appearance, write_text};
const BYTES_PER_ROW: usize = 16;
fn find_section(obj: &ObjInfo, section_name: &str) -> Option<usize> {
obj.sections.iter().position(|section| section.name == section_name)
}
pub(crate) const BYTES_PER_ROW: usize = 16;
fn data_row_hover_ui(
ui: &mut egui::Ui,
@@ -122,7 +109,7 @@ fn get_color_for_diff_kind(diff_kind: ObjDataDiffKind, appearance: &Appearance)
}
}
fn data_row_ui(
pub(crate) fn data_row_ui(
ui: &mut egui::Ui,
obj: Option<&ObjInfo>,
address: usize,
@@ -212,7 +199,7 @@ fn data_row_ui(
}
}
fn split_diffs(
pub(crate) fn split_diffs(
diffs: &[ObjDataDiff],
reloc_diffs: &[ObjDataRelocDiff],
) -> Vec<Vec<(ObjDataDiff, Vec<ObjDataRelocDiff>)>> {
@@ -273,169 +260,3 @@ fn split_diffs(
}
split_diffs
}
#[derive(Clone, Copy)]
struct SectionDiffContext<'a> {
obj: &'a ObjInfo,
diff: &'a ObjDiff,
section_index: Option<usize>,
}
impl<'a> SectionDiffContext<'a> {
pub fn new(obj: Option<&'a (ObjInfo, ObjDiff)>, section_name: Option<&str>) -> Option<Self> {
obj.map(|(obj, diff)| Self {
obj,
diff,
section_index: section_name.and_then(|section_name| find_section(obj, section_name)),
})
}
#[inline]
pub fn has_section(&self) -> bool { self.section_index.is_some() }
}
fn data_table_ui(
ui: &mut egui::Ui,
available_width: f32,
left_ctx: Option<SectionDiffContext<'_>>,
right_ctx: Option<SectionDiffContext<'_>>,
config: &Appearance,
) -> Option<()> {
let left_obj = left_ctx.map(|ctx| ctx.obj);
let right_obj = right_ctx.map(|ctx| ctx.obj);
let left_section = left_ctx
.and_then(|ctx| ctx.section_index.map(|i| (&ctx.obj.sections[i], &ctx.diff.sections[i])));
let right_section = right_ctx
.and_then(|ctx| ctx.section_index.map(|i| (&ctx.obj.sections[i], &ctx.diff.sections[i])));
let total_bytes = left_section
.or(right_section)?
.1
.data_diff
.iter()
.fold(0usize, |accum, item| accum + item.len);
if total_bytes == 0 {
return None;
}
let total_rows = (total_bytes - 1) / BYTES_PER_ROW + 1;
let left_diffs =
left_section.map(|(_, section)| split_diffs(&section.data_diff, &section.reloc_diff));
let right_diffs =
right_section.map(|(_, section)| split_diffs(&section.data_diff, &section.reloc_diff));
hotkeys::check_scroll_hotkeys(ui, true);
render_table(ui, available_width, 2, config.code_font.size, total_rows, |row, column| {
let i = row.index();
let address = i * BYTES_PER_ROW;
row.col(|ui| {
if column == 0 {
if let Some(left_diffs) = &left_diffs {
data_row_ui(ui, left_obj, address, &left_diffs[i], config);
}
} else if column == 1 {
if let Some(right_diffs) = &right_diffs {
data_row_ui(ui, right_obj, address, &right_diffs[i], config);
}
}
});
});
Some(())
}
#[must_use]
pub fn data_diff_ui(
ui: &mut egui::Ui,
state: &DiffViewState,
appearance: &Appearance,
) -> Option<DiffViewAction> {
let mut ret = None;
let Some(result) = &state.build else {
return ret;
};
let section_name =
state.symbol_state.left_symbol.as_ref().and_then(|s| s.section_name.as_deref()).or_else(
|| state.symbol_state.right_symbol.as_ref().and_then(|s| s.section_name.as_deref()),
);
let left_ctx = SectionDiffContext::new(result.first_obj.as_ref(), section_name);
let right_ctx = SectionDiffContext::new(result.second_obj.as_ref(), section_name);
// If both sides are missing a symbol, switch to symbol diff view
if !right_ctx.is_some_and(|ctx| ctx.has_section())
&& !left_ctx.is_some_and(|ctx| ctx.has_section())
{
return Some(DiffViewAction::Navigate(DiffViewNavigation::symbol_diff()));
}
// Header
let available_width = ui.available_width();
render_header(ui, available_width, 2, |ui, column| {
if column == 0 {
// Left column
if ui.button("⏴ Back").clicked() || hotkeys::back_pressed(ui.ctx()) {
ret = Some(DiffViewAction::Navigate(DiffViewNavigation::symbol_diff()));
}
if let Some(section) =
left_ctx.and_then(|ctx| ctx.section_index.map(|i| &ctx.obj.sections[i]))
{
ui.label(
RichText::new(section.name.clone())
.font(appearance.code_font.clone())
.color(appearance.highlight_color),
);
} else {
ui.label(
RichText::new("Missing")
.font(appearance.code_font.clone())
.color(appearance.replace_color),
);
}
} else if column == 1 {
// Right column
ui.horizontal(|ui| {
if ui.add_enabled(!state.build_running, egui::Button::new("Build")).clicked() {
ret = Some(DiffViewAction::Build);
}
ui.scope(|ui| {
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
if state.build_running {
ui.colored_label(appearance.replace_color, "Building…");
} else {
ui.label("Last built:");
let format = format_description::parse("[hour]:[minute]:[second]").unwrap();
ui.label(
result.time.to_offset(appearance.utc_offset).format(&format).unwrap(),
);
}
});
});
if let Some(section) =
right_ctx.and_then(|ctx| ctx.section_index.map(|i| &ctx.obj.sections[i]))
{
ui.label(
RichText::new(section.name.clone())
.font(appearance.code_font.clone())
.color(appearance.highlight_color),
);
} else {
ui.label(
RichText::new("Missing")
.font(appearance.code_font.clone())
.color(appearance.replace_color),
);
}
}
});
// Table
let id =
Id::new(state.symbol_state.left_symbol.as_ref().and_then(|s| s.section_name.as_deref()))
.with(state.symbol_state.right_symbol.as_ref().and_then(|s| s.section_name.as_deref()));
ui.push_id(id, |ui| {
data_table_ui(ui, available_width, left_ctx, right_ctx, appearance);
});
ret
}
File diff suppressed because it is too large Load Diff
+3 -190
View File
@@ -1,22 +1,10 @@
use egui::{RichText, ScrollArea};
use egui::ScrollArea;
use objdiff_core::{
arch::ppc::ExceptionInfo,
obj::{ObjInfo, ObjSymbol},
};
use time::format_description;
use crate::{
hotkeys,
views::{
appearance::Appearance,
column_layout::{render_header, render_strips},
function_diff::FunctionDiffContext,
symbol_diff::{
match_color_for_symbol, DiffViewAction, DiffViewNavigation, DiffViewState,
SymbolRefByName, View,
},
},
};
use crate::views::{appearance::Appearance, function_diff::FunctionDiffContext};
fn decode_extab(extab: &ExceptionInfo) -> String {
let mut text = String::from("");
@@ -57,7 +45,7 @@ fn extab_text_ui(
None
}
fn extab_ui(
pub(crate) fn extab_ui(
ui: &mut egui::Ui,
ctx: FunctionDiffContext<'_>,
appearance: &Appearance,
@@ -76,178 +64,3 @@ fn extab_ui(
});
});
}
#[must_use]
pub fn extab_diff_ui(
ui: &mut egui::Ui,
state: &DiffViewState,
appearance: &Appearance,
) -> Option<DiffViewAction> {
let mut ret = None;
let Some(result) = &state.build else {
return ret;
};
let mut left_ctx = FunctionDiffContext::new(
result.first_obj.as_ref(),
state.symbol_state.left_symbol.as_ref(),
);
let mut right_ctx = FunctionDiffContext::new(
result.second_obj.as_ref(),
state.symbol_state.right_symbol.as_ref(),
);
// If one side is missing a symbol, but the diff process found a match, use that symbol
let left_diff_symbol = left_ctx.and_then(|ctx| {
ctx.symbol_ref.and_then(|symbol_ref| ctx.diff.symbol_diff(symbol_ref).target_symbol)
});
let right_diff_symbol = right_ctx.and_then(|ctx| {
ctx.symbol_ref.and_then(|symbol_ref| ctx.diff.symbol_diff(symbol_ref).target_symbol)
});
if left_diff_symbol.is_some() && right_ctx.is_some_and(|ctx| !ctx.has_symbol()) {
let (right_section, right_symbol) =
right_ctx.unwrap().obj.section_symbol(left_diff_symbol.unwrap());
let symbol_ref = SymbolRefByName::new(right_symbol, right_section);
right_ctx = FunctionDiffContext::new(result.second_obj.as_ref(), Some(&symbol_ref));
ret = Some(DiffViewAction::Navigate(DiffViewNavigation {
view: Some(View::FunctionDiff),
left_symbol: state.symbol_state.left_symbol.clone(),
right_symbol: Some(symbol_ref),
}));
} else if right_diff_symbol.is_some() && left_ctx.is_some_and(|ctx| !ctx.has_symbol()) {
let (left_section, left_symbol) =
left_ctx.unwrap().obj.section_symbol(right_diff_symbol.unwrap());
let symbol_ref = SymbolRefByName::new(left_symbol, left_section);
left_ctx = FunctionDiffContext::new(result.first_obj.as_ref(), Some(&symbol_ref));
ret = Some(DiffViewAction::Navigate(DiffViewNavigation {
view: Some(View::FunctionDiff),
left_symbol: Some(symbol_ref),
right_symbol: state.symbol_state.right_symbol.clone(),
}));
}
// If both sides are missing a symbol, switch to symbol diff view
if right_ctx.is_some_and(|ctx| !ctx.has_symbol())
&& left_ctx.is_some_and(|ctx| !ctx.has_symbol())
{
return Some(DiffViewAction::Navigate(DiffViewNavigation::symbol_diff()));
}
// Header
let available_width = ui.available_width();
render_header(ui, available_width, 2, |ui, column| {
if column == 0 {
// Left column
ui.horizontal(|ui| {
if ui.button("⏴ Back").clicked() || hotkeys::back_pressed(ui.ctx()) {
ret = Some(DiffViewAction::Navigate(DiffViewNavigation::symbol_diff()));
}
ui.separator();
if ui
.add_enabled(
!state.scratch_running
&& state.scratch_available
&& left_ctx.is_some_and(|ctx| ctx.has_symbol()),
egui::Button::new("📲 decomp.me"),
)
.on_hover_text_at_pointer("Create a new scratch on decomp.me (beta)")
.on_disabled_hover_text("Scratch configuration missing")
.clicked()
{
if let Some((_section, symbol)) = left_ctx.and_then(|ctx| {
ctx.symbol_ref.map(|symbol_ref| ctx.obj.section_symbol(symbol_ref))
}) {
ret = Some(DiffViewAction::CreateScratch(symbol.name.clone()));
}
}
});
if let Some((_section, symbol)) = left_ctx
.and_then(|ctx| ctx.symbol_ref.map(|symbol_ref| ctx.obj.section_symbol(symbol_ref)))
{
let name = symbol.demangled_name.as_deref().unwrap_or(&symbol.name);
ui.label(
RichText::new(name)
.font(appearance.code_font.clone())
.color(appearance.highlight_color),
);
} else {
ui.label(
RichText::new("Missing")
.font(appearance.code_font.clone())
.color(appearance.replace_color),
);
}
} else if column == 1 {
// Right column
ui.horizontal(|ui| {
if ui.add_enabled(!state.build_running, egui::Button::new("Build")).clicked() {
ret = Some(DiffViewAction::Build);
}
ui.scope(|ui| {
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
if state.build_running {
ui.colored_label(appearance.replace_color, "Building…");
} else {
ui.label("Last built:");
let format = format_description::parse("[hour]:[minute]:[second]").unwrap();
ui.label(
result.time.to_offset(appearance.utc_offset).format(&format).unwrap(),
);
}
});
ui.separator();
if ui
.add_enabled(state.source_path_available, egui::Button::new("🖹 Source file"))
.on_hover_text_at_pointer("Open the source file in the default editor")
.on_disabled_hover_text("Source file metadata missing")
.clicked()
{
ret = Some(DiffViewAction::OpenSourcePath);
}
});
if let Some(((_section, symbol), symbol_diff)) = right_ctx.and_then(|ctx| {
ctx.symbol_ref.map(|symbol_ref| {
(ctx.obj.section_symbol(symbol_ref), ctx.diff.symbol_diff(symbol_ref))
})
}) {
let name = symbol.demangled_name.as_deref().unwrap_or(&symbol.name);
ui.label(
RichText::new(name)
.font(appearance.code_font.clone())
.color(appearance.highlight_color),
);
if let Some(match_percent) = symbol_diff.match_percent {
ui.label(
RichText::new(format!("{:.0}%", match_percent.floor()))
.font(appearance.code_font.clone())
.color(match_color_for_symbol(match_percent, appearance)),
);
}
} else {
ui.label(
RichText::new("Missing")
.font(appearance.code_font.clone())
.color(appearance.replace_color),
);
}
}
});
hotkeys::check_scroll_hotkeys(ui, true);
// Table
render_strips(ui, available_width, 2, |ui, column| {
if column == 0 {
if let Some(ctx) = left_ctx {
extab_ui(ui, ctx, appearance, column);
}
} else if column == 1 {
if let Some(ctx) = right_ctx {
extab_ui(ui, ctx, appearance, column);
}
}
});
ret
}
File diff suppressed because it is too large Load Diff
+1
View File
@@ -6,6 +6,7 @@ pub(crate) mod config;
pub(crate) mod data_diff;
pub(crate) mod debug;
pub(crate) mod demangle;
pub(crate) mod diff;
pub(crate) mod extab_diff;
pub(crate) mod file;
pub(crate) mod frame_history;
+49 -238
View File
@@ -1,12 +1,11 @@
use std::{collections::BTreeMap, mem::take, ops::Bound};
use egui::{
style::ScrollAnimation, text::LayoutJob, CollapsingHeader, Color32, Id, Layout, OpenUrl,
ScrollArea, SelectableLabel, TextEdit, Ui, Widget,
style::ScrollAnimation, text::LayoutJob, CollapsingHeader, Color32, Id, OpenUrl, ScrollArea,
SelectableLabel, Ui, Widget,
};
use objdiff_core::{
arch::ObjArch,
build::BuildStatus,
diff::{display::HighlightKind, ObjDiff, ObjSymbolDiff},
jobs::{create_scratch::CreateScratchResult, objdiff::ObjDiffResult, Job, JobQueue, JobResult},
obj::{
@@ -19,15 +18,10 @@ use crate::{
app::AppStateRef,
hotkeys,
jobs::{is_create_scratch_available, start_create_scratch},
views::{
appearance::Appearance,
column_layout::{render_header, render_strips},
function_diff::FunctionViewState,
write_text,
},
views::{appearance::Appearance, function_diff::FunctionViewState, write_text},
};
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct SymbolRefByName {
pub symbol_name: String,
pub section_name: Option<String>,
@@ -79,16 +73,16 @@ pub enum DiffViewAction {
SetShowMappedSymbols(bool),
}
#[derive(Debug, Clone, Default)]
#[derive(Debug, Clone, Default, Eq, PartialEq)]
pub struct DiffViewNavigation {
pub view: Option<View>,
pub view: View,
pub left_symbol: Option<SymbolRefByName>,
pub right_symbol: Option<SymbolRefByName>,
}
impl DiffViewNavigation {
pub fn symbol_diff() -> Self {
Self { view: Some(View::SymbolDiff), left_symbol: None, right_symbol: None }
Self { view: View::SymbolDiff, left_symbol: None, right_symbol: None }
}
pub fn with_symbols(
@@ -107,8 +101,24 @@ impl DiffViewNavigation {
})
});
match column {
0 => Self { view: Some(view), left_symbol: symbol1, right_symbol: symbol2 },
1 => Self { view: Some(view), left_symbol: symbol2, right_symbol: symbol1 },
0 => Self { view, left_symbol: symbol1, right_symbol: symbol2 },
1 => Self { view, left_symbol: symbol2, right_symbol: symbol1 },
_ => unreachable!("Invalid column index"),
}
}
pub fn data_diff(section: &ObjSection, column: usize) -> Self {
let symbol = Some(SymbolRefByName {
symbol_name: "".to_string(),
section_name: Some(section.name.clone()),
});
match column {
0 => Self {
view: View::DataDiff,
left_symbol: symbol.clone(),
right_symbol: symbol.clone(),
},
1 => Self { view: View::DataDiff, left_symbol: symbol.clone(), right_symbol: symbol },
_ => unreachable!("Invalid column index"),
}
}
@@ -151,9 +161,7 @@ impl DiffViewState {
// TODO: where should this go?
if let Some(result) = self.post_build_nav.take() {
if let Some(view) = result.view {
self.current_view = view;
}
self.current_view = result.view;
self.symbol_state.left_symbol = result.left_symbol;
self.symbol_state.right_symbol = result.right_symbol;
}
@@ -219,7 +227,7 @@ impl DiffViewState {
};
if (nav.left_symbol.is_some() && nav.right_symbol.is_some())
|| (nav.left_symbol.is_none() && nav.right_symbol.is_none())
|| nav.view != Some(View::FunctionDiff)
|| nav.view != View::FunctionDiff
{
// Regular navigation
if state.is_selecting_symbol() {
@@ -228,9 +236,7 @@ impl DiffViewState {
self.post_build_nav = Some(nav);
} else {
// Navigate immediately
if let Some(view) = nav.view {
self.current_view = view;
}
self.current_view = nav.view;
self.symbol_state.left_symbol = nav.left_symbol;
self.symbol_state.right_symbol = nav.right_symbol;
}
@@ -301,7 +307,7 @@ impl DiffViewState {
};
state.set_selecting_left(&right_ref.symbol_name);
self.post_build_nav = Some(DiffViewNavigation {
view: Some(View::FunctionDiff),
view: View::FunctionDiff,
left_symbol: None,
right_symbol: Some(right_ref),
});
@@ -316,7 +322,7 @@ impl DiffViewState {
};
state.set_selecting_right(&left_ref.symbol_name);
self.post_build_nav = Some(DiffViewNavigation {
view: Some(View::FunctionDiff),
view: View::FunctionDiff,
left_symbol: Some(left_ref),
right_symbol: None,
});
@@ -337,7 +343,7 @@ impl DiffViewState {
self.post_build_nav = Some(DiffViewNavigation::symbol_diff());
} else {
self.post_build_nav = Some(DiffViewNavigation {
view: Some(view),
view,
left_symbol: Some(left_ref),
right_symbol: Some(right_ref),
});
@@ -409,13 +415,13 @@ fn symbol_context_menu_ui(
let symbol_ref = SymbolRefByName::new(symbol, Some(section));
if column == 0 {
ret = Some(DiffViewNavigation {
view: Some(View::FunctionDiff),
view: View::FunctionDiff,
left_symbol: Some(symbol_ref),
right_symbol: None,
});
} else {
ret = Some(DiffViewNavigation {
view: Some(View::FunctionDiff),
view: View::FunctionDiff,
left_symbol: None,
right_symbol: Some(symbol_ref),
});
@@ -552,13 +558,8 @@ fn symbol_ui(
)));
}
ObjSectionKind::Data => {
ret = Some(DiffViewAction::Navigate(DiffViewNavigation::with_symbols(
View::DataDiff,
other_ctx,
symbol,
section,
symbol_diff,
column,
ret = Some(DiffViewAction::Navigate(DiffViewNavigation::data_diff(
section, column,
)));
}
ObjSectionKind::Bss => {}
@@ -591,9 +592,15 @@ fn symbol_matches_filter(
SymbolFilter::None => true,
SymbolFilter::Search(regex) => {
regex.is_match(&symbol.name)
|| symbol.demangled_name.as_ref().map(|s| regex.is_match(s)).unwrap_or(false)
|| symbol.demangled_name.as_deref().is_some_and(|s| regex.is_match(s))
}
SymbolFilter::Mapping(symbol_ref, regex) => {
diff.target_symbol == Some(symbol_ref)
&& regex.is_none_or(|r| {
r.is_match(&symbol.name)
|| symbol.demangled_name.as_deref().is_some_and(|s| r.is_match(s))
})
}
SymbolFilter::Mapping(symbol_ref) => diff.target_symbol == Some(symbol_ref),
}
}
@@ -601,7 +608,7 @@ fn symbol_matches_filter(
pub enum SymbolFilter<'a> {
None,
Search(&'a Regex),
Mapping(SymbolRef),
Mapping(SymbolRef, Option<&'a Regex>),
}
#[must_use]
@@ -619,13 +626,16 @@ pub fn symbol_list_ui(
let mut ret = None;
ScrollArea::both().auto_shrink([false, false]).show(ui, |ui| {
let mut mapping = BTreeMap::new();
if let SymbolFilter::Mapping(target_ref) = filter {
if let SymbolFilter::Mapping(_, _) = filter {
let mut show_mapped_symbols = state.show_mapped_symbols;
if ui.checkbox(&mut show_mapped_symbols, "Show mapped symbols").changed() {
ret = Some(DiffViewAction::SetShowMappedSymbols(show_mapped_symbols));
}
for mapping_diff in &ctx.diff.mapping_symbols {
if mapping_diff.target_symbol == Some(target_ref) {
let symbol = ctx.obj.section_symbol(mapping_diff.symbol_ref).1;
if !symbol_matches_filter(symbol, mapping_diff, filter) {
continue;
}
if !show_mapped_symbols {
let symbol_diff = ctx.diff.symbol_diff(mapping_diff.symbol_ref);
if symbol_diff.target_symbol.is_some() {
@@ -634,7 +644,6 @@ pub fn symbol_list_ui(
}
mapping.insert(mapping_diff.symbol_ref, mapping_diff);
}
}
} else {
for (symbol, diff) in ctx.obj.common.iter().zip(&ctx.diff.common) {
if !symbol_matches_filter(symbol, diff, filter) {
@@ -819,206 +828,8 @@ pub fn symbol_list_ui(
ret
}
fn build_log_ui(ui: &mut Ui, status: &BuildStatus, appearance: &Appearance) {
ScrollArea::both().auto_shrink([false, false]).show(ui, |ui| {
ui.horizontal(|ui| {
if !status.cmdline.is_empty() && ui.button("Copy command").clicked() {
ui.output_mut(|output| output.copied_text.clone_from(&status.cmdline));
}
if ui.button("Copy log").clicked() {
ui.output_mut(|output| {
output.copied_text = format!("{}\n{}", status.stdout, status.stderr)
});
}
});
ui.scope(|ui| {
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
if !status.cmdline.is_empty() {
ui.label(&status.cmdline);
}
if !status.stdout.is_empty() {
ui.colored_label(appearance.replace_color, &status.stdout);
}
if !status.stderr.is_empty() {
ui.colored_label(appearance.delete_color, &status.stderr);
}
});
});
}
fn missing_obj_ui(ui: &mut Ui, appearance: &Appearance) {
ui.scope(|ui| {
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
ui.colored_label(appearance.replace_color, "No object configured");
});
}
#[derive(Copy, Clone)]
pub struct SymbolDiffContext<'a> {
pub obj: &'a ObjInfo,
pub diff: &'a ObjDiff,
}
#[must_use]
pub fn symbol_diff_ui(
ui: &mut Ui,
state: &mut DiffViewState,
appearance: &Appearance,
) -> Option<DiffViewAction> {
let mut ret = None;
let Some(result) = &state.build else {
return ret;
};
// Header
let available_width = ui.available_width();
let mut open_sections = (None, None);
render_header(ui, available_width, 2, |ui, column| {
if column == 0 {
// Left column
ui.scope(|ui| {
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
ui.label("Target object");
if result.first_status.success {
if result.first_obj.is_none() {
ui.colored_label(appearance.replace_color, "Missing");
} else {
ui.colored_label(appearance.highlight_color, state.object_name.clone());
}
} else {
ui.colored_label(appearance.delete_color, "Fail");
}
});
ui.horizontal(|ui| {
let mut search = state.search.clone();
let response = TextEdit::singleline(&mut search).hint_text("Filter symbols").ui(ui);
if hotkeys::consume_symbol_filter_shortcut(ui.ctx()) {
response.request_focus();
}
if response.changed() {
ret = Some(DiffViewAction::SetSearch(search));
}
ui.with_layout(Layout::right_to_left(egui::Align::TOP), |ui| {
if ui.small_button("").on_hover_text_at_pointer("Expand all").clicked() {
open_sections.0 = Some(true);
}
if ui.small_button("").on_hover_text_at_pointer("Collapse all").clicked() {
open_sections.0 = Some(false);
}
})
});
} else if column == 1 {
// Right column
ui.horizontal(|ui| {
ui.scope(|ui| {
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
ui.label("Base object");
});
ui.separator();
if ui
.add_enabled(state.source_path_available, egui::Button::new("🖹 Source file"))
.on_hover_text_at_pointer("Open the source file in the default editor")
.on_disabled_hover_text("Source file metadata missing")
.clicked()
{
ret = Some(DiffViewAction::OpenSourcePath);
}
});
ui.scope(|ui| {
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
if result.second_status.success {
if result.second_obj.is_none() {
ui.colored_label(appearance.replace_color, "Missing");
} else {
ui.colored_label(appearance.highlight_color, "OK");
}
} else {
ui.colored_label(appearance.delete_color, "Fail");
}
});
ui.horizontal(|ui| {
if ui.add_enabled(!state.build_running, egui::Button::new("Build")).clicked() {
ret = Some(DiffViewAction::Build);
}
ui.with_layout(Layout::right_to_left(egui::Align::TOP), |ui| {
if ui.small_button("").on_hover_text_at_pointer("Expand all").clicked() {
open_sections.1 = Some(true);
}
if ui.small_button("").on_hover_text_at_pointer("Collapse all").clicked() {
open_sections.1 = Some(false);
}
})
});
}
});
// Table
let filter = match &state.search_regex {
Some(regex) => SymbolFilter::Search(regex),
_ => SymbolFilter::None,
};
render_strips(ui, available_width, 2, |ui, column| {
if column == 0 {
// Left column
if result.first_status.success {
if let Some((obj, diff)) = &result.first_obj {
if let Some(result) = symbol_list_ui(
ui,
SymbolDiffContext { obj, diff },
result
.second_obj
.as_ref()
.map(|(obj, diff)| SymbolDiffContext { obj, diff }),
&state.symbol_state,
filter,
appearance,
column,
open_sections.0,
) {
ret = Some(result);
}
} else {
missing_obj_ui(ui, appearance);
}
} else {
build_log_ui(ui, &result.first_status, appearance);
}
} else if column == 1 {
// Right column
if result.second_status.success {
if let Some((obj, diff)) = &result.second_obj {
if let Some(result) = symbol_list_ui(
ui,
SymbolDiffContext { obj, diff },
result
.first_obj
.as_ref()
.map(|(obj, diff)| SymbolDiffContext { obj, diff }),
&state.symbol_state,
filter,
appearance,
column,
open_sections.1,
) {
ret = Some(result);
}
} else {
missing_obj_ui(ui, appearance);
}
} else {
build_log_ui(ui, &result.second_status, appearance);
}
}
});
ret
}