X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FHunspellChecker.cpp;h=a2d2c95e05250c6976d25bdef81f57e42faac338;hb=e903d0158e1d92c0d9c17656af10df9fa9a3d723;hp=83bd586dcfc29dd8eb03288a5e44bf584fb532fd;hpb=dec437fbac739fb4d8e78842b9f9286bbaaf0971;p=lyx.git diff --git a/src/HunspellChecker.cpp b/src/HunspellChecker.cpp index 83bd586dcf..a2d2c95e05 100644 --- a/src/HunspellChecker.cpp +++ b/src/HunspellChecker.cpp @@ -11,6 +11,7 @@ #include #include "HunspellChecker.h" +#include "PersonalWordList.h" #include "LyXRC.h" #include "WordLangTuple.h" @@ -42,6 +43,8 @@ namespace lyx { namespace { typedef map Spellers; +typedef map LangPersonalWordList; + typedef vector IgnoreList; } // anon namespace @@ -49,39 +52,89 @@ typedef vector IgnoreList; struct HunspellChecker::Private { - Private() {} - + Private(); ~Private(); + void cleanCache(); + void setUserPath(std::string path); const string dictPath(int selector); bool haveLanguageFiles(string const & hpath); - bool haveDictionary(string const & lang, string & hpath); - bool haveDictionary(string const & lang); - Hunspell * addSpeller(string const & lang, string & hpath); - Hunspell * addSpeller(string const & lang); - Hunspell * speller(string const & lang); + bool haveDictionary(Language const * lang, string & hpath); + bool haveDictionary(Language const * lang); + int numDictionaries() const; + Hunspell * addSpeller(Language const * lang, string & hpath); + Hunspell * addSpeller(Language const * lang); + Hunspell * speller(Language const * lang); /// ignored words bool isIgnored(WordLangTuple const & wl) const; - + /// personal word list interface + void remove(WordLangTuple const & wl); + void insert(WordLangTuple const & wl); + bool learned(WordLangTuple const & wl); /// the spellers Spellers spellers_; /// IgnoreList ignored_; + /// + LangPersonalWordList personal_; + /// + std::string user_path_; /// the location below system/user directory /// there the aff+dic files lookup will happen - const string dictDirectory(void) const { return "dict"; } - int maxLookupSelector(void) const { return 3; } + const string dictDirectory(void) const { return "dicts"; } + int maxLookupSelector(void) const { return 4; } + const string HunspellDictionaryName(Language const * lang) { + return lang->variety().empty() + ? lang->code() + : lang->code() + "-" + lang->variety(); + } + const string osPackageDictDirectory(void) { + return "/usr/share/myspell"; + } }; +HunspellChecker::Private::Private() +{ + setUserPath(lyxrc.hunspelldir_path); +} + + HunspellChecker::Private::~Private() +{ + cleanCache(); +} + + +void HunspellChecker::Private::setUserPath(std::string path) +{ + if (user_path_ != lyxrc.hunspelldir_path) { + cleanCache(); + user_path_ = path; + } +} + + +void HunspellChecker::Private::cleanCache() { Spellers::iterator it = spellers_.begin(); Spellers::iterator end = spellers_.end(); for (; it != end; ++it) { - if ( 0 != it->second) delete it->second; + delete it->second; + it->second = 0; + } + + LangPersonalWordList::const_iterator pdit = personal_.begin(); + LangPersonalWordList::const_iterator pdet = personal_.end(); + + for (; pdit != pdet; ++pdit) { + if ( 0 == pdit->second) + continue; + PersonalWordList * pd = pdit->second; + pd->save(); + delete pd; } } @@ -97,66 +150,80 @@ bool HunspellChecker::Private::haveLanguageFiles(string const & hpath) const string HunspellChecker::Private::dictPath(int selector) { switch (selector) { + case 3: + return addName(osPackageDictDirectory(),dictDirectory()); + break; case 2: - return addName(lyx::support::package().system_support().absFileName(),dictDirectory()); + return addName(package().system_support().absFileName(),dictDirectory()); break; case 1: - return addName(lyx::support::package().user_support().absFileName(),dictDirectory()); + return addName(package().user_support().absFileName(),dictDirectory()); break; default: - return lyxrc.hunspelldir_path; + return user_path_; } } -bool HunspellChecker::Private::haveDictionary(string const & lang, string & hpath) +bool HunspellChecker::Private::haveDictionary(Language const * lang, string & hpath) { if (hpath.empty()) { return false; } - LYXERR(Debug::FILES, "check hunspell path: " << hpath << " for language " << lang); - string h_path = addName(hpath, lang); + LYXERR(Debug::FILES, "check hunspell path: " << hpath + << " for language " << (lang ? lang->lang() : "NULL" )); + + string h_path = addName(hpath, HunspellDictionaryName(lang)); + // first we try lang code+variety + if (haveLanguageFiles(h_path)) { + LYXERR(Debug::FILES, " found " << h_path); + hpath = h_path; + return true; + } + // another try with code, '_' replaced by '-' + h_path = addName(hpath, subst(lang->code(), '_', '-')); if (!haveLanguageFiles(h_path)) { - // try with '_' replaced by '-' - h_path = addName(hpath, subst(lang, '_', '-')); - if (!haveLanguageFiles(h_path)) { - // FIXME: 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 false; - } + return false; } + LYXERR(Debug::FILES, " found " << h_path); hpath = h_path; return true; } -bool HunspellChecker::Private::haveDictionary(string const & lang) +bool HunspellChecker::Private::haveDictionary(Language const * lang) { bool result = false; + + setUserPath(lyxrc.hunspelldir_path); 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(string const & lang) +Hunspell * HunspellChecker::Private::speller(Language const * lang) { - Spellers::iterator it = spellers_.find(lang); - if (it != spellers_.end()) + 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(string const & lang,string & path) +Hunspell * HunspellChecker::Private::addSpeller(Language const * lang,string & path) { if (!haveDictionary(lang, path)) { - spellers_[lang] = 0; + spellers_[lang->lang()] = 0; return 0; } @@ -164,22 +231,47 @@ Hunspell * HunspellChecker::Private::addSpeller(string const & lang,string & pat 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"); - spellers_[lang] = h; + spellers_[lang->lang()] = h; return h; } -Hunspell * HunspellChecker::Private::addSpeller(string const & lang) +Hunspell * HunspellChecker::Private::addSpeller(Language const * lang) { Hunspell * h = 0; for ( int p = 0; p < maxLookupSelector() && 0 == h; p++ ) { string lpath = dictPath(p); h = addSpeller(lang, lpath); } + if (0 != h) { + string const encoding = h->get_dic_encoding(); + PersonalWordList * pd = new PersonalWordList(lang->lang()); + pd->load(); + personal_[lang->lang()] = pd; + docstring_list::const_iterator it = pd->begin(); + docstring_list::const_iterator et = pd->end(); + for (; it != et; ++it) { + string const word_to_add = to_iconv_encoding(*it, encoding); + h->add(word_to_add.c_str()); + } + } return h; } +int HunspellChecker::Private::numDictionaries() const +{ + int result = 0; + Spellers::const_iterator it = spellers_.begin(); + Spellers::const_iterator et = spellers_.end(); + + for (; it != et; ++it) { + result += it->second != 0; + } + return result; +} + + bool HunspellChecker::Private::isIgnored(WordLangTuple const & wl) const { IgnoreList::const_iterator it = ignored_.begin(); @@ -192,6 +284,45 @@ bool HunspellChecker::Private::isIgnored(WordLangTuple const & wl) const return false; } +/// personal word list interface +void HunspellChecker::Private::remove(WordLangTuple const & wl) +{ + Hunspell * h = speller(wl.lang()); + if (!h) + return; + string const encoding = h->get_dic_encoding(); + string const word_to_check = to_iconv_encoding(wl.word(), encoding); + h->remove(word_to_check.c_str()); + PersonalWordList * pd = personal_[wl.lang()->lang()]; + if (!pd) + return; + pd->remove(wl.word()); +} + + +void HunspellChecker::Private::insert(WordLangTuple const & wl) +{ + Hunspell * h = speller(wl.lang()); + if (!h) + return; + string const encoding = h->get_dic_encoding(); + string const word_to_check = to_iconv_encoding(wl.word(), encoding); + h->add(word_to_check.c_str()); + PersonalWordList * pd = personal_[wl.lang()->lang()]; + if (!pd) + return; + pd->insert(wl.word()); +} + + +bool HunspellChecker::Private::learned(WordLangTuple const & wl) +{ + PersonalWordList * pd = personal_[wl.lang()->lang()]; + if (!pd) + return false; + return pd->exists(wl.word()); +} + HunspellChecker::HunspellChecker(): d(new Private) { @@ -209,23 +340,23 @@ SpellChecker::Result HunspellChecker::check(WordLangTuple const & wl) if (d->isIgnored(wl)) return WORD_OK; - Hunspell * h = d->speller(wl.lang()->code()); + Hunspell * h = d->speller(wl.lang()); if (!h) - return WORD_OK; + return NO_DICTIONARY; int info; string const encoding = h->get_dic_encoding(); string const word_to_check = to_iconv_encoding(wl.word(), encoding); - + if (h->spell(word_to_check.c_str(), &info)) - return WORD_OK; + 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); } if (info & SPELL_FORBIDDEN) { - // FIXME: What to do with that? + // This was removed from personal dictionary LYXERR(Debug::FILES, "Hunspell explicit forbidden word found " << word_to_check); } @@ -241,11 +372,16 @@ void HunspellChecker::advanceChangeNumber() void HunspellChecker::insert(WordLangTuple const & wl) { - string const word_to_check = to_utf8(wl.word()); - Hunspell * h = d->speller(wl.lang()->code()); - if (!h) - return; - h->add(word_to_check.c_str()); + d->insert(wl); + LYXERR(Debug::GUI, "learn word: \"" << wl.word() << "\"") ; + advanceChangeNumber(); +} + + +void HunspellChecker::remove(WordLangTuple const & wl) +{ + d->remove(wl); + LYXERR(Debug::GUI, "unlearn word: \"" << wl.word() << "\"") ; advanceChangeNumber(); } @@ -253,6 +389,7 @@ void HunspellChecker::insert(WordLangTuple const & wl) void HunspellChecker::accept(WordLangTuple const & wl) { d->ignored_.push_back(wl); + LYXERR(Debug::GUI, "ignore word: \"" << wl.word() << "\"") ; advanceChangeNumber(); } @@ -261,7 +398,7 @@ void HunspellChecker::suggest(WordLangTuple const & wl, docstring_list & suggestions) { suggestions.clear(); - Hunspell * h = d->speller(wl.lang()->code()); + Hunspell * h = d->speller(wl.lang()); if (!h) return; string const encoding = h->get_dic_encoding(); @@ -276,11 +413,36 @@ void HunspellChecker::suggest(WordLangTuple const & wl, } +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); + 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); +} + + bool HunspellChecker::hasDictionary(Language const * lang) const { if (!lang) return false; - return (d->haveDictionary(lang->code())); + return (d->haveDictionary(lang)); +} + + +int HunspellChecker::numDictionaries() const +{ + return d->numDictionaries(); }