aboutsummaryrefslogtreecommitdiff
path: root/src/nvim
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2022-08-14 18:30:37 +0800
committerGitHub <noreply@github.com>2022-08-14 18:30:37 +0800
commitc77cce615b1bdacd60f98ba5f41540535d142a1f (patch)
treedff6b8c1624de19df0cf6197ffa652419bf4e3af /src/nvim
parentc96020b2bf671995a3c125d4db219fa1e9635a86 (diff)
downloadrneovim-c77cce615b1bdacd60f98ba5f41540535d142a1f.tar.gz
rneovim-c77cce615b1bdacd60f98ba5f41540535d142a1f.tar.bz2
rneovim-c77cce615b1bdacd60f98ba5f41540535d142a1f.zip
vim-patch:8.1.2081: the spell.c file is too big (#19767)
Problem: The spell.c file is too big. Solution: Move the code for spell suggestions to a separate file. (Yegappan Lakshmanan, closes vim/vim#4988) https://github.com/vim/vim/commit/46a426c9acfdd3d6c0fa134a17681634b9325bee
Diffstat (limited to 'src/nvim')
-rw-r--r--src/nvim/eval/funcs.c1
-rw-r--r--src/nvim/normal.c1
-rw-r--r--src/nvim/option.c1
-rw-r--r--src/nvim/spell.c3751
-rw-r--r--src/nvim/spell_defs.h65
-rw-r--r--src/nvim/spellsuggest.c3788
-rw-r--r--src/nvim/spellsuggest.h9
7 files changed, 3830 insertions, 3786 deletions
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index fe3c0f825e..349bb6c226 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -68,6 +68,7 @@
#include "nvim/sha256.h"
#include "nvim/sign.h"
#include "nvim/spell.h"
+#include "nvim/spellsuggest.h"
#include "nvim/state.h"
#include "nvim/syntax.h"
#include "nvim/tag.h"
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index b293841bb0..e6fa9e76c6 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -58,6 +58,7 @@
#include "nvim/search.h"
#include "nvim/spell.h"
#include "nvim/spellfile.h"
+#include "nvim/spellsuggest.h"
#include "nvim/state.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 5e88ed98e8..da29d40b05 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -72,6 +72,7 @@
#include "nvim/screen.h"
#include "nvim/spell.h"
#include "nvim/spellfile.h"
+#include "nvim/spellsuggest.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/ui.h"
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index 17e677c6b7..ed2b8bbdb5 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -56,18 +56,6 @@
// Use DEBUG_TRIEWALK to print the changes made in suggest_trie_walk() for a
// specific word.
-// Use this to adjust the score after finding suggestions, based on the
-// suggested word sounding like the bad word. This is much faster than doing
-// it for every possible suggestion.
-// Disadvantage: When "the" is typed as "hte" it sounds quite different ("@"
-// vs "ht") and goes down in the list.
-// Used when 'spellsuggest' is set to "best".
-#define RESCORE(word_score, sound_score) ((3 * (word_score) + (sound_score)) / 4)
-
-// Do the opposite: based on a maximum end score and a known sound score,
-// compute the maximum word score that can be used.
-#define MAXSCORE(word_score, sound_score) ((4 * (word_score) - (sound_score)) / 3)
-
#include <assert.h>
#include <inttypes.h>
#include <limits.h>
@@ -85,13 +73,10 @@
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/edit.h"
-#include "nvim/eval.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_docmd.h"
-#include "nvim/fileio.h"
#include "nvim/func_attr.h"
#include "nvim/garray.h"
-#include "nvim/getchar.h"
#include "nvim/hashtab.h"
#include "nvim/input.h"
#include "nvim/insexpand.h"
@@ -106,22 +91,15 @@
#include "nvim/os/os.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
-#include "nvim/profile.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/spell.h"
#include "nvim/spellfile.h"
+#include "nvim/spellsuggest.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
-#include "nvim/ui.h"
#include "nvim/undo.h"
-// only used for su_badflags
-#define WF_MIXCAP 0x20 // mix of upper and lower case: macaRONI
-
-#define WF_CAPMASK (WF_ONECAP | WF_ALLCAP | WF_KEEPCAP | WF_FIXCAP)
-
// Result values. Lower number is accepted over higher one.
#define SP_BANNED (-1)
#define SP_RARE 0
@@ -136,104 +114,6 @@ slang_T *first_lang = NULL;
// file used for "zG" and "zW"
char_u *int_wordlist = NULL;
-typedef struct wordcount_S {
- uint16_t wc_count; // nr of times word was seen
- char_u wc_word[1]; // word, actually longer
-} wordcount_T;
-
-#define WC_KEY_OFF offsetof(wordcount_T, wc_word)
-#define HI2WC(hi) ((wordcount_T *)((hi)->hi_key - WC_KEY_OFF))
-#define MAXWORDCOUNT 0xffff
-
-// Information used when looking for suggestions.
-typedef struct suginfo_S {
- garray_T su_ga; // suggestions, contains "suggest_T"
- int su_maxcount; // max. number of suggestions displayed
- int su_maxscore; // maximum score for adding to su_ga
- int su_sfmaxscore; // idem, for when doing soundfold words
- garray_T su_sga; // like su_ga, sound-folded scoring
- char_u *su_badptr; // start of bad word in line
- int su_badlen; // length of detected bad word in line
- int su_badflags; // caps flags for bad word
- char_u su_badword[MAXWLEN]; // bad word truncated at su_badlen
- char_u su_fbadword[MAXWLEN]; // su_badword case-folded
- char_u su_sal_badword[MAXWLEN]; // su_badword soundfolded
- hashtab_T su_banned; // table with banned words
- slang_T *su_sallang; // default language for sound folding
-} suginfo_T;
-
-// One word suggestion. Used in "si_ga".
-typedef struct {
- char_u *st_word; // suggested word, allocated string
- int st_wordlen; // STRLEN(st_word)
- int st_orglen; // length of replaced text
- int st_score; // lower is better
- int st_altscore; // used when st_score compares equal
- bool st_salscore; // st_score is for soundalike
- bool st_had_bonus; // bonus already included in score
- slang_T *st_slang; // language used for sound folding
-} suggest_T;
-
-#define SUG(ga, i) (((suggest_T *)(ga).ga_data)[i])
-
-// True if a word appears in the list of banned words.
-#define WAS_BANNED(su, word) (!HASHITEM_EMPTY(hash_find(&(su)->su_banned, word)))
-
-// Number of suggestions kept when cleaning up. We need to keep more than
-// what is displayed, because when rescore_suggestions() is called the score
-// may change and wrong suggestions may be removed later.
-#define SUG_CLEAN_COUNT(su) ((su)->su_maxcount < \
- 130 ? 150 : (su)->su_maxcount + 20)
-
-// Threshold for sorting and cleaning up suggestions. Don't want to keep lots
-// of suggestions that are not going to be displayed.
-#define SUG_MAX_COUNT(su) (SUG_CLEAN_COUNT(su) + 50)
-
-// score for various changes
-#define SCORE_SPLIT 149 // split bad word
-#define SCORE_SPLIT_NO 249 // split bad word with NOSPLITSUGS
-#define SCORE_ICASE 52 // slightly different case
-#define SCORE_REGION 200 // word is for different region
-#define SCORE_RARE 180 // rare word
-#define SCORE_SWAP 75 // swap two characters
-#define SCORE_SWAP3 110 // swap two characters in three
-#define SCORE_REP 65 // REP replacement
-#define SCORE_SUBST 93 // substitute a character
-#define SCORE_SIMILAR 33 // substitute a similar character
-#define SCORE_SUBCOMP 33 // substitute a composing character
-#define SCORE_DEL 94 // delete a character
-#define SCORE_DELDUP 66 // delete a duplicated character
-#define SCORE_DELCOMP 28 // delete a composing character
-#define SCORE_INS 96 // insert a character
-#define SCORE_INSDUP 67 // insert a duplicate character
-#define SCORE_INSCOMP 30 // insert a composing character
-#define SCORE_NONWORD 103 // change non-word to word char
-
-#define SCORE_FILE 30 // suggestion from a file
-#define SCORE_MAXINIT 350 // Initial maximum score: higher == slower.
- // 350 allows for about three changes.
-
-#define SCORE_COMMON1 30 // subtracted for words seen before
-#define SCORE_COMMON2 40 // subtracted for words often seen
-#define SCORE_COMMON3 50 // subtracted for words very often seen
-#define SCORE_THRES2 10 // word count threshold for COMMON2
-#define SCORE_THRES3 100 // word count threshold for COMMON3
-
-// When trying changed soundfold words it becomes slow when trying more than
-// two changes. With less than two changes it's slightly faster but we miss a
-// few good suggestions. In rare cases we need to try three of four changes.
-#define SCORE_SFMAX1 200 // maximum score for first try
-#define SCORE_SFMAX2 300 // maximum score for second try
-#define SCORE_SFMAX3 400 // maximum score for third try
-
-#define SCORE_BIG (SCORE_INS * 3) // big difference
-#define SCORE_MAXMAX 999999 // accept any score
-#define SCORE_LIMITMAX 350 // for spell_edit_score_limit()
-
-// for spell_edit_score_limit() we need to know the minimum value of
-// SCORE_ICASE, SCORE_SWAP, SCORE_DEL, SCORE_SIMILAR and SCORE_INS
-#define SCORE_EDIT_MIN SCORE_SIMILAR
-
// Structure to store info for word matching.
typedef struct matchinf_S {
langp_T *mi_lp; // info for language and region
@@ -289,38 +169,10 @@ typedef struct syl_item_S {
spelltab_T spelltab;
int did_set_spelltab;
-// structure used to store soundfolded words that add_sound_suggest() has
-// handled already.
-typedef struct {
- int16_t sft_score; // lowest score used
- char_u sft_word[1]; // soundfolded word, actually longer
-} sftword_T;
-
-typedef struct {
- int badi;
- int goodi;
- int score;
-} limitscore_T;
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "spell.c.generated.h"
#endif
-// values for ts_isdiff
-#define DIFF_NONE 0 // no different byte (yet)
-#define DIFF_YES 1 // different byte found
-#define DIFF_INSERT 2 // inserting character
-
-// values for ts_flags
-#define TSF_PREFIXOK 1 // already checked that prefix is OK
-#define TSF_DIDSPLIT 2 // tried split at this point
-#define TSF_DIDDEL 4 // did a delete, "ts_delidx" has index
-
-// special values ts_prefixdepth
-#define PFD_NOPREFIX 0xff // not using prefixes
-#define PFD_PREFIXTREE 0xfe // walking through the prefix tree
-#define PFD_NOTSPECIAL 0xfd // highest value that's not special
-
// mode values for find_word
#define FIND_FOLDWORD 0 // find word case-folded
#define FIND_KEEPWORD 1 // find keep-case word
@@ -331,8 +183,8 @@ typedef struct {
char *e_format = N_("E759: Format error in spell file");
// Remember what "z?" replaced.
-static char_u *repl_from = NULL;
-static char_u *repl_to = NULL;
+char_u *repl_from = NULL;
+char_u *repl_to = NULL;
/// Main spell-checking function.
/// "ptr" points to a character that could be the start of a word.
@@ -1050,7 +902,7 @@ static void find_word(matchinf_T *mip, int mode)
/// end of ptr[wlen] and the second part matches after it.
///
/// @param gap &sl_comppat
-static bool match_checkcompoundpattern(char_u *ptr, int wlen, garray_T *gap)
+bool match_checkcompoundpattern(char_u *ptr, int wlen, garray_T *gap)
{
char_u *p;
int len;
@@ -1072,7 +924,7 @@ static bool match_checkcompoundpattern(char_u *ptr, int wlen, garray_T *gap)
// Returns true if "flags" is a valid sequence of compound flags and "word"
// does not have too many syllables.
-static bool can_compound(slang_T *slang, const char_u *word, const char_u *flags)
+bool can_compound(slang_T *slang, const char_u *word, const char_u *flags)
FUNC_ATTR_NONNULL_ALL
{
char_u uflags[MAXWLEN * 2] = { 0 };
@@ -1101,37 +953,11 @@ static bool can_compound(slang_T *slang, const char_u *word, const char_u *flags
return true;
}
-// Returns true when the sequence of flags in "compflags" plus "flag" can
-// possibly form a valid compounded word. This also checks the COMPOUNDRULE
-// lines if they don't contain wildcards.
-static bool can_be_compound(trystate_T *sp, slang_T *slang, char_u *compflags, int flag)
-{
- // If the flag doesn't appear in sl_compstartflags or sl_compallflags
- // then it can't possibly compound.
- if (!byte_in_str(sp->ts_complen == sp->ts_compsplit
- ? slang->sl_compstartflags : slang->sl_compallflags, flag)) {
- return false;
- }
-
- // If there are no wildcards, we can check if the flags collected so far
- // possibly can form a match with COMPOUNDRULE patterns. This only
- // makes sense when we have two or more words.
- if (slang->sl_comprules != NULL && sp->ts_complen > sp->ts_compsplit) {
- compflags[sp->ts_complen] = (char_u)flag;
- compflags[sp->ts_complen + 1] = NUL;
- bool v = match_compoundrule(slang, compflags + sp->ts_compsplit);
- compflags[sp->ts_complen] = NUL;
- return v;
- }
-
- return true;
-}
-
// Returns true if the compound flags in compflags[] match the start of any
// compound rule. This is used to stop trying a compound if the flags
// collected so far can't possibly match any compound rule.
// Caller must check that slang->sl_comprules is not NULL.
-static bool match_compoundrule(slang_T *slang, char_u *compflags)
+bool match_compoundrule(slang_T *slang, char_u *compflags)
{
char_u *p;
int i;
@@ -1188,8 +1014,8 @@ static bool match_compoundrule(slang_T *slang, char_u *compflags)
/// @param totprefcnt nr of prefix IDs
/// @param arridx idx in sl_pidxs[]
/// @param cond_req only use prefixes with a condition
-static int valid_word_prefix(int totprefcnt, int arridx, int flags, char_u *word, slang_T *slang,
- bool cond_req)
+int valid_word_prefix(int totprefcnt, int arridx, int flags, char_u *word, slang_T *slang,
+ bool cond_req)
{
int prefcnt;
int pidx;
@@ -1368,7 +1194,7 @@ static int fold_more(matchinf_T *mip)
///
/// @param wordflags Flags for the checked word.
/// @param treeflags Flags for the word in the spell tree.
-static bool spell_valid_case(int wordflags, int treeflags)
+bool spell_valid_case(int wordflags, int treeflags)
{
return (wordflags == WF_ALLCAP && (treeflags & WF_FIXCAP) == 0)
|| ((treeflags & (WF_ALLCAP | WF_KEEPCAP)) == 0
@@ -1377,7 +1203,7 @@ static bool spell_valid_case(int wordflags, int treeflags)
}
// Returns true if spell checking is not enabled.
-static bool no_spell_checking(win_T *wp)
+bool no_spell_checking(win_T *wp)
{
if (!wp->w_p_spell || *wp->w_s->b_p_spl == NUL
|| GA_EMPTY(&wp->w_s->b_langp)) {
@@ -1903,38 +1729,6 @@ void count_common_word(slang_T *lp, char_u *word, int len, uint8_t count)
}
}
-/// Adjust the score of common words.
-///
-/// @param split word was split, less bonus
-static int score_wordcount_adj(slang_T *slang, int score, char_u *word, bool split)
-{
- wordcount_T *wc;
- int bonus;
- int newscore;
-
- hashitem_T *hi = hash_find(&slang->sl_wordcount, (char *)word);
- if (!HASHITEM_EMPTY(hi)) {
- wc = HI2WC(hi);
- if (wc->wc_count < SCORE_THRES2) {
- bonus = SCORE_COMMON1;
- } else if (wc->wc_count < SCORE_THRES3) {
- bonus = SCORE_COMMON2;
- } else {
- bonus = SCORE_COMMON3;
- }
- if (split) {
- newscore = score - bonus / 2;
- } else {
- newscore = score - bonus;
- }
- if (newscore < 0) {
- return 0;
- }
- return newscore;
- }
- return score;
-}
-
// Returns true if byte "n" appears in "str".
// Like strchr() but independent of locale.
bool byte_in_str(char_u *str, int n)
@@ -2444,51 +2238,6 @@ int captype(char_u *word, char_u *end)
return 0;
}
-// Like captype() but for a KEEPCAP word add ONECAP if the word starts with a
-// capital. So that make_case_word() can turn WOrd into Word.
-// Add ALLCAP for "WOrD".
-static int badword_captype(char_u *word, char_u *end)
- FUNC_ATTR_NONNULL_ALL
-{
- int flags = captype(word, end);
- int c;
- int l, u;
- bool first;
- char_u *p;
-
- if (flags & WF_KEEPCAP) {
- // Count the number of UPPER and lower case letters.
- l = u = 0;
- first = false;
- for (p = word; p < end; MB_PTR_ADV(p)) {
- c = utf_ptr2char((char *)p);
- if (SPELL_ISUPPER(c)) {
- ++u;
- if (p == word) {
- first = true;
- }
- } else {
- ++l;
- }
- }
-
- // If there are more UPPER than lower case letters suggest an
- // ALLCAP word. Otherwise, if the first letter is UPPER then
- // suggest ONECAP. Exception: "ALl" most likely should be "All",
- // require three upper case letters.
- if (u > l && u > 2) {
- flags |= WF_ALLCAP;
- } else if (first) {
- flags |= WF_ONECAP;
- }
-
- if (u >= 2 && l >= 2) { // maCARONI maCAroni
- flags |= WF_MIXCAP;
- }
- }
- return flags;
-}
-
// Delete the internal wordlist and its .spl file.
void spell_delete_wordlist(void)
{
@@ -2547,36 +2296,6 @@ void spell_reload(void)
}
}
-// Opposite of offset2bytes().
-// "pp" points to the bytes and is advanced over it.
-// Returns the offset.
-static int bytes2offset(char_u **pp)
-{
- char_u *p = *pp;
- int nr;
- int c;
-
- c = *p++;
- if ((c & 0x80) == 0x00) { // 1 byte
- nr = c - 1;
- } else if ((c & 0xc0) == 0x80) { // 2 bytes
- nr = (c & 0x3f) - 1;
- nr = nr * 255 + (*p++ - 1);
- } else if ((c & 0xe0) == 0xc0) { // 3 bytes
- nr = (c & 0x1f) - 1;
- nr = nr * 255 + (*p++ - 1);
- nr = nr * 255 + (*p++ - 1);
- } else { // 4 bytes
- nr = (c & 0x0f) - 1;
- nr = nr * 255 + (*p++ - 1);
- nr = nr * 255 + (*p++ - 1);
- nr = nr * 255 + (*p++ - 1);
- }
-
- *pp = p;
- return nr;
-}
-
// Open a spell buffer. This is a nameless buffer that is not in the buffer
// list and only contains text lines. Can use a swapfile to reduce memory
// use.
@@ -2665,7 +2384,7 @@ void init_spell_chartab(void)
/// Thus this only works properly when past the first character of the word.
///
/// @param wp Buffer used.
-static bool spell_iswordp(const char_u *p, const win_T *wp)
+bool spell_iswordp(const char_u *p, const win_T *wp)
FUNC_ATTR_NONNULL_ALL
{
int c;
@@ -2783,298 +2502,9 @@ int spell_casefold(const win_T *wp, char_u *str, int len, char_u *buf, int bufle
return OK;
}
-// values for sps_flags
-#define SPS_BEST 1
-#define SPS_FAST 2
-#define SPS_DOUBLE 4
-
-static int sps_flags = SPS_BEST; // flags from 'spellsuggest'
-static int sps_limit = 9999; // max nr of suggestions given
-
-// Check the 'spellsuggest' option. Return FAIL if it's wrong.
-// Sets "sps_flags" and "sps_limit".
-int spell_check_sps(void)
-{
- char *p;
- char *s;
- char_u buf[MAXPATHL];
- int f;
-
- sps_flags = 0;
- sps_limit = 9999;
-
- for (p = (char *)p_sps; *p != NUL;) {
- copy_option_part(&p, (char *)buf, MAXPATHL, ",");
-
- f = 0;
- if (ascii_isdigit(*buf)) {
- s = (char *)buf;
- sps_limit = getdigits_int(&s, true, 0);
- if (*s != NUL && !ascii_isdigit(*s)) {
- f = -1;
- }
- } else if (STRCMP(buf, "best") == 0) {
- f = SPS_BEST;
- } else if (STRCMP(buf, "fast") == 0) {
- f = SPS_FAST;
- } else if (STRCMP(buf, "double") == 0) {
- f = SPS_DOUBLE;
- } else if (STRNCMP(buf, "expr:", 5) != 0
- && STRNCMP(buf, "file:", 5) != 0) {
- f = -1;
- }
-
- if (f == -1 || (sps_flags != 0 && f != 0)) {
- sps_flags = SPS_BEST;
- sps_limit = 9999;
- return FAIL;
- }
- if (f != 0) {
- sps_flags = f;
- }
- }
-
- if (sps_flags == 0) {
- sps_flags = SPS_BEST;
- }
-
- return OK;
-}
-
-// "z=": Find badly spelled word under or after the cursor.
-// Give suggestions for the properly spelled word.
-// In Visual mode use the highlighted word as the bad word.
-// When "count" is non-zero use that suggestion.
-void spell_suggest(int count)
-{
- char_u *line;
- pos_T prev_cursor = curwin->w_cursor;
- char_u wcopy[MAXWLEN + 2];
- char_u *p;
- int c;
- suginfo_T sug;
- suggest_T *stp;
- int mouse_used;
- int need_cap;
- int limit;
- int selected = count;
- int badlen = 0;
- int msg_scroll_save = msg_scroll;
- const int wo_spell_save = curwin->w_p_spell;
-
- if (!curwin->w_p_spell) {
- did_set_spelllang(curwin);
- curwin->w_p_spell = true;
- }
-
- if (*curwin->w_s->b_p_spl == NUL) {
- emsg(_(e_no_spell));
- return;
- }
-
- if (VIsual_active) {
- // Use the Visually selected text as the bad word. But reject
- // a multi-line selection.
- if (curwin->w_cursor.lnum != VIsual.lnum) {
- vim_beep(BO_SPELL);
- return;
- }
- badlen = (int)curwin->w_cursor.col - (int)VIsual.col;
- if (badlen < 0) {
- badlen = -badlen;
- } else {
- curwin->w_cursor.col = VIsual.col;
- }
- badlen++;
- end_visual_mode();
- } else
- // Find the start of the badly spelled word.
- if (spell_move_to(curwin, FORWARD, true, true, NULL) == 0
- || curwin->w_cursor.col > prev_cursor.col) {
- // No bad word or it starts after the cursor: use the word under the
- // cursor.
- curwin->w_cursor = prev_cursor;
- line = get_cursor_line_ptr();
- p = line + curwin->w_cursor.col;
- // Backup to before start of word.
- while (p > line && spell_iswordp_nmw(p, curwin)) {
- MB_PTR_BACK(line, p);
- }
- // Forward to start of word.
- while (*p != NUL && !spell_iswordp_nmw(p, curwin)) {
- MB_PTR_ADV(p);
- }
-
- if (!spell_iswordp_nmw(p, curwin)) { // No word found.
- beep_flush();
- return;
- }
- curwin->w_cursor.col = (colnr_T)(p - line);
- }
-
- // Get the word and its length.
-
- // Figure out if the word should be capitalised.
- need_cap = check_need_cap(curwin->w_cursor.lnum, curwin->w_cursor.col);
-
- // Make a copy of current line since autocommands may free the line.
- line = vim_strsave(get_cursor_line_ptr());
-
- // Get the list of suggestions. Limit to 'lines' - 2 or the number in
- // 'spellsuggest', whatever is smaller.
- if (sps_limit > Rows - 2) {
- limit = Rows - 2;
- } else {
- limit = sps_limit;
- }
- spell_find_suggest(line + curwin->w_cursor.col, badlen, &sug, limit,
- true, need_cap, true);
-
- if (GA_EMPTY(&sug.su_ga)) {
- msg(_("Sorry, no suggestions"));
- } else if (count > 0) {
- if (count > sug.su_ga.ga_len) {
- smsg(_("Sorry, only %" PRId64 " suggestions"),
- (int64_t)sug.su_ga.ga_len);
- }
- } else {
- // When 'rightleft' is set the list is drawn right-left.
- cmdmsg_rl = curwin->w_p_rl;
- if (cmdmsg_rl) {
- msg_col = Columns - 1;
- }
-
- // List the suggestions.
- msg_start();
- msg_row = Rows - 1; // for when 'cmdheight' > 1
- lines_left = Rows; // avoid more prompt
- vim_snprintf((char *)IObuff, IOSIZE, _("Change \"%.*s\" to:"),
- sug.su_badlen, sug.su_badptr);
- if (cmdmsg_rl && STRNCMP(IObuff, "Change", 6) == 0) {
- // And now the rabbit from the high hat: Avoid showing the
- // untranslated message rightleft.
- vim_snprintf((char *)IObuff, IOSIZE, ":ot \"%.*s\" egnahC",
- sug.su_badlen, sug.su_badptr);
- }
- msg_puts((const char *)IObuff);
- msg_clr_eos();
- msg_putchar('\n');
-
- msg_scroll = TRUE;
- for (int i = 0; i < sug.su_ga.ga_len; ++i) {
- stp = &SUG(sug.su_ga, i);
-
- // The suggested word may replace only part of the bad word, add
- // the not replaced part. But only when it's not getting too long.
- STRLCPY(wcopy, stp->st_word, MAXWLEN + 1);
- int el = sug.su_badlen - stp->st_orglen;
- if (el > 0 && stp->st_wordlen + el <= MAXWLEN) {
- STRLCPY(wcopy + stp->st_wordlen, sug.su_badptr + stp->st_orglen, el + 1);
- }
- vim_snprintf((char *)IObuff, IOSIZE, "%2d", i + 1);
- if (cmdmsg_rl) {
- rl_mirror(IObuff);
- }
- msg_puts((const char *)IObuff);
-
- vim_snprintf((char *)IObuff, IOSIZE, " \"%s\"", wcopy);
- msg_puts((const char *)IObuff);
-
- // The word may replace more than "su_badlen".
- if (sug.su_badlen < stp->st_orglen) {
- vim_snprintf((char *)IObuff, IOSIZE, _(" < \"%.*s\""),
- stp->st_orglen, sug.su_badptr);
- msg_puts((const char *)IObuff);
- }
-
- if (p_verbose > 0) {
- // Add the score.
- if (sps_flags & (SPS_DOUBLE | SPS_BEST)) {
- vim_snprintf((char *)IObuff, IOSIZE, " (%s%d - %d)",
- stp->st_salscore ? "s " : "",
- stp->st_score, stp->st_altscore);
- } else {
- vim_snprintf((char *)IObuff, IOSIZE, " (%d)",
- stp->st_score);
- }
- if (cmdmsg_rl) {
- // Mirror the numbers, but keep the leading space.
- rl_mirror(IObuff + 1);
- }
- msg_advance(30);
- msg_puts((const char *)IObuff);
- }
- msg_putchar('\n');
- }
-
- cmdmsg_rl = FALSE;
- msg_col = 0;
- // Ask for choice.
- selected = prompt_for_number(&mouse_used);
-
- if (ui_has(kUIMessages)) {
- ui_call_msg_clear();
- }
-
- if (mouse_used) {
- selected -= lines_left;
- }
- lines_left = Rows; // avoid more prompt
- // don't delay for 'smd' in normal_cmd()
- msg_scroll = msg_scroll_save;
- }
-
- if (selected > 0 && selected <= sug.su_ga.ga_len && u_save_cursor() == OK) {
- // Save the from and to text for :spellrepall.
- XFREE_CLEAR(repl_from);
- XFREE_CLEAR(repl_to);
-
- stp = &SUG(sug.su_ga, selected - 1);
- if (sug.su_badlen > stp->st_orglen) {
- // Replacing less than "su_badlen", append the remainder to
- // repl_to.
- repl_from = vim_strnsave(sug.su_badptr, (size_t)sug.su_badlen);
- vim_snprintf((char *)IObuff, IOSIZE, "%s%.*s", stp->st_word,
- sug.su_badlen - stp->st_orglen,
- sug.su_badptr + stp->st_orglen);
- repl_to = vim_strsave(IObuff);
- } else {
- // Replacing su_badlen or more, use the whole word.
- repl_from = vim_strnsave(sug.su_badptr, (size_t)stp->st_orglen);
- repl_to = vim_strsave(stp->st_word);
- }
-
- // Replace the word.
- p = xmalloc(STRLEN(line) - (size_t)stp->st_orglen + (size_t)stp->st_wordlen + 1);
- c = (int)(sug.su_badptr - line);
- memmove(p, line, (size_t)c);
- STRCPY(p + c, stp->st_word);
- STRCAT(p, sug.su_badptr + stp->st_orglen);
-
- // For redo we use a change-word command.
- ResetRedobuff();
- AppendToRedobuff("ciw");
- AppendToRedobuffLit((char *)p + c,
- stp->st_wordlen + sug.su_badlen - stp->st_orglen);
- AppendCharToRedobuff(ESC);
-
- // "p" may be freed here
- ml_replace(curwin->w_cursor.lnum, (char *)p, false);
- curwin->w_cursor.col = c;
-
- inserted_bytes(curwin->w_cursor.lnum, c, stp->st_orglen, stp->st_wordlen);
- } else {
- curwin->w_cursor = prev_cursor;
- }
-
- spell_find_cleanup(&sug);
- xfree(line);
- curwin->w_p_spell = wo_spell_save;
-}
-
// Check if the word at line "lnum" column "col" is required to start with a
// capital. This uses 'spellcapcheck' of the current buffer.
-static bool check_need_cap(linenr_T lnum, colnr_T col)
+bool check_need_cap(linenr_T lnum, colnr_T col)
{
bool need_cap = false;
char_u *line;
@@ -3195,336 +2625,6 @@ void ex_spellrepall(exarg_T *eap)
}
}
-/// Find spell suggestions for "word". Return them in the growarray "*gap" as
-/// a list of allocated strings.
-///
-/// @param maxcount maximum nr of suggestions
-/// @param need_cap 'spellcapcheck' matched
-void spell_suggest_list(garray_T *gap, char_u *word, int maxcount, bool need_cap, bool interactive)
-{
- suginfo_T sug;
- suggest_T *stp;
- char_u *wcopy;
-
- spell_find_suggest(word, 0, &sug, maxcount, false, need_cap, interactive);
-
- // Make room in "gap".
- ga_init(gap, sizeof(char_u *), sug.su_ga.ga_len + 1);
- ga_grow(gap, sug.su_ga.ga_len);
- for (int i = 0; i < sug.su_ga.ga_len; ++i) {
- stp = &SUG(sug.su_ga, i);
-
- // The suggested word may replace only part of "word", add the not
- // replaced part.
- wcopy = xmalloc((size_t)stp->st_wordlen + STRLEN(sug.su_badptr + stp->st_orglen) + 1);
- STRCPY(wcopy, stp->st_word);
- STRCPY(wcopy + stp->st_wordlen, sug.su_badptr + stp->st_orglen);
- ((char_u **)gap->ga_data)[gap->ga_len++] = wcopy;
- }
-
- spell_find_cleanup(&sug);
-}
-
-/// Find spell suggestions for the word at the start of "badptr".
-/// Return the suggestions in "su->su_ga".
-/// The maximum number of suggestions is "maxcount".
-/// Note: does use info for the current window.
-/// This is based on the mechanisms of Aspell, but completely reimplemented.
-///
-/// @param badlen length of bad word or 0 if unknown
-/// @param banbadword don't include badword in suggestions
-/// @param need_cap word should start with capital
-static void spell_find_suggest(char_u *badptr, int badlen, suginfo_T *su, int maxcount,
- bool banbadword, bool need_cap, bool interactive)
-{
- hlf_T attr = HLF_COUNT;
- char_u buf[MAXPATHL];
- char *p;
- bool do_combine = false;
- char_u *sps_copy;
- static bool expr_busy = false;
- int c;
- langp_T *lp;
- bool did_intern = false;
-
- // Set the info in "*su".
- CLEAR_POINTER(su);
- ga_init(&su->su_ga, (int)sizeof(suggest_T), 10);
- ga_init(&su->su_sga, (int)sizeof(suggest_T), 10);
- if (*badptr == NUL) {
- return;
- }
- hash_init(&su->su_banned);
-
- su->su_badptr = badptr;
- if (badlen != 0) {
- su->su_badlen = badlen;
- } else {
- size_t tmplen = spell_check(curwin, su->su_badptr, &attr, NULL, false);
- assert(tmplen <= INT_MAX);
- su->su_badlen = (int)tmplen;
- }
- su->su_maxcount = maxcount;
- su->su_maxscore = SCORE_MAXINIT;
-
- if (su->su_badlen >= MAXWLEN) {
- su->su_badlen = MAXWLEN - 1; // just in case
- }
- STRLCPY(su->su_badword, su->su_badptr, su->su_badlen + 1);
- (void)spell_casefold(curwin, su->su_badptr, su->su_badlen, su->su_fbadword,
- MAXWLEN);
-
- // TODO(vim): make this work if the case-folded text is longer than the
- // original text. Currently an illegal byte causes wrong pointer
- // computations.
- su->su_fbadword[su->su_badlen] = NUL;
-
- // get caps flags for bad word
- su->su_badflags = badword_captype(su->su_badptr,
- su->su_badptr + su->su_badlen);
- if (need_cap) {
- su->su_badflags |= WF_ONECAP;
- }
-
- // Find the default language for sound folding. We simply use the first
- // one in 'spelllang' that supports sound folding. That's good for when
- // using multiple files for one language, it's not that bad when mixing
- // languages (e.g., "pl,en").
- for (int i = 0; i < curbuf->b_s.b_langp.ga_len; ++i) {
- lp = LANGP_ENTRY(curbuf->b_s.b_langp, i);
- if (lp->lp_sallang != NULL) {
- su->su_sallang = lp->lp_sallang;
- break;
- }
- }
-
- // Soundfold the bad word with the default sound folding, so that we don't
- // have to do this many times.
- if (su->su_sallang != NULL) {
- spell_soundfold(su->su_sallang, su->su_fbadword, true,
- su->su_sal_badword);
- }
-
- // If the word is not capitalised and spell_check() doesn't consider the
- // word to be bad then it might need to be capitalised. Add a suggestion
- // for that.
- c = utf_ptr2char((char *)su->su_badptr);
- if (!SPELL_ISUPPER(c) && attr == HLF_COUNT) {
- make_case_word(su->su_badword, buf, WF_ONECAP);
- add_suggestion(su, &su->su_ga, buf, su->su_badlen, SCORE_ICASE,
- 0, true, su->su_sallang, false);
- }
-
- // Ban the bad word itself. It may appear in another region.
- if (banbadword) {
- add_banned(su, su->su_badword);
- }
-
- // Make a copy of 'spellsuggest', because the expression may change it.
- sps_copy = vim_strsave(p_sps);
-
- // Loop over the items in 'spellsuggest'.
- for (p = (char *)sps_copy; *p != NUL;) {
- copy_option_part(&p, (char *)buf, MAXPATHL, ",");
-
- if (STRNCMP(buf, "expr:", 5) == 0) {
- // Evaluate an expression. Skip this when called recursively,
- // when using spellsuggest() in the expression.
- if (!expr_busy) {
- expr_busy = true;
- spell_suggest_expr(su, buf + 5);
- expr_busy = false;
- }
- } else if (STRNCMP(buf, "file:", 5) == 0) {
- // Use list of suggestions in a file.
- spell_suggest_file(su, buf + 5);
- } else if (!did_intern) {
- // Use internal method once.
- spell_suggest_intern(su, interactive);
- if (sps_flags & SPS_DOUBLE) {
- do_combine = true;
- }
- did_intern = true;
- }
- }
-
- xfree(sps_copy);
-
- if (do_combine) {
- // Combine the two list of suggestions. This must be done last,
- // because sorting changes the order again.
- score_combine(su);
- }
-}
-
-// Find suggestions by evaluating expression "expr".
-static void spell_suggest_expr(suginfo_T *su, char_u *expr)
-{
- int score;
- const char *p;
-
- // The work is split up in a few parts to avoid having to export
- // suginfo_T.
- // First evaluate the expression and get the resulting list.
- list_T *const list = eval_spell_expr((char *)su->su_badword, (char *)expr);
- if (list != NULL) {
- // Loop over the items in the list.
- TV_LIST_ITER(list, li, {
- if (TV_LIST_ITEM_TV(li)->v_type == VAR_LIST) {
- // Get the word and the score from the items.
- score = get_spellword(TV_LIST_ITEM_TV(li)->vval.v_list, &p);
- if (score >= 0 && sc