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