# HG changeset patch # User vimboss # Date 1118178002 0 # Node ID 04bf54c587f45b7bc777537a4c70d1c614a98d4a # Parent ba02e70f9b18693b4752690b9da3c49fb058a9e8 updated for version 7.0082 diff --git a/src/ex_docmd.c b/src/ex_docmd.c --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -222,8 +222,7 @@ static void ex_popup __ARGS((exarg_T *ea #endif #ifndef FEAT_SYN_HL # define ex_syntax ex_ni -#endif -#if !defined(FEAT_SYN_HL) || !defined(FEAT_MBYTE) +# define ex_spell ex_ni # define ex_mkspell ex_ni #endif #ifndef FEAT_MZSCHEME @@ -9906,7 +9905,7 @@ ex_viminfo(eap) #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) || defined(PROTO) /* * Make a dialog message in "buff[IOSIZE]". - * "format" must contain "%.*s". + * "format" must contain "%s". */ void dialog_msg(buff, format, fname) @@ -9914,15 +9913,9 @@ dialog_msg(buff, format, fname) char *format; char_u *fname; { - int len; - if (fname == NULL) fname = (char_u *)_("Untitled"); - len = (int)STRLEN(format) + (int)STRLEN(fname); - if (len >= IOSIZE) - sprintf((char *)buff, format, (int)(IOSIZE - STRLEN(format)), fname); - else - sprintf((char *)buff, format, (int)STRLEN(fname), fname); + vim_snprintf((char *)buff, IOSIZE, format, fname); } #endif diff --git a/src/normal.c b/src/normal.c --- a/src/normal.c +++ b/src/normal.c @@ -4665,6 +4665,27 @@ dozet: #endif /* FEAT_FOLDING */ +#ifdef FEAT_SYN_HL + case 'g': /* "zg": add good word to word list */ + case 'w': /* "zw": add wrong word to word list */ + { + char_u *ptr = NULL; + int len; + + if (checkclearop(cap->oap)) + break; +# ifdef FEAT_VISUAL + if (VIsual_active && get_visual_text(cap, &ptr, &len) + == FAIL) + return; +# endif + if (ptr == NULL && (len = find_ident_under_cursor(&ptr, + FIND_IDENT)) == 0) + return; + spell_add_word(ptr, len, nchar == 'w'); + } +#endif + default: clearopbeep(cap->oap); } diff --git a/src/spell.c b/src/spell.c --- a/src/spell.c +++ b/src/spell.c @@ -151,6 +151,8 @@ struct slang_S { slang_T *sl_next; /* next language */ char_u *sl_name; /* language name "en", "en.rare", "nl", etc. */ + char_u *sl_fname; /* name of .spl file */ + int sl_add; /* TRUE if it's an addition. */ char_u *sl_fbyts; /* case-folded word bytes */ int *sl_fidxs; /* case-folded word indexes */ char_u *sl_kbyts; /* keep-case word bytes */ @@ -248,17 +250,19 @@ static int set_spell_finish __ARGS((spel static slang_T *slang_alloc __ARGS((char_u *lang)); static void slang_free __ARGS((slang_T *lp)); +static void slang_clear __ARGS((slang_T *lp)); static void find_word __ARGS((matchinf_T *mip, int keepcap)); static void spell_load_lang __ARGS((char_u *lang)); -static void spell_load_file __ARGS((char_u *fname, void *cookie)); +static char_u *spell_enc __ARGS((void)); +static void spell_load_cb __ARGS((char_u *fname, void *cookie)); +static void spell_load_file __ARGS((char_u *fname, char_u *lang, slang_T *old_lp)); static int read_tree __ARGS((FILE *fd, char_u *byts, int *idxs, int maxidx, int startidx)); static int find_region __ARGS((char_u *rp, char_u *region)); static int captype __ARGS((char_u *word, char_u *end)); +static void spell_reload_one __ARGS((char_u *fname)); static int set_spell_charflags __ARGS((char_u *flags, int cnt, char_u *upp)); -#ifdef FEAT_MBYTE static int set_spell_chartab __ARGS((char_u *fol, char_u *low, char_u *upp)); static void write_spell_chartab __ARGS((FILE *fd)); -#endif static int spell_isupper __ARGS((int c)); static int spell_casefold __ARGS((char_u *p, int len, char_u *buf, int buflen)); @@ -633,7 +637,7 @@ spell_move_to(dir, allwords) int col; int can_spell; - if (!curwin->w_p_spell || *curwin->w_buffer->b_p_spl == NUL) + if (!curwin->w_p_spell || *curbuf->b_p_spl == NUL) { EMSG(_("E756: Spell checking not enabled")); return FAIL; @@ -746,44 +750,59 @@ spell_move_to(dir, allwords) /* * Load word list(s) for "lang" from Vim spell file(s). - * "lang" must be the language without the region: "en". + * "lang" must be the language without the region: e.g., "en". */ static void spell_load_lang(lang) char_u *lang; { - char_u fname_enc[80]; - char_u *p; + char_u fname_enc[85]; int r; char_u langcp[MAXWLEN + 1]; - /* Copy the language name to pass it to spell_load_file() as a cookie. + /* Copy the language name to pass it to spell_load_cb() as a cookie. * It's truncated when an error is detected. */ STRCPY(langcp, lang); - /* Find all spell files for "lang" in 'runtimepath' and load them. - * Use 'encoding', except that we use "latin1" for "latin9". */ -#ifdef FEAT_MBYTE - if (STRLEN(p_enc) < 60 && STRCMP(p_enc, "iso-8859-15") != 0) - p = p_enc; - else -#endif - p = (char_u *)"latin1"; - vim_snprintf((char *)fname_enc, sizeof(fname_enc), - "spell/%s.%s.spl", lang, p); - r = do_in_runtimepath(fname_enc, TRUE, spell_load_file, &langcp); + /* + * Find the first spell file for "lang" in 'runtimepath' and load it. + */ + vim_snprintf((char *)fname_enc, sizeof(fname_enc) - 5, + "spell/%s.%s.spl", lang, spell_enc()); + r = do_in_runtimepath(fname_enc, FALSE, spell_load_cb, &langcp); if (r == FAIL && *langcp != NUL) { /* Try loading the ASCII version. */ - vim_snprintf((char *)fname_enc, sizeof(fname_enc), + vim_snprintf((char *)fname_enc, sizeof(fname_enc) - 5, "spell/%s.ascii.spl", lang); - r = do_in_runtimepath(fname_enc, TRUE, spell_load_file, &langcp); + r = do_in_runtimepath(fname_enc, FALSE, spell_load_cb, &langcp); } if (r == FAIL) smsg((char_u *)_("Warning: Cannot find word list \"%s\""), fname_enc + 6); + else if (*langcp != NUL) + { + /* Load all the additions. */ + STRCPY(fname_enc + STRLEN(fname_enc) - 3, "add.spl"); + do_in_runtimepath(fname_enc, TRUE, spell_load_cb, &langcp); + } +} + +/* + * Return the encoding used for spell checking: Use 'encoding', except that we + * use "latin1" for "latin9". And limit to 60 characters (just in case). + */ + static char_u * +spell_enc() +{ + +#ifdef FEAT_MBYTE + if (STRLEN(p_enc) < 60 && STRCMP(p_enc, "iso-8859-15") != 0) + return p_enc; +#endif + return (char_u *)"latin1"; } /* @@ -813,13 +832,29 @@ slang_free(lp) slang_T *lp; { vim_free(lp->sl_name); + vim_free(lp->sl_fname); + slang_clear(lp); + vim_free(lp); +} + +/* + * Clear an slang_T so that the file can be reloaded. + */ + static void +slang_clear(lp) + slang_T *lp; +{ vim_free(lp->sl_fbyts); + lp->sl_fbyts = NULL; vim_free(lp->sl_kbyts); + lp->sl_kbyts = NULL; vim_free(lp->sl_fidxs); + lp->sl_fidxs = NULL; vim_free(lp->sl_kidxs); + lp->sl_kidxs = NULL; ga_clear(&lp->sl_rep); vim_free(lp->sl_try); - vim_free(lp); + lp->sl_try = NULL; } /* @@ -827,11 +862,28 @@ slang_free(lp) * Invoked through do_in_runtimepath(). */ static void -spell_load_file(fname, cookie) +spell_load_cb(fname, cookie) char_u *fname; void *cookie; /* points to the language name */ { - char_u *lang = cookie; + spell_load_file(fname, (char_u *)cookie, NULL); +} + +/* + * Load one spell file and store the info into a slang_T. + * + * This is invoked in two ways: + * - From spell_load_cb() to load a spell file for the first time. "lang" is + * the language name, "old_lp" is NULL. Will allocate an slang_T. + * - To reload a spell file that was changed. "lang" is NULL and "old_lp" + * points to the existing slang_T. + */ + static void +spell_load_file(fname, lang, old_lp) + char_u *fname; + char_u *lang; + slang_T *old_lp; +{ FILE *fd; char_u buf[MAXWLEN + 1]; char_u *p; @@ -844,16 +896,35 @@ spell_load_file(fname, cookie) char_u *fol; slang_T *lp = NULL; - fd = fopen((char *)fname, "r"); + fd = mch_fopen((char *)fname, "r"); if (fd == NULL) { EMSG2(_(e_notopen), fname); goto endFAIL; } - - lp = slang_alloc(lang); - if (lp == NULL) - goto endFAIL; + if (p_verbose > 2) + { + verbose_enter(); + smsg((char_u *)_("Reading spell file \"%s\""), fname); + verbose_leave(); + } + + if (old_lp == NULL) + { + lp = slang_alloc(lang); + if (lp == NULL) + goto endFAIL; + + /* Remember the file name, used to reload the file when it's updated. */ + lp->sl_fname = vim_strsave(fname); + if (lp->sl_fname == NULL) + goto endFAIL; + + /* Check for .add.spl. */ + lp->sl_add = strstr((char *)gettail(fname), ".add.") != NULL; + } + else + lp = old_lp; /* Set sourcing_name, so that error messages mention the file name. */ sourcing_name = fname; @@ -978,15 +1049,20 @@ formerr: } } - lp->sl_next = first_lang; - first_lang = lp; + /* For a new file link it in the list of spell files. */ + if (old_lp == NULL) + { + lp->sl_next = first_lang; + first_lang = lp; + } goto endOK; endFAIL: - /* truncating the name signals the error to spell_load_lang() */ - *lang = NUL; - if (lp != NULL) + if (lang != NULL) + /* truncating the name signals the error to spell_load_lang() */ + *lang = NUL; + if (lp != NULL && old_lp == NULL) slang_free(lp); endOK: @@ -1143,7 +1219,7 @@ did_set_spelllang(buf) for (lp = first_lang; lp != NULL; lp = lp->sl_next) if (STRNICMP(lp->sl_name, lang, 2) == 0) { - if (region == NULL) + if (region == NULL || lp->sl_add) region_mask = REGION_ALL; else { @@ -1237,10 +1313,11 @@ captype(word, end) if (p >= end) return 0; /* only non-word characters, illegal word */ #ifdef FEAT_MBYTE - c = mb_ptr2char_adv(&p); -#else - c = *p++; + if (has_mbyte) + c = mb_ptr2char_adv(&p); + else #endif + c = *p++; firstcap = allcap = spell_isupper(c); /* @@ -1307,11 +1384,27 @@ spell_reload() } # endif - -#if defined(FEAT_MBYTE) || defined(PROTO) +/* + * Reload the spell file "fname" if it's loaded. + */ + static void +spell_reload_one(fname) + char_u *fname; +{ + slang_T *lp; + + for (lp = first_lang; lp != NULL; lp = lp->sl_next) + if (fullpathcmp(fname, lp->sl_fname, FALSE) == FPC_SAME) + { + slang_clear(lp); + spell_load_file(fname, NULL, lp); + redraw_all_later(NOT_VALID); + } +} + + /* * Functions for ":mkspell". - * Only possible with the multi-byte feature. */ #define MAXLINELEN 500 /* Maximum length in bytes of a line in a .aff @@ -1323,8 +1416,8 @@ typedef struct afffile_S { char_u *af_enc; /* "SET", normalized, alloc'ed string or NULL */ char_u *af_try; /* "TRY" line in "af_enc" encoding */ - int af_rar; /* ID for rare word */ - int af_huh; /* ID for keep-case word */ + int af_rar; /* RAR ID for rare word */ + int af_kep; /* KEP ID for keep-case word */ hashtab_T af_pref; /* hashtable for prefixes, affheader_T */ hashtab_T af_suff; /* hashtable for suffixes, affheader_T */ garray_T af_rep; /* list of repentry_T entries from REP lines */ @@ -1395,9 +1488,11 @@ typedef struct spellinfo_S wordnode_T *si_keeproot; /* tree with keep-case words */ sblock_T *si_blocks; /* memory blocks used */ int si_ascii; /* handling only ASCII words */ + int si_add; /* addition file */ int si_region; /* region mask */ vimconv_T si_conv; /* for conversion to 'encoding' */ int si_memtot; /* runtime memory used */ + int si_verbose; /* verbose messages */ } spellinfo_T; static afffile_T *spell_read_aff __ARGS((char_u *fname, spellinfo_T *spin)); @@ -1412,11 +1507,13 @@ static void free_blocks __ARGS((sblock_T static wordnode_T *wordtree_alloc __ARGS((sblock_T **blp)); static int store_word __ARGS((char_u *word, spellinfo_T *spin, int flags)); static int tree_add_word __ARGS((char_u *word, wordnode_T *tree, int flags, int region, sblock_T **blp)); -static void wordtree_compress __ARGS((wordnode_T *root)); +static void wordtree_compress __ARGS((wordnode_T *root, spellinfo_T *spin)); static int node_compress __ARGS((wordnode_T *node, hashtab_T *ht, int *tot)); static int node_equal __ARGS((wordnode_T *n1, wordnode_T *n2)); static void write_vim_spell __ARGS((char_u *fname, spellinfo_T *spin, int regcount, char_u *regchars)); static int put_tree __ARGS((FILE *fd, wordnode_T *node, int index, int regionmask)); +static void mkspell __ARGS((int fcount, char_u **fnames, int ascii, int overwrite, int verbose)); +static void init_spellfile __ARGS((void)); /* * Read an affix ".aff" file. @@ -1447,15 +1544,22 @@ spell_read_aff(fname, spin) /* * Open the file. */ - fd = fopen((char *)fname, "r"); + fd = mch_fopen((char *)fname, "r"); if (fd == NULL) { EMSG2(_(e_notopen), fname); return NULL; } - smsg((char_u *)_("Reading affix file %s..."), fname); - out_flush(); + if (spin->si_verbose || p_verbose > 2) + { + if (!spin->si_verbose) + verbose_enter(); + smsg((char_u *)_("Reading affix file %s..."), fname); + out_flush(); + if (!spin->si_verbose) + verbose_leave(); + } /* * Allocate and init the afffile_T structure. @@ -1481,6 +1585,7 @@ spell_read_aff(fname, spin) /* Convert from "SET" to 'encoding' when needed. */ vim_free(pc); +#ifdef FEAT_MBYTE if (spin->si_conv.vc_type != CONV_NONE) { pc = string_convert(&spin->si_conv, rline, NULL); @@ -1493,6 +1598,7 @@ spell_read_aff(fname, spin) line = pc; } else +#endif { pc = NULL; line = rline; @@ -1523,6 +1629,7 @@ spell_read_aff(fname, spin) if (STRCMP(items[0], "SET") == 0 && itemcnt == 2 && aff->af_enc == NULL) { +#ifdef FEAT_MBYTE /* Setup for conversion from "ENC" to 'encoding'. */ aff->af_enc = enc_canonize(items[1]); if (aff->af_enc != NULL && !spin->si_ascii @@ -1530,6 +1637,9 @@ spell_read_aff(fname, spin) p_enc) == FAIL) smsg((char_u *)_("Conversion in %s not supported: from %s to %s"), fname, aff->af_enc, p_enc); +#else + smsg((char_u *)_("Conversion in %s not supported"), fname); +#endif } else if (STRCMP(items[0], "NOSPLITSUGS") == 0 && itemcnt == 1) { @@ -1547,10 +1657,10 @@ spell_read_aff(fname, spin) if (items[1][1] != NUL) smsg((char_u *)_(e_affname), fname, lnum, items[1]); } - else if (STRCMP(items[0], "HUH") == 0 && itemcnt == 2 - && aff->af_huh == 0) + else if (STRCMP(items[0], "KEP") == 0 && itemcnt == 2 + && aff->af_kep == 0) { - aff->af_huh = items[1][0]; + aff->af_kep = items[1][0]; if (items[1][1] != NUL) smsg((char_u *)_(e_affname), fname, lnum, items[1]); } @@ -1782,7 +1892,7 @@ spell_read_dic(fname, spin, affile) /* * Open the file. */ - fd = fopen((char *)fname, "r"); + fd = mch_fopen((char *)fname, "r"); if (fd == NULL) { EMSG2(_(e_notopen), fname); @@ -1792,8 +1902,15 @@ spell_read_dic(fname, spin, affile) /* The hashtable is only used to detect duplicated words. */ hash_init(&ht); - smsg((char_u *)_("Reading dictionary file %s..."), fname); - out_flush(); + if (spin->si_verbose || p_verbose > 2) + { + if (!spin->si_verbose) + verbose_enter(); + smsg((char_u *)_("Reading dictionary file %s..."), fname); + out_flush(); + if (!spin->si_verbose) + verbose_leave(); + } /* Read and ignore the first line: word count. */ (void)vim_fgets(line, MAXLINELEN, fd); @@ -1820,7 +1937,7 @@ spell_read_dic(fname, spin, affile) line[l] = NUL; /* This takes time, print a message now and then. */ - if ((lnum & 0x3ff) == 0) + if (spin->si_verbose && (lnum & 0x3ff) == 0) { vim_snprintf((char *)message, sizeof(message), _("line %6d - %s"), lnum, line); @@ -1844,6 +1961,7 @@ spell_read_dic(fname, spin, affile) continue; } +#ifdef FEAT_MBYTE /* Convert from "SET" to 'encoding' when needed. */ if (spin->si_conv.vc_type != CONV_NONE) { @@ -1857,6 +1975,7 @@ spell_read_dic(fname, spin, affile) w = pc; } else +#endif { pc = NULL; w = line; @@ -1883,8 +2002,8 @@ spell_read_dic(fname, spin, affile) { /* Check for affix name that stands for keep-case word and stands * for rare word (if defined). */ - if (affile->af_huh != NUL - && vim_strchr(afflist, affile->af_huh) != NULL) + if (affile->af_kep != NUL + && vim_strchr(afflist, affile->af_kep) != NULL) flags |= WF_KEEPCAP; if (affile->af_rar != NUL && vim_strchr(afflist, affile->af_rar) != NULL) @@ -1983,9 +2102,17 @@ store_aff_word(word, spin, afflist, ht, STRCPY(newword, ae->ae_add); p = word; if (ae->ae_chop != NULL) + { /* Skip chop string. */ - for (i = mb_charlen(ae->ae_chop); i > 0; --i) +#ifdef FEAT_MBYTE + if (has_mbyte) + i = mb_charlen(ae->ae_chop); + else +#endif + i = STRLEN(ae->ae_chop); + for ( ; i > 0; --i) mb_ptr_adv(p); + } STRCAT(newword, p); } else @@ -1996,7 +2123,13 @@ store_aff_word(word, spin, afflist, ht, { /* Remove chop string. */ p = newword + STRLEN(newword); - for (i = mb_charlen(ae->ae_chop); i > 0; --i) +#ifdef FEAT_MBYTE + if (has_mbyte) + i = mb_charlen(ae->ae_chop); + else +#endif + i = STRLEN(ae->ae_chop); + for ( ; i > 0; --i) mb_ptr_back(newword, p); *p = NUL; } @@ -2040,21 +2173,27 @@ spell_read_wordfile(fname, spin) int retval = OK; int did_word = FALSE; int non_ascii = 0; - char_u *enc; int flags; /* * Open the file. */ - fd = fopen((char *)fname, "r"); + fd = mch_fopen((char *)fname, "r"); if (fd == NULL) { EMSG2(_(e_notopen), fname); return FAIL; } - smsg((char_u *)_("Reading word file %s..."), fname); - out_flush(); + if (spin->si_verbose || p_verbose > 2) + { + if (!spin->si_verbose) + verbose_enter(); + smsg((char_u *)_("Reading word file %s..."), fname); + out_flush(); + if (!spin->si_verbose) + verbose_leave(); + } /* * Read all the lines in the file one by one. @@ -2078,6 +2217,7 @@ spell_read_wordfile(fname, spin) /* Convert from "=encoding={encoding}" to 'encoding' when needed. */ vim_free(pc); +#ifdef FEAT_MBYTE if (spin->si_conv.vc_type != CONV_NONE) { pc = string_convert(&spin->si_conv, rline, NULL); @@ -2090,6 +2230,7 @@ spell_read_wordfile(fname, spin) line = pc; } else +#endif { pc = NULL; line = rline; @@ -2110,6 +2251,9 @@ spell_read_wordfile(fname, spin) fname, lnum, line); else { +#ifdef FEAT_MBYTE + char_u *enc; + /* Setup for conversion to 'encoding'. */ enc = enc_canonize(line + 10); if (enc != NULL && !spin->si_ascii @@ -2118,6 +2262,9 @@ spell_read_wordfile(fname, spin) smsg((char_u *)_("Conversion in %s not supported: from %s to %s"), fname, line + 10, p_enc); vim_free(enc); +#else + smsg((char_u *)_("Conversion in %s not supported"), fname); +#endif } continue; } @@ -2169,9 +2316,15 @@ spell_read_wordfile(fname, spin) vim_free(pc); fclose(fd); - if (spin->si_ascii && non_ascii > 0) + if (spin->si_ascii && non_ascii > 0 && (spin->si_verbose || p_verbose > 2)) + { + if (p_verbose > 2) + verbose_enter(); smsg((char_u *)_("Ignored %d words with non-ASCII characters"), non_ascii); + if (p_verbose > 2) + verbose_leave(); + } return retval; } @@ -2343,8 +2496,9 @@ tree_add_word(word, root, flags, region, * Compress a tree: find tails that are identical and can be shared. */ static void -wordtree_compress(root) +wordtree_compress(root, spin) wordnode_T *root; + spellinfo_T *spin; { hashtab_T ht; int n; @@ -2354,8 +2508,15 @@ wordtree_compress(root) { hash_init(&ht); n = node_compress(root, &ht, &tot); - smsg((char_u *)_("Compressed %d of %d nodes; %d%% remaining"), + if (spin->si_verbose || p_verbose > 2) + { + if (!spin->si_verbose) + verbose_enter(); + smsg((char_u *)_("Compressed %d of %d nodes; %d%% remaining"), n, tot, (tot - n) * 100 / tot); + if (p_verbose > 2) + verbose_leave(); + } hash_clear(&ht); } } @@ -2516,7 +2677,7 @@ write_vim_spell(fname, spin, regcount, r wordnode_T *tree; int nodecount; - fd = fopen((char *)fname, "w"); + fd = mch_fopen((char *)fname, "w"); if (fd == NULL) { EMSG2(_(e_notopen), fname); @@ -2690,7 +2851,8 @@ put_tree(fd, node, index, regionmask) /* - * ":mkspell outfile infile ..." + * ":mkspell [-ascii] outfile infile ..." + * ":mkspell [-ascii] addfile" */ void ex_mkspell(eap) @@ -2698,84 +2860,141 @@ ex_mkspell(eap) { int fcount; char_u **fnames; + char_u *arg = eap->arg; + int ascii = FALSE; + + if (STRNCMP(arg, "-ascii", 6) == 0) + { + ascii = TRUE; + arg = skipwhite(arg + 6); + } + + /* Expand all the remaining arguments (e.g., $VIMRUNTIME). */ + if (get_arglist_exp(arg, &fcount, &fnames) == OK) + { + mkspell(fcount, fnames, ascii, eap->forceit, TRUE); + FreeWild(fcount, fnames); + } +} + +/* + * Create a Vim spell file from one or more word lists. + * "fnames[0]" is the output file name. + * "fnames[fcount - 1]" is the last input file name. + * Exception: when "fnames[0]" ends in ".add" it's used as the input file name + * and ".spl" is appended to make the output file name. + */ + static void +mkspell(fcount, fnames, ascii, overwrite, verbose) + int fcount; + char_u **fnames; + int ascii; /* -ascii argument given */ + int overwrite; /* overwrite existing output file */ + int verbose; /* give progress messages */ +{ char_u fname[MAXPATHL]; char_u wfname[MAXPATHL]; + char_u **innames; + int incount; afffile_T *(afile[8]); int i; int len; char_u region_name[16]; struct stat st; - char_u *arg = eap->arg; int error = FALSE; spellinfo_T spin; vim_memset(&spin, 0, sizeof(spin)); - - if (STRNCMP(arg, "-ascii", 6) == 0) + spin.si_verbose = verbose; + spin.si_ascii = ascii; + + /* default: fnames[0] is output file, following are input files */ + innames = &fnames[1]; + incount = fcount - 1; + + if (fcount >= 1) { - spin.si_ascii = TRUE; - arg = skipwhite(arg + 6); + len = STRLEN(fnames[0]); + if (fcount == 1 && len > 4 && STRCMP(fnames[0] + len - 4, ".add") == 0) + { + /* For ":mkspell path/en.latin1.add" output file is + * "path/en.latin1.add.spl". */ + innames = &fnames[0]; + incount = 1; + vim_snprintf((char *)wfname, sizeof(wfname), "%s.spl", fnames[0]); + } + else if (len > 4 && STRCMP(fnames[0] + len - 4, ".spl") == 0) + { + /* Name ends in ".spl", use as the file name. */ + STRNCPY(wfname, fnames[0], sizeof(wfname)); + wfname[sizeof(wfname) - 1] = NUL; + } + else + /* Name should be language, make the file name from it. */ + vim_snprintf((char *)wfname, sizeof(wfname), "%s.%s.spl", fnames[0], + spin.si_ascii ? (char_u *)"ascii" : spell_enc()); + + /* Check for .ascii.spl. */ + if (strstr((char *)gettail(wfname), ".ascii.") != NULL) + spin.si_ascii = TRUE; + + /* Check for .add.spl. */ + if (strstr((char *)gettail(wfname), ".add.") != NULL) + spin.si_add = TRUE; } - /* Expand all the remaining arguments (e.g., $VIMRUNTIME). */ - if (get_arglist_exp(arg, &fcount, &fnames) == FAIL) - return; - if (fcount < 2) + if (incount <= 0) EMSG(_(e_invarg)); /* need at least output and input names */ - else if (fcount > 9) + else if (incount > 8) EMSG(_("E754: Only up to 8 regions supported")); else { /* Check for overwriting before doing things that may take a lot of * time. */ - vim_snprintf((char *)wfname, sizeof(wfname), "%s.%s.spl", fnames[0], - spin.si_ascii ? (char_u *)"ascii" : p_enc); - if (!eap->forceit && mch_stat((char *)wfname, &st) >= 0) + if (!overwrite && mch_stat((char *)wfname, &st) >= 0) { EMSG(_(e_exists)); - goto theend; + return; } - if (mch_isdir(fnames[0])) + if (mch_isdir(wfname)) { - EMSG2(_(e_isadir2), fnames[0]); - goto theend; + EMSG2(_(e_isadir2), wfname); + return; } /* * Init the aff and dic pointers. * Get the region names if there are more than 2 arguments. */ - for (i = 1; i < fcount; ++i) + for (i = 0; i < incount; ++i) { - afile[i - 1] = NULL; + afile[i] = NULL; if (fcount > 2) { - len = STRLEN(fnames[i]); - if (STRLEN(gettail(fnames[i])) < 5 || fnames[i][len - 3] != '_') + len = STRLEN(innames[i]); + if (STRLEN(gettail(innames[i])) < 5 + || innames[i][len - 3] != '_') { - EMSG2(_("E755: Invalid region in %s"), fnames[i]); - goto theend; + EMSG2(_("E755: Invalid region in %s"), innames[i]); + return; } - else - { - region_name[(i - 1) * 2] = TOLOWER_ASC(fnames[i][len - 2]); - region_name[(i - 1) * 2 + 1] = - TOLOWER_ASC(fnames[i][len - 1]); - } + region_name[i * 2] = TOLOWER_ASC(innames[i][len - 2]); + region_name[i * 2 + 1] = TOLOWER_ASC(innames[i][len - 1]); } } - /* Clear the char type tables, don't want to use any of the currently - * used spell properties. */ - init_spell_chartab(); + if (!spin.si_add) + /* Clear the char type tables, don't want to use any of the + * currently used spell properties. */ + init_spell_chartab(); spin.si_foldroot = wordtree_alloc(&spin.si_blocks); spin.si_keeproot = wordtree_alloc(&spin.si_blocks); if (spin.si_foldroot == NULL || spin.si_keeproot == NULL) { error = TRUE; - goto theend; + return; } /* @@ -2783,25 +3002,25 @@ ex_mkspell(eap) * Text is converted to 'encoding'. * Words are stored in the case-folded and keep-case trees. */ - for (i = 1; i < fcount && !error; ++i) + for (i = 0; i < incount && !error; ++i) { spin.si_conv.vc_type = CONV_NONE; - spin.si_region = 1 << (i - 1); - - vim_snprintf((char *)fname, sizeof(fname), "%s.aff", fnames[i]); + spin.si_region = 1 << i; + + vim_snprintf((char *)fname, sizeof(fname), "%s.aff", innames[i]); if (mch_stat((char *)fname, &st) >= 0) { /* Read the .aff file. Will init "spin->si_conv" based on the * "SET" line. */ - afile[i - 1] = spell_read_aff(fname, &spin); - if (afile[i - 1] == NULL) + afile[i] = spell_read_aff(fname, &spin); + if (afile[i] == NULL) error = TRUE; else { /* Read the .dic file and store the words in the trees. */ vim_snprintf((char *)fname, sizeof(fname), "%s.dic", - fnames[i]); - if (spell_read_dic(fname, &spin, afile[i - 1]) == FAIL) + innames[i]); + if (spell_read_dic(fname, &spin, afile[i]) == FAIL) error = TRUE; } } @@ -2809,12 +3028,14 @@ ex_mkspell(eap) { /* No .aff file, try reading the file as a word list. Store * the words in the trees. */ - if (spell_read_wordfile(fnames[i], &spin) == FAIL) + if (spell_read_wordfile(innames[i], &spin) == FAIL) error = TRUE; } +#ifdef FEAT_MBYTE /* Free any conversion stuff. */ convert_setup(&spin.si_conv, NULL, NULL); +#endif } if (!error) @@ -2828,10 +3049,17 @@ ex_mkspell(eap) /* * Combine tails in the tree. */ - MSG(_("Compressing word tree...")); - out_flush(); - wordtree_compress(spin.si_foldroot); - wordtree_compress(spin.si_keeproot); + if (verbose || p_verbose > 2) + { + if (!verbose) + verbose_enter(); + MSG(_("Compressing word tree...")); + out_flush(); + if (!verbose) + verbose_leave(); + } + wordtree_compress(spin.si_foldroot, &spin); + wordtree_compress(spin.si_keeproot, &spin); } if (!error) @@ -2839,33 +3067,138 @@ ex_mkspell(eap) /* * Write the info in the spell file. */ - smsg((char_u *)_("Writing spell file %s..."), wfname); - out_flush(); - write_vim_spell(wfname, &spin, fcount - 1, region_name); - MSG(_("Done!")); - - smsg((char_u *)_("Estimated runtime memory use: %d bytes"), + if (verbose || p_verbose > 2) + { + if (!verbose) + verbose_enter(); + smsg((char_u *)_("Writing spell file %s..."), wfname); + out_flush(); + if (!verbose) + verbose_leave(); + } + + write_vim_spell(wfname, &spin, incount, region_name); + + if (verbose || p_verbose > 2) + { + if (!verbose) + verbose_enter(); + MSG(_("Done!")); + smsg((char_u *)_("Estimated runtime memory use: %d bytes"), spin.si_memtot); - out_flush(); - - /* May need to reload spell dictionaries */ - spell_reload(); + out_flush(); + if (!verbose) + verbose_leave(); + } + + /* If the file is loaded need to reload it. */ + spell_reload_one(wfname); } /* Free the allocated memory. */ free_blocks(spin.si_blocks); /* Free the .aff file structures. */ - for (i = 1; i < fcount; ++i) - if (afile[i - 1] != NULL) - spell_free_aff(afile[i - 1]); + for (i = 0; i < incount; ++i) + if (afile[i] != NULL) + spell_free_aff(afile[i]); } - -theend: - FreeWild(fcount, fnames); +} + + +/* + * ":spellgood {word}" + * ":spellwrong {word}" + */ + void +ex_spell(eap) + exarg_T *eap; +{ + spell_add_word(eap->arg, STRLEN(eap->arg), eap->cmdidx == CMD_spellwrong); } -#endif /* FEAT_MBYTE */ +/* + * Add "word[len]" to 'spellfile' as a good or bad word. + */ + void +spell_add_word(word, len, bad) + char_u *word; + int len; + int bad; +{ + FILE *fd; + buf_T *buf; + + if (*curbuf->b_p_spf == NUL) + init_spellfile(); + if (*curbuf->b_p_spf == NUL) + EMSG(_("E999: 'spellfile' is not set")); + else + { + /* Check that the user isn't editing the .add file somewhere. */ + buf = buflist_findname_exp(curbuf->b_p_spf); + if (buf != NULL && buf->b_ml.ml_mfp == NULL) + buf = NULL; + if (buf != NULL && bufIsChanged(buf)) + EMSG(_(e_bufloaded)); + else + { + fd = mch_fopen((char *)curbuf->b_p_spf, "a"); + if (fd == NULL) + EMSG2(_(e_notopen), curbuf->b_p_spf); + else + { + if (bad) + fprintf(fd, "/!%.*s\n", len, word); + else + fprintf(fd, "%.*s\n", len, word); + fclose(fd); + + /* Update the .add.spl file. */ + mkspell(1, &curbuf->b_p_spf, FALSE, TRUE, FALSE); + + /* If the .add file is edited somewhere, reload it. */ + if (buf != NULL) + buf_reload(buf); + } + } + } +} + +/* + * Initialize 'spellfile' for the current buffer. + */ + static void +init_spellfile() +{ + char_u buf[MAXPATHL]; + int l; + slang_T *sl; + char_u *rtp; + + if (*curbuf->b_p_spl != NUL && curbuf->b_langp.ga_len > 0) + { + /* Loop over all entries in 'runtimepath'. */ + rtp = p_rtp; + while (*rtp != NUL) + { + /* Copy the path from 'runtimepath' to buf[]. */ + copy_option_part(&rtp, buf, MAXPATHL, ","); + if (filewritable(buf) == 2) + { + sl = LANGP_ENTRY(curbuf->b_langp, 0)->lp_slang; + l = STRLEN(buf); + vim_snprintf((char *)buf + l, MAXPATHL - l, + "/spell/%.2s.%s.add", + sl->sl_name, + strstr((char *)gettail(sl->sl_fname), ".ascii.") != NULL + ? (char_u *)"ascii" : spell_enc()); + set_option_value((char_u *)"spellfile", 0L, buf, OPT_LOCAL); + break; + } + } + } +} /* @@ -2937,7 +3270,6 @@ init_spell_chartab() } } -#if defined(FEAT_MBYTE) || defined(PROTO) static char *e_affform = N_("E761: Format error in affix file FOL, LOW or UPP"); static char *e_affrange = N_("E762: Character in FOL, LOW or UPP is out of range"); @@ -3016,7 +3348,6 @@ set_spell_chartab(fol, low, upp) return set_spell_finish(&new_st); } -#endif /* * Set the spell character tables from strings in the .spl file. @@ -3082,7 +3413,6 @@ set_spell_finish(new_st) return OK; } -#if defined(FEAT_MBYTE) || defined(PROTO) /* * Write the current tables into the .spl file. * This makes sure the same characters are recognized as word characters when @@ -3107,13 +3437,17 @@ write_spell_chartab(fd) flags |= SPELL_ISUPPER; fputc(flags, fd); /* */ - len += mb_char2bytes(spelltab.st_fold[i], charbuf + len); +#ifdef FEAT_MBYTE + if (has_mbyte) + len += mb_char2bytes(spelltab.st_fold[i], charbuf + len); + else +#endif + charbuf[len++] = spelltab.st_fold[i]; } put_bytes(fd, (long_u)len, 2); /* */ fwrite(charbuf, (size_t)len, (size_t)1, fd); /* */ } -#endif /* * Return TRUE if "c" is an upper-case character for spelling.