From 4be0e1a493b1512d7dbd6e6bbbfe239e2df0913c Mon Sep 17 00:00:00 2001 From: Svyatoslav Gryaznov Date: Sat, 21 May 2016 06:22:27 +0300 Subject: [PATCH 1/8] Add initial support for multilingual spellcheck --- lib/spellchecker.js | 6 +++ src/main.cc | 22 +++++++++- src/spellchecker.h | 4 +- src/spellchecker_hunspell.cc | 80 ++++++++++++++++++++++++++---------- src/spellchecker_hunspell.h | 5 ++- src/spellchecker_mac.h | 3 +- src/spellchecker_mac.mm | 5 +++ src/spellchecker_win.cc | 5 +++ src/spellchecker_win.h | 1 + 9 files changed, 106 insertions(+), 25 deletions(-) diff --git a/lib/spellchecker.js b/lib/spellchecker.js index 90c72e0..7790976 100644 --- a/lib/spellchecker.js +++ b/lib/spellchecker.js @@ -17,6 +17,11 @@ var ensureDefaultSpellCheck = function() { setDictionary(lang, getDictionaryPath()); }; +var addDictionary = function(lang, dictPath) { + ensureDefaultSpellCheck(); + return defaultSpellcheck.addDictionary(lang, dictPath); +}; + var setDictionary = function(lang, dictPath) { ensureDefaultSpellCheck(); return defaultSpellcheck.setDictionary(lang, dictPath); @@ -72,6 +77,7 @@ var getDictionaryPath = function() { } module.exports = { + addDictionary: addDictionary, setDictionary: setDictionary, add: add, remove: remove, diff --git a/src/main.cc b/src/main.cc index 673837e..7c9e069 100644 --- a/src/main.cc +++ b/src/main.cc @@ -19,6 +19,25 @@ class Spellchecker : public Nan::ObjectWrap { info.GetReturnValue().Set(info.This()); } + static NAN_METHOD(AddDictionary) { + Nan::HandleScope scope; + + if (info.Length() < 1) { + return Nan::ThrowError("Bad argument"); + } + + Spellchecker* that = Nan::ObjectWrap::Unwrap(info.Holder()); + + std::string language = *String::Utf8Value(info[0]); + std::string directory = "."; + if (info.Length() > 1) { + directory = *String::Utf8Value(info[1]); + } + + bool result = that->impl->AddDictionary(language, directory); + info.GetReturnValue().Set(Nan::New(result)); + } + static NAN_METHOD(SetDictionary) { Nan::HandleScope scope; @@ -98,7 +117,7 @@ class Spellchecker : public Nan::ObjectWrap { that->impl->Add(word); return; } - + static NAN_METHOD(Remove) { Nan::HandleScope scope; if (info.Length() < 1) { @@ -174,6 +193,7 @@ class Spellchecker : public Nan::ObjectWrap { tpl->SetClassName(Nan::New("Spellchecker").ToLocalChecked()); tpl->InstanceTemplate()->SetInternalFieldCount(1); + Nan::SetMethod(tpl->InstanceTemplate(), "addDictionary", Spellchecker::AddDictionary); Nan::SetMethod(tpl->InstanceTemplate(), "setDictionary", Spellchecker::SetDictionary); Nan::SetMethod(tpl->InstanceTemplate(), "getAvailableDictionaries", Spellchecker::GetAvailableDictionaries); Nan::SetMethod(tpl->InstanceTemplate(), "getCorrectionsForMisspelling", Spellchecker::GetCorrectionsForMisspelling); diff --git a/src/spellchecker.h b/src/spellchecker.h index 80852ff..c77b4e5 100644 --- a/src/spellchecker.h +++ b/src/spellchecker.h @@ -1,6 +1,7 @@ #ifndef SRC_SPELLCHECKER_H_ #define SRC_SPELLCHECKER_H_ +#include #include #include #include @@ -14,6 +15,7 @@ struct MisspelledRange { class SpellcheckerImplementation { public: + virtual bool AddDictionary(const std::string& language, const std::string& path) = 0; virtual bool SetDictionary(const std::string& language, const std::string& path) = 0; virtual std::vector GetAvailableDictionaries(const std::string& path) = 0; @@ -29,7 +31,7 @@ class SpellcheckerImplementation { // NB: When using Hunspell, this will not modify the .dic file; custom words must be added each // time the spellchecker is created. Use a custom dictionary file. virtual void Add(const std::string& word) = 0; - + // Removes a word from the custom dictionary added by Add. // NB: When using Hunspell, this will not modify the .dic file; custom words must be added each // time the spellchecker is created. Use a custom dictionary file. diff --git a/src/spellchecker_hunspell.cc b/src/spellchecker_hunspell.cc index 0ca629b..0eccf8e 100644 --- a/src/spellchecker_hunspell.cc +++ b/src/spellchecker_hunspell.cc @@ -6,11 +6,11 @@ namespace spellchecker { -HunspellSpellchecker::HunspellSpellchecker() : hunspell(NULL), transcoder(NewTranscoder()) { } +HunspellSpellchecker::HunspellSpellchecker() : transcoder(NewTranscoder()) { } HunspellSpellchecker::~HunspellSpellchecker() { - if (hunspell) { - delete hunspell; + for (size_t i = 0; i < hunspells.size(); ++i) { + delete hunspells[i].second; } if (transcoder) { @@ -18,12 +18,7 @@ HunspellSpellchecker::~HunspellSpellchecker() { } } -bool HunspellSpellchecker::SetDictionary(const std::string& language, const std::string& dirname) { - if (hunspell) { - delete hunspell; - hunspell = NULL; - } - +bool HunspellSpellchecker::AddDictionary(const std::string& language, const std::string& dirname) { // NB: Hunspell uses underscore to separate language and locale, and Win8 uses // dash - if they use the wrong one, just silently replace it for them std::string lang = language; @@ -39,25 +34,42 @@ bool HunspellSpellchecker::SetDictionary(const std::string& language, const std: } fclose(handle); - hunspell = new Hunspell(affixpath.c_str(), dpath.c_str()); + // TODO: On windows locale names are different + std::locale loc(lang + ".UTF-8"); + Hunspell* hunspell = new Hunspell(affixpath.c_str(), dpath.c_str()); + hunspells.push_back(std::make_pair(loc, hunspell)); return true; } +bool HunspellSpellchecker::SetDictionary(const std::string& language, const std::string& dirname) { + for (size_t i = 0; i < hunspells.size(); ++i) { + delete hunspells[i].second; + } + hunspells.clear(); + + return AddDictionary(language, dirname); +} + std::vector HunspellSpellchecker::GetAvailableDictionaries(const std::string& path) { return std::vector(); } bool HunspellSpellchecker::IsMisspelled(const std::string& word) { - if (!hunspell) { - return false; + for (size_t i = 0; i < hunspells.size(); ++i) { + Hunspell* hunspell = hunspells[i].second; + bool misspelled = hunspell->spell(word.c_str()) == 0; + if (!misspelled) { + return false; + } } - return hunspell->spell(word.c_str()) == 0; + + return true; } std::vector HunspellSpellchecker::CheckSpelling(const uint16_t *utf16_text, size_t utf16_length) { std::vector result; - if (!hunspell || !transcoder) { + if (hunspells.empty() || !transcoder) { return result; } @@ -80,7 +92,7 @@ std::vector HunspellSpellchecker::CheckSpelling(const uint16_t break; case in_separator: - if (iswalpha(c)) { + if (isAlpha(c)) { word_start = i; state = in_word; } else if (!iswpunct(c) && !iswspace(c)) { @@ -89,20 +101,29 @@ std::vector HunspellSpellchecker::CheckSpelling(const uint16_t break; case in_word: - if (c == '\'' && iswalpha(utf16_text[i + 1])) { + if (c == '\'' && isAlpha(utf16_text[i + 1])) { i++; } else if (c == 0 || iswpunct(c) || iswspace(c)) { state = in_separator; bool converted = TranscodeUTF16ToUTF8(transcoder, (char *)utf8_buffer.data(), utf8_buffer.size(), utf16_text + word_start, i - word_start); if (converted) { - if (hunspell->spell(utf8_buffer.data()) == 0) { + bool all_misspelled = true; + for (size_t i = 0; i < hunspells.size(); ++i) { + Hunspell* hunspell = hunspells[i].second; + bool misspelled = hunspell->spell(utf8_buffer.data()) == 0; + if (!misspelled) { + all_misspelled = false; + break; + } + } + if (all_misspelled) { MisspelledRange range; range.start = word_start; range.end = i; result.push_back(range); } } - } else if (!iswalpha(c)) { + } else if (!isAlpha(c)) { state = unknown; } break; @@ -113,13 +134,15 @@ std::vector HunspellSpellchecker::CheckSpelling(const uint16_t } void HunspellSpellchecker::Add(const std::string& word) { - if (hunspell) { + if (!hunspells.empty()) { + Hunspell* hunspell = hunspells[0].second; hunspell->add(word.c_str()); } } void HunspellSpellchecker::Remove(const std::string& word) { - if (hunspell) { + if (!hunspells.empty()) { + Hunspell* hunspell = hunspells[0].second; hunspell->remove(word.c_str()); } } @@ -127,7 +150,9 @@ void HunspellSpellchecker::Remove(const std::string& word) { std::vector HunspellSpellchecker::GetCorrectionsForMisspelling(const std::string& word) { std::vector corrections; - if (hunspell) { + for (size_t i = 0; i < hunspells.size(); ++i) { + Hunspell* hunspell = hunspells[i].second; + char** slist; int size = hunspell->suggest(&slist, word.c_str()); @@ -141,4 +166,17 @@ std::vector HunspellSpellchecker::GetCorrectionsForMisspelling(cons return corrections; } +bool HunspellSpellchecker::isAlpha(std::wint_t c) { + if (iswalpha(c)) { + return true; + } + for (size_t i = 0; i < hunspells.size(); ++i) { + std::locale loc = hunspells[i].first; + if (std::isalpha((wchar_t)c, loc)) { + return true; + } + } + return false; +} + } // namespace spellchecker diff --git a/src/spellchecker_hunspell.h b/src/spellchecker_hunspell.h index bcc9d65..fadd5e2 100644 --- a/src/spellchecker_hunspell.h +++ b/src/spellchecker_hunspell.h @@ -13,6 +13,7 @@ class HunspellSpellchecker : public SpellcheckerImplementation { HunspellSpellchecker(); ~HunspellSpellchecker(); + bool AddDictionary(const std::string& language, const std::string& path); bool SetDictionary(const std::string& language, const std::string& path); std::vector GetAvailableDictionaries(const std::string& path); std::vector GetCorrectionsForMisspelling(const std::string& word); @@ -22,7 +23,9 @@ class HunspellSpellchecker : public SpellcheckerImplementation { void Remove(const std::string& word); private: - Hunspell* hunspell; + bool isAlpha(std::wint_t c); + + std::vector> hunspells; Transcoder *transcoder; }; diff --git a/src/spellchecker_mac.h b/src/spellchecker_mac.h index 0d692d5..fc71ef4 100644 --- a/src/spellchecker_mac.h +++ b/src/spellchecker_mac.h @@ -13,6 +13,7 @@ class MacSpellchecker : public SpellcheckerImplementation { MacSpellchecker(); ~MacSpellchecker(); + bool AddDictionary(const std::string& language, const std::string& path); bool SetDictionary(const std::string& language, const std::string& path); std::vector GetAvailableDictionaries(const std::string& path); std::vector GetCorrectionsForMisspelling(const std::string& word); @@ -20,7 +21,7 @@ class MacSpellchecker : public SpellcheckerImplementation { std::vector CheckSpelling(const uint16_t *text, size_t length); void Add(const std::string& word); void Remove(const std::string& word); - + private: NSSpellChecker* spellChecker; NSString* spellCheckerLanguage; diff --git a/src/spellchecker_mac.mm b/src/spellchecker_mac.mm index a80e803..0342d9d 100644 --- a/src/spellchecker_mac.mm +++ b/src/spellchecker_mac.mm @@ -18,6 +18,11 @@ this->spellCheckerLanguage = nil; } +bool MacSpellchecker::AddDictionary(const std::string& language, const std::string& path) { + // TODO: write appropriate method + return this->SetDictionary(language, path); +} + bool MacSpellchecker::SetDictionary(const std::string& language, const std::string& path) { @autoreleasepool { [this->spellCheckerLanguage release]; diff --git a/src/spellchecker_win.cc b/src/spellchecker_win.cc index b0d0406..d0010af 100644 --- a/src/spellchecker_win.cc +++ b/src/spellchecker_win.cc @@ -93,6 +93,11 @@ bool WindowsSpellchecker::IsSupported() { return !(g_COMFailed || (this->spellcheckerFactory == NULL)); } +bool WindowsSpellchecker::AddDictionary(const std::string& language, const std::string& path) { + // TODO: write appropriate method + return SetDictionary(language, path); +} + bool WindowsSpellchecker::SetDictionary(const std::string& language, const std::string& path) { if (!this->spellcheckerFactory) { return false; diff --git a/src/spellchecker_win.h b/src/spellchecker_win.h index c5bbefb..ed5d441 100644 --- a/src/spellchecker_win.h +++ b/src/spellchecker_win.h @@ -13,6 +13,7 @@ class WindowsSpellchecker : public SpellcheckerImplementation { WindowsSpellchecker(); ~WindowsSpellchecker(); + bool AddDictionary(const std::string& language, const std::string& path); bool SetDictionary(const std::string& language, const std::string& path); std::vector GetAvailableDictionaries(const std::string& path); From 83b10bef1e5cf43d4cd18b065da64acc0f85654b Mon Sep 17 00:00:00 2001 From: Svyatoslav Gryaznov Date: Sat, 21 May 2016 07:22:05 +0300 Subject: [PATCH 2/8] Make isAlpha constant --- src/spellchecker_hunspell.cc | 2 +- src/spellchecker_hunspell.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/spellchecker_hunspell.cc b/src/spellchecker_hunspell.cc index 0eccf8e..bdcb1ed 100644 --- a/src/spellchecker_hunspell.cc +++ b/src/spellchecker_hunspell.cc @@ -166,7 +166,7 @@ std::vector HunspellSpellchecker::GetCorrectionsForMisspelling(cons return corrections; } -bool HunspellSpellchecker::isAlpha(std::wint_t c) { +bool HunspellSpellchecker::isAlpha(std::wint_t c) const { if (iswalpha(c)) { return true; } diff --git a/src/spellchecker_hunspell.h b/src/spellchecker_hunspell.h index fadd5e2..7ab0d13 100644 --- a/src/spellchecker_hunspell.h +++ b/src/spellchecker_hunspell.h @@ -23,7 +23,7 @@ class HunspellSpellchecker : public SpellcheckerImplementation { void Remove(const std::string& word); private: - bool isAlpha(std::wint_t c); + bool isAlpha(std::wint_t c) const; std::vector> hunspells; Transcoder *transcoder; From efa8892baba876a267c1216811012cbbef1b2241 Mon Sep 17 00:00:00 2001 From: Svyatoslav Gryaznov Date: Sat, 21 May 2016 07:51:09 +0300 Subject: [PATCH 3/8] Implement partial support for Windows --- src/spellchecker_win.cc | 174 +++++++++++++++++++++++----------------- src/spellchecker_win.h | 2 +- 2 files changed, 100 insertions(+), 76 deletions(-) diff --git a/src/spellchecker_win.cc b/src/spellchecker_win.cc index d0010af..35f428a 100644 --- a/src/spellchecker_win.cc +++ b/src/spellchecker_win.cc @@ -56,7 +56,6 @@ std::wstring ToWString(const std::string& string) { WindowsSpellchecker::WindowsSpellchecker() { this->spellcheckerFactory = NULL; - this->currentSpellchecker = NULL; if (InterlockedIncrement(&g_COMRefcount) == 1) { g_COMFailed = FAILED(CoInitialize(NULL)); @@ -74,10 +73,11 @@ WindowsSpellchecker::WindowsSpellchecker() { } WindowsSpellchecker::~WindowsSpellchecker() { - if (this->currentSpellchecker) { - this->currentSpellchecker->Release(); - this->currentSpellchecker = NULL; + for (size_t i = 0; i < this->currentSpellcheckers.size(); ++i) { + ISpellChecker* currentSpellchecker = this->currentSpellcheckers[i]; + currentSpellchecker->Release(); } + this->currentSpellcheckers.clear(); if (this->spellcheckerFactory) { this->spellcheckerFactory->Release(); @@ -94,20 +94,10 @@ bool WindowsSpellchecker::IsSupported() { } bool WindowsSpellchecker::AddDictionary(const std::string& language, const std::string& path) { - // TODO: write appropriate method - return SetDictionary(language, path); -} - -bool WindowsSpellchecker::SetDictionary(const std::string& language, const std::string& path) { if (!this->spellcheckerFactory) { return false; } - if (this->currentSpellchecker != NULL) { - this->currentSpellchecker->Release(); - this->currentSpellchecker = NULL; - } - // Figure out if we have a dictionary installed for the language they want // NB: Hunspell uses underscore to separate language and locale, and Win8 uses // dash - if they use the wrong one, just silently replace it for them @@ -123,13 +113,29 @@ bool WindowsSpellchecker::SetDictionary(const std::string& language, const std:: if (!isSupported) return false; - if (FAILED(this->spellcheckerFactory->CreateSpellChecker(wlanguage.c_str(), &this->currentSpellchecker))) { + ISpellChecker* currentSpellchecker = NULL; + if (FAILED(this->spellcheckerFactory->CreateSpellChecker(wlanguage.c_str(), ¤tSpellchecker))) { return false; } + this->currentSpellcheckers.push_back(currentSpellchecker); return true; } +bool WindowsSpellchecker::SetDictionary(const std::string& language, const std::string& path) { + if (!this->spellcheckerFactory) { + return false; + } + + for (size_t i = 0; i < this->currentSpellcheckers.size(); ++i) { + ISpellChecker* currentSpellchecker = this->currentSpellcheckers[i]; + currentSpellchecker->Release(); + } + this->currentSpellcheckers.clear(); + + return AddDictionary(language, path); +} + std::vector WindowsSpellchecker::GetAvailableDictionaries(const std::string& path) { HRESULT hr; @@ -157,78 +163,92 @@ std::vector WindowsSpellchecker::GetAvailableDictionaries(const std } bool WindowsSpellchecker::IsMisspelled(const std::string& word) { - if (this->currentSpellchecker == NULL) { + if (this->currentSpellcheckers.empty()) { return false; } IEnumSpellingError* errors = NULL; std::wstring wword = ToWString(word); - if (FAILED(this->currentSpellchecker->Check(wword.c_str(), &errors))) { - return false; - } - bool ret; - - ISpellingError* dontcare; - HRESULT hr = errors->Next(&dontcare); - - switch (hr) { - case S_OK: - // S_OK == There are errors to examine - ret = true; - dontcare->Release(); - break; - case S_FALSE: - // Worked, but error free - ret = false; - break; - default: - // Something went pear-shaped - ret = false; - break; + for (size_t i = 0; i < this->currentSpellcheckers.size(); ++i) { + ISpellChecker* currentSpellchecker = this->currentSpellcheckers[i]; + errors = NULL; + if (FAILED(currentSpellchecker->Check(wword.c_str(), &errors))) { + continue; + } + + ISpellingError* dontcare; + HRESULT hr = errors->Next(&dontcare); + + switch (hr) { + case S_OK: + // S_OK == There are errors to examine + ret = true; + dontcare->Release(); + break; + case S_FALSE: + // Worked, but error free + ret = false; + break; + default: + // Something went pear-shaped + ret = false; + break; + } + + errors->Release(); + + if (ret == false) { + break; + } } - errors->Release(); return ret; } std::vector WindowsSpellchecker::CheckSpelling(const uint16_t *text, size_t length) { std::vector result; - if (this->currentSpellchecker == NULL) { + if (this->currentSpellcheckers.empty()) { return result; } IEnumSpellingError* errors = NULL; std::wstring wtext(reinterpret_cast(text), length); - if (FAILED(this->currentSpellchecker->Check(wtext.c_str(), &errors))) { - return result; - } - ISpellingError *error; - while (errors->Next(&error) == S_OK) { - ULONG start, length; - error->get_StartIndex(&start); - error->get_Length(&length); - - MisspelledRange range; - range.start = start; - range.end = start + length; - result.push_back(range); - error->Release(); + // TODO: Intersect results + for (size_t i = 0; i < this->currentSpellcheckers.size(); ++i) { + ISpellChecker* currentSpellchecker = this->currentSpellcheckers[i]; + if (FAILED(currentSpellchecker->Check(wtext.c_str(), &errors))) { + return result; + } + + ISpellingError* error; + while (errors->Next(&error) == S_OK) { + ULONG start, length; + error->get_StartIndex(&start); + error->get_Length(&length); + + MisspelledRange range; + range.start = start; + range.end = start + length; + result.push_back(range); + error->Release(); + } + + errors->Release(); } - errors->Release(); return result; } void WindowsSpellchecker::Add(const std::string& word) { - if (this->currentSpellchecker == NULL) { + if (this->currentSpellcheckers.empty()) { return; } std::wstring wword = ToWString(word); - this->currentSpellchecker->Add(wword.c_str()); + this->currentSpellcheckers[0]->Add(wword.c_str()); } void WindowsSpellchecker::Remove(const std::string& word) { @@ -238,37 +258,41 @@ void WindowsSpellchecker::Remove(const std::string& word) { std::vector WindowsSpellchecker::GetCorrectionsForMisspelling(const std::string& word) { - if (this->currentSpellchecker == NULL) { + if (this->currentSpellcheckers.empty()) { return std::vector(); } std::wstring& wword = ToWString(word); IEnumString* words = NULL; + std::vector ret; - HRESULT hr = this->currentSpellchecker->Suggest(wword.c_str(), &words); + for (size_t i = 0; i < this->currentSpellcheckers.size(); ++i) { + ISpellChecker* currentSpellchecker = this->currentSpellcheckers[i]; + words = NULL; + HRESULT hr = this->currentSpellchecker->Suggest(wword.c_str(), &words); - if (FAILED(hr)) { - return std::vector(); - } + if (FAILED(hr)) { + continue; + } - // NB: S_FALSE == word is spelled correctly - if (hr == S_FALSE) { - words->Release(); - return std::vector(); - } + // NB: S_FALSE == word is spelled correctly + if (hr == S_FALSE) { + words->Release(); + continue; + } - std::vector ret; + LPOLESTR correction; + while (words->Next(1, &correction, NULL) == S_OK) { + std::wstring wcorr; + wcorr.assign(correction); + ret.push_back(ToUTF8(wcorr)); - LPOLESTR correction; - while (words->Next(1, &correction, NULL) == S_OK) { - std::wstring wcorr; - wcorr.assign(correction); - ret.push_back(ToUTF8(wcorr)); + CoTaskMemFree(correction); + } - CoTaskMemFree(correction); + words->Release(); } - words->Release(); return ret; } diff --git a/src/spellchecker_win.h b/src/spellchecker_win.h index ed5d441..ceb24b9 100644 --- a/src/spellchecker_win.h +++ b/src/spellchecker_win.h @@ -24,7 +24,7 @@ class WindowsSpellchecker : public SpellcheckerImplementation { void Remove(const std::string& word); private: - ISpellChecker* currentSpellchecker; + std::vector currentSpellcheckers; ISpellCheckerFactory* spellcheckerFactory; }; From 0886cf2a2cb829682ff1d0102d0b4988fda49deb Mon Sep 17 00:00:00 2001 From: Svyatoslav Gryaznov Date: Sat, 21 May 2016 17:02:50 +0300 Subject: [PATCH 4/8] Fix typo --- src/spellchecker_win.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/spellchecker_win.cc b/src/spellchecker_win.cc index 35f428a..e1843d6 100644 --- a/src/spellchecker_win.cc +++ b/src/spellchecker_win.cc @@ -269,7 +269,7 @@ std::vector WindowsSpellchecker::GetCorrectionsForMisspelling(const for (size_t i = 0; i < this->currentSpellcheckers.size(); ++i) { ISpellChecker* currentSpellchecker = this->currentSpellcheckers[i]; words = NULL; - HRESULT hr = this->currentSpellchecker->Suggest(wword.c_str(), &words); + HRESULT hr = currentSpellchecker->Suggest(wword.c_str(), &words); if (FAILED(hr)) { continue; @@ -278,7 +278,7 @@ std::vector WindowsSpellchecker::GetCorrectionsForMisspelling(const // NB: S_FALSE == word is spelled correctly if (hr == S_FALSE) { words->Release(); - continue; + continue; } LPOLESTR correction; From 9ab375d1149f2ef30a4cb179c6ab62d6a0cb7a20 Mon Sep 17 00:00:00 2001 From: Svyatoslav Gryaznov Date: Sat, 21 May 2016 17:23:42 +0300 Subject: [PATCH 5/8] Fix Windows 8+ spellchecker --- src/spellchecker_win.cc | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/spellchecker_win.cc b/src/spellchecker_win.cc index e1843d6..0fbea9f 100644 --- a/src/spellchecker_win.cc +++ b/src/spellchecker_win.cc @@ -20,6 +20,10 @@ namespace spellchecker { LONG g_COMRefcount = 0; bool g_COMFailed = false; +static bool compareMisspelledRanges(const MisspelledRange& lhs, const MisspelledRange& rhs) { + return lhs.start < rhs.start; +} + std::string ToUTF8(const std::wstring& string) { if (string.length() < 1) { return std::string(); @@ -216,13 +220,14 @@ std::vector WindowsSpellchecker::CheckSpelling(const uint16_t * IEnumSpellingError* errors = NULL; std::wstring wtext(reinterpret_cast(text), length); - // TODO: Intersect results for (size_t i = 0; i < this->currentSpellcheckers.size(); ++i) { ISpellChecker* currentSpellchecker = this->currentSpellcheckers[i]; if (FAILED(currentSpellchecker->Check(wtext.c_str(), &errors))) { - return result; + continue; } + std::vector currentResult; + ISpellingError* error; while (errors->Next(&error) == S_OK) { ULONG start, length; @@ -232,11 +237,25 @@ std::vector WindowsSpellchecker::CheckSpelling(const uint16_t * MisspelledRange range; range.start = start; range.end = start + length; - result.push_back(range); + currentResult.push_back(range); error->Release(); } errors->Release(); + + if (currentResult.empty()) { + return std::vector(); + } + if (result.empty()) { + std::swap(result, currentResult); + } else { + std::vector intersection; + std::set_intersection( + result.begin(), result.end(), + currentResult.begin(), currentResult.end(), + std::back_inserter(intersection), compareMisspelledRanges); + std::swap(result, intersection); + } } return result; From 2663cd658100a5538bbce4be998752d965eb10af Mon Sep 17 00:00:00 2001 From: Svyatoslav Gryaznov Date: Sat, 21 May 2016 19:15:28 +0300 Subject: [PATCH 6/8] Fix Hunspell for Windows --- src/spellchecker_hunspell.cc | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/spellchecker_hunspell.cc b/src/spellchecker_hunspell.cc index bdcb1ed..a4ee19d 100644 --- a/src/spellchecker_hunspell.cc +++ b/src/spellchecker_hunspell.cc @@ -34,8 +34,17 @@ bool HunspellSpellchecker::AddDictionary(const std::string& language, const std: } fclose(handle); - // TODO: On windows locale names are different - std::locale loc(lang + ".UTF-8"); + std::locale loc; + try { + // On Linux locale requires "UTF-8" suffix; e.g., "en_US.UTF-8" + loc = std::locale((lang + ".UTF-8").c_str()); + } catch (std::runtime_error & e) { + // On Windows locale names are different; e.g. en-US + std::string langDashed = language; + std::replace(langDashed.begin(), langDashed.end(), '_', '-'); + std::locale loc(langDashed.c_str()); + } + Hunspell* hunspell = new Hunspell(affixpath.c_str(), dpath.c_str()); hunspells.push_back(std::make_pair(loc, hunspell)); return true; From 07946c7e4709a0084333fa493a330f379629020f Mon Sep 17 00:00:00 2001 From: Svyatoslav Gryaznov Date: Sat, 21 May 2016 19:46:59 +0300 Subject: [PATCH 7/8] Enable exceptions on non-Windows --- binding.gyp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/binding.gyp b/binding.gyp index 84fc1fc..a8defb0 100644 --- a/binding.gyp +++ b/binding.gyp @@ -50,12 +50,16 @@ 'src/spellchecker_linux.cc', 'src/transcoder_posix.cc', ], + 'cflags!': [ '-fno-exceptions' ], + 'cflags_cc!': [ '-fno-exceptions' ], }], ['OS=="mac"', { 'sources': [ 'src/spellchecker_mac.mm', 'src/transcoder_posix.cc', ], + 'cflags!': [ '-fno-exceptions' ], + 'cflags_cc!': [ '-fno-exceptions' ], 'link_settings': { 'libraries': [ '$(SDKROOT)/System/Library/Frameworks/AppKit.framework', From 66bf643f19eb7e505662ed178718b3fa3bfaf9aa Mon Sep 17 00:00:00 2001 From: Svyatoslav Gryaznov Date: Sat, 21 May 2016 19:48:42 +0300 Subject: [PATCH 8/8] Add stdexcept header --- src/spellchecker_hunspell.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/spellchecker_hunspell.cc b/src/spellchecker_hunspell.cc index a4ee19d..2794ac8 100644 --- a/src/spellchecker_hunspell.cc +++ b/src/spellchecker_hunspell.cc @@ -1,6 +1,7 @@ #include #include #include +#include #include "../vendor/hunspell/src/hunspell/hunspell.hxx" #include "spellchecker_hunspell.h"