mirror of https://github.com/encounter/objdiff.git
Debounce loaded object modification check
Before, this was running 2 fs::metadata calls every frame. We don't need to do it nearly that often, so now it only checks once every 500ms. This required refactoring AppConfig into a separate AppState that holds transient runtime state along with the loaded AppConfig.
This commit is contained in:
parent
b0123b3f83
commit
8fc142d316
|
@ -7,6 +7,7 @@ use std::{
|
||||||
atomic::{AtomicBool, Ordering},
|
atomic::{AtomicBool, Ordering},
|
||||||
Arc, Mutex, RwLock,
|
Arc, Mutex, RwLock,
|
||||||
},
|
},
|
||||||
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
use filetime::FileTime;
|
use filetime::FileTime;
|
||||||
|
@ -82,6 +83,36 @@ fn default_watch_patterns() -> Vec<Glob> {
|
||||||
DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect()
|
DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct AppState {
|
||||||
|
pub config: AppConfig,
|
||||||
|
pub objects: Vec<ProjectObject>,
|
||||||
|
pub object_nodes: Vec<ProjectObjectNode>,
|
||||||
|
pub watcher_change: bool,
|
||||||
|
pub config_change: bool,
|
||||||
|
pub obj_change: bool,
|
||||||
|
pub queue_build: bool,
|
||||||
|
pub queue_reload: bool,
|
||||||
|
pub project_config_info: Option<ProjectConfigInfo>,
|
||||||
|
pub last_mod_check: Instant,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AppState {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
config: Default::default(),
|
||||||
|
objects: vec![],
|
||||||
|
object_nodes: vec![],
|
||||||
|
watcher_change: false,
|
||||||
|
config_change: false,
|
||||||
|
obj_change: false,
|
||||||
|
queue_build: false,
|
||||||
|
queue_reload: false,
|
||||||
|
project_config_info: None,
|
||||||
|
last_mod_check: Instant::now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, serde::Deserialize, serde::Serialize)]
|
||||||
pub struct AppConfig {
|
pub struct AppConfig {
|
||||||
// TODO: https://github.com/ron-rs/ron/pull/455
|
// TODO: https://github.com/ron-rs/ron/pull/455
|
||||||
|
@ -116,23 +147,6 @@ pub struct AppConfig {
|
||||||
pub recent_projects: Vec<PathBuf>,
|
pub recent_projects: Vec<PathBuf>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub diff_obj_config: DiffObjConfig,
|
pub diff_obj_config: DiffObjConfig,
|
||||||
|
|
||||||
#[serde(skip)]
|
|
||||||
pub objects: Vec<ProjectObject>,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub object_nodes: Vec<ProjectObjectNode>,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub watcher_change: bool,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub config_change: bool,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub obj_change: bool,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub queue_build: bool,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub queue_reload: bool,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub project_config_info: Option<ProjectConfigInfo>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AppConfig {
|
impl Default for AppConfig {
|
||||||
|
@ -153,30 +167,22 @@ impl Default for AppConfig {
|
||||||
watch_patterns: DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect(),
|
watch_patterns: DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect(),
|
||||||
recent_projects: vec![],
|
recent_projects: vec![],
|
||||||
diff_obj_config: Default::default(),
|
diff_obj_config: Default::default(),
|
||||||
objects: vec![],
|
|
||||||
object_nodes: vec![],
|
|
||||||
watcher_change: false,
|
|
||||||
config_change: false,
|
|
||||||
obj_change: false,
|
|
||||||
queue_build: false,
|
|
||||||
queue_reload: false,
|
|
||||||
project_config_info: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppConfig {
|
impl AppState {
|
||||||
pub fn set_project_dir(&mut self, path: PathBuf) {
|
pub fn set_project_dir(&mut self, path: PathBuf) {
|
||||||
self.recent_projects.retain(|p| p != &path);
|
self.config.recent_projects.retain(|p| p != &path);
|
||||||
if self.recent_projects.len() > 9 {
|
if self.config.recent_projects.len() > 9 {
|
||||||
self.recent_projects.truncate(9);
|
self.config.recent_projects.truncate(9);
|
||||||
}
|
}
|
||||||
self.recent_projects.insert(0, path.clone());
|
self.config.recent_projects.insert(0, path.clone());
|
||||||
self.project_dir = Some(path);
|
self.config.project_dir = Some(path);
|
||||||
self.target_obj_dir = None;
|
self.config.target_obj_dir = None;
|
||||||
self.base_obj_dir = None;
|
self.config.base_obj_dir = None;
|
||||||
self.selected_obj = None;
|
self.config.selected_obj = None;
|
||||||
self.build_target = false;
|
self.config.build_target = false;
|
||||||
self.objects.clear();
|
self.objects.clear();
|
||||||
self.object_nodes.clear();
|
self.object_nodes.clear();
|
||||||
self.watcher_change = true;
|
self.watcher_change = true;
|
||||||
|
@ -187,33 +193,33 @@ impl AppConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_target_obj_dir(&mut self, path: PathBuf) {
|
pub fn set_target_obj_dir(&mut self, path: PathBuf) {
|
||||||
self.target_obj_dir = Some(path);
|
self.config.target_obj_dir = Some(path);
|
||||||
self.selected_obj = None;
|
self.config.selected_obj = None;
|
||||||
self.obj_change = true;
|
self.obj_change = true;
|
||||||
self.queue_build = false;
|
self.queue_build = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_base_obj_dir(&mut self, path: PathBuf) {
|
pub fn set_base_obj_dir(&mut self, path: PathBuf) {
|
||||||
self.base_obj_dir = Some(path);
|
self.config.base_obj_dir = Some(path);
|
||||||
self.selected_obj = None;
|
self.config.selected_obj = None;
|
||||||
self.obj_change = true;
|
self.obj_change = true;
|
||||||
self.queue_build = false;
|
self.queue_build = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_selected_obj(&mut self, object: ObjectConfig) {
|
pub fn set_selected_obj(&mut self, object: ObjectConfig) {
|
||||||
self.selected_obj = Some(object);
|
self.config.selected_obj = Some(object);
|
||||||
self.obj_change = true;
|
self.obj_change = true;
|
||||||
self.queue_build = false;
|
self.queue_build = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type AppConfigRef = Arc<RwLock<AppConfig>>;
|
pub type AppStateRef = Arc<RwLock<AppState>>;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct App {
|
pub struct App {
|
||||||
appearance: Appearance,
|
appearance: Appearance,
|
||||||
view_state: ViewState,
|
view_state: ViewState,
|
||||||
config: AppConfigRef,
|
state: AppStateRef,
|
||||||
modified: Arc<AtomicBool>,
|
modified: Arc<AtomicBool>,
|
||||||
watcher: Option<notify::RecommendedWatcher>,
|
watcher: Option<notify::RecommendedWatcher>,
|
||||||
app_path: Option<PathBuf>,
|
app_path: Option<PathBuf>,
|
||||||
|
@ -241,16 +247,17 @@ impl App {
|
||||||
if let Some(appearance) = eframe::get_value::<Appearance>(storage, APPEARANCE_KEY) {
|
if let Some(appearance) = eframe::get_value::<Appearance>(storage, APPEARANCE_KEY) {
|
||||||
app.appearance = appearance;
|
app.appearance = appearance;
|
||||||
}
|
}
|
||||||
if let Some(mut config) = deserialize_config(storage) {
|
if let Some(config) = deserialize_config(storage) {
|
||||||
if config.project_dir.is_some() {
|
let mut state = AppState { config, ..Default::default() };
|
||||||
config.config_change = true;
|
if state.config.project_dir.is_some() {
|
||||||
config.watcher_change = true;
|
state.config_change = true;
|
||||||
|
state.watcher_change = true;
|
||||||
}
|
}
|
||||||
if config.selected_obj.is_some() {
|
if state.config.selected_obj.is_some() {
|
||||||
config.queue_build = true;
|
state.queue_build = true;
|
||||||
}
|
}
|
||||||
app.view_state.config_state.queue_check_update = config.auto_update_check;
|
app.view_state.config_state.queue_check_update = state.config.auto_update_check;
|
||||||
app.config = Arc::new(RwLock::new(config));
|
app.state = Arc::new(RwLock::new(state));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
app.appearance.init_fonts(&cc.egui_ctx);
|
app.appearance.init_fonts(&cc.egui_ctx);
|
||||||
|
@ -336,8 +343,8 @@ impl App {
|
||||||
jobs.results.append(&mut results);
|
jobs.results.append(&mut results);
|
||||||
jobs.clear_finished();
|
jobs.clear_finished();
|
||||||
|
|
||||||
diff_state.pre_update(jobs, &self.config);
|
diff_state.pre_update(jobs, &self.state);
|
||||||
config_state.pre_update(jobs, &self.config);
|
config_state.pre_update(jobs, &self.state);
|
||||||
debug_assert!(jobs.results.is_empty());
|
debug_assert!(jobs.results.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,23 +352,23 @@ impl App {
|
||||||
self.appearance.post_update(ctx);
|
self.appearance.post_update(ctx);
|
||||||
|
|
||||||
let ViewState { jobs, diff_state, config_state, graphics_state, .. } = &mut self.view_state;
|
let ViewState { jobs, diff_state, config_state, graphics_state, .. } = &mut self.view_state;
|
||||||
config_state.post_update(ctx, jobs, &self.config);
|
config_state.post_update(ctx, jobs, &self.state);
|
||||||
diff_state.post_update(ctx, jobs, &self.config);
|
diff_state.post_update(ctx, jobs, &self.state);
|
||||||
|
|
||||||
let Ok(mut config) = self.config.write() else {
|
let Ok(mut state) = self.state.write() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let config = &mut *config;
|
let state = &mut *state;
|
||||||
|
|
||||||
if let Some(info) = &config.project_config_info {
|
if let Some(info) = &state.project_config_info {
|
||||||
if file_modified(&info.path, info.timestamp) {
|
if file_modified(&info.path, info.timestamp) {
|
||||||
config.config_change = true;
|
state.config_change = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.config_change {
|
if state.config_change {
|
||||||
config.config_change = false;
|
state.config_change = false;
|
||||||
match load_project_config(config) {
|
match load_project_config(state) {
|
||||||
Ok(()) => config_state.load_error = None,
|
Ok(()) => config_state.load_error = None,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Failed to load project config: {e}");
|
log::error!("Failed to load project config: {e}");
|
||||||
|
@ -370,47 +377,50 @@ impl App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.watcher_change {
|
if state.watcher_change {
|
||||||
drop(self.watcher.take());
|
drop(self.watcher.take());
|
||||||
|
|
||||||
if let Some(project_dir) = &config.project_dir {
|
if let Some(project_dir) = &state.config.project_dir {
|
||||||
match build_globset(&config.watch_patterns).map_err(anyhow::Error::new).and_then(
|
match build_globset(&state.config.watch_patterns)
|
||||||
|globset| {
|
.map_err(anyhow::Error::new)
|
||||||
|
.and_then(|globset| {
|
||||||
create_watcher(ctx.clone(), self.modified.clone(), project_dir, globset)
|
create_watcher(ctx.clone(), self.modified.clone(), project_dir, globset)
|
||||||
.map_err(anyhow::Error::new)
|
.map_err(anyhow::Error::new)
|
||||||
},
|
}) {
|
||||||
) {
|
|
||||||
Ok(watcher) => self.watcher = Some(watcher),
|
Ok(watcher) => self.watcher = Some(watcher),
|
||||||
Err(e) => log::error!("Failed to create watcher: {e}"),
|
Err(e) => log::error!("Failed to create watcher: {e}"),
|
||||||
}
|
}
|
||||||
config.watcher_change = false;
|
state.watcher_change = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.obj_change {
|
if state.obj_change {
|
||||||
*diff_state = Default::default();
|
*diff_state = Default::default();
|
||||||
if config.selected_obj.is_some() {
|
if state.config.selected_obj.is_some() {
|
||||||
config.queue_build = true;
|
state.queue_build = true;
|
||||||
}
|
}
|
||||||
config.obj_change = false;
|
state.obj_change = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.modified.swap(false, Ordering::Relaxed) && config.rebuild_on_changes {
|
if self.modified.swap(false, Ordering::Relaxed) && state.config.rebuild_on_changes {
|
||||||
config.queue_build = true;
|
state.queue_build = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(result) = &diff_state.build {
|
if let Some(result) = &diff_state.build {
|
||||||
if let Some((obj, _)) = &result.first_obj {
|
if state.last_mod_check.elapsed().as_millis() >= 500 {
|
||||||
if let (Some(path), Some(timestamp)) = (&obj.path, obj.timestamp) {
|
state.last_mod_check = Instant::now();
|
||||||
if file_modified(path, timestamp) {
|
if let Some((obj, _)) = &result.first_obj {
|
||||||
config.queue_reload = true;
|
if let (Some(path), Some(timestamp)) = (&obj.path, obj.timestamp) {
|
||||||
|
if file_modified(path, timestamp) {
|
||||||
|
state.queue_reload = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if let Some((obj, _)) = &result.second_obj {
|
||||||
if let Some((obj, _)) = &result.second_obj {
|
if let (Some(path), Some(timestamp)) = (&obj.path, obj.timestamp) {
|
||||||
if let (Some(path), Some(timestamp)) = (&obj.path, obj.timestamp) {
|
if file_modified(path, timestamp) {
|
||||||
if file_modified(path, timestamp) {
|
state.queue_reload = true;
|
||||||
config.queue_reload = true;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -418,17 +428,20 @@ impl App {
|
||||||
|
|
||||||
// Don't clear `queue_build` if a build is running. A file may have been modified during
|
// Don't clear `queue_build` if a build is running. A file may have been modified during
|
||||||
// the build, so we'll start another build after the current one finishes.
|
// the build, so we'll start another build after the current one finishes.
|
||||||
if config.queue_build && config.selected_obj.is_some() && !jobs.is_running(Job::ObjDiff) {
|
if state.queue_build
|
||||||
jobs.push(start_build(ctx, ObjDiffConfig::from_config(config)));
|
&& state.config.selected_obj.is_some()
|
||||||
config.queue_build = false;
|
&& !jobs.is_running(Job::ObjDiff)
|
||||||
config.queue_reload = false;
|
{
|
||||||
} else if config.queue_reload && !jobs.is_running(Job::ObjDiff) {
|
jobs.push(start_build(ctx, ObjDiffConfig::from_config(&state.config)));
|
||||||
let mut diff_config = ObjDiffConfig::from_config(config);
|
state.queue_build = false;
|
||||||
|
state.queue_reload = false;
|
||||||
|
} else if state.queue_reload && !jobs.is_running(Job::ObjDiff) {
|
||||||
|
let mut diff_config = ObjDiffConfig::from_config(&state.config);
|
||||||
// Don't build, just reload the current files
|
// Don't build, just reload the current files
|
||||||
diff_config.build_base = false;
|
diff_config.build_base = false;
|
||||||
diff_config.build_target = false;
|
diff_config.build_target = false;
|
||||||
jobs.push(start_build(ctx, diff_config));
|
jobs.push(start_build(ctx, diff_config));
|
||||||
config.queue_reload = false;
|
state.queue_reload = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if graphics_state.should_relaunch {
|
if graphics_state.should_relaunch {
|
||||||
|
@ -453,7 +466,7 @@ impl eframe::App for App {
|
||||||
|
|
||||||
self.pre_update(ctx);
|
self.pre_update(ctx);
|
||||||
|
|
||||||
let Self { config, appearance, view_state, .. } = self;
|
let Self { state, appearance, view_state, .. } = self;
|
||||||
let ViewState {
|
let ViewState {
|
||||||
jobs,
|
jobs,
|
||||||
config_state,
|
config_state,
|
||||||
|
@ -485,8 +498,8 @@ impl eframe::App for App {
|
||||||
*show_project_config = !*show_project_config;
|
*show_project_config = !*show_project_config;
|
||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
}
|
}
|
||||||
let recent_projects = if let Ok(guard) = config.read() {
|
let recent_projects = if let Ok(guard) = state.read() {
|
||||||
guard.recent_projects.clone()
|
guard.config.recent_projects.clone()
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
};
|
};
|
||||||
|
@ -495,12 +508,12 @@ impl eframe::App for App {
|
||||||
} else {
|
} else {
|
||||||
ui.menu_button("Recent Projects…", |ui| {
|
ui.menu_button("Recent Projects…", |ui| {
|
||||||
if ui.button("Clear").clicked() {
|
if ui.button("Clear").clicked() {
|
||||||
config.write().unwrap().recent_projects.clear();
|
state.write().unwrap().config.recent_projects.clear();
|
||||||
};
|
};
|
||||||
ui.separator();
|
ui.separator();
|
||||||
for path in recent_projects {
|
for path in recent_projects {
|
||||||
if ui.button(format!("{}", path.display())).clicked() {
|
if ui.button(format!("{}", path.display())).clicked() {
|
||||||
config.write().unwrap().set_project_dir(path);
|
state.write().unwrap().set_project_dir(path);
|
||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -533,12 +546,12 @@ impl eframe::App for App {
|
||||||
*show_arch_config = !*show_arch_config;
|
*show_arch_config = !*show_arch_config;
|
||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
}
|
}
|
||||||
let mut config = config.write().unwrap();
|
let mut state = state.write().unwrap();
|
||||||
let response = ui
|
let response = ui
|
||||||
.checkbox(&mut config.rebuild_on_changes, "Rebuild on changes")
|
.checkbox(&mut state.config.rebuild_on_changes, "Rebuild on changes")
|
||||||
.on_hover_text("Automatically re-run the build & diff when files change.");
|
.on_hover_text("Automatically re-run the build & diff when files change.");
|
||||||
if response.changed() {
|
if response.changed() {
|
||||||
config.watcher_change = true;
|
state.watcher_change = true;
|
||||||
};
|
};
|
||||||
ui.add_enabled(
|
ui.add_enabled(
|
||||||
!diff_state.symbol_state.disable_reverse_fn_order,
|
!diff_state.symbol_state.disable_reverse_fn_order,
|
||||||
|
@ -554,7 +567,7 @@ impl eframe::App for App {
|
||||||
);
|
);
|
||||||
if ui
|
if ui
|
||||||
.checkbox(
|
.checkbox(
|
||||||
&mut config.diff_obj_config.relax_reloc_diffs,
|
&mut state.config.diff_obj_config.relax_reloc_diffs,
|
||||||
"Relax relocation diffs",
|
"Relax relocation diffs",
|
||||||
)
|
)
|
||||||
.on_hover_text(
|
.on_hover_text(
|
||||||
|
@ -562,26 +575,26 @@ impl eframe::App for App {
|
||||||
)
|
)
|
||||||
.changed()
|
.changed()
|
||||||
{
|
{
|
||||||
config.queue_reload = true;
|
state.queue_reload = true;
|
||||||
}
|
}
|
||||||
if ui
|
if ui
|
||||||
.checkbox(
|
.checkbox(
|
||||||
&mut config.diff_obj_config.space_between_args,
|
&mut state.config.diff_obj_config.space_between_args,
|
||||||
"Space between args",
|
"Space between args",
|
||||||
)
|
)
|
||||||
.changed()
|
.changed()
|
||||||
{
|
{
|
||||||
config.queue_reload = true;
|
state.queue_reload = true;
|
||||||
}
|
}
|
||||||
if ui
|
if ui
|
||||||
.checkbox(
|
.checkbox(
|
||||||
&mut config.diff_obj_config.combine_data_sections,
|
&mut state.config.diff_obj_config.combine_data_sections,
|
||||||
"Combine data sections",
|
"Combine data sections",
|
||||||
)
|
)
|
||||||
.on_hover_text("Combines data sections with equal names.")
|
.on_hover_text("Combines data sections with equal names.")
|
||||||
.changed()
|
.changed()
|
||||||
{
|
{
|
||||||
config.queue_reload = true;
|
state.queue_reload = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -603,7 +616,7 @@ impl eframe::App for App {
|
||||||
} else {
|
} else {
|
||||||
egui::SidePanel::left("side_panel").show(ctx, |ui| {
|
egui::SidePanel::left("side_panel").show(ctx, |ui| {
|
||||||
egui::ScrollArea::both().show(ui, |ui| {
|
egui::ScrollArea::both().show(ui, |ui| {
|
||||||
config_ui(ui, config, show_project_config, config_state, appearance);
|
config_ui(ui, state, show_project_config, config_state, appearance);
|
||||||
jobs_ui(ui, jobs, appearance);
|
jobs_ui(ui, jobs, appearance);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -613,11 +626,11 @@ impl eframe::App for App {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
project_window(ctx, config, show_project_config, config_state, appearance);
|
project_window(ctx, state, show_project_config, config_state, appearance);
|
||||||
appearance_window(ctx, show_appearance_config, appearance);
|
appearance_window(ctx, show_appearance_config, appearance);
|
||||||
demangle_window(ctx, show_demangle, demangle_state, appearance);
|
demangle_window(ctx, show_demangle, demangle_state, appearance);
|
||||||
rlwinm_decode_window(ctx, show_rlwinm_decode, rlwinm_decode_state, appearance);
|
rlwinm_decode_window(ctx, show_rlwinm_decode, rlwinm_decode_state, appearance);
|
||||||
arch_config_window(ctx, config, show_arch_config, appearance);
|
arch_config_window(ctx, state, show_arch_config, appearance);
|
||||||
debug_window(ctx, show_debug, frame_history, appearance);
|
debug_window(ctx, show_debug, frame_history, appearance);
|
||||||
graphics_window(ctx, show_graphics, frame_history, graphics_state, appearance);
|
graphics_window(ctx, show_graphics, frame_history, graphics_state, appearance);
|
||||||
|
|
||||||
|
@ -626,8 +639,8 @@ impl eframe::App for App {
|
||||||
|
|
||||||
/// Called by the frame work to save state before shutdown.
|
/// Called by the frame work to save state before shutdown.
|
||||||
fn save(&mut self, storage: &mut dyn eframe::Storage) {
|
fn save(&mut self, storage: &mut dyn eframe::Storage) {
|
||||||
if let Ok(config) = self.config.read() {
|
if let Ok(state) = self.state.read() {
|
||||||
eframe::set_value(storage, CONFIG_KEY, &*config);
|
eframe::set_value(storage, CONFIG_KEY, &state.config);
|
||||||
}
|
}
|
||||||
eframe::set_value(storage, APPEARANCE_KEY, &self.appearance);
|
eframe::set_value(storage, APPEARANCE_KEY, &self.appearance);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use anyhow::Result;
|
||||||
use globset::Glob;
|
use globset::Glob;
|
||||||
use objdiff_core::config::{try_project_config, ProjectObject, DEFAULT_WATCH_PATTERNS};
|
use objdiff_core::config::{try_project_config, ProjectObject, DEFAULT_WATCH_PATTERNS};
|
||||||
|
|
||||||
use crate::app::AppConfig;
|
use crate::app::AppState;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum ProjectObjectNode {
|
pub enum ProjectObjectNode {
|
||||||
|
@ -64,30 +64,30 @@ fn build_nodes(
|
||||||
nodes
|
nodes
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_project_config(config: &mut AppConfig) -> Result<()> {
|
pub fn load_project_config(state: &mut AppState) -> Result<()> {
|
||||||
let Some(project_dir) = &config.project_dir else {
|
let Some(project_dir) = &state.config.project_dir else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
if let Some((result, info)) = try_project_config(project_dir) {
|
if let Some((result, info)) = try_project_config(project_dir) {
|
||||||
let project_config = result?;
|
let project_config = result?;
|
||||||
config.custom_make = project_config.custom_make;
|
state.config.custom_make = project_config.custom_make;
|
||||||
config.custom_args = project_config.custom_args;
|
state.config.custom_args = project_config.custom_args;
|
||||||
config.target_obj_dir = project_config.target_dir.map(|p| project_dir.join(p));
|
state.config.target_obj_dir = project_config.target_dir.map(|p| project_dir.join(p));
|
||||||
config.base_obj_dir = project_config.base_dir.map(|p| project_dir.join(p));
|
state.config.base_obj_dir = project_config.base_dir.map(|p| project_dir.join(p));
|
||||||
config.build_base = project_config.build_base;
|
state.config.build_base = project_config.build_base;
|
||||||
config.build_target = project_config.build_target;
|
state.config.build_target = project_config.build_target;
|
||||||
config.watch_patterns = project_config.watch_patterns.unwrap_or_else(|| {
|
state.config.watch_patterns = project_config.watch_patterns.unwrap_or_else(|| {
|
||||||
DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect()
|
DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect()
|
||||||
});
|
});
|
||||||
config.watcher_change = true;
|
state.watcher_change = true;
|
||||||
config.objects = project_config.objects;
|
state.objects = project_config.objects;
|
||||||
config.object_nodes = build_nodes(
|
state.object_nodes = build_nodes(
|
||||||
&config.objects,
|
&state.objects,
|
||||||
project_dir,
|
project_dir,
|
||||||
config.target_obj_dir.as_deref(),
|
state.config.target_obj_dir.as_deref(),
|
||||||
config.base_obj_dir.as_deref(),
|
state.config.base_obj_dir.as_deref(),
|
||||||
);
|
);
|
||||||
config.project_config_info = Some(info);
|
state.project_config_info = Some(info);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ use objdiff_core::{
|
||||||
use strum::{EnumMessage, VariantArray};
|
use strum::{EnumMessage, VariantArray};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{AppConfig, AppConfigRef, ObjectConfig},
|
app::{AppConfig, AppState, AppStateRef, ObjectConfig},
|
||||||
config::ProjectObjectNode,
|
config::ProjectObjectNode,
|
||||||
jobs::{
|
jobs::{
|
||||||
check_update::{start_check_update, CheckUpdateResult},
|
check_update::{start_check_update, CheckUpdateResult},
|
||||||
|
@ -54,7 +54,7 @@ pub struct ConfigViewState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConfigViewState {
|
impl ConfigViewState {
|
||||||
pub fn pre_update(&mut self, jobs: &mut JobQueue, config: &AppConfigRef) {
|
pub fn pre_update(&mut self, jobs: &mut JobQueue, state: &AppStateRef) {
|
||||||
jobs.results.retain_mut(|result| {
|
jobs.results.retain_mut(|result| {
|
||||||
if let JobResult::CheckUpdate(result) = result {
|
if let JobResult::CheckUpdate(result) = result {
|
||||||
self.check_update = take(result);
|
self.check_update = take(result);
|
||||||
|
@ -71,21 +71,21 @@ impl ConfigViewState {
|
||||||
match self.file_dialog_state.poll() {
|
match self.file_dialog_state.poll() {
|
||||||
FileDialogResult::None => {}
|
FileDialogResult::None => {}
|
||||||
FileDialogResult::ProjectDir(path) => {
|
FileDialogResult::ProjectDir(path) => {
|
||||||
let mut guard = config.write().unwrap();
|
let mut guard = state.write().unwrap();
|
||||||
guard.set_project_dir(path.to_path_buf());
|
guard.set_project_dir(path.to_path_buf());
|
||||||
}
|
}
|
||||||
FileDialogResult::TargetDir(path) => {
|
FileDialogResult::TargetDir(path) => {
|
||||||
let mut guard = config.write().unwrap();
|
let mut guard = state.write().unwrap();
|
||||||
guard.set_target_obj_dir(path.to_path_buf());
|
guard.set_target_obj_dir(path.to_path_buf());
|
||||||
}
|
}
|
||||||
FileDialogResult::BaseDir(path) => {
|
FileDialogResult::BaseDir(path) => {
|
||||||
let mut guard = config.write().unwrap();
|
let mut guard = state.write().unwrap();
|
||||||
guard.set_base_obj_dir(path.to_path_buf());
|
guard.set_base_obj_dir(path.to_path_buf());
|
||||||
}
|
}
|
||||||
FileDialogResult::Object(path) => {
|
FileDialogResult::Object(path) => {
|
||||||
let mut guard = config.write().unwrap();
|
let mut guard = state.write().unwrap();
|
||||||
if let (Some(base_dir), Some(target_dir)) =
|
if let (Some(base_dir), Some(target_dir)) =
|
||||||
(&guard.base_obj_dir, &guard.target_obj_dir)
|
(&guard.config.base_obj_dir, &guard.config.target_obj_dir)
|
||||||
{
|
{
|
||||||
if let Ok(obj_path) = path.strip_prefix(base_dir) {
|
if let Ok(obj_path) = path.strip_prefix(base_dir) {
|
||||||
let target_path = target_dir.join(obj_path);
|
let target_path = target_dir.join(obj_path);
|
||||||
|
@ -113,11 +113,11 @@ impl ConfigViewState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn post_update(&mut self, ctx: &egui::Context, jobs: &mut JobQueue, config: &AppConfigRef) {
|
pub fn post_update(&mut self, ctx: &egui::Context, jobs: &mut JobQueue, state: &AppStateRef) {
|
||||||
if self.queue_build {
|
if self.queue_build {
|
||||||
self.queue_build = false;
|
self.queue_build = false;
|
||||||
if let Ok(mut config) = config.write() {
|
if let Ok(mut state) = state.write() {
|
||||||
config.queue_build = true;
|
state.queue_build = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,42 +167,40 @@ fn fetch_wsl2_distros() -> Vec<String> {
|
||||||
|
|
||||||
pub fn config_ui(
|
pub fn config_ui(
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
config: &AppConfigRef,
|
state: &AppStateRef,
|
||||||
show_config_window: &mut bool,
|
show_config_window: &mut bool,
|
||||||
state: &mut ConfigViewState,
|
config_state: &mut ConfigViewState,
|
||||||
appearance: &Appearance,
|
appearance: &Appearance,
|
||||||
) {
|
) {
|
||||||
let mut config_guard = config.write().unwrap();
|
let mut state_guard = state.write().unwrap();
|
||||||
let AppConfig {
|
let AppState {
|
||||||
target_obj_dir,
|
config: AppConfig { target_obj_dir, base_obj_dir, selected_obj, auto_update_check, .. },
|
||||||
base_obj_dir,
|
|
||||||
selected_obj,
|
|
||||||
auto_update_check,
|
|
||||||
objects,
|
objects,
|
||||||
object_nodes,
|
object_nodes,
|
||||||
..
|
..
|
||||||
} = &mut *config_guard;
|
} = &mut *state_guard;
|
||||||
|
|
||||||
ui.heading("Updates");
|
ui.heading("Updates");
|
||||||
ui.checkbox(auto_update_check, "Check for updates on startup");
|
ui.checkbox(auto_update_check, "Check for updates on startup");
|
||||||
if ui.add_enabled(!state.check_update_running, egui::Button::new("Check now")).clicked() {
|
if ui.add_enabled(!config_state.check_update_running, egui::Button::new("Check now")).clicked()
|
||||||
state.queue_check_update = true;
|
{
|
||||||
|
config_state.queue_check_update = true;
|
||||||
}
|
}
|
||||||
ui.label(format!("Current version: {}", env!("CARGO_PKG_VERSION")));
|
ui.label(format!("Current version: {}", env!("CARGO_PKG_VERSION")));
|
||||||
if let Some(result) = &state.check_update {
|
if let Some(result) = &config_state.check_update {
|
||||||
ui.label(format!("Latest version: {}", result.latest_release.version));
|
ui.label(format!("Latest version: {}", result.latest_release.version));
|
||||||
if result.update_available {
|
if result.update_available {
|
||||||
ui.colored_label(appearance.insert_color, "Update available");
|
ui.colored_label(appearance.insert_color, "Update available");
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
if let Some(bin_name) = &result.found_binary {
|
if let Some(bin_name) = &result.found_binary {
|
||||||
if ui
|
if ui
|
||||||
.add_enabled(!state.update_running, egui::Button::new("Automatic"))
|
.add_enabled(!config_state.update_running, egui::Button::new("Automatic"))
|
||||||
.on_hover_text_at_pointer(
|
.on_hover_text_at_pointer(
|
||||||
"Automatically download and replace the current build",
|
"Automatically download and replace the current build",
|
||||||
)
|
)
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
state.queue_update = Some(bin_name.clone());
|
config_state.queue_update = Some(bin_name.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ui
|
if ui
|
||||||
|
@ -231,7 +229,7 @@ pub fn config_ui(
|
||||||
if objects.is_empty() {
|
if objects.is_empty() {
|
||||||
if let (Some(_base_dir), Some(target_dir)) = (base_obj_dir, target_obj_dir) {
|
if let (Some(_base_dir), Some(target_dir)) = (base_obj_dir, target_obj_dir) {
|
||||||
if ui.button("Select object").clicked() {
|
if ui.button("Select object").clicked() {
|
||||||
state.file_dialog_state.queue(
|
config_state.file_dialog_state.queue(
|
||||||
|| {
|
|| {
|
||||||
Box::pin(
|
Box::pin(
|
||||||
rfd::AsyncFileDialog::new()
|
rfd::AsyncFileDialog::new()
|
||||||
|
@ -254,8 +252,8 @@ pub fn config_ui(
|
||||||
ui.colored_label(appearance.delete_color, "Missing project settings");
|
ui.colored_label(appearance.delete_color, "Missing project settings");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let had_search = !state.object_search.is_empty();
|
let had_search = !config_state.object_search.is_empty();
|
||||||
egui::TextEdit::singleline(&mut state.object_search).hint_text("Filter").ui(ui);
|
egui::TextEdit::singleline(&mut config_state.object_search).hint_text("Filter").ui(ui);
|
||||||
|
|
||||||
let mut root_open = None;
|
let mut root_open = None;
|
||||||
let mut node_open = NodeOpen::Default;
|
let mut node_open = NodeOpen::Default;
|
||||||
|
@ -277,19 +275,22 @@ pub fn config_ui(
|
||||||
node_open = NodeOpen::Object;
|
node_open = NodeOpen::Object;
|
||||||
}
|
}
|
||||||
let mut filters_text = RichText::new("Filter ⏷");
|
let mut filters_text = RichText::new("Filter ⏷");
|
||||||
if state.filter_diffable || state.filter_incomplete || state.show_hidden {
|
if config_state.filter_diffable
|
||||||
|
|| config_state.filter_incomplete
|
||||||
|
|| config_state.show_hidden
|
||||||
|
{
|
||||||
filters_text = filters_text.color(appearance.replace_color);
|
filters_text = filters_text.color(appearance.replace_color);
|
||||||
}
|
}
|
||||||
egui::menu::menu_button(ui, filters_text, |ui| {
|
egui::menu::menu_button(ui, filters_text, |ui| {
|
||||||
ui.checkbox(&mut state.filter_diffable, "Diffable")
|
ui.checkbox(&mut config_state.filter_diffable, "Diffable")
|
||||||
.on_hover_text_at_pointer("Only show objects with a source file");
|
.on_hover_text_at_pointer("Only show objects with a source file");
|
||||||
ui.checkbox(&mut state.filter_incomplete, "Incomplete")
|
ui.checkbox(&mut config_state.filter_incomplete, "Incomplete")
|
||||||
.on_hover_text_at_pointer("Only show objects not marked complete");
|
.on_hover_text_at_pointer("Only show objects not marked complete");
|
||||||
ui.checkbox(&mut state.show_hidden, "Hidden")
|
ui.checkbox(&mut config_state.show_hidden, "Hidden")
|
||||||
.on_hover_text_at_pointer("Show hidden (auto-generated) objects");
|
.on_hover_text_at_pointer("Show hidden (auto-generated) objects");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
if state.object_search.is_empty() {
|
if config_state.object_search.is_empty() {
|
||||||
if had_search {
|
if had_search {
|
||||||
root_open = Some(true);
|
root_open = Some(true);
|
||||||
node_open = NodeOpen::Object;
|
node_open = NodeOpen::Object;
|
||||||
|
@ -306,15 +307,15 @@ pub fn config_ui(
|
||||||
.open(root_open)
|
.open(root_open)
|
||||||
.default_open(true)
|
.default_open(true)
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
let search = state.object_search.to_ascii_lowercase();
|
let search = config_state.object_search.to_ascii_lowercase();
|
||||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||||
for node in object_nodes.iter().filter_map(|node| {
|
for node in object_nodes.iter().filter_map(|node| {
|
||||||
filter_node(
|
filter_node(
|
||||||
node,
|
node,
|
||||||
&search,
|
&search,
|
||||||
state.filter_diffable,
|
config_state.filter_diffable,
|
||||||
state.filter_incomplete,
|
config_state.filter_incomplete,
|
||||||
state.show_hidden,
|
config_state.show_hidden,
|
||||||
)
|
)
|
||||||
}) {
|
}) {
|
||||||
display_node(ui, &mut new_selected_obj, &node, appearance, node_open);
|
display_node(ui, &mut new_selected_obj, &node, appearance, node_open);
|
||||||
|
@ -324,13 +325,13 @@ pub fn config_ui(
|
||||||
if new_selected_obj != *selected_obj {
|
if new_selected_obj != *selected_obj {
|
||||||
if let Some(obj) = new_selected_obj {
|
if let Some(obj) = new_selected_obj {
|
||||||
// Will set obj_changed, which will trigger a rebuild
|
// Will set obj_changed, which will trigger a rebuild
|
||||||
config_guard.set_selected_obj(obj);
|
state_guard.set_selected_obj(obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if config_guard.selected_obj.is_some()
|
if state_guard.config.selected_obj.is_some()
|
||||||
&& ui.add_enabled(!state.build_running, egui::Button::new("Build")).clicked()
|
&& ui.add_enabled(!config_state.build_running, egui::Button::new("Build")).clicked()
|
||||||
{
|
{
|
||||||
state.queue_build = true;
|
config_state.queue_build = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
@ -523,33 +524,33 @@ fn pick_folder_ui(
|
||||||
|
|
||||||
pub fn project_window(
|
pub fn project_window(
|
||||||
ctx: &egui::Context,
|
ctx: &egui::Context,
|
||||||
config: &AppConfigRef,
|
state: &AppStateRef,
|
||||||
show: &mut bool,
|
show: &mut bool,
|
||||||
state: &mut ConfigViewState,
|
config_state: &mut ConfigViewState,
|
||||||
appearance: &Appearance,
|
appearance: &Appearance,
|
||||||
) {
|
) {
|
||||||
let mut config_guard = config.write().unwrap();
|
let mut state_guard = state.write().unwrap();
|
||||||
|
|
||||||
egui::Window::new("Project").open(show).show(ctx, |ui| {
|
egui::Window::new("Project").open(show).show(ctx, |ui| {
|
||||||
split_obj_config_ui(ui, &mut config_guard, state, appearance);
|
split_obj_config_ui(ui, &mut state_guard, config_state, appearance);
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(error) = &state.load_error {
|
if let Some(error) = &config_state.load_error {
|
||||||
let mut open = true;
|
let mut open = true;
|
||||||
egui::Window::new("Error").open(&mut open).show(ctx, |ui| {
|
egui::Window::new("Error").open(&mut open).show(ctx, |ui| {
|
||||||
ui.label("Failed to load project config:");
|
ui.label("Failed to load project config:");
|
||||||
ui.colored_label(appearance.delete_color, error);
|
ui.colored_label(appearance.delete_color, error);
|
||||||
});
|
});
|
||||||
if !open {
|
if !open {
|
||||||
state.load_error = None;
|
config_state.load_error = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_obj_config_ui(
|
fn split_obj_config_ui(
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
config: &mut AppConfig,
|
state: &mut AppState,
|
||||||
state: &mut ConfigViewState,
|
config_state: &mut ConfigViewState,
|
||||||
appearance: &Appearance,
|
appearance: &Appearance,
|
||||||
) {
|
) {
|
||||||
let text_format = TextFormat::simple(appearance.ui_font.clone(), appearance.text_color);
|
let text_format = TextFormat::simple(appearance.ui_font.clone(), appearance.text_color);
|
||||||
|
@ -560,7 +561,7 @@ fn split_obj_config_ui(
|
||||||
|
|
||||||
let response = pick_folder_ui(
|
let response = pick_folder_ui(
|
||||||
ui,
|
ui,
|
||||||
&config.project_dir,
|
&state.config.project_dir,
|
||||||
"Project directory",
|
"Project directory",
|
||||||
|ui| {
|
|ui| {
|
||||||
let mut job = LayoutJob::default();
|
let mut job = LayoutJob::default();
|
||||||
|
@ -576,7 +577,7 @@ fn split_obj_config_ui(
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
if response.clicked() {
|
if response.clicked() {
|
||||||
state.file_dialog_state.queue(
|
config_state.file_dialog_state.queue(
|
||||||
|| Box::pin(rfd::AsyncFileDialog::new().pick_folder()),
|
|| Box::pin(rfd::AsyncFileDialog::new().pick_folder()),
|
||||||
FileDialogResult::ProjectDir,
|
FileDialogResult::ProjectDir,
|
||||||
);
|
);
|
||||||
|
@ -605,33 +606,35 @@ fn split_obj_config_ui(
|
||||||
ui.label(job);
|
ui.label(job);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
let mut custom_make_str = config.custom_make.clone().unwrap_or_default();
|
let mut custom_make_str = state.config.custom_make.clone().unwrap_or_default();
|
||||||
if ui
|
if ui
|
||||||
.add_enabled(
|
.add_enabled(
|
||||||
config.project_config_info.is_none(),
|
state.project_config_info.is_none(),
|
||||||
egui::TextEdit::singleline(&mut custom_make_str).hint_text("make"),
|
egui::TextEdit::singleline(&mut custom_make_str).hint_text("make"),
|
||||||
)
|
)
|
||||||
.on_disabled_hover_text(CONFIG_DISABLED_TEXT)
|
.on_disabled_hover_text(CONFIG_DISABLED_TEXT)
|
||||||
.changed()
|
.changed()
|
||||||
{
|
{
|
||||||
if custom_make_str.is_empty() {
|
if custom_make_str.is_empty() {
|
||||||
config.custom_make = None;
|
state.config.custom_make = None;
|
||||||
} else {
|
} else {
|
||||||
config.custom_make = Some(custom_make_str);
|
state.config.custom_make = Some(custom_make_str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(all(windows, feature = "wsl"))]
|
#[cfg(all(windows, feature = "wsl"))]
|
||||||
{
|
{
|
||||||
if state.available_wsl_distros.is_none() {
|
if config_state.available_wsl_distros.is_none() {
|
||||||
state.available_wsl_distros = Some(fetch_wsl2_distros());
|
config_state.available_wsl_distros = Some(fetch_wsl2_distros());
|
||||||
}
|
}
|
||||||
egui::ComboBox::from_label("Run in WSL2")
|
egui::ComboBox::from_label("Run in WSL2")
|
||||||
.selected_text(config.selected_wsl_distro.as_ref().unwrap_or(&"Disabled".to_string()))
|
.selected_text(
|
||||||
|
state.config.selected_wsl_distro.as_ref().unwrap_or(&"Disabled".to_string()),
|
||||||
|
)
|
||||||
.show_ui(ui, |ui| {
|
.show_ui(ui, |ui| {
|
||||||
ui.selectable_value(&mut config.selected_wsl_distro, None, "Disabled");
|
ui.selectable_value(&mut state.config.selected_wsl_distro, None, "Disabled");
|
||||||
for distro in state.available_wsl_distros.as_ref().unwrap() {
|
for distro in config_state.available_wsl_distros.as_ref().unwrap() {
|
||||||
ui.selectable_value(
|
ui.selectable_value(
|
||||||
&mut config.selected_wsl_distro,
|
&mut state.config.selected_wsl_distro,
|
||||||
Some(distro.clone()),
|
Some(distro.clone()),
|
||||||
distro,
|
distro,
|
||||||
);
|
);
|
||||||
|
@ -640,10 +643,10 @@ fn split_obj_config_ui(
|
||||||
}
|
}
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
if let Some(project_dir) = config.project_dir.clone() {
|
if let Some(project_dir) = state.config.project_dir.clone() {
|
||||||
let response = pick_folder_ui(
|
let response = pick_folder_ui(
|
||||||
ui,
|
ui,
|
||||||
&config.target_obj_dir,
|
&state.config.target_obj_dir,
|
||||||
"Target build directory",
|
"Target build directory",
|
||||||
|ui| {
|
|ui| {
|
||||||
let mut job = LayoutJob::default();
|
let mut job = LayoutJob::default();
|
||||||
|
@ -660,17 +663,17 @@ fn split_obj_config_ui(
|
||||||
ui.label(job);
|
ui.label(job);
|
||||||
},
|
},
|
||||||
appearance,
|
appearance,
|
||||||
config.project_config_info.is_none(),
|
state.project_config_info.is_none(),
|
||||||
);
|
);
|
||||||
if response.clicked() {
|
if response.clicked() {
|
||||||
state.file_dialog_state.queue(
|
config_state.file_dialog_state.queue(
|
||||||
|| Box::pin(rfd::AsyncFileDialog::new().set_directory(&project_dir).pick_folder()),
|
|| Box::pin(rfd::AsyncFileDialog::new().set_directory(&project_dir).pick_folder()),
|
||||||
FileDialogResult::TargetDir,
|
FileDialogResult::TargetDir,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ui.add_enabled(
|
ui.add_enabled(
|
||||||
config.project_config_info.is_none(),
|
state.project_config_info.is_none(),
|
||||||
egui::Checkbox::new(&mut config.build_target, "Build target objects"),
|
egui::Checkbox::new(&mut state.config.build_target, "Build target objects"),
|
||||||
)
|
)
|
||||||
.on_disabled_hover_text(CONFIG_DISABLED_TEXT)
|
.on_disabled_hover_text(CONFIG_DISABLED_TEXT)
|
||||||
.on_hover_ui(|ui| {
|
.on_hover_ui(|ui| {
|
||||||
|
@ -704,7 +707,7 @@ fn split_obj_config_ui(
|
||||||
|
|
||||||
let response = pick_folder_ui(
|
let response = pick_folder_ui(
|
||||||
ui,
|
ui,
|
||||||
&config.base_obj_dir,
|
&state.config.base_obj_dir,
|
||||||
"Base build directory",
|
"Base build directory",
|
||||||
|ui| {
|
|ui| {
|
||||||
let mut job = LayoutJob::default();
|
let mut job = LayoutJob::default();
|
||||||
|
@ -716,17 +719,17 @@ fn split_obj_config_ui(
|
||||||
ui.label(job);
|
ui.label(job);
|
||||||
},
|
},
|
||||||
appearance,
|
appearance,
|
||||||
config.project_config_info.is_none(),
|
state.project_config_info.is_none(),
|
||||||
);
|
);
|
||||||
if response.clicked() {
|
if response.clicked() {
|
||||||
state.file_dialog_state.queue(
|
config_state.file_dialog_state.queue(
|
||||||
|| Box::pin(rfd::AsyncFileDialog::new().set_directory(&project_dir).pick_folder()),
|
|| Box::pin(rfd::AsyncFileDialog::new().set_directory(&project_dir).pick_folder()),
|
||||||
FileDialogResult::BaseDir,
|
FileDialogResult::BaseDir,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ui.add_enabled(
|
ui.add_enabled(
|
||||||
config.project_config_info.is_none(),
|
state.project_config_info.is_none(),
|
||||||
egui::Checkbox::new(&mut config.build_base, "Build base objects"),
|
egui::Checkbox::new(&mut state.config.build_base, "Build base objects"),
|
||||||
)
|
)
|
||||||
.on_disabled_hover_text(CONFIG_DISABLED_TEXT)
|
.on_disabled_hover_text(CONFIG_DISABLED_TEXT)
|
||||||
.on_hover_ui(|ui| {
|
.on_hover_ui(|ui| {
|
||||||
|
@ -757,7 +760,7 @@ fn split_obj_config_ui(
|
||||||
|
|
||||||
subheading(ui, "Watch settings", appearance);
|
subheading(ui, "Watch settings", appearance);
|
||||||
let response =
|
let response =
|
||||||
ui.checkbox(&mut config.rebuild_on_changes, "Rebuild on changes").on_hover_ui(|ui| {
|
ui.checkbox(&mut state.config.rebuild_on_changes, "Rebuild on changes").on_hover_ui(|ui| {
|
||||||
let mut job = LayoutJob::default();
|
let mut job = LayoutJob::default();
|
||||||
job.append(
|
job.append(
|
||||||
"Automatically re-run the build & diff when files change.",
|
"Automatically re-run the build & diff when files change.",
|
||||||
|
@ -767,23 +770,23 @@ fn split_obj_config_ui(
|
||||||
ui.label(job);
|
ui.label(job);
|
||||||
});
|
});
|
||||||
if response.changed() {
|
if response.changed() {
|
||||||
config.watcher_change = true;
|
state.watcher_change = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label(RichText::new("File patterns").color(appearance.text_color));
|
ui.label(RichText::new("File patterns").color(appearance.text_color));
|
||||||
if ui
|
if ui
|
||||||
.add_enabled(config.project_config_info.is_none(), egui::Button::new("Reset"))
|
.add_enabled(state.project_config_info.is_none(), egui::Button::new("Reset"))
|
||||||
.on_disabled_hover_text(CONFIG_DISABLED_TEXT)
|
.on_disabled_hover_text(CONFIG_DISABLED_TEXT)
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
config.watch_patterns =
|
state.config.watch_patterns =
|
||||||
DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect();
|
DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect();
|
||||||
config.watcher_change = true;
|
state.watcher_change = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let mut remove_at: Option<usize> = None;
|
let mut remove_at: Option<usize> = None;
|
||||||
for (idx, glob) in config.watch_patterns.iter().enumerate() {
|
for (idx, glob) in state.config.watch_patterns.iter().enumerate() {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label(
|
ui.label(
|
||||||
RichText::new(format!("{}", glob))
|
RichText::new(format!("{}", glob))
|
||||||
|
@ -791,7 +794,7 @@ fn split_obj_config_ui(
|
||||||
.family(FontFamily::Monospace),
|
.family(FontFamily::Monospace),
|
||||||
);
|
);
|
||||||
if ui
|
if ui
|
||||||
.add_enabled(config.project_config_info.is_none(), egui::Button::new("-").small())
|
.add_enabled(state.project_config_info.is_none(), egui::Button::new("-").small())
|
||||||
.on_disabled_hover_text(CONFIG_DISABLED_TEXT)
|
.on_disabled_hover_text(CONFIG_DISABLED_TEXT)
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
|
@ -800,24 +803,24 @@ fn split_obj_config_ui(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if let Some(idx) = remove_at {
|
if let Some(idx) = remove_at {
|
||||||
config.watch_patterns.remove(idx);
|
state.config.watch_patterns.remove(idx);
|
||||||
config.watcher_change = true;
|
state.watcher_change = true;
|
||||||
}
|
}
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.add_enabled(
|
ui.add_enabled(
|
||||||
config.project_config_info.is_none(),
|
state.project_config_info.is_none(),
|
||||||
egui::TextEdit::singleline(&mut state.watch_pattern_text).desired_width(100.0),
|
egui::TextEdit::singleline(&mut config_state.watch_pattern_text).desired_width(100.0),
|
||||||
)
|
)
|
||||||
.on_disabled_hover_text(CONFIG_DISABLED_TEXT);
|
.on_disabled_hover_text(CONFIG_DISABLED_TEXT);
|
||||||
if ui
|
if ui
|
||||||
.add_enabled(config.project_config_info.is_none(), egui::Button::new("+").small())
|
.add_enabled(state.project_config_info.is_none(), egui::Button::new("+").small())
|
||||||
.on_disabled_hover_text(CONFIG_DISABLED_TEXT)
|
.on_disabled_hover_text(CONFIG_DISABLED_TEXT)
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
if let Ok(glob) = Glob::new(&state.watch_pattern_text) {
|
if let Ok(glob) = Glob::new(&config_state.watch_pattern_text) {
|
||||||
config.watch_patterns.push(glob);
|
state.config.watch_patterns.push(glob);
|
||||||
config.watcher_change = true;
|
state.watcher_change = true;
|
||||||
state.watch_pattern_text.clear();
|
config_state.watch_pattern_text.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -825,131 +828,131 @@ fn split_obj_config_ui(
|
||||||
|
|
||||||
pub fn arch_config_window(
|
pub fn arch_config_window(
|
||||||
ctx: &egui::Context,
|
ctx: &egui::Context,
|
||||||
config: &AppConfigRef,
|
state: &AppStateRef,
|
||||||
show: &mut bool,
|
show: &mut bool,
|
||||||
appearance: &Appearance,
|
appearance: &Appearance,
|
||||||
) {
|
) {
|
||||||
let mut config_guard = config.write().unwrap();
|
let mut state_guard = state.write().unwrap();
|
||||||
egui::Window::new("Arch Settings").open(show).show(ctx, |ui| {
|
egui::Window::new("Arch Settings").open(show).show(ctx, |ui| {
|
||||||
arch_config_ui(ui, &mut config_guard, appearance);
|
arch_config_ui(ui, &mut state_guard, appearance);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn arch_config_ui(ui: &mut egui::Ui, config: &mut AppConfig, _appearance: &Appearance) {
|
fn arch_config_ui(ui: &mut egui::Ui, state: &mut AppState, _appearance: &Appearance) {
|
||||||
ui.heading("x86");
|
ui.heading("x86");
|
||||||
egui::ComboBox::new("x86_formatter", "Format")
|
egui::ComboBox::new("x86_formatter", "Format")
|
||||||
.selected_text(config.diff_obj_config.x86_formatter.get_message().unwrap())
|
.selected_text(state.config.diff_obj_config.x86_formatter.get_message().unwrap())
|
||||||
.show_ui(ui, |ui| {
|
.show_ui(ui, |ui| {
|
||||||
for &formatter in X86Formatter::VARIANTS {
|
for &formatter in X86Formatter::VARIANTS {
|
||||||
if ui
|
if ui
|
||||||
.selectable_label(
|
.selectable_label(
|
||||||
config.diff_obj_config.x86_formatter == formatter,
|
state.config.diff_obj_config.x86_formatter == formatter,
|
||||||
formatter.get_message().unwrap(),
|
formatter.get_message().unwrap(),
|
||||||
)
|
)
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
config.diff_obj_config.x86_formatter = formatter;
|
state.config.diff_obj_config.x86_formatter = formatter;
|
||||||
config.queue_reload = true;
|
state.queue_reload = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.heading("MIPS");
|
ui.heading("MIPS");
|
||||||
egui::ComboBox::new("mips_abi", "ABI")
|
egui::ComboBox::new("mips_abi", "ABI")
|
||||||
.selected_text(config.diff_obj_config.mips_abi.get_message().unwrap())
|
.selected_text(state.config.diff_obj_config.mips_abi.get_message().unwrap())
|
||||||
.show_ui(ui, |ui| {
|
.show_ui(ui, |ui| {
|
||||||
for &abi in MipsAbi::VARIANTS {
|
for &abi in MipsAbi::VARIANTS {
|
||||||
if ui
|
if ui
|
||||||
.selectable_label(
|
.selectable_label(
|
||||||
config.diff_obj_config.mips_abi == abi,
|
state.config.diff_obj_config.mips_abi == abi,
|
||||||
abi.get_message().unwrap(),
|
abi.get_message().unwrap(),
|
||||||
)
|
)
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
config.diff_obj_config.mips_abi = abi;
|
state.config.diff_obj_config.mips_abi = abi;
|
||||||
config.queue_reload = true;
|
state.queue_reload = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
egui::ComboBox::new("mips_instr_category", "Instruction Category")
|
egui::ComboBox::new("mips_instr_category", "Instruction Category")
|
||||||
.selected_text(config.diff_obj_config.mips_instr_category.get_message().unwrap())
|
.selected_text(state.config.diff_obj_config.mips_instr_category.get_message().unwrap())
|
||||||
.show_ui(ui, |ui| {
|
.show_ui(ui, |ui| {
|
||||||
for &category in MipsInstrCategory::VARIANTS {
|
for &category in MipsInstrCategory::VARIANTS {
|
||||||
if ui
|
if ui
|
||||||
.selectable_label(
|
.selectable_label(
|
||||||
config.diff_obj_config.mips_instr_category == category,
|
state.config.diff_obj_config.mips_instr_category == category,
|
||||||
category.get_message().unwrap(),
|
category.get_message().unwrap(),
|
||||||
)
|
)
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
config.diff_obj_config.mips_instr_category = category;
|
state.config.diff_obj_config.mips_instr_category = category;
|
||||||
config.queue_reload = true;
|
state.queue_reload = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.heading("ARM");
|
ui.heading("ARM");
|
||||||
egui::ComboBox::new("arm_arch_version", "Architecture Version")
|
egui::ComboBox::new("arm_arch_version", "Architecture Version")
|
||||||
.selected_text(config.diff_obj_config.arm_arch_version.get_message().unwrap())
|
.selected_text(state.config.diff_obj_config.arm_arch_version.get_message().unwrap())
|
||||||
.show_ui(ui, |ui| {
|
.show_ui(ui, |ui| {
|
||||||
for &version in ArmArchVersion::VARIANTS {
|
for &version in ArmArchVersion::VARIANTS {
|
||||||
if ui
|
if ui
|
||||||
.selectable_label(
|
.selectable_label(
|
||||||
config.diff_obj_config.arm_arch_version == version,
|
state.config.diff_obj_config.arm_arch_version == version,
|
||||||
version.get_message().unwrap(),
|
version.get_message().unwrap(),
|
||||||
)
|
)
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
config.diff_obj_config.arm_arch_version = version;
|
state.config.diff_obj_config.arm_arch_version = version;
|
||||||
config.queue_reload = true;
|
state.queue_reload = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let response = ui
|
let response = ui
|
||||||
.checkbox(&mut config.diff_obj_config.arm_unified_syntax, "Unified syntax")
|
.checkbox(&mut state.config.diff_obj_config.arm_unified_syntax, "Unified syntax")
|
||||||
.on_hover_text("Disassemble as unified assembly language (UAL).");
|
.on_hover_text("Disassemble as unified assembly language (UAL).");
|
||||||
if response.changed() {
|
if response.changed() {
|
||||||
config.queue_reload = true;
|
state.queue_reload = true;
|
||||||
}
|
}
|
||||||
let response = ui
|
let response = ui
|
||||||
.checkbox(&mut config.diff_obj_config.arm_av_registers, "Use A/V registers")
|
.checkbox(&mut state.config.diff_obj_config.arm_av_registers, "Use A/V registers")
|
||||||
.on_hover_text("Display R0-R3 as A1-A4 and R4-R11 as V1-V8");
|
.on_hover_text("Display R0-R3 as A1-A4 and R4-R11 as V1-V8");
|
||||||
if response.changed() {
|
if response.changed() {
|
||||||
config.queue_reload = true;
|
state.queue_reload = true;
|
||||||
}
|
}
|
||||||
egui::ComboBox::new("arm_r9_usage", "Display R9 as")
|
egui::ComboBox::new("arm_r9_usage", "Display R9 as")
|
||||||
.selected_text(config.diff_obj_config.arm_r9_usage.get_message().unwrap())
|
.selected_text(state.config.diff_obj_config.arm_r9_usage.get_message().unwrap())
|
||||||
.show_ui(ui, |ui| {
|
.show_ui(ui, |ui| {
|
||||||
for &usage in ArmR9Usage::VARIANTS {
|
for &usage in ArmR9Usage::VARIANTS {
|
||||||
if ui
|
if ui
|
||||||
.selectable_label(
|
.selectable_label(
|
||||||
config.diff_obj_config.arm_r9_usage == usage,
|
state.config.diff_obj_config.arm_r9_usage == usage,
|
||||||
usage.get_message().unwrap(),
|
usage.get_message().unwrap(),
|
||||||
)
|
)
|
||||||
.on_hover_text(usage.get_detailed_message().unwrap())
|
.on_hover_text(usage.get_detailed_message().unwrap())
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
config.diff_obj_config.arm_r9_usage = usage;
|
state.config.diff_obj_config.arm_r9_usage = usage;
|
||||||
config.queue_reload = true;
|
state.queue_reload = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let response = ui
|
let response = ui
|
||||||
.checkbox(&mut config.diff_obj_config.arm_sl_usage, "Display R10 as SL")
|
.checkbox(&mut state.config.diff_obj_config.arm_sl_usage, "Display R10 as SL")
|
||||||
.on_hover_text("Used for explicit stack limits.");
|
.on_hover_text("Used for explicit stack limits.");
|
||||||
if response.changed() {
|
if response.changed() {
|
||||||
config.queue_reload = true;
|
state.queue_reload = true;
|
||||||
}
|
}
|
||||||
let response = ui
|
let response = ui
|
||||||
.checkbox(&mut config.diff_obj_config.arm_fp_usage, "Display R11 as FP")
|
.checkbox(&mut state.config.diff_obj_config.arm_fp_usage, "Display R11 as FP")
|
||||||
.on_hover_text("Used for frame pointers.");
|
.on_hover_text("Used for frame pointers.");
|
||||||
if response.changed() {
|
if response.changed() {
|
||||||
config.queue_reload = true;
|
state.queue_reload = true;
|
||||||
}
|
}
|
||||||
let response = ui
|
let response = ui
|
||||||
.checkbox(&mut config.diff_obj_config.arm_ip_usage, "Display R12 as IP")
|
.checkbox(&mut state.config.diff_obj_config.arm_ip_usage, "Display R12 as IP")
|
||||||
.on_hover_text("Used for interworking and long branches.");
|
.on_hover_text("Used for interworking and long branches.");
|
||||||
if response.changed() {
|
if response.changed() {
|
||||||
config.queue_reload = true;
|
state.queue_reload = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ use objdiff_core::{
|
||||||
use regex::{Regex, RegexBuilder};
|
use regex::{Regex, RegexBuilder};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::AppConfigRef,
|
app::AppStateRef,
|
||||||
jobs::{
|
jobs::{
|
||||||
create_scratch::{start_create_scratch, CreateScratchConfig, CreateScratchResult},
|
create_scratch::{start_create_scratch, CreateScratchConfig, CreateScratchResult},
|
||||||
objdiff::{BuildStatus, ObjDiffResult},
|
objdiff::{BuildStatus, ObjDiffResult},
|
||||||
|
@ -64,7 +64,7 @@ pub struct SymbolViewState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DiffViewState {
|
impl DiffViewState {
|
||||||
pub fn pre_update(&mut self, jobs: &mut JobQueue, config: &AppConfigRef) {
|
pub fn pre_update(&mut self, jobs: &mut JobQueue, state: &AppStateRef) {
|
||||||
jobs.results.retain_mut(|result| match result {
|
jobs.results.retain_mut(|result| match result {
|
||||||
JobResult::ObjDiff(result) => {
|
JobResult::ObjDiff(result) => {
|
||||||
self.build = take(result);
|
self.build = take(result);
|
||||||
|
@ -80,26 +80,26 @@ impl DiffViewState {
|
||||||
self.scratch_running = jobs.is_running(Job::CreateScratch);
|
self.scratch_running = jobs.is_running(Job::CreateScratch);
|
||||||
|
|
||||||
self.symbol_state.disable_reverse_fn_order = false;
|
self.symbol_state.disable_reverse_fn_order = false;
|
||||||
if let Ok(config) = config.read() {
|
if let Ok(state) = state.read() {
|
||||||
if let Some(obj_config) = &config.selected_obj {
|
if let Some(obj_config) = &state.config.selected_obj {
|
||||||
if let Some(value) = obj_config.reverse_fn_order {
|
if let Some(value) = obj_config.reverse_fn_order {
|
||||||
self.symbol_state.reverse_fn_order = value;
|
self.symbol_state.reverse_fn_order = value;
|
||||||
self.symbol_state.disable_reverse_fn_order = true;
|
self.symbol_state.disable_reverse_fn_order = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.scratch_available = CreateScratchConfig::is_available(&config);
|
self.scratch_available = CreateScratchConfig::is_available(&state.config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn post_update(&mut self, ctx: &egui::Context, jobs: &mut JobQueue, config: &AppConfigRef) {
|
pub fn post_update(&mut self, ctx: &egui::Context, jobs: &mut JobQueue, state: &AppStateRef) {
|
||||||
if let Some(result) = take(&mut self.scratch) {
|
if let Some(result) = take(&mut self.scratch) {
|
||||||
ctx.output_mut(|o| o.open_url = Some(OpenUrl::new_tab(result.scratch_url)));
|
ctx.output_mut(|o| o.open_url = Some(OpenUrl::new_tab(result.scratch_url)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.queue_build {
|
if self.queue_build {
|
||||||
self.queue_build = false;
|
self.queue_build = false;
|
||||||
if let Ok(mut config) = config.write() {
|
if let Ok(mut state) = state.write() {
|
||||||
config.queue_build = true;
|
state.queue_build = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,8 +108,8 @@ impl DiffViewState {
|
||||||
if let Some(function_name) =
|
if let Some(function_name) =
|
||||||
self.symbol_state.selected_symbol.as_ref().map(|sym| sym.symbol_name.clone())
|
self.symbol_state.selected_symbol.as_ref().map(|sym| sym.symbol_name.clone())
|
||||||
{
|
{
|
||||||
if let Ok(config) = config.read() {
|
if let Ok(state) = state.read() {
|
||||||
match CreateScratchConfig::from_config(&config, function_name) {
|
match CreateScratchConfig::from_config(&state.config, function_name) {
|
||||||
Ok(config) => {
|
Ok(config) => {
|
||||||
jobs.push_once(Job::CreateScratch, || {
|
jobs.push_once(Job::CreateScratch, || {
|
||||||
start_create_scratch(ctx, config)
|
start_create_scratch(ctx, config)
|
||||||
|
|
Loading…
Reference in New Issue