From: Isaac Date: Tue, 26 Apr 2022 07:14:34 +0000 (+1200) Subject: Allow removing words from the personal dictionary, that weren't previously added. X-Git-Url: https://git.lyx.org/gitweb/?a=commitdiff_plain;h=9350076b3aa268919a4bf6e05c4a10caebabb913;p=features.git Allow removing words from the personal dictionary, that weren't previously added. --- diff --git a/src/AspellChecker.cpp b/src/AspellChecker.cpp index a5c54d8c74..8789b6c789 100644 --- a/src/AspellChecker.cpp +++ b/src/AspellChecker.cpp @@ -75,13 +75,12 @@ struct AspellChecker::Private string toAspellWord(docstring const & word) const; SpellChecker::Result check(AspellSpeller * m, - WordLangTuple const & word) const; + WordLangTuple const & word); 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); @@ -238,8 +237,8 @@ void AspellChecker::Private::initSessionDictionary( { 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(); + docstring_list::const_iterator it = pd->includes_begin(); + docstring_list::const_iterator et = pd->includes_end(); for (; it != et; ++it) { addToSession(speller.e_speller, *it); } @@ -352,8 +351,8 @@ string AspellChecker::Private::toAspellWord(docstring const & word) const SpellChecker::Result AspellChecker::Private::check( AspellSpeller * m, WordLangTuple const & word) - const { + PersonalWordList * pd = personal_[word.lang()->lang()]; SpellChecker::Result result = WORD_OK; docstring w1; LYXERR(Debug::GUI, "spellCheck: \"" << @@ -364,6 +363,9 @@ SpellChecker::Result AspellChecker::Private::check( 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 (result == WORD_OK && pd && pd->excluded(w1)) { + result = UNKNOWN_WORD; + } if (rest.empty()) break; rest = split(rest, w1, '-'); @@ -373,7 +375,11 @@ SpellChecker::Result AspellChecker::Private::check( 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; + result = (word_ok) ? WORD_OK : UNKNOWN_WORD; + if (result == WORD_OK && pd && pd->excluded(word.word())) { + result = UNKNOWN_WORD; + } + return result; } void AspellChecker::Private::accept(Speller & speller, WordLangTuple const & word) @@ -408,14 +414,6 @@ void AspellChecker::Private::insert(WordLangTuple const & word) } } -bool AspellChecker::Private::learned(WordLangTuple const & word) -{ - PersonalWordList * pd = personal_[word.lang()->lang()]; - if (!pd) - return false; - return pd->exists(word.word()); -} - AspellChecker::AspellChecker() : d(new Private) @@ -447,8 +445,7 @@ SpellChecker::Result AspellChecker::check(WordLangTuple const & word, if (it->word() == word.word()) return DOCUMENT_LEARNED_WORD; } - SpellChecker::Result rc = d->check(m, word); - return (rc == WORD_OK && d->learned(word)) ? LEARNED_WORD : rc; + return d->check(m, word); } diff --git a/src/HunspellChecker.cpp b/src/HunspellChecker.cpp index 01ec19498b..fa9ec9c203 100644 --- a/src/HunspellChecker.cpp +++ b/src/HunspellChecker.cpp @@ -72,7 +72,6 @@ struct HunspellChecker::Private /// personal word list interface void remove(WordLangTuple const & wl); void insert(WordLangTuple const & wl); - bool learned(WordLangTuple const & wl); /// the spellers Spellers spellers_; /// @@ -256,12 +255,18 @@ Hunspell * HunspellChecker::Private::addSpeller(Language const * lang) 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(); + docstring_list::const_iterator it = pd->includes_begin(); + docstring_list::const_iterator et = pd->includes_end(); for (; it != et; ++it) { string const word_to_add = to_iconv_encoding(*it, encoding); h->add(word_to_add.c_str()); } + it = pd->excludes_begin(); + et = pd->excludes_end(); + for (; it != et; ++it) { + string const word_to_add = to_iconv_encoding(*it, encoding); + h->remove(word_to_add.c_str()); + } } return h; } @@ -322,15 +327,6 @@ void HunspellChecker::Private::insert(WordLangTuple const & wl) } -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) {} @@ -371,7 +367,7 @@ SpellChecker::Result HunspellChecker::check(WordLangTuple const & wl, #else if (h->spell(word_to_check.c_str(), &info)) #endif - return d->learned(wl) ? LEARNED_WORD : WORD_OK; + return WORD_OK; if (info & SPELL_COMPOUND) { // FIXME: What to do with that? diff --git a/src/PersonalWordList.cpp b/src/PersonalWordList.cpp index ca61d8a171..5a34a28e83 100644 --- a/src/PersonalWordList.cpp +++ b/src/PersonalWordList.cpp @@ -26,26 +26,27 @@ using namespace lyx::support; namespace lyx { -FileName PersonalWordList::dictfile() const +FileName PersonalWordListPart::dictfile() const { - string fname = "pwl_" + lang_ + ".dict"; + string fext = is_includes_ ? ".dict" : ".excl"; + string fname = "pwl_" + lang_ + fext; return FileName(addName(package().user_support().absFileName(),fname)); } -docstring_list::const_iterator PersonalWordList::begin() const +docstring_list::const_iterator PersonalWordListPart::begin() const { return words_.begin(); } -docstring_list::const_iterator PersonalWordList::end() const +docstring_list::const_iterator PersonalWordListPart::end() const { return words_.end(); } -void PersonalWordList::load() +void PersonalWordListPart::load() { FileName fn = dictfile(); LYXERR(Debug::FILES, "load personal dictionary from: " << fn); @@ -72,7 +73,7 @@ void PersonalWordList::load() } -void PersonalWordList::save() +void PersonalWordListPart::save() { if (!isDirty()) return; @@ -90,13 +91,13 @@ void PersonalWordList::save() } -bool PersonalWordList::equalwords(docstring const & w1, docstring const & w2) const +bool PersonalWordListPart::equalwords(docstring const & w1, docstring const & w2) const { return w1 == w2; } -bool PersonalWordList::exists(docstring const & word) const +bool PersonalWordListPart::exists(docstring const & word) const { docstring_list::const_iterator it = words_.begin(); docstring_list::const_iterator et = words_.end(); @@ -108,7 +109,7 @@ bool PersonalWordList::exists(docstring const & word) const } -void PersonalWordList::insert(docstring const & word) +void PersonalWordListPart::insert(docstring const & word) { if (exists(word)) return; @@ -117,7 +118,7 @@ void PersonalWordList::insert(docstring const & word) } -void PersonalWordList::remove(docstring const & word) +void PersonalWordListPart::remove(docstring const & word) { docstring_list::iterator it = words_.begin(); docstring_list::const_iterator et = words_.end(); diff --git a/src/PersonalWordList.h b/src/PersonalWordList.h index 0194bbcbe6..a7217f2b5b 100644 --- a/src/PersonalWordList.h +++ b/src/PersonalWordList.h @@ -20,11 +20,10 @@ namespace lyx { -/// A PersonalWordList holds a word list with persistent state -class PersonalWordList { -public: - /// the word list has an associated language - PersonalWordList(std::string const & lang) : lang_(lang), dirty_(false) {} +/// A PersonalWordListPart holds a part of the word list with persistent state +struct PersonalWordListPart { + /// the word list has an associated language, and a flag indicating whether it is an includes or excludes list + PersonalWordListPart(std::string lang, bool is_includes) : lang_(lang), is_includes_(is_includes), dirty_(false) {} /// the location of the file to hold to word list lyx::support::FileName dictfile() const; /// (re)load the word list from file @@ -49,15 +48,51 @@ private: /// std::string lang_; /// + bool is_includes_; + /// bool dirty_; /// bool equalwords(docstring const & w1, docstring const & w2) const; /// - std::string header() const { return "# personal word list"; } + std::string header() const { return is_includes_ ? "# personal word list" : "# personal world list (exclusions)"; } /// void dirty(bool flag) { dirty_ = flag; } }; +/// A PersonalWordState holds a list of words to include (i.e. marked as spelt correctly), and a list of words to exclude (i.e. marked as spelled incorrectly) +class PersonalWordList { +public: + /// the word list has an associated language + PersonalWordList(std::string lang) : includes_(lang, true), excludes_(lang, false) {} + + /// first item in includes word list + docstring_list::const_iterator includes_begin() const { return includes_.begin(); } + /// end of includes word list + docstring_list::const_iterator includes_end() const { return includes_.end(); } + /// first item in excludes word list + docstring_list::const_iterator excludes_begin() const { return excludes_.begin(); } + /// end of excludes word list + docstring_list::const_iterator excludes_end() const { return excludes_.end(); } + /// (re)load both word lists from file + void load() { includes_.load(); excludes_.load(); } + /// save both word lists to file + void save() { includes_.save(); excludes_.save(); } + /// is the given word excluded? (i.e. we previously called remove) + bool excluded(docstring const & word) const { return excludes_.exists(word); } + /// is the given word included? (i.e. we previously called insert) + bool included(docstring const & word) const { return includes_.exists(word); } + /// insert a given word to the set of valid words + void insert(docstring const & word) { excludes_.remove(word); includes_.insert(word); } + /// remove given word from the set of valid words + void remove(docstring const & word) { includes_.remove(word); excludes_.insert(word); } + +private: + /// The list of words to include + PersonalWordListPart includes_; + /// The list of words to exclude + PersonalWordListPart excludes_; +}; + } // namespace lyx #endif // PERSONAL_WORD_LIST_H diff --git a/src/SpellChecker.h b/src/SpellChecker.h index 58e5de6bd2..e3c65707b6 100644 --- a/src/SpellChecker.h +++ b/src/SpellChecker.h @@ -34,16 +34,8 @@ public: enum Result { /// word is correct WORD_OK = 1, - /// root of given word was found - ROOT_FOUND, - /// word found through compound formation - COMPOUND_WORD, /// word not found UNKNOWN_WORD, - /// number of other ignored "word" - IGNORED_WORD, - /// number of personal dictionary "word" - LEARNED_WORD, /// number of document dictionary "word" DOCUMENT_LEARNED_WORD, /// missing dictionary for language @@ -57,9 +49,7 @@ public: /// does the spell check failed static bool misspelled(Result res) { return res != WORD_OK - && res != IGNORED_WORD && res != NO_DICTIONARY - && res != LEARNED_WORD && res != DOCUMENT_LEARNED_WORD; } /// check the given word of the given lang code and return the result diff --git a/src/frontends/qt/Menus.cpp b/src/frontends/qt/Menus.cpp index 6b0c12b821..679efe461c 100644 --- a/src/frontends/qt/Menus.cpp +++ b/src/frontends/qt/Menus.cpp @@ -880,8 +880,8 @@ void MenuDefinition::expandSpellingSuggestions(BufferView const * bv) } } break; - case SpellChecker::LEARNED_WORD: { - LYXERR(Debug::GUI, "Learned Word."); + case SpellChecker::WORD_OK: { + LYXERR(Debug::GUI, "Valid Word."); docstring const arg = wl.word() + " " + from_ascii(wl.lang()->lang()); add(MenuItem(MenuItem::Command, qt_("Remove from personal dictionary|r"), FuncRequest(LFUN_SPELLING_REMOVE, arg))); @@ -897,11 +897,6 @@ void MenuDefinition::expandSpellingSuggestions(BufferView const * bv) case SpellChecker::NO_DICTIONARY: LYXERR(Debug::GUI, "No dictionary for language " + from_ascii(wl.lang()->lang())); // FALLTHROUGH - case SpellChecker::WORD_OK: - case SpellChecker::COMPOUND_WORD: - case SpellChecker::ROOT_FOUND: - case SpellChecker::IGNORED_WORD: - break; } }