diff options
Diffstat (limited to 'src/nvim/spell.c')
-rw-r--r-- | src/nvim/spell.c | 652 |
1 files changed, 350 insertions, 302 deletions
diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 1259736e0e..48aed9c6de 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -50,72 +50,73 @@ // Use SPELL_PRINTTREE for debugging: dump the word tree after adding a word. // Only use it for small word lists! -// Use SPELL_COMPRESS_ALLWAYS for debugging: compress the word tree after +// Use SPELL_COMPRESS_ALWAYS for debugging: compress the word tree after // adding a word. Only use it for small word lists! // Use DEBUG_TRIEWALK to print the changes made in suggest_trie_walk() for a // specific word. -#include <assert.h> // for assert -#include <inttypes.h> // for uint32_t, uint16_t, uint8_t -#include <limits.h> // for INT_MAX -#include <stdbool.h> // for false, true, bool -#include <stddef.h> // for NULL, size_t, ptrdiff_t -#include <stdio.h> // for snprintf -#include <string.h> // for memmove, strstr, memcpy, memset - -#include "nvim/ascii.h" // for NUL, ascii_isdigit, ascii_iswhite -#include "nvim/autocmd.h" // for apply_autocmds -#include "nvim/buffer.h" // for bufref_valid, set_bufref, buf_is_empty -#include "nvim/buffer_defs.h" // for win_T, synblock_T, buf_T, w_p_... -#include "nvim/change.h" // for changed_bytes -#include "nvim/charset.h" // for skipwhite, getwhitecols, skipbin -#include "nvim/cursor.h" // for get_cursor_line_ptr -#include "nvim/drawscreen.h" // for NOT_VALID, redraw_later -#include "nvim/eval/typval.h" // for semsg -#include "nvim/ex_cmds.h" // for do_sub_msg -#include "nvim/ex_cmds_defs.h" // for exarg_T -#include "nvim/ex_docmd.h" // for do_cmdline_cmd -#include "nvim/garray.h" // for garray_T, GA_EMPTY, GA_APPEND_... -#include "nvim/gettext.h" // for _, N_ -#include "nvim/hashtab.h" // for hash_clear_all, hash_init, has... -#include "nvim/highlight_defs.h" // for HLF_COUNT, hlf_T, HLF_SPB, HLF... -#include "nvim/insexpand.h" // for ins_compl_add_infercase, ins_c... -#include "nvim/log.h" // for ELOG -#include "nvim/macros.h" // for MB_PTR_ADV, MB_PTR_BACK, ASCII... -#include "nvim/mark.h" // for clearpos -#include "nvim/mbyte.h" // for utf_ptr2char, utf_char2bytes -#include "nvim/memline.h" // for ml_append, ml_get_buf, ml_close -#include "nvim/memline_defs.h" // for memline_T -#include "nvim/memory.h" // for xfree, xmalloc, xcalloc, xstrdup -#include "nvim/message.h" // for emsg, msg_puts, give_warning -#include "nvim/option.h" // for copy_option_part, set_option_v... -#include "nvim/option_defs.h" // for p_ws, OPT_LOCAL, p_enc, SHM_SE... -#include "nvim/os/fs.h" // for os_remove -#include "nvim/os/input.h" // for line_breakcheck -#include "nvim/os/os_defs.h" // for MAXPATHL -#include "nvim/path.h" // for path_full_compare, path_tail... -#include "nvim/pos.h" // for pos_T, colnr_T, linenr_T -#include "nvim/regexp.h" // for vim_regfree, vim_regexec, vim_... -#include "nvim/regexp_defs.h" // for regmatch_T, regprog_T -#include "nvim/runtime.h" // for DIP_ALL, do_in_runtimepath -#include "nvim/search.h" // for SEARCH_KEEP, for do_search -#include "nvim/spell.h" // for FUNC_ATTR_NONNULL_ALL, FUNC_AT... -#include "nvim/spell_defs.h" // for slang_T, langp_T, MAXWLEN, sal... -#include "nvim/spellfile.h" // for spell_load_file -#include "nvim/spellsuggest.h" // for spell_suggest_list -#include "nvim/strings.h" // for vim_strchr, vim_snprintf, conc... -#include "nvim/syntax.h" // for syn_get_id, syntax_present -#include "nvim/types.h" // for char_u -#include "nvim/undo.h" // for u_save_cursor -#include "nvim/vim.h" // for curwin, STRLEN, STRLCPY, STRNCMP +#include <assert.h> +#include <inttypes.h> +#include <limits.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> + +#include "nvim/ascii.h" +#include "nvim/autocmd.h" +#include "nvim/buffer.h" +#include "nvim/change.h" +#include "nvim/charset.h" +#include "nvim/cursor.h" +#include "nvim/decoration.h" +#include "nvim/decoration_provider.h" +#include "nvim/drawscreen.h" +#include "nvim/ex_cmds.h" +#include "nvim/ex_cmds_defs.h" +#include "nvim/ex_docmd.h" +#include "nvim/garray.h" +#include "nvim/gettext.h" +#include "nvim/globals.h" +#include "nvim/hashtab.h" +#include "nvim/highlight_defs.h" +#include "nvim/insexpand.h" +#include "nvim/log.h" +#include "nvim/macros.h" +#include "nvim/mark.h" +#include "nvim/mbyte.h" +#include "nvim/memline.h" +#include "nvim/memory.h" +#include "nvim/message.h" +#include "nvim/option.h" +#include "nvim/os/fs.h" +#include "nvim/os/input.h" +#include "nvim/os/os_defs.h" +#include "nvim/path.h" +#include "nvim/pos.h" +#include "nvim/regexp.h" +#include "nvim/runtime.h" +#include "nvim/search.h" +#include "nvim/spell.h" +#include "nvim/spell_defs.h" +#include "nvim/spellfile.h" +#include "nvim/spellsuggest.h" +#include "nvim/strings.h" +#include "nvim/syntax.h" +#include "nvim/types.h" +#include "nvim/undo.h" +#include "nvim/vim.h" +#include "nvim/window.h" // Result values. Lower number is accepted over higher one. -#define SP_BANNED (-1) -#define SP_RARE 0 -#define SP_OK 1 -#define SP_LOCAL 2 -#define SP_BAD 3 +enum { + SP_BANNED = -1, + SP_RARE = 0, + SP_OK = 1, + SP_LOCAL = 2, + SP_BAD = 3, +}; // First language that is loaded, start of the linked list of loaded // languages. @@ -129,14 +130,14 @@ typedef struct matchinf_S { langp_T *mi_lp; // info for language and region // pointers to original text to be checked - char_u *mi_word; // start of word being checked + char *mi_word; // start of word being checked char_u *mi_end; // end of matching word so far char_u *mi_fend; // next char to be added to mi_fword char_u *mi_cend; // char after what was used for // mi_capflags // case-folded text - char_u mi_fword[MAXWLEN + 1]; // mi_word case-folded + char mi_fword[MAXWLEN + 1]; // mi_word case-folded int mi_fwordlen; // nr of valid bytes in mi_fword // for when checking word after a prefix @@ -159,7 +160,7 @@ typedef struct matchinf_S { win_T *mi_win; // buffer being checked // for NOBREAK - int mi_result2; // "mi_resul" without following word + int mi_result2; // "mi_result" without following word char_u *mi_end2; // "mi_end" without following word } matchinf_T; @@ -172,7 +173,7 @@ typedef struct spelload_S { #define SY_MAXLEN 30 typedef struct syl_item_S { - char_u sy_chars[SY_MAXLEN]; // the sequence of chars + char sy_chars[SY_MAXLEN]; // the sequence of chars int sy_len; } syl_item_T; @@ -193,8 +194,8 @@ int did_set_spelltab; char *e_format = N_("E759: Format error in spell file"); // Remember what "z?" replaced. -char_u *repl_from = NULL; -char_u *repl_to = NULL; +char *repl_from = NULL; +char *repl_to = NULL; /// Main spell-checking function. /// "ptr" points to a character that could be the start of a word. @@ -220,7 +221,7 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou size_t nrlen = 0; // found a number first size_t wrongcaplen = 0; bool count_word = docount; - bool use_camel_case = *wp->w_s->b_p_spo != NUL; + bool use_camel_case = (wp->w_s->b_p_spo_flags & SPO_CAMEL) != 0; bool camel_case = false; // A word never starts at a space or a control character. Return quickly @@ -251,7 +252,7 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou } // Find the normal end of the word (until the next non-word character). - mi.mi_word = ptr; + mi.mi_word = (char *)ptr; mi.mi_fend = ptr; if (spell_iswordp(mi.mi_fend, wp)) { bool this_upper = false; // init for gcc @@ -299,9 +300,9 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou MB_PTR_ADV(mi.mi_fend); } - (void)spell_casefold(wp, ptr, (int)(mi.mi_fend - ptr), mi.mi_fword, + (void)spell_casefold(wp, ptr, (int)(mi.mi_fend - ptr), (char_u *)mi.mi_fword, MAXWLEN + 1); - mi.mi_fwordlen = (int)STRLEN(mi.mi_fword); + mi.mi_fwordlen = (int)strlen(mi.mi_fword); if (camel_case && mi.mi_fwordlen > 0) { // introduce a fake word end space into the folded word. @@ -343,7 +344,7 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou // Count the word in the first language where it's found to be OK. if (count_word && mi.mi_result == SP_OK) { - count_common_word(mi.mi_lp->lp_slang, ptr, + count_common_word(mi.mi_lp->lp_slang, (char *)ptr, (int)(mi.mi_end - ptr), 1); count_word = false; } @@ -368,7 +369,7 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou int r = vim_regexec(®match, (char *)ptr, 0); wp->w_s->b_cap_prog = regmatch.regprog; if (r) { - *capcol = (int)(regmatch.endp[0] - ptr); + *capcol = (int)(regmatch.endp[0] - (char *)ptr); } } @@ -386,15 +387,15 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou // at which any word would be valid. mi.mi_lp = LANGP_ENTRY(wp->w_s->b_langp, 0); if (mi.mi_lp->lp_slang->sl_fidxs != NULL) { - p = mi.mi_word; - fp = mi.mi_fword; + p = (char_u *)mi.mi_word; + fp = (char_u *)mi.mi_fword; for (;;) { MB_PTR_ADV(p); MB_PTR_ADV(fp); if (p >= mi.mi_end) { break; } - mi.mi_compoff = (int)(fp - mi.mi_fword); + mi.mi_compoff = (int)(fp - (char_u *)mi.mi_fword); find_word(&mi, FIND_COMPOUND); if (mi.mi_result != SP_BAD) { mi.mi_end = p; @@ -434,7 +435,7 @@ static void find_word(matchinf_T *mip, int mode) { int wlen = 0; int flen; - char_u *ptr; + char *ptr; slang_T *slang = mip->mi_lp->lp_slang; char_u *byts; idx_T *idxs; @@ -517,7 +518,7 @@ static void find_word(matchinf_T *mip, int mode) } // Perform a binary search in the list of accepted bytes. - c = ptr[wlen]; + c = (uint8_t)ptr[wlen]; if (c == TAB) { // <Tab> is handled like <Space> c = ' '; } @@ -561,7 +562,7 @@ static void find_word(matchinf_T *mip, int mode) } } - char_u *p; + char *p; bool word_ends; // Verify that one of the possible endings is valid. Try the longest @@ -574,7 +575,7 @@ static void find_word(matchinf_T *mip, int mode) if (utf_head_off(ptr, ptr + wlen) > 0) { continue; // not at first byte of character } - if (spell_iswordp(ptr + wlen, mip->mi_win)) { + if (spell_iswordp((char_u *)ptr + wlen, mip->mi_win)) { if (slang->sl_compprog == NULL && !slang->sl_nobreak) { continue; // next char is a word character } @@ -591,8 +592,8 @@ static void find_word(matchinf_T *mip, int mode) // when folding case. This can be slow, take a shortcut when the // case-folded word is equal to the keep-case word. p = mip->mi_word; - if (STRNCMP(ptr, p, wlen) != 0) { - for (char_u *s = ptr; s < ptr + wlen; MB_PTR_ADV(s)) { + if (strncmp(ptr, p, (size_t)wlen) != 0) { + for (char *s = ptr; s < ptr + wlen; MB_PTR_ADV(s)) { MB_PTR_ADV(p); } wlen = (int)(p - mip->mi_word); @@ -611,11 +612,11 @@ static void find_word(matchinf_T *mip, int mode) // For keep-case tree the case is always right. For prefixes we // don't bother to check. if (mode == FIND_FOLDWORD) { - if (mip->mi_cend != mip->mi_word + wlen) { + if (mip->mi_cend != (char_u *)mip->mi_word + wlen) { // mi_capflags was set for a different word length, need // to do it again. - mip->mi_cend = mip->mi_word + wlen; - mip->mi_capflags = captype(mip->mi_word, mip->mi_cend); + mip->mi_cend = (char_u *)mip->mi_word + wlen; + mip->mi_capflags = captype((char_u *)mip->mi_word, mip->mi_cend); } if (mip->mi_capflags == WF_KEEPCAP @@ -628,7 +629,7 @@ static void find_word(matchinf_T *mip, int mode) // mip->mi_prefarridx that find_prefix() filled. c = valid_word_prefix(mip->mi_prefcnt, mip->mi_prefarridx, (int)flags, - mip->mi_word + mip->mi_cprefixlen, slang, + (char_u *)mip->mi_word + mip->mi_cprefixlen, slang, false); if (c == 0) { continue; @@ -663,7 +664,7 @@ static void find_word(matchinf_T *mip, int mode) // For multi-byte chars check character length against // COMPOUNDMIN. if (slang->sl_compminlen > 0 - && mb_charlen_len(mip->mi_word + mip->mi_compoff, + && mb_charlen_len((char_u *)mip->mi_word + mip->mi_compoff, wlen - mip->mi_compoff) < slang->sl_compminlen) { continue; } @@ -702,16 +703,16 @@ static void find_word(matchinf_T *mip, int mode) // Need to check the caps type of the appended compound // word. - if (STRNCMP(ptr, mip->mi_word, mip->mi_compoff) != 0) { + if (strncmp(ptr, mip->mi_word, (size_t)mip->mi_compoff) != 0) { // case folding may have changed the length p = mip->mi_word; - for (char_u *s = ptr; s < ptr + mip->mi_compoff; MB_PTR_ADV(s)) { + for (char *s = ptr; s < ptr + mip->mi_compoff; MB_PTR_ADV(s)) { MB_PTR_ADV(p); } } else { p = mip->mi_word + mip->mi_compoff; } - capflags = captype(p, mip->mi_word + wlen); + capflags = captype((char_u *)p, (char_u *)mip->mi_word + wlen); if (capflags == WF_KEEPCAP || (capflags == WF_ALLCAP && (flags & WF_FIXCAP) != 0)) { continue; @@ -723,7 +724,7 @@ static void find_word(matchinf_T *mip, int mode) // accept a no-caps word, even when the dictionary // word specifies ONECAP. MB_PTR_BACK(mip->mi_word, p); - if (spell_iswordp_nmw(p, mip->mi_win) + if (spell_iswordp_nmw((char_u *)p, mip->mi_win) ? capflags == WF_ONECAP : (flags & WF_ONECAP) != 0 && capflags != WF_ONECAP) { @@ -738,17 +739,17 @@ static void find_word(matchinf_T *mip, int mode) mip->mi_compflags[mip->mi_complen] = (char_u)((unsigned)flags >> 24); mip->mi_compflags[mip->mi_complen + 1] = NUL; if (word_ends) { - char_u fword[MAXWLEN] = { 0 }; + char fword[MAXWLEN] = { 0 }; if (slang->sl_compsylmax < MAXWLEN) { // "fword" is only needed for checking syllables. if (ptr == mip->mi_word) { - (void)spell_casefold(mip->mi_win, ptr, wlen, fword, MAXWLEN); + (void)spell_casefold(mip->mi_win, (char_u *)ptr, wlen, (char_u *)fword, MAXWLEN); } else { - STRLCPY(fword, ptr, endlen[endidxcnt] + 1); + xstrlcpy(fword, ptr, (size_t)endlen[endidxcnt] + 1); } } - if (!can_compound(slang, fword, mip->mi_compflags)) { + if (!can_compound(slang, (char *)fword, mip->mi_compflags)) { continue; } } else if (slang->sl_comprules != NULL @@ -786,8 +787,8 @@ static void find_word(matchinf_T *mip, int mode) // folding case. This can be slow, take a shortcut when // the case-folded word is equal to the keep-case word. p = mip->mi_fword; - if (STRNCMP(ptr, p, wlen) != 0) { - for (char_u *s = ptr; s < ptr + wlen; MB_PTR_ADV(s)) { + if (strncmp(ptr, p, (size_t)wlen) != 0) { + for (char *s = ptr; s < ptr + wlen; MB_PTR_ADV(s)) { MB_PTR_ADV(p); } mip->mi_compoff = (int)(p - mip->mi_fword); @@ -877,16 +878,16 @@ static void find_word(matchinf_T *mip, int mode) if (nobreak_result == SP_BAD) { if (mip->mi_result2 > res) { mip->mi_result2 = res; - mip->mi_end2 = mip->mi_word + wlen; + mip->mi_end2 = (char_u *)mip->mi_word + wlen; } else if (mip->mi_result2 == res - && mip->mi_end2 < mip->mi_word + wlen) { - mip->mi_end2 = mip->mi_word + wlen; + && mip->mi_end2 < (char_u *)mip->mi_word + wlen) { + mip->mi_end2 = (char_u *)mip->mi_word + wlen; } } else if (mip->mi_result > res) { mip->mi_result = res; - mip->mi_end = mip->mi_word + wlen; - } else if (mip->mi_result == res && mip->mi_end < mip->mi_word + wlen) { - mip->mi_end = mip->mi_word + wlen; + mip->mi_end = (char_u *)mip->mi_word + wlen; + } else if (mip->mi_result == res && mip->mi_end < (char_u *)mip->mi_word + wlen) { + mip->mi_end = (char_u *)mip->mi_word + wlen; } if (mip->mi_result == SP_OK) { @@ -907,16 +908,16 @@ static void find_word(matchinf_T *mip, int mode) /// end of ptr[wlen] and the second part matches after it. /// /// @param gap &sl_comppat -bool match_checkcompoundpattern(char_u *ptr, int wlen, garray_T *gap) +bool match_checkcompoundpattern(char *ptr, int wlen, garray_T *gap) { for (int i = 0; i + 1 < gap->ga_len; i += 2) { - char_u *p = ((char_u **)gap->ga_data)[i + 1]; - if (STRNCMP(ptr + wlen, p, STRLEN(p)) == 0) { + char *p = ((char **)gap->ga_data)[i + 1]; + if (strncmp(ptr + wlen, p, strlen(p)) == 0) { // Second part matches at start of following compound word, now // check if first part matches at end of previous word. - p = ((char_u **)gap->ga_data)[i]; - int len = (int)STRLEN(p); - if (len <= wlen && STRNCMP(ptr + wlen - len, p, len) == 0) { + p = ((char **)gap->ga_data)[i]; + int len = (int)strlen(p); + if (len <= wlen && strncmp(ptr + wlen - len, p, (size_t)len) == 0) { return true; } } @@ -924,9 +925,9 @@ bool match_checkcompoundpattern(char_u *ptr, int wlen, garray_T *gap) return false; } -// Returns true if "flags" is a valid sequence of compound flags and "word" -// does not have too many syllables. -bool can_compound(slang_T *slang, const char_u *word, const char_u *flags) +/// @return true if "flags" is a valid sequence of compound flags and "word" +/// does not have too many syllables. +bool can_compound(slang_T *slang, const char *word, const uint8_t *flags) FUNC_ATTR_NONNULL_ALL { char_u uflags[MAXWLEN * 2] = { 0 }; @@ -949,8 +950,8 @@ bool can_compound(slang_T *slang, const char_u *word, const char_u *flags) // are too many syllables AND the number of compound words is above // COMPOUNDWORDMAX then compounding is not allowed. if (slang->sl_compsylmax < MAXWLEN - && count_syllables(slang, word) > slang->sl_compsylmax) { - return (int)STRLEN(flags) < slang->sl_compmax; + && count_syllables(slang, (char_u *)word) > slang->sl_compsylmax) { + return (int)strlen((char *)flags) < slang->sl_compmax; } return true; } @@ -959,7 +960,7 @@ bool can_compound(slang_T *slang, const char_u *word, const char_u *flags) // 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. -bool match_compoundrule(slang_T *slang, char_u *compflags) +bool match_compoundrule(slang_T *slang, const char_u *compflags) { // loop over all the COMPOUNDRULE entries for (char_u *p = slang->sl_comprules; *p != NUL; p++) { @@ -1066,7 +1067,7 @@ static void find_prefix(matchinf_T *mip, int mode) } // We use the case-folded word here, since prefixes are always // case-folded. - char_u *ptr = mip->mi_fword; + char_u *ptr = (char_u *)mip->mi_fword; int flen = mip->mi_fwordlen; // available case-folded bytes if (mode == FIND_COMPOUND) { // Skip over the previously found word(s). @@ -1109,8 +1110,8 @@ static void find_prefix(matchinf_T *mip, int mode) } // Case-folded length may differ from original length. - mip->mi_cprefixlen = nofold_len(mip->mi_fword, mip->mi_prefixlen, - mip->mi_word); + mip->mi_cprefixlen = nofold_len((char_u *)mip->mi_fword, mip->mi_prefixlen, + (char_u *)mip->mi_word); find_word(mip, FIND_PREFIX); if (len == 0) { @@ -1167,9 +1168,9 @@ static int fold_more(matchinf_T *mip) } (void)spell_casefold(mip->mi_win, p, (int)(mip->mi_fend - p), - mip->mi_fword + mip->mi_fwordlen, + (char_u *)mip->mi_fword + mip->mi_fwordlen, MAXWLEN - mip->mi_fwordlen); - int flen = (int)STRLEN(mip->mi_fword + mip->mi_fwordlen); + int flen = (int)strlen(mip->mi_fword + mip->mi_fwordlen); mip->mi_fwordlen += flen; return flen; } @@ -1198,6 +1199,31 @@ bool no_spell_checking(win_T *wp) return false; } +static void decor_spell_nav_start(win_T *wp) +{ + decor_state = (DecorState){ 0 }; + decor_redraw_reset(wp->w_buffer, &decor_state); +} + +static bool decor_spell_nav_col(win_T *wp, linenr_T lnum, linenr_T *decor_lnum, int col, + char **decor_error) +{ + if (*decor_lnum != lnum) { + decor_providers_invoke_spell(wp, lnum - 1, col, lnum - 1, -1, decor_error); + decor_redraw_line(wp->w_buffer, lnum - 1, &decor_state); + *decor_lnum = lnum; + } + decor_redraw_col(wp->w_buffer, col, col, false, &decor_state); + return decor_state.spell == kTrue; +} + +static inline bool can_syn_spell(win_T *wp, linenr_T lnum, int col) +{ + bool can_spell; + (void)syn_get_id(wp, lnum, col, false, &can_spell, false); + return can_spell; +} + /// Moves to the next spell error. /// "curline" is false for "[s", "]s", "[S" and "]S". /// "curline" is true to find word under/after cursor in the same line. @@ -1216,11 +1242,11 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att hlf_T attr = HLF_COUNT; size_t len; int has_syntax = syntax_present(wp); - int col; - char_u *buf = NULL; + colnr_T col; + char *buf = NULL; size_t buflen = 0; int skip = 0; - int capcol = -1; + colnr_T capcol = -1; bool found_one = false; bool wrapped = false; @@ -1228,6 +1254,8 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att return 0; } + size_t ret = 0; + // Start looking for bad word at the start of the line, because we can't // start halfway through a word, we don't know where it starts or ends. // @@ -1240,10 +1268,23 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att linenr_T lnum = wp->w_cursor.lnum; clearpos(&found_pos); + char *decor_error = NULL; + // Ephemeral extmarks are currently stored in the global decor_state. + // When looking for spell errors, we need to: + // - temporarily reset decor_state + // - run the _on_spell_nav decor callback for each line we look at + // - detect if any spell marks are present + // - restore decor_state to the value saved here. + // TODO(lewis6991): un-globalize decor_state and allow ephemeral marks to be stored into a + // temporary DecorState. + DecorState saved_decor_start = decor_state; + linenr_T decor_lnum = -1; + decor_spell_nav_start(wp); + while (!got_int) { - char_u *line = ml_get_buf(wp->w_buffer, lnum, false); + char *line = ml_get_buf(wp->w_buffer, lnum, false); - len = STRLEN(line); + len = strlen(line); if (buflen < len + MAXWLEN + 2) { xfree(buf); buflen = len + MAXWLEN + 2; @@ -1258,10 +1299,10 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att // For checking first word with a capital skip white space. if (capcol == 0) { - capcol = (int)getwhitecols((char *)line); + capcol = (colnr_T)getwhitecols(line); } else if (curline && wp == curwin) { // For spellbadword(): check if first word needs a capital. - col = (int)getwhitecols((char *)line); + col = (colnr_T)getwhitecols(line); if (check_need_cap(lnum, col)) { capcol = col; } @@ -1277,12 +1318,12 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att bool empty_line = *skipwhite((const char *)line) == NUL; STRCPY(buf, line); if (lnum < wp->w_buffer->b_ml.ml_line_count) { - spell_cat_line(buf + STRLEN(buf), - ml_get_buf(wp->w_buffer, lnum + 1, false), + spell_cat_line(buf + strlen(buf), + (char_u *)ml_get_buf(wp->w_buffer, lnum + 1, false), MAXWLEN); } - char_u *p = buf + skip; - char_u *endp = buf + len; + char *p = buf + skip; + char *endp = buf + len; while (p < endp) { // When searching backward don't search after the cursor. Unless // we wrapped around the end of the buffer. @@ -1295,7 +1336,7 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att // start of word attr = HLF_COUNT; - len = spell_check(wp, p, &attr, &capcol, false); + len = spell_check(wp, (char_u *)p, &attr, &capcol, false); if (attr != HLF_COUNT) { // We found a bad word. Check the attribute. @@ -1308,33 +1349,31 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att || ((colnr_T)(curline ? p - buf + (ptrdiff_t)len : p - buf) > wp->w_cursor.col)) { - bool can_spell; - if (has_syntax) { - col = (int)(p - buf); - (void)syn_get_id(wp, lnum, (colnr_T)col, - false, &can_spell, false); - if (!can_spell) { - attr = HLF_COUNT; - } - } else { - can_spell = true; + col = (colnr_T)(p - buf); + + bool can_spell = (!has_syntax && (wp->w_s->b_p_spo_flags & SPO_NPBUFFER) == 0) + || decor_spell_nav_col(wp, lnum, &decor_lnum, col, &decor_error) + || (has_syntax && can_syn_spell(wp, lnum, col)); + + if (!can_spell) { + attr = HLF_COUNT; } if (can_spell) { found_one = true; found_pos = (pos_T) { .lnum = lnum, - .col = (int)(p - buf), + .col = col, .coladd = 0 }; if (dir == FORWARD) { // No need to search further. wp->w_cursor = found_pos; - xfree(buf); if (attrp != NULL) { *attrp = attr; } - return len; + ret = len; + goto theend; } else if (curline) { // Insert mode completion: put cursor after // the bad word. @@ -1358,8 +1397,8 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att if (dir == BACKWARD && found_pos.lnum != 0) { // Use the last match in the line (before the cursor). wp->w_cursor = found_pos; - xfree(buf); - return found_len; + ret = found_len; + goto theend; } if (curline) { @@ -1429,15 +1468,19 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att line_breakcheck(); } +theend: + decor_state_free(&decor_state); + xfree(decor_error); + decor_state = saved_decor_start; xfree(buf); - return 0; + return ret; } // For spell checking: concatenate the start of the following line "line" into // "buf", blanking-out special characters. Copy less than "maxlen" bytes. // Keep the blanks at the start of the next line, this is used in win_line() // to skip those bytes if the word was OK. -void spell_cat_line(char_u *buf, char_u *line, int maxlen) +void spell_cat_line(char *buf, char_u *line, int maxlen) { char_u *p = (char_u *)skipwhite((char *)line); while (vim_strchr("*#/\"\t", *p) != NULL) { @@ -1450,7 +1493,7 @@ void spell_cat_line(char_u *buf, char_u *line, int maxlen) int n = (int)(p - line) + 1; if (n < maxlen - 1) { memset(buf, ' ', (size_t)n); - STRLCPY(buf + n, p, maxlen - n); + xstrlcpy(buf + n, (char *)p, (size_t)(maxlen - n)); } } } @@ -1469,6 +1512,10 @@ static void spell_load_lang(char_u *lang) sl.sl_slang = NULL; sl.sl_nobreak = false; + // Disallow deleting the current buffer. Autocommands can do weird things + // and cause "lang" to be freed. + curbuf->b_locked++; + // We may retry when no spell file is found for the language, an // autocommand may load it then. for (int round = 1; round <= 2; round++) { @@ -1508,16 +1555,18 @@ static void spell_load_lang(char_u *lang) } } else if (sl.sl_slang != NULL) { // At least one file was loaded, now load ALL the additions. - STRCPY(fname_enc + STRLEN(fname_enc) - 3, "add.spl"); + STRCPY(fname_enc + strlen(fname_enc) - 3, "add.spl"); do_in_runtimepath((char *)fname_enc, DIP_ALL, spell_load_cb, &sl); } + + curbuf->b_locked--; } // Return the encoding used for spell checking: Use 'encoding', except that we // use "latin1" for "latin9". And limit to 60 characters (just in case). char_u *spell_enc(void) { - if (STRLEN(p_enc) < 60 && STRCMP(p_enc, "iso-8859-15") != 0) { + if (strlen(p_enc) < 60 && strcmp(p_enc, "iso-8859-15") != 0) { return (char_u *)p_enc; } return (char_u *)"latin1"; @@ -1531,15 +1580,15 @@ static void int_wordlist_spl(char_u *fname) int_wordlist, spell_enc()); } -// Allocate a new slang_T for language "lang". "lang" can be NULL. -// Caller must fill "sl_next". -slang_T *slang_alloc(char_u *lang) +/// Allocate a new slang_T for language "lang". "lang" can be NULL. +/// Caller must fill "sl_next". +slang_T *slang_alloc(char *lang) FUNC_ATTR_NONNULL_RET { slang_T *lp = xcalloc(1, sizeof(slang_T)); if (lang != NULL) { - lp->sl_name = vim_strsave(lang); + lp->sl_name = xstrdup(lang); } ga_init(&lp->sl_rep, sizeof(fromto_T), 10); ga_init(&lp->sl_repsal, sizeof(fromto_T), 10); @@ -1651,7 +1700,7 @@ void slang_clear_sug(slang_T *lp) static void spell_load_cb(char *fname, void *cookie) { spelload_T *slp = (spelload_T *)cookie; - slang_T *slang = spell_load_file((char_u *)fname, slp->sl_lang, NULL, false); + slang_T *slang = spell_load_file(fname, (char *)slp->sl_lang, NULL, false); if (slang != NULL) { // When a previously loaded file has NOBREAK also use it for the // ".add" files. @@ -1672,23 +1721,23 @@ static void spell_load_cb(char *fname, void *cookie) /// @param[in] word added to common words hashtable /// @param[in] len length of word or -1 for NUL terminated /// @param[in] count 1 to count once, 10 to init -void count_common_word(slang_T *lp, char_u *word, int len, uint8_t count) +void count_common_word(slang_T *lp, char *word, int len, uint8_t count) { - char_u buf[MAXWLEN]; - char_u *p; + char buf[MAXWLEN]; + char *p; if (len == -1) { p = word; } else if (len >= MAXWLEN) { return; } else { - STRLCPY(buf, word, len + 1); + xstrlcpy(buf, word, (size_t)len + 1); p = buf; } wordcount_T *wc; - hash_T hash = hash_hash(p); - const size_t p_len = STRLEN(p); + hash_T hash = hash_hash((char_u *)p); + const size_t p_len = strlen(p); hashitem_T *hi = hash_lookup(&lp->sl_wordcount, (const char *)p, p_len, hash); if (HASHITEM_EMPTY(hi)) { wc = xmalloc(sizeof(wordcount_T) + p_len); @@ -1721,17 +1770,17 @@ bool byte_in_str(char_u *str, int n) int init_syl_tab(slang_T *slang) { ga_init(&slang->sl_syl_items, sizeof(syl_item_T), 4); - char_u *p = (char_u *)vim_strchr((char *)slang->sl_syllable, '/'); + char *p = vim_strchr((char *)slang->sl_syllable, '/'); while (p != NULL) { *p++ = NUL; if (*p == NUL) { // trailing slash break; } - char_u *s = p; - p = (char_u *)vim_strchr((char *)p, '/'); + char *s = p; + p = vim_strchr(p, '/'); int l; if (p == NULL) { - l = (int)STRLEN(s); + l = (int)strlen(s); } else { l = (int)(p - s); } @@ -1740,7 +1789,7 @@ int init_syl_tab(slang_T *slang) } syl_item_T *syl = GA_APPEND_VIA_PTR(syl_item_T, &slang->sl_syl_items); - STRLCPY(syl->sy_chars, s, l + 1); + xstrlcpy(syl->sy_chars, s, (size_t)l + 1); syl->sy_len = l; } return OK; @@ -1760,7 +1809,7 @@ static int count_syllables(slang_T *slang, const char_u *word) return 0; } - for (const char_u *p = word; *p != NUL; p += len) { + for (const char *p = (char *)word; *p != NUL; p += len) { // When running into a space reset counter. if (*p == ' ') { len = 1; @@ -1773,7 +1822,7 @@ static int count_syllables(slang_T *slang, const char_u *word) for (int i = 0; i < slang->sl_syl_items.ga_len; i++) { syl_item_T *syl = ((syl_item_T *)slang->sl_syl_items.ga_data) + i; if (syl->sy_len > len - && STRNCMP(p, syl->sy_chars, syl->sy_len) == 0) { + && strncmp(p, syl->sy_chars, (size_t)syl->sy_len) == 0) { len = syl->sy_len; } } @@ -1782,8 +1831,8 @@ static int count_syllables(slang_T *slang, const char_u *word) skip = false; } else { // No recognized syllable item, at least a syllable char then? - int c = utf_ptr2char((char *)p); - len = utfc_ptr2len((char *)p); + int c = utf_ptr2char(p); + len = utfc_ptr2len(p); if (vim_strchr((char *)slang->sl_syllable, c) == NULL) { skip = false; // No, search for next syllable } else if (!skip) { @@ -1801,25 +1850,25 @@ char *did_set_spelllang(win_T *wp) { garray_T ga; char *splp; - char_u *region; - char_u region_cp[3]; + char *region; + char region_cp[3]; bool filename; int region_mask; slang_T *slang; int c; - char_u lang[MAXWLEN + 1]; - char_u spf_name[MAXPATHL]; + char lang[MAXWLEN + 1]; + char spf_name[MAXPATHL]; int len; - char_u *p; + char *p; int round; char *spf; - char_u *use_region = NULL; + char *use_region = NULL; bool dont_use_region = false; bool nobreak = false; langp_T *lp, *lp2; static bool recursive = false; char *ret_msg = NULL; - char_u *spl_copy; + char *spl_copy; bufref_T bufref; set_bufref(&bufref, wp->w_buffer); @@ -1837,22 +1886,22 @@ char *did_set_spelllang(win_T *wp) // Make a copy of 'spelllang', the SpellFileMissing autocommands may change // it under our fingers. - spl_copy = vim_strsave((char_u *)wp->w_s->b_p_spl); + spl_copy = xstrdup(wp->w_s->b_p_spl); wp->w_s->b_cjk = 0; // Loop over comma separated language names. - for (splp = (char *)spl_copy; *splp != NUL;) { + for (splp = spl_copy; *splp != NUL;) { // Get one language name. copy_option_part(&splp, (char *)lang, MAXWLEN, ","); region = NULL; - len = (int)STRLEN(lang); + len = (int)strlen(lang); if (!valid_spelllang((char *)lang)) { continue; } - if (STRCMP(lang, "cjk") == 0) { + if (strcmp(lang, "cjk") == 0) { wp->w_s->b_cjk = 1; continue; } @@ -1860,14 +1909,14 @@ char *did_set_spelllang(win_T *wp) // If the name ends in ".spl" use it as the name of the spell file. // If there is a region name let "region" point to it and remove it // from the name. - if (len > 4 && FNAMECMP(lang + len - 4, ".spl") == 0) { + if (len > 4 && path_fnamecmp(lang + len - 4, ".spl") == 0) { filename = true; // Locate a region and remove it from the file name. - p = (char_u *)vim_strchr(path_tail((char *)lang), '_'); + p = vim_strchr(path_tail((char *)lang), '_'); if (p != NULL && ASCII_ISALPHA(p[1]) && ASCII_ISALPHA(p[2]) && !ASCII_ISALPHA(p[3])) { - STRLCPY(region_cp, p + 1, 3); + xstrlcpy(region_cp, p + 1, 3); memmove(p, p + 3, (size_t)(len - (p - lang) - 2)); region = region_cp; } else { @@ -1901,7 +1950,7 @@ char *did_set_spelllang(win_T *wp) if (region != NULL) { // If the region differs from what was used before then don't // use it for 'spellfile'. - if (use_region != NULL && STRCMP(region, use_region) != 0) { + if (use_region != NULL && strcmp(region, use_region) != 0) { dont_use_region = true; } use_region = region; @@ -1910,12 +1959,12 @@ char *did_set_spelllang(win_T *wp) // If not found try loading the language now. if (slang == NULL) { if (filename) { - (void)spell_load_file(lang, lang, NULL, false); + (void)spell_load_file((char *)lang, (char *)lang, NULL, false); } else { - spell_load_lang(lang); + spell_load_lang((char_u *)lang); // SpellFileMissing autocommands may do anything, including - // destroying the buffer we are using... - if (!bufref_valid(&bufref)) { + // destroying the buffer we are using or closing the window. + if (!bufref_valid(&bufref) || !win_valid_any_tab(wp)) { ret_msg = N_("E797: SpellFileMissing autocommand deleted buffer"); goto theend; } @@ -1930,7 +1979,7 @@ char *did_set_spelllang(win_T *wp) region_mask = REGION_ALL; if (!filename && region != NULL) { // find region in sl_regions - c = find_region(slang->sl_regions, region); + c = find_region(slang->sl_regions, (char_u *)region); if (c == REGION_ALL) { if (slang->sl_add) { if (*slang->sl_regions != NUL) { @@ -1973,7 +2022,7 @@ char *did_set_spelllang(win_T *wp) if (int_wordlist == NULL) { continue; } - int_wordlist_spl(spf_name); + int_wordlist_spl((char_u *)spf_name); } else { // One entry in 'spellfile'. copy_option_part(&spf, (char *)spf_name, MAXPATHL - 5, ","); @@ -1981,9 +2030,9 @@ char *did_set_spelllang(win_T *wp) // If it was already found above then skip it. for (c = 0; c < ga.ga_len; c++) { - p = (char_u *)LANGP_ENTRY(ga, c)->lp_slang->sl_fname; + p = LANGP_ENTRY(ga, c)->lp_slang->sl_fname; if (p != NULL - && path_full_compare((char *)spf_name, (char *)p, false, true) == kEqualFiles) { + && path_full_compare((char *)spf_name, p, false, true) == kEqualFiles) { break; } } @@ -2006,13 +2055,13 @@ char *did_set_spelllang(win_T *wp) if (round == 0) { STRCPY(lang, "internal wordlist"); } else { - STRLCPY(lang, path_tail((char *)spf_name), MAXWLEN + 1); - p = (char_u *)vim_strchr((char *)lang, '.'); + xstrlcpy(lang, path_tail((char *)spf_name), MAXWLEN + 1); + p = vim_strchr((char *)lang, '.'); if (p != NULL) { *p = NUL; // truncate at ".encoding.add" } } - slang = spell_load_file(spf_name, lang, NULL, true); + slang = spell_load_file((char *)spf_name, (char *)lang, NULL, true); // If one of the languages has NOBREAK we assume the addition // files also have this. @@ -2024,7 +2073,7 @@ char *did_set_spelllang(win_T *wp) region_mask = REGION_ALL; if (use_region != NULL && !dont_use_region) { // find region in sl_regions - c = find_region(slang->sl_regions, use_region); + c = find_region(slang->sl_regions, (char_u *)use_region); if (c != REGION_ALL) { region_mask = 1 << c; } else if (*slang->sl_regions != NUL) { @@ -2064,7 +2113,7 @@ char *did_set_spelllang(win_T *wp) for (int j = 0; j < ga.ga_len; j++) { lp2 = LANGP_ENTRY(ga, j); if (!GA_EMPTY(&lp2->lp_slang->sl_sal) - && STRNCMP(lp->lp_slang->sl_name, + && strncmp(lp->lp_slang->sl_name, lp2->lp_slang->sl_name, 2) == 0) { lp->lp_sallang = lp2->lp_slang; break; @@ -2081,7 +2130,7 @@ char *did_set_spelllang(win_T *wp) for (int j = 0; j < ga.ga_len; j++) { lp2 = LANGP_ENTRY(ga, j); if (!GA_EMPTY(&lp2->lp_slang->sl_rep) - && STRNCMP(lp->lp_slang->sl_name, + && strncmp(lp->lp_slang->sl_name, lp2->lp_slang->sl_name, 2) == 0) { lp->lp_replang = lp2->lp_slang; break; @@ -2104,8 +2153,8 @@ static void clear_midword(win_T *wp) XFREE_CLEAR(wp->w_s->b_spell_ismw_mb); } -// Use the "sl_midword" field of language "lp" for buffer "buf". -// They add up to any currently used midword characters. +/// Use the "sl_midword" field of language "lp" for buffer "buf". +/// They add up to any currently used midword characters. static void use_midword(slang_T *lp, win_T *wp) FUNC_ATTR_NONNULL_ALL { @@ -2113,21 +2162,21 @@ static void use_midword(slang_T *lp, win_T *wp) return; } - for (char_u *p = lp->sl_midword; *p != NUL;) { - const int c = utf_ptr2char((char *)p); - const int l = utfc_ptr2len((char *)p); + for (char *p = (char *)lp->sl_midword; *p != NUL;) { + const int c = utf_ptr2char(p); + const int l = utfc_ptr2len(p); if (c < 256 && l <= 2) { wp->w_s->b_spell_ismw[c] = true; } else if (wp->w_s->b_spell_ismw_mb == NULL) { // First multi-byte char in "b_spell_ismw_mb". - wp->w_s->b_spell_ismw_mb = (char *)vim_strnsave(p, (size_t)l); + wp->w_s->b_spell_ismw_mb = xstrnsave(p, (size_t)l); } else { // Append multi-byte chars to "b_spell_ismw_mb". - const int n = (int)STRLEN(wp->w_s->b_spell_ismw_mb); - char_u *bp = vim_strnsave((char_u *)wp->w_s->b_spell_ismw_mb, (size_t)n + (size_t)l); + const int n = (int)strlen(wp->w_s->b_spell_ismw_mb); + char *bp = xstrnsave(wp->w_s->b_spell_ismw_mb, (size_t)n + (size_t)l); xfree(wp->w_s->b_spell_ismw_mb); - wp->w_s->b_spell_ismw_mb = (char *)bp; - STRLCPY(bp + n, p, l + 1); + wp->w_s->b_spell_ismw_mb = bp; + xstrlcpy(bp + n, p, (size_t)l + 1); } p += l; } @@ -2136,7 +2185,7 @@ static void use_midword(slang_T *lp, win_T *wp) // Find the region "region[2]" in "rp" (points to "sl_regions"). // Each region is simply stored as the two characters of its name. // Returns the index if found (first is 0), REGION_ALL if not found. -static int find_region(char_u *rp, char_u *region) +static int find_region(const char_u *rp, const char_u *region) { int i; @@ -2161,7 +2210,7 @@ static int find_region(char_u *rp, char_u *region) /// @param[in] end End of word or NULL for NUL delimited string /// /// @returns Case type of word -int captype(char_u *word, char_u *end) +int captype(char_u *word, const char_u *end) FUNC_ATTR_NONNULL_ARG(1) { char_u *p; @@ -2366,7 +2415,7 @@ bool spell_iswordp(const char_u *p, const win_T *wp) int c = utf_ptr2char((char *)s); if (c > 255) { - return spell_mb_isword_class(mb_get_class(s), wp); + return spell_mb_isword_class(mb_get_class((char *)s), wp); } return spelltab.st_isw[c]; } @@ -2377,7 +2426,7 @@ bool spell_iswordp_nmw(const char_u *p, win_T *wp) { int c = utf_ptr2char((char *)p); if (c > 255) { - return spell_mb_isword_class(mb_get_class(p), wp); + return spell_mb_isword_class(mb_get_class((char *)p), wp); } return spelltab.st_isw[c]; } @@ -2470,23 +2519,23 @@ bool check_need_cap(linenr_T lnum, colnr_T col) return false; } - char_u *line = get_cursor_line_ptr(); - char_u *line_copy = NULL; + char *line = get_cursor_line_ptr(); + char *line_copy = NULL; colnr_T endcol = 0; - if (getwhitecols((char *)line) >= (int)col) { + if (getwhitecols(line) >= (int)col) { // At start of line, check if previous line is empty or sentence // ends there. if (lnum == 1) { need_cap = true; } else { line = ml_get(lnum - 1); - if (*skipwhite((char *)line) == NUL) { + if (*skipwhite(line) == NUL) { need_cap = true; } else { // Append a space in place of the line break. - line_copy = (char_u *)concat_str((char *)line, " "); + line_copy = concat_str(line, " "); line = line_copy; - endcol = (colnr_T)STRLEN(line); + endcol = (colnr_T)strlen(line); } } } else { @@ -2499,13 +2548,13 @@ bool check_need_cap(linenr_T lnum, colnr_T col) .regprog = curwin->w_s->b_cap_prog, .rm_ic = false }; - char_u *p = line + endcol; + char *p = line + endcol; for (;;) { MB_PTR_BACK(line, p); - if (p == line || spell_iswordp_nmw(p, curwin)) { + if (p == line || spell_iswordp_nmw((char_u *)p, curwin)) { break; } - if (vim_regexec(®match, (char *)p, 0) + if (vim_regexec(®match, p, 0) && regmatch.endp[0] == line + endcol) { need_cap = true; break; @@ -2530,9 +2579,9 @@ void ex_spellrepall(exarg_T *eap) emsg(_("E752: No previous spell replacement")); return; } - int addlen = (int)(STRLEN(repl_to) - STRLEN(repl_from)); + int addlen = (int)(strlen(repl_to) - strlen(repl_from)); - size_t frompatlen = STRLEN(repl_from) + 7; + size_t frompatlen = strlen(repl_from) + 7; char_u *frompat = xmalloc(frompatlen); snprintf((char *)frompat, frompatlen, "\\V\\<%s\\>", repl_from); p_ws = false; @@ -2541,20 +2590,20 @@ void ex_spellrepall(exarg_T *eap) sub_nlines = 0; curwin->w_cursor.lnum = 0; while (!got_int) { - if (do_search(NULL, '/', '/', frompat, 1L, SEARCH_KEEP, NULL) == 0 + if (do_search(NULL, '/', '/', (char *)frompat, 1L, SEARCH_KEEP, NULL) == 0 || u_save_cursor() == FAIL) { break; } // Only replace when the right word isn't there yet. This happens // when changing "etc" to "etc.". - char_u *line = get_cursor_line_ptr(); - if (addlen <= 0 || STRNCMP(line + curwin->w_cursor.col, - repl_to, STRLEN(repl_to)) != 0) { - char_u *p = xmalloc(STRLEN(line) + (size_t)addlen + 1); + char *line = get_cursor_line_ptr(); + if (addlen <= 0 || strncmp(line + curwin->w_cursor.col, + repl_to, strlen(repl_to)) != 0) { + char_u *p = xmalloc(strlen(line) + (size_t)addlen + 1); memmove(p, line, (size_t)curwin->w_cursor.col); STRCPY(p + curwin->w_cursor.col, repl_to); - STRCAT(p, line + curwin->w_cursor.col + STRLEN(repl_from)); + STRCAT(p, line + curwin->w_cursor.col + strlen(repl_from)); ml_replace(curwin->w_cursor.lnum, (char *)p, false); changed_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col); @@ -2564,7 +2613,7 @@ void ex_spellrepall(exarg_T *eap) } sub_nsubs++; } - curwin->w_cursor.col += (colnr_T)STRLEN(repl_to); + curwin->w_cursor.col += (colnr_T)strlen(repl_to); } p_ws = save_ws; @@ -2585,7 +2634,7 @@ void ex_spellrepall(exarg_T *eap) /// @param[in] word source string to copy /// @param[in,out] wcopy copied string, with case of first letter changed /// @param[in] upper True to upper case, otherwise lower case -void onecap_copy(char_u *word, char_u *wcopy, bool upper) +void onecap_copy(char_u *word, char *wcopy, bool upper) { char_u *p = word; int c = mb_cptr2char_adv((const char_u **)&p); @@ -2594,8 +2643,8 @@ void onecap_copy(char_u *word, char_u *wcopy, bool upper) } else { c = SPELL_TOFOLD(c); } - int l = utf_char2bytes(c, (char *)wcopy); - STRLCPY(wcopy + l, p, MAXWLEN - l); + int l = utf_char2bytes(c, wcopy); + xstrlcpy(wcopy + l, (char *)p, (size_t)(MAXWLEN - l)); } // Make a copy of "word" with all the letters upper cased into @@ -2648,7 +2697,7 @@ void make_case_word(char_u *fword, char_u *cword, int flags) allcap_copy(fword, cword); } else if (flags & WF_ONECAP) { // Make the first letter upper-case - onecap_copy(fword, cword, true); + onecap_copy(fword, (char *)cword, true); } else { // Use goodword as-is. STRCPY(cword, fword); @@ -2672,7 +2721,7 @@ char *eval_soundfold(const char *const word) if (!GA_EMPTY(&lp->lp_slang->sl_sal)) { // soundfold the word char_u sound[MAXWLEN]; - spell_soundfold(lp->lp_slang, (char_u *)word, false, sound); + spell_soundfold(lp->lp_slang, (char *)word, false, (char *)sound); return xstrdup((const char *)sound); } } @@ -2697,23 +2746,23 @@ char *eval_soundfold(const char *const word) /// @param[in] inword word to soundfold /// @param[in] folded whether inword is already case-folded /// @param[in,out] res destination for soundfolded word -void spell_soundfold(slang_T *slang, char_u *inword, bool folded, char_u *res) +void spell_soundfold(slang_T *slang, char *inword, bool folded, char *res) { if (slang->sl_sofo) { // SOFOFROM and SOFOTO used - spell_soundfold_sofo(slang, inword, res); + spell_soundfold_sofo(slang, (char_u *)inword, (char_u *)res); } else { - char_u fword[MAXWLEN]; - char_u *word; + char fword[MAXWLEN]; + char *word; // SAL items used. Requires the word to be case-folded. if (folded) { word = inword; } else { - (void)spell_casefold(curwin, inword, (int)STRLEN(inword), fword, MAXWLEN); + (void)spell_casefold(curwin, (char_u *)inword, (int)strlen(inword), (char_u *)fword, MAXWLEN); word = fword; } - spell_soundfold_wsal(slang, word, res); + spell_soundfold_wsal(slang, (char_u *)word, (char_u *)res); } } @@ -2766,7 +2815,7 @@ static void spell_soundfold_sofo(slang_T *slang, char_u *inword, char_u *res) // Turn "inword" into its sound-a-like equivalent in "res[MAXWLEN]". // Multi-byte version of spell_soundfold(). -static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) +static void spell_soundfold_wsal(slang_T *slang, const char_u *inword, char_u *res) { salitem_T *smp = (salitem_T *)slang->sl_sal.ga_data; int word[MAXWLEN] = { 0 }; @@ -3097,7 +3146,7 @@ void ex_spelldump(exarg_T *eap) } char *spl; long dummy; - (void)get_option_value("spl", &dummy, &spl, OPT_LOCAL); + (void)get_option_value("spl", &dummy, &spl, NULL, OPT_LOCAL); // Create a new empty buffer in a new window. do_cmdline_cmd("new"); @@ -3129,23 +3178,23 @@ void ex_spelldump(exarg_T *eap) /// @param ic ignore case /// @param dir direction for adding matches /// @param dumpflags_arg DUMPFLAG_* -void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg) +void spell_dump_compl(char *pat, int ic, Direction *dir, int dumpflags_arg) { langp_T *lp; slang_T *slang; idx_T arridx[MAXWLEN]; int curi[MAXWLEN]; - char_u word[MAXWLEN]; + char word[MAXWLEN]; int c; - char_u *byts; + char *byts; idx_T *idxs; linenr_T lnum = 0; int depth; int n; int flags; - char_u *region_names = NULL; // region names being used + char *region_names = NULL; // region names being used bool do_region = true; // dump region names and numbers - char_u *p; + char *p; int dumpflags = dumpflags_arg; int patlen; @@ -3155,11 +3204,11 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg) if (ic) { dumpflags |= DUMPFLAG_ICASE; } else { - n = captype(pat, NULL); + n = captype((char_u *)pat, NULL); if (n == WF_ONECAP) { dumpflags |= DUMPFLAG_ONECAP; } else if (n == WF_ALLCAP - && (int)STRLEN(pat) > utfc_ptr2len((char *)pat)) { + && (int)strlen(pat) > utfc_ptr2len(pat)) { dumpflags |= DUMPFLAG_ALLCAP; } } @@ -3169,11 +3218,11 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg) // regions or none at all. for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; lpi++) { lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi); - p = lp->lp_slang->sl_regions; + p = (char *)lp->lp_slang->sl_regions; if (p[0] != 0) { if (region_names == NULL) { // first language with regions region_names = p; - } else if (STRCMP(region_names, p) != 0) { + } else if (strcmp(region_names, p) != 0) { do_region = false; // region names are different break; } @@ -3182,8 +3231,8 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg) if (do_region && region_names != NULL) { if (pat == NULL) { - vim_snprintf((char *)IObuff, IOSIZE, "/regions=%s", region_names); - ml_append(lnum++, (char *)IObuff, (colnr_T)0, false); + vim_snprintf(IObuff, IOSIZE, "/regions=%s", region_names); + ml_append(lnum++, IObuff, (colnr_T)0, false); } } else { do_region = false; @@ -3198,14 +3247,14 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg) } if (pat == NULL) { - vim_snprintf((char *)IObuff, IOSIZE, "# file: %s", slang->sl_fname); - ml_append(lnum++, (char *)IObuff, (colnr_T)0, false); + vim_snprintf(IObuff, IOSIZE, "# file: %s", slang->sl_fname); + ml_append(lnum++, IObuff, (colnr_T)0, false); } // When matching with a pattern and there are no prefixes only use // parts of the tree that match "pat". if (pat != NULL && slang->sl_pbyts == NULL) { - patlen = (int)STRLEN(pat); + patlen = (int)strlen(pat); } else { patlen = -1; } @@ -3215,11 +3264,11 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg) for (int round = 1; round <= 2; round++) { if (round == 1) { dumpflags &= ~DUMPFLAG_KEEPCASE; - byts = slang->sl_fbyts; + byts = (char *)slang->sl_fbyts; idxs = slang->sl_fidxs; } else { dumpflags |= DUMPFLAG_KEEPCASE; - byts = slang->sl_kbyts; + byts = (char *)slang->sl_kbyts; idxs = slang->sl_kidxs; } if (byts == NULL) { @@ -3239,7 +3288,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg) // Do one more byte at this node. n = arridx[depth] + curi[depth]; curi[depth]++; - c = byts[n]; + c = (uint8_t)byts[n]; if (c == 0 || depth >= MAXWLEN - 1) { // End of word or reached maximum length, deal with the // word. @@ -3262,8 +3311,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg) // when it's the first one. c = (int)((unsigned)flags >> 24); if (c == 0 || curi[depth] == 2) { - dump_word(slang, word, pat, dir, - dumpflags, flags, lnum); + dump_word(slang, word, pat, dir, dumpflags, flags, lnum); if (pat == NULL) { lnum++; } @@ -3271,13 +3319,13 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg) // Apply the prefix, if there is one. if (c != 0) { - lnum = dump_prefixes(slang, word, pat, dir, + lnum = dump_prefixes(slang, word, (char_u *)pat, dir, dumpflags, flags, lnum); } } } else { // Normal char, go one level deeper. - word[depth++] = (char_u)c; + word[depth++] = (char)c; arridx[depth] = idxs[n]; curi[depth] = 1; @@ -3289,7 +3337,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg) // ignore case... assert(depth >= 0); if (depth <= patlen - && mb_strnicmp(word, pat, (size_t)depth) != 0) { + && mb_strnicmp((char *)word, pat, (size_t)depth) != 0) { depth--; } } @@ -3299,15 +3347,15 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg) } } -// Dumps one word: apply case modifications and append a line to the buffer. -// When "lnum" is zero add insert mode completion. -static void dump_word(slang_T *slang, char_u *word, char_u *pat, Direction *dir, int dumpflags, +/// Dumps one word: apply case modifications and append a line to the buffer. +/// When "lnum" is zero add insert mode completion. +static void dump_word(slang_T *slang, char *word, char *pat, Direction *dir, int dumpflags, int wordflags, linenr_T lnum) { bool keepcap = false; - char_u *p; - char_u cword[MAXWLEN]; - char_u badword[MAXWLEN + 10]; + char *p; + char cword[MAXWLEN]; + char badword[MAXWLEN + 10]; int flags = wordflags; if (dumpflags & DUMPFLAG_ONECAP) { @@ -3319,17 +3367,17 @@ static void dump_word(slang_T *slang, char_u *word, char_u *pat, Direction *dir, if ((dumpflags & DUMPFLAG_KEEPCASE) == 0 && (flags & WF_CAPMASK) != 0) { // Need to fix case according to "flags". - make_case_word(word, cword, flags); + make_case_word((char_u *)word, (char_u *)cword, flags); p = cword; } else { p = word; if ((dumpflags & DUMPFLAG_KEEPCASE) - && ((captype(word, NULL) & WF_KEEPCAP) == 0 + && ((captype((char_u *)word, NULL) & WF_KEEPCAP) == 0 || (flags & WF_FIXCAP) != 0)) { keepcap = true; } } - char_u *tw = p; + char *tw = p; if (pat == NULL) { // Add flags and regions after a slash. @@ -3347,8 +3395,8 @@ static void dump_word(slang_T *slang, char_u *word, char_u *pat, Direction *dir, if (flags & WF_REGION) { for (int i = 0; i < 7; i++) { if (flags & (0x10000 << i)) { - const size_t badword_len = STRLEN(badword); - snprintf((char *)badword + badword_len, + const size_t badword_len = strlen(badword); + snprintf(badword + badword_len, sizeof(badword) - badword_len, "%d", i + 1); } @@ -3361,19 +3409,19 @@ static void dump_word(slang_T *slang, char_u *word, char_u *pat, Direction *dir, hashitem_T *hi; // Include the word count for ":spelldump!". - hi = hash_find(&slang->sl_wordcount, (char *)tw); + hi = hash_find(&slang->sl_wordcount, tw); if (!HASHITEM_EMPTY(hi)) { - vim_snprintf((char *)IObuff, IOSIZE, "%s\t%d", + vim_snprintf(IObuff, IOSIZE, "%s\t%d", tw, HI2WC(hi)->wc_count); p = IObuff; } } - ml_append(lnum, (char *)p, (colnr_T)0, false); + ml_append(lnum, p, (colnr_T)0, false); } else if (((dumpflags & DUMPFLAG_ICASE) - ? mb_strnicmp(p, pat, STRLEN(pat)) == 0 - : STRNCMP(p, pat, STRLEN(pat)) == 0) - && ins_compl_add_infercase(p, (int)STRLEN(p), + ? mb_strnicmp(p, pat, strlen(pat)) == 0 + : strncmp(p, pat, strlen(pat)) == 0) + && ins_compl_add_infercase(p, (int)strlen(p), p_ic, NULL, *dir, false) == OK) { // if dir was BACKWARD then honor it just once *dir = FORWARD; @@ -3388,21 +3436,21 @@ static void dump_word(slang_T *slang, char_u *word, char_u *pat, Direction *dir, /// @param flags flags with prefix ID /// /// @return the updated line number. -static linenr_T dump_prefixes(slang_T *slang, char_u *word, char_u *pat, Direction *dir, +static linenr_T dump_prefixes(slang_T *slang, char *word, char_u *pat, Direction *dir, int dumpflags, int flags, linenr_T startlnum) { idx_T arridx[MAXWLEN]; int curi[MAXWLEN]; - char_u prefix[MAXWLEN]; - char_u word_up[MAXWLEN]; + char prefix[MAXWLEN]; + char word_up[MAXWLEN]; bool has_word_up = false; linenr_T lnum = startlnum; // If the word starts with a lower-case letter make the word with an // upper-case letter in word_up[]. - int c = utf_ptr2char((char *)word); + int c = utf_ptr2char(word); if (SPELL_TOUPPER(c) != c) { - onecap_copy(word, word_up, true); + onecap_copy((char_u *)word, (char *)word_up, true); has_word_up = true; } @@ -3436,10 +3484,10 @@ static linenr_T dump_prefixes(slang_T *slang, char_u *word, char_u *pat, Directi } curi[depth] += i - 1; - c = valid_word_prefix(i, n, flags, word, slang, false); + c = valid_word_prefix(i, n, flags, (char_u *)word, slang, false); if (c != 0) { - STRLCPY(prefix + depth, word, MAXWLEN - depth); - dump_word(slang, prefix, pat, dir, dumpflags, + xstrlcpy(prefix + depth, word, (size_t)(MAXWLEN - depth)); + dump_word(slang, (char *)prefix, (char *)pat, dir, dumpflags, (c & WF_RAREPFX) ? (flags | WF_RARE) : flags, lnum); if (lnum != 0) { lnum++; @@ -3450,11 +3498,11 @@ static linenr_T dump_prefixes(slang_T *slang, char_u *word, char_u *pat, Directi // first letter is upper-case, but only if the prefix has // a condition. if (has_word_up) { - c = valid_word_prefix(i, n, flags, word_up, slang, + c = valid_word_prefix(i, n, flags, (char_u *)word_up, slang, true); if (c != 0) { - STRLCPY(prefix + depth, word_up, MAXWLEN - depth); - dump_word(slang, prefix, pat, dir, dumpflags, + xstrlcpy(prefix + depth, word_up, (size_t)(MAXWLEN - depth)); + dump_word(slang, (char *)prefix, (char *)pat, dir, dumpflags, (c & WF_RAREPFX) ? (flags | WF_RARE) : flags, lnum); if (lnum != 0) { lnum++; @@ -3463,7 +3511,7 @@ static linenr_T dump_prefixes(slang_T *slang, char_u *word, char_u *pat, Directi } } else { // Normal char, go one level deeper. - prefix[depth++] = (char_u)c; + prefix[depth++] = (char)c; arridx[depth] = idxs[n]; curi[depth] = 1; } @@ -3497,7 +3545,7 @@ int spell_word_start(int startcol) return startcol; } - char_u *line = get_cursor_line_ptr(); + char_u *line = (char_u *)get_cursor_line_ptr(); char_u *p; // Find a word character before "startcol". @@ -3536,11 +3584,11 @@ void spell_expand_check_cap(colnr_T col) // Used for Insert mode completion CTRL-X ?. // Returns the number of matches. The matches are in "matchp[]", array of // allocated strings. -int expand_spelling(linenr_T lnum, char_u *pat, char ***matchp) +int expand_spelling(linenr_T lnum, char *pat, char ***matchp) { garray_T ga; - spell_suggest_list(&ga, pat, 100, spell_expand_need_cap, true); + spell_suggest_list(&ga, (char_u *)pat, 100, spell_expand_need_cap, true); *matchp = ga.ga_data; return ga.ga_len; } @@ -3556,8 +3604,8 @@ bool valid_spelllang(const char *val) bool valid_spellfile(const char *val) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - for (const char_u *s = (char_u *)val; *s != NUL; s++) { - if (!vim_isfilec(*s) && *s != ',' && *s != ' ') { + for (const char *s = val; *s != NUL; s++) { + if (!vim_isfilec((uint8_t)(*s)) && *s != ',' && *s != ' ') { return false; } } @@ -3569,9 +3617,9 @@ char *did_set_spell_option(bool is_spellfile) char *errmsg = NULL; if (is_spellfile) { - int l = (int)STRLEN(curwin->w_s->b_p_spf); + int l = (int)strlen(curwin->w_s->b_p_spf); if (l > 0 - && (l < 4 || STRCMP(curwin->w_s->b_p_spf + l - 4, ".add") != 0)) { + && (l < 4 || strcmp(curwin->w_s->b_p_spf + l - 4, ".add") != 0)) { errmsg = e_invarg; } } |