From 89febbfa1dfba396e33739d3b5fb2d4380f18587 Mon Sep 17 00:00:00 2001 From: cxl Date: Sat, 19 Jan 2019 15:32:48 +0000 Subject: [PATCH] RichEdit: Now suggesting spelling fixes git-svn-id: svn://ultimatepp.org/upp/trunk@12679 f0d560ea-af0d-0410-9eb7-867de7ffcac7 --- uppsrc/Core/Speller.cpp | 111 +++++++++++++++++++++++++++++++++++++ uppsrc/Core/Util.h | 1 + uppsrc/RichEdit/Cursor.cpp | 18 ++++-- uppsrc/RichEdit/Mouse.cpp | 15 +++++ uppsrc/RichEdit/RichEdit.h | 1 + 5 files changed, 142 insertions(+), 4 deletions(-) diff --git a/uppsrc/Core/Speller.cpp b/uppsrc/Core/Speller.cpp index a4eea4eda..5ffe69c78 100644 --- a/uppsrc/Core/Speller.cpp +++ b/uppsrc/Core/Speller.cpp @@ -319,4 +319,115 @@ void SpellerAdd(const WString& w, int lang) } } +struct WordDistanceTester { + byte pchars[256]; + byte wchars[256]; + byte pairs[256 * 256]; + int used_pairs[256]; // store used positions in pairs to be able to clear them at exit + int used_pairs_count; // count of pairs + + int Get(const String& w, const String& p); + + WordDistanceTester(); +}; + +WordDistanceTester::WordDistanceTester() +{ + Zero(pchars); + Zero(wchars); + Zero(pairs); + Zero(used_pairs); + used_pairs_count = 0; +} + +int WordDistanceTester::Get(const String& p, const String& w) +{ + if(abs(p.GetLength() - w.GetLength()) > 4 || w.GetCount() > 200 || p.GetCount() > 200) + return INT_MAX; + + VectorMap chars; + for(int i = 0; i < p.GetCount(); i++) + pchars[(byte)p[i]]++; + + int score = 0; + String pp; + VectorMap found; + for(int i = 0; i < w.GetCount(); i++) { + int c = (byte)w[i]; + if(pchars[c]) { + pchars[c]--; + wchars[c]++; + score++; + LLOG("Letter " << (char)w[i]); + } + } + + int first = 0; + used_pairs_count = 0; + for(int i = 0; i < p.GetCount(); i++) { + int c = p[i]; + if(wchars[c]) { + wchars[c]--; + if(first) { + LLOG("Pair " << (char)first << (char)c); + int pairi = MAKEWORD(first, c); + pairs[pairi]++; + used_pairs[used_pairs_count++] = pairi; + } + first = c; + } + } + + for(int i = 1; i < w.GetCount(); i++) { + int pairi = MAKEWORD(w[i - 1], w[i]); + if(pairs[pairi]) { + pairs[pairi]--; + score++; + } + } + + score -= abs(p.GetLength() - w.GetLength()); + + Zero(pchars); + Zero(wchars); + for(int i = 0; i < used_pairs_count; i++) + pairs[used_pairs[i]] = 0; + + return -score; +} + +Vector SpellerFindCloseWords(int lang, const String& w, int n) +{ + Vector r; + Vector min_distance; + if(n < 1) + return r; + String aw = ToUpper(ToAscii(w)); + One tester; + tester.Create(); + AllSpellerWords(lang, [&] (String h) -> bool { + if(abs(h.GetLength() - w.GetLength()) < 5) { + int d = tester->Get(Utf8ToUpperAscii(h), aw); + if(min_distance.GetCount() == 0) { + min_distance.Add(d); + r.Add(h); + } + else + if(d <= min_distance.Top()) { + int ii = min_distance.GetCount() - 1; + while(ii > 0 && d < min_distance[ii - 1]) + ii--; + min_distance.Insert(ii, d); + r.Insert(ii, h); + if(r.GetCount() > n) { + r.Trim(n); + min_distance.Trim(n); + } + } + } + return false; + }); + return r; +} + }; \ No newline at end of file diff --git a/uppsrc/Core/Util.h b/uppsrc/Core/Util.h index 71bfc46fa..fe38c2bfc 100644 --- a/uppsrc/Core/Util.h +++ b/uppsrc/Core/Util.h @@ -493,6 +493,7 @@ bool SpellWord(const WString& ws, int lang); bool SpellWord(const wchar *ws, int len, int lang); void SpellerAdd(const WString& w, int lang); bool AllSpellerWords(int lang, Gate iter); +Vector SpellerFindCloseWords(int lang, const String& w, int n); String GetP7Signature(const void *data, int length, const String& cert_pem, const String& pkey_pem); String GetP7Signature(const String& data, const String& cert_pem, const String& pkey_pem); diff --git a/uppsrc/RichEdit/Cursor.cpp b/uppsrc/RichEdit/Cursor.cpp index f511c08c5..bd5136bea 100644 --- a/uppsrc/RichEdit/Cursor.cpp +++ b/uppsrc/RichEdit/Cursor.cpp @@ -365,18 +365,28 @@ bool RichEdit::RemoveSelection(bool back) return false; } -WString RichEdit::GetWordAtCursor() +void RichEdit::GetWordAtCursorPos(int& pos, int& count) { WString w; int c = cursor; + pos = count = 0; if(IsLetter(text[c])) { while(c > 0 && IsLetter(text[c - 1])) c--; - while(w.GetLength() < 64 && IsLetter(text[c])) { - w.Cat(text[c]); + pos = c; + while(w.GetLength() < 64 && IsLetter(text[c])) c++; - } + count = c - pos; } +} + +WString RichEdit::GetWordAtCursor() +{ + int pos, count; + GetWordAtCursorPos(pos, count); + WString w; + for(int i = 0; i < count; i++) + w.Cat(text[i + pos]); return w; } diff --git a/uppsrc/RichEdit/Mouse.cpp b/uppsrc/RichEdit/Mouse.cpp index 945cc5c94..9d817afee 100644 --- a/uppsrc/RichEdit/Mouse.cpp +++ b/uppsrc/RichEdit/Mouse.cpp @@ -275,6 +275,21 @@ void RichEdit::StdBar(Bar& menu) WString w = GetWordAtCursor(); if(!w.IsEmpty() && !SpellWord(w, w.GetLength(), fixedlang ? fixedlang : formatinfo.language)) { + if(true) { + Vector h = SpellerFindCloseWords(fixedlang ? fixedlang : formatinfo.language, w.ToString(), 10); + for(String s : h) + menu.Add(s, [=] { + int pos, count; + GetWordAtCursorPos(pos, count); + if(count) { + Remove(pos, count); + WString h = s.ToWString(); + Insert(pos, AsRichText(h, formatinfo)); + Move(pos + h.GetCount()); + Finish(); + } + }); + } menu.Add(t_("Add to user dictionary"), THISBACK(AddUserDict)); menu.Separator(); } diff --git a/uppsrc/RichEdit/RichEdit.h b/uppsrc/RichEdit/RichEdit.h index bdb6a3579..c45a7b4a8 100644 --- a/uppsrc/RichEdit/RichEdit.h +++ b/uppsrc/RichEdit/RichEdit.h @@ -579,6 +579,7 @@ private: void AddUserDict(); WString GetWordAtCursor(); + void GetWordAtCursorPos(int& pos, int& count); Rect GetCaretRect(const RichCaret& pr) const; Rect GetCaretRect() const;