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