diff options
Diffstat (limited to 'src/nvim/spell.c')
-rw-r--r-- | src/nvim/spell.c | 462 |
1 files changed, 262 insertions, 200 deletions
diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 2204cda169..905f5c25b4 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -1,6 +1,3 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - // spell.c: code for spell checking // // See spellfile.c for the Vim spell file format. @@ -64,7 +61,7 @@ #include <stdio.h> #include <string.h> -#include "nvim/ascii.h" +#include "nvim/ascii_defs.h" #include "nvim/autocmd.h" #include "nvim/buffer.h" #include "nvim/change.h" @@ -76,6 +73,7 @@ #include "nvim/ex_cmds.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" +#include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/gettext.h" #include "nvim/globals.h" @@ -83,18 +81,18 @@ #include "nvim/highlight_defs.h" #include "nvim/insexpand.h" #include "nvim/log.h" -#include "nvim/macros.h" +#include "nvim/macros_defs.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/option_vars.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/pos_defs.h" #include "nvim/regexp.h" #include "nvim/runtime.h" #include "nvim/search.h" @@ -104,9 +102,9 @@ #include "nvim/spellsuggest.h" #include "nvim/strings.h" #include "nvim/syntax.h" -#include "nvim/types.h" +#include "nvim/types_defs.h" #include "nvim/undo.h" -#include "nvim/vim.h" +#include "nvim/vim_defs.h" #include "nvim/window.h" // Result values. Lower number is accepted over higher one. @@ -150,7 +148,7 @@ typedef struct matchinf_S { // for when checking a compound word int mi_compoff; // start of following word offset - char_u mi_compflags[MAXWLEN]; // flags for compound words used + uint8_t mi_compflags[MAXWLEN]; // flags for compound words used int mi_complen; // nr of compound words used int mi_compextra; // nr of COMPOUNDROOT words @@ -184,12 +182,21 @@ int did_set_spelltab; # include "spell.c.generated.h" #endif -// mode values for find_word -#define FIND_FOLDWORD 0 // find word case-folded -#define FIND_KEEPWORD 1 // find keep-case word -#define FIND_PREFIX 2 // find word after prefix -#define FIND_COMPOUND 3 // find case-folded compound word -#define FIND_KEEPCOMPOUND 4 // find keep-case compound word +/// mode values for find_word +enum { + FIND_FOLDWORD = 0, ///< find word case-folded + FIND_KEEPWORD = 1, ///< find keep-case word + FIND_PREFIX = 2, ///< find word after prefix + FIND_COMPOUND = 3, ///< find case-folded compound word + FIND_KEEPCOMPOUND = 4, ///< find keep-case compound word +}; + +/// type values for get_char_type +enum { + CHAR_OTHER = 0, + CHAR_UPPER = 1, + CHAR_DIGIT = 2, +}; char *e_format = N_("E759: Format error in spell file"); @@ -222,7 +229,7 @@ size_t spell_check(win_T *wp, char *ptr, hlf_T *attrp, int *capcol, bool docount size_t wrongcaplen = 0; bool count_word = docount; bool use_camel_case = (wp->w_s->b_p_spo_flags & SPO_CAMEL) != 0; - bool camel_case = false; + bool is_camel_case = false; // A word never starts at a space or a control character. Return quickly // then, skipping over the character. @@ -255,24 +262,14 @@ size_t spell_check(win_T *wp, char *ptr, hlf_T *attrp, int *capcol, bool docount mi.mi_word = ptr; mi.mi_fend = ptr; if (spell_iswordp(mi.mi_fend, wp)) { - bool this_upper = false; // init for gcc - if (use_camel_case) { - int c = utf_ptr2char(mi.mi_fend); - this_upper = SPELL_ISUPPER(c); + mi.mi_fend = advance_camelcase_word(ptr, wp, &is_camel_case); + } else { + do { + MB_PTR_ADV(mi.mi_fend); + } while (*mi.mi_fend != NUL && spell_iswordp(mi.mi_fend, wp)); } - do { - MB_PTR_ADV(mi.mi_fend); - if (use_camel_case) { - const bool prev_upper = this_upper; - int c = utf_ptr2char(mi.mi_fend); - this_upper = SPELL_ISUPPER(c); - camel_case = !prev_upper && this_upper; - } - } while (*mi.mi_fend != NUL && spell_iswordp(mi.mi_fend, wp) - && !camel_case); - if (capcol != NULL && *capcol == 0 && wp->w_s->b_cap_prog != NULL) { // Check word starting with capital letter. int c = utf_ptr2char(ptr); @@ -304,7 +301,7 @@ size_t spell_check(win_T *wp, char *ptr, hlf_T *attrp, int *capcol, bool docount MAXWLEN + 1); mi.mi_fwordlen = (int)strlen(mi.mi_fword); - if (camel_case && mi.mi_fwordlen > 0) { + if (is_camel_case && mi.mi_fwordlen > 0) { // introduce a fake word end space into the folded word. mi.mi_fword[mi.mi_fwordlen - 1] = ' '; } @@ -380,7 +377,7 @@ size_t spell_check(win_T *wp, char *ptr, hlf_T *attrp, int *capcol, bool docount MB_PTR_ADV(mi.mi_end); } else if (mi.mi_result == SP_BAD && LANGP_ENTRY(wp->w_s->b_langp, 0)->lp_slang->sl_nobreak) { - char *p, *fp; + char *p; int save_result = mi.mi_result; // First language in 'spelllang' is NOBREAK. Find first position @@ -388,8 +385,8 @@ size_t spell_check(win_T *wp, char *ptr, hlf_T *attrp, int *capcol, bool docount 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; - for (;;) { + char *fp = mi.mi_fword; + while (true) { MB_PTR_ADV(p); MB_PTR_ADV(fp); if (p >= mi.mi_end) { @@ -424,6 +421,66 @@ size_t spell_check(win_T *wp, char *ptr, hlf_T *attrp, int *capcol, bool docount return (size_t)(mi.mi_end - ptr); } +/// Determine the type of character "c". +static int get_char_type(int c) +{ + if (ascii_isdigit(c)) { + return CHAR_DIGIT; + } + if (SPELL_ISUPPER(c)) { + return CHAR_UPPER; + } + return CHAR_OTHER; +} + +/// Returns a pointer to the end of the word starting at "str". +/// Supports camelCase words. +static char *advance_camelcase_word(char *str, win_T *wp, bool *is_camel_case) +{ + char *end = str; + + *is_camel_case = false; + + if (*str == NUL) { + return str; + } + + int c = utf_ptr2char(end); + MB_PTR_ADV(end); + // We need at most the types of the type of the last two chars. + int last_last_type = -1; + int last_type = get_char_type(c); + + while (*end != NUL && spell_iswordp(end, wp)) { + c = utf_ptr2char(end); + int this_type = get_char_type(c); + + if (last_last_type == CHAR_UPPER && last_type == CHAR_UPPER + && this_type == CHAR_OTHER) { + // Handle the following cases: + // UpperUpperLower + *is_camel_case = true; + // Back up by one char. + MB_PTR_BACK(str, end); + break; + } else if ((this_type == CHAR_UPPER && last_type == CHAR_OTHER) + || (this_type != last_type + && (this_type == CHAR_DIGIT || last_type == CHAR_DIGIT))) { + // Handle the following cases: + // LowerUpper LowerDigit UpperDigit DigitUpper DigitLower + *is_camel_case = true; + break; + } + + last_last_type = last_type; + last_type = this_type; + + MB_PTR_ADV(end); + } + + return end; +} + // Check if the word at "mip->mi_word" is in the tree. // When "mode" is FIND_FOLDWORD check in fold-case word tree. // When "mode" is FIND_KEEPWORD check in keep-case word tree. @@ -437,14 +494,14 @@ static void find_word(matchinf_T *mip, int mode) int flen; char *ptr; slang_T *slang = mip->mi_lp->lp_slang; - char_u *byts; + uint8_t *byts; idx_T *idxs; if (mode == FIND_KEEPWORD || mode == FIND_KEEPCOMPOUND) { // Check for word with matching case in keep-case tree. ptr = mip->mi_word; flen = 9999; // no case folding, always enough bytes - byts = (char_u *)slang->sl_kbyts; + byts = slang->sl_kbyts; idxs = slang->sl_kidxs; if (mode == FIND_KEEPCOMPOUND) { @@ -455,7 +512,7 @@ static void find_word(matchinf_T *mip, int mode) // Check for case-folded in case-folded tree. ptr = mip->mi_fword; flen = mip->mi_fwordlen; // available case-folded bytes - byts = (char_u *)slang->sl_fbyts; + byts = slang->sl_fbyts; idxs = slang->sl_fidxs; if (mode == FIND_PREFIX) { @@ -482,7 +539,7 @@ static void find_word(matchinf_T *mip, int mode) // - there is a byte that doesn't match, // - we reach the end of the tree, // - or we reach the end of the line. - for (;;) { + while (true) { if (flen <= 0 && *mip->mi_fend != NUL) { flen = fold_more(mip); } @@ -549,7 +606,7 @@ static void find_word(matchinf_T *mip, int mode) // One space in the good word may stand for several spaces in the // checked word. if (c == ' ') { - for (;;) { + while (true) { if (flen <= 0 && *mip->mi_fend != NUL) { flen = fold_more(mip); } @@ -737,7 +794,7 @@ static void find_word(matchinf_T *mip, int mode) // If the word ends the sequence of compound flags of the // words must match with one of the COMPOUNDRULE items and // the number of syllables must not be too large. - mip->mi_compflags[mip->mi_complen] = (char_u)((unsigned)flags >> 24); + mip->mi_compflags[mip->mi_complen] = (uint8_t)((unsigned)flags >> 24); mip->mi_compflags[mip->mi_complen + 1] = NUL; if (word_ends) { char fword[MAXWLEN] = { 0 }; @@ -795,9 +852,6 @@ static void find_word(matchinf_T *mip, int mode) mip->mi_compoff = (int)(p - mip->mi_fword); } } -#if 0 - c = mip->mi_compoff; -#endif mip->mi_complen++; if (flags & WF_COMPROOT) { mip->mi_compextra++; @@ -823,16 +877,6 @@ static void find_word(matchinf_T *mip, int mode) // Find following word in keep-case tree. mip->mi_compoff = wlen; find_word(mip, FIND_KEEPCOMPOUND); - -#if 0 // Disabled, a prefix must not appear halfway through a compound - // word, unless the COMPOUNDPERMITFLAG is used, in which case it - // can't be a postponed prefix. - if (!slang->sl_nobreak || mip->mi_result == SP_BAD) { - // Check for following word with prefix. - mip->mi_compoff = c; - find_prefix(mip, FIND_COMPOUND); - } -#endif } if (!slang->sl_nobreak) { @@ -961,10 +1005,10 @@ bool can_compound(slang_T *slang, const char *word, const uint8_t *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, const char_u *compflags) +bool match_compoundrule(slang_T *slang, const uint8_t *compflags) { // loop over all the COMPOUNDRULE entries - for (char_u *p = (char_u *)slang->sl_comprules; *p != NUL; p++) { + for (char *p = (char *)slang->sl_comprules; *p != NUL; p++) { // loop over the flags in the compound word we have made, match // them against the current rule entry for (int i = 0;; i++) { @@ -982,21 +1026,21 @@ bool match_compoundrule(slang_T *slang, const char_u *compflags) // compare against all the flags in [] p++; while (*p != ']' && *p != NUL) { - if (*p++ == c) { + if ((uint8_t)(*p++) == c) { match = true; } } if (!match) { break; // none matches } - } else if (*p != c) { + } else if ((uint8_t)(*p) != c) { break; // flag of word doesn't match flag in pattern } p++; } // Skip to the next "/", where the next pattern starts. - p = (char_u *)vim_strchr((char *)p, '/'); + p = vim_strchr(p, '/'); if (p == NULL) { break; } @@ -1062,13 +1106,13 @@ static void find_prefix(matchinf_T *mip, int mode) int wlen = 0; slang_T *slang = mip->mi_lp->lp_slang; - char_u *byts = (char_u *)slang->sl_pbyts; + uint8_t *byts = slang->sl_pbyts; if (byts == NULL) { return; // array is empty } // We use the case-folded word here, since prefixes are always // case-folded. - char_u *ptr = (char_u *)mip->mi_fword; + char *ptr = mip->mi_fword; int flen = mip->mi_fwordlen; // available case-folded bytes if (mode == FIND_COMPOUND) { // Skip over the previously found word(s). @@ -1081,7 +1125,7 @@ static void find_prefix(matchinf_T *mip, int mode) // - there is a byte that doesn't match, // - we reach the end of the tree, // - or we reach the end of the line. - for (;;) { + while (true) { if (flen == 0 && *mip->mi_fend != NUL) { flen = fold_more(mip); } @@ -1126,7 +1170,7 @@ static void find_prefix(matchinf_T *mip, int mode) } // Perform a binary search in the list of accepted bytes. - int c = ptr[wlen]; + int c = (uint8_t)ptr[wlen]; idx_T lo = arridx; idx_T hi = arridx + len - 1; while (lo < hi) { @@ -1189,11 +1233,19 @@ bool spell_valid_case(int wordflags, int treeflags) || (wordflags & WF_ONECAP) != 0)); } -// Returns true if spell checking is not enabled. +/// Return true if spell checking is enabled for "wp". +bool spell_check_window(win_T *wp) +{ + return wp->w_p_spell + && *wp->w_s->b_p_spl != NUL + && wp->w_s->b_langp.ga_len > 0 + && *(char **)(wp->w_s->b_langp.ga_data) != NULL; +} + +/// Return true and give an error if spell checking is not enabled. 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)) { + if (!wp->w_p_spell || *wp->w_s->b_p_spl == NUL || GA_EMPTY(&wp->w_s->b_langp)) { emsg(_(e_no_spell)); return true; } @@ -1203,19 +1255,18 @@ bool no_spell_checking(win_T *wp) static void decor_spell_nav_start(win_T *wp) { decor_state = (DecorState){ 0 }; - decor_redraw_reset(wp->w_buffer, &decor_state); + decor_redraw_reset(wp, &decor_state); } -static bool decor_spell_nav_col(win_T *wp, linenr_T lnum, linenr_T *decor_lnum, int col, - char **decor_error) +static TriState decor_spell_nav_col(win_T *wp, linenr_T lnum, linenr_T *decor_lnum, int col) { 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_providers_invoke_spell(wp, lnum - 1, col, lnum - 1, -1); + decor_redraw_line(wp, lnum - 1, &decor_state); *decor_lnum = lnum; } - decor_redraw_col(wp->w_buffer, col, col, false, &decor_state); - return decor_state.spell == kTrue; + decor_redraw_col(wp, col, 0, false, &decor_state); + return decor_state.spell; } static inline bool can_syn_spell(win_T *wp, linenr_T lnum, int col) @@ -1269,7 +1320,6 @@ 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 @@ -1283,7 +1333,7 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att decor_spell_nav_start(wp); while (!got_int) { - char *line = ml_get_buf(wp->w_buffer, lnum, false); + char *line = ml_get_buf(wp->w_buffer, lnum); len = strlen(line); if (buflen < len + MAXWLEN + 2) { @@ -1304,23 +1354,23 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att } else if (curline && wp == curwin) { // For spellbadword(): check if first word needs a capital. col = (colnr_T)getwhitecols(line); - if (check_need_cap(lnum, col)) { + if (check_need_cap(curwin, lnum, col)) { capcol = col; } // Need to get the line again, may have looked at the previous // one. - line = ml_get_buf(wp->w_buffer, lnum, false); + line = ml_get_buf(wp->w_buffer, lnum); } // Copy the line into "buf" and append the start of the next line if // possible. Note: this ml_get_buf() may make "line" invalid, check // for empty line first. - bool empty_line = *skipwhite((const char *)line) == NUL; + bool empty_line = *skipwhite(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), + ml_get_buf(wp->w_buffer, lnum + 1), MAXWLEN); } char *p = buf + skip; @@ -1352,9 +1402,18 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att : p - buf) > wp->w_cursor.col)) { 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)); + bool no_plain_buffer = (wp->w_s->b_p_spo_flags & SPO_NPBUFFER) != 0; + bool can_spell = !no_plain_buffer; + switch (decor_spell_nav_col(wp, lnum, &decor_lnum, col)) { + case kTrue: + can_spell = true; break; + case kFalse: + can_spell = false; break; + case kNone: + if (has_syntax) { + can_spell = can_syn_spell(wp, lnum, col); + } + } if (!can_spell) { attr = HLF_COUNT; @@ -1471,7 +1530,6 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att theend: decor_state_free(&decor_state); - xfree(decor_error); decor_state = saved_decor_start; xfree(buf); return ret; @@ -1483,9 +1541,9 @@ theend: // to skip those bytes if the word was OK. void spell_cat_line(char *buf, char *line, int maxlen) { - char_u *p = (char_u *)skipwhite(line); + char *p = skipwhite(line); while (vim_strchr("*#/\"\t", (uint8_t)(*p)) != NULL) { - p = (char_u *)skipwhite((char *)p + 1); + p = skipwhite(p + 1); } if (*p == NUL) { @@ -1494,10 +1552,10 @@ void spell_cat_line(char *buf, char *line, int maxlen) // Only worth concatenating if there is something else than spaces to // concatenate. - int n = (int)(p - (char_u *)line) + 1; + int n = (int)(p - line) + 1; if (n < maxlen - 1) { memset(buf, ' ', (size_t)n); - xstrlcpy(buf + n, (char *)p, (size_t)(maxlen - n)); + xstrlcpy(buf + n, p, (size_t)(maxlen - n)); } } @@ -1553,7 +1611,7 @@ static void spell_load_lang(char *lang) lang); do_cmdline_cmd(autocmd_buf); } else { - smsg(_("Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\""), + smsg(0, _("Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\""), lang, spell_enc(), lang); } } else if (sl.sl_slang != NULL) { @@ -1700,23 +1758,32 @@ void slang_clear_sug(slang_T *lp) // Load one spell file and store the info into a slang_T. // Invoked through do_in_runtimepath(). -static void spell_load_cb(char *fname, void *cookie) +static bool spell_load_cb(int num_fnames, char **fnames, bool all, void *cookie) { spelload_T *slp = (spelload_T *)cookie; - slang_T *slang = spell_load_file(fname, slp->sl_lang, NULL, false); - if (slang == NULL) { - return; - } + for (int i = 0; i < num_fnames; i++) { + slang_T *slang = spell_load_file(fnames[i], slp->sl_lang, NULL, false); + + if (slang == NULL) { + continue; + } - // When a previously loaded file has NOBREAK also use it for the - // ".add" files. - if (slp->sl_nobreak && slang->sl_add) { - slang->sl_nobreak = true; - } else if (slang->sl_nobreak) { - slp->sl_nobreak = true; + // When a previously loaded file has NOBREAK also use it for the + // ".add" files. + if (slp->sl_nobreak && slang->sl_add) { + slang->sl_nobreak = true; + } else if (slang->sl_nobreak) { + slp->sl_nobreak = true; + } + + slp->sl_slang = slang; + + if (!all) { + break; + } } - slp->sl_slang = slang; + return num_fnames > 0; } /// Add a word to the hashtable of common words. @@ -1743,12 +1810,12 @@ void count_common_word(slang_T *lp, char *word, int len, uint8_t count) wordcount_T *wc; hash_T hash = hash_hash(p); const size_t p_len = strlen(p); - hashitem_T *hi = hash_lookup(&lp->sl_wordcount, (const char *)p, p_len, hash); + hashitem_T *hi = hash_lookup(&lp->sl_wordcount, p, p_len, hash); if (HASHITEM_EMPTY(hi)) { wc = xmalloc(offsetof(wordcount_T, wc_word) + p_len + 1); memcpy(wc->wc_word, p, p_len + 1); wc->wc_count = count; - hash_add_item(&lp->sl_wordcount, hi, (char *)wc->wc_word, hash); + hash_add_item(&lp->sl_wordcount, hi, wc->wc_word, hash); } else { wc = HI2WC(hi); wc->wc_count = (uint16_t)(wc->wc_count + count); @@ -1775,7 +1842,7 @@ bool byte_in_str(uint8_t *str, int n) int init_syl_tab(slang_T *slang) { ga_init(&slang->sl_syl_items, sizeof(syl_item_T), 4); - char *p = vim_strchr((char *)slang->sl_syllable, '/'); + char *p = vim_strchr(slang->sl_syllable, '/'); while (p != NULL) { *p++ = NUL; if (*p == NUL) { // trailing slash @@ -1838,7 +1905,7 @@ static int count_syllables(slang_T *slang, const char *word) // No recognized syllable item, at least a syllable char then? int c = utf_ptr2char(p); len = utfc_ptr2len(p); - if (vim_strchr((char *)slang->sl_syllable, c) == NULL) { + if (vim_strchr(slang->sl_syllable, c) == NULL) { skip = false; // No, search for next syllable } else if (!skip) { cnt++; // Yes, count it @@ -1851,7 +1918,7 @@ static int count_syllables(slang_T *slang, const char *word) /// Parse 'spelllang' and set w_s->b_langp accordingly. /// @return NULL if it's OK, an untranslated error message otherwise. -char *did_set_spelllang(win_T *wp) +char *parse_spelllang(win_T *wp) { garray_T ga; char *splp; @@ -1863,14 +1930,12 @@ char *did_set_spelllang(win_T *wp) int c; char lang[MAXWLEN + 1]; char spf_name[MAXPATHL]; - int len; char *p; int round; char *spf; 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 *spl_copy; @@ -1900,7 +1965,7 @@ char *did_set_spelllang(win_T *wp) // Get one language name. copy_option_part(&splp, lang, MAXWLEN, ","); region = NULL; - len = (int)strlen(lang); + int len = (int)strlen(lang); if (!valid_spelllang(lang)) { continue; @@ -1994,7 +2059,7 @@ char *did_set_spelllang(win_T *wp) } else { // This is probably an error. Give a warning and // accept the words anyway. - smsg(_("Warning: region %s not supported"), + smsg(0, _("Warning: region %s not supported"), region); } } else { @@ -2107,7 +2172,7 @@ char *did_set_spelllang(win_T *wp) // REP items. If the language doesn't support it itself use another one // with the same name. E.g. for "en-math" use "en". for (int i = 0; i < ga.ga_len; i++) { - lp = LANGP_ENTRY(ga, i); + langp_T *lp = LANGP_ENTRY(ga, i); // sound folding if (!GA_EMPTY(&lp->lp_slang->sl_sal)) { @@ -2116,7 +2181,7 @@ char *did_set_spelllang(win_T *wp) } else { // find first similar language that does sound folding for (int j = 0; j < ga.ga_len; j++) { - lp2 = LANGP_ENTRY(ga, j); + langp_T *lp2 = LANGP_ENTRY(ga, j); if (!GA_EMPTY(&lp2->lp_slang->sl_sal) && strncmp(lp->lp_slang->sl_name, lp2->lp_slang->sl_name, 2) == 0) { @@ -2133,7 +2198,7 @@ char *did_set_spelllang(win_T *wp) } else { // find first similar language that has REP items for (int j = 0; j < ga.ga_len; j++) { - lp2 = LANGP_ENTRY(ga, j); + langp_T *lp2 = LANGP_ENTRY(ga, j); if (!GA_EMPTY(&lp2->lp_slang->sl_rep) && strncmp(lp->lp_slang->sl_name, lp2->lp_slang->sl_name, 2) == 0) { @@ -2167,14 +2232,14 @@ static void use_midword(slang_T *lp, win_T *wp) return; } - for (char *p = (char *)lp->sl_midword; *p != NUL;) { + for (char *p = 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 = xstrnsave(p, (size_t)l); + wp->w_s->b_spell_ismw_mb = xmemdupz(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); @@ -2215,10 +2280,10 @@ static int find_region(const char *rp, const char *region) /// @param[in] end End of word or NULL for NUL delimited string /// /// @returns Case type of word -int captype(char *word, const char *end) +int captype(const char *word, const char *end) FUNC_ATTR_NONNULL_ARG(1) { - char *p; + const char *p; // find first letter for (p = word; !spell_iswordp_nmw(p, curwin); MB_PTR_ADV(p)) { @@ -2226,7 +2291,7 @@ int captype(char *word, const char *end) return 0; // only non-word characters, illegal word } } - int c = mb_ptr2char_adv((const char **)&p); + int c = mb_ptr2char_adv(&p); bool allcap; bool firstcap = allcap = SPELL_ISUPPER(c); bool past_second = false; // past second word char @@ -2309,7 +2374,7 @@ void spell_reload(void) // window for this buffer in which 'spell' is set. if (*wp->w_s->b_p_spl != NUL) { if (wp->w_p_spell) { - (void)did_set_spelllang(wp); + (void)parse_spelllang(wp); break; } } @@ -2354,8 +2419,8 @@ void clear_spell_chartab(spelltab_T *sp) CLEAR_FIELD(sp->st_isu); for (int i = 0; i < 256; i++) { - sp->st_fold[i] = (char_u)i; - sp->st_upper[i] = (char_u)i; + sp->st_fold[i] = (uint8_t)i; + sp->st_upper[i] = (uint8_t)i; } // We include digits. A word shouldn't start with a digit, but handling @@ -2366,11 +2431,11 @@ void clear_spell_chartab(spelltab_T *sp) for (int i = 'A'; i <= 'Z'; i++) { sp->st_isw[i] = true; sp->st_isu[i] = true; - sp->st_fold[i] = (char_u)(i + 0x20); + sp->st_fold[i] = (uint8_t)(i + 0x20); } for (int i = 'a'; i <= 'z'; i++) { sp->st_isw[i] = true; - sp->st_upper[i] = (char_u)(i - 0x20); + sp->st_upper[i] = (uint8_t)(i - 0x20); } } @@ -2391,8 +2456,8 @@ void init_spell_chartab(void) // The folded/upper-cased value is different between latin1 and // utf8 for 0xb5, causing E763 for no good reason. Use the latin1 // value for utf-8 to avoid this. - spelltab.st_fold[i] = (f < 256) ? (char_u)f : (char_u)i; - spelltab.st_upper[i] = (u < 256) ? (char_u)u : (char_u)i; + spelltab.st_fold[i] = (f < 256) ? (uint8_t)f : (uint8_t)i; + spelltab.st_upper[i] = (u < 256) ? (uint8_t)u : (uint8_t)i; } } @@ -2480,7 +2545,7 @@ static bool spell_iswordp_w(const int *p, const win_T *wp) // Uses the character definitions from the .spl file. // When using a multi-byte 'encoding' the length may change! // Returns FAIL when something wrong. -int spell_casefold(const win_T *wp, char *str, int len, char *buf, int buflen) +int spell_casefold(const win_T *wp, const char *str, int len, char *buf, int buflen) FUNC_ATTR_NONNULL_ALL { if (len >= buflen) { @@ -2491,12 +2556,12 @@ int spell_casefold(const win_T *wp, char *str, int len, char *buf, int buflen) int outi = 0; // Fold one character at a time. - for (char *p = str; p < str + len;) { + for (const char *p = str; p < str + len;) { if (outi + MB_MAXBYTES > buflen) { buf[outi] = NUL; return FAIL; } - int c = mb_cptr2char_adv((const char **)&p); + int c = mb_cptr2char_adv(&p); // Exception: greek capital sigma 0x03A3 folds to 0x03C3, except // when it is the last character in a word, then it folds to @@ -2519,25 +2584,24 @@ int spell_casefold(const win_T *wp, char *str, int len, char *buf, int buflen) } // Check if the word at line "lnum" column "col" is required to start with a -// capital. This uses 'spellcapcheck' of the current buffer. -bool check_need_cap(linenr_T lnum, colnr_T col) +// capital. This uses 'spellcapcheck' of the buffer in window "wp". +bool check_need_cap(win_T *wp, linenr_T lnum, colnr_T col) { - bool need_cap = false; - - if (curwin->w_s->b_cap_prog == NULL) { + if (wp->w_s->b_cap_prog == NULL) { return false; } - char *line = get_cursor_line_ptr(); + bool need_cap = false; + char *line = col ? ml_get_buf(wp->w_buffer, lnum) : NULL; char *line_copy = NULL; colnr_T endcol = 0; - if (getwhitecols(line) >= (int)col) { + if (col == 0 || getwhitecols(line) >= 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); + line = ml_get_buf(wp->w_buffer, lnum - 1); if (*skipwhite(line) == NUL) { need_cap = true; } else { @@ -2554,13 +2618,13 @@ bool check_need_cap(linenr_T lnum, colnr_T col) if (endcol > 0) { // Check if sentence ends before the bad word. regmatch_T regmatch = { - .regprog = curwin->w_s->b_cap_prog, + .regprog = wp->w_s->b_cap_prog, .rm_ic = false }; char *p = line + endcol; - for (;;) { + while (true) { MB_PTR_BACK(line, p); - if (p == line || spell_iswordp_nmw(p, curwin)) { + if (p == line || spell_iswordp_nmw(p, wp)) { break; } if (vim_regexec(®match, p, 0) @@ -2569,7 +2633,7 @@ bool check_need_cap(linenr_T lnum, colnr_T col) break; } } - curwin->w_s->b_cap_prog = regmatch.regprog; + wp->w_s->b_cap_prog = regmatch.regprog; } xfree(line_copy); @@ -2588,9 +2652,11 @@ void ex_spellrepall(exarg_T *eap) emsg(_("E752: No previous spell replacement")); return; } - int addlen = (int)(strlen(repl_to) - strlen(repl_from)); + const size_t repl_from_len = strlen(repl_from); + const size_t repl_to_len = strlen(repl_to); + const int addlen = (int)(repl_to_len - repl_from_len); - size_t frompatlen = strlen(repl_from) + 7; + const size_t frompatlen = repl_from_len + 7; char *frompat = xmalloc(frompatlen); snprintf(frompat, frompatlen, "\\V\\<%s\\>", repl_from); p_ws = false; @@ -2599,7 +2665,7 @@ 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, '/', '/', frompat, 1, SEARCH_KEEP, NULL) == 0 || u_save_cursor() == FAIL) { break; } @@ -2607,14 +2673,15 @@ void ex_spellrepall(exarg_T *eap) // Only replace when the right word isn't there yet. This happens // when changing "etc" to "etc.". char *line = get_cursor_line_ptr(); - if (addlen <= 0 || strncmp(line + curwin->w_cursor.col, - repl_to, strlen(repl_to)) != 0) { + if (addlen <= 0 + || strncmp(line + curwin->w_cursor.col, repl_to, repl_to_len) != 0) { char *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 + repl_from_len); ml_replace(curwin->w_cursor.lnum, p, false); - changed_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col); + inserted_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col, + (int)repl_from_len, (int)repl_to_len); if (curwin->w_cursor.lnum != prev_lnum) { sub_nlines++; @@ -2622,7 +2689,7 @@ void ex_spellrepall(exarg_T *eap) } sub_nsubs++; } - curwin->w_cursor.col += (colnr_T)strlen(repl_to); + curwin->w_cursor.col += (colnr_T)repl_to_len; } p_ws = save_ws; @@ -2643,10 +2710,10 @@ 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 *word, char *wcopy, bool upper) +void onecap_copy(const char *word, char *wcopy, bool upper) { - char *p = word; - int c = mb_cptr2char_adv((const char **)&p); + const char *p = word; + int c = mb_cptr2char_adv(&p); if (upper) { c = SPELL_TOUPPER(c); } else { @@ -2658,26 +2725,26 @@ void onecap_copy(char *word, char *wcopy, bool upper) // Make a copy of "word" with all the letters upper cased into // "wcopy[MAXWLEN]". The result is NUL terminated. -void allcap_copy(char *word, char *wcopy) +void allcap_copy(const char *word, char *wcopy) { - char_u *d = (char_u *)wcopy; - for (char *s = word; *s != NUL;) { - int c = mb_cptr2char_adv((const char **)&s); + char *d = wcopy; + for (const char *s = word; *s != NUL;) { + int c = mb_cptr2char_adv(&s); if (c == 0xdf) { c = 'S'; - if (d - (char_u *)wcopy >= MAXWLEN - 1) { + if (d - wcopy >= MAXWLEN - 1) { break; } - *d++ = (char_u)c; + *d++ = (char)c; } else { c = SPELL_TOUPPER(c); } - if (d - (char_u *)wcopy >= MAXWLEN - MB_MAXBYTES) { + if (d - wcopy >= MAXWLEN - MB_MAXBYTES) { break; } - d += utf_char2bytes(c, (char *)d); + d += utf_char2bytes(c, d); } *d = NUL; } @@ -2777,7 +2844,7 @@ void spell_soundfold(slang_T *slang, char *inword, bool folded, char *res) // Perform sound folding of "inword" into "res" according to SOFOFROM and // SOFOTO lines. -static void spell_soundfold_sofo(slang_T *slang, char *inword, char *res) +static void spell_soundfold_sofo(slang_T *slang, const char *inword, char *res) { int ri = 0; @@ -2785,8 +2852,8 @@ static void spell_soundfold_sofo(slang_T *slang, char *inword, char *res) // The sl_sal_first[] table contains the translation for chars up to // 255, sl_sal the rest. - for (char *s = inword; *s != NUL;) { - int c = mb_cptr2char_adv((const char **)&s); + for (const char *s = inword; *s != NUL;) { + int c = mb_cptr2char_adv(&s); if (utf_class(c) == 0) { c = ' '; } else if (c < 256) { @@ -2796,7 +2863,7 @@ static void spell_soundfold_sofo(slang_T *slang, char *inword, char *res) if (ip == NULL) { // empty list, can't match c = NUL; } else { - for (;;) { // find "c" in the list + while (true) { // find "c" in the list if (*ip == 0) { // not found c = NUL; break; @@ -2834,7 +2901,6 @@ static void spell_soundfold_wsal(slang_T *slang, const char *inword, char *res) int j, z; int reslen; int k = 0; - int z0; int k0; int n0; int pri; @@ -2846,8 +2912,8 @@ static void spell_soundfold_wsal(slang_T *slang, const char *inword, char *res) // Remove accents, if wanted. We actually remove all non-word characters. // But keep white space. int wordlen = 0; - for (const char *s = (char *)inword; *s != NUL;) { - const char_u *t = (char_u *)s; + for (const char *s = inword; *s != NUL;) { + const char *t = s; int c = mb_cptr2char_adv(&s); if (slang->sl_rem_accents) { if (utf_class(c) == 0) { @@ -2858,7 +2924,7 @@ static void spell_soundfold_wsal(slang_T *slang, const char *inword, char *res) did_white = true; } else { did_white = false; - if (!spell_iswordp_nmw((char *)t, curwin)) { + if (!spell_iswordp_nmw(t, curwin)) { continue; } } @@ -2875,7 +2941,7 @@ static void spell_soundfold_wsal(slang_T *slang, const char *inword, char *res) while ((c = word[i]) != NUL) { // Start with the first rule that has the character in the word. int n = slang->sl_sal_first[c & 0xff]; - z0 = 0; + int z0 = 0; if (n >= 0) { // Check all rules for the same index byte. @@ -2915,10 +2981,10 @@ static void spell_soundfold_wsal(slang_T *slang, const char *inword, char *res) } k++; } - char_u *s = (char_u *)smp[n].sm_rules; + char *s = smp[n].sm_rules; pri = 5; // default priority - p0 = *s; + p0 = (uint8_t)(*s); k0 = k; while (*s == '-' && k > 1) { k--; @@ -2929,7 +2995,7 @@ static void spell_soundfold_wsal(slang_T *slang, const char *inword, char *res) } if (ascii_isdigit(*s)) { // determine priority - pri = *s - '0'; + pri = (uint8_t)(*s) - '0'; s++; } if (*s == '^' && *(s + 1) == '^') { @@ -2992,7 +3058,7 @@ static void spell_soundfold_wsal(slang_T *slang, const char *inword, char *res) } p0 = 5; - s = (char_u *)smp[n0].sm_rules; + s = smp[n0].sm_rules; while (*s == '-') { // "k0" gets NOT reduced because // "if (k0 == k)" @@ -3002,7 +3068,7 @@ static void spell_soundfold_wsal(slang_T *slang, const char *inword, char *res) s++; } if (ascii_isdigit(*s)) { - p0 = *s - '0'; + p0 = (uint8_t)(*s) - '0'; s++; } @@ -3033,8 +3099,8 @@ static void spell_soundfold_wsal(slang_T *slang, const char *inword, char *res) // replace string ws = smp[n].sm_to_w; - s = (char_u *)smp[n].sm_rules; - p0 = (vim_strchr((char *)s, '<') != NULL) ? 1 : 0; + s = smp[n].sm_rules; + p0 = (vim_strchr(s, '<') != NULL) ? 1 : 0; if (p0 == 1 && z == 0) { // rule with '<' is used if (reslen > 0 && ws != NULL && *ws != NUL @@ -3077,7 +3143,7 @@ static void spell_soundfold_wsal(slang_T *slang, const char *inword, char *res) } else { c = *ws; } - if (strstr((char *)s, "^^") != NULL) { + if (strstr(s, "^^") != NULL) { if (c != NUL) { wres[reslen++] = c; } @@ -3130,9 +3196,9 @@ void ex_spellinfo(exarg_T *eap) for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len && !got_int; lpi++) { langp_T *const lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi); msg_puts("file: "); - msg_puts((const char *)lp->lp_slang->sl_fname); + msg_puts(lp->lp_slang->sl_fname); msg_putchar('\n'); - const char *const p = (const char *)lp->lp_slang->sl_info; + const char *const p = lp->lp_slang->sl_info; if (p != NULL) { msg_puts(p); msg_putchar('\n'); @@ -3153,17 +3219,15 @@ void ex_spelldump(exarg_T *eap) if (no_spell_checking(curwin)) { return; } - char *spl; - long dummy; - (void)get_option_value("spl", &dummy, &spl, NULL, OPT_LOCAL); + OptVal spl = get_option_value("spl", NULL, OPT_LOCAL, NULL); // Create a new empty buffer in a new window. do_cmdline_cmd("new"); // enable spelling locally in the new window - set_option_value_give_err("spell", true, "", OPT_LOCAL); - set_option_value_give_err("spl", dummy, spl, OPT_LOCAL); - xfree(spl); + set_option_value_give_err("spell", BOOLEAN_OPTVAL(true), OPT_LOCAL); + set_option_value_give_err("spl", spl, OPT_LOCAL); + optval_free(spl); if (!buf_is_empty(curbuf)) { return; @@ -3195,7 +3259,7 @@ void spell_dump_compl(char *pat, int ic, Direction *dir, int dumpflags_arg) int curi[MAXWLEN]; char word[MAXWLEN]; int c; - char *byts; + uint8_t *byts; idx_T *idxs; linenr_T lnum = 0; int depth; @@ -3238,11 +3302,9 @@ void spell_dump_compl(char *pat, int ic, Direction *dir, int dumpflags_arg) } } - if (do_region && region_names != NULL) { - if (pat == NULL) { - vim_snprintf(IObuff, IOSIZE, "/regions=%s", region_names); - ml_append(lnum++, IObuff, (colnr_T)0, false); - } + if (do_region && region_names != NULL && pat == NULL) { + vim_snprintf(IObuff, IOSIZE, "/regions=%s", region_names); + ml_append(lnum++, IObuff, 0, false); } else { do_region = false; } @@ -3257,7 +3319,7 @@ void spell_dump_compl(char *pat, int ic, Direction *dir, int dumpflags_arg) if (pat == NULL) { vim_snprintf(IObuff, IOSIZE, "# file: %s", slang->sl_fname); - ml_append(lnum++, IObuff, (colnr_T)0, false); + ml_append(lnum++, IObuff, 0, false); } // When matching with a pattern and there are no prefixes only use @@ -3297,7 +3359,7 @@ void spell_dump_compl(char *pat, int ic, Direction *dir, int dumpflags_arg) // Do one more byte at this node. n = arridx[depth] + curi[depth]; curi[depth]++; - c = (uint8_t)byts[n]; + c = byts[n]; if (c == 0 || depth >= MAXWLEN - 1) { // End of word or reached maximum length, deal with the // word. @@ -3426,7 +3488,7 @@ static void dump_word(slang_T *slang, char *word, char *pat, Direction *dir, int } } - ml_append(lnum, p, (colnr_T)0, false); + ml_append(lnum, p, 0, false); } else if (((dumpflags & DUMPFLAG_ICASE) ? mb_strnicmp(p, pat, strlen(pat)) == 0 : strncmp(p, pat, strlen(pat)) == 0) @@ -3463,7 +3525,7 @@ static linenr_T dump_prefixes(slang_T *slang, char *word, char *pat, Direction * has_word_up = true; } - char_u *byts = (char_u *)slang->sl_pbyts; + uint8_t *byts = slang->sl_pbyts; idx_T *idxs = slang->sl_pidxs; if (byts != NULL) { // array not is empty // Loop over all prefixes, building them byte-by-byte in prefix[]. @@ -3585,7 +3647,7 @@ static bool spell_expand_need_cap; void spell_expand_check_cap(colnr_T col) { - spell_expand_need_cap = check_need_cap(curwin->w_cursor.lnum, col); + spell_expand_need_cap = check_need_cap(curwin, curwin->w_cursor.lnum, col); } // Get list of spelling suggestions. @@ -3613,16 +3675,16 @@ bool valid_spellfile(const char *val) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { for (const char *s = val; *s != NUL; s++) { - if (!vim_isfilec((uint8_t)(*s)) && *s != ',' && *s != ' ') { + if (!vim_is_fname_char((uint8_t)(*s))) { return false; } } return true; } -char *did_set_spell_option(bool is_spellfile) +const char *did_set_spell_option(bool is_spellfile) { - char *errmsg = NULL; + const char *errmsg = NULL; if (is_spellfile) { int l = (int)strlen(curwin->w_s->b_p_spf); @@ -3638,7 +3700,7 @@ char *did_set_spell_option(bool is_spellfile) FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_buffer == curbuf && wp->w_p_spell) { - errmsg = did_set_spelllang(wp); + errmsg = parse_spelllang(wp); break; } } @@ -3647,7 +3709,7 @@ char *did_set_spell_option(bool is_spellfile) /// Set curbuf->b_cap_prog to the regexp program for 'spellcapcheck'. /// Return error message when failed, NULL when OK. -char *compile_cap_prog(synblock_T *synblock) +const char *compile_cap_prog(synblock_T *synblock) FUNC_ATTR_NONNULL_ALL { regprog_T *rp = synblock->b_cap_prog; |