) -> Html {
+ let tds = self.tds.iter().enumerate().map(|(i, td)| {
+ let mut errors = Vec::new();
+ let idx = NonMaxUsize::new(i).unwrap();
+ let link = ctx.link().clone();
+ let matcher_change = Callback::from(move |e: Event| {
+ let link = link.clone();
+ gloo::timers::callback::Timeout::new(10, move || link.send_message(TdcMsg::ChangedRow(idx, true, e))).forget();
+ });
+ let link = ctx.link().clone();
+ let matcher_focus = Callback::from(move |_| {
+ link.send_message(TdcMsg::OnFocus(idx, true));
+ });
+ let link = ctx.link().clone();
+ let matcher_blur = Callback::from(move |_| {
+ link.send_message(TdcMsg::OnBlur(idx, true));
+ });
+ let matcher_class = if td.matcher_err.is_some() { "td-matcher error" } else { "td-matcher" };
+ td.matcher_err.as_ref().map(AnyError::Matcher).map(|e| errors.push(e));
+
+ let link = ctx.link().clone();
+ let formatter_change = Callback::from(move |e: Event| {
+ let link = link.clone();
+ gloo::timers::callback::Timeout::new(10, move || link.send_message(TdcMsg::ChangedRow(idx, false, e))).forget();
+ });
+ let link = ctx.link().clone();
+ let formatter_focus = Callback::from(move |_| {
+ link.send_message(TdcMsg::OnFocus(idx, false));
+ });
+ let link = ctx.link().clone();
+ let formatter_blur = Callback::from(move |_| {
+ link.send_message(TdcMsg::OnBlur(idx, false));
+ });
+ let formatter_class = if td.formatter_err.is_some() { "td-formatter error" } else { "td-formatter" };
+ td.formatter_err.as_ref().map(AnyError::Formatter).map(|e| errors.push(e));
+
+ let error = td.parsed.as_ref().is_some_and(|p| p.is_err());
+ let class = if error { "td-row error" } else { "td-row" };
+ td.parsed.as_ref().map(|p| p.as_ref().map_err(AnyError::TermDisplay).map_err(|e| errors.push(e)));
+ yew::html! {
+
+
+
+ {error_tooltip(errors)}
+
+ }
+ });
+ let link = ctx.link().clone();
+ let formatter_change = Callback::from(move |e: Event| {
+ link.send_message(TdcMsg::ChangedFallback(e));
+ });
+ let formatter_class = if self.fallback_parsed.is_err() { "td-formatter error" } else { "td-formatter" };
+ let fallback = yew::html! {
+
+ {"Fallback:"}
+
+ {error_tooltip(self.fallback_parsed.as_ref().err().into_iter().map(AnyError::Fallback).collect())}
+
+ };
+ let tds = [fallback].into_iter().chain(tds);
+ let modified = self.modified;
+ let can_apply = modified && self.fallback_parsed.is_ok() && self.tds.iter().all(|td|
+ td.is_empty() || td.parsed.as_ref().is_some_and(|p| p.is_ok())
+ );
+ let link = ctx.link().clone();
+ let apply = Callback::from(move |_| {
+ if !can_apply {
+ return;
+ }
+ link.send_message(TdcMsg::Apply);
+ });
+ let link = ctx.link().clone();
+ let revert = Callback::from(move |_| {
+ if !modified {
+ return;
+ }
+ link.send_message(TdcMsg::Revert);
+ });
+ let reset = ctx.props().reset.clone();
+ let reset = Callback::from(move |_| reset.emit(()));
+ yew::html! {
+ <>
+
+
+
+
+
+ >
+ }
+ }
+
+ fn rendered(&mut self, _ctx: &Context, _first_render: bool) {
+ if let Some((idx, matcher)) = self.focused {
+ let td = &self.tds[idx.get()];
+ let ref_ = if matcher { &td.matcher_ref } else { &td.formatter_ref };
+ if let Some(element) = ref_.cast::() {
+ element.focus().ok();
+ }
+ }
+ }
+}
+
+impl TermDisplayComponent {
+ pub fn try_parse(&mut self) {
+ self.parsed = None;
+ let Ok(fallback) = &self.fallback_parsed else {
+ return;
+ };
+ let all_td_ok = self.tds.iter().all(|td| td.is_empty() || td.parsed.as_ref().is_some_and(|p| p.is_ok()));
+ if !all_td_ok {
+ return;
+ }
+
+ let tds = self.tds.iter().flat_map(|td| &td.parsed).map(|p| p.as_ref().unwrap().clone());
+ let mut tdc: Result = tds.collect();
+ if let Ok(tdc) = &mut tdc {
+ tdc.set_fallback(fallback.clone());
+ }
+ self.parsed = Some(tdc);
+ }
+
+ pub fn check_last_td(&mut self) {
+ if !self.tds.last().is_some_and(|td| td.is_empty()) {
+ self.tds.push(TermDisplayRow {
+ matcher: String::new(),
+ matcher_ref: NodeRef::default(),
+ matcher_err: None,
+ formatter: String::new(),
+ formatter_ref: NodeRef::default(),
+ formatter_err: None,
+ parsed: None,
+ });
+ }
+ }
+}
+
+impl TermDisplayRow {
+ pub fn try_parse(&mut self) -> Option<()> {
+ let matcher = self.matcher_ref.cast::()?;
+ self.matcher = matcher.value();
+ let formatter = self.formatter_ref.cast::()?;
+ self.formatter = formatter.value();
+
+ let matcher = self.matcher.parse::();
+ let formatter = self.formatter.parse::();
+
+ self.matcher_err = None;
+ self.formatter_err = None;
+ self.parsed = None;
+
+ match (matcher, formatter) {
+ (Ok(matcher), Ok(formatter)) => {
+ if !self.is_empty() {
+ self.parsed = Some(TermDisplay::new(matcher, formatter));
+ }
+ }
+ (Err(matcher), Err(formatter)) => {
+ self.matcher_err = Some(matcher);
+ self.formatter_err = Some(formatter);
+ }
+ (Err(matcher), Ok(_)) => {
+ self.matcher_err = Some(matcher);
+ }
+ (Ok(_), Err(formatter)) => {
+ self.formatter_err = Some(formatter);
+ }
+ }
+ Some(())
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.matcher.is_empty() && self.formatter.is_empty()
+ }
+}
+
+#[derive(Debug)]
+pub enum AnyError<'a> {
+ Matcher(&'a ConversionError),
+ Formatter(&'a FormatterParseError),
+ TermDisplay(&'a ConversionError),
+ Fallback(&'a FallbackParseError),
+}
+
+impl fmt::Display for AnyError<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ // TODO: add a proper display here
+ write!(f, "{self:?}")
+ }
+}
+
+pub fn error_tooltip(errors: Vec>) -> Html {
+ let title = errors.iter().map(AnyError::to_string).collect::>().join("\n");
+ let class = if errors.is_empty() { "td-error" } else { "td-error error" };
+ yew::html! {
+ {if errors.is_empty() { "" } else { "❌" }}
+ }
+}
diff --git a/axiom-profiler-GUI/src/configuration/provider.rs b/axiom-profiler-GUI/src/configuration/provider.rs
index 87f27abb..75f8384a 100644
--- a/axiom-profiler-GUI/src/configuration/provider.rs
+++ b/axiom-profiler-GUI/src/configuration/provider.rs
@@ -1,106 +1,26 @@
-use std::{cell::RefCell, rc::Rc};
+use std::rc::Rc;
-use gloo::{console::log, storage::Storage};
-use smt_log_parser::display_with::DisplayConfiguration;
+use gloo::storage::Storage;
use yew::{html, prelude::{Context, Html}, Callback, Children, Component, ContextProvider, Properties};
-use crate::RcParser;
+use crate::utils::updater::{Update, Updater};
-// Public
+use super::Configuration;
-pub trait ConfigurationContext {
- fn get_configuration(&self) -> Option>;
-}
-impl ConfigurationContext for html::Scope {
- fn get_configuration(&self) -> Option> {
- self.context(Callback::noop()).map(|c| c.0)
- }
-}
+// Public
#[derive(Clone, PartialEq)]
pub struct ConfigurationProvider {
pub config: Configuration,
- pub update: ConfigurationUpdater,
-}
-#[derive(Clone, PartialEq)]
-pub struct ConfigurationUpdater(Callback);
-impl ConfigurationUpdater {
- pub fn update(&self, f: impl for<'a> FnOnce(&'a mut Configuration) -> bool + 'static) {
- self.0.emit(ConfigurationUpdate::new(f));
- }
-}
-pub struct ConfigurationUpdate(Callback<&'static mut Configuration, bool>);
-impl ConfigurationUpdate {
- fn new(f: impl for<'a> FnOnce(&'a mut Configuration) -> bool + 'static) -> Self {
- let f = RefCell::new(Some(f));
- let f = Callback::from(move |config|
- f.borrow_mut().take().unwrap()(config)
- );
- Self(f)
- }
- fn apply(self, config: &mut Configuration) -> bool {
- // SAFETY: the callback can only have been created with a
- // `impl for<'a> FnOnce(&'a mut Configuration) -> bool + 'static`, and thus
- // it cannot make use of the `&'static mut Configuration` lifetime.
- let config = unsafe { &mut *(config as *mut _) };
- self.0.emit(config)
- }
-}
-
-impl ConfigurationProvider {
- pub fn reset_persistent(&self) {
- self.update.update(|cfg| {
- let new = Default::default();
- if cfg.persistent != new {
- cfg.persistent = new;
- true
- } else {
- false
- }
- });
- }
- pub fn update_display(&self, f: impl FnOnce(&mut DisplayConfiguration) -> bool + 'static) {
- self.update.update(|cfg| f(&mut cfg.persistent.display));
- }pub fn update_parser(&self, f: impl FnOnce(&mut Option) -> bool + 'static) {
- self.update.update(|cfg| f(&mut cfg.parser));
- }
- pub fn reset_ml_viewer_mode(&self) {
- self.update.update(|cfg| {cfg.persistent.ml_viewer_mode = false; true});
- }
+ pub update: Updater,
}
-#[derive(Clone, Default, PartialEq, Eq)]
-pub struct Configuration {
- pub parser: Option,
- pub persistent: PersistentConfiguration,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
-pub struct PersistentConfiguration {
- pub display: DisplayConfiguration,
- pub ml_viewer_mode: bool,
-}
-impl PersistentConfiguration {
- pub const fn default_const() -> Self {
- let display = DisplayConfiguration {
- display_term_ids: false,
- display_quantifier_name: false,
- use_mathematical_symbols: true,
- html: true,
- // Set manually elsewhere
- enode_char_limit: None,
- ast_depth_limit: None,
- };
- Self {
- display,
- ml_viewer_mode: false,
- }
- }
+pub trait ConfigurationContext {
+ fn get_configuration(&self) -> Option>;
}
-
-impl Default for PersistentConfiguration {
- fn default() -> Self {
- Self::default_const()
+impl ConfigurationContext for html::Scope {
+ fn get_configuration(&self) -> Option> {
+ self.context(Callback::noop()).map(|c| c.0)
}
}
@@ -112,39 +32,39 @@ pub struct ConfigurationProviderProps {
}
impl Component for ConfigurationProvider {
- type Message = ConfigurationUpdate;
+ type Message = Update;
type Properties = ConfigurationProviderProps;
fn create(ctx: &Context) -> Self {
- let link = ctx.link().clone();
- let mut config = Configuration::default();
- if let Ok(cached) = gloo::storage::LocalStorage::get::("config") {
- // log::warn!("ConfigurationProvider loaded: {cached:?}");
- config.persistent = cached;
- config.persistent.ml_viewer_mode = false;
+ let config = gloo::storage::LocalStorage::get::("config");
+ match &config {
+ Ok(_) | Err(gloo::storage::errors::StorageError::KeyNotFound(_)) => {}
+ Err(result) => log::error!("Configuration load error: {result:?}"),
}
- let update = Callback::from(move |config| link.send_message(config));
+
+ let config = config.unwrap_or_default();
Self {
config,
- update: ConfigurationUpdater(update),
+ update: Updater::new_link(ctx.link().clone()),
}
}
fn update(&mut self, _ctx: &Context, msg: Self::Message) -> bool {
- // log::warn!("ConfigurationProvider update: {:?}/{:?}: {:?}", msg.parser.is_some(), msg.parser.as_ref().map(|p| p.graph.is_some()), msg.persistent);
let changed = msg.apply(&mut self.config);
if changed {
- gloo::storage::LocalStorage::set::<&PersistentConfiguration>("config", &self.config.persistent).ok();
+ let result = gloo::storage::LocalStorage::set::<&Configuration>("config", &self.config);
+ if let Err(result) = result {
+ log::error!("Configuration save error: {result:?}");
+ }
}
changed
}
fn view(&self, ctx: &Context) -> Html {
- // log::warn!("ConfigurationProvider view: {:?}/{:?}", self.config.parser.is_some(), self.config.parser.as_ref().map(|p| p.graph.is_some()));
html! {
- > context={Rc::new(self.clone())}>
- {ctx.props().children.clone()}
- >>
+ > context={Rc::new(self.clone())}>
+ {for ctx.props().children.iter()}
+ >>
}
}
}
diff --git a/axiom-profiler-GUI/src/file.rs b/axiom-profiler-GUI/src/file.rs
index 186c8f12..a188a7e6 100644
--- a/axiom-profiler-GUI/src/file.rs
+++ b/axiom-profiler-GUI/src/file.rs
@@ -7,7 +7,7 @@ use wasm_bindgen::JsCast;
use web_sys::DataTransfer;
use yew::{html::Scope, Callback, DragEvent};
-use crate::{global_callbacks::GlobalCallbacks, infobars::OmnibarMessage, CallbackRef, FileDataComponent, LoadingState, Msg, ParseProgress, PREVENT_DEFAULT_DRAG_OVER};
+use crate::{global_callbacks::GlobalCallbacks, infobars::OmnibarMessage, state::{FileInfo, StateContext}, CallbackRef, FileDataComponent, LoadingState, Msg, ParseProgress, PREVENT_DEFAULT_DRAG_OVER};
impl FileDataComponent {
pub fn file_drag(registerer: &GlobalCallbacks, link: &Scope) -> [CallbackRef; 3] {
@@ -72,6 +72,12 @@ impl FileDataComponent {
let file_name = file.name();
let file_size = file.size();
+ let (name, size) = (file_name.clone(), file_size);
+ link.get_state().unwrap().update_file_info(move |info| {
+ *info = Some(FileInfo { name, size });
+ true
+ });
+
log::info!("Selected file \"{file_name}\"");
*self.cancel.borrow_mut() = false;
let cancel = self.cancel.clone();
@@ -119,7 +125,7 @@ impl FileDataComponent {
_ => (),
}
link.send_message(Msg::LoadingState(LoadingState::DoneParsing(finished.is_timeout(), cancel)));
- link.send_message(Msg::LoadedFile(file_name, file_size, parser.take_parser(), finished, cancel))
+ link.send_message(Msg::LoadedFile(parser.take_parser(), finished, cancel))
});
}
Err((_err, _stream)) => {
@@ -170,7 +176,7 @@ impl FileDataComponent {
_ => (),
}
link.send_message(Msg::LoadingState(LoadingState::DoneParsing(finished.is_timeout(), cancel)));
- link.send_message(Msg::LoadedFile(file_name, file_size, parser.take_parser(), finished, cancel))
+ link.send_message(Msg::LoadedFile(parser.take_parser(), finished, cancel))
});
});
self.reader = Some(reader);
diff --git a/axiom-profiler-GUI/src/filters/add_filter.rs b/axiom-profiler-GUI/src/filters/add_filter.rs
index c65eed17..69a98ab4 100644
--- a/axiom-profiler-GUI/src/filters/add_filter.rs
+++ b/axiom-profiler-GUI/src/filters/add_filter.rs
@@ -5,7 +5,7 @@ use petgraph::{visit::{Dfs, Walker}, Direction};
use smt_log_parser::parsers::z3::graph::{raw::NodeKind, RawNodeIndex};
use yew::{function_component, html, use_context, Callback, Html, MouseEvent, Properties};
-use crate::{configuration::ConfigurationProvider, results::{filters::Filter, svg_result::DEFAULT_NODE_COUNT}};
+use crate::{results::{filters::Filter, svg_result::DEFAULT_NODE_COUNT}, state::StateProvider, RcParser};
#[derive(PartialEq, Properties)]
pub struct AddFilterSidebarProps {
@@ -17,8 +17,8 @@ pub struct AddFilterSidebarProps {
#[function_component]
pub fn AddFilterSidebar(props: &AddFilterSidebarProps) -> Html {
- let cfg = use_context::>().unwrap();
- let Some(parser) = &cfg.config.parser else {
+ let data = use_context::>().unwrap();
+ let Some(parser) = &data.state.parser else {
return html!{}
};
diff --git a/axiom-profiler-GUI/src/filters/manage_filter.rs b/axiom-profiler-GUI/src/filters/manage_filter.rs
index 51afdccf..6dc7add7 100644
--- a/axiom-profiler-GUI/src/filters/manage_filter.rs
+++ b/axiom-profiler-GUI/src/filters/manage_filter.rs
@@ -6,7 +6,7 @@ use smt_log_parser::items::QuantIdx;
use web_sys::{Element, HtmlElement, HtmlInputElement};
use yew::{function_component, html, use_context, Callback, Children, Component, Context, Html, NodeRef, Properties};
-use crate::{configuration::ConfigurationProvider, mouse_position, results::filters::Filter, PREVENT_DEFAULT_DRAG_OVER};
+use crate::{mouse_position, results::filters::Filter, state::StateProvider, PREVENT_DEFAULT_DRAG_OVER};
pub enum Msg {
OnDragStart(usize, usize),
@@ -291,8 +291,8 @@ pub struct ExistingFilterProps {
#[function_component]
pub fn ExistingFilter(props: &ExistingFilterProps) -> Html {
- let cfg = use_context::>().unwrap();
- let graph = cfg.config.parser.as_ref().and_then(|p| p.graph.as_ref());
+ let data = use_context::>().unwrap();
+ let graph = data.state.parser.as_ref().and_then(|p| p.graph.as_ref());
let fc = |i| graph.as_ref().map(|g| {
*g.borrow().raw[i].kind()
}).unwrap();
diff --git a/axiom-profiler-GUI/src/filters/mod.rs b/axiom-profiler-GUI/src/filters/mod.rs
index 626ef669..7482007d 100644
--- a/axiom-profiler-GUI/src/filters/mod.rs
+++ b/axiom-profiler-GUI/src/filters/mod.rs
@@ -1,7 +1,7 @@
mod add_filter;
mod manage_filter;
-use std::fmt::Display;
+use std::{borrow::Borrow, fmt::Display};
use gloo::console::log;
use material_yew::icon::MatIcon;
@@ -9,7 +9,7 @@ use petgraph::Direction;
use smt_log_parser::parsers::{z3::graph::{raw::NodeKind, RawNodeIndex}, ParseState};
use yew::{html, Callback, Component, Context, Html, MouseEvent, NodeRef, Properties};
-use crate::{configuration::ConfigurationContext, filters::{add_filter::AddFilterSidebar, manage_filter::{DraggableList, ExistingFilter}}, infobars::SidebarSectionHeader, results::{filters::{Disabler, Filter, DEFAULT_DISABLER_CHAIN, DEFAULT_FILTER_CHAIN}, svg_result::Msg as SVGMsg}, utils::{indexer::Indexer, toggle_list::ToggleList}, OpenedFileInfo, SIZE_NAMES};
+use crate::{filters::{add_filter::AddFilterSidebar, manage_filter::{DraggableList, ExistingFilter}}, infobars::SidebarSectionHeader, results::{filters::{Disabler, Filter, DEFAULT_DISABLER_CHAIN, DEFAULT_FILTER_CHAIN}, svg_result::Msg as SVGMsg}, state::StateContext, utils::toggle_list::ToggleList, OpenedFileInfo, RcParser, SIZE_NAMES};
use self::manage_filter::DragState;
use material_yew::WeakComponentLink;
@@ -160,9 +160,9 @@ impl Component for FiltersState {
modified = true;
}
if let Filter::SelectNthMatchingLoop(n) = &filter {
- // TODO: re-add finding matching loops
- let graph = &ctx.props().file.parser.graph;
- if !graph.as_ref().is_some_and(|g| (& *g.borrow()).found_matching_loops().is_some_and(|mls| mls > *n)) {
+ let state = ctx.link().get_state().unwrap();
+ let graph = &state.state.parser.as_ref().unwrap().graph;
+ if !graph.as_ref().is_some_and(|g| (**g).borrow().found_matching_loops().is_some_and(|mls| mls > *n)) {
return modified;
}
}
@@ -171,10 +171,10 @@ impl Component for FiltersState {
}
Msg::AddFilter(edit, filter) => {
if let Filter::SelectNthMatchingLoop(n) = &filter {
- // TODO: re-add finding matching loops
- let graph = &ctx.props().file.parser.graph;
+ let state = ctx.link().get_state().unwrap();
+ let graph = &state.state.parser.as_ref().unwrap().graph;
// This relies on the fact that the graph is updated before the `AddFilter` is
- if !graph.as_ref().is_some_and(|g| g.borrow().found_matching_loops().is_some_and(|mls| mls > *n)) {
+ if !graph.as_ref().is_some_and(|g| (**g).borrow().found_matching_loops().is_some_and(|mls| mls > *n)) {
return false;
}
}
@@ -193,30 +193,32 @@ impl Component for FiltersState {
},
Msg::ToggleMlViewerMode => {
let search_matching_loops = ctx.props().search_matching_loops.clone();
- let found_mls = ctx.props().file.parser.found_mls;
+
+ let state = ctx.link().get_state().unwrap();
+ let found_mls = &state.state.parser.as_ref().unwrap().found_mls;
if let None = found_mls {
search_matching_loops.emit(());
}
- let link = ctx.link().clone();
- let cfg = link.get_configuration().unwrap();
- cfg.update.update(|cfg| { cfg.persistent.ml_viewer_mode = !cfg.persistent.ml_viewer_mode; true });
+ state.set_ml_viewer_mode(!state.state.ml_viewer_mode);
true
}
}
}
fn view(&self, ctx: &Context) -> Html {
+ let data = ctx.link().get_state().unwrap();
+ let info = data.state.file_info.as_ref().unwrap();
let file = &ctx.props().file;
- let (size, unit) = file_size_display(file.file_size);
+ let (size, unit) = file_size_display(info.size);
let details = match &file.parser_state {
ParseState::Paused(_, state) => {
let (parse_size, parse_unit) = file_size_display(state.bytes_read as u64);
- format!("{} ({parse_size} {parse_unit}/{size} {unit})", file.file_name)
+ format!("{} ({parse_size} {parse_unit}/{size} {unit})", info.name)
}
ParseState::Completed { .. } =>
- format!("{} ({size} {unit})", file.file_name),
+ format!("{} ({size} {unit})", info.name),
ParseState::Error(err) =>
- format!("{} (error {err:?})", file.file_name),
+ format!("{} (error {err:?})", info.name),
};
// Existing ops
let elem_hashes: Vec<_> = self.filter_chain.iter().map(Filter::get_hash).collect();
@@ -231,13 +233,14 @@ impl Component for FiltersState {
}).collect();
let drag = ctx.link().callback(Msg::Drag);
let will_delete = ctx.link().callback(Msg::WillDelete);
- // TODO: re-add finding matching loops
- let found_mls = ctx.props().file.parser.found_mls;
+
+ let state = ctx.link().get_state().unwrap();
+ let found_mls = &state.state.parser.as_ref().unwrap().found_mls;
let toggle_ml_viewer_mode = ctx.link().callback(|ev: MouseEvent| {
ev.prevent_default();
Msg::ToggleMlViewerMode
});
- let ml_viewer_mode = if ctx.link().get_configuration().unwrap().config.persistent.ml_viewer_mode {
+ let ml_viewer_mode = if state.state.ml_viewer_mode {
html! {
{"close"}
{"Exit matching loop viewer"}
}
@@ -292,8 +295,7 @@ impl Component for FiltersState {
};
let graph_details = file.rendered.as_ref().map(|g| {
let class = if self.dragging { "hidden" } else { "" };
- // TODO: re-add finding matching loops
- let mls = ctx.props().file.parser.found_mls.map(|mls| format!(", {mls} mtch loops")).unwrap_or_default();
+ let mls = found_mls.map(|mls| format!(", {mls} mtch loops")).unwrap_or_default();
let details = format!("{} nodes, {} edges{mls}", g.graph.graph.node_count(), g.graph.graph.edge_count());
html! { {details} }
});
diff --git a/axiom-profiler-GUI/src/infobars/omnibox/mod.rs b/axiom-profiler-GUI/src/infobars/omnibox/mod.rs
index 942c92dd..6889106e 100644
--- a/axiom-profiler-GUI/src/infobars/omnibox/mod.rs
+++ b/axiom-profiler-GUI/src/infobars/omnibox/mod.rs
@@ -113,9 +113,7 @@ impl Component for Omnibox {
}
}
fn changed(&mut self, ctx: &Context, old_props: &Self::Properties) -> bool {
- if ctx.props() == old_props {
- return false;
- }
+ debug_assert!(ctx.props() != old_props);
if ctx.props().progress != old_props.progress {
self.focused = false;
self.command_mode = false;
diff --git a/axiom-profiler-GUI/src/infobars/topbar.rs b/axiom-profiler-GUI/src/infobars/topbar.rs
index 58582aaa..047d7fa8 100644
--- a/axiom-profiler-GUI/src/infobars/topbar.rs
+++ b/axiom-profiler-GUI/src/infobars/topbar.rs
@@ -2,7 +2,7 @@ use material_yew::linear_progress::MatLinearProgress;
use smt_log_parser::parsers::z3::graph::RawNodeIndex;
use yew::{function_component, html, use_context, Callback, Html, NodeRef, Properties};
-use crate::{configuration::ConfigurationProvider, infobars::{ml_omnibox::MlOmnibox, Omnibox, SearchActionResult}, utils::lookup::Kind, LoadingState};
+use crate::{configuration::ConfigurationProvider, infobars::{ml_omnibox::MlOmnibox, Omnibox, SearchActionResult}, state::StateProvider, utils::lookup::Kind, LoadingState};
#[derive(Debug, Clone, PartialEq)]
pub struct OmnibarMessage {
@@ -18,7 +18,6 @@ pub struct TopbarProps {
pub search: Callback>,
pub pick: Callback<(String, Kind), Option>>,
pub select: Callback,
- pub found_mls: Option,
pub pick_nth_ml: Callback,
}
@@ -60,10 +59,11 @@ pub fn Topbar(props: &TopbarProps) -> Html {
closed = false;
indeterminate = false;
}
- let ml_viewer_mode = use_context:: >().expect("no ctx found");
- let omnibox = if ml_viewer_mode.config.persistent.ml_viewer_mode {
+ let state = use_context::>().expect("no ctx found");
+ let omnibox = if state.state.ml_viewer_mode {
+ let found_mls = state.state.parser.as_ref().unwrap().found_mls.unwrap();
html! {
-
+
}
} else {
html! {
diff --git a/axiom-profiler-GUI/src/lib.rs b/axiom-profiler-GUI/src/lib.rs
index 00621c1f..75fd21fa 100644
--- a/axiom-profiler-GUI/src/lib.rs
+++ b/axiom-profiler-GUI/src/lib.rs
@@ -22,6 +22,7 @@ use yew::prelude::*;
use material_yew::{MatIcon, MatIconButton, MatDialog, WeakComponentLink};
use crate::commands::CommandsProvider;
+use crate::state::{StateContext, StateProviderContext};
use crate::configuration::{ConfigurationContext, ConfigurationProvider, Flags};
use crate::filters::FiltersState;
use crate::infobars::{OmnibarMessage, SearchActionResult, SidebarSectionHeader, Topbar};
@@ -43,6 +44,7 @@ pub mod homepage;
pub mod shortcuts;
pub mod commands;
pub mod file;
+pub mod state;
pub const GIT_DESCRIBE: &str = env!("VERGEN_GIT_DESCRIBE");
pub fn version() -> Option {
@@ -61,7 +63,7 @@ pub static PREVENT_DEFAULT_DRAG_OVER: OnceLock> = OnceLock::new();
pub enum Msg {
File(Option),
- LoadedFile(String, u64, Z3Parser, ParseState, bool),
+ LoadedFile(Z3Parser, ParseState, bool),
LoadingState(LoadingState),
RenderedGraph(RenderedGraph),
FailedOpening(String),
@@ -126,8 +128,6 @@ impl ParseProgress {
#[derive(Clone)]
pub struct OpenedFileInfo {
- file_name: String,
- file_size: u64,
parser_state: ParseState,
parser_cancelled: bool,
update: Rc, Vec>>>,
@@ -135,14 +135,11 @@ pub struct OpenedFileInfo {
selected_nodes: Vec,
selected_edges: Vec,
rendered: Option,
- parser: RcParser,
}
impl PartialEq for OpenedFileInfo {
fn eq(&self, other: &Self) -> bool {
- self.file_name == other.file_name
- && self.file_size == other.file_size
- && std::mem::discriminant(&self.parser_state) == std::mem::discriminant(&other.parser_state)
+ std::mem::discriminant(&self.parser_state) == std::mem::discriminant(&other.parser_state)
&& self.selected_nodes == other.selected_nodes
&& self.selected_edges == other.selected_edges
&& self.rendered.as_ref().map(|r| r.graph.generation) == other.rendered.as_ref().map(|r| r.graph.generation)
@@ -304,10 +301,10 @@ impl Component for FileDataComponent {
let Some(file) = file else {
return false;
};
- // remove any old parser in the configuration
- let cfg = ctx.link().get_configuration().unwrap();
- cfg.update_parser(|p| p.take().is_some());
- cfg.reset_ml_viewer_mode();
+ // remove any old parser in the state
+ let state = ctx.link().get_state().unwrap();
+ state.update_parser(|p| p.take().is_some());
+ state.set_ml_viewer_mode(false);
// hide the flags page if shown
self.flags_visible.borrow().emit(Some(false));
@@ -367,8 +364,9 @@ impl Component for FileDataComponent {
self.progress = LoadingState::NoFileSelected;
let file = self.file.take();
drop(file);
- let cfg = ctx.link().get_configuration().unwrap();
- cfg.update_parser(|p| p.take().is_some());
+ let state = ctx.link().get_state().unwrap();
+ state.update_file_info(|fi| fi.take().is_some());
+ state.update_parser(|p| p.take().is_some());
if let Some(navigation_section) = self.navigation_section.cast::() {
let _ = navigation_section.class_list().add_1("expanded");
@@ -383,19 +381,15 @@ impl Component for FileDataComponent {
self.message.take();
true
}
- Msg::LoadedFile(file_name, file_size, parser, parser_state, parser_cancelled) => {
- log::info!("Processing \"{file_name}\"");
+ Msg::LoadedFile(parser, parser_state, parser_cancelled) => {
drop(self.reader.take());
let parser = RcParser::new(parser);
- let rc_parser = parser.clone();
- let cfg = ctx.link().get_configuration().unwrap();
- cfg.update_parser(move |p| {
- *p = Some(rc_parser);
+ let state = ctx.link().get_state().unwrap();
+ state.update_parser(move |p| {
+ *p = Some(parser);
true
});
let file = OpenedFileInfo {
- file_name,
- file_size,
parser_state,
parser_cancelled,
filter: WeakComponentLink::default(),
@@ -403,7 +397,6 @@ impl Component for FileDataComponent {
selected_nodes: Vec::new(),
selected_edges: Vec::new(),
rendered: None,
- parser,
};
self.file = Some(file);
if let Some(navigation_section) = self.navigation_section.cast::() {
@@ -466,44 +459,17 @@ impl Component for FileDataComponent {
Msg::SearchMatchingLoops => {
log::info!("Searching matching loops");
if let Some(file) = &mut self.file {
- // TODO: re-add finding matching loops
- // assert!(file.parser.graph.is_some());
- // let parser = ctx.link().get_configuration().unwrap().config.parser.unwrap();
- // let tmp = ctx.link().get_configuration().unwrap().config.parser.unwrap().clone();
- // let mut parser = tmp.parser.borrow_mut();
- // file.parser = ctx.link().get_configuration().unwrap().config.parser.unwrap().clone();
- let cfg = ctx.link().get_configuration().unwrap();
- let parser = cfg.config.parser.as_ref().unwrap();
- file.parser = parser.clone();
- if let Some(g) = &file.parser.graph {
- file.parser.found_mls = Some((&mut *g.borrow_mut())
- // .search_matching_loops(&mut *parser))
- .search_matching_loops(&mut *file.parser.parser.borrow_mut()))
+ let state = ctx.link().get_state().unwrap();
+ let parser = state.state.parser.as_ref().unwrap();
+ if let Some(g) = &parser.graph {
+ let found_mls = Some((&mut *g.borrow_mut())
+ .search_matching_loops(&mut *parser.parser.borrow_mut()));
+ state.update_parser(move |p| {
+ p.as_mut().unwrap().found_mls = found_mls;
+ true
+ });
+ return true;
}
- return true;
- // file.parser.found_mls = Some(
- // &file
- // .parser
- // .graph
- // .unwrap()
- // .borrow_mut()
- // .deref_mut()
- // .search_matching_loops()
- // );
- // assert!(parser.graph.is_some());
- // if let Some(g) = &file
- // .parser
- // .graph
- // // .borrow_mut()
- // // .as_mut()
- // {
- // file.parser.found_mls = Some(g
- // .borrow_mut()
- // .deref_mut()
- // .search_matching_loops());
- // log::info!("Returning true");
- // return true;
- // }
}
log::info!("Returning false");
false
@@ -539,8 +505,14 @@ impl Component for FileDataComponent {
None => html!{},
};
- let cfg = ctx.link().get_configuration().unwrap();
- let parser = cfg.config.parser.clone();
+ let link = ctx.link().clone();
+ let flags_visible_changed = Callback::from(move |visible| {
+ let data = link.get_state().unwrap();
+ data.set_overlay_visible(visible);
+ });
+
+ let data = ctx.link().get_state().unwrap();
+ let parser = data.state.parser.clone();
let parser_ref = parser.clone();
let visible = self.file.as_ref().and_then(|f| f.rendered.as_ref().map(|r| r.graph.clone()));
let visible_ref = visible.clone();
@@ -569,13 +541,12 @@ impl Component for FileDataComponent {
let pick_nth_ml = Callback::from({
let file = self.file.clone();
move |n: usize| {
- if let Some(found_mls) = file.as_ref().and_then(|file| file.parser.found_mls) {
let Some(filters_state_link) = &*filters_state_link.borrow() else {
return;
};
filters_state_link.send_message(crate::filters::Msg::AddFilter(false, Filter::SelectNthMatchingLoop(n)));
}
- }});
+ });
// Callbacks
let file_select_ref = self.file_select.clone();
@@ -641,18 +612,18 @@ impl Component for FileDataComponent {
-
+
{page}
-
+
// Shortcuts dialog
@@ -683,11 +654,9 @@ impl Component for FileDataComponent {
#[function_component(App)]
pub fn app() -> Html {
html! {
-
-
+
-
-
+