X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FAspellChecker.cpp;h=8e41765e3d70bb302adc14504a4582935f17466c;hb=c0a1893008bd13650d470afff16f56720b65c87c;hp=642f804977c77d37ab6298a206701a855451fe73;hpb=a2287675d2362eea8ae6f853a1666db5f78a9d89;p=lyx.git diff --git a/src/AspellChecker.cpp b/src/AspellChecker.cpp index 642f804977..8e41765e3d 100644 --- a/src/AspellChecker.cpp +++ b/src/AspellChecker.cpp @@ -19,12 +19,13 @@ #include "support/lassert.h" #include "support/debug.h" +#include "support/lstrings.h" #include "support/docstring_list.h" #include "support/filetools.h" #include "support/Package.h" #include "support/FileName.h" -#include "support/Path.h" +#include "support/PathChanger.h" #include @@ -41,17 +42,19 @@ namespace { struct Speller { AspellConfig * config; AspellCanHaveError * e_speller; + bool accept_compound; docstring_list ignored_words_; }; typedef std::map Spellers; typedef map LangPersonalWordList; -} // anon namespace +} // namespace struct AspellChecker::Private { - Private() {} + Private() + {} ~Private(); @@ -61,20 +64,21 @@ struct AspellChecker::Private /// AspellSpeller * speller(Language const * lang); - /// create a unique ID from lang code and variety - string const spellerID(Language const * lang); - bool isValidDictionary(AspellConfig * config, string const & lang, string const & variety); + int numDictionaries() const; bool checkAspellData(AspellConfig * config, string const & basepath, string const & datapath, string const & dictpath, 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, - string const & word) const; + 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); @@ -88,28 +92,38 @@ struct AspellChecker::Private /// 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"; } + const string dataDirectory(void) + { + return "data"; + } /// os package directory constants /// macports on Mac OS X or /// aspell rpms on Linux - const string osPackageBase(void) { + const string osPackageBase(void) + { #ifdef USE_MACOSX_PACKAGING return "/opt/local"; #else return "/usr"; #endif } - const string osPackageDictDirectory(void) { + const string osPackageDictDirectory(void) + { #ifdef USE_MACOSX_PACKAGING return "/share/aspell"; #else return "/lib/aspell-0.60"; #endif } - const string osPackageDataDirectory(void) { return "/lib/aspell-0.60"; } - + const string osPackageDataDirectory(void) + { + return "/lib/aspell-0.60"; + } }; @@ -129,9 +143,9 @@ AspellChecker::Private::~Private() LangPersonalWordList::const_iterator pdit = personal_.begin(); LangPersonalWordList::const_iterator pdet = personal_.end(); - + for (; pdit != pdet; ++pdit) { - if ( 0 == pdit->second) + if (0 == pdit->second) continue; PersonalWordList * pd = pdit->second; pd->save(); @@ -171,7 +185,7 @@ bool AspellChecker::Private::checkAspellData(AspellConfig * config, string const & lang, string const & variety) { FileName base(basepath); - bool have_dict = base.isDirectory() ; + bool have_dict = base.isDirectory(); if (have_dict) { FileName data(addPath(base.absFileName(), datapath)); @@ -185,7 +199,7 @@ bool AspellChecker::Private::checkAspellData(AspellConfig * config, have_dict = isValidDictionary(config, lang, variety); } } - return have_dict ; + return have_dict; } @@ -193,8 +207,8 @@ AspellConfig * AspellChecker::Private::getConfig(string const & lang, string con { AspellConfig * config = new_aspell_config(); bool have_dict = false; - string const sysdir = lyx::support::package().system_support().absFileName() ; - string const userdir = lyx::support::package().user_support().absFileName() ; + string const sysdir = lyx::support::package().system_support().absFileName(); + string const userdir = lyx::support::package().user_support().absFileName(); LYXERR(Debug::FILES, "aspell user dir: " << userdir); have_dict = checkAspellData(config, userdir, dataDirectory(), dictDirectory(), lang, variety); @@ -204,9 +218,17 @@ AspellConfig * AspellChecker::Private::getConfig(string const & lang, string con } if (!have_dict) { // check for package data of OS installation - have_dict = checkAspellData(config, osPackageBase(), osPackageDataDirectory(), osPackageDictDirectory(), lang, variety); + checkAspellData(config, osPackageBase(), osPackageDataDirectory(), osPackageDictDirectory(), lang, variety); } - return config ; + return config; +} + + +void AspellChecker::Private::addToSession(AspellCanHaveError * speller, docstring const & word) +{ + 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)); } @@ -219,14 +241,12 @@ void AspellChecker::Private::initSessionDictionary( docstring_list::const_iterator it = pd->begin(); docstring_list::const_iterator et = pd->end(); for (; it != et; ++it) { - string const word_to_add = to_utf8(*it); - aspell_speller_add_to_session(aspell, word_to_add.c_str(), -1); + addToSession(speller.e_speller, *it); } it = speller.ignored_words_.begin(); et = speller.ignored_words_.end(); for (; it != et; ++it) { - string const word_to_add = to_utf8(*it); - aspell_speller_add_to_session(aspell, word_to_add.c_str(), -1); + addToSession(speller.e_speller, *it); } } @@ -258,6 +278,7 @@ AspellSpeller * AspellChecker::Private::addSpeller(Language 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. @@ -272,34 +293,86 @@ AspellSpeller * AspellChecker::Private::addSpeller(Language const * lang) personal_[lang->lang()] = pd; initSessionDictionary(m, pd); } - - spellers_[spellerID(lang)] = m; + + spellers_[lang->lang()] = m; return m.e_speller ? to_aspell_speller(m.e_speller) : 0; } AspellSpeller * AspellChecker::Private::speller(Language const * lang) { - Spellers::iterator it = spellers_.find(spellerID(lang)); - if (it != spellers_.end()) - return to_aspell_speller(it->second.e_speller); - + 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(Language const * lang) +int AspellChecker::Private::numDictionaries() const { - return lang->code() + "-" + lang->variety(); + int result = 0; + Spellers::const_iterator it = spellers_.begin(); + Spellers::const_iterator et = spellers_.end(); + + for (; it != et; ++it) { + Speller aspell = it->second; + result += aspell.e_speller != 0; + } + return result; +} + + +string AspellChecker::Private::toAspellWord(docstring const & word) const +{ + 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, string const & word) + AspellSpeller * m, WordLangTuple const & word) const { - int const word_ok = aspell_speller_check(m, word.c_str(), -1); - LASSERT(word_ok != -1, /**/); + SpellChecker::Result result = WORD_OK; + docstring w1; + LYXERR(Debug::GUI, "spellCheck: \"" << + word.word() << "\", lang = " << word.lang()->lang()) ; + 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, return UNKNOWN_WORD); + 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 UNKNOWN_WORD); return (word_ok) ? WORD_OK : UNKNOWN_WORD; } @@ -316,19 +389,18 @@ void AspellChecker::Private::remove(WordLangTuple const & word) if (!pd) return; pd->remove(word.word()); - Spellers::iterator it = spellers_.find(spellerID(word.lang())); + 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(spellerID(word.lang())); + Spellers::iterator it = spellers_.find(word.lang()->lang()); if (it != spellers_.end()) { - AspellSpeller * speller = to_aspell_speller(it->second.e_speller); - aspell_speller_add_to_session(speller, to_utf8(word.word()).c_str(), -1); + addToSession(it->second.e_speller, word.word()); PersonalWordList * pd = personal_[word.lang()->lang()]; if (!pd) return; @@ -345,9 +417,9 @@ bool AspellChecker::Private::learned(WordLangTuple const & word) } -AspellChecker::AspellChecker(): d(new Private) -{ -} +AspellChecker::AspellChecker() + : d(new Private) +{} AspellChecker::~AspellChecker() @@ -358,18 +430,16 @@ AspellChecker::~AspellChecker() SpellChecker::Result AspellChecker::check(WordLangTuple const & word) { - AspellSpeller * m = d->speller(word.lang()); if (!m) - return WORD_OK; + return NO_DICTIONARY; if (word.word().empty()) // MSVC compiled Aspell doesn't like it. return WORD_OK; - string const word_str = to_utf8(word.word()); - SpellChecker::Result rc = d->check(m, word_str); + SpellChecker::Result rc = d->check(m, word); return (rc == WORD_OK && d->learned(word)) ? LEARNED_WORD : rc; } @@ -389,10 +459,9 @@ void AspellChecker::insert(WordLangTuple const & word) void AspellChecker::accept(WordLangTuple const & word) { - Spellers::iterator it = d->spellers_.find(d->spellerID(word.lang())); + 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(); } @@ -408,9 +477,10 @@ void AspellChecker::suggest(WordLangTuple const & wl, if (!m) return; + string const word = d->toAspellWord(wl.word()); AspellWordList const * sugs = - aspell_speller_suggest(m, to_utf8(wl.word()).c_str(), -1); - LASSERT(sugs != 0, /**/); + aspell_speller_suggest(m, word.c_str(), -1); + LASSERT(sugs != 0, return); AspellStringEnumeration * els = aspell_word_list_elements(sugs); if (!els || aspell_word_list_empty(sugs)) return; @@ -425,12 +495,14 @@ 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 { bool have = false; @@ -451,6 +523,12 @@ bool AspellChecker::hasDictionary(Language const * lang) const } +int AspellChecker::numDictionaries() const +{ + return d->numDictionaries(); +} + + docstring const AspellChecker::error() { Spellers::iterator it = d->spellers_.begin();