diff --git a/resym/src/main.rs b/resym/src/main.rs index c8c0fd3..9829f09 100644 --- a/resym/src/main.rs +++ b/resym/src/main.rs @@ -53,6 +53,16 @@ struct ResymApp { backend: Backend, } +#[derive(PartialEq)] +enum ResymAppMode { + /// Mode in which the application starts + Idle, + /// This mode means we're browsing a single PDB file + Browsing(String, usize, String), + /// This mode means we're comparing two PDB files for differences + Comparing(String, String, usize, Vec, String), +} + // GUI-related trait impl eframe::App for ResymApp { fn save(&mut self, storage: &mut dyn eframe::Storage) { @@ -199,7 +209,7 @@ impl<'p> ResymApp { } self.current_mode = - ResymAppMode::Browsing(String::default(), String::default()); + ResymAppMode::Browsing(String::default(), 0, String::default()); // Request a type list update if let Err(err) = self.backend.send_command(BackendCommand::UpdateTypeFilter( @@ -215,6 +225,7 @@ impl<'p> ResymApp { self.current_mode = ResymAppMode::Comparing( String::default(), String::default(), + 0, vec![], String::default(), ); @@ -234,46 +245,70 @@ impl<'p> ResymApp { } }, - FrontendCommand::UpdateReconstructedType(data) => { - let line_numbers = - (1..1 + data.lines().count()).fold(String::default(), |mut acc, e| { - let _r = writeln!(&mut acc, "{}", e); - acc - }); - self.current_mode = ResymAppMode::Browsing(line_numbers, data); + FrontendCommand::ReconstructTypeResult(type_reconstruction_result) => { + match type_reconstruction_result { + Err(err) => { + log::error!("Failed to reconstruct type: {}", err); + } + Ok(reconstructed_type) => { + let last_line_number = 1 + reconstructed_type.lines().count(); + let line_numbers = + (1..last_line_number).fold(String::default(), |mut acc, e| { + let _r = writeln!(&mut acc, "{}", e); + acc + }); + self.current_mode = ResymAppMode::Browsing( + line_numbers, + last_line_number, + reconstructed_type, + ); + } + } } - FrontendCommand::UpdateReconstructedTypeDiff(type_diff) => { - let (line_numbers_old, line_numbers_new, line_changes) = - type_diff.metadata.iter().fold( - (String::default(), String::default(), vec![]), - |(mut acc_old, mut acc_new, mut acc_changes), metadata| { - let indices = metadata.0; - - if let Some(indice) = indices.0 { - let _r = writeln!(&mut acc_old, "{}", 1 + indice); - } else { - let _r = writeln!(&mut acc_old); - } + FrontendCommand::DiffTypeResult(type_diff_result) => match type_diff_result { + Err(err) => { + log::error!("Failed to diff type: {}", err); + } + Ok(type_diff) => { + let mut last_line_number = 1; + let (line_numbers_old, line_numbers_new, line_changes) = + type_diff.metadata.iter().fold( + (String::default(), String::default(), vec![]), + |(mut acc_old, mut acc_new, mut acc_changes), metadata| { + let indices = metadata.0; + + if let Some(indice) = indices.0 { + last_line_number = + std::cmp::max(last_line_number, 1 + indice); + let _r = writeln!(&mut acc_old, "{}", 1 + indice); + } else { + let _r = writeln!(&mut acc_old); + } - if let Some(indice) = indices.1 { - let _r = writeln!(&mut acc_new, "{}", 1 + indice); - } else { - let _r = writeln!(&mut acc_new); - } + if let Some(indice) = indices.1 { + last_line_number = + std::cmp::max(last_line_number, 1 + indice); + let _r = writeln!(&mut acc_new, "{}", 1 + indice); + } else { + let _r = writeln!(&mut acc_new); + } - acc_changes.push(metadata.1); + acc_changes.push(metadata.1); - (acc_old, acc_new, acc_changes) - }, + (acc_old, acc_new, acc_changes) + }, + ); + + self.current_mode = ResymAppMode::Comparing( + line_numbers_old, + line_numbers_new, + last_line_number, + line_changes, + type_diff.data, ); - self.current_mode = ResymAppMode::Comparing( - line_numbers_old, - line_numbers_new, - line_changes, - type_diff.data, - ); - } + } + }, FrontendCommand::UpdateFilteredTypes(filtered_types) => { self.filtered_type_list = filtered_types; @@ -408,11 +443,12 @@ impl<'p> ResymApp { CodeTheme::dark() }; - let line_desc = if let ResymAppMode::Comparing(_, _, line_changes, _) = &self.current_mode { - Some(line_changes) - } else { - None - }; + let line_desc = + if let ResymAppMode::Comparing(_, _, _, line_changes, _) = &self.current_mode { + Some(line_changes) + } else { + None + }; // Layouter that'll disable wrapping and apply syntax highlighting if needed let mut layouter = |ui: &egui::Ui, string: &str, _wrap_width: f32| { @@ -431,28 +467,45 @@ impl<'p> ResymApp { egui::ScrollArea::both() .auto_shrink([false, false]) .show(ui, |ui| { - let num_colums = if self.settings.print_line_numbers { - if let ResymAppMode::Comparing(..) = self.current_mode { - // Old index + new index + code editor - 3 - } else { - // Line numbers + code editor - 2 + const LINE_NUMBER_DIGIT_WIDTH: usize = 10; + let (num_colums, min_column_width) = if self.settings.print_line_numbers { + match self.current_mode { + ResymAppMode::Comparing(_, _, last_line_number, ..) => { + // Compute the columns' sizes from the number of digits + let char_count = int_log10(last_line_number); + let line_number_width = (char_count * LINE_NUMBER_DIGIT_WIDTH) as f32; + + // Old index + new index + code editor + (3, line_number_width) + } + ResymAppMode::Browsing(_, last_line_number, _) => { + // Compute the columns' sizes from the number of digits + let char_count = int_log10(last_line_number); + let line_number_width = (char_count * LINE_NUMBER_DIGIT_WIDTH) as f32; + + // Line numbers + code editor + (2, line_number_width) + } + _ => { + // Code editor only + (1, 0.0) + } } } else { // Code editor only - 1 + (1, 0.0) }; - const LINE_NUMBER_WIDTH: f32 = 40.0; + egui::Grid::new("code_editor_grid") .num_columns(num_colums) - .min_col_width(LINE_NUMBER_WIDTH) + .min_col_width(min_column_width) .show(ui, |ui| { match &self.current_mode { ResymAppMode::Comparing( line_numbers_old, line_numbers_new, _, + _, reconstructed_type_diff, ) => { // Line numbers @@ -460,12 +513,12 @@ impl<'p> ResymApp { ui.add( egui::TextEdit::multiline(&mut line_numbers_old.as_str()) .interactive(false) - .desired_width(LINE_NUMBER_WIDTH), + .desired_width(min_column_width), ); ui.add( egui::TextEdit::multiline(&mut line_numbers_new.as_str()) .interactive(false) - .desired_width(LINE_NUMBER_WIDTH), + .desired_width(min_column_width), ); } // Text content @@ -477,13 +530,13 @@ impl<'p> ResymApp { .layouter(&mut layouter), ); } - ResymAppMode::Browsing(line_numbers, reconstructed_type_content) => { + ResymAppMode::Browsing(line_numbers, _, reconstructed_type_content) => { // Line numbers if self.settings.print_line_numbers { ui.add( egui::TextEdit::multiline(&mut line_numbers.as_str()) .interactive(false) - .desired_width(LINE_NUMBER_WIDTH), + .desired_width(min_column_width), ); } // Text content @@ -582,9 +635,23 @@ impl Default for ResymAppSettings { } } -#[derive(PartialEq)] -enum ResymAppMode { - Idle, - Browsing(String, String), - Comparing(String, String, Vec, String), +// FIXME: Replace with `checked_log10` once it's stabilized. +fn int_log10(mut i: T) -> usize +where + T: std::ops::DivAssign + std::cmp::PartialOrd + From + Copy, +{ + let zero = T::from(0); + if i == zero { + return 1; + } + + let mut len = 0; + let ten = T::from(10); + + while i > zero { + i /= ten; + len += 1; + } + + len } diff --git a/resym_core/src/backend.rs b/resym_core/src/backend.rs index 0cf604c..8afd348 100644 --- a/resym_core/src/backend.rs +++ b/resym_core/src/backend.rs @@ -119,15 +119,15 @@ fn worker_thread_routine( print_access_specifiers, ) => { if let Some(pdb_file) = pdb_files.get(&pdb_slot) { - let reconstructed_type = reconstruct_type_by_index_command( + let reconstructed_type_result = reconstruct_type_by_index_command( pdb_file, type_index, print_header, reconstruct_dependencies, print_access_specifiers, ); - frontend_controller.send_command(FrontendCommand::UpdateReconstructedType( - reconstructed_type, + frontend_controller.send_command(FrontendCommand::ReconstructTypeResult( + reconstructed_type_result, ))?; } } @@ -140,15 +140,15 @@ fn worker_thread_routine( print_access_specifiers, ) => { if let Some(pdb_file) = pdb_files.get(&pdb_slot) { - let reconstructed_type = reconstruct_type_by_name_command( + let reconstructed_type_result = reconstruct_type_by_name_command( pdb_file, &type_name, print_header, reconstruct_dependencies, print_access_specifiers, ); - frontend_controller.send_command(FrontendCommand::UpdateReconstructedType( - reconstructed_type, + frontend_controller.send_command(FrontendCommand::ReconstructTypeResult( + reconstructed_type_result, ))?; } } @@ -211,17 +211,16 @@ fn worker_thread_routine( ) => { if let Some(pdb_file_from) = pdb_files.get(&pdb_from_slot) { if let Some(pdb_file_to) = pdb_files.get(&pdb_to_slot) { - let type_diff = diff_type_by_name( + let type_diff_result = diff_type_by_name( pdb_file_from, pdb_file_to, &type_name, print_header, reconstruct_dependencies, print_access_specifiers, - )?; - frontend_controller.send_command( - FrontendCommand::UpdateReconstructedTypeDiff(type_diff), - )?; + ); + frontend_controller + .send_command(FrontendCommand::DiffTypeResult(type_diff_result))?; } } } @@ -237,24 +236,17 @@ fn reconstruct_type_by_index_command( print_header: bool, reconstruct_dependencies: bool, print_access_specifiers: bool, -) -> String { - match pdb_file.reconstruct_type_by_type_index( +) -> Result { + let data = pdb_file.reconstruct_type_by_type_index( type_index, reconstruct_dependencies, print_access_specifiers, - ) { - Err(err) => { - // Make it obvious an error occured - format!("Error: {}", err) - } - Ok(data) => { - if print_header { - let file_header = generate_file_header(pdb_file, true); - format!("{}{}", file_header, data) - } else { - data - } - } + )?; + if print_header { + let file_header = generate_file_header(pdb_file, true); + Ok(format!("{}{}", file_header, data)) + } else { + Ok(data) } } @@ -264,24 +256,17 @@ fn reconstruct_type_by_name_command( print_header: bool, reconstruct_dependencies: bool, print_access_specifiers: bool, -) -> String { - match pdb_file.reconstruct_type_by_name( +) -> Result { + let data = pdb_file.reconstruct_type_by_name( type_name, reconstruct_dependencies, print_access_specifiers, - ) { - Err(err) => { - // Make it obvious an error occured - format!("Error: {}", err) - } - Ok(data) => { - if print_header { - let file_header = generate_file_header(pdb_file, true); - format!("{}{}", file_header, data) - } else { - data - } - } + )?; + if print_header { + let file_header = generate_file_header(pdb_file, true); + Ok(format!("{}{}", file_header, data)) + } else { + Ok(data) } } diff --git a/resym_core/src/frontend.rs b/resym_core/src/frontend.rs index 73ac4d4..3612849 100644 --- a/resym_core/src/frontend.rs +++ b/resym_core/src/frontend.rs @@ -7,8 +7,8 @@ pub type TypeList = Vec<(String, pdb::TypeIndex)>; pub enum FrontendCommand { LoadPDBResult(Result), UpdateFilteredTypes(TypeList), - UpdateReconstructedType(String), - UpdateReconstructedTypeDiff(DiffedType), + ReconstructTypeResult(Result), + DiffTypeResult(Result), } pub trait FrontendController { diff --git a/resymc/src/main.rs b/resymc/src/main.rs index 312bde4..02b7744 100644 --- a/resymc/src/main.rs +++ b/resymc/src/main.rs @@ -247,25 +247,30 @@ impl ResymcApp { print_access_specifiers, ))?; // Wait for the backend to finish filtering types - if let FrontendCommand::UpdateReconstructedType(reconstructed_type) = + if let FrontendCommand::ReconstructTypeResult(reconstructed_type_result) = self.frontend_controller.rx_ui.recv()? { - // Dump output - if let Some(output_file_path) = output_file_path { - let mut output_file = File::create(output_file_path)?; - output_file.write_all(reconstructed_type.as_bytes())?; - } else if highlight_syntax { - const LANGUAGE_SYNTAX: &str = "cpp"; - let theme = CodeTheme::dark(); - if let Some(colorized_reconstructed_type) = - highlight_code(&theme, &reconstructed_type, LANGUAGE_SYNTAX, None) - { - println!("{}", colorized_reconstructed_type); + match reconstructed_type_result { + Err(err) => Err(err), + Ok(reconstructed_type) => { + // Dump output + if let Some(output_file_path) = output_file_path { + let mut output_file = File::create(output_file_path)?; + output_file.write_all(reconstructed_type.as_bytes())?; + } else if highlight_syntax { + const LANGUAGE_SYNTAX: &str = "cpp"; + let theme = CodeTheme::dark(); + if let Some(colorized_reconstructed_type) = + highlight_code(&theme, &reconstructed_type, LANGUAGE_SYNTAX, None) + { + println!("{}", colorized_reconstructed_type); + } + } else { + println!("{}", reconstructed_type); + } + Ok(()) } - } else { - println!("{}", reconstructed_type); } - Ok(()) } else { Err(anyhow!("Invalid response received from the backend?")) } @@ -329,36 +334,41 @@ impl ResymcApp { print_access_specifiers, ))?; // Wait for the backend to finish - if let FrontendCommand::UpdateReconstructedTypeDiff(reconstructed_type_diff) = + if let FrontendCommand::DiffTypeResult(reconstructed_type_diff_result) = self.frontend_controller.rx_ui.recv()? { - // Dump output - if let Some(output_file_path) = output_file_path { - let mut output_file = File::create(output_file_path)?; - output_file.write_all(reconstructed_type_diff.data.as_bytes())?; - } else if highlight_syntax { - const LANGUAGE_SYNTAX: &str = "cpp"; - let theme = CodeTheme::dark(); - let line_descriptions = - reconstructed_type_diff - .metadata - .iter() - .fold(vec![], |mut acc, e| { - acc.push(e.1); - acc - }); - if let Some(colorized_reconstructed_type) = highlight_code( - &theme, - &reconstructed_type_diff.data, - LANGUAGE_SYNTAX, - Some(line_descriptions), - ) { - println!("{}", colorized_reconstructed_type); + match reconstructed_type_diff_result { + Err(err) => Err(err), + Ok(reconstructed_type_diff) => { + // Dump output + if let Some(output_file_path) = output_file_path { + let mut output_file = File::create(output_file_path)?; + output_file.write_all(reconstructed_type_diff.data.as_bytes())?; + } else if highlight_syntax { + const LANGUAGE_SYNTAX: &str = "cpp"; + let theme = CodeTheme::dark(); + let line_descriptions = + reconstructed_type_diff + .metadata + .iter() + .fold(vec![], |mut acc, e| { + acc.push(e.1); + acc + }); + if let Some(colorized_reconstructed_type) = highlight_code( + &theme, + &reconstructed_type_diff.data, + LANGUAGE_SYNTAX, + Some(line_descriptions), + ) { + println!("{}", colorized_reconstructed_type); + } + } else { + println!("{}", reconstructed_type_diff.data); + } + Ok(()) } - } else { - println!("{}", reconstructed_type_diff.data); } - Ok(()) } else { Err(anyhow!("Invalid response received from the backend?")) }