@@ -12,6 +12,7 @@ pub(super) struct PointsEditorState {
1212 pub ( super ) text : String ,
1313 pub ( super ) cache : Option < ParsedPointsCache > ,
1414 pub ( super ) cache_dirty : bool ,
15+ pub ( super ) text_sync_pending : bool ,
1516 pub ( super ) parse_debounce_deadline : Option < Instant > ,
1617 pub ( super ) undo_stack : Vec < String > ,
1718 pub ( super ) redo_stack : Vec < String > ,
@@ -23,6 +24,7 @@ impl Default for PointsEditorState {
2324 text : String :: new ( ) ,
2425 cache : None ,
2526 cache_dirty : true ,
27+ text_sync_pending : false ,
2628 parse_debounce_deadline : None ,
2729 undo_stack : Vec :: new ( ) ,
2830 redo_stack : Vec :: new ( ) ,
@@ -33,6 +35,7 @@ impl Default for PointsEditorState {
3335impl CurveFitApp {
3436 pub ( super ) fn invalidate_points_cache ( & mut self ) {
3537 self . points . cache_dirty = true ;
38+ self . points . text_sync_pending = false ;
3639 // Небольшой debounce уменьшает число парсингов во время быстрого ввода текста.
3740 self . points . parse_debounce_deadline =
3841 Some ( Instant :: now ( ) + Duration :: from_millis ( POINTS_PARSE_DEBOUNCE_MS ) ) ;
@@ -59,6 +62,7 @@ impl CurveFitApp {
5962 if should_parse {
6063 self . points . cache = Some ( parse_points_text_cache ( & self . points . text ) ) ;
6164 self . points . cache_dirty = false ;
65+ self . points . text_sync_pending = false ;
6266 self . points . parse_debounce_deadline = None ;
6367 }
6468 self . points
@@ -130,6 +134,88 @@ impl CurveFitApp {
130134 }
131135 }
132136
137+ pub ( super ) fn flush_points_text_from_cache_if_pending ( & mut self ) {
138+ if !self . points . text_sync_pending {
139+ return ;
140+ }
141+
142+ let synced_text = self . points . cache . as_ref ( ) . and_then ( |cache| {
143+ cache
144+ . parsed_points
145+ . as_ref ( )
146+ . ok ( )
147+ . map ( |points| points_to_text ( points) )
148+ } ) ;
149+ if let Some ( synced_text) = synced_text {
150+ self . points . text = synced_text;
151+ }
152+ self . points . text_sync_pending = false ;
153+ }
154+
155+ pub ( super ) fn push_current_points_undo_snapshot ( & mut self ) {
156+ self . flush_points_text_from_cache_if_pending ( ) ;
157+ self . push_points_undo_snapshot ( self . points . text . clone ( ) ) ;
158+ }
159+
160+ pub ( super ) fn edit_valid_points_in_cache < F > (
161+ & mut self ,
162+ record_undo : bool ,
163+ sync_text_immediately : bool ,
164+ edit : F ,
165+ ) -> Result < ( ) , String >
166+ where
167+ F : FnOnce ( & mut Vec < Point > ) ,
168+ {
169+ let parse_error = match & self . points_cache_with_policy ( true ) . parsed_points {
170+ Ok ( _) => None ,
171+ Err ( error) => Some ( error. clone ( ) ) ,
172+ } ;
173+ if let Some ( error) = parse_error {
174+ return Err ( error) ;
175+ }
176+
177+ if record_undo {
178+ self . push_current_points_undo_snapshot ( ) ;
179+ }
180+ self . points . redo_stack . clear ( ) ;
181+
182+ let maybe_synced_text = {
183+ let cache = self
184+ . points
185+ . cache
186+ . as_mut ( )
187+ . expect ( "points cache must be initialized" ) ;
188+ let points = cache
189+ . parsed_points
190+ . as_mut ( )
191+ . expect ( "parse error branch is handled above" ) ;
192+ edit ( points) ;
193+ cache. parse_error_line = None ;
194+ cache. plot_points = points
195+ . iter ( )
196+ . map ( |point| PlotPoint :: new ( point. x ( ) , point. y ( ) ) )
197+ . collect :: < Vec < _ > > ( )
198+ . into ( ) ;
199+ sync_text_immediately. then ( || points_to_text ( points) )
200+ } ;
201+
202+ if let Some ( synced_text) = maybe_synced_text {
203+ self . points . text = synced_text;
204+ self . points . text_sync_pending = false ;
205+ } else {
206+ self . points . text_sync_pending = true ;
207+ }
208+
209+ if matches ! (
210+ self . status. as_ref( ) ,
211+ Some ( StatusMessage :: Error ( message) ) if message. starts_with( POINTS_PARSE_ERROR_PREFIX )
212+ ) {
213+ self . status = Some ( self . idle_status_after_points_edit ( ) ) ;
214+ }
215+
216+ Ok ( ( ) )
217+ }
218+
133219 pub ( super ) fn apply_points_text_change ( & mut self , new_text : String , keep_redo : bool ) {
134220 if self . points . text == new_text {
135221 return ;
@@ -160,20 +246,17 @@ impl CurveFitApp {
160246 let Some ( next) = self . points . redo_stack . pop ( ) else {
161247 return ;
162248 } ;
163- self . push_points_undo_snapshot ( self . points . text . clone ( ) ) ;
249+ self . push_current_points_undo_snapshot ( ) ;
164250 self . apply_points_text_change ( next, true ) ;
165251 self . refresh_status_after_points_edit ( ) ;
166252 }
167253
168- pub ( super ) fn parse_points_for_edit ( & mut self ) -> Result < Vec < Point > , String > {
169- match & self . points_cache_with_policy ( true ) . parsed_points {
170- Ok ( points) => Ok ( points. clone ( ) ) ,
171- Err ( error) => Err ( error. clone ( ) ) ,
172- }
173- }
174-
175254 pub ( super ) fn parse_points_strict ( & mut self ) -> Result < Points , String > {
176- Points :: try_from ( self . parse_points_for_edit ( ) ?) . map_err ( |error| error. to_string ( ) )
255+ let parsed_points = match & self . points_cache_with_policy ( true ) . parsed_points {
256+ Ok ( points) => points. clone ( ) ,
257+ Err ( error) => return Err ( error. clone ( ) ) ,
258+ } ;
259+ Points :: try_from ( parsed_points) . map_err ( |error| error. to_string ( ) )
177260 }
178261
179262 pub ( super ) fn set_points_cache_from_valid_points ( & mut self , points : & [ Point ] ) {
@@ -189,10 +272,12 @@ impl CurveFitApp {
189272 plot_points,
190273 } ) ;
191274 self . points . cache_dirty = false ;
275+ self . points . text_sync_pending = false ;
192276 self . points . parse_debounce_deadline = None ;
193277 }
194278
195279 pub ( super ) fn clear_points_text ( & mut self , record_undo : bool ) {
280+ self . flush_points_text_from_cache_if_pending ( ) ;
196281 if self . points . text . is_empty ( ) {
197282 return ;
198283 }
@@ -211,12 +296,13 @@ impl CurveFitApp {
211296 }
212297
213298 pub ( super ) fn write_points_text ( & mut self , points : & [ Point ] , record_undo : bool ) {
299+ self . flush_points_text_from_cache_if_pending ( ) ;
214300 let new_text = points_to_text ( points) ;
215301 if self . points . text == new_text {
216302 return ;
217303 }
218304 if record_undo {
219- self . push_points_undo_snapshot ( self . points . text . clone ( ) ) ;
305+ self . push_current_points_undo_snapshot ( ) ;
220306 }
221307 self . points . text = new_text;
222308 self . points . redo_stack . clear ( ) ;
0 commit comments