4#include "Stroika/Frameworks/StroikaPreComp.h"
9#include "Stroika/Frameworks/Led/Config.h"
15using namespace Stroika::Frameworks;
16using namespace Stroika::Frameworks::Led;
18inline bool IsASCIIAlpha (
int c)
20 return isascii (c) and isalpha (c);
23void Led::Private_::SetMarkerRange_ (TextStore& textstore, Marker* marker,
size_t start,
size_t end)
noexcept
25 textstore.SetMarkerRange (marker, start, end);
27void Led::Private_::PreRemoveMarker_ (TextStore& textstore, Marker* marker)
29 textstore.PreRemoveMarker (marker);
31void Led::Private_::RemoveMarkers_ (TextStore& textstore, Marker*
const markerArray[],
size_t markerCount)
33 textstore.RemoveMarkers (markerArray, markerCount);
46void TextStore::VectorMarkerSink::Append (Marker* m)
51 PUSH_BACK (*fMarkers, m);
64void TextStore::InlineBufferMarkerSink::Append (Marker* m)
68 fMarkers.push_back (m);
83TextStore::~TextStore ()
85 Require (fMarkerOwners.size () == 1 and fMarkerOwners[0] ==
this);
96void TextStore::Replace (
size_t from,
size_t to,
const Led_tChar* withWhat,
size_t withWhatCount)
99 Require (to <= GetEnd ());
101 if (from != to or withWhatCount != 0) {
102 UpdateInfo updateInfo (from, to, withWhat, withWhatCount,
true,
true);
103 SimpleUpdater updater (*
this, updateInfo);
105 ReplaceWithoutUpdate (from, to, withWhat, withWhatCount);
119size_t TextStore::GetStartOfLine (
size_t lineNumber)
const
121 Require (lineNumber >= 0);
122 Require (lineNumber <= GetLineCount ());
128 size_t curLineNum = 0;
129 size_t len = GetLength ();
130 for (
size_t i = 0; i < len; i = FindNextCharacter (i)) {
131 if (curLineNum == lineNumber) {
140 Assert (curLineNum == lineNumber);
149size_t TextStore::GetEndOfLine (
size_t lineNumber)
const
152 Require (lineNumber >= 0);
153 Require (lineNumber <= GetLineCount ());
159 size_t curLineNum = 0;
160 size_t len = GetLength ();
161 for (
size_t i = 0; i < len; i = FindNextCharacter (i)) {
165 if (curLineNum == lineNumber) {
180size_t TextStore::GetLineContainingPosition (
size_t charPosition)
const
183 Assert (charPosition >= 0);
184 Assert (charPosition <= GetEnd ());
185 size_t curLineNum = 0;
186 size_t lineStart = 0;
188 size_t len = GetLength ();
189 for (
size_t i = 0; i < len; i = FindNextCharacter (i)) {
191 if (Contains (lineStart, lineEnd, charPosition)) {
210size_t TextStore::GetLineCount ()
const
217 size_t curLineNum = 0;
218 size_t len = GetLength ();
219 for (
size_t i = 0; i < len; i = FindNextCharacter (i)) {
234size_t TextStore::GetStartOfLineContainingPosition (
size_t afterPos)
const
236 Assert (afterPos >= 0);
237 Assert (afterPos <= GetEnd ());
249 size_t lastReadAtPos = afterPos;
250 Led_tChar charBuf[64];
251 const size_t kBufSize =
sizeof (charBuf) /
sizeof (charBuf[0]);
252 Assert (afterPos < INT_MAX);
253 for (ptrdiff_t curPos = afterPos - 1; curPos > -1; --curPos) {
254 Assert (curPos >= 0);
255 if (lastReadAtPos >
static_cast<size_t> (curPos)) {
256 if (lastReadAtPos > kBufSize) {
257 lastReadAtPos -= kBufSize;
262 Assert (lastReadAtPos >= 0);
263 CopyOut (lastReadAtPos, min (kBufSize, GetLength ()), charBuf);
265 Assert (curPos - lastReadAtPos < kBufSize);
266 Led_tChar& thisChar = charBuf[curPos - lastReadAtPos];
269#if qStroika_Foundation_Debug_AssertionsChecked && 0
272 CopyOut (curPos, 1, &xxx);
273 Assert (xxx == thisChar);
277 if (thisChar ==
'\n') {
289size_t TextStore::GetEndOfLineContainingPosition (
size_t afterPos)
const
291 Assert (afterPos >= 0);
292 Assert (afterPos <= GetEnd ());
304 size_t lastReadAtPos = 0;
305 Led_tChar charBuf[64];
306 const size_t kBufSize =
sizeof (charBuf) /
sizeof (charBuf[0]);
307 for (
size_t curPos = afterPos; curPos + 1 <= GetLength (); ++curPos) {
308 if (lastReadAtPos == 0 or lastReadAtPos + kBufSize <= curPos) {
309 lastReadAtPos = curPos;
310 Assert (lastReadAtPos >= 0);
311 CopyOut (lastReadAtPos, min (kBufSize, GetLength () - (lastReadAtPos)), charBuf);
313 Assert (curPos - lastReadAtPos < kBufSize);
314 Led_tChar& thisChar = charBuf[curPos - lastReadAtPos];
317 CopyOut (curPos, 1, &xxx);
318 Assert (xxx == thisChar);
320 if (thisChar ==
'\n') {
332size_t TextStore::FindPreviousCharacter (
size_t beforePos)
const
334 Assert (beforePos >= 0);
335 if (beforePos == 0) {
339 size_t newPos = beforePos - 1;
340 Ensure (newPos >= 0);
353void TextStore::FindWordBreaks (
size_t afterPosition,
size_t* wordStartResult,
size_t* wordEndResult,
bool* wordReal, TextBreaks* useTextBreaker)
358 size_t startOfThisLine = GetStartOfLineContainingPosition (afterPosition);
359 size_t endOfThisLine = GetEndOfLineContainingPosition (afterPosition);
360 size_t len = endOfThisLine - startOfThisLine;
363 CopyOut (startOfThisLine, len, buf.data ());
365 Assert (afterPosition >= startOfThisLine);
366 Assert (afterPosition <= endOfThisLine);
367 size_t zeroBasedStart = 0;
368 size_t zeroBasedEnd = 0;
369 if (useTextBreaker ==
nullptr) {
370 useTextBreaker = GetTextBreaker ().get ();
373 useTextBreaker->FindWordBreaks (buf.data (), len, afterPosition - startOfThisLine, &zeroBasedStart, &zeroBasedEnd, wordReal);
374 Assert (zeroBasedStart <= zeroBasedEnd);
375 Assert (zeroBasedEnd <= len);
376 *wordStartResult = zeroBasedStart + startOfThisLine;
377 *wordEndResult = zeroBasedEnd + startOfThisLine;
386void TextStore::FindLineBreaks (
size_t afterPosition,
size_t* wordEndResult,
bool* wordReal, TextBreaks* useTextBreaker)
390 size_t startOfThisLine = GetStartOfLineContainingPosition (afterPosition);
391 size_t endOfThisLine = GetEndOfLineContainingPosition (afterPosition);
392 size_t len = endOfThisLine - startOfThisLine;
395 CopyOut (startOfThisLine, len, buf.data ());
397 Assert (afterPosition >= startOfThisLine);
398 Assert (afterPosition <= endOfThisLine);
399 size_t zeroBasedEnd = 0;
400 if (useTextBreaker ==
nullptr) {
401 useTextBreaker = GetTextBreaker ().get ();
404 useTextBreaker->FindLineBreaks (buf.data (), len, afterPosition - startOfThisLine, &zeroBasedEnd, wordReal);
405 Assert (zeroBasedEnd <= len);
406 *wordEndResult = zeroBasedEnd + startOfThisLine;
409size_t TextStore::FindFirstWordStartBeforePosition (
size_t position,
bool wordMustBeReal)
419 for (
size_t goalPos = position; goalPos > 0; goalPos = FindPreviousCharacter (goalPos)) {
420 size_t wordStart = 0;
422 bool wordReal =
false;
423 FindWordBreaks (goalPos, &wordStart, &wordEnd, &wordReal);
424 if (wordStart <= position and (not wordMustBeReal or wordReal)) {
431size_t TextStore::FindFirstWordStartStrictlyBeforePosition (
size_t position,
bool wordMustBeReal)
443 for (
size_t goalPos = FindPreviousCharacter (position); goalPos > 0; goalPos = FindPreviousCharacter (goalPos)) {
444 size_t wordStart = 0;
446 bool wordReal =
false;
447 FindWordBreaks (goalPos, &wordStart, &wordEnd, &wordReal);
448 if (wordStart != wordEnd and wordStart < position and (not wordMustBeReal or wordReal)) {
455size_t TextStore::FindFirstWordEndAfterPosition (
size_t position,
bool wordMustBeReal)
465 for (
size_t goalPos = FindPreviousCharacter (position); goalPos < GetEnd (); goalPos = FindNextCharacter (goalPos)) {
466 size_t wordStart = 0;
468 bool wordReal =
false;
469 FindWordBreaks (goalPos, &wordStart, &wordEnd, &wordReal);
470 if (wordStart != wordEnd and wordEnd >= position and (not wordMustBeReal or wordReal)) {
477size_t TextStore::FindFirstWordStartAfterPosition (
size_t position)
486 for (
size_t goalPos = position; goalPos < GetEnd (); goalPos = FindNextCharacter (goalPos)) {
487 size_t wordStart = 0;
489 bool wordReal =
false;
490 FindWordBreaks (goalPos, &wordStart, &wordEnd, &wordReal);
491 if (wordStart != wordEnd and wordStart >= position and wordReal) {
503size_t TextStore::Find (
const SearchParameters& params,
size_t searchFrom,
size_t searchTo)
505 Require (searchTo == eUseSearchParameters or searchTo <= GetEnd ());
507 const Led_tChar* pattern = params.fMatchString.c_str ();
508 bool matchCase = params.fCaseSensativeSearch;
509 bool wholeWords = params.fWholeWordSearch;
510 bool wrapAtEnd = (searchTo == eUseSearchParameters) ? params.fWrapSearch : false;
512 bool wrappedYet =
false;
513 size_t patternLen = Led_tStrlen (pattern);
514 size_t bufferEnd = (searchTo == eUseSearchParameters) ? GetEnd () : searchTo;
515 size_t searchIdx = searchFrom;
520 if (searchIdx + patternLen > bufferEnd) {
521 goto notFoundByBuffersEnd;
523 if (searchIdx >= bufferEnd) {
524 goto notFoundByBuffersEnd;
528 CopyOut (searchIdx, patternLen, lookingAtData.data ());
529 for (
size_t i = 0; i < patternLen; ++i) {
530 bool charsEqual = (lookingAtData[i] == pattern[i]);
531 if (not matchCase and not charsEqual) {
534#if qUseWin32CompareStringCallForCaseInsensitiveSearch
535#define X_COMPARESTRING ::CompareStringW
536 if (X_COMPARESTRING (LOCALE_USER_DEFAULT, NORM_IGNORECASE, &lookingAtData[i], 1, &pattern[i], 1) == CSTR_EQUAL) {
539#undef X_COMPARESTRING
541 if (IsASCIIAlpha (lookingAtData[i]) and IsASCIIAlpha (pattern[i]) and (tolower (lookingAtData[i]) == tolower (pattern[i]))) {
547 if (not charsEqual) {
568 CopyOut (searchIdx, 1, &c);
569 bool boundaryChar = (isascii (c) and (isspace (c) or ispunct (c)));
570 if (not boundaryChar) {
575 CopyOut (searchIdx - 1, 1, &c);
576 boundaryChar = (isascii (c) and (isspace (c) or ispunct (c)));
577 if (not boundaryChar) {
587 if (patternLen > 0) {
588 size_t lastCharMatchedIdx = searchIdx + (patternLen - 1);
589 CopyOut (lastCharMatchedIdx, 1, &c);
590 boundaryChar = (isascii (c) and (isspace (c) or ispunct (c)));
591 if (not boundaryChar) {
595 if (lastCharMatchedIdx < GetEnd ()) {
596 CopyOut (lastCharMatchedIdx + 1, 1, &c);
597 boundaryChar = (isascii (c) and (isspace (c) or ispunct (c)));
598 if (not boundaryChar) {
638void TextStore::DoAboutToUpdateCalls (
const UpdateInfo& updateInfo, Marker*
const* markersBegin, Marker*
const* markersEnd)
645 vector<MarkerOwner*>::const_iterator start = GetMarkerOwners ().begin ();
646 vector<MarkerOwner*>::const_iterator end = GetMarkerOwners ().end ();
647 for (
auto i = start; i != end; ++i) {
648 (*i)->AboutToUpdateText (updateInfo);
652 for (Marker*
const* i = markersBegin; i != markersEnd; ++i) {
653 (*i)->AboutToUpdateText (updateInfo);
666void TextStore::DoDidUpdateCalls (
const UpdateInfo& updateInfo, Marker*
const* markersBegin, Marker*
const* markersEnd)
noexcept
668 vector<MarkerOwner*> markerOwners = GetMarkerOwners ();
674 vector<MarkerOwner*>::reverse_iterator start = markerOwners.rbegin ();
675 vector<MarkerOwner*>::reverse_iterator end = markerOwners.rend ();
676 for (
auto i = start; i != end; ++i) {
677 Assert (GetMarkerOwners () == markerOwners);
678 (*i)->EarlyDidUpdateText (updateInfo);
679 Assert (GetMarkerOwners () == markerOwners);
684 for (Marker*
const* i = markersEnd; i != markersBegin;) {
687 (*i)->DidUpdateText (updateInfo);
688 Assert (GetMarkerOwners () == markerOwners);
693 vector<Marker*>::const_reverse_iterator start = markers.rbegin ();
694 vector<Marker*>::const_reverse_iterator end = markers.rend ();
695 for (
auto i = start; i != end; ++i) {
696 (*i)->DidUpdateText (updateInfo);
697 Assert (GetMarkerOwners () == markerOwners);
702 vector<MarkerOwner*>::reverse_iterator start = markerOwners.rbegin ();
703 vector<MarkerOwner*>::reverse_iterator end = markerOwners.rend ();
704 for (
auto i = start; i != end; ++i) {
705 Assert (GetMarkerOwners () == markerOwners);
706 (*i)->DidUpdateText (updateInfo);
707 Assert (GetMarkerOwners () == markerOwners);
716TextStore* TextStore::PeekAtTextStore ()
const
718 return const_cast<TextStore*
> (
this);
721#if qStroika_Foundation_Debug_AssertionsChecked
727void TextStore::Invariant_ ()
const
729 size_t len = GetLength ();
731 CopyOut (0, len, buf.data ());
732 const Led_tChar* start = buf.data ();
733 const Led_tChar* end = &start[len];
734 const Led_tChar* curChar = start;
735 for (; curChar < end; curChar = Led_NextChar (curChar)) {
738 Assert (curChar == end);
#define qStroika_Foundation_Debug_AssertionsChecked
The qStroika_Foundation_Debug_AssertionsChecked flag determines if assertions are checked and validat...
#define RequireNotNull(p)
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...