diff --git a/Makefile.am b/Makefile.am index 7e15851f..03762719 100644 --- a/Makefile.am +++ b/Makefile.am @@ -90,6 +90,8 @@ libaspell_la_SOURCES =\ lib/find_speller.cpp\ lib/speller-c.cpp\ lib/string_pair_enumeration-c.cpp\ + lib/suggestion_enumeration-c.cpp\ + lib/suggestion_list-c.cpp\ lib/new_checker.cpp libaspell_la_LIBADD = $(LTLIBINTL) $(PTHREAD_LIB) diff --git a/auto/MkSrc/ProcImpl.pm b/auto/MkSrc/ProcImpl.pm index b8628fdd..a847ad79 100644 --- a/auto/MkSrc/ProcImpl.pm +++ b/auto/MkSrc/ProcImpl.pm @@ -1,5 +1,5 @@ # This file is part of The New Aspell -# Copyright (C) 2001-2002 by Kevin Atkinson under the GNU LGPL +# Copyright (C) 2001-2006 by Kevin Atkinson under the GNU LGPL # license version 2.0 or 2.1. You should have received a copy of the # LGPL license along with this library if you did not you can find it # at http://www.gnu.org/. @@ -102,6 +102,11 @@ $info{class}{proc}{impl} = sub { $ret .= " if (ret.data)\n"; $ret .= " const_cast(ret.data)->from_internal_ = ths->from_internal_;\n"; } + if ($ret_type->{type} eq 'const suggestion list') { + $accum->{headers}{'suggestion list'} = true; + $ret .= " if (ret.data)\n"; + $ret .= " const_cast(ret.data)->from_internal_ = ths->from_internal_;\n"; + } $ret .= " "; $ret .= "return " unless $ret_type->{type} eq 'void'; $ret .= $exp; diff --git a/auto/mk-src.in b/auto/mk-src.in index dc9bab12..ceaa3dec 100644 --- a/auto/mk-src.in +++ b/auto/mk-src.in @@ -3,7 +3,7 @@ # generate interface code. # # This file is part of The New Aspell -# Copyright (C) 2001-2005 by Kevin Atkinson under the GNU LGPL +# Copyright (C) 2001-2006 by Kevin Atkinson under the GNU LGPL # license version 2.0 or 2.1. You should have received a copy of the # LGPL license along with this library if you did not you can find it # at http://www.gnu.org/. @@ -714,11 +714,21 @@ class: speller posib err desc => Return NULL on error. The word list returned by suggest is only - valid until the next call to suggest. + valid until the next call to suggest or suggest_plus. / const word list encoded string: word + method: suggest plus + + posib err + desc => Return NULL on error. + The word list returned by suggest_plus is only + valid until the next call to suggest or suggest_plus. + / + const suggestion list + encoded string: word + method: store replacement posib err @@ -905,6 +915,7 @@ group: string enumeration class: string enumeration c impl headers => convert / + copyable methods method: at end const @@ -1063,6 +1074,96 @@ class: string pair enumeration enumeration methods copyable methods } + +group: suggestion +{ +/ +struct: suggestion + / + string: word + default => "" + int: word len + default => 0 + int: score + desc => How likely is this word given the original word? +} +group: suggestion enumeration +{ +/ +class: suggestion enumeration + c impl headers => suggestion + / + + cxx member: from internal + headers => convert + what => class Convert * from_internal_ + default => 0 + desc => Conversion routine to external representation. + / + + cxx member: temp str + what => String temp_str_ + default => "" + desc => Holds the string in external format when converted. + / + + copyable methods + + method: at end + const + / + bool + + method: next + c impl => + const Suggestion * s = ths->next(); + if (s == 0 || ths->from_internal_ == 0) \{ + return s; + \} else \{ + ths->temp_str_.clear(); + ths->from_internal_->convert(s->word, -1, ths->temp_str_); + ths->from_internal_->append_null(ths->temp_str_); + (const_cast(s))->word = ths->temp_str_.data(); + return s; + \} + / + const suggestion +} + +group: suggestion list +{ +/ +class: suggestion list + c impl headers => suggestion enumeration + / + constructor + + method: empty + const + / + bool + + method: size + const + / + unsigned int + + method: elements + const + c impl => + SuggestionEnumeration * els = ths->elements(); + els->from_internal_ = ths->from_internal_; + return els; + / + suggestion enumeration + + copyable methods + + cxx member: from internal + what => class Convert * from_internal_ + default => 0 +} + group: cache { / diff --git a/common/speller.hpp b/common/speller.hpp index 031299c4..1b13176c 100644 --- a/common/speller.hpp +++ b/common/speller.hpp @@ -1,5 +1,5 @@ // This file is part of The New Aspell -// Copyright (C) 2001 by Kevin Atkinson under the GNU LGPL license +// Copyright (C) 2001-2006 by Kevin Atkinson under the GNU LGPL license // version 2.0 or 2.1. You should have received a copy of the LGPL // license along with this library if you did not you can find // it at http://www.gnu.org/. @@ -22,6 +22,7 @@ #include "posib_err.hpp" #include "parm_string.hpp" #include "char_vector.hpp" +#include "suggestion_list.hpp" namespace acommon { @@ -107,9 +108,10 @@ namespace acommon { virtual PosibErr clear_session() = 0; virtual PosibErr suggest(MutableString) = 0; + virtual PosibErr suggest_plus(MutableString) = 0; // return null on error // the word list returned by suggest is only valid until the next - // call to suggest + // call to suggest or suggest_plus virtual PosibErr store_replacement(MutableString, MutableString) = 0; diff --git a/manual/aspell.texi b/manual/aspell.texi index 8708ad1d..96d0cbd9 100644 --- a/manual/aspell.texi +++ b/manual/aspell.texi @@ -2221,7 +2221,7 @@ AspellWordList * suggestions = aspell_speller_suggest(spell_checker, @var{word}, @var{size}); AspellStringEnumeration * elements = aspell_word_list_elements(suggestions); const char * word; -while ( (word = aspell_string_enumeration_next(aspell_elements)) != NULL ) +while ( (word = aspell_string_enumeration_next(elements)) != NULL ) @{ // add to suggestion list @} @@ -2230,7 +2230,30 @@ delete_aspell_string_enumeration(elements); Notice how @code{elements} is deleted but @code{suggestions} is not. The value returned by @code{suggestions} is only valid to the next -call to @code{suggest}. Once a replacement is made the +call to @code{suggest}. + +If you want to sort the Aspell words by some additional criterion before +presenting them, you should also take into account the ``score'' that +Aspell used to sort them. The score gives the ``distance'' of the +suggestion from the original, with increasing scores giving increasing +distances. To cycle through a list of suggestions with scores, +instead of the above example, copy this code: +@smallexample +AspellSuggestionList * suggestions = aspell_speller_suggest_plus( + spell_checker, + @var{word}, @var{size}); +AspellSuggestionEnumeration * elements = + aspell_suggestion_list_elements(suggestions); +const AspellSuggestion * sugg; +while ( (sugg = aspell_suggestion_enumeration_next(elements)) != NULL ) +@{ + // Add to suggestion list sugg->word, of length sugg->word_len, and + // with score sugg->score. +@} +delete_aspell_suggestion_enumeration(elements); +@end smallexample + +Once a replacement is made the @code{store_repl} method should be used to communicate the replacement pair back to the spell checker (for the reason, @pxref{Notes on Storing Replacement Pairs}). Its usage is as follows: diff --git a/modules/speller/default/speller_impl.cpp b/modules/speller/default/speller_impl.cpp index f0704e8a..7004de36 100644 --- a/modules/speller/default/speller_impl.cpp +++ b/modules/speller/default/speller_impl.cpp @@ -1,5 +1,5 @@ // This file is part of The New Aspell -// Copyright (C) 2000-2001,2011 by Kevin Atkinson under the GNU LGPL +// Copyright (C) 2000-2006,2011 by Kevin Atkinson under the GNU LGPL // license version 2.0 or 2.1. You should have received a copy of the // LGPL license along with this library if you did not you can find it // at http://www.gnu.org/. @@ -133,6 +133,11 @@ namespace aspeller { return &suggest_->suggest(word); } + PosibErr SpellerImpl::suggest_plus(MutableString word) + { + return &suggest_->suggest_plus(word); + } + bool SpellerImpl::check_simple (ParmString w, WordEntry & w0) { w0.clear(); // FIXME: is this necessary? @@ -739,4 +744,3 @@ namespace aspeller { return new SpellerImpl(); } } - diff --git a/modules/speller/default/speller_impl.hpp b/modules/speller/default/speller_impl.hpp index 6a2f6de7..5c1cfc5a 100644 --- a/modules/speller/default/speller_impl.hpp +++ b/modules/speller/default/speller_impl.hpp @@ -1,5 +1,5 @@ // Aspell main C++ include file -// Copyright 1998-2000 by Kevin Atkinson under the terms of the LGPL. +// Copyright 1998-2006 by Kevin Atkinson under the terms of the LGPL. #ifndef __aspeller_speller__ #define __aspeller_speller__ @@ -141,8 +141,9 @@ namespace aspeller { PosibErr clear_session(); PosibErr suggest(MutableString word); + PosibErr suggest_plus(MutableString word); // the suggestion list and the elements in it are only - // valid until the next call to suggest. + // valid until the next call to suggest or suggest_plus. PosibErr store_replacement(MutableString mis, MutableString cor); diff --git a/modules/speller/default/suggest.cpp b/modules/speller/default/suggest.cpp index 92da1271..da1fb634 100644 --- a/modules/speller/default/suggest.cpp +++ b/modules/speller/default/suggest.cpp @@ -1,4 +1,4 @@ -// Copyright 2000-2005 by Kevin Atkinson under the terms of the LGPL +// Copyright 2000-2006 by Kevin Atkinson under the terms of the LGPL // suggest.cpp Suggestion code for Aspell @@ -62,6 +62,9 @@ #include "suggest.hpp" #include "vararray.hpp" #include "string_list.hpp" +#include "suggestion.hpp" +#include "suggestion_list.hpp" +#include "suggestion_enumeration.hpp" #include "gettext.h" @@ -74,7 +77,11 @@ using namespace std; namespace { - typedef vector NearMissesFinal; + struct SuggestionPlus { + String word; + int score; + }; + typedef vector NearMissesFinal; template inline Iterator preview_next (Iterator i) { @@ -286,9 +293,10 @@ namespace { : Score(l,w,p), threshold(1), max_word_length(0), sp(m) { memset(check_info, 0, sizeof(check_info)); } - void get_suggestions(NearMissesFinal &sug); + void get_suggestions(NearMissesFinal & sug); }; + // Return a list of suggestions (with scores) for word, sorted by score. void Working::get_suggestions(NearMissesFinal & sug) { if (original.word.size() * parms->edit_distance_weights.max >= 0x8000) @@ -378,6 +386,7 @@ namespace { transfer(); } + // Forms a word by combining CheckInfo fields. // Will grow the grow the temp in the buffer. The final // word must be null terminated and commited. @@ -1196,6 +1205,8 @@ namespace { } } + // Transfer the final suggestion list to the stored output ptr., + // *near_misses_final. void Working::transfer() { # ifdef DEBUG_SUGGEST @@ -1227,19 +1238,26 @@ namespace { ((pos = dup_pair.first->find(' '), pos == String::npos) ? (bool)sp->check(*dup_pair.first) : (sp->check((String)dup_pair.first->substr(0,pos)) - && sp->check((String)dup_pair.first->substr(pos+1))) )) - near_misses_final->push_back(*dup_pair.first); + && sp->check((String)dup_pair.first->substr(pos+1))) )) { + SuggestionPlus sug_plus = {*dup_pair.first, i->score}; + near_misses_final->push_back(sug_plus); + } } while (i->repl_list->adv()); } else { fix_case(i->word); dup_pair = duplicates_check.insert(i->word); - if (dup_pair.second ) - near_misses_final->push_back(*dup_pair.first); + if (dup_pair.second) { + SuggestionPlus sug_plus = {*dup_pair.first, i->score}; + near_misses_final->push_back(sug_plus); + } } } } - - class SuggestionListImpl : public SuggestionList { + + // This is for the class which ultimately builds on WordList + // and is returned to the user as a WordList, so the elements() method + // must return a StringEnumeration. + class SuggestWordListImpl : public SuggestWordList { struct Parms { typedef const char * Value; typedef NearMissesFinal::const_iterator Iterator; @@ -1247,14 +1265,14 @@ namespace { Parms(Iterator e) : end(e) {} bool endf(Iterator e) const {return e == end;} Value end_state() const {return 0;} - Value deref(Iterator i) const {return i->c_str();} + Value deref(Iterator i) const {return i->word.c_str();} }; public: NearMissesFinal suggestions; - SuggestionList * clone() const {return new SuggestionListImpl(*this);} - void assign(const SuggestionList * other) { - *this = *static_cast(other); + SuggestWordList * clone() const {return new SuggestWordListImpl(*this);} + void assign(const SuggestWordList * other) { + *this = *static_cast(other); } bool empty() const { return suggestions.empty(); } @@ -1265,9 +1283,70 @@ namespace { } }; + // This is for the class which builds on SuggestionList + // and is returned to the user as a SuggestionList, so the elements() method + // must return a SuggestionEnumeration. + class SuggestionListImpl : public SuggestionList { + public: + SuggestWordListImpl words; // For its Suggestion list + + private: + class EnumerationImpl : public SuggestionEnumeration { + NearMissesFinal::const_iterator pos_; // Steps through suggested words. + NearMissesFinal::const_iterator end_; // When we're done w/ current + Suggestion data_; // For repackaging current data. + + public: + SuggestionEnumeration * clone() const { + return new EnumerationImpl(*this); + } + + EnumerationImpl(const SuggestionListImpl * base) : + pos_(base->words.suggestions.begin()), + end_(base->words.suggestions.end()) + {} + + bool at_end() const { return pos_ == end_; } + Suggestion * next() { + if (at_end()) return 0; + + data_.word = pos_->word.c_str(); + data_.word_len = pos_->word.size(); + data_.score = pos_->score; + + ++pos_; + return &data_; + } + + void assign(const SuggestionEnumeration * other) { + *this = *static_cast(other); + } + }; + + public: + + SuggestionList * clone() const {return new SuggestionListImpl(*this);} + + void assign(const SuggestionList * other) { + *this = *static_cast(other); + } + + bool empty() const { return words.suggestions.empty(); } + unsigned int size() const { return words.suggestions.size(); } + SuggestionEnumeration * elements() const { + return new EnumerationImpl(this); + } + + void reset() { + words.suggestions.resize(0); + } + }; + + + // Central class of this file: build suggestion lists. class SuggestImpl : public Suggest { SpellerImpl * speller_; - SuggestionListImpl suggestion_list; + SuggestionListImpl scored_suggestion_list; SuggestParms parms_; public: PosibErr setup(SpellerImpl * m); @@ -1286,7 +1365,8 @@ namespace { //return sws.score; return -1; } - SuggestionList & suggest(const char * word); + SuggestWordList & suggest(const char * word); + SuggestionList & suggest_plus(const char * word); }; PosibErr SuggestImpl::setup(SpellerImpl * m) @@ -1316,20 +1396,27 @@ namespace { return no_err; } - SuggestionList & SuggestImpl::suggest(const char * word) { + // Return a list of suggestions (with scores). + SuggestionList & SuggestImpl::suggest_plus(const char * word) { # ifdef DEBUG_SUGGEST COUT << "=========== begin suggest " << word << " ===========\n"; # endif parms_.set_original_word_size(strlen(word)); - suggestion_list.suggestions.resize(0); - Working sug(speller_, &speller_->lang(),word,&parms_); - sug.get_suggestions(suggestion_list.suggestions); + scored_suggestion_list.reset(); + Working sug(speller_, &speller_->lang(), word, &parms_); + sug.get_suggestions(scored_suggestion_list.words.suggestions); # ifdef DEBUG_SUGGEST COUT << "^^^^^^^^^^^ end suggest " << word << " ^^^^^^^^^^^\n"; # endif - return suggestion_list; + return scored_suggestion_list; } - + + // Return a list of suggestions (without scores). + SuggestWordList & SuggestImpl::suggest(const char * word) { + suggest_plus(word); + return scored_suggestion_list.words; + } + } namespace aspeller { @@ -1428,3 +1515,9 @@ namespace aspeller { word_weight = 100 - soundslike_weight; } } + +namespace acommon { + // This is a function with an auto-generated demand. Not sure + // where to define it. + SuggestionList * new_suggestion_list() { return new SuggestionListImpl(); } +} diff --git a/modules/speller/default/suggest.hpp b/modules/speller/default/suggest.hpp index 5fc62805..725a6307 100644 --- a/modules/speller/default/suggest.hpp +++ b/modules/speller/default/suggest.hpp @@ -1,4 +1,4 @@ -// Copyright 2000 by Kevin Atkinson under the terms of the LGPL +// Copyright 2000-2006 by Kevin Atkinson under the terms of the LGPL #ifndef ASPELLER_SUGGEST__HPP #define ASPELLER_SUGGEST__HPP @@ -13,27 +13,28 @@ namespace aspeller { class SpellerImpl; - class SuggestionList : public WordList { + class SuggestWordList : public WordList { public: typedef StringEnumeration VirEmul; typedef Enumeration Emul; typedef const char * Value; typedef unsigned int Size; - virtual SuggestionList * clone() const = 0; - virtual void assign(const SuggestionList *) = 0; + virtual SuggestWordList * clone() const = 0; + virtual void assign(const SuggestWordList *) = 0; virtual bool empty() const = 0; virtual Size size() const = 0; virtual VirEmul * elements() const = 0; - virtual ~SuggestionList() {} + virtual ~SuggestWordList() {} }; class Suggest { public: virtual PosibErr set_mode(ParmString) = 0; virtual double score(const char * base, const char * other) = 0; - virtual SuggestionList & suggest(const char * word) = 0; + virtual SuggestWordList & suggest(const char * word) = 0; + virtual SuggestionList & suggest_plus(const char * word) = 0; virtual ~Suggest() {} };