Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
TextStore.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4
5namespace Stroika::Frameworks::Led {
6
7 /*
8 ********************************************************************************
9 *************************** TextStore::SimpleUpdater ***************************
10 ********************************************************************************
11 */
12 inline TextStore::SimpleUpdater::SimpleUpdater (TextStore& ts, const UpdateInfo& updateInfo)
13 : fTextStore (ts)
14 , fMarkerSink ()
15 , fUpdateInfo (updateInfo)
16 , fCanceled (false)
17 {
18 // Note that we EXPAND the list of markers we will notify to be sure markers just next to a change
19 // are given a crack at it (CollectAllMarkersInRange_OrSurroundings)
20 ts.CollectAllMarkersInRangeInto_OrSurroundings (updateInfo.fReplaceFrom, updateInfo.fReplaceTo, kAnyMarkerOwner, fMarkerSink);
21 ts.DoAboutToUpdateCalls (fUpdateInfo, fMarkerSink.fMarkers.begin (), fMarkerSink.fMarkers.end ());
22 }
23 inline TextStore::SimpleUpdater::SimpleUpdater (TextStore& ts, size_t from, size_t to, bool realContentUpdate)
24 : fTextStore (ts)
25 , fMarkerSink ()
26 , fUpdateInfo (from, to, LED_TCHAR_OF (""), 0, false, realContentUpdate)
27 , fCanceled (false)
28 {
29 // Note that we EXPAND the list of markers we will notify to be sure markers just next to a change
30 // are given a crack at it (CollectAllMarkersInRange_OrSurroundings)
31 ts.CollectAllMarkersInRangeInto_OrSurroundings (from, to, kAnyMarkerOwner, fMarkerSink);
32 ts.DoAboutToUpdateCalls (fUpdateInfo, fMarkerSink.fMarkers.begin (), fMarkerSink.fMarkers.end ());
33 }
34 inline TextStore::SimpleUpdater::~SimpleUpdater ()
35 {
36 if (not fCanceled) {
37 fTextStore.DoDidUpdateCalls (fUpdateInfo, fMarkerSink.fMarkers.begin (), fMarkerSink.fMarkers.end ());
38 }
39 }
40 inline void TextStore::SimpleUpdater::Cancel ()
41 {
42 fCanceled = true;
43 }
44
45 /*
46 ********************************************************************************
47 ************************ TextStore::SearchParameters ***************************
48 ********************************************************************************
49 */
50 inline TextStore::SearchParameters::SearchParameters (const Led_tString& searchString, bool wrap, bool wholeWord, bool caseSensative)
51 : fMatchString (searchString)
52 , fWrapSearch (wrap)
53 , fWholeWordSearch (wholeWord)
54 , fCaseSensativeSearch (caseSensative)
55 {
56 }
57
58 /*
59 ********************************************************************************
60 ************************ TextStore::VectorMarkerSink ***************************
61 ********************************************************************************
62 */
63 inline TextStore::VectorMarkerSink::VectorMarkerSink (vector<Marker*>* markers)
64 : fMarkers (markers)
65 {
66 RequireNotNull (fMarkers);
67 }
68
69 /*
70 ********************************************************************************
71 ************************************ TextStore *********************************
72 ********************************************************************************
73 */
74 inline TextStore::TextStore ()
75 {
76 fMarkerOwners.push_back (this);
77 }
78 /*
79 @METHOD: TextStore::AddMarkerOwner
80 @DESCRIPTION: <p>Register the given MarkerOwner with the TextStore for later notification of updates to the text.
81 Remove via RemoveMarkerOwner ().</p>
82 <p>NB: It is illegal to add a @'MarkerOwner' to more than one @'TextStore' at a time, or to the same one
83 multiple times.</p>
84 */
85 inline void TextStore::AddMarkerOwner (MarkerOwner* owner)
86 {
87 RequireNotNull (owner);
88#if !qVirtualBaseMixinCallDuringCTORBug
89 Require (owner->PeekAtTextStore () == this);
90#endif
91 Require (find (fMarkerOwners.begin (), fMarkerOwners.end (), owner) == fMarkerOwners.end ());
92 PUSH_BACK (fMarkerOwners, owner);
93 }
94 /*
95 @METHOD: TextStore::RemoveMarkerOwner
96 @DESCRIPTION:
97 <p>Unregister the given MarkerOwner which was previously registed with AddMarkerOwner ().</p>
98 */
99 inline void TextStore::RemoveMarkerOwner (MarkerOwner* owner)
100 {
101 RequireNotNull (owner);
102#if !qVirtualBaseMixinCallDuringCTORBug
103 Require (owner->PeekAtTextStore () == this);
104#endif
105 vector<MarkerOwner*>::iterator i = find (fMarkerOwners.begin (), fMarkerOwners.end (), owner);
106 Assert (i != fMarkerOwners.end ());
107 fMarkerOwners.erase (i);
108 }
109 /*
110 @METHOD: TextStore::GetMarkerOwners
111 @DESCRIPTION:
112 <p>Returns the list of all MarkerOwners registered for notification of changes to the text.</p>
113 */
114 inline const vector<MarkerOwner*>& TextStore::GetMarkerOwners () const noexcept
115 {
116 return fMarkerOwners;
117 }
118 /*
119 @METHOD: TextStore::RemoveMarker
120 @DESCRIPTION: <p>Remove the given marker from the text.</p>
121 */
122 inline void TextStore::RemoveMarker (Marker* marker)
123 {
124 RemoveMarkers (&marker, 1);
125 }
126 /*
127 @METHOD: TextStore::GetStart
128 @DESCRIPTION: <p>Returns the marker position of the beginning of the
129 text buffer (always 0).</p>
130 */
131 inline size_t TextStore::GetStart ()
132 {
133 return (0);
134 }
135 /*
136 @METHOD: TextStore::GetEnd
137 @DESCRIPTION: <p>Returns the marker position of the end of the text buffer.</p>
138 */
139 inline size_t TextStore::GetEnd () const
140 {
141 return (GetLength ());
142 }
143 /*
144 @METHOD: TextStore::CollectAllMarkersInRange
145 @DESCRIPTION: <p>CollectAllMarkersInRange () is part of a family of routines to retreive markers
146 of interest in a particular range of the text.</p>
147 <p>The @'TextStore::Overlap' method is what is used to see if a marker is considered to be in the
148 given from/to range for the purpose of collection (does the obvious intersection test
149 with the added caveat of not including markers which only overlap at one edge or the other
150 - and not including any common characters - except special case of zero-sized marker).</p>
151 <p>You can either specify a callback function/object to be called with each found marker.
152 If you only need the first such, you can throw to terminate the search. There is a help class
153 and helper fuctions to allow you to fill an array with the all the matching Markers.</p>
154 <p>NB: this has changed somewhat since Led22 - see SPR#0489.</p>
155 */
156 inline vector<Marker*> TextStore::CollectAllMarkersInRange (size_t from, size_t to, const MarkerOwner* owner) const
157 {
158 Require (from <= to);
159 Require (to <= GetEnd () + 1);
160 vector<Marker*> list;
161 VectorMarkerSink vml (&list);
162 CollectAllMarkersInRangeInto (from, to, owner, vml);
163 return (list);
164 }
165 inline void TextStore::CollectAllMarkersInRangeInto_OrSurroundings (size_t from, size_t to, const MarkerOwner* owner, MarkerSink& output) const
166 {
167 Require (from <= to);
168 Require (to <= GetEnd () + 1);
169 CollectAllMarkersInRangeInto ((from > 0) ? (from - 1) : from, min (to + 1, GetEnd () + 1), owner, output);
170 }
171 inline void TextStore::CollectAllMarkersInRangeInto (size_t from, size_t to, const MarkerOwner* owner, vector<Marker*>* markerList) const
172 {
173 RequireNotNull (markerList);
174 markerList->clear ();
175 VectorMarkerSink vml (markerList);
176 CollectAllMarkersInRangeInto (from, to, owner, vml);
177 }
178 inline vector<Marker*> TextStore::CollectAllMarkersInRange_OrSurroundings (size_t from, size_t to, const MarkerOwner* owner) const
179 {
180 Require (from <= to);
181 Require (to <= GetEnd () + 1);
182 return CollectAllMarkersInRange ((from > 0) ? (from - 1) : from, min (to + 1, GetEnd () + 1), owner);
183 }
184 inline void TextStore::CollectAllMarkersInRangeInto_OrSurroundings (size_t from, size_t to, const MarkerOwner* owner, vector<Marker*>* markerList) const
185 {
186 Require (from <= to);
187 Require (to <= GetEnd () + 1);
188 RequireNotNull (markerList);
189 VectorMarkerSink vml (markerList);
190 CollectAllMarkersInRangeInto ((from > 0) ? (from - 1) : from, min (to + 1, GetEnd () + 1), owner, vml);
191 }
192 inline size_t TextStore::CharacterToTCharIndex (size_t i)
193 {
194 return (i);
195 }
196 inline size_t TextStore::TCharToCharacterIndex (size_t i)
197 {
198 return (i);
199 }
200 /*
201 @METHOD: TextStore::SetMarkerStart
202 @DESCRIPTION: <p>Similar to @'TextStore::SetMarkerRange', except that the end-point doesn't change.
203 Vectors to @'TextStore::SetMarkerRange'. See @'TextStore::SetMarkerEnd'.</p>
204 */
205 inline void TextStore::SetMarkerStart (Marker* marker, size_t start) noexcept
206 {
207 SetMarkerRange (marker, start, marker->GetEnd ());
208 }
209 /*
210 @METHOD: TextStore::SetMarkerEnd
211 @DESCRIPTION: <p>Similar to @'TextStore::SetMarkerRange', except that the start-point doesn't change.
212 Vectors to @'TextStore::SetMarkerRange'. See @'TextStore::SetMarkerStart'.</p>
213 */
214 inline void TextStore::SetMarkerEnd (Marker* marker, size_t end) noexcept
215 {
216 SetMarkerRange (marker, marker->GetStart (), end);
217 }
218 /*
219 @METHOD: TextStore::SetMarkerLength
220 @DESCRIPTION: <p>Similar to @'TextStore::SetMarkerRange', except that the start-point doesn't change.
221 Similar to @'TextStore::SetMarkerEnd' except that it takes a length, not an end-point.
222 Vectors to @'TextStore::SetMarkerRange'. See @'TextStore::SetMarkerStart'.</p>
223 */
224 inline void TextStore::SetMarkerLength (Marker* marker, size_t length) noexcept
225 {
226 size_t start = marker->GetStart ();
227 SetMarkerRange (marker, start, start + length);
228 }
229 inline size_t TextStore::GetLineLength (size_t lineNumber) const
230 {
231 return (GetStartOfLine (lineNumber) - GetEndOfLine (lineNumber));
232 }
233 inline size_t TextStore::FindNextCharacter (size_t afterPos) const
234 {
235 if (afterPos >= GetEnd ()) {
236 return (GetEnd ());
237 }
238 size_t result = afterPos + 1;
239 Ensure (result <= GetEnd ());
240 return (result);
241 }
242 /*
243 @METHOD: TextStore::GetTextBreaker
244 @DESCRIPTION: <p>Returns a @'shared_ptr<T>' wrapper on the @'TextBreaks' subclass associated
245 with this TextStore. This
246 procedure can be changed at any time (though if any information in other parts of Led is cached and dependent on this procedures
247 results - you may wish to invalidate those caches).</p>
248 <p>If none is associated with the TextStore right now - and default one is built and returned.</p>
249 <p>See also See @'TextStore::SetTextBreaker'.</p>
250 */
251 inline shared_ptr<TextBreaks> TextStore::GetTextBreaker () const
252 {
253 if (fTextBreaker == nullptr) {
254 fTextBreaker = make_shared<TextBreaks_DefaultImpl> ();
255 }
256 return fTextBreaker;
257 }
258 /*
259 @METHOD: TextStore::SetTextBreaker
260 @DESCRIPTION: <p>See @'TextStore::GetTextBreaker'.</p>
261 */
262 inline void TextStore::SetTextBreaker (const shared_ptr<TextBreaks>& textBreaker)
263 {
264 fTextBreaker = textBreaker;
265 }
266 inline void TextStore::Invariant () const
267 {
268#if qStroika_Foundation_Debug_AssertionsChecked and qStroika_Frameworks_Led_HeavyDebugging
269 Invariant_ ();
270#endif
271 }
272 inline bool TextStore::Overlap (size_t mStart, size_t mEnd, size_t from, size_t to)
273 {
274 Require (mStart <= mEnd);
275 Require (from <= to);
276
277 if ((from <= mEnd) and (mStart <= to)) {
278 // Maybe overlap - handle nuanced cases of zero-sized overlaps
279 size_t overlapSize;
280 if (to >= mEnd) {
281 Assert (mEnd >= from);
282 overlapSize = min (mEnd - from, mEnd - mStart);
283 }
284 else {
285 Assert (to >= mStart);
286 overlapSize = min (to - from, to - mStart);
287 }
288 Assert (overlapSize <= (to - from));
289 Assert (overlapSize <= (mEnd - mStart));
290
291 if (overlapSize == 0) {
292 /*
293 * The ONLY case where we want to allow for a zero-overlap to imply a legit overlap is when the marker itself
294 * is zero-sized (cuz otherwise - it would never get found).
295 */
296 return mEnd == mStart;
297 }
298 else {
299 return true;
300 }
301 }
302 else {
303 return false;
304 }
305 }
306 /*
307 @METHOD: TextStore::Overlap
308 @DESCRIPTION: <p>The idea here is to test if a marker overlaps with a given range of the document. But
309 only in some <b>INTERESTING</b> way.</p>
310 <p>The one case one might plausibly consider overlap which this routine
311 does NOT is the case where two markers touch only at the edges (lhs of one == rhs of the other).
312 For the purposes for which markers are used - in my experience - this is NOT an interesting case
313 and it makes code using @'TextStore::CollectAllMarkersInRange' nearly always simpler and more efficient to
314 be able to avoid those cases.</p>
315 <p>There is one further refinement added here in Led 2.3 (970929, for spr#0489). When the marker is
316 zero-length (not the from/to args, but the 'm' arg), then the overlap is allowed to be zero width
317 and it is still considerd a valid overlap. This is true because we want CollectAllMarkersInRange ()
318 to be usable to pick up zero-length markers. Essentially, this means it is "OverlapOrStrictlyContains".</p>
319 <p>NB: The details of this special 'zero-width' overlap case were further clarified, and improved, and
320 revised in Led 3.0d6 (2000/04/26, SPR#0745).</p>
321 <p><em>Important:</em><br>
322 The definition of overlap is now that a marker overlaps with a given region if it overlaps by ONE FULL
323 marker position, or one special case of ZERO overlap. The only ZERO-overlap case which is supported is
324 simply if the marker-width is zero sized.</p>
325 <p>The part which is a change from Led 2.3 (and earlier) is the details of WHICH zero-width overlap cases
326 are now considered overlap. I think the new rule is simpler, and more intuitive. See Led 2.3 for the old rule/code.</p>
327 <p>NB: This routine is mainly called by the @'TextStore::CollectAllMarkersInRange' () family of functions.</p>
328 <p>NB: This routine is <b>NOT</b> symmetric. By this I mean that Overlap (A,B) is not always the same
329 as Overlap (B,A). The reason for this is because we specially treat the case of a zero-width first arg to
330 overlap. And we make no such special treatment of the second argument.</p>
331 <p>See SPR#0745 for more details. Also, SPR#0489, and SPR#420.</p>
332 */
333 inline bool TextStore::Overlap (const Marker& m, size_t from, size_t to)
334 {
335 Require (from <= to);
336
337 size_t start;
338 size_t end;
339 m.GetRange (&start, &end);
340 Assert (start <= end);
341
342#if qStroika_Foundation_Debug_AssertionsChecked
343 // Note - the old algorithm DOESNT give the same answers as the new one. Otherwise - we wouldn't bother with a new algorithm.
344 // This assertion/testing code is just temporary - for me to get a sense how often we're producing different answers, and how
345 // serious this will be (a testing issue) - LGP 2000/04/26
346 // Well - its been almost a year - and we've not seen this yet... Hmmm - LGP 2001-03-05
347 bool oldAlgorithmAnswer;
348 {
349 size_t Xend = end;
350 if (start == end) {
351 ++Xend;
352 }
353
354 oldAlgorithmAnswer = (from < Xend) and (start < to);
355 }
356#endif
357
358 if ((from <= end) and (start <= to)) {
359 // Maybe overlap - handle nuanced cases of zero-sized overlaps
360 size_t overlapSize;
361 if (to >= end) {
362 Assert (end >= from);
363 overlapSize = min (end - from, end - start);
364 }
365 else {
366 Assert (to >= start);
367 overlapSize = min (to - from, to - start);
368 }
369 Assert (overlapSize <= (to - from));
370 Assert (overlapSize <= (end - start));
371
372 if (overlapSize == 0) {
373 /*
374 * The ONLY case where we want to allow for a zero-overlap to imply a legit overlap is when the marker itself
375 * is zero-sized (cuz otherwise - it would never get found).
376 */
377 // Ensure (oldAlgorithmAnswer == (end == start));
378 return end == start;
379 }
380 else {
381#if qStroika_Foundation_Debug_AssertionsChecked
382 Ensure (oldAlgorithmAnswer == true);
383#endif
384 return true;
385 }
386 }
387 else {
388#if qStroika_Foundation_Debug_AssertionsChecked
389 Ensure (oldAlgorithmAnswer == false);
390#endif
391 return false;
392 }
393 }
394
395 /*
396 ********************************************************************************
397 ************************** MarkerOfATypeMarkerSink<T> **************************
398 ********************************************************************************
399 */
400 template <typename T>
401 inline MarkerOfATypeMarkerSink<T>::MarkerOfATypeMarkerSink ()
402 : fResult (nullptr)
403 {
404 }
405 template <typename T>
406 void MarkerOfATypeMarkerSink<T>::Append (Marker* m)
407 {
408 RequireNotNull (m);
409 T* tMarker = dynamic_cast<T*> (m);
410 if (tMarker != nullptr) {
411 Assert (fResult == nullptr); // we require at most one marker be added to us
412 fResult = tMarker;
413 }
414 }
415
416 /*
417 ********************************************************************************
418 ******************** MarkersOfATypeMarkerSink2Vector<T> ************************
419 ********************************************************************************
420 */
421 template <typename T>
422 inline MarkersOfATypeMarkerSink2Vector<T>::MarkersOfATypeMarkerSink2Vector ()
423 : fResult ()
424 {
425 }
426 template <typename T>
427 void MarkersOfATypeMarkerSink2Vector<T>::Append (Marker* m)
428 {
429 RequireNotNull (m);
430 T* tMarker = dynamic_cast<T*> (m);
431 if (tMarker != nullptr) {
432 PUSH_BACK (fResult, tMarker);
433 }
434 }
435
436 /*
437 ********************************************************************************
438 ************ MarkersOfATypeMarkerSink2SmallStackBuffer<T> **********************
439 ********************************************************************************
440 */
441 template <typename T>
442 inline MarkersOfATypeMarkerSink2SmallStackBuffer<T>::MarkersOfATypeMarkerSink2SmallStackBuffer ()
443 : fResult ()
444 {
445 }
446 template <typename T>
447 void MarkersOfATypeMarkerSink2SmallStackBuffer<T>::Append (Marker* m)
448 {
449 RequireNotNull (m);
450 T* tMarker = dynamic_cast<T*> (m);
451 if (tMarker != nullptr) {
452 fResult.push_back (tMarker);
453 }
454 }
455
456}
#define RequireNotNull(p)
Definition Assertions.h:347