X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FAspellChecker.cpp;h=c549980e0378b42a3b33ad65a260eb793a0d5440;hb=bf3f3f9816657c2895d838a57bfaa6b658730c7c;hp=36e8e04c8f23375ba608b61261a4e16a4328bd70;hpb=a174f43bc5515291c4d0b28cacd806c9c9ea4de4;p=lyx.git diff --git a/src/AspellChecker.cpp b/src/AspellChecker.cpp index 36e8e04c8f..c549980e03 100644 --- a/src/AspellChecker.cpp +++ b/src/AspellChecker.cpp @@ -12,11 +12,14 @@ #include #include "AspellChecker.h" +#include "PersonalWordList.h" + #include "LyXRC.h" #include "WordLangTuple.h" #include "support/lassert.h" #include "support/debug.h" +#include "support/lstrings.h" #include "support/docstring_list.h" #include "support/filetools.h" @@ -39,9 +42,12 @@ namespace { struct Speller { AspellConfig * config; AspellCanHaveError * e_speller; + bool accept_compound; + docstring_list ignored_words_; }; typedef std::map Spellers; +typedef map LangPersonalWordList; } // anon namespace @@ -52,16 +58,10 @@ struct AspellChecker::Private ~Private(); /// add a speller of the given language and variety - AspellSpeller * addSpeller(string const & lang, - string const & variety = string()); + AspellSpeller * addSpeller(Language const * lang); /// - AspellSpeller * speller(string const & lang, - string const & variety); - - /// create a unique ID from lang code and variety - string const spellerID(string const & lang, - string const & variety); + AspellSpeller * speller(Language const * lang); bool isValidDictionary(AspellConfig * config, string const & lang, string const & variety); @@ -70,12 +70,27 @@ struct AspellChecker::Private string const & lang, string const & variety); AspellConfig * getConfig(string const & lang, string const & variety); + string toAspellWord(docstring const & word) const; + + SpellChecker::Result check(AspellSpeller * m, + WordLangTuple const & word) const; + + void initSessionDictionary(Speller const & speller, PersonalWordList * pd); + void addToSession(AspellCanHaveError * speller, docstring const & word); + void insert(WordLangTuple const & word); + void remove(WordLangTuple const & word); + bool learned(WordLangTuple const & word); + + void accept(Speller & speller, WordLangTuple const & word); + /// the spellers Spellers spellers_; + LangPersonalWordList personal_; + /// the location below system/user directory /// there the rws files lookup will happen - const string dictDirectory(void) { return "dict"; } + const string dictDirectory(void) { return "dicts"; } /// there the dat+cmap files lookup will happen const string dataDirectory(void) { return "data"; } /// os package directory constants @@ -113,6 +128,17 @@ AspellChecker::Private::~Private() } delete_aspell_config(it->second.config); } + + 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; + } } @@ -186,16 +212,43 @@ AspellConfig * AspellChecker::Private::getConfig(string const & lang, string con } -AspellSpeller * AspellChecker::Private::addSpeller(string const & lang, - string const & variety) +void AspellChecker::Private::addToSession(AspellCanHaveError * speller, docstring const & word) { - Speller m; + string const word_to_add = toAspellWord(word); + if(1 != aspell_speller_add_to_session(to_aspell_speller(speller), word_to_add.c_str(), -1)) + LYXERR(Debug::GUI, "aspell add to session: " << aspell_error_message(speller)); +} - m.config = getConfig(lang, variety); + +void AspellChecker::Private::initSessionDictionary( + Speller const & speller, + PersonalWordList * pd) +{ + AspellSpeller * aspell = to_aspell_speller(speller.e_speller); + aspell_speller_clear_session(aspell); + docstring_list::const_iterator it = pd->begin(); + docstring_list::const_iterator et = pd->end(); + for (; it != et; ++it) { + addToSession(speller.e_speller, *it); + } + it = speller.ignored_words_.begin(); + et = speller.ignored_words_.end(); + for (; it != et; ++it) { + addToSession(speller.e_speller, *it); + } +} + + +AspellSpeller * AspellChecker::Private::addSpeller(Language const * lang) +{ + Speller m; + string const code = lang->code(); + string const variety = lang->variety(); + m.config = getConfig(code, variety); // Aspell supports both languages and varieties (such as German // old vs. new spelling). The respective naming convention is // lang_REGION-variety (e.g. de_DE-alt). - aspell_config_replace(m.config, "lang", lang.c_str()); + aspell_config_replace(m.config, "lang", code.c_str()); if (!variety.empty()) aspell_config_replace(m.config, "variety", variety.c_str()); // Set the encoding to utf-8. @@ -213,6 +266,7 @@ AspellSpeller * AspellChecker::Private::addSpeller(string const & lang, // Report run-together words as errors aspell_config_replace(m.config, "run-together", "false"); + m.accept_compound = lyxrc.spellchecker_accept_compound; m.e_speller = new_aspell_speller(m.config); if (aspell_error_number(m.e_speller) != 0) { // FIXME: We should indicate somehow that this language is not supported. @@ -221,30 +275,117 @@ AspellSpeller * AspellChecker::Private::addSpeller(string const & lang, delete_aspell_config(m.config); m.config = 0; m.e_speller = 0; + } else { + PersonalWordList * pd = new PersonalWordList(lang->lang()); + pd->load(); + personal_[lang->lang()] = pd; + initSessionDictionary(m, pd); } - - spellers_[spellerID(lang, variety)] = m; + + spellers_[lang->lang()] = m; return m.e_speller ? to_aspell_speller(m.e_speller) : 0; } -AspellSpeller * AspellChecker::Private::speller(string const & lang, - string const & variety) +AspellSpeller * AspellChecker::Private::speller(Language const * lang) { - Spellers::iterator it = spellers_.find(spellerID(lang, variety)); - if (it != spellers_.end()) - return to_aspell_speller(it->second.e_speller); - - return addSpeller(lang, variety); + Spellers::iterator it = spellers_.find(lang->lang()); + if (it != spellers_.end()) { + Speller aspell = it->second; + if (lyxrc.spellchecker_accept_compound != aspell.accept_compound) { + // spell checker setting changed... adjust run-together + aspell.accept_compound = lyxrc.spellchecker_accept_compound; + if (aspell.accept_compound) + // Consider run-together words as legal compounds + aspell_config_replace(aspell.config, "run-together", "true"); + else + // Report run-together words as errors + aspell_config_replace(aspell.config, "run-together", "false"); + AspellCanHaveError * e_speller = aspell.e_speller; + aspell.e_speller = new_aspell_speller(aspell.config); + delete_aspell_speller(to_aspell_speller(e_speller)); + spellers_[lang->lang()] = aspell; + } + return to_aspell_speller(aspell.e_speller); + } + + return addSpeller(lang); } -string const AspellChecker::Private::spellerID(string const & lang, - string const & variety) +string AspellChecker::Private::toAspellWord(docstring const & word) const { - if (variety.empty()) - return lang; - return lang + "-" + variety; + size_t mpos; + string word_str = to_utf8(word); + while ((mpos = word_str.find('-')) != word_str.npos) { + word_str.erase(mpos, 1); + } + return word_str; +} + + +SpellChecker::Result AspellChecker::Private::check( + AspellSpeller * m, WordLangTuple const & word) + const +{ + SpellChecker::Result result = WORD_OK; + docstring w1; + docstring rest = split(word.word(), w1, '-'); + for (; result == WORD_OK;) { + string const word_str = toAspellWord(w1); + int const word_ok = aspell_speller_check(m, word_str.c_str(), -1); + LASSERT(word_ok != -1, /**/); + result = (word_ok) ? WORD_OK : UNKNOWN_WORD; + if (rest.empty()) + break; + rest = split(rest,w1,'-'); + } + if (result == WORD_OK) + return result; + string const word_str = toAspellWord(word.word()); + int const word_ok = aspell_speller_check(m, word_str.c_str(), -1); + LASSERT(word_ok != -1, /**/); + return (word_ok) ? WORD_OK : UNKNOWN_WORD; +} + +void AspellChecker::Private::accept(Speller & speller, WordLangTuple const & word) +{ + speller.ignored_words_.push_back(word.word()); +} + + +/// personal word list interface +void AspellChecker::Private::remove(WordLangTuple const & word) +{ + PersonalWordList * pd = personal_[word.lang()->lang()]; + if (!pd) + return; + pd->remove(word.word()); + Spellers::iterator it = spellers_.find(word.lang()->lang()); + if (it != spellers_.end()) { + initSessionDictionary(it->second, pd); + } +} + + +void AspellChecker::Private::insert(WordLangTuple const & word) +{ + Spellers::iterator it = spellers_.find(word.lang()->lang()); + if (it != spellers_.end()) { + addToSession(it->second.e_speller, word.word()); + PersonalWordList * pd = personal_[word.lang()->lang()]; + if (!pd) + return; + pd->insert(word.word()); + } +} + +bool AspellChecker::Private::learned(WordLangTuple const & word) +{ + PersonalWordList * pd = personal_[word.lang()->lang()]; + if (!pd) + return false; + return pd->exists(word.word()); } @@ -262,42 +403,40 @@ AspellChecker::~AspellChecker() SpellChecker::Result AspellChecker::check(WordLangTuple const & word) { - AspellSpeller * m = - d->speller(word.lang()->code(), word.lang()->variety()); + AspellSpeller * m = d->speller(word.lang()); if (!m) - return OK; + return WORD_OK; if (word.word().empty()) // MSVC compiled Aspell doesn't like it. - return OK; + return WORD_OK; + + SpellChecker::Result rc = d->check(m, word); + return (rc == WORD_OK && d->learned(word)) ? LEARNED_WORD : rc; +} - string const word_str = to_utf8(word.word()); - int const word_ok = aspell_speller_check(m, word_str.c_str(), -1); - LASSERT(word_ok != -1, /**/); - return (word_ok) ? OK : UNKNOWN_WORD; +void AspellChecker::advanceChangeNumber() +{ + nextChangeNumber(); } void AspellChecker::insert(WordLangTuple const & word) { - Spellers::iterator it = d->spellers_.find( - d->spellerID(word.lang()->code(), word.lang()->variety())); - if (it != d->spellers_.end()) { - AspellSpeller * speller = to_aspell_speller(it->second.e_speller); - aspell_speller_add_to_personal(speller, to_utf8(word.word()).c_str(), -1); - } + d->insert(word); + advanceChangeNumber(); } void AspellChecker::accept(WordLangTuple const & word) { - Spellers::iterator it = d->spellers_.find( - d->spellerID(word.lang()->code(), word.lang()->variety())); + Spellers::iterator it = d->spellers_.find(word.lang()->lang()); if (it != d->spellers_.end()) { - AspellSpeller * speller = to_aspell_speller(it->second.e_speller); - aspell_speller_add_to_session(speller, to_utf8(word.word()).c_str(), -1); + d->addToSession(it->second.e_speller, word.word()); + d->accept(it->second, word); + advanceChangeNumber(); } } @@ -306,14 +445,14 @@ void AspellChecker::suggest(WordLangTuple const & wl, docstring_list & suggestions) { suggestions.clear(); - AspellSpeller * m = - d->speller(wl.lang()->code(), wl.lang()->variety()); + AspellSpeller * m = d->speller(wl.lang()); if (!m) return; + string const word = d->toAspellWord(wl.word()); AspellWordList const * sugs = - aspell_speller_suggest(m, to_utf8(wl.word()).c_str(), -1); + aspell_speller_suggest(m, word.c_str(), -1); LASSERT(sugs != 0, /**/); AspellStringEnumeration * els = aspell_word_list_elements(sugs); if (!els || aspell_word_list_empty(sugs)) @@ -329,6 +468,11 @@ void AspellChecker::suggest(WordLangTuple const & wl, delete_aspell_string_enumeration(els); } +void AspellChecker::remove(WordLangTuple const & word) +{ + d->remove(word); + advanceChangeNumber(); +} bool AspellChecker::hasDictionary(Language const * lang) const {