diff -Naur aspell-0.60.6.1.orig/Makefile.am aspell-0.60.6.1/Makefile.am --- aspell-0.60.6.1.orig/Makefile.am 2011-07-02 16:42:34.000000000 -0700 +++ aspell-0.60.6.1/Makefile.am 2017-05-08 09:58:19.000000000 -0700 @@ -89,6 +89,8 @@ 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 -Naur aspell-0.60.6.1.orig/auto/MkSrc/ProcImpl.pm aspell-0.60.6.1/auto/MkSrc/ProcImpl.pm --- aspell-0.60.6.1.orig/auto/MkSrc/ProcImpl.pm 2011-07-02 14:09:08.000000000 -0700 +++ aspell-0.60.6.1/auto/MkSrc/ProcImpl.pm 2017-05-08 09:58:19.000000000 -0700 @@ -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 @@ $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 -Naur aspell-0.60.6.1.orig/auto/mk-src.in aspell-0.60.6.1/auto/mk-src.in --- aspell-0.60.6.1.orig/auto/mk-src.in 2011-07-02 14:09:08.000000000 -0700 +++ aspell-0.60.6.1/auto/mk-src.in 2017-05-08 09:58:19.000000000 -0700 @@ -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/. @@ -705,11 +705,21 @@ 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 @@ -896,6 +906,7 @@ class: string enumeration c impl headers => convert / + copyable methods method: at end const @@ -1054,6 +1065,96 @@ 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 -Naur aspell-0.60.6.1.orig/common/speller.hpp aspell-0.60.6.1/common/speller.hpp --- aspell-0.60.6.1.orig/common/speller.hpp 2011-07-02 14:09:08.000000000 -0700 +++ aspell-0.60.6.1/common/speller.hpp 2017-05-08 09:58:19.000000000 -0700 @@ -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 @@ 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 -Naur aspell-0.60.6.1.orig/interfaces/cc/aspell.h aspell-0.60.6.1/interfaces/cc/aspell.h --- aspell-0.60.6.1.orig/interfaces/cc/aspell.h 2011-07-02 14:53:27.000000000 -0700 +++ aspell-0.60.6.1/interfaces/cc/aspell.h 2017-05-08 09:58:19.000000000 -0700 @@ -375,9 +375,14 @@ /* 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 struct AspellWordList * aspell_speller_suggest(struct AspellSpeller * ths, const char * word, int word_size); +/* Return NULL on error. + * The word list returned by suggest_plus is only + * valid until the next call to suggest or suggest_plus. */ +const struct AspellSuggestionList * aspell_speller_suggest_plus(struct AspellSpeller * ths, const char * word, int word_size); + int aspell_speller_store_replacement(struct AspellSpeller * ths, const char * mis, int mis_size, const char * cor, int cor_size); @@ -718,6 +723,64 @@ +/****************************** suggestion ******************************/ + + +struct AspellSuggestion { + + const char * word; + + int word_len; + + /* How likely is this word given the original word? */ + int score; + +}; + + +typedef struct AspellSuggestion AspellSuggestion; + + +/************************ suggestion enumeration ************************/ + + +typedef struct AspellSuggestionEnumeration AspellSuggestionEnumeration; + + +void delete_aspell_suggestion_enumeration(struct AspellSuggestionEnumeration * ths); + +struct AspellSuggestionEnumeration * aspell_suggestion_enumeration_clone(const struct AspellSuggestionEnumeration * ths); + +void aspell_suggestion_enumeration_assign(struct AspellSuggestionEnumeration * ths, const struct AspellSuggestionEnumeration * other); + +int aspell_suggestion_enumeration_at_end(const struct AspellSuggestionEnumeration * ths); + +const struct AspellSuggestion * aspell_suggestion_enumeration_next(struct AspellSuggestionEnumeration * ths); + + + +/*************************** suggestion list ***************************/ + + +typedef struct AspellSuggestionList AspellSuggestionList; + + +struct AspellSuggestionList * new_aspell_suggestion_list(); + +int aspell_suggestion_list_empty(const struct AspellSuggestionList * ths); + +unsigned int aspell_suggestion_list_size(const struct AspellSuggestionList * ths); + +struct AspellSuggestionEnumeration * aspell_suggestion_list_elements(const struct AspellSuggestionList * ths); + +void delete_aspell_suggestion_list(struct AspellSuggestionList * ths); + +struct AspellSuggestionList * aspell_suggestion_list_clone(const struct AspellSuggestionList * ths); + +void aspell_suggestion_list_assign(struct AspellSuggestionList * ths, const struct AspellSuggestionList * other); + + + /******************************** cache ********************************/ diff -Naur aspell-0.60.6.1.orig/lib/speller-c.cpp aspell-0.60.6.1/lib/speller-c.cpp --- aspell-0.60.6.1.orig/lib/speller-c.cpp 2011-07-02 14:53:27.000000000 -0700 +++ aspell-0.60.6.1/lib/speller-c.cpp 2017-05-08 09:58:19.000000000 -0700 @@ -11,6 +11,7 @@ #include "mutable_string.hpp" #include "posib_err.hpp" #include "speller.hpp" +#include "suggestion_list.hpp" #include "word_list.hpp" namespace acommon { @@ -19,6 +20,7 @@ class Config; struct Error; class Speller; +class SuggestionList; class WordList; extern "C" CanHaveError * new_aspell_speller(Config * config) @@ -153,6 +155,19 @@ return ret.data; } +extern "C" const SuggestionList * aspell_speller_suggest_plus(Speller * ths, const char * word, int word_size) +{ + ths->temp_str_0.clear(); + ths->to_internal_->convert(word, word_size, ths->temp_str_0); + unsigned int s0 = ths->temp_str_0.size(); + PosibErr ret = ths->suggest_plus(MutableString(ths->temp_str_0.mstr(), s0)); + ths->err_.reset(ret.release_err()); + if (ths->err_ != 0) return 0; + if (ret.data) + const_cast(ret.data)->from_internal_ = ths->from_internal_; + return ret.data; +} + extern "C" int aspell_speller_store_replacement(Speller * ths, const char * mis, int mis_size, const char * cor, int cor_size) { ths->temp_str_0.clear(); diff -Naur aspell-0.60.6.1.orig/manual/aspell.texi aspell-0.60.6.1/manual/aspell.texi --- aspell-0.60.6.1.orig/manual/aspell.texi 2011-07-04 01:57:58.000000000 -0700 +++ aspell-0.60.6.1/manual/aspell.texi 2017-05-08 09:58:19.000000000 -0700 @@ -2217,7 +2217,7 @@ @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 @} @@ -2226,7 +2226,30 @@ 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 -Naur aspell-0.60.6.1.orig/modules/speller/default/speller_impl.cpp aspell-0.60.6.1/modules/speller/default/speller_impl.cpp --- aspell-0.60.6.1.orig/modules/speller/default/speller_impl.cpp 2011-07-04 02:15:44.000000000 -0700 +++ aspell-0.60.6.1/modules/speller/default/speller_impl.cpp 2017-05-08 09:58:19.000000000 -0700 @@ -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 @@ 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 @@ return new SpellerImpl(); } } - diff -Naur aspell-0.60.6.1.orig/modules/speller/default/speller_impl.hpp aspell-0.60.6.1/modules/speller/default/speller_impl.hpp --- aspell-0.60.6.1.orig/modules/speller/default/speller_impl.hpp 2011-07-02 14:09:09.000000000 -0700 +++ aspell-0.60.6.1/modules/speller/default/speller_impl.hpp 2017-05-08 09:58:19.000000000 -0700 @@ -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 @@ 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 -Naur aspell-0.60.6.1.orig/modules/speller/default/suggest.cpp aspell-0.60.6.1/modules/speller/default/suggest.cpp --- aspell-0.60.6.1.orig/modules/speller/default/suggest.cpp 2011-07-02 14:09:09.000000000 -0700 +++ aspell-0.60.6.1/modules/speller/default/suggest.cpp 2017-05-08 10:03:53.000000000 -0700 @@ -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,14 @@ namespace { - typedef vector NearMissesFinal; + // The internal, C++ version of the external Suggest/AspellSuggest + // struct. I don't use that struct because I want the convenience + // of automatically allocated space for the strings. + struct SuggestionPlus { + String word; + int score; + }; + typedef vector NearMissesFinal; template inline Iterator preview_next (Iterator i) { @@ -286,9 +296,10 @@ : 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 +389,7 @@ 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 +1208,8 @@ } } + // Transfer the final suggestion list to the stored output ptr., + // *near_misses_final. void Working::transfer() { # ifdef DEBUG_SUGGEST @@ -1227,19 +1241,26 @@ ((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 +1268,14 @@ 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 +1286,70 @@ } }; + // 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 +1368,8 @@ //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 +1399,27 @@ 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 +1518,9 @@ 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 -Naur aspell-0.60.6.1.orig/modules/speller/default/suggest.hpp aspell-0.60.6.1/modules/speller/default/suggest.hpp --- aspell-0.60.6.1.orig/modules/speller/default/suggest.hpp 2011-07-02 14:09:09.000000000 -0700 +++ aspell-0.60.6.1/modules/speller/default/suggest.hpp 2017-05-08 09:58:19.000000000 -0700 @@ -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 @@ 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() {} };