Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
MarkerCover.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4
5namespace Stroika::Frameworks::Led {
6
7 /*
8 ********************************************************************************
9 ***************************** Implementation Details ***************************
10 ********************************************************************************
11 */
12 // class MarkerCover<MARKER,MARKERINFO,INCREMENTALMARKERINFO>
13 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
14 MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::MarkerCover (TextStore& useTextStore, MARKERINFO defaultInfo)
15 : MarkerOwner ()
16 , fTextStore (useTextStore)
17 , fNeedExtraUpdateCheck (false)
18 , fEarlyDidUpdateCalled (false)
19 , fNeedExtraUpdateCheck_UpdateInfo (0, 0, nullptr, 0, false, false)
20 {
21 /*
22 * Add ourselves to the TextStore (for callbacks), and add one marker to span
23 * the entire buffer length (plus one so catches anything at the end).
24 */
25 fTextStore.AddMarkerOwner (this);
26 fTextStore.AddMarker (new MARKER (defaultInfo), 0, fTextStore.GetEnd () + 1, this);
27 }
28 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
29 MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::~MarkerCover ()
30 {
31 try {
32 // NB: We are collecting ALL markers we own here, not just markers we added!
33 // This is because it is common practice to add other markers - such as embeddings - to a textstore
34 // using this dbase as the marker owner.
35 MarkersOfATypeMarkerSink2Vector<Marker> result;
36 fTextStore.CollectAllMarkersInRangeInto_OrSurroundings (0, fTextStore.GetLength () + 1, this, result);
37 vector<Marker*>::const_iterator end = result.fResult.end ();
38 for (auto i = result.fResult.begin (); i != end; ++i) {
39 fTextStore.RemoveMarker (*i);
40 delete *i;
41 }
42 }
43 catch (...) {
44 Assert (false); // NYI - must come up with some safe way to delete all these markers
45 // without using any memory! - maybe do the above in small pieces?
46 // One cell at a time? That would be painfully slow, but if we only did
47 // it on caught excpetions, that might be OK...
48 // LGP 960427
49 // not right, but til we fix this, eat the exception... LGP 960427
50 }
51 Assert (CollectAllInRange_OrSurroundings (0, fTextStore.GetLength () + 1).size () == 0);
52 fTextStore.RemoveMarkerOwner (this);
53 }
54 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
55 TextStore* MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::PeekAtTextStore () const
56 {
57 return &fTextStore;
58 }
59 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
60 inline typename MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::MarkerVector
61 MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::CollectAllInRange (size_t from, size_t to) const
62 {
63 /*
64 * Walk through all the markers in existence (in this range), and throw away all
65 * but our standard style markers.
66 * This is an inefficient approach. It would be far
67 * faster to keep a linked, or doubly linked list of all these guys.
68 * But this approach saves a bit of memory, and til we see this as a problem, lets just
69 * live with it.
70 */
71 MarkersOfATypeMarkerSink2Vector<MARKER> result;
72 fTextStore.CollectAllMarkersInRangeInto (from, to, this, result);
73 return result.fResult;
74 }
75 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
76 inline typename MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::MarkerVector
77 MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::CollectAllInRange_OrSurroundings (size_t from, size_t to) const
78 {
79 /*
80 * Walk through all the markers in existence (in this range), and throw away all
81 * but our standard style markers.
82 * This is an inefficient approach. It would be far
83 * faster to keep a linked, or doubly linked list of all these guys.
84 * But this approach saves a bit of memory, and til we see this as a problem, lets just
85 * live with it.
86 */
87 MarkersOfATypeMarkerSink2Vector<MARKER> result;
88 fTextStore.CollectAllMarkersInRangeInto_OrSurroundings (from, to, this, result);
89 return result.fResult;
90 }
91 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
92 inline typename MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::MarkerVector
93 MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::CollectAllNonEmptyInRange (size_t from, size_t to) const
94 {
95 /*
96 * See CollectAllInRange - but with extra test to eliminate zero-len markers...
97 */
98 NonEmptyOnes result;
99 fTextStore.CollectAllMarkersInRangeInto (from, to, this, result);
100 return result.fResult;
101 }
102 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
103 inline typename MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::MarkerVector
104 MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::CollectAllNonEmptyInRange_OrSurroundings (size_t from, size_t to) const
105 {
106 /*
107 * See CollectAllInRange - but with extra test to eliminate zero-len markers...
108 */
109 NonEmptyOnes result;
110 fTextStore.CollectAllMarkersInRangeInto_OrSurroundings (from, to, this, result);
111 return result.fResult;
112 }
113 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
114 inline const MARKERINFO& MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::GetInfo (size_t charAfterPos) const
115 {
116 HandleCallBeforeDidUpdateComplete ();
117 Invariant ();
118 MarkerOfATypeMarkerSink<MARKER> result;
119 fTextStore.CollectAllMarkersInRangeInto (charAfterPos, charAfterPos + 1, this, result);
120 AssertNotNull (result.fResult);
122 MarkerVector markers = CollectAllInRange (charAfterPos, charAfterPos + 1);
123 Assert (markers.size () == 1);
124 Assert (result.fResult == markers[0]);
125 }
126 return result.fResult->GetInfo ();
127 }
128 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
129 /*
130 @METHOD: MarkerCover<MARKER,MARKERINFO,INCREMENTALMARKERINFO>::GetInfoMarkers
131 @DESCRIPTION:
132 <p>Returns the marker objects (of type 'MARKER') which contain the marker-info (of type 'MARKERINFO').
133 which are found in the range from charAfterPos and up to nTCharsFollowing. If nTCharsFollowing == 0, it is
134 assumed to really be at least one.</p>
135 <p>The marker list is returned sorted, and always contains at least one element.</p>
136 */
137 inline typename MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::MarkerVector
138 MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::GetInfoMarkers (size_t charAfterPos, size_t nTCharsFollowing) const
139 {
140 HandleCallBeforeDidUpdateComplete ();
141 Invariant ();
142 if (nTCharsFollowing == 0) {
143 nTCharsFollowing = 1;
144 }
145 MarkerVector markers = CollectAllInRange (charAfterPos, charAfterPos + nTCharsFollowing);
146 sort (markers.begin (), markers.end (), LessThan<MARKER> ());
147 Ensure (markers.size () >= 1);
148 return markers;
149 }
150 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
151 /*
152 @METHOD: MarkerCover<MARKER,MARKERINFO,INCREMENTALMARKERINFO>::GetInfo
153 @DESCRIPTION:
154 <p>Returns the a vector of pairs: MARKERINFO and length (pair<MARKERINFO,size_t>). These are returned in
155 order. These are the info which is found in the range from charAfterPos and up to nTCharsFollowing.
156 If nTCharsFollowing == 0, it is assumed to really be at least one.</p>
157 <p>The list is returned sorted, and always contains at least one element.</p>
158 <p>See also @'MarkerCover<MARKER,MARKERINFO,INCREMENTALMARKERINFO>::SetInfo',
159 and @'MarkerCover<MARKER,MARKERINFO,INCREMENTALMARKERINFO>::SetInfos'</p>
160 */
161 inline vector<pair<MARKERINFO, size_t>> MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::GetInfo (size_t charAfterPos, size_t nTCharsFollowing) const
162 {
163 MarkerVector markers = GetInfoMarkers (charAfterPos, nTCharsFollowing);
164 vector<pair<MARKERINFO, size_t>> result;
165 for (auto i = markers.begin (); i != markers.end (); ++i) {
166 // Simply append makerinfo's, but be careful on the endpoints. We have have cut one or both endpoints off slightly
167 if (i == markers.begin ()) {
168 result.push_back (pair<MARKERINFO, size_t> ((*i)->GetInfo (), min ((*i)->GetEnd () - charAfterPos, nTCharsFollowing)));
169 }
170 else if (i + 1 == markers.end ()) {
171 result.push_back (pair<MARKERINFO, size_t> ((*i)->GetInfo (), charAfterPos + nTCharsFollowing - (*i)->GetStart ()));
172 }
173 else {
174 result.push_back (pair<MARKERINFO, size_t> ((*i)->GetInfo (), (*i)->GetLength ()));
175 }
176 }
177 Ensure (result.size () >= 1);
178 return result;
179 }
180 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
181 /*
182 @METHOD: MarkerCover<MARKER,MARKERINFO,INCREMENTALMARKERINFO>::SetInfo
183 @DESCRIPTION:
184 <p>Apply the given INCREMENTALMARKERINFO to the range of 'nTCharsFollowing' chars starting at charAfterPos.
185 This could have no effect (if nTCharsFollowing is zero, and the substypes ConstrainSetInfoArgs () doesn't modify the start/end,
186 or if the INCREMENTALMARKERINFO specifies no change).</p>
187 <p>At any rate - the method will assure the given INCREMENTALMARKERINFO is applied to the entire range from 'charAfterPos;
188 to 'charAfterPos + nTCharsFollowing', and any splitting or coalescing
189 of adjacent style runs will be handled automatically.</p>
190 <p>See also @'MarkerCover<MARKER,MARKERINFO,INCREMENTALMARKERINFO>::GetInfo', and
191 @'MarkerCover<MARKER,MARKERINFO,INCREMENTALMARKERINFO>::SetInfos'</p>
192 */
193 void MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::SetInfo (size_t charAfterPos, size_t nTCharsFollowing,
194 const INCREMENTALMARKERINFO& infoForMarkers)
195 {
196 HandleCallBeforeDidUpdateComplete ();
197 Invariant ();
198
199 ConstrainSetInfoArgs (&charAfterPos, &nTCharsFollowing);
200
201 if (nTCharsFollowing == 0) {
202 return;
203 }
204
205 size_t from = charAfterPos;
206 size_t to = from + nTCharsFollowing;
207 UpdateInfo allMarkersUpdateInfo (charAfterPos, charAfterPos + nTCharsFollowing, LED_TCHAR_OF (""), 0, false, true);
208
209 TextStore::SimpleUpdater* updater = nullptr;
210 try {
211 SetInfoInnerLoop (from, to, infoForMarkers, allMarkersUpdateInfo, &updater);
212 if (updater != nullptr) {
213 NoteCoverRangeDirtied (allMarkersUpdateInfo.fReplaceFrom, allMarkersUpdateInfo.fReplaceTo);
214 delete updater; // forces DidUpdate calls
215 }
216 }
217 catch (...) {
218 if (updater != nullptr) {
219 updater->Cancel ();
220 }
221 delete updater;
222 throw;
223 }
224 Invariant ();
225 }
226 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
227 /*
228 @METHOD: MarkerCover<MARKER,MARKERINFO,INCREMENTALMARKERINFO>::SetInfos
229 @DESCRIPTION:
230 <p>DOC LATER. SHOULD TIS TAKE A VECTOR OF INCREMENTALMARKERINFO???? INSTEAD OF MARKERINFO</p>
231 */
232 void MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::SetInfos (size_t charAfterPos,
233 const vector<pair<INCREMENTALMARKERINFO, size_t>>& infoForMarkers)
234 {
235 HandleCallBeforeDidUpdateComplete ();
236
237 Invariant ();
238
239 size_t totalStart = 0;
240 size_t totalEnd = 0;
241 {
242 /*
243 * We must compute the entire range of the affected area. This is very nearly trivial - except for the ConstrainSetInfoArgs ()
244 * effect. If the ConstrainSetInfoArgs () call expands the nCharsFollowing count past the end of what would have been the sum
245 * of the '(*i).second' values - then we must use the larger value - so that we do an AboutToUpdate/DidUpdate on the entire
246 * affected area.
247 *
248 * If the ConstrainSetInfoArgs () turn out to be expensive - then we could probably optimize this so that we only called
249 * the ConstrainSetInfoArgs () on the last few markers in the list.
250 */
251 totalStart = charAfterPos;
252 size_t curPos = charAfterPos;
253 for (auto i = infoForMarkers.begin (); i != infoForMarkers.end (); ++i) {
254 size_t from = curPos;
255 size_t to = from;
256 {
257 size_t nCharsFollowing = (*i).second;
258 ConstrainSetInfoArgs (&from, &nCharsFollowing);
259 to = from + nCharsFollowing;
260 }
261
262 curPos += (*i).second;
263
264 totalStart = min (totalStart, from);
265 totalEnd = max (to, curPos);
266 }
267 }
268
269 if (totalStart == totalEnd) {
270 return;
271 }
272
273 UpdateInfo allMarkersUpdateInfo (totalStart, totalEnd, LED_TCHAR_OF (""), 0, false, true);
274 TextStore::SimpleUpdater* updater = nullptr;
275 try {
276 {
277 size_t curPos = charAfterPos;
278 for (auto i = infoForMarkers.begin (); i != infoForMarkers.end (); ++i) {
279 size_t from = curPos;
280 size_t to = from;
281 {
282 size_t nCharsFollowing = (*i).second;
283 ConstrainSetInfoArgs (&from, &nCharsFollowing);
284 to = from + nCharsFollowing;
285 }
286
287 /*
288 * Assure region we are updating is contained within the 'total' update region.
289 */
290 Assert (totalStart <= from);
291 Assert (from <= to);
292 Assert (to <= totalEnd);
293
294 SetInfoInnerLoop (from, to, (*i).first, allMarkersUpdateInfo, &updater);
295 curPos += (*i).second;
296 }
297 }
298
299 if (updater != nullptr) {
300 NoteCoverRangeDirtied (allMarkersUpdateInfo.fReplaceFrom, allMarkersUpdateInfo.fReplaceTo);
301 delete updater; // forces DidUpdate calls
302 }
303 }
304 catch (...) {
305 if (updater != nullptr) {
306 updater->Cancel ();
307 }
308 delete updater;
309 throw;
310 }
311
312 Invariant ();
313 }
314 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
315 /*
316 @METHOD: MarkerCover<MARKER,MARKERINFO,INCREMENTALMARKERINFO>::SetInfos2
317 @DESCRIPTION:
318 */
319 void MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::SetInfos2 (size_t charAfterPos, const vector<pair<MARKERINFO, size_t>>& infoForMarkers)
320 {
321 vector<pair<INCREMENTALMARKERINFO, size_t>> tmp;
322 tmp.reserve (infoForMarkers.size ());
323 for (auto i = infoForMarkers.begin (); i != infoForMarkers.end (); ++i) {
324 tmp.push_back (pair<INCREMENTALMARKERINFO, size_t> (INCREMENTALMARKERINFO ((*i).first), (*i).second));
325 }
326 SetInfos (charAfterPos, tmp);
327 }
328 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
329 /*
330 @METHOD: MarkerCover<MARKER,MARKERINFO,INCREMENTALMARKERINFO>::SetInfoInnerLoop
331 @ACCESS: protected
332 @DESCRIPTION:
333 <p>Internal helper routine. You probably should not use/call this directly.</p>
334 <p>Apply the given 'INCREMENTALMARKERINFO' to the 'from' to 'to' range of text. Keep track of some
335 outer scope, passed in variables that help the overall update process.</p>
336 <p>Only update 'allMarkersInRange' - calling aboutToUpdate - if the passed in argument is non-nullptr.
337 Only update 'changedAnything' - flag if its pointer is non-nullptr. 'allMarkersInRange' can be null if-and-only-if
338 'changedAnything' is null (pointer).</p>.
339 */
340 void MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::SetInfoInnerLoop (size_t from, size_t to, const INCREMENTALMARKERINFO& infoForMarkers,
341 const UpdateInfo& allMarkersUpdateInfo,
342 TextStore::SimpleUpdater** updater)
343 {
344 // Gather all style markers and sumarize them for the region which overlaps my change
345 // Sort the markers to make the coalesce code below simpler, and more efficient...
346 MarkerVector markers = CollectAllNonEmptyInRange (from > 0 ? from - 1 : from, to);
347 MARKER* prevNonEmptyMarker = nullptr; // used for coalescing...
348 sort (markers.begin (), markers.end (), LessThan<MARKER> ());
349 typename MarkerVector::const_iterator miStart = markers.begin ();
350 typename MarkerVector::const_iterator miEnd = markers.end ();
351 if (from > 0) {
353 MarkerVector tmp = CollectAllNonEmptyInRange (from - 1, from);
354 Assert (tmp.size () == 1);
355 Assert (tmp[0] == markers[0]);
356 }
357 prevNonEmptyMarker = markers[0];
358 if (prevNonEmptyMarker->GetEnd () > from) { // else it would be the FIRST in our marker list!
359 Assert (markers[0] == prevNonEmptyMarker);
360 prevNonEmptyMarker = nullptr;
361 }
362 else {
363 ++miStart;
364 Assert (miStart != miEnd);
365 }
366 }
367
368 bool changedAnythingHERE = false;
369 // iterate through markers, and create an ordered list of the MARKERs which overlap our change
370 for (auto mi = miStart; mi != miEnd; ++mi) {
371 MARKER* m = *mi;
372 AssertMember (m, MARKER);
373
374 // Be sure prev marker lines up with next in list...
375 Assert (prevNonEmptyMarker != nullptr or mi == miStart); // if mi > miStart, we must have had at least ONE non-empty marker!
376 Assert (mi == miStart or (prevNonEmptyMarker->GetEnd () == m->GetStart ()));
377
378 // First see if we need make any change at all
379 MARKERINFO fsp = m->GetInfo ();
380 fsp.MergeIn (infoForMarkers);
381 if (fsp != m->GetInfo ()) {
382 if (updater != nullptr and *updater == nullptr) {
383 *updater = new TextStore::SimpleUpdater (fTextStore, allMarkersUpdateInfo);
384 }
385 changedAnythingHERE = true;
386
387 /*
388 * First apply the actual change, splitting any markers as needed along the way.
389 */
390 if (m->GetStart () < from) {
391 // WE MUST SPLIT
392 MARKERINFO origFSP = m->GetInfo ();
393 size_t itsOldEnd = m->GetEnd ();
394 fTextStore.SetMarkerEnd (m, from);
395
396 size_t lenLeftToFixup = itsOldEnd - from;
397 size_t ourNewLen = min (lenLeftToFixup, (to - from));
398 MARKER* newMarker = new MARKER (fsp);
399 fTextStore.AddMarker (newMarker, from, ourNewLen, this);
400
401 Assert (m->GetLength () != 0);
402 prevNonEmptyMarker = m; // update prevNonEmptyMarker
403 m = newMarker; // be sure m points to LAST marker of results for this range
404
405 // Now do we need to clone the old marker to go after ours?
406 lenLeftToFixup -= ourNewLen;
407 if (lenLeftToFixup != 0) {
408 MARKER* nm = new MARKER (origFSP);
409 fTextStore.AddMarker (nm, from + ourNewLen, lenLeftToFixup, this);
410 Assert (m->GetLength () != 0);
411 prevNonEmptyMarker = m; // update prevNonEmptyMarker
412 m = nm; // be sure m points to LAST marker of results for this range
413 }
414 }
415 else if (m->GetEnd () > to) {
416 // WE MUST SPLIT
417 MARKER* newMarker = new MARKER (fsp);
418 size_t itsOldStart = m->GetStart ();
419 fTextStore.SetMarkerStart (m, to);
420 size_t newMarkerLen = to - itsOldStart;
421 Assert (newMarkerLen > 0);
422 fTextStore.AddMarker (newMarker, itsOldStart, newMarkerLen, this);
423 Assert (newMarker->GetLength () != 0);
424
425 m = newMarker; // so we attempt to merge 'm' (really newMarker) onto end of prevNonEmptyMarker
426 }
427 else {
428 Assert (m->GetStart () >= from);
429 Assert (m->GetEnd () <= to);
430 m->SetInfo (fsp);
431 // NB: we don't need to update 'm' cuz no markers created
432 // and we didn't change prev either!
433 }
434 }
435
436 /*
437 * Now for each marker - as a result of this merged in style - may now be
438 * EQUAL to its predecesor marker, or its following marker.
439 *
440 * Since all our markers are now ordered (sorted above), checking the
441 * predecessor is easy (except for the first maker). And we can avoid
442 * the redundancy of checking the following markers (not to mention
443 * possible futility since they are about to change) by simply arranging to
444 * check the very LAST marker, and see if it can be merged with the
445 * one following it.
446 *
447 * Note: we do this OUTSIDE the test for (fsp != m->GetInfo ())
448 * cuz even if they were the same, we could have had PREVIOUS differences,
449 * and so needed to merge together all those blocks.
450 */
451 if (changedAnythingHERE and prevNonEmptyMarker != nullptr and prevNonEmptyMarker != m) {
452 AssertMember (prevNonEmptyMarker, MARKER);
453 if (prevNonEmptyMarker->GetInfo () == m->GetInfo ()) {
454 // simply zero out the size of 'm', and put that size into
455 // prevMarker. Zero'd out markers will automagically be deleted
456 // in the DidUpdate () call below...
457 Assert (prevNonEmptyMarker->GetEnd () == m->GetStart ());
458 fTextStore.SetMarkerEnd (prevNonEmptyMarker, m->GetEnd ());
459 fTextStore.SetMarkerEnd (m, m->GetStart ()); // note we zero out by setting END to start - rather than
460 // the other way cuz of the special case where 'm' is the
461 // last marker, and otherwise it won't get found to 'cull'
462 }
463 }
464 DISABLE_COMPILER_MSC_WARNING_START (28182) // Asserts above make clear m not null
465 if (m->GetLength () != 0) {
466 prevNonEmptyMarker = m;
467 }
468 DISABLE_COMPILER_MSC_WARNING_END (28182)
469 }
470 }
471 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
472 /*
473 @METHOD: MarkerCover<MARKER,MARKERINFO,INCREMENTALMARKERINFO>::ConstrainSetInfoArgs
474 @ACCESS: protected
475 @DESCRIPTION:
476 <p>Override this to apply any special constraints on the boundaries of regions in the MarkerCover. For example, for
477 a ParagraphDatabase, this makes sure the boundaries on lookups/changes fall on partition element boundaries. By default,
478 this routine does nothing.</p>
479 */
480 void MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::ConstrainSetInfoArgs (size_t* /*charAfterPos*/, size_t* /*nTCharsFollowing*/)
481 {
482 }
483 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
484 /*
485 @METHOD: MarkerCover<MARKER,MARKERINFO,INCREMENTALMARKERINFO>::NoteCoverRangeDirtied
486 @ACCESS: protected
487 @DESCRIPTION:
488 <p>Internal utility routine, used to react to a region of text having had some of its MARKERINFO changed. This code
489 will walk the affected region, and make sure all contraints (like no zero-length markers) are observed. It calls the
490 virtual @'MarkerCover<MARKER,MARKERINFO,INCREMENTALMARKERINFO>::NoteCoverRangeDirtied' to handle the real gruntwork.</p>
491 */
492 void MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::NoteCoverRangeDirtied (size_t from, size_t to)
493 {
494 Require (from <= to);
495 Require (to <= fTextStore.GetEnd () + 1);
496 MarkerVector markers = CollectAllInRange_OrSurroundings (from, to);
497 sort (markers.begin (), markers.end (), LessThan<MARKER> ());
498 NoteCoverRangeDirtied (from, to, markers);
499 }
500 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
501 void MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::NoteCoverRangeDirtied ([[maybe_unused]] size_t from, [[maybe_unused]] size_t to,
502 const MarkerVector& rangeAndSurroundingsMarkers)
503 {
504 Require (from <= to);
505 Require (to <= fTextStore.GetEnd () + 1);
506 CullZerod (rangeAndSurroundingsMarkers);
507 CheckForMerges (rangeAndSurroundingsMarkers);
508 }
509 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
510 void MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::AboutToUpdateText (const UpdateInfo& updateInfo)
511 {
512 fNeedExtraUpdateCheck = false; // If we set true before this call - must be because an 'AboutToUpdate' was aborted.
513 Assert (fMarkersToBeDeleted.IsEmpty ());
514 Invariant ();
515 inherited::AboutToUpdateText (updateInfo);
516 if (updateInfo.fTextModified) {
517 fNeedExtraUpdateCheck = true;
518 fEarlyDidUpdateCalled = false;
519 fNeedExtraUpdateCheck_UpdateInfo = updateInfo;
520 }
521 }
522 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
523 void MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::EarlyDidUpdateText (const UpdateInfo& /*updateInfo*/) noexcept
524 {
525 // See docs on @'MarkerCover<MARKER,MARKERINFO,INCREMENTALMARKERINFO>::HandleCallBeforeDidUpdateComplete' for an
526 // explanation of this code.
527 fEarlyDidUpdateCalled = true;
528 }
529 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
530 /*
531 @METHOD: MarkerCover<MARKER,MARKERINFO,INCREMENTALMARKERINFO>::DidUpdateText
532 @DESCRIPTION:
533 <p>Override the standard @'MarkerOwner::DidUpdateText' to handle any updates which maybe needed to the MarkerCover.
534 Calls @'MarkerCover<MARKER,MARKERINFO,INCREMENTALMARKERINFO>::NoteCoverRangeDirtied'. Also checks the Invariant ()
535 after its called (invariant could fail before DidUpdate() call).</p>
536 */
537 void MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::DidUpdateText (const UpdateInfo& updateInfo) noexcept
538 {
539 fNeedExtraUpdateCheck = false;
540 if (updateInfo.fTextModified) {
541 NoteCoverRangeDirtied (updateInfo.fReplaceFrom, updateInfo.GetResultingRHS ());
542 }
543 inherited::DidUpdateText (updateInfo);
544 fMarkersToBeDeleted.FinalizeMarkerDeletions ();
545 Invariant ();
546 }
547 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
548 /*
549 @METHOD: MarkerCover<MARKER,MARKERINFO,INCREMENTALMARKERINFO>::CullZerod
550 @ACCESS: protected
551 @DESCRIPTION:
552 <p>Internal utility routine, used to check for (and safely delete) zero-width cover elements.</p>
553 */
554 void MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::CullZerod (size_t around) noexcept
555 {
556 CullZerod (CollectAllInRange_OrSurroundings (around, around));
557 }
558 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
559 void MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::CullZerod (const MarkerVector& rangeAndSurroundingsMarkers) noexcept
560 {
561 // all effected text is diff if we did a replace or not - if no, then from-to,
562 // else from to from+textInserted (cuz from-to deleted)
563 for (auto i = rangeAndSurroundingsMarkers.begin (); i != rangeAndSurroundingsMarkers.end (); ++i) {
564 MARKER* m = *i;
565 if (m->GetLength () == 0) {
566 fMarkersToBeDeleted.SafeAccumulateMarkerForDeletion (m);
567 }
568 }
569 }
570 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
571 void MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::CheckForMerges (size_t around) noexcept
572 {
573 // Gather all style markers and sumarize them for the region which overlaps my change
574 MarkerVector markers = CollectAllNonEmptyInRange_OrSurroundings (around, around);
575 Assert (markers.size () != 0);
576
577 if (markers.size () > 1) {
578 Assert (markers.size () == 2); // since two character range, can be at most two style markers!
579 MARKER* m1 = markers[0];
580 MARKER* m2 = markers[1];
581 Assert (m1->GetLength () != 0);
582 Assert (m2->GetLength () != 0);
583 if (m1->GetInfo () == m2->GetInfo ()) {
584 // We then must merge one out. Doesn't matter which. Arbitrarily chose to keep rightmost one
585 MARKER* deleteMe = (m1->GetStart () < m2->GetStart ()) ? m1 : m2;
586 MARKER* keepMe = (deleteMe == m1) ? m2 : m1;
587 Assert (keepMe->GetStart () == deleteMe->GetEnd ());
588 fTextStore.SetMarkerStart (keepMe, deleteMe->GetStart ());
589 fTextStore.SetMarkerLength (deleteMe, 0); // so won't be refered to again once accumulated for deletion
590 fMarkersToBeDeleted.AccumulateMarkerForDeletion (deleteMe);
591 }
592 }
593 }
594 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
595 void MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::CheckForMerges (const MarkerVector& rangeAndSurroundingsMarkers) noexcept
596 {
597 /*
598 * Argument markers must be pre-sorted.
599 *
600 * Walk the list - and if any adjacent pairs are identical, merge one out.
601 */
602 if (rangeAndSurroundingsMarkers.size () >= 2) {
603 typename MarkerVector::const_iterator i = rangeAndSurroundingsMarkers.begin ();
604 typename MarkerVector::const_iterator end = rangeAndSurroundingsMarkers.end ();
605 MARKER* prevMarker = *i;
606 ++i;
607 for (; i != end; ++i) {
608 MARKER* m1 = prevMarker;
609 MARKER* m2 = *i;
610
611 // handle tricky cases of zero-length markers...
612 {
613 if (m1->GetLength () == 0) {
614 // if prev empty - skip ahead one...
615 prevMarker = m2;
616 continue;
617 }
618 if (m2->GetLength () == 0) {
619 // prev is set to the last non-empty marker, but we're currently looking at an empty one.
620 // Just skip it
621 continue;
622 }
623 }
624
625 Assert (m1->GetLength () != 0);
626 Assert (m2->GetLength () != 0);
627 if (m1->GetInfo () == m2->GetInfo ()) {
628 // We then must merge one out. Doesn't matter which. Arbitrarily chose to keep rightmost one
629 // (though the choice is arbitrary - it is assumed above - eariler on in this routine - when we
630 // handle zero-length markers).
631 Assert (m1->GetStart () < m2->GetStart ());
632 MARKER* deleteMe = m1;
633 MARKER* keepMe = m2;
634 Assert (keepMe->GetStart () == deleteMe->GetEnd ());
635 fTextStore.SetMarkerStart (keepMe, deleteMe->GetStart ());
636 fTextStore.SetMarkerLength (deleteMe, 0); // so won't be refered to again once accumulated for deletion
637 fMarkersToBeDeleted.AccumulateMarkerForDeletion (deleteMe);
638 }
639 prevMarker = m2;
640 }
641 }
642 }
643 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
644 /*
645 @METHOD: MarkerCover<MARKER,MARKERINFO,INCREMENTALMARKERINFO>::HandleCallBeforeDidUpdateComplete
646 @ACCESS: private
647 @DESCRIPTION:
648 <p>For the most part, we can call @'MarkerCover<MARKER,MARKERINFO,INCREMENTALMARKERINFO>::NoteCoverRangeDirtied' to cleanup
649 any changes made to the text in this region, either through a 'SetInfo' call, or because of a plain text update.
650 We hook the @'MarkerOwner::DidUpdateText' call to perform this validation of our MarkerCover.</p>
651 <p>However, what happens if someone makes a change to the text, and after our 'AboutToUpdate' hook happens, but BEFORE
652 our 'DidUpdate' hook - someone tries to query some info about the MarkerCover<>? See SPR#0599 for an example of this
653 problem.</p>
654 <p>Anyhow - we must protect our database so that it remains consistent, even if someone queries information after our
655 'AboutToUpdate' hook, but before our 'DidUpdate' hook. Thats what this routine, together with a few flags, does.</p>
656 <p>There is one more twist: Just what area (in HandleCallBeforeDidUpdateComplete) to we check if its after our
657 'AboutToUpdate' hook and before our 'DidUpdate' hook? The answer is - it depends? If we are called from another AboutToUpdate ()
658 handler, then the text hasn't changed, so we have no work todo. If we are called from another 'DidUpdate' hook, then we MUST process
659 the NoteCoverRangeDirtied (). But we cannot BLINDLY do this. We need to know if the actual text modification has taken place (so we know
660 which buffer offsets to use). It is for this reason that we have the 'fEarlyDidUpdateCalled' variable.</p>
661 */
662 inline void MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::HandleCallBeforeDidUpdateComplete () const noexcept
663 {
664 if (fNeedExtraUpdateCheck) {
665 HandleCallBeforeDidUpdateComplete_ ();
666 }
667 }
668 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
669 void MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::HandleCallBeforeDidUpdateComplete_ () const noexcept
670 {
671 /*
672 * If some 'GetInfo' routine is called - AFTER our AboutToUpdate () call but before our EarlyDidUpdate call, then
673 * the text hasn't truely been updated - so we cannot yet do our checks (and nor do we need to).
674 */
675 Assert (fNeedExtraUpdateCheck);
676 if (fEarlyDidUpdateCalled) {
677 const_cast<MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>*> (this)->NoteCoverRangeDirtied (
678 fNeedExtraUpdateCheck_UpdateInfo.fReplaceFrom, fNeedExtraUpdateCheck_UpdateInfo.GetResultingRHS ());
679 fMarkersToBeDeleted.FinalizeMarkerDeletions ();
680 fNeedExtraUpdateCheck = false;
681 }
682 }
683 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
684 inline void MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::Invariant () const
685 {
686#if qStroika_Foundation_Debug_AssertionsChecked && qStroika_Frameworks_Led_HeavyDebugging
687 Invariant_ ();
688#endif
689 }
690#if qStroika_Foundation_Debug_AssertionsChecked
691 template <typename MARKER, typename MARKERINFO, typename INCREMENTALMARKERINFO>
692 void MarkerCover<MARKER, MARKERINFO, INCREMENTALMARKERINFO>::Invariant_ () const
693 {
694 MarkerVector markers = CollectAllInRange (0, fTextStore.GetEnd () + 1);
695 sort (markers.begin (), markers.end (), LessThan<MARKER> ());
696
697 // Walk through - and see we have a cover, and non-overlapping...
698 Assert (markers.size () > 0);
699 size_t lastEnd = 0;
700 for (auto i = markers.begin (); i != markers.end (); ++i) {
701 MARKER* m = *i;
702 AssertMember (m, MARKER);
703 Assert (m->GetLength () > 0); // we should eliminate all zero-length markers...
704 if (i == markers.begin ()) {
705 Assert (m->GetStart () == 0);
706 }
707 if (i == markers.end () - 1) {
708 Assert (m->GetStart () == lastEnd);
709 Assert (m->GetEnd () == fTextStore.GetLength () + 1);
710 }
711 Assert (m->GetStart () == lastEnd);
712 if (i != markers.begin ()) {
713 MARKER* prevMarker = *(i - 1);
714 AssertMember (prevMarker, MARKER);
715 Assert (prevMarker->GetInfo () != m->GetInfo ()); // otherwise they should have been merged together
716 }
717 lastEnd = m->GetEnd ();
718 }
719 Assert (lastEnd == fTextStore.GetLength () + 1);
720 }
721#endif
722
723}
#define AssertNotNull(p)
Definition Assertions.h:333
#define qStroika_Foundation_Debug_AssertionsChecked
The qStroika_Foundation_Debug_AssertionsChecked flag determines if assertions are checked and validat...
Definition Assertions.h:48
#define AssertMember(p, c)
Definition Assertions.h:312