X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FHunspellChecker.cpp;h=01ec19498b70e28075db5e5b8a9c6bb8c4c77e49;hb=26ba2a65838731ce639a09539f617cb0f0be3b22;hp=d044889107e19706759cbafed44644812575d37e;hpb=c91e2c59f47431247b44563bed2d0f0eae96f9d6;p=lyx.git diff --git a/src/HunspellChecker.cpp b/src/HunspellChecker.cpp index d044889107..01ec19498b 100644 --- a/src/HunspellChecker.cpp +++ b/src/HunspellChecker.cpp @@ -16,17 +16,13 @@ #include "LyXRC.h" #include "WordLangTuple.h" -#include "frontends/alert.h" - #include "support/debug.h" #include "support/docstring_list.h" #include "support/filetools.h" #include "support/Package.h" #include "support/FileName.h" -#include "support/gettext.h" #include "support/lassert.h" #include "support/lstrings.h" -#include "support/os.h" #include @@ -45,9 +41,14 @@ namespace { typedef map Spellers; typedef map LangPersonalWordList; -typedef vector IgnoreList; +docstring remap_result(docstring const & s) +{ + // substitute RIGHT SINGLE QUOTATION MARK + // by APOSTROPHE + return subst(s, 0x2019, 0x0027); +} -} // anon namespace +} // namespace struct HunspellChecker::Private @@ -56,7 +57,7 @@ struct HunspellChecker::Private ~Private(); void cleanCache(); - void setUserPath(std::string path); + void setUserPath(std::string const & path); const string dictPath(int selector); bool haveLanguageFiles(string const & hpath); bool haveDictionary(Language const * lang, string & hpath); @@ -65,6 +66,7 @@ struct HunspellChecker::Private Hunspell * addSpeller(Language const * lang, string & hpath); Hunspell * addSpeller(Language const * lang); Hunspell * speller(Language const * lang); + Hunspell * lookup(Language const * lang); /// ignored words bool isIgnored(WordLangTuple const & wl) const; /// personal word list interface @@ -74,7 +76,7 @@ struct HunspellChecker::Private /// the spellers Spellers spellers_; /// - IgnoreList ignored_; + WordLangTable ignored_; /// LangPersonalWordList personal_; /// @@ -83,15 +85,18 @@ struct HunspellChecker::Private /// the location below system/user directory /// there the aff+dic files lookup will happen const string dictDirectory(void) const { return "dicts"; } - int maxLookupSelector(void) const { return 4; } + int maxLookupSelector(void) const { return 5; } const string HunspellDictionaryName(Language const * lang) { - return lang->variety().empty() + return lang->variety().empty() ? lang->code() : lang->code() + "-" + lang->variety(); } - const string osPackageDictDirectory(void) { + const string myspellPackageDictDirectory(void) { return "/usr/share/myspell"; } + const string hunspellPackageDictDirectory(void) { + return "/usr/share/hunspell"; + } }; @@ -107,7 +112,7 @@ HunspellChecker::Private::~Private() } -void HunspellChecker::Private::setUserPath(std::string path) +void HunspellChecker::Private::setUserPath(std::string const & path) { if (user_path_ != lyxrc.hunspelldir_path) { cleanCache(); @@ -123,14 +128,14 @@ void HunspellChecker::Private::cleanCache() for (; it != end; ++it) { delete it->second; - it->second = 0; + it->second = nullptr; } LangPersonalWordList::const_iterator pdit = personal_.begin(); LangPersonalWordList::const_iterator pdet = personal_.end(); for (; pdit != pdet; ++pdit) { - if ( 0 == pdit->second) + if (pdit->second == nullptr) continue; PersonalWordList * pd = pdit->second; pd->save(); @@ -150,15 +155,14 @@ bool HunspellChecker::Private::haveLanguageFiles(string const & hpath) const string HunspellChecker::Private::dictPath(int selector) { switch (selector) { + case 4: + return hunspellPackageDictDirectory(); case 3: - return addName(osPackageDictDirectory(),dictDirectory()); - break; + return myspellPackageDictDirectory(); case 2: return addName(package().system_support().absFileName(),dictDirectory()); - break; case 1: return addName(package().user_support().absFileName(),dictDirectory()); - break; default: return user_path_; } @@ -167,14 +171,17 @@ const string HunspellChecker::Private::dictPath(int selector) bool HunspellChecker::Private::haveDictionary(Language const * lang, string & hpath) { - if (hpath.empty()) { + if (hpath.empty() || !lang) return false; - } + + if (lookup(lang)) return true; + + string d_name = HunspellDictionaryName(lang); LYXERR(Debug::FILES, "check hunspell path: " << hpath - << " for language " << (lang ? lang->lang() : "NULL" )); + << " for language " << lang->lang() << " with name " << d_name); - string h_path = addName(hpath, HunspellDictionaryName(lang)); + string h_path = addName(hpath, d_name); // first we try lang code+variety if (haveLanguageFiles(h_path)) { LYXERR(Debug::FILES, " found " << h_path); @@ -183,9 +190,8 @@ bool HunspellChecker::Private::haveDictionary(Language const * lang, string & hp } // another try with code, '_' replaced by '-' h_path = addName(hpath, subst(lang->code(), '_', '-')); - if (!haveLanguageFiles(h_path)) { + if (!haveLanguageFiles(h_path)) return false; - } LYXERR(Debug::FILES, " found " << h_path); hpath = h_path; return true; @@ -197,40 +203,42 @@ bool HunspellChecker::Private::haveDictionary(Language const * lang) bool result = false; setUserPath(lyxrc.hunspelldir_path); - for ( int p = 0; !result && p < maxLookupSelector(); p++ ) { + for (int p = 0; !result && p < maxLookupSelector(); ++p) { string lpath = dictPath(p); result = haveDictionary(lang, lpath); } - // FIXME: if result is false... - // we should indicate somehow that this language is not - // supported, probably by popping a warning. But we'll need to - // remember which warnings we've issued. return result; } Hunspell * HunspellChecker::Private::speller(Language const * lang) { + Hunspell * h = lookup(lang); + if (h) return h; + setUserPath(lyxrc.hunspelldir_path); - Spellers::iterator it = spellers_.find(lang->lang()); - if (it != spellers_.end()) { - return it->second; - } return addSpeller(lang); } -Hunspell * HunspellChecker::Private::addSpeller(Language const * lang,string & path) +Hunspell * HunspellChecker::Private::lookup(Language const * lang) +{ + Spellers::iterator it = spellers_.find(lang->lang()); + return it != spellers_.end() ? it->second : nullptr; +} + + +Hunspell * HunspellChecker::Private::addSpeller(Language const * lang, string & path) { if (!haveDictionary(lang, path)) { - spellers_[lang->lang()] = 0; - return 0; + spellers_[lang->lang()] = nullptr; + return nullptr; } FileName const affix(path + ".aff"); FileName const dict(path + ".dic"); Hunspell * h = new Hunspell(affix.absFileName().c_str(), dict.absFileName().c_str()); - LYXERR(Debug::FILES, "Hunspell speller for langage " << lang << " at " << dict << " found"); + LYXERR(Debug::FILES, "Hunspell speller for langage " << lang << " at " << dict << " added."); spellers_[lang->lang()] = h; return h; } @@ -238,12 +246,12 @@ Hunspell * HunspellChecker::Private::addSpeller(Language const * lang,string & p Hunspell * HunspellChecker::Private::addSpeller(Language const * lang) { - Hunspell * h = 0; - for ( int p = 0; p < maxLookupSelector() && 0 == h; p++ ) { + Hunspell * h = nullptr; + for (int p = 0; p < maxLookupSelector() && nullptr == h; ++p) { string lpath = dictPath(p); h = addSpeller(lang, lpath); } - if (0 != h) { + if (h) { string const encoding = h->get_dic_encoding(); PersonalWordList * pd = new PersonalWordList(lang->lang()); pd->load(); @@ -265,20 +273,19 @@ int HunspellChecker::Private::numDictionaries() const Spellers::const_iterator it = spellers_.begin(); Spellers::const_iterator et = spellers_.end(); - for (; it != et; ++it) { - result += it->second != 0; - } + for (; it != et; ++it) + result += it->second != nullptr; return result; } bool HunspellChecker::Private::isIgnored(WordLangTuple const & wl) const { - IgnoreList::const_iterator it = ignored_.begin(); + WordLangTable::const_iterator it = ignored_.begin(); for (; it != ignored_.end(); ++it) { - if ((*it).lang()->code() != wl.lang()->code()) + if (it->lang()->code() != wl.lang()->code()) continue; - if ((*it).word() == wl.word()) + if (it->word() == wl.word()) return true; } return false; @@ -324,9 +331,9 @@ bool HunspellChecker::Private::learned(WordLangTuple const & wl) } -HunspellChecker::HunspellChecker(): d(new Private) -{ -} +HunspellChecker::HunspellChecker() + : d(new Private) +{} HunspellChecker::~HunspellChecker() @@ -335,11 +342,20 @@ HunspellChecker::~HunspellChecker() } -SpellChecker::Result HunspellChecker::check(WordLangTuple const & wl) +SpellChecker::Result HunspellChecker::check(WordLangTuple const & wl, + vector const & docdict) { if (d->isIgnored(wl)) return WORD_OK; + WordLangTable::const_iterator it = docdict.begin(); + for (; it != docdict.end(); ++it) { + if (it->lang()->code() != wl.lang()->code()) + continue; + if (it->word() == wl.word()) + return DOCUMENT_LEARNED_WORD; + } + Hunspell * h = d->speller(wl.lang()); if (!h) return NO_DICTIONARY; @@ -348,16 +364,22 @@ SpellChecker::Result HunspellChecker::check(WordLangTuple const & wl) string const encoding = h->get_dic_encoding(); string const word_to_check = to_iconv_encoding(wl.word(), encoding); + LYXERR(Debug::GUI, "spellCheck: \"" << + wl.word() << "\", lang = " << wl.lang()->lang()) ; +#ifdef HAVE_HUNSPELL_CXXABI + if (h->spell(word_to_check, &info)) +#else if (h->spell(word_to_check.c_str(), &info)) +#endif return d->learned(wl) ? LEARNED_WORD : WORD_OK; if (info & SPELL_COMPOUND) { // FIXME: What to do with that? - LYXERR(Debug::FILES, "Hunspell compound word found " << word_to_check); + LYXERR(Debug::GUI, "Hunspell compound word found " << word_to_check); } if (info & SPELL_FORBIDDEN) { // This was removed from personal dictionary - LYXERR(Debug::FILES, "Hunspell explicit forbidden word found " << word_to_check); + LYXERR(Debug::GUI, "Hunspell explicit forbidden word found " << word_to_check); } return UNKNOWN_WORD; @@ -403,13 +425,44 @@ void HunspellChecker::suggest(WordLangTuple const & wl, return; string const encoding = h->get_dic_encoding(); string const word_to_check = to_iconv_encoding(wl.word(), encoding); +#ifdef HAVE_HUNSPELL_CXXABI + vector wlst = h->suggest(word_to_check); + for (auto const & s : wlst) + suggestions.push_back(remap_result(from_iconv_encoding(s, encoding))); +#else char ** suggestion_list; int const suggestion_number = h->suggest(&suggestion_list, word_to_check.c_str()); + if (suggestion_number <= 0) + return; + for (int i = 0; i != suggestion_number; ++i) + suggestions.push_back(remap_result(from_iconv_encoding(suggestion_list[i], encoding))); + h->free_list(&suggestion_list, suggestion_number); +#endif +} + + +void HunspellChecker::stem(WordLangTuple const & wl, + docstring_list & suggestions) +{ + suggestions.clear(); + Hunspell * h = d->speller(wl.lang()); + if (!h) + return; + string const encoding = h->get_dic_encoding(); + string const word_to_check = to_iconv_encoding(wl.word(), encoding); +#ifdef HAVE_HUNSPELL_CXXABI + vector wlst = h->stem(word_to_check); + for (auto const & s : wlst) + suggestions.push_back(from_iconv_encoding(s, encoding)); +#else + char ** suggestion_list; + int const suggestion_number = h->stem(&suggestion_list, word_to_check.c_str()); if (suggestion_number <= 0) return; for (int i = 0; i != suggestion_number; ++i) suggestions.push_back(from_iconv_encoding(suggestion_list[i], encoding)); h->free_list(&suggestion_list, suggestion_number); +#endif } @@ -417,7 +470,7 @@ bool HunspellChecker::hasDictionary(Language const * lang) const { if (!lang) return false; - return (d->haveDictionary(lang)); + return d->haveDictionary(lang); }