diff --git a/uppsrc/CodeEditor/CHighlight.cpp b/uppsrc/CodeEditor/CHighlight.cpp index f463c70a7..b59044ff3 100644 --- a/uppsrc/CodeEditor/CHighlight.cpp +++ b/uppsrc/CodeEditor/CHighlight.cpp @@ -89,16 +89,23 @@ const wchar *HighlightNumber(HighlightOutput& hls, const wchar *p, bool ts, bool const wchar *CSyntax::DoComment(HighlightOutput& hls, const wchar *p, const wchar *e) { - String w; - for(const wchar *s = p; s < e && IsAlpha(*s) && w.GetCount() < 5; s++) + WString w; + for(const wchar *s = p; s < e && IsLetter(*s); s++) w.Cat(ToUpper(*s)); int n = w.GetCount(); - if(findarg(w, "TODO", "FIXME") >= 0) + if(!n) + for(const wchar *s = p; s < e && !IsLetter(*s); s++) + n++; + word flags = 0; + if(n && comments_lang && !SpellWord(w, comments_lang)) + flags = LineEdit::SPELLERROR; + hls.SetFlags(n, flags); + static WString todo = "TODO"; + static WString fixme = "FIXME"; + if(w.GetCount() >= 4 && w.GetCount() <= 5 && findarg(w, todo, fixme) >= 0) hls.Put(n, hl_style[INK_COMMENT_WORD], hl_style[PAPER_COMMENT_WORD]); - else { - n = max(n, 1); + else hls.Put(n, hl_style[INK_COMMENT]); - } return p + n; } diff --git a/uppsrc/CodeEditor/CSyntax.h b/uppsrc/CodeEditor/CSyntax.h index 258fb9fde..c694a5e08 100644 --- a/uppsrc/CodeEditor/CSyntax.h +++ b/uppsrc/CodeEditor/CSyntax.h @@ -41,7 +41,7 @@ protected: static int InitUpp(const char **q); static void InitKeywords(); - static const wchar *DoComment(HighlightOutput& hls, const wchar *p, const wchar *e); + const wchar *DoComment(HighlightOutput& hls, const wchar *p, const wchar *e); static Vector< Index > keyword; static Vector< Index > name; diff --git a/uppsrc/CodeEditor/CodeEditor.cpp b/uppsrc/CodeEditor/CodeEditor.cpp index 7dbc2a6e4..31d229c43 100644 --- a/uppsrc/CodeEditor/CodeEditor.cpp +++ b/uppsrc/CodeEditor/CodeEditor.cpp @@ -17,6 +17,7 @@ One CodeEditor::GetSyntax(int line) { CTIMING("GetSyntax"); One syntax = EditorSyntax::Create(highlight); + syntax->SpellCheckComments(spellcheck_comments); int ln = 0; for(int i = 0; i < __countof(syntax_cache); i++) if(line >= syntax_cache[i].line && syntax_cache[i].line > 0) { diff --git a/uppsrc/CodeEditor/CodeEditor.h b/uppsrc/CodeEditor/CodeEditor.h index 98ec655ab..6724f5682 100644 --- a/uppsrc/CodeEditor/CodeEditor.h +++ b/uppsrc/CodeEditor/CodeEditor.h @@ -274,6 +274,8 @@ protected: String highlight; + int spellcheck_comments = 0; + struct Tip : Ctrl { Value v; const Display *d; @@ -462,6 +464,9 @@ public: void IndentAmount(int ia) { indent_amount = ia; } void NoParenthesisIndent(bool b) { no_parenthesis_indent = b; } + void SpellcheckComments(int lang) { spellcheck_comments = lang; Refresh(); } + int GetSpellcheckComments() const { return spellcheck_comments; } + void NoFindReplace() { withfindreplace = false; } void LineNumbers(bool b) { bar.LineNumbers(b); } diff --git a/uppsrc/CodeEditor/HighlightOut.cpp b/uppsrc/CodeEditor/HighlightOut.cpp index 821d5e0e9..adc93f93d 100644 --- a/uppsrc/CodeEditor/HighlightOut.cpp +++ b/uppsrc/CodeEditor/HighlightOut.cpp @@ -59,6 +59,14 @@ void HighlightOutput::SetPaper(int pos, int count, Color paper) v[pos++].paper = paper; } +void HighlightOutput::SetFlags(int pos, int count, word flags) +{ + if(pos + count > v.GetCount()) + v.At(pos + count - 1, def); + while(count-- > 0) + v[pos++].flags = flags; +} + void HighlightOutput::SetInk(int pos, int count, Color ink) { if(pos + count > v.GetCount()) diff --git a/uppsrc/CodeEditor/Syntax.h b/uppsrc/CodeEditor/Syntax.h index 85eff5815..4b0beb91b 100644 --- a/uppsrc/CodeEditor/Syntax.h +++ b/uppsrc/CodeEditor/Syntax.h @@ -72,13 +72,16 @@ struct HighlightOutput : HighlightSetup { public: void SetChar(int pos, int chr) { v[pos].chr = chr; } void Set(int pos, int count, const HlStyle& ink); + void SetFlags(int pos, int count, word flags); void SetFont(int pos, int count, const HlStyle& f); void SetPaper(int pos, int count, Color paper); void SetInk(int pos, int count, Color ink); + void SetFlags(int count, word flags) { SetFlags(pos, count, flags); } void Put(int count, const HlStyle& ink) { Set(pos, count, ink); pos += count; } void Put(int count, const HlStyle& ink, const HlStyle& paper); void Put(const HlStyle& ink) { Put(1, ink); } void Put(const HlStyle& ink, word flags) { Put(1, ink); v[pos - 1].flags = flags; } + void Flags(word flags) { v[pos - 1].flags = flags; } int GetCount() const { return v.GetCount(); } const wchar *CString(const wchar *p); @@ -98,6 +101,7 @@ class EditorSyntax : public HighlightSetup { // Inheriting to make static member protected: bool ignore_errors; + int comments_lang; public: virtual void Clear(); @@ -118,6 +122,7 @@ public: String Get() { CTIMING("Get"); return StoreAsString(*this); } void IgnoreErrors() { ignore_errors = true; } + void SpellCheckComments(int lang) { comments_lang = lang; } EditorSyntax() { Clear(); ignore_errors = false; } diff --git a/uppsrc/Core/App.cpp b/uppsrc/Core/App.cpp index aac3408a8..21d49cc55 100644 --- a/uppsrc/Core/App.cpp +++ b/uppsrc/Core/App.cpp @@ -179,6 +179,11 @@ String ConfigFile(const char *file) { #endif//PLATFORM } +String GetConfigFolder() +{ + return GetFileFolder(ConfigFile("x")); +} + String ConfigFile() { return ConfigFile(GetExeTitle() + ".cfg"); } diff --git a/uppsrc/Core/App.h b/uppsrc/Core/App.h index 38f0f7f4a..8b60c98cb 100644 --- a/uppsrc/Core/App.h +++ b/uppsrc/Core/App.h @@ -16,6 +16,7 @@ void SetHomeDirectory(const char *dir); void UseHomeDirectoryConfig(bool b = true); +String GetConfigFolder(); String ConfigFile(const char *file); String ConfigFile(); diff --git a/uppsrc/Core/Core.upp b/uppsrc/Core/Core.upp index 380f9e8a1..61e09e355 100644 --- a/uppsrc/Core/Core.upp +++ b/uppsrc/Core/Core.upp @@ -83,6 +83,7 @@ file Util.cpp, mathutil.cpp, Random.cpp, + Speller.cpp, LocalProcess.h, LocalProcess.cpp, Containers readonly separator, diff --git a/uppsrc/Core/Speller.cpp b/uppsrc/Core/Speller.cpp new file mode 100644 index 000000000..904aefb9f --- /dev/null +++ b/uppsrc/Core/Speller.cpp @@ -0,0 +1,288 @@ +#include "Core.h" + +namespace Upp { + +#define LLOG(x) // LOG(x) + +struct SpellBlock : Moveable { + String first; + int offset; + int ctrl_len; + int text_len; +}; + +struct Speller { + String data; + byte charset; + const char *voc[256]; + int dict; + struct Line : Moveable { + const byte *begin; + const byte *end; + }; + VectorMap line; + Index user; + + String path; + Array block; + + bool SetOld(const String& data); + void Clear() { data.Clear(); path.Clear(); } + operator bool() const { return !data.IsEmpty() || path.GetCount(); } + bool CheckOld(const WString& wstr) const; + + String Get(int offset, int len); +}; + +bool Speller::SetOld(const String& _data) +{ + data = _data; + const char *s = data; + if(s >= data.End()) { + data.Clear(); + return false; + } + charset = *s++; + s++;// reserved for prefixes + dict = *s++; + for(int i = 0; i < 256 - dict; i++) { + if(s >= data.End()) { + data.Clear(); + return false; + } + voc[i] = s; + while(*s) s++; + s++; + } + line.Clear(); + while(s < data.End()) { + if(s + 8 >= data.End()) { + data.Clear(); + return false; + } + int code = Peek32le(s); + s += 4; + int len = Peek32le(s); + s += 4; + Line& l = line.Add(code); + l.begin = (const byte *)s; + s += len; + l.end = (const byte *)s; + }; + return true; +} + +bool Speller::CheckOld(const WString& wstr) const +{ + int len = wstr.GetLength(); + if(len < 2) + return true; + if(len < 64) { + String w = FromUnicode(wstr, charset); + String wl = FromUnicode(ToLower(wstr), charset); + int i; + if(len == 2) { + w.Cat(127); + wl.Cat(127); + } + i = line.Find(ToLower(wl[0], charset) + + (ToLower(wl[1], charset) << 8) + + (ToLower(wl[2], charset) << 16)); + if(i >= 0) { + const byte *s = line[i].begin; + const byte *e = line[i].end; + String q; + while(s < e) + if(*s < dict) { + if(q == w || q == wl) + return true; + q.Trim(*s++); + } + else { + ASSERT(*s >= dict); + const char *x = voc[(int)*s++ - dict]; + q.Cat(x); + } + if(q == w || q == wl) + return true; + } + } + return user.Find(wstr) >= 0;; +} + +static String sUserFile(int lang) +{ + return ConfigFile(LNGAsText(lang) + ".usp"); +} + +String spell_path; + +void SetSpellPath(const String& p) +{ + spell_path = p; +} + +static String sZet(FileIn& in, int offset, int len) +{ + in.Seek(offset); + return ZDecompress(in.Get(len)); +} + +void DoSpellerPath(String& pp, String dir) +{ + for(;;) { + pp << dir << ';'; + String d = GetFileFolder(dir); + if(d == dir) break; + dir = d; + } +} + +Speller *sGetSpeller(int lang) +{ + static ArrayMap speller; + int q = speller.Find(lang); + if(q < 0) { + String pp = spell_path; + DoSpellerPath(pp, GetExeDirFile("scd")); + DoSpellerPath(pp, ConfigFile("scd")); + pp << spell_path << ';' << getenv("LIB") << ';' << getenv("PATH") << ';'; + String path = GetFileOnPath(ToLower(LNGAsText(lang)) + ".udc", pp); + if(IsNull(path)) + path = GetFileOnPath(ToLower(LNGAsText(lang)) + ".scd", pp); + if(IsNull(path)) + return NULL; + FileIn in(path); + if(!in) + return NULL; + q = speller.GetCount(); + Speller& f = speller.Add(lang); + FileIn user(sUserFile(lang)); + while(!user.IsEof()) { + String s = user.GetLine(); + if(!s.IsEmpty()) + f.user.Add(FromUtf8(s)); + } + if(in.Get() != 255) + f.SetOld(LoadFile(path)); + else { + f.path = path; + int n = in.GetL(); + LLOG("Found scd file " << path << " blocks " << n); + if(n > 0 && n < 100000) { + for(int i = 0; i < n; i++) { + SpellBlock& b = f.block.Add(); + b.first = in.Get(in.Get()); + b.ctrl_len = in.GetL(); + b.text_len = in.GetL(); + } + if(in.IsEof()) + f.block.Clear(); + else { + int off = (int)in.GetPos(); + for(int i = 0; i < n; i++) { + SpellBlock& b = f.block[i]; + b.offset = off; + off += b.ctrl_len + b.text_len; + } + } + } + } + } + return &speller[q]; +} + +bool SpellWordRaw(const WString& wrd, int lang, Vector *withdia) +{ + Speller *f = sGetSpeller(lang); + if(!f) + return true; + if(f->data.GetCount()) + return f->CheckOld(wrd); + String awrd = ToUpper(ToAscii(wrd).ToString()); + String t1 = ToUtf8(wrd); + String t2 = ToUtf8(ToLower(wrd)); + for(int i = 0;; i++) { + if(i + 1 >= f->block.GetCount() || awrd <= f->block[i + 1].first) { + for(;;) { + if(i >= f->block.GetCount()) + return f->user.Find(wrd) >= 0;; + LLOG("Spell block " << i << ": " << f->block[i].first); + const SpellBlock& b = f->block[i++]; + if(b.first > awrd) { + LLOG(" --- end"); + return f->user.Find(wrd) >= 0;; + } + FileIn in(f->path); + String ctrl = sZet(in, b.offset, b.ctrl_len); + String text = sZet(in, b.offset + b.ctrl_len, b.text_len); + in.Close(); + String w; + const char *s = ctrl; + const char *e = ctrl.End(); + const char *t = text; + const char *te = text.End(); + while(s < e && t < te) { + w.Trim(*s++); + while(*t) + w.Cat(*t++); + if(w == t1 || w == t2) + return true; + if(withdia && t2 == ToLower(ToAscii(w.ToWString()).ToString())) + withdia->Add(w); + t++; + } + } + } + } + return f->user.Find(wrd) >= 0;; +} + +struct SpellKey : Moveable { + int lang; + WString wrd; + + unsigned GetHashValue() const { return CombineHash(lang, wrd); } + bool operator==(const SpellKey& b) const { return lang == b.lang && wrd == b.wrd; } +}; + +struct SpellMaker : LRUCache::Maker { + SpellKey k; + + SpellKey Key() const { return k; } + int Make(bool& r) const { + r = SpellWordRaw(k.wrd, k.lang); + return 1; + } +}; + +static LRUCache speller_cache; + +bool SpellWord(const WString& ws, int lang) +{ + speller_cache.Shrink(2000); + SpellMaker m; + m.k.lang = lang; + m.k.wrd = ws; + return speller_cache.Get(m); +} + +bool SpellWord(const wchar *ws, int len, int lang) +{ + return SpellWord(WString(ws, len), lang); +} + +void SpellerAdd(const WString& w, int lang) +{ + if(!SpellWord(w, w.GetCount(), lang)) { + Speller *f = sGetSpeller(lang); + if(f) { + FileAppend fa(sUserFile(lang)); + fa.PutLine(ToUtf8(w)); + f->user.Add(w); + speller_cache.Clear(); + } + } +} + +}; \ No newline at end of file diff --git a/uppsrc/Core/Util.h b/uppsrc/Core/Util.h index 1bd09c3e6..61f23ce16 100644 --- a/uppsrc/Core/Util.h +++ b/uppsrc/Core/Util.h @@ -482,6 +482,11 @@ String Replace(const String& s, const VectorMap& fr); WString Replace(const WString& s, const Vector& find, const Vector& replace); WString Replace(const WString& s, const VectorMap& fr); +bool SpellWordRaw(const WString& wrd, int lang, Vector *withdia = NULL); +bool SpellWord(const WString& ws, int lang); +bool SpellWord(const wchar *ws, int len, int lang); +void SpellerAdd(const WString& w, int lang); + 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); @@ -500,4 +505,3 @@ String Garble(const String& s); String Encode64(const String& s); String Decode64(const String& s); - diff --git a/uppsrc/Core/srcdoc.tpp/Core2016$en-us.tpp b/uppsrc/Core/srcdoc.tpp/Core2016$en-us.tpp index ce4b3026e..42f49ac7c 100644 --- a/uppsrc/Core/srcdoc.tpp/Core2016$en-us.tpp +++ b/uppsrc/Core/srcdoc.tpp/Core2016$en-us.tpp @@ -66,9 +66,8 @@ has ability to `'add`' functions using operator<<. Also, unlike exception.&] [s5; [* Event ]is equivalent of Callback `- unlike Callback, it is not necessary to have Callback, Callback1, Callback2, number -of parameters is resolved by C`+`+11 template varargs. For backward -compatibility Callback`[N`] are aliased as Event variants.&] -[s5; Similarly, [* EventGate] is a new equivalent of [* Gate].&] +of parameters is resolved by C`+`+11 template varargs.&] +[s5; Callback is deprecated but supported for backward compatibility.&] [s3; Algorithms and Containers&] [s5; The set of algorithms provided by U`+`+ is now streamlined by introduction of [/ Range] concept. Range is entity that provides diff --git a/uppsrc/CtrlLib/LineEdit.cpp b/uppsrc/CtrlLib/LineEdit.cpp index 0fcab78fe..5c3b2216a 100644 --- a/uppsrc/CtrlLib/LineEdit.cpp +++ b/uppsrc/CtrlLib/LineEdit.cpp @@ -461,7 +461,7 @@ void LineEdit::Paint0(Draw& w) { else ln = tx.GetCount(); int lgp = -1; - for(int pass = 0; pass < 2; pass++) { + for(int pass = 0; pass < 3; pass++) { int gp = 0; int scx = fsz.cx * sc.x; sOptimizedRectRenderer rw(w); @@ -546,7 +546,9 @@ void LineEdit::Paint0(Draw& w) { if(bordercolumn > 0 && bordercolumn >= gp && bordercolumn < gp + 1 + cjk) rw.DrawRect((bordercolumn - sc.x) * fsz.cx, y, 1, fsz.cy, bordercolor); } - else + if(pass == 1 && (h.flags & SPELLERROR)) + rw.DrawRect(x, max(y, y + fsz.cy - Zy(1)), (cjk + 1) * fsz.cx, Zy(1), LtRed()); + if(pass == 2) tw.DrawChar(x + (h.flags & SHIFT_L ? -fsz.cx / 6 : h.flags & SHIFT_R ? fsz.cx / 6 : 0), y + fascent - h.font.GetAscent(), h.chr, (cjk + 1) * fsz.cx, h.font, h.ink); diff --git a/uppsrc/CtrlLib/TextEdit.h b/uppsrc/CtrlLib/TextEdit.h index caeeccd9b..93a9bf6b7 100644 --- a/uppsrc/CtrlLib/TextEdit.h +++ b/uppsrc/CtrlLib/TextEdit.h @@ -243,6 +243,7 @@ public: enum Flags { SHIFT_L = 1, SHIFT_R = 2, + SPELLERROR = 4, }; struct Highlight : Moveable { diff --git a/uppsrc/RichEdit/RichEdit.h b/uppsrc/RichEdit/RichEdit.h index 35cb9581e..be9b29a78 100644 --- a/uppsrc/RichEdit/RichEdit.h +++ b/uppsrc/RichEdit/RichEdit.h @@ -197,10 +197,6 @@ public: StyleManager(); }; -bool SpellWordRaw(const WString& wrd, int lang, Vector *withdia = NULL); -bool SpellWord(const wchar *ws, int len, int lang); -bool SpellWord(const WString& ws, int lang); - void SetupFaceList(DropList& face); class RichEdit : public Ctrl, private TextArrayOps { diff --git a/uppsrc/RichEdit/Speller.cpp b/uppsrc/RichEdit/Speller.cpp index 31f6e9262..b6a8769b0 100644 --- a/uppsrc/RichEdit/Speller.cpp +++ b/uppsrc/RichEdit/Speller.cpp @@ -2,287 +2,9 @@ namespace Upp { -#define LLOG(x) // LOG(x) - -struct SpellBlock : Moveable { - String first; - int offset; - int ctrl_len; - int text_len; -}; - -struct Speller { - String data; - byte charset; - const char *voc[256]; - int dict; - struct Line : Moveable { - const byte *begin; - const byte *end; - }; - VectorMap line; - Index user; - - String path; - Array block; - - bool SetOld(const String& data); - void Clear() { data.Clear(); path.Clear(); } - operator bool() const { return !data.IsEmpty() || path.GetCount(); } - bool CheckOld(const WString& wstr) const; - - String Get(int offset, int len); -}; - -bool Speller::SetOld(const String& _data) -{ - data = _data; - const char *s = data; - if(s >= data.End()) { - data.Clear(); - return false; - } - charset = *s++; - s++;// reserved for prefixes - dict = *s++; - for(int i = 0; i < 256 - dict; i++) { - if(s >= data.End()) { - data.Clear(); - return false; - } - voc[i] = s; - while(*s) s++; - s++; - } - line.Clear(); - while(s < data.End()) { - if(s + 8 >= data.End()) { - data.Clear(); - return false; - } - int code = Peek32le(s); - s += 4; - int len = Peek32le(s); - s += 4; - Line& l = line.Add(code); - l.begin = (const byte *)s; - s += len; - l.end = (const byte *)s; - }; - return true; -} - -bool Speller::CheckOld(const WString& wstr) const -{ - int len = wstr.GetLength(); - if(len == 1) - return true; - if(len < 64) { - String w = FromUnicode(wstr, charset); - String wl = FromUnicode(ToLower(wstr), charset); - int i; - if(len == 2) { - w.Cat(127); - wl.Cat(127); - } - i = line.Find(ToLower(wl[0], charset) + - (ToLower(wl[1], charset) << 8) + - (ToLower(wl[2], charset) << 16)); - if(i >= 0) { - const byte *s = line[i].begin; - const byte *e = line[i].end; - String q; - while(s < e) - if(*s < dict) { - if(q == w || q == wl) - return true; - q.Trim(*s++); - } - else { - ASSERT(*s >= dict); - const char *x = voc[(int)*s++ - dict]; - q.Cat(x); - } - if(q == w || q == wl) - return true; - } - } - return user.Find(wstr) >= 0;; -} - -static String sUserFile(int lang) -{ - return ConfigFile(LNGAsText(lang) + ".usp"); -} - -String spell_path; - -void SetSpellPath(const String& p) -{ - spell_path = p; -} - -static String sZet(FileIn& in, int offset, int len) -{ - in.Seek(offset); - return ZDecompress(in.Get(len)); -} - -void DoSpellerPath(String& pp, String dir) -{ - for(;;) { - pp << dir << ';'; - String d = GetFileFolder(dir); - if(d == dir) break; - dir = d; - } -} - -Speller *sGetSpeller(int lang) -{ - static ArrayMap speller; - int q = speller.Find(lang); - if(q < 0) { - String pp = spell_path; - DoSpellerPath(pp, GetExeDirFile("scd")); - DoSpellerPath(pp, ConfigFile("scd")); - pp << spell_path << ';' << getenv("LIB") << ';' << getenv("PATH") << ';'; - String path = GetFileOnPath(ToLower(LNGAsText(lang)) + ".udc", pp); - if(IsNull(path)) - path = GetFileOnPath(ToLower(LNGAsText(lang)) + ".scd", pp); - if(IsNull(path)) - return NULL; - FileIn in(path); - if(!in) - return NULL; - q = speller.GetCount(); - Speller& f = speller.Add(lang); - FileIn user(sUserFile(lang)); - while(!user.IsEof()) { - String s = user.GetLine(); - if(!s.IsEmpty()) - f.user.Add(FromUtf8(s)); - } - if(in.Get() != 255) - f.SetOld(LoadFile(path)); - else { - f.path = path; - int n = in.GetL(); - LLOG("Found scd file " << path << " blocks " << n); - if(n > 0 && n < 100000) { - for(int i = 0; i < n; i++) { - SpellBlock& b = f.block.Add(); - b.first = in.Get(in.Get()); - b.ctrl_len = in.GetL(); - b.text_len = in.GetL(); - } - if(in.IsEof()) - f.block.Clear(); - else { - int off = (int)in.GetPos(); - for(int i = 0; i < n; i++) { - SpellBlock& b = f.block[i]; - b.offset = off; - off += b.ctrl_len + b.text_len; - } - } - } - } - } - return &speller[q]; -} - -bool SpellWordRaw(const WString& wrd, int lang, Vector *withdia) -{ - Speller *f = sGetSpeller(lang); - if(!f) - return true; - if(f->data.GetCount()) - return f->CheckOld(wrd); - String awrd = ToUpper(ToAscii(wrd).ToString()); - String t1 = ToUtf8(wrd); - String t2 = ToUtf8(ToLower(wrd)); - for(int i = 0;; i++) { - if(i + 1 >= f->block.GetCount() || awrd <= f->block[i + 1].first) { - for(;;) { - if(i >= f->block.GetCount()) - return f->user.Find(wrd) >= 0;; - LLOG("Spell block " << i << ": " << f->block[i].first); - const SpellBlock& b = f->block[i++]; - if(b.first > awrd) { - LLOG(" --- end"); - return f->user.Find(wrd) >= 0;; - } - FileIn in(f->path); - String ctrl = sZet(in, b.offset, b.ctrl_len); - String text = sZet(in, b.offset + b.ctrl_len, b.text_len); - in.Close(); - String w; - const char *s = ctrl; - const char *e = ctrl.End(); - const char *t = text; - const char *te = text.End(); - while(s < e && t < te) { - w.Trim(*s++); - while(*t) - w.Cat(*t++); - if(w == t1 || w == t2) - return true; - if(withdia && t2 == ToLower(ToAscii(w.ToWString()).ToString())) - withdia->Add(w); - t++; - } - } - } - } - return f->user.Find(wrd) >= 0;; -} - -struct SpellKey : Moveable { - int lang; - WString wrd; - - unsigned GetHashValue() const { return CombineHash(lang, wrd); } - bool operator==(const SpellKey& b) const { return lang == b.lang && wrd == b.wrd; } -}; - -struct SpellMaker : LRUCache::Maker { - SpellKey k; - - SpellKey Key() const { return k; } - int Make(bool& r) const { - r = SpellWordRaw(k.wrd, k.lang); - return 1; - } -}; - -static LRUCache speller_cache; - -bool SpellWord(const WString& ws, int lang) -{ - speller_cache.Shrink(2000); - SpellMaker m; - m.k.lang = lang; - m.k.wrd = ws; - return speller_cache.Get(m); -} - -bool SpellWord(const wchar *ws, int len, int lang) -{ - return SpellWord(WString(ws, len), lang); -} - void RichEdit::SpellerAdd(const WString& w, int lang) { - if(!SpellWord(w, w.GetCount(), lang)) { - Speller *f = sGetSpeller(lang); - if(f) { - FileAppend fa(sUserFile(lang)); - fa.PutLine(ToUtf8(w)); - f->user.Add(w); - speller_cache.Clear(); - } - } + Upp::SpellerAdd(w, lang); } int RichEdit::fixedlang; diff --git a/uppsrc/ide/Config.cpp b/uppsrc/ide/Config.cpp index ad7befcf0..a21ab2d1c 100644 --- a/uppsrc/ide/Config.cpp +++ b/uppsrc/ide/Config.cpp @@ -162,7 +162,7 @@ void Sentinel(Stream& s, const char *txt) void Ide::Serialize(Stream& s) { - int version = 10; + int version = 11; Sentinel(s, "before 12341234"); s.Magic(0x12341234); Sentinel(s, "after magic"); @@ -200,6 +200,8 @@ void Ide::Serialize(Stream& s) s % tabs_grouping; s % tabs_serialize; s % tabs_stacking; + if(version >= 11) + s % spellcheck_comments; bool dummy_force_crlf = false; s % dummy_force_crlf; if(version >= 1) diff --git a/uppsrc/ide/Core/Core.h b/uppsrc/ide/Core/Core.h index 40177d840..7cfda059d 100644 --- a/uppsrc/ide/Core/Core.h +++ b/uppsrc/ide/Core/Core.h @@ -302,10 +302,11 @@ public: int font; String highlight; bool pch, nopch, noblitz; + int spellcheck_comments; void operator=(const String& s) { String::operator=(s); readonly = separator = false; } void Init() { readonly = separator = false; tabsize = Null; charset = 0; font = 0; - pch = nopch = noblitz = false; } + pch = nopch = noblitz = false; spellcheck_comments = Null; } File() { Init(); } File(const String& s) : String(s) { Init(); } @@ -334,6 +335,7 @@ public: Time time; bool bold, italic; Color ink; + int spellcheck_comments; int GetCount() const { return file.GetCount(); } File& operator[](int i) { return file[i]; } diff --git a/uppsrc/ide/Core/Package.cpp b/uppsrc/ide/Core/Package.cpp index bc5477a76..37529a6a7 100644 --- a/uppsrc/ide/Core/Package.cpp +++ b/uppsrc/ide/Core/Package.cpp @@ -163,6 +163,7 @@ void Package::Reset() noblitz = nowarnings = false; bold = italic = false; ink = Null; + spellcheck_comments = Null; } Package::Package() @@ -319,6 +320,9 @@ bool Package::Load(const char *path) else if(p.Id("highlight")) f.highlight = p.ReadId(); + else + if(p.Id("spellcheck_comments")) + f.spellcheck_comments = LNGFromText(p.ReadString()); else p.SkipTerm(); } @@ -348,6 +352,9 @@ bool Package::Load(const char *path) else if(p.Id("custom")) custom.Add().Load(p); + else + if(p.Id("spellcheck_comments")) + spellcheck_comments = LNGFromText(p.ReadString()); else p.SkipTerm(); } @@ -422,6 +429,12 @@ String IdeCharsetName(byte charset) { CharsetName(charset)); } +void PutSpellCheckComments(StringStream& out, int sc) +{ + if(!IsNull(sc)) + out << " spellcheck_comments " << AsCString(sc ? LNGAsText(sc) : ""); +} + bool Package::Save(const char *path) const { StringStream out; if(description.GetCount() || italic || bold || !IsNull(ink)) { @@ -475,6 +488,7 @@ bool Package::Save(const char *path) const { out << " charset " << AsCString(IdeCharsetName(f.charset)); if(!IsNull(f.highlight)) out << " highlight " << f.highlight; + PutSpellCheckComments(out, f.spellcheck_comments); putfopt(out, "options", f.option); putfopt(out, "depends", f.depends); } @@ -489,6 +503,7 @@ bool Package::Save(const char *path) const { } out << ";\n\n"; } + PutSpellCheckComments(out, spellcheck_comments); for(int i = 0; i < custom.GetCount(); i++) out << custom[i].AsString(); return SaveChangedFile(path, out.GetResult()); diff --git a/uppsrc/ide/Setup.cpp b/uppsrc/ide/Setup.cpp index e31c9662c..1570948e3 100644 --- a/uppsrc/ide/Setup.cpp +++ b/uppsrc/ide/Setup.cpp @@ -332,6 +332,32 @@ void InsertPath(EditString *es) es->SetWantFocus(); } +void DlSpellerLangs(DropList& dl) +{ + static Vector lng; + ONCELOCK { + VectorMap lngs; + String path = GetExeDirFile("scd") + ';' + ConfigFile("scd") + ';' + + GetExeFolder() + ';' + GetConfigFolder() + ';' + + getenv("LIB") + ';' + getenv("PATH"); + Vector p = Split(path, ';'); + for(auto dir : p) { + FindFile ff(AppendFileName(dir, "*.scd")); + while(ff) { + int lang = LNGFromText(ff.GetName()); + if(lang) + lngs.Add(lang, LNGAsText(lang)); + ff.Next(); + } + } + SortByValue(lngs); + lng = lngs.PickKeys(); + } + dl.Add(0, "Off"); + for(auto l : lng) + dl.Add(l, LNGAsText(l)); +} + void Ide::SetupFormat() { FormatDlg dlg; dlg.Title("Settings"); @@ -401,6 +427,9 @@ void Ide::SetupFormat() { edt.indent_amount.Enable(indent_spaces); CtrlRetriever rtvr; int hs = hilite_scope; + + DlSpellerLangs(edt.spellcheck_comments); + rtvr (hlt.hilite_scope, hs) (hlt.hilite_bracket, hilite_bracket) @@ -427,6 +456,7 @@ void Ide::SetupFormat() { (edt.tabs_grouping, tabs_grouping) (edt.tabs_stacking, tabs_stacking) (edt.tabs_serialize, tabs_serialize) + (edt.spellcheck_comments, spellcheck_comments) (edt.persistent_find_replace, persistent_find_replace) (edt.find_replace_restore_pos, find_replace_restore_pos) diff --git a/uppsrc/ide/UppDlg.cpp b/uppsrc/ide/UppDlg.cpp index c4c6ac952..28e6933c8 100644 --- a/uppsrc/ide/UppDlg.cpp +++ b/uppsrc/ide/UppDlg.cpp @@ -90,6 +90,7 @@ void PackageEditor::SaveOptions() { actual.bold = ~bold; actual.italic = ~italic; actual.charset = (byte)(int)~charset; + actual.spellcheck_comments = ~spellcheck_comments; actual.accepts = Split(accepts.GetText().ToString(), ' '); actual.noblitz = noblitz; actual.nowarnings = nowarnings; @@ -116,6 +117,7 @@ void PackageEditor::Empty() { FileEmpty(); charset.Disable(); + spellcheck_comments.Disable(); noblitz.Disable(); nowarnings.Disable(); description.Disable(); @@ -174,6 +176,7 @@ void PackageEditor::PackageCursor() bold <<= actual.bold; italic <<= actual.italic; charset <<= (int)actual.charset; + spellcheck_comments <<= actual.spellcheck_comments; noblitz = actual.noblitz; nowarnings = actual.nowarnings; String s; @@ -187,6 +190,7 @@ void PackageEditor::PackageCursor() bold.Enable(); italic.Enable(); charset.Enable(); + spellcheck_comments.Enable(); noblitz.Enable(); nowarnings.Enable(); accepts.Enable(); @@ -579,12 +583,15 @@ PackageEditor::PackageEditor() CtrlLayoutOKCancel(*this, "Package organizer"); description.Disable(); description <<= THISBACK(Description); + + spellcheck_comments.Add(Null, "Default"); + DlSpellerLangs(spellcheck_comments); DlCharsetD(charset); charset.Disable(); filelist.Disable(); + spellcheck_comments.Disable(); accepts.SetFilter(FlagFilter); - accepts <<= - charset <<= THISBACK(SaveOptions); + accepts ^= spellcheck_comments ^= charset ^= THISFN(SaveOptions); noblitz <<= nowarnings <<= pch_file <<= diff --git a/uppsrc/ide/UppWspc.cpp b/uppsrc/ide/UppWspc.cpp index 6824c3ece..192473577 100644 --- a/uppsrc/ide/UppWspc.cpp +++ b/uppsrc/ide/UppWspc.cpp @@ -161,6 +161,7 @@ void WorkspaceWork::SavePackage() b.data = String::GetVoid(); } actual.Save(pp); +#if 0 // nobody is using 'init'. to be replaced by "add by include" method String init; String mnm = Filter('_' + actualpackage + "_icpp_init_stub", CharFilterMacro); init << "#ifndef " << mnm << "\r\n"; @@ -182,6 +183,7 @@ void WorkspaceWork::SavePackage() } init << "#endif\r\n"; SaveChangedFile(SourcePath(actualpackage, "init"), init); +#endif } void WorkspaceWork::RestoreBackup() diff --git a/uppsrc/ide/ide.h b/uppsrc/ide/ide.h index 93a6b0a9d..dc0041ac4 100644 --- a/uppsrc/ide/ide.h +++ b/uppsrc/ide/ide.h @@ -303,6 +303,8 @@ void DrawFileName(Draw& w, const Rect& r, const String& h, Color ink); void AddPath(EditString *es); void InsertPath(EditString *es); +void DlSpellerLangs(DropList& dl); + #include "Assist.h" void DirSel(EditField& f, FrameRight