]> git.lyx.org Git - features.git/commitdiff
Allow removing words from the personal dictionary, that weren't previously added.
authorIsaac <IsaacOscar@live.com.au>
Tue, 26 Apr 2022 07:14:34 +0000 (19:14 +1200)
committerStephan Witt <switt@lyx.org>
Fri, 25 Nov 2022 08:07:40 +0000 (09:07 +0100)
src/AspellChecker.cpp
src/HunspellChecker.cpp
src/PersonalWordList.cpp
src/PersonalWordList.h
src/SpellChecker.h
src/frontends/qt/Menus.cpp

index a5c54d8c74b6ae727e10526d29ef15f3d2cfddea..8789b6c7891665fd3a10b63bf662ec97b7af8876 100644 (file)
@@ -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);
 }
 
 
index 01ec19498b70e28075db5e5b8a9c6bb8c4c77e49..fa9ec9c2035d10568a905b50f9f740eb9a83632a 100644 (file)
@@ -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?
index ca61d8a17106f5577c9d66de2308eb52b4a7355e..5a34a28e832dabaddab0e1067565ef83fe9edce5 100644 (file)
@@ -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();
index 0194bbcbe69e0eb206903c06b0b3d61b932d1731..a7217f2b5b899f4385992b8171484501befde8b2 100644 (file)
 
 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
index 58e5de6bd2acc66fefb723abcfd510da27d1cfb9..e3c65707b6efb0f09e8e5bb35c8c9563d45bfd28 100644 (file)
@@ -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
index 6b0c12b82181f9275fd716a4e823db902da432fa..679efe461ced015ea4b3fdbf2702b3f65078b6f5 100644 (file)
@@ -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;
        }
 }