2 * \file AspellChecker.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Kevin Atkinson
9 * Full author contact details are available in file CREDITS.
14 #include "AspellChecker.h"
16 #include "WordLangTuple.h"
18 #include "support/lassert.h"
19 #include "support/debug.h"
33 AspellSpeller * speller;
34 AspellConfig * config;
37 typedef std::map<std::string, Speller> Spellers;
41 struct AspellChecker::Private
43 Private(): els(0), spell_error_object(0) {}
47 /// add a speller of the given language
48 void addSpeller(std::string const & lang);
54 AspellStringEnumeration * els;
56 AspellCanHaveError * spell_error_object;
60 AspellChecker::Private::~Private()
62 if (spell_error_object) {
63 delete_aspell_can_have_error(spell_error_object);
64 spell_error_object = 0;
68 delete_aspell_string_enumeration(els);
70 Spellers::iterator it = spellers_.begin();
71 Spellers::iterator end = spellers_.end();
73 for (; it != end; ++it) {
74 aspell_speller_save_all_word_lists(it->second.speller);
75 delete_aspell_speller(it->second.speller);
76 delete_aspell_config(it->second.config);
81 void AspellChecker::Private::addSpeller(string const & lang)
83 AspellConfig * config = new_aspell_config();
84 // FIXME The aspell documentation says to use "lang"
85 aspell_config_replace(config, "language-tag", lang.c_str());
86 // Set the encoding to utf-8.
87 // aspell does also understand "ucs-4", so we would not need a
88 // conversion in theory, but if this is used it expects all
89 // char const * arguments to be a cast from uint const *, and it
90 // seems that this uint is not compatible with our char_type on some
91 // platforms (cygwin, OS X). Therefore we use utf-8, that does
93 aspell_config_replace(config, "encoding", "utf-8");
94 if (lyxrc.spellchecker_accept_compound)
95 // Consider run-together words as legal compounds
96 aspell_config_replace(config, "run-together", "true");
98 // Report run-together words as errors
99 aspell_config_replace(config, "run-together", "false");
100 AspellCanHaveError * err = new_aspell_speller(config);
101 if (spell_error_object)
102 delete_aspell_can_have_error(spell_error_object);
103 spell_error_object = 0;
105 if (aspell_error_number(err) == 0) {
107 m.speller = to_aspell_speller(err);
111 spell_error_object = err;
116 AspellChecker::AspellChecker(): d(new Private)
121 AspellChecker::~AspellChecker()
127 SpellChecker::Result AspellChecker::check(WordLangTuple const & word)
129 Result res = UNKNOWN_WORD;
131 Spellers::iterator it = d->spellers_.find(word.lang_code());
132 if (it == d->spellers_.end()) {
133 d->addSpeller(word.lang_code());
134 it = d->spellers_.find(word.lang_code());
136 if (it == d->spellers_.end())
140 AspellSpeller * m = it->second.speller;
142 if (word.word().empty())
143 // MSVC compiled Aspell doesn't like it.
146 int const word_ok = aspell_speller_check(m, to_utf8(word.word()).c_str(), -1);
147 LASSERT(word_ok != -1, /**/);
152 AspellWordList const * sugs =
153 aspell_speller_suggest(m, to_utf8(word.word()).c_str(), -1);
154 LASSERT(sugs != 0, /**/);
155 d->els = aspell_word_list_elements(sugs);
156 if (aspell_word_list_empty(sugs))
159 res = SUGGESTED_WORDS;
165 void AspellChecker::insert(WordLangTuple const & word)
167 Spellers::iterator it = d->spellers_.find(word.lang_code());
168 if (it != d->spellers_.end())
169 aspell_speller_add_to_personal(it->second.speller, to_utf8(word.word()).c_str(), -1);
173 void AspellChecker::accept(WordLangTuple const & word)
175 Spellers::iterator it = d->spellers_.find(word.lang_code());
176 if (it != d->spellers_.end())
177 aspell_speller_add_to_session(it->second.speller, to_utf8(word.word()).c_str(), -1);
181 docstring const AspellChecker::nextMiss()
183 char const * str = 0;
186 str = aspell_string_enumeration_next(d->els);
188 return (str ? from_utf8(str) : docstring());
192 docstring const AspellChecker::error()
194 char const * err = 0;
196 if (d->spell_error_object && aspell_error_number(d->spell_error_object) != 0)
197 err = aspell_error_message(d->spell_error_object);
199 // FIXME UNICODE: err is not in UTF8, but probably the locale encoding
200 return (err ? from_utf8(err) : docstring());