123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229open!Coreopen!ImportmoduleAction=structtype('dynamic_action,'static_action)t=|Dynamicof'dynamic_action|Staticof'static_actionendtype('i,'m,'dynamic_action,'static_action,'r)unpacked={input_var:'iIncr.Var.t;model_var:'mIncr.Var.t;default_model:'m;clock:Incr.Clock.t;inject:('dynamic_action,'static_action)Action.t->unitUi_effect.t;sexp_of_model:'m->Sexp.t;dynamic_apply_action_incr:(schedule_event:(unitUi_effect.t->unit)->'m->'dynamic_action->'m)Incr.t;dynamic_apply_action:(schedule_event:(unitUi_effect.t->unit)->'m->'dynamic_action->'m)Incr.Observer.t;static_apply_action:schedule_event:(unitUi_effect.t->unit)->'m->'static_action->'m;result:'rIncr.Observer.t;result_incr:'rIncr.t;lifecycle:Bonsai.Private.Lifecycle.Collection.tIncr.Observer.t;lifecycle_incr:Bonsai.Private.Lifecycle.Collection.tIncr.t;queue:('dynamic_action,'static_action)Action.tQueue.t;mutableshould_replace_bonsai_path_string:bool;mutableshould_replace_bonsai_hash_string:bool;mutablelast_view:string;mutablelast_lifecycle:Bonsai.Private.Lifecycle.Collection.t}type('i,'r)t=T:('i,_,_,_,'r)unpacked->('i,'r)tletcreate(typeir)?initial_model_sexp~clock~(initial_input:i)(component:(i,r)Bonsai.Arrow_deprecated.t):(i,r)t=letinput_var=Incr.Var.createinitial_inputinletinput=Incr.Var.watchinput_varinletfresh=Type_equal.Id.create~name:"fresh"sexp_of_opaqueinletvar=Bonsai.Private.(Value.namedfresh|>conceal_value)inletcomputation=componentvarinlet(Bonsai.Private.Computation.T{t=component_unpacked;dynamic_action=_;static_action=_;apply_static;model={default=default_model;sexp_of=sexp_of_model;equal=_;type_id=_;of_sexp=model_of_sexp}})=computation|>Bonsai.Private.reveal_computationinletenvironment=Bonsai.Private.Environment.(empty|>add_exn~key:fresh~data:input)inletstarting_model=Option.value_mapinitial_model_sexp~default:default_model~f:[%of_sexp:model]inletmodel_var=Incr.Var.createstarting_modelin(* Sadly the only way to give a name to the existential type that we just introduced
into the environment is by defining a function like this. See
https://github.com/ocaml/ocaml/issues/7074. *)letcreate_polymorphic(typedynamic_actionstatic_action)(computation:(_,dynamic_action,static_action,r)Bonsai.Private.Computation.t)apply_static:(i,r)t=letqueue=Queue.create()inletmoduleA=Ui_effect.Define(structmoduleAction=structtypet=(dynamic_action,static_action)Action.tendlethandle=Queue.enqueuequeueend)inletinject=A.injectinletinject_dynamica=A.inject(Dynamica)inletinject_statica=A.inject(Statica)inletsnapshot=Bonsai.Private.eval~environment~path:Bonsai.Private.Path.empty~clock~model:(Incr.Var.watchmodel_var)~inject_dynamic~inject_staticcomputationinletresult_incr=Bonsai.Private.Snapshot.resultsnapshotinletdynamic_apply_action_incr=Bonsai.Private.Apply_action.to_incremental(Bonsai.Private.Snapshot.apply_actionsnapshot)inletdynamic_apply_action=Incr.observedynamic_apply_action_incrinletresult=result_incr|>Incr.observeinletlifecycle_incr=Bonsai.Private.Snapshot.lifecycle_or_emptysnapshotinletlifecycle=Incr.observelifecycle_incrinIncr.stabilize();T{input_var;model_var;default_model;clock;inject;dynamic_apply_action;dynamic_apply_action_incr;static_apply_action=apply_static~inject:inject_static;result;result_incr;sexp_of_model;lifecycle;lifecycle_incr;queue;should_replace_bonsai_path_string=true;should_replace_bonsai_hash_string=true;last_view="";last_lifecycle=Bonsai.Private.Lifecycle.Collection.empty}increate_polymorphiccomponent_unpackedapply_static;;letschedule_event_=Ui_effect.Expert.handleletflush(T{model_var;static_apply_action;dynamic_apply_action;queue;_})=letupdate_model~action~apply_action=(* The only difference between [Var.latest_value] and [Var.value] is that
if [Var.set] is called _while stabilizing_, then calling [Var.value]
will return the value that was set when stabilization started, whereas
[latest_value] will give you the value that was just [set]. Now,
setting a model in the middle of a stabilizaiton should never happen,
but I think it's important to be explicit about which behavior we use,
so I chose the one that would be least surprising if a stabilization
does happen to occur. *)Incr.Var.setmodel_var(apply_action~schedule_event:Ui_effect.Expert.handle(Incr.Var.latest_valuemodel_var)action)inletprocess_event(action:_Action.t)=matchactionwith|Staticaction->update_model~apply_action:static_apply_action~action|Dynamicaction->(* We need to stabilize before every action so that the [input] for the
apply-actions are up to date. *)Incr.stabilize();letapply_action=Incr.Observer.value_exndynamic_apply_actioninupdate_model~apply_action~actioninwhilenot(Queue.is_emptyqueue)doprocess_event(Queue.dequeue_exnqueue)done;Incr.stabilize();;letset_input(T{input_var;_})input=Incr.Var.setinput_varinputletinput(T{input_var;_})=Incr.Var.valueinput_varletresult(T{result;_})=Incr.Observer.value_exnresultletlast_view(T{last_view;_})=last_viewletstore_view(Tunpacked)s=unpacked.last_view<-slethas_after_display_events(Tt)=letlifecycle=t.lifecycle|>Incr.Observer.value_exninBonsai.Private.Lifecycle.Collection.has_after_displaylifecycle;;lettrigger_lifecycles(Tt)=letold=t.last_lifecycleinletnew_=t.lifecycle|>Incr.Observer.value_exnint.last_lifecycle<-new_;schedule_event()(Bonsai.Private.Lifecycle.Collection.diffoldnew_);;letshould_censor_bonsai_path(T{should_replace_bonsai_path_string;_})=should_replace_bonsai_path_string;;letdisable_bonsai_path_censoring(Tunpacked)=unpacked.should_replace_bonsai_path_string<-false;;letshould_censor_bonsai_hash(T{should_replace_bonsai_hash_string;_})=should_replace_bonsai_hash_string;;letdisable_bonsai_hash_censoring(Tunpacked)=unpacked.should_replace_bonsai_hash_string<-false;;letsexp_of_model(T{sexp_of_model;model_var;_})=sexp_of_model(Incr.Var.valuemodel_var);;letresult_incr(T{result_incr;_})=result_incrletapply_action_incr(T{dynamic_apply_action_incr;_})=Ui_incr.packdynamic_apply_action_incr;;letlifecycle_incr(T{lifecycle_incr;_})=Ui_incr.packlifecycle_incrletclock(T{clock;_})=clockletinvalidate_observers(T{dynamic_apply_action;result;lifecycle;_})=Incr.Observer.disallow_future_usedynamic_apply_action;Incr.Observer.disallow_future_useresult;Incr.Observer.disallow_future_uselifecycle;;letreset_model_to_default(T{model_var;default_model;_})=Incr.Var.setmodel_vardefault_model;;