4#include "Stroika/Frameworks/StroikaPreComp.h"
16#include "Stroika/Foundation/Streams/TextToBinary.h"
18#include "SpellCheckEngine_Basic.h"
25using namespace Stroika::Frameworks;
26using namespace Stroika::Frameworks::Led;
32#if qStroika_Frameworks_Led_IncludeBakedInDictionaries
33#include "Dictionary-Compiled-US-English.inc"
36 inline bool IsASCIIUpper (Led_tChar c)
38 return isascii (c) and isupper (c);
41 inline bool AsymmetricCaseInsensativeCompare (Led_tChar wordChar, Led_tChar dictChar)
43 if (wordChar == dictChar) {
46 if (isascii (wordChar) and isupper (wordChar)) {
47 return tolower (wordChar) == dictChar;
51 inline bool AsymmetricCaseInsensativeCompare (
const Led_tChar* word,
const Led_tChar* dictWord)
53 const Led_tChar* wi = word;
54 const Led_tChar* di = dictWord;
56 if (not AsymmetricCaseInsensativeCompare (*wi, *di)) {
59 if (*wi ==
'\0' and *di ==
'\0') {
62 if (*wi ==
'\0' or *di ==
'\0') {
68 struct DictLookup_Compare {
69 DictLookup_Compare (
const Led_tChar* base)
73 bool operator() (
const SpellCheckEngine_Basic::InfoBlock& _Left,
const Led_tString& _Right)
const
76 bool answer = _Right.compare (0, _Right.length (), fBase + _Left.fIndex, _Left.fWordLen) > 0;
78 Led_tString left = Led_tString{fBase + _Left.fIndex, fBase + _Left.fIndex + _Left.fWordLen};
79 Assert (answer == (left < _Right));
83 const Led_tChar* fBase;
88 inline bool IsASCIISpace (Led_tChar c)
90 return isascii (c) and isspace (c);
92 inline bool IsASCIIAlnum (Led_tChar c)
94 return isascii (c) and isalnum (c);
103#if qStroika_Frameworks_Led_IncludeBakedInDictionaries
104const SpellCheckEngine_Basic::CompiledDictionary SpellCheckEngine_Basic::kDictionary_US_English (Dictionary_US_English);
107SpellCheckEngine_Basic::SpellCheckEngine_Basic (
const Dictionary* mainDictionary)
111 if (mainDictionary != NULL) {
112 fDictionaries.push_back (mainDictionary);
116SpellCheckEngine_Basic::~SpellCheckEngine_Basic ()
124bool SpellCheckEngine_Basic::ScanForUndefinedWord (
const Led_tChar* startBuf,
const Led_tChar* endBuf,
const Led_tChar** cursor,
125 const Led_tChar** wordStartResult,
const Led_tChar** wordEndResult)
132 Require (*cursor == NULL or (*cursor >= startBuf and *cursor <= endBuf));
134 if (*cursor == NULL) {
139 while (ScanForWord (startBuf, endBuf, cursor, wordStartResult, wordEndResult)) {
140 if (not LookupWord (Led_tString{*wordStartResult, *wordEndResult}) and not OtherStringToIgnore (Led_tString{*wordStartResult, *wordEndResult})) {
152bool SpellCheckEngine_Basic::LookupWord_ (
const Led_tString& checkWord, Led_tString* matchedWordResult)
159 if (LookupWordHelper_ (checkWord, matchedWordResult)) {
163 if (checkWord.empty ()) {
173 if (checkWord[0] < 127 and isupper (checkWord[0])) {
174 Led_tString w2 = checkWord;
175 w2[0] =
static_cast<char> (tolower (w2[0]));
176 if (LookupWordHelper_ (w2, matchedWordResult)) {
185 bool allUpper =
true;
186 Led_tString caseFixedWord = checkWord;
187 for (
auto i = caseFixedWord.begin (); i != caseFixedWord.end (); ++i) {
188 if (IsASCIIUpper (*i)) {
189 *i =
static_cast<char> (tolower (*i));
196 if (allUpper and LookupWordHelper_ (caseFixedWord, matchedWordResult)) {
199 caseFixedWord[0] = checkWord[0];
200 if (allUpper and LookupWordHelper_ (caseFixedWord, matchedWordResult)) {
222 size_t lastFoundAt = 0;
223 bool mustCheckLastSeg =
false;
224 for (
size_t i = checkWord.find (
'-'); i != Led_tString::npos or mustCheckLastSeg; i = checkWord.find (
'-', i + 1)) {
225 if (i == Led_tString::npos) {
226 Assert (mustCheckLastSeg);
227 mustCheckLastSeg =
false;
228 i = checkWord.length ();
231 mustCheckLastSeg =
true;
233 Led_tString segWord = Led_tString{checkWord.substr (lastFoundAt, i - lastFoundAt)};
235 if (segWord.empty ()) {
239 else if (LookupWord_ (segWord, &tmpMWR)) {
240 if (lastFoundAt != 0) {
241 genMWR += LED_TCHAR_OF (
"-");
245 else if (i != checkWord.length () and LookupWordHelper_ (segWord + LED_TCHAR_OF (
"-"), &tmpMWR)) {
246 if (lastFoundAt != 0) {
247 genMWR += LED_TCHAR_OF (
"-");
249 Assert (tmpMWR.length () >= 2);
250 genMWR += tmpMWR.substr (0, tmpMWR.length () - 1);
261 if (not genMWR.empty ()) {
262 if (matchedWordResult != NULL) {
263 *matchedWordResult = genMWR;
270 const wchar_t kRightSingleQuotationMark = L
'\x2019';
271 size_t apos = checkWord.find (kRightSingleQuotationMark);
272 if (apos != Led_tString::npos) {
273 Led_tString tmp = checkWord;
275 return LookupWord_ (tmp, matchedWordResult);
293bool SpellCheckEngine_Basic::LookupWordHelper_ (
const Led_tString& checkWord, Led_tString* matchedWordResult)
const
297 for (
auto i = fDictionaries.begin (); i != fDictionaries.end (); ++i) {
298 const Dictionary* dict = *i;
301 const InfoBlock* ibsStart = NULL;
302 const InfoBlock* ibsEnd = NULL;
303 dict->GetInfoBlocks (&ibsStart, &ibsEnd);
304 const Led_tChar* dictBufStart = dict->GetTextBase ();
306 const InfoBlock* r = lower_bound (ibsStart, ibsEnd, checkWord, DictLookup_Compare (dictBufStart));
308 Led_tString x = Led_tString{dictBufStart + (*r).fIndex, dictBufStart + (*r).fIndex + (*r).fWordLen};
309 if (x == checkWord) {
310 if (matchedWordResult != NULL) {
311 *matchedWordResult = x;
327bool SpellCheckEngine_Basic::OtherStringToIgnore (
const Led_tString& checkWord)
329 return OtherStringToIgnore_AllPunctuation (checkWord) or OtherStringToIgnore_Sentinels (checkWord) or OtherStringToIgnore_Number (checkWord);
332bool SpellCheckEngine_Basic::OtherStringToIgnore_AllPunctuation (
const Led_tString& checkWord)
334 for (
size_t i = 0; i < checkWord.length (); ++i) {
335 Led_tChar c = checkWord[i];
336 if (not
Character (c).IsPunctuation ()) {
343bool SpellCheckEngine_Basic::OtherStringToIgnore_Number (
const Led_tString& checkWord)
345 for (
size_t i = 0; i < checkWord.length (); ++i) {
346 Led_tChar c = checkWord[i];
361 template <
class B
idirectionalIterator1,
class B
idirectionalIterator2>
362 BidirectionalIterator2 My_copy_backward_ (BidirectionalIterator1 first, BidirectionalIterator1 last, BidirectionalIterator2 result)
364 while (last != first)
365 *(--result) = *(--last);
371 void AddToListsHelper (
const size_t kMaxSug, Led_tString topSugs[],
float topSugScores[],
float* scoreCutOff,
float s,
const Led_tString& w)
374 if (s > *scoreCutOff) {
376 for (
size_t ii = 0; ii < kMaxSug; ++ii) {
377 if (s > topSugScores[ii]) {
378 My_copy_backward_ (topSugs + ii, topSugs + kMaxSug - 1, topSugs + kMaxSug);
379 My_copy_backward_ (topSugScores + ii, topSugScores + kMaxSug - 1, topSugScores + kMaxSug);
381 topSugScores[ii] = s;
385 *scoreCutOff = topSugScores[kMaxSug - 1];
390vector<Led_tString> SpellCheckEngine_Basic::GenerateSuggestions (
const Led_tString& misspelledWord)
392 const size_t kMaxSug = 8;
393 Led_tString topSugs[kMaxSug];
394 float topSugScores[kMaxSug];
395 float scoreCutOff = -100000.0f;
397 fill (topSugScores, topSugScores + kMaxSug, scoreCutOff);
400 if (misspelledWord.find (
'-') == Led_tString::npos) {
401 size_t maxInitSegSize = misspelledWord.size () - 1;
402 for (
size_t initialWordSize = 1; initialWordSize < maxInitSegSize; ++initialWordSize) {
403 if (LookupWord (misspelledWord.substr (0, initialWordSize)) and LookupWord (misspelledWord.substr (initialWordSize))) {
404 AddToListsHelper (kMaxSug, topSugs, topSugScores, &scoreCutOff, -1.2f,
405 misspelledWord.substr (0, initialWordSize) + LED_TCHAR_OF (
"-") + misspelledWord.substr (initialWordSize));
411 for (
auto i = fDictionaries.begin (); i != fDictionaries.end (); ++i) {
412 const Dictionary* dict = *i;
414 const InfoBlock* ibsStart = NULL;
415 const InfoBlock* ibsEnd = NULL;
416 dict->GetInfoBlocks (&ibsStart, &ibsEnd);
417 const Led_tChar* dictBufStart = dict->GetTextBase ();
419 for (
const InfoBlock* ib = ibsStart; ib != ibsEnd; ++ib) {
420 Led_tString w = Led_tString{dictBufStart + (*ib).fIndex, dictBufStart + (*ib).fIndex + (*ib).fWordLen};
421 float s = Heuristic (misspelledWord, w, scoreCutOff);
422 AddToListsHelper (kMaxSug, topSugs, topSugScores, &scoreCutOff, s, w);
429 if (not misspelledWord.empty ()) {
430 Led_tString topSug = topSugs[0];
431 if (not topSug.empty ()) {
432 bool capitalize =
false;
433 bool allCaps =
false;
435 if (
Character (misspelledWord[0]).IsAlphabetic () and
Character (topSug[0]).IsAlphabetic ()) {
436 if (isupper (misspelledWord[0]) and not isupper (topSug[0])) {
440 for (
size_t i = 0; i < misspelledWord.length (); ++i) {
441 if (
Character (misspelledWord[i]).IsAlphabetic ()) {
442 if (isupper (misspelledWord[i])) {
453 Led_tString newWord = topSug;
455 for (
size_t i = 0; i < newWord.length (); ++i) {
456 if (
Character (newWord[i]).IsAlphabetic ()) {
457 newWord[i] =
static_cast<char> (toupper (newWord[i]));
461 float newScore = topSugScores[0];
463 AddToListsHelper (kMaxSug, topSugs, topSugScores, &scoreCutOff, newScore, newWord);
465 else if (capitalize) {
466 Led_tString newWord = topSug;
468 if (
Character (newWord[0]).IsAlphabetic ()) {
469 newWord[0] =
static_cast<char> (toupper (newWord[0]));
472 float newScore = topSugScores[0];
474 AddToListsHelper (kMaxSug, topSugs, topSugScores, &scoreCutOff, newScore, newWord);
480 size_t endScoreList = kMaxSug;
482 const float kTrigger = 1.5f;
483 float lastScore = 0.0f;
484 for (
size_t i = 0; i < kMaxSug; ++i) {
486 if (fabs (lastScore - topSugScores[i]) > kTrigger) {
491 lastScore = topSugScores[i];
495 vector<Led_tString> result;
496 for (
size_t j = 0; j < endScoreList; ++j) {
497 if (topSugs[j].empty ()) {
501 result.push_back (topSugs[j]);
507SpellCheckEngine_Basic::UDInterface* SpellCheckEngine_Basic::GetUDInterface ()
512TextBreaks* SpellCheckEngine_Basic::PeekAtTextBreaksUsed ()
514 return GetTextBreaker ().get ();
517float SpellCheckEngine_Basic::Heuristic (
const Led_tString& misspelledWord,
const Led_tString& candidateWord,
float atLeast)
521 size_t mwl = misspelledWord.length ();
522 size_t cwl = candidateWord.length ();
523 float thisCharImportance = 2.0f;
524 for (
size_t i = 0; i < mwl; ++i) {
526 h -= thisCharImportance * 5.0f;
528 else if (misspelledWord[i] != candidateWord[i]) {
532 if (AsymmetricCaseInsensativeCompare (misspelledWord[i], candidateWord[i])) {
533 h -= thisCharImportance * 0.1f;
537 if (i > 0 and AsymmetricCaseInsensativeCompare (misspelledWord[i], candidateWord[i - 1])) {
538 h -= thisCharImportance * 0.5f;
540 else if (i + 1 < cwl and AsymmetricCaseInsensativeCompare (misspelledWord[i], candidateWord[i + 1])) {
541 h -= thisCharImportance * 0.5f;
544 h -= thisCharImportance * 3.0f;
550 if (i < mwl and (misspelledWord[i] == candidateWord[i + 1]) and (misspelledWord[i + 1] == candidateWord[i])) {
551 float thisLetterCost = h - prevH;
552 h += (-thisLetterCost) * 1.2f;
563 thisCharImportance *= 0.5f;
566 thisCharImportance *= 0.8f;
569 thisCharImportance *= 0.9f;
573 const float kPenaltyForLettersOffEnd = 0.5f;
574 const float kPenaltyForLettersOffEndGrowthFactor = 1.2f;
575 float curPenalty = kPenaltyForLettersOffEnd;
576 for (
size_t i = mwl; i < cwl; ++i) {
579 curPenalty *= kPenaltyForLettersOffEndGrowthFactor;
593bool SpellCheckEngine_Basic::ScanForWord (
const Led_tChar* startBuf,
const Led_tChar* endBuf,
const Led_tChar** cursor,
594 const Led_tChar** wordStartResult,
const Led_tChar** wordEndResult)
601 Require ((*cursor >= startBuf and *cursor <= endBuf));
603 if (*cursor >= endBuf) {
607 size_t bufLen = endBuf - startBuf;
609 size_t initialCrs = *cursor - startBuf;
610 size_t p = initialCrs;
611 size_t wordStart = 0;
613 bool wordReal =
false;
615 while (not wordReal or wordStart < initialCrs) {
616 GetTextBreaker ()->FindWordBreaks (startBuf, bufLen, p, &wordStart, &wordEnd, &wordReal);
617 if (not wordReal or wordStart < initialCrs) {
619 p = Led_NextChar (&startBuf[p]) - startBuf;
634 *cursor = startBuf + wordEnd;
639 Assert (wordStart < wordEnd);
642 Assert (*cursor <= startBuf + p);
643 Assert ((*cursor < startBuf + p) or (*cursor == endBuf));
644 *cursor = startBuf + p;
646 *wordStartResult = startBuf + wordStart;
647 *wordEndResult = startBuf + wordEnd;
652vector<const SpellCheckEngine_Basic::Dictionary*> SpellCheckEngine_Basic::GetDictionaries ()
const
654 return fDictionaries;
657void SpellCheckEngine_Basic::SetDictionaries (
const vector<const Dictionary*>& dictionaries)
659 fDictionaries = dictionaries;
662#if qStroika_Foundation_Debug_AssertionsChecked
667void SpellCheckEngine_Basic::Invariant_ ()
const
669 Assert (
sizeof (InfoBlock) ==
sizeof (
int));
671 for (
const Dictionary* dict : fDictionaries) {
673 const InfoBlock* ibsStart = NULL;
674 const InfoBlock* ibsEnd = NULL;
675 dict->GetInfoBlocks (&ibsStart, &ibsEnd);
676 const Led_tChar* dictBufStart = dict->GetTextBase ();
677 const Led_tChar* dictBufEnd = dict->GetTextEnd ();
678 if (dictBufStart != dictBufEnd) {
681 Assert (dictBufStart <= dictBufEnd);
682 size_t bufSize = dictBufEnd - dictBufStart;
683 Led_tString prevWord;
684 for (
const InfoBlock* i = ibsStart; i != ibsEnd; ++i) {
686 Assert ((*i).fIndex < bufSize);
687 Assert ((*i).fIndex + (*i).fWordLen <= bufSize);
688 Assert ((*i).fWordLen > 0);
689 Led_tString w = Led_tString{dictBufStart + (*i).fIndex, dictBufStart + (*i).fIndex + (*i).fWordLen};
690 Assert (not w.empty ());
693 Assert (Led_tStrCmp (prevWord.c_str (), w.c_str ()) < 0);
702#if qStroika_Foundation_Debug_AssertionsChecked
703void SpellCheckEngine_Basic::RegressionTest ()
715void SpellCheckEngine_Basic::RegressionTest_1 ()
717 const Led_tChar* xxx = LED_TCHAR_OF (
"IBM\na\napple\ndog\nfrog\ngood\nthis\nzipper\n");
718 SpellCheckEngine_Basic::EditableDictionary testerDict;
719 testerDict.ReadFromBuffer (xxx, xxx + Led_tStrlen (xxx));
720 SpellCheckEngine_Basic tester (&testerDict);
721 const Led_tChar* testText = LED_TCHAR_OF (
"This is a very good test. ");
726 bool r1 = tester.LookupWord (LED_TCHAR_OF (
"Frog"));
732 bool r2n = tester.LookupWord (LED_TCHAR_OF (
"ziPPer"), &r2S);
733 bool r2y = tester.LookupWord (LED_TCHAR_OF (
"Zipper"), &r2S);
734 Assert (not r2n and r2y and r2S == LED_TCHAR_OF (
"zipper"));
739 Assert (not tester.LookupWord (LED_TCHAR_OF (
"ibm")));
740 Assert (not tester.LookupWord (LED_TCHAR_OF (
"Ibm")));
741 bool r3 = tester.LookupWord (LED_TCHAR_OF (
"IBM"), &r3S);
742 Assert (r3 and r3S == LED_TCHAR_OF (
"IBM"));
746 const Led_tChar* wordStart = NULL;
747 const Led_tChar* wordEnd = NULL;
748 const Led_tChar* p = NULL;
749 bool result = tester.ScanForUndefinedWord (testText, testText + Led_tStrlen (testText), &p, &wordStart, &wordEnd);
750 Assert (result and (Led_tString{wordStart, wordEnd} == LED_TCHAR_OF (
"is")));
754 const Led_tChar* cursor = NULL;
755 const Led_tChar* wordStartResult = NULL;
756 const Led_tChar* wordEndResult = NULL;
758 while (tester.ScanForUndefinedWord (testText, testText + Led_tStrlen (testText), &cursor, &wordStartResult, &wordEndResult)) {
760 Led_tString word = Led_tString{wordStartResult, wordEndResult};
762 if (nWordsFound == 1) {
763 Assert ((Led_tString{wordStartResult, wordEndResult} == LED_TCHAR_OF (
"is")));
765 if (nWordsFound == 2) {
766 Assert ((Led_tString{wordStartResult, wordEndResult} == LED_TCHAR_OF (
"very")));
768 if (nWordsFound == 3) {
769 Assert ((Led_tString{wordStartResult, wordEndResult} == LED_TCHAR_OF (
"test")));
772 Assert (nWordsFound == 3);
782SpellCheckEngine_Basic::EditableDictionary::~EditableDictionary ()
784 delete[] fDictBufStart;
787void SpellCheckEngine_Basic::EditableDictionary::AddWordToUserDictionary (
const Led_tString& word)
789 fSortedWordList.insert (word);
790 ConstructInfoBlocksEtcFromWordList ();
793const Led_tChar* SpellCheckEngine_Basic::EditableDictionary::GetTextBase ()
const
795 return fDictBufStart;
798const Led_tChar* SpellCheckEngine_Basic::EditableDictionary::GetTextEnd ()
const
803void SpellCheckEngine_Basic::EditableDictionary::GetInfoBlocks (
const InfoBlock** start,
const InfoBlock** end)
const
811void SpellCheckEngine_Basic::EditableDictionary::ReadFromBuffer (
const Led_tChar* readOnlyRAMDictStart,
const Led_tChar* readOnlyRAMDictEnd)
813 fSortedWordList.clear ();
817 for (
const Led_tChar* p = readOnlyRAMDictStart; p < readOnlyRAMDictEnd;) {
819 const Led_tChar* i1 = Led_tStrChr (p,
'\r');
820 const Led_tChar* i2 = Led_tStrChr (p,
'\n');
822 i1 = p + Led_tStrlen (p);
825 i2 = p + Led_tStrlen (p);
827 const Led_tChar* wordStart = p;
828 const Led_tChar* wordEnd = min (i1, i2);
829 if (wordStart != wordEnd) {
830 fSortedWordList.insert (Led_tString{wordStart, wordEnd});
834 if (p < readOnlyRAMDictEnd) {
842 ConstructInfoBlocksEtcFromWordList ();
845vector<Led_tChar> SpellCheckEngine_Basic::EditableDictionary::SaveToBuffer ()
const
847 StackBuffer<Led_tChar> buf{1};
849#if qStroika_Platform_Windows
850 const Led_tChar kLineTerm[] = LED_TCHAR_OF (
"\r\n");
852 const Led_tChar kLineTerm[] = LED_TCHAR_OF (
"\n");
854 const size_t kLineTerm_Length = std::size (kLineTerm) - 1;
855 size_t totalBufSizeSoFar = 0;
856 for (
auto i = fSortedWordList.begin (); i != fSortedWordList.end (); ++i) {
858 size_t prevCopyTo = totalBufSizeSoFar;
859 totalBufSizeSoFar += (*i).length ();
860 buf.GrowToSize (totalBufSizeSoFar);
861 (void)::memcpy (
static_cast<Led_tChar*
> (buf) + prevCopyTo,
Containers::Start (*i), i->size () *
sizeof (Led_tChar));
864 size_t prevCopyTo = totalBufSizeSoFar;
865 totalBufSizeSoFar += kLineTerm_Length;
866 buf.GrowToSize (totalBufSizeSoFar);
867 (void)::memcpy (
static_cast<Led_tChar*
> (buf) + prevCopyTo, kLineTerm, kLineTerm_Length *
sizeof (Led_tChar));
870 return vector<Led_tChar> (
static_cast<Led_tChar*
> (buf),
static_cast<Led_tChar*
> (buf) + totalBufSizeSoFar);
877 template <
class InputIterator,
class OutputIterator>
878 OutputIterator my_copy_ (InputIterator first, InputIterator last, OutputIterator result)
880 while (first != last) {
889void SpellCheckEngine_Basic::EditableDictionary::ConstructInfoBlocksEtcFromWordList ()
892 delete[] fDictBufStart;
893 fDictBufStart = NULL;
895 fInfoBlocks.clear ();
897 size_t totalBlockSize = 0;
899 for (
auto i = fSortedWordList.begin (); i != fSortedWordList.end (); ++i) {
900 totalBlockSize += (*i).length ();
904 fDictBufStart =
new Led_tChar[totalBlockSize];
905 fDictBufEnd = fDictBufStart + totalBlockSize;
911 Led_tChar* intoBufPtr = fDictBufStart;
912 for (
auto i = fSortedWordList.begin (); i != fSortedWordList.end (); ++i) {
913 my_copy_ ((*i).begin (), (*i).end (), intoBufPtr);
916 (void)::memset (&iB, 0,
sizeof (iB));
917 iB.fIndex = intoBufPtr - fDictBufStart;
918 iB.fWordLen = (*i).length ();
920 Assert (iB.fWordLen > 0);
921 fInfoBlocks.push_back (iB);
923 intoBufPtr += (*i).length ();
932SpellCheckEngine_Basic::CompiledDictionary::CompiledDictionary (
const CompiledDictionaryData& data)
937const Led_tChar* SpellCheckEngine_Basic::CompiledDictionary::GetTextBase ()
const
939 return fData.fTextDataStart;
942const Led_tChar* SpellCheckEngine_Basic::CompiledDictionary::GetTextEnd ()
const
944 return fData.fTextDataEnd;
947void SpellCheckEngine_Basic::CompiledDictionary::GetInfoBlocks (
const InfoBlock** start,
const InfoBlock** end)
const
951 *start = fData.fInfoBlocksStart;
952 *end = fData.fInfoBlocksEnd;
961TextBreaks_SpellChecker::TextBreaks_SpellChecker ()
963#if qStroika_Foundation_Debug_AssertionsChecked
972TextBreaks_SpellChecker::CharacterClasses TextBreaks_SpellChecker::CharToCharacterClass (
const Led_tChar* startOfText,
size_t lengthOfText,
973 const Led_tChar* charToExamine)
const
975 switch (*charToExamine) {
985 if (charToExamine > startOfText and charToExamine < &startOfText[lengthOfText]) {
986 const Led_tChar* prevChar = Led_PreviousChar (startOfText, charToExamine);
987 const Led_tChar* nextChar = charToExamine + 1;
990 if ((IsASCIIAlnum (*prevChar) and *nextChar ==
's') or (*prevChar ==
's' and IsASCIISpace (*nextChar))) {
996 return inherited::CharToCharacterClass (startOfText, lengthOfText, charToExamine);
999#if qStroika_Foundation_Debug_AssertionsChecked
1000void TextBreaks_SpellChecker::RegressionTest ()
1003 const Led_tChar* kTest = LED_TCHAR_OF (
"This is a good test of Simone's bug with the 'word'.");
1004 size_t wordEndResult = 0;
1007 FindLineBreaks (kTest, Led_tStrlen (kTest), 25, &wordEndResult, &wordReal);
1008 Assert (wordEndResult == 31);
1009 Assert (wordReal ==
true);
1019SpellCheckEngine_Basic_Simple::SpellCheckEngine_Basic_Simple ()
1020 : fMainDictionary{nullptr}
1024#if qStroika_Frameworks_Led_IncludeBakedInDictionaries
1025 SetMainDictionary (&kDictionary_US_English);
1029SpellCheckEngine_Basic_Simple::~SpellCheckEngine_Basic_Simple ()
1034SpellCheckEngine_Basic_Simple::UDInterface* SpellCheckEngine_Basic_Simple::GetUDInterface ()
1039bool SpellCheckEngine_Basic_Simple::AddWordToUserDictionarySupported ()
const
1044void SpellCheckEngine_Basic_Simple::AddWordToUserDictionary (
const Led_tString& word)
1046 Execution::ThrowIfNull (fUD);
1047 fUD->AddWordToUserDictionary (word);
1051const SpellCheckEngine_Basic_Simple::Dictionary* SpellCheckEngine_Basic_Simple::GetMainDictionary ()
const
1053 return fMainDictionary;
1056void SpellCheckEngine_Basic_Simple::SetMainDictionary (
const Dictionary* mainDictionary)
1058 fMainDictionary = mainDictionary;
1059 vector<const Dictionary*> dicts;
1060 if (fMainDictionary != NULL) {
1061 dicts.push_back (fMainDictionary);
1064 dicts.push_back (fUD);
1066 SetDictionaries (dicts);
1069filesystem::path SpellCheckEngine_Basic_Simple::GetUserDictionary ()
const
1074void SpellCheckEngine_Basic_Simple::SetUserDictionary (
const filesystem::path& userDictionary)
1076 fUDName = userDictionary;
1077 bool noUD = userDictionary.empty ();
1082 fUD =
new EditableDictionary{};
1085 SetMainDictionary (fMainDictionary);
1088void SpellCheckEngine_Basic_Simple::ReadFromUD ()
1094 Memory::BLOB b = IO::FileSystem::FileInputStream::New (filesystem::path (fUDName)).ReadAll ();
1095 span<const byte> rawByteSpan{b};
1097 size_t outCharCnt = converter.ComputeTargetCharacterBufferSize (rawByteSpan);
1099 auto charsRead = converter.Bytes2Characters (&rawByteSpan, span{fileData2});
1100 fUD->ReadFromBuffer (charsRead.data (), charsRead.data () + charsRead.size ());
1106void SpellCheckEngine_Basic_Simple::WriteToUD ()
1108 Execution::ThrowIfNull (fUD);
1109 vector<Led_tChar> data = fUD->SaveToBuffer ();
1110 IO::FileSystem::FileOutputStream::Ptr writer = IO::FileSystem::FileOutputStream::New (filesystem::path (fUDName));
#define qStroika_Foundation_Debug_AssertionsChecked
The qStroika_Foundation_Debug_AssertionsChecked flag determines if assertions are checked and validat...
#define RequireNotNull(p)
CodeCvt unifies byte <-> unicode conversions, vaguely inspired by (and wraps) std::codecvt,...
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
nonvirtual void Write(span< ELEMENT_TYPE2, EXTENT_2 > elts) const
CONTAINER::value_type * End(CONTAINER &c)
For a contiguous container (such as a vector or basic_string) - find the pointer to the end of the co...
CONTAINER::value_type * Start(CONTAINER &c)
For a contiguous container (such as a vector or basic_string) - find the pointer to the start of the ...
Ptr New(const Streams::OutputStream::Ptr< byte > &src, const Characters::CodeCvt<> &char2OutputConverter)