SpellChecker work:

* Move suggestion searching out of check() and onto suggest().
* Cleanup a bit AspellChecker
* Begin Hunspell support (not tested and does not link yet)


git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@30824 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Abdelrazak Younes 2009-08-02 09:17:32 +00:00
parent 17e6ab45b0
commit 1f53de6592
6 changed files with 148 additions and 70 deletions

View File

@ -17,6 +17,7 @@
#include "support/lassert.h" #include "support/lassert.h"
#include "support/debug.h" #include "support/debug.h"
#include "support/docstring_list.h"
#include <aspell.h> #include <aspell.h>
@ -40,18 +41,19 @@ typedef std::map<std::string, Speller> Spellers;
struct AspellChecker::Private struct AspellChecker::Private
{ {
Private(): els(0), spell_error_object(0) {} Private(): spell_error_object(0) {}
~Private(); ~Private();
/// add a speller of the given language /// add a speller of the given language
void addSpeller(std::string const & lang); AspellSpeller * addSpeller(string const & lang);
///
AspellSpeller * speller(string const & lang);
/// the spellers /// the spellers
Spellers spellers_; Spellers spellers_;
/// FIXME
AspellStringEnumeration * els;
/// FIXME /// FIXME
AspellCanHaveError * spell_error_object; AspellCanHaveError * spell_error_object;
}; };
@ -64,9 +66,6 @@ AspellChecker::Private::~Private()
spell_error_object = 0; spell_error_object = 0;
} }
if (els)
delete_aspell_string_enumeration(els);
Spellers::iterator it = spellers_.begin(); Spellers::iterator it = spellers_.begin();
Spellers::iterator end = spellers_.end(); Spellers::iterator end = spellers_.end();
@ -78,7 +77,7 @@ AspellChecker::Private::~Private()
} }
void AspellChecker::Private::addSpeller(string const & lang) AspellSpeller * AspellChecker::Private::addSpeller(string const & lang)
{ {
AspellConfig * config = new_aspell_config(); AspellConfig * config = new_aspell_config();
// FIXME The aspell documentation says to use "lang" // FIXME The aspell documentation says to use "lang"
@ -102,14 +101,27 @@ void AspellChecker::Private::addSpeller(string const & lang)
delete_aspell_can_have_error(spell_error_object); delete_aspell_can_have_error(spell_error_object);
spell_error_object = 0; spell_error_object = 0;
if (aspell_error_number(err) == 0) { if (aspell_error_number(err) != 0) {
Speller m; // FIXME: We should we indicate somehow that this language is not
m.speller = to_aspell_speller(err); // supported.
m.config = config;
spellers_[lang] = m;
} else {
spell_error_object = err; spell_error_object = err;
return 0;
} }
Speller m;
m.speller = to_aspell_speller(err);
m.config = config;
spellers_[lang] = m;
return m.speller;
}
AspellSpeller * AspellChecker::Private::speller(string const & lang)
{
Spellers::iterator it = spellers_.find(lang);
if (it != spellers_.end())
return it->second.speller;
return addSpeller(lang);
} }
@ -126,18 +138,9 @@ AspellChecker::~AspellChecker()
SpellChecker::Result AspellChecker::check(WordLangTuple const & word) SpellChecker::Result AspellChecker::check(WordLangTuple const & word)
{ {
Result res = UNKNOWN_WORD; AspellSpeller * m = d->speller(word.lang_code());
if (!m)
Spellers::iterator it = d->spellers_.find(word.lang_code()); return OK;
if (it == d->spellers_.end()) {
d->addSpeller(word.lang_code());
it = d->spellers_.find(word.lang_code());
// FIXME
if (it == d->spellers_.end())
return res;
}
AspellSpeller * m = it->second.speller;
if (word.word().empty()) if (word.word().empty())
// MSVC compiled Aspell doesn't like it. // MSVC compiled Aspell doesn't like it.
@ -146,19 +149,7 @@ SpellChecker::Result AspellChecker::check(WordLangTuple const & word)
int const word_ok = aspell_speller_check(m, to_utf8(word.word()).c_str(), -1); int const word_ok = aspell_speller_check(m, to_utf8(word.word()).c_str(), -1);
LASSERT(word_ok != -1, /**/); LASSERT(word_ok != -1, /**/);
if (word_ok) return (word_ok) ? OK : UNKNOWN_WORD;
return OK;
AspellWordList const * sugs =
aspell_speller_suggest(m, to_utf8(word.word()).c_str(), -1);
LASSERT(sugs != 0, /**/);
d->els = aspell_word_list_elements(sugs);
if (aspell_word_list_empty(sugs))
res = UNKNOWN_WORD;
else
res = SUGGESTED_WORDS;
return res;
} }
@ -178,14 +169,29 @@ void AspellChecker::accept(WordLangTuple const & word)
} }
docstring const AspellChecker::nextMiss() void AspellChecker::suggest(WordLangTuple const & wl,
docstring_list & suggestions)
{ {
char const * str = 0; suggestions.clear();
AspellSpeller * m = d->speller(wl.lang_code());
if (!m)
return;
if (d->els) AspellWordList const * sugs =
str = aspell_string_enumeration_next(d->els); aspell_speller_suggest(m, to_utf8(wl.word()).c_str(), -1);
LASSERT(sugs != 0, /**/);
AspellStringEnumeration * els = aspell_word_list_elements(sugs);
if (!els || aspell_word_list_empty(sugs))
return;
return (str ? from_utf8(str) : docstring()); for (;;) {
char const * str = aspell_string_enumeration_next(els);
if (!str)
break;
suggestions.push_back(from_utf8(str));
}
delete_aspell_string_enumeration(els);
} }

View File

@ -23,20 +23,14 @@ public:
AspellChecker(); AspellChecker();
~AspellChecker(); ~AspellChecker();
/// check the given word and return the result /// SpellChecker inherited methods.
///@{
enum Result check(WordLangTuple const &); enum Result check(WordLangTuple const &);
void suggest(WordLangTuple const &, docstring_list &);
/// insert the given word into the personal dictionary
void insert(WordLangTuple const &); void insert(WordLangTuple const &);
/// accept the given word temporarily
void accept(WordLangTuple const &); void accept(WordLangTuple const &);
/// return the next near miss after a SUGGESTED_WORDS result
docstring const nextMiss();
/// give an error message on messy exit
docstring const error(); docstring const error();
///@}
private: private:
struct Private; struct Private;

View File

@ -17,12 +17,17 @@
#include "support/lassert.h" #include "support/lassert.h"
#include "support/debug.h" #include "support/debug.h"
#include "support/docstring_list.h"
#include <hunspell/hunspell.hxx> #include <hunspell/hunspell.hxx>
#include <map> #include <map>
#include <string> #include <string>
// FIXME (Abdel): I still got linking problems but if anybody wants
// to try, defines this to 1.
#define TRY_HUNSPELL 0
using namespace std; using namespace std;
namespace lyx { namespace lyx {
@ -35,11 +40,51 @@ typedef map<std::string, Hunspell *> Spellers;
struct HunspellChecker::Private struct HunspellChecker::Private
{ {
Private() {}
~Private();
Hunspell * addSpeller(string const & lang);
Hunspell * speller(string const & lang);
/// the spellers /// the spellers
Spellers spellers_; Spellers spellers_;
}; };
HunspellChecker::Private::~Private()
{
#if TRY_HUNSPELL
Spellers::iterator it = spellers_.begin();
Spellers::iterator end = spellers_.end();
for (; it != end; ++it) {
delete it->second;
}
#endif
}
Hunspell * HunspellChecker::Private::addSpeller(string const & lang)
{
// FIXME: not implemented!
// FIXME: We should we indicate somehow that this language is not
// supported.
return 0;
}
Hunspell * HunspellChecker::Private::speller(string const & lang)
{
Spellers::iterator it = spellers_.find(lang);
if (it != spellers_.end())
return it->second;
return addSpeller(lang);
}
HunspellChecker::HunspellChecker(): d(new Private) HunspellChecker::HunspellChecker(): d(new Private)
{ {
} }
@ -51,25 +96,56 @@ HunspellChecker::~HunspellChecker()
} }
SpellChecker::Result HunspellChecker::check(WordLangTuple const & word) SpellChecker::Result HunspellChecker::check(WordLangTuple const & wl)
{ {
string const word_to_check = to_utf8(wl.word());
#if TRY_HUNSPELL
Hunspell * h = d->speller(wl.lang_code());
int info;
if (h->spell(word_to_check.c_str(), &info))
return OK;
// FIXME: What to do with that?
switch (info) {
case SPELL_COMPOUND:
case SPELL_FORBIDDEN:
default:
return UNKNOWN_WORD;
}
return UNKNOWN_WORD;
#endif
return OK; return OK;
} }
void HunspellChecker::insert(WordLangTuple const & word) void HunspellChecker::insert(WordLangTuple const & wl)
{ {
string const word_to_check = to_utf8(wl.word());
#if TRY_HUNSPELL
Hunspell * h = d->speller(wl.lang_code());
h->add(word_to_check.c_str());
#endif
} }
void HunspellChecker::accept(WordLangTuple const & word) void HunspellChecker::accept(WordLangTuple const & word)
{ {
// FIXME: not implemented!
} }
docstring const HunspellChecker::nextMiss() void HunspellChecker::suggest(WordLangTuple const & wl,
docstring_list & suggestions)
{ {
return docstring(); suggestions.clear();
string const word_to_check = to_utf8(wl.word());
#if TRY_HUNSPELL
Hunspell * h = d->speller(wl.lang_code());
char *** suggestion_list = 0;
int const suggestion_number = h->suggest(suggestion_list, word_to_check.c_str());
if (suggestion_number == 0)
return;
h->free_list(suggestion_list, suggestion_number);
#endif
} }

View File

@ -23,11 +23,14 @@ public:
HunspellChecker(); HunspellChecker();
~HunspellChecker(); ~HunspellChecker();
SpellChecker::Result check(WordLangTuple const &); /// SpellChecker inherited methods.
///@{
enum Result check(WordLangTuple const &);
void suggest(WordLangTuple const &, docstring_list &);
void insert(WordLangTuple const &); void insert(WordLangTuple const &);
void accept(WordLangTuple const &); void accept(WordLangTuple const &);
docstring const nextMiss();
docstring const error(); docstring const error();
///@}
private: private:
struct Private; struct Private;

View File

@ -3088,10 +3088,11 @@ bool Paragraph::spellCheck(pos_type & from, pos_type & to, WordLangTuple & wl,
if (lyxrc.spellcheck_continuously) if (lyxrc.spellcheck_continuously)
d->fontlist_.setMisspelled(from, to, misspelled); d->fontlist_.setMisspelled(from, to, misspelled);
if (misspelled) { if (misspelled)
while (!(word = speller->nextMiss()).empty()) speller->suggest(wl, suggestions);
suggestions.push_back(word); else
} suggestions.clear();
return misspelled; return misspelled;
} }

View File

@ -20,6 +20,7 @@ namespace lyx {
class BufferParams; class BufferParams;
class WordLangTuple; class WordLangTuple;
class docstring_list;
/** /**
* Pure virtual base class of all spellchecker implementations. * Pure virtual base class of all spellchecker implementations.
@ -37,8 +38,6 @@ public:
COMPOUND_WORD, COMPOUND_WORD,
/// word not found /// word not found
UNKNOWN_WORD, UNKNOWN_WORD,
/// not found, with suggestions
SUGGESTED_WORDS,
/// number of other ignored "word" /// number of other ignored "word"
IGNORED_WORD IGNORED_WORD
}; };
@ -47,7 +46,9 @@ public:
/// check the given word of the given lang code and return the result /// check the given word of the given lang code and return the result
virtual enum Result check(WordLangTuple const &) = 0; virtual enum Result check(WordLangTuple const &) = 0;
/// Gives suggestions.
virtual void suggest(WordLangTuple const &, docstring_list & suggestions) = 0;
/// insert the given word into the personal dictionary /// insert the given word into the personal dictionary
virtual void insert(WordLangTuple const &) = 0; virtual void insert(WordLangTuple const &) = 0;
@ -55,9 +56,6 @@ public:
/// accept the given word temporarily /// accept the given word temporarily
virtual void accept(WordLangTuple const &) = 0; virtual void accept(WordLangTuple const &) = 0;
/// return the next near miss after a SUGGESTED_WORDS result
virtual docstring const nextMiss() = 0;
/// give an error message on messy exit /// give an error message on messy exit
virtual docstring const error() = 0; virtual docstring const error() = 0;
}; };