# HG changeset patch # User vimboss # Date 1120257232 0 # Node ID 997a094e44d2891027c3f848c7d9a4a473e2733b # Parent da6e29ecd0fcd3115e2ee088b2f0e6e07f0836f3 updated for version 7.0099 diff --git a/runtime/optwin.vim b/runtime/optwin.vim --- a/runtime/optwin.vim +++ b/runtime/optwin.vim @@ -1,7 +1,7 @@ " These commands create the option window. " " Maintainer: Bram Moolenaar -" Last Change: 2005 Mar 07 +" Last Change: 2005 Jul 01 " If there already is an option window, jump to that one. if bufwinnr("option-window") > 0 @@ -368,7 +368,7 @@ if has("linebreak") endif -call Header("syntax and highlighting") +call Header("syntax, highlighting and spelling") call append("$", "background\t\"dark\" or \"light\"; the background color brightness") call OptionG("bg", &bg) if has("autocmd") @@ -385,6 +385,19 @@ call append("$", "highlight\twhich highl call OptionG("hl", &hl) call append("$", "hlsearch\thighlight all matches for the last used search pattern") call BinOptionG("hls", &hls) +if has("syntax") + call append("$", "spell\thighlight spelling mistakes") + call append("$", "\t(local to window)") + call BinOptionL("spell") + call append("$", "spelllang\tlist of accepted languages") + call append("$", "\t(local to buffer)") + call OptionL("spl") + call append("$", "spellfile\tfile that \"zg\" adds good words to") + call append("$", "\t(local to buffer)") + call OptionL("spf") + call append("$", "spellsuggest\tmethods used to suggest corrections") + call OptionG("sps", &sps) +endif call Header("multiple windows") @@ -614,6 +627,8 @@ call append("$", "report\tthreshold for call append("$", " \tset report=" . &report) call append("$", "verbose\tthe higher the more messages are given") call append("$", " \tset vbs=" . &vbs) +call append("$", "verbosefile\tfile to write messages in") +call OptionG("vfile", &vfile) call append("$", "more\tpause listings when the screen is full") call BinOptionG("more", &more) if has("dialog_con") || has("dialog_gui") diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -3,74 +3,101 @@ # It builds on Windows 95 and all four NT platforms: i386, Alpha, MIPS, and # PowerPC. The NT/i386 binary and the Windows 95 binary are identical. # +# To build using Borland C++, use Make_bc3.mak or Make_bc5.mak. +# # This makefile can build the console, GUI, OLE-enable, Perl-enabled and # Python-enabled versions of vim for Win32 platforms. # -# When compiling different versions, do "nmake clean" first! +# The basic command line to build vim is: # -# The basic command line to build vim is: # nmake -f Make_mvc.mak +# # This will build the console version of vim with no additional interfaces. -# To add interfaces, define any of the following: +# To add features, define any of the following: +# +# !!!! After changing features do "nmake clean" first !!!! +# +# Feature Set: FEATURES=[TINY, SMALL, NORMAL, BIG, HUGE] (default is BIG) +# # GUI interface: GUI=yes (default is no) +# # OLE interface: OLE=yes (usually with GUI=yes) +# # Multibyte support: MBYTE=yes +# # IME support: IME=yes (requires GUI=yes) # DYNAMIC_IME=[yes or no] (to load the imm32.dll dynamically, default # is yes) # Global IME support: GIME=yes (requires GUI=yes) +# # MzScheme interface: # MZSCHEME=[Path to MzScheme directory] # DYNAMIC_MZSCHEME=yes (to load the MzScheme DLLs dynamically) # MZSCHEME_VER=[version, 205_000, ...] +# # Perl interface: # PERL=[Path to Perl directory] # DYNAMIC_PERL=yes (to load the Perl DLL dynamically) -# PERL_VER=[Perl version, in the form 55 (5.005), 56 (5.6.x), etc] (default is 56) +# PERL_VER=[Perl version, in the form 55 (5.005), 56 (5.6.x), etc] +# (default is 56) +# # Python interface: # PYTHON=[Path to Python directory] # DYNAMIC_PYTHON=yes (to load the Python DLL dynamically) # PYTHON_VER=[Python version, eg 15, 20] (default is 22) +# # Ruby interface: # RUBY=[Path to Ruby directory] # DYNAMIC_RUBY=yes (to load the Ruby DLL dynamically) # RUBY_VER=[Ruby version, eg 16, 17] (default is 18) # RUBY_VER_LONG=[Ruby version, eg 1.6, 1.7] (default is 1.8) # You must set RUBY_VER_LONG when change RUBY_VER. +# # Tcl interface: # TCL=[Path to Tcl directory] # DYNAMIC_TCL=yes (to load the Tcl DLL dynamically) # TCL_VER=[Tcl version, e.g. 80, 83] (default is 83) # TCL_VER_LONG=[Tcl version, eg 8.3] (default is 8.3) # You must set TCL_VER_LONG when you set TCL_VER. +# +# SNiFF+ interface: SNIFF=yes +# +# Cscope support: CSCOPE=yes +# +# Iconv library support (always dynamically loaded): +# ICONV=[yes or no] (default is yes) +# +# Intl library support (always dynamically loaded): +# GETTEXT=[yes or no] (default is yes) +# See http://sourceforge.net/projects/gettext/ +# +# PostScript printing: POSTSCRIPT=yes (default is no) +# +# Netbeans Support: NETBEANS=[yes or no] (default is yes if GUI is yes) +# +# XPM Image Support: XPM=[path to XPM directory] +# +# Optimization: OPTIMIZE=[SPACE, SPEED, MAXSPEED] (default is MAXSPEED) +# +# Processor Version: CPUNR=[i386, i486, i586, i686, pentium4] (default is +# i386) +# +# Version Support: WINVER=[0x0400, 0x0500] (default is 0x0400) +# # Debug version: DEBUG=yes # Mapfile: MAP=[no, yes or lines] (default is yes) # no: Don't write a mapfile. # yes: Write a normal mapfile. # lines: Write a mapfile with line numbers (only for VC6 and later) -# SNiFF+ interface: SNIFF=yes -# Cscope support: CSCOPE=yes -# Iconv library support (always dynamically loaded): -# ICONV=[yes or no] (default is yes) -# Intl library support (always dynamically loaded): -# GETTEXT=[yes or no] (default is yes) -# See http://sourceforge.net/projects/gettext/ -# PostScript printing: POSTSCRIPT=yes (default is no) -# Feature Set: FEATURES=[TINY, SMALL, NORMAL, BIG, HUGE] (default is BIG) -# Version Support: WINVER=[0x0400, 0x0500] (default is 0x0400) -# Processor Version: CPUNR=[i386, i486, i586, i686, pentium4] (default is i386) -# Optimization: OPTIMIZE=[SPACE, SPEED, MAXSPEED] (default is MAXSPEED) -# Netbeans Support: NETBEANS=[yes or no] (default is yes if GUI is yes) -# Netbeans Debugging Support: NBDEBUG=[yes or no] (default is no) -# XPM Image Support: XPM=[path to XPM directory] +# +# Netbeans Debugging Support: NBDEBUG=[yes or no] (should be no, yes +# doesn't work) # # You can combine any of these interfaces # # Example: To build the non-debug, GUI version with Perl interface: # nmake -f Make_mvc.mak GUI=yes PERL=C:\Perl # -# To build using Borland C++, use Make_bc3.mak or Make_bc5.mak. -# # DEBUG with Make_mvc.mak and Make_dvc.mak: # This makefile gives a fineness of control which is not supported in # Visual C++ configuration files. Therefore, debugging requires a bit of diff --git a/src/normal.c b/src/normal.c --- a/src/normal.c +++ b/src/normal.c @@ -4665,6 +4665,8 @@ dozet: #ifdef FEAT_SYN_HL case 'g': /* "zg": add good word to word list */ case 'w': /* "zw": add wrong word to word list */ + case 'G': /* "zG": add good word to temp word list */ + case 'W': /* "zW": add wrong word to temp word list */ { char_u *ptr = NULL; int len; @@ -4679,7 +4681,8 @@ dozet: if (ptr == NULL && (len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0) return; - spell_add_word(ptr, len, nchar == 'w'); + spell_add_word(ptr, len, nchar == 'w' || nchar == 'W', + nchar == 'G' || nchar == 'W'); } break; diff --git a/src/spell.c b/src/spell.c --- a/src/spell.c +++ b/src/spell.c @@ -377,6 +377,9 @@ typedef struct langp_S #define VIMSPELLMAGIC "VIMspell08" /* string at start of Vim spell file */ #define VIMSPELLMAGICL 10 +/* file used for "zG" and "zW" */ +static char_u *temp_wordlist = NULL; + /* * Information used when looking for suggestions. */ @@ -574,10 +577,6 @@ typedef struct trystate_S #define FIND_KEEPWORD 1 /* find keep-case word */ #define FIND_PREFIX 2 /* find word after prefix */ -/* values for read_cnt_string() */ -#define ERR_NOMEM -1 -#define ERR_TRUNC -2 - 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)); @@ -592,6 +591,8 @@ static char_u *spell_enc __ARGS((void)); static void spell_load_cb __ARGS((char_u *fname, void *cookie)); static slang_T *spell_load_file __ARGS((char_u *fname, char_u *lang, slang_T *old_lp, int silent)); static char_u *read_cnt_string __ARGS((FILE *fd, int cnt_bytes, int *errp)); +static int set_sofo __ARGS((slang_T *lp, char_u *from, char_u *to)); +static void set_sal_first __ARGS((slang_T *lp)); #ifdef FEAT_MBYTE static int *mb_str2wide __ARGS((char_u *s)); #endif @@ -601,7 +602,7 @@ static void use_midword __ARGS((slang_T 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, int added_word)); -static int set_spell_charflags __ARGS((char_u *flags, int cnt, char_u *upp)); +static int set_spell_charflags __ARGS((char_u *flags, char_u *upp)); static int set_spell_chartab __ARGS((char_u *fol, char_u *low, char_u *upp)); static void write_spell_chartab __ARGS((FILE *fd)); static int spell_casefold __ARGS((char_u *p, int len, char_u *buf, int buflen)); @@ -683,6 +684,7 @@ static linenr_T apply_prefixes __ARGS((s static char *e_format = N_("E759: Format error in spell file"); +static char *e_spell_trunc = N_("E758: Truncated spell file"); /* * Main spell-checking function. @@ -1690,9 +1692,7 @@ spell_load_file(fname, lang, old_lp, sil garray_T *gap; fromto_T *ftp; salitem_T *smp; - int rr; short *first; - salfirst_T *sfirst; idx_T idx; int c = 0; @@ -1756,7 +1756,7 @@ spell_load_file(fname, lang, old_lp, sil if (cnt < 0) { truncerr: - EMSG(_("E758: Truncated spell file")); + EMSG(_(e_spell_trunc)); goto endFAIL; } if (cnt > 8) @@ -1772,48 +1772,33 @@ formerr: } lp->sl_regions[cnt * 2] = NUL; - cnt = getc(fd); /* */ - if (cnt > 0) - { - p = alloc((unsigned)cnt); - if (p == NULL) - goto endFAIL; - for (i = 0; i < cnt; ++i) - p[i] = getc(fd); /* */ - - /* */ - fol = read_cnt_string(fd, 2, &ccnt); - if (ccnt != 0) - { - vim_free(p); - if (ccnt == ERR_NOMEM) - goto endFAIL; - if (ccnt == ERR_TRUNC) - goto formerr; - } - - /* Set the word-char flags and fill SPELL_ISUPPER() table. */ - i = set_spell_charflags(p, cnt, fol); + /* */ + p = read_cnt_string(fd, 1, &cnt); + if (cnt == FAIL) + goto endFAIL; + + /* */ + fol = read_cnt_string(fd, 2, &cnt); + if (cnt == FAIL) + { vim_free(p); - vim_free(fol); -#if 0 /* tolerate the differences */ - if (i == FAIL) - goto formerr; -#endif - } - else - { - /* When is zero then must also be zero. */ - cnt = (getc(fd) << 8) + getc(fd); - if (cnt != 0) - goto formerr; - } + goto endFAIL; + } + + /* Set the word-char flags and fill SPELL_ISUPPER() table. */ + if (p != NULL && fol != NULL) + i = set_spell_charflags(p, fol); + + vim_free(p); + vim_free(fol); + + /* When is zero then must also be zero. */ + if ((p == NULL) != (fol == NULL)) + goto formerr; /* */ lp->sl_midword = read_cnt_string(fd, 2, &cnt); - if (cnt == ERR_TRUNC) - goto truncerr; - if (cnt == ERR_NOMEM) + if (cnt == FAIL) goto endFAIL; /* ... */ @@ -1863,28 +1848,14 @@ formerr: for (; gap->ga_len < cnt; ++gap->ga_len) { ftp = &((fromto_T *)gap->ga_data)[gap->ga_len]; - for (rr = 1; rr <= 2; ++rr) - { - ccnt = getc(fd); - if (ccnt < 0) - { - if (rr == 2) - vim_free(ftp->ft_from); - goto formerr; - } - if ((p = alloc(ccnt + 1)) == NULL) - { - if (rr == 2) - vim_free(ftp->ft_from); - goto endFAIL; - } - for (i = 0; i < ccnt; ++i) - p[i] = getc(fd); /* or */ - p[i] = NUL; - if (rr == 1) - ftp->ft_from = p; - else - ftp->ft_to = p; + ftp->ft_from = read_cnt_string(fd, 1, &i); + if (i == FAIL) + goto endFAIL; + ftp->ft_to = read_cnt_string(fd, 1, &i); + if (i == FAIL) + { + vim_free(ftp->ft_from); + goto endFAIL; } } @@ -1921,117 +1892,26 @@ formerr: if (cnt != 1) goto formerr; - cnt = (getc(fd) << 8) + getc(fd); /* */ - if (cnt < 0) - goto formerr; - if ((bp = alloc(cnt + 1)) == NULL) + /* */ + bp = read_cnt_string(fd, 2, &cnt); + if (cnt == FAIL) goto endFAIL; - for (i = 0; i < cnt; ++i) - bp[i] = getc(fd); /* */ - bp[i] = NUL; - - ccnt = (getc(fd) << 8) + getc(fd); /* */ - if (ccnt < 0) - { - vim_free(bp); - goto formerr; - } - if ((fol = alloc(ccnt + 1)) == NULL) + + /* */ + fol = read_cnt_string(fd, 2, &cnt); + if (cnt == FAIL) { vim_free(bp); goto endFAIL; } - for (i = 0; i < ccnt; ++i) - fol[i] = getc(fd); /* */ - fol[i] = NUL; - -#ifdef FEAT_MBYTE - if (has_mbyte) - { - char_u *s; - - /* Use "sl_sal" as an array with 256 pointers to a list of wide - * characters. The index is the low byte of the character. - * The list contains from-to pairs with a terminating NUL. - * sl_sal_first[] is used for latin1 "from" characters. */ - gap = &lp->sl_sal; - ga_init2(gap, sizeof(int *), 1); - if (ga_grow(gap, 256) == FAIL) - { -sofoFAIL: - vim_free(bp); - vim_free(fol); - goto endFAIL; - } - vim_memset(gap->ga_data, 0, sizeof(int *) * 256); - gap->ga_len = 256; - - /* First count the number of items for each list. Temporarily use - * sl_sal_first[] for this. */ - for (p = bp, s = fol; *p != NUL && *s != NUL; ) - { - c = mb_ptr2char_adv(&p); - mb_ptr_adv(s); - if (c >= 256) - ++lp->sl_sal_first[c & 0xff]; - } - if (*p != NUL || *s != NUL) /* lengths differ */ - goto sofoerr; - - /* Allocate the lists. */ - for (i = 0; i < 256; ++i) - if (lp->sl_sal_first[i] > 0) - { - p = alloc(sizeof(int) * (lp->sl_sal_first[i] * 2 + 1)); - if (p == NULL) - goto sofoFAIL; - ((int **)gap->ga_data)[i] = (int *)p; - *(int *)p = 0; - } - - /* Put the characters in sl_sal_first[] or a sl_sal list. */ - vim_memset(lp->sl_sal_first, 0, sizeof(salfirst_T) * 256); - for (p = bp, s = fol; *p != NUL && *s != NUL; ) - { - c = mb_ptr2char_adv(&p); - i = mb_ptr2char_adv(&s); - if (c >= 256) - { - int *inp; - - /* Append the from-to chars at the end of the list with - * the low byte. */ - inp = ((int **)gap->ga_data)[c & 0xff]; - while (*inp != 0) - ++inp; - *inp++ = c; /* from char */ - *inp++ = i; /* to char */ - *inp++ = NUL; /* NUL at the end */ - } - else - /* mapping byte to char is done in sl_sal_first[] */ - lp->sl_sal_first[c] = i; - } - } - else -#endif - { - /* mapping bytes to bytes is done in sl_sal_first[] */ - if (cnt != ccnt) - { -#ifdef FEAT_MBYTE -sofoerr: -#endif - vim_free(bp); - vim_free(fol); - goto formerr; - } - for (i = 0; i < cnt; ++i) - lp->sl_sal_first[bp[i]] = fol[i]; - lp->sl_sal.ga_len = 1; /* indicates we have soundfolding */ - } + + /* Store the info in lp->sl_sal and/or lp->sl_sal_first. */ + i = set_sofo(lp, bp, fol); + vim_free(bp); vim_free(fol); + if (i == FAIL) + goto formerr; } else { @@ -2091,22 +1971,13 @@ sofoerr: *p++ = getc(fd); /* */ *p++ = NUL; - ccnt = getc(fd); /* */ - if (ccnt < 0) + /* */ + smp->sm_to = read_cnt_string(fd, 1, &ccnt); + if (ccnt == FAIL) { vim_free(smp->sm_lead); goto formerr; } - if ((p = alloc(ccnt + 1)) == NULL) - { - vim_free(smp->sm_lead); - goto endFAIL; - } - smp->sm_to = p; - - for (i = 0; i < ccnt; ++i) - *p++ = getc(fd); /* */ - *p++ = NUL; #ifdef FEAT_MBYTE if (has_mbyte) @@ -2135,64 +2006,13 @@ sofoerr: } /* Fill the first-index table. */ - sfirst = lp->sl_sal_first; - for (i = 0; i < 256; ++i) - sfirst[i] = -1; - smp = (salitem_T *)gap->ga_data; - for (i = 0; i < gap->ga_len; ++i) - { -#ifdef FEAT_MBYTE - if (has_mbyte) - /* Use the lowest byte of the first character. For latin1 it's - * the character, for other encodings it should differ for most - * characters. */ - c = *smp[i].sm_lead_w & 0xff; - else -#endif - c = *smp[i].sm_lead; - if (sfirst[c] == -1) - { - sfirst[c] = i; -#ifdef FEAT_MBYTE - if (has_mbyte) - { - /* Make sure all entries with this byte are following each - * other. Move the ones that are in the wrong position. Do - * keep the same ordering! */ - while (i + 1 < gap->ga_len - && (*smp[i + 1].sm_lead_w & 0xff) == c) - /* Skip over entry with same index byte. */ - ++i; - - for (n = 1; i + n < gap->ga_len; ++n) - if ((*smp[i + n].sm_lead_w & 0xff) == c) - { - salitem_T tsal; - - /* Move entry with same index byte after the entries - * we already found. */ - ++i; - --n; - tsal = smp[i + n]; - mch_memmove(smp + i + 1, smp + i, - sizeof(salitem_T) * n); - smp[i] = tsal; - } - } -#endif - } - } - } - - cnt = (getc(fd) << 8) + getc(fd); /* */ - if (cnt < 0) - goto formerr; - p = alloc(cnt + 1); - if (p == NULL) + set_sal_first(lp); + } + + /* */ + p = read_cnt_string(fd, 2, &cnt); + if (cnt == FAIL) goto endFAIL; - for (i = 0; i < cnt; ++i) - p[i] = getc(fd); /* */ - p[i] = NUL; set_map_str(lp, p); vim_free(p); @@ -2270,10 +2090,9 @@ endOK: /* * Read a length field from "fd" in "cnt_bytes" bytes. - * Allocate memory and read the string into it. + * Allocate memory, read the string into it and add a NUL at the end. * Returns NULL when the count is zero. - * Sets "errp" to ERR_TRUNC when reading failed, ERR_NOMEM when out of - * memory, zero when OK. + * Sets "*errp" to FAIL when there is an error, OK otherwise. */ static char_u * read_cnt_string(fd, cnt_bytes, errp) @@ -2290,7 +2109,8 @@ read_cnt_string(fd, cnt_bytes, errp) cnt = (cnt << 8) + getc(fd); if (cnt < 0) { - *errp = ERR_TRUNC; + EMSG(_(e_spell_trunc)); + *errp = FAIL; return NULL; } @@ -2298,10 +2118,10 @@ read_cnt_string(fd, cnt_bytes, errp) str = alloc((unsigned)cnt + 1); if (str == NULL) { - *errp = ERR_NOMEM; + *errp = FAIL; return NULL; } - *errp = 0; + *errp = OK; /* Read the string. Doesn't check for truncated file. */ for (i = 0; i < cnt; ++i) @@ -2311,6 +2131,162 @@ read_cnt_string(fd, cnt_bytes, errp) return str; } +/* + * Set the SOFOFROM and SOFOTO items in language "lp". + * Returns FAIL when there is something wrong. + */ + static int +set_sofo(lp, from, to) + slang_T *lp; + char_u *from; + char_u *to; +{ + int i; + +#ifdef FEAT_MBYTE + garray_T *gap; + char_u *s; + char_u *p; + int c; + int *inp; + + if (has_mbyte) + { + /* Use "sl_sal" as an array with 256 pointers to a list of wide + * characters. The index is the low byte of the character. + * The list contains from-to pairs with a terminating NUL. + * sl_sal_first[] is used for latin1 "from" characters. */ + gap = &lp->sl_sal; + ga_init2(gap, sizeof(int *), 1); + if (ga_grow(gap, 256) == FAIL) + return FAIL; + vim_memset(gap->ga_data, 0, sizeof(int *) * 256); + gap->ga_len = 256; + + /* First count the number of items for each list. Temporarily use + * sl_sal_first[] for this. */ + for (p = from, s = to; *p != NUL && *s != NUL; ) + { + c = mb_ptr2char_adv(&p); + mb_ptr_adv(s); + if (c >= 256) + ++lp->sl_sal_first[c & 0xff]; + } + if (*p != NUL || *s != NUL) /* lengths differ */ + return FAIL; + + /* Allocate the lists. */ + for (i = 0; i < 256; ++i) + if (lp->sl_sal_first[i] > 0) + { + p = alloc(sizeof(int) * (lp->sl_sal_first[i] * 2 + 1)); + if (p == NULL) + return FAIL; + ((int **)gap->ga_data)[i] = (int *)p; + *(int *)p = 0; + } + + /* Put the characters up to 255 in sl_sal_first[] the rest in a sl_sal + * list. */ + vim_memset(lp->sl_sal_first, 0, sizeof(salfirst_T) * 256); + for (p = from, s = to; *p != NUL && *s != NUL; ) + { + c = mb_ptr2char_adv(&p); + i = mb_ptr2char_adv(&s); + if (c >= 256) + { + /* Append the from-to chars at the end of the list with + * the low byte. */ + inp = ((int **)gap->ga_data)[c & 0xff]; + while (*inp != 0) + ++inp; + *inp++ = c; /* from char */ + *inp++ = i; /* to char */ + *inp++ = NUL; /* NUL at the end */ + } + else + /* mapping byte to char is done in sl_sal_first[] */ + lp->sl_sal_first[c] = i; + } + } + else +#endif + { + /* mapping bytes to bytes is done in sl_sal_first[] */ + if (STRLEN(from) != STRLEN(to)) + return FAIL; + + for (i = 0; to[i] != NUL; ++i) + lp->sl_sal_first[from[i]] = to[i]; + lp->sl_sal.ga_len = 1; /* indicates we have soundfolding */ + } + + return OK; +} + +/* + * Fill the first-index table for "lp". + */ + static void +set_sal_first(lp) + slang_T *lp; +{ + salfirst_T *sfirst; + int i; + salitem_T *smp; + int c; + garray_T *gap = &lp->sl_sal; + + sfirst = lp->sl_sal_first; + for (i = 0; i < 256; ++i) + sfirst[i] = -1; + smp = (salitem_T *)gap->ga_data; + for (i = 0; i < gap->ga_len; ++i) + { +#ifdef FEAT_MBYTE + if (has_mbyte) + /* Use the lowest byte of the first character. For latin1 it's + * the character, for other encodings it should differ for most + * characters. */ + c = *smp[i].sm_lead_w & 0xff; + else +#endif + c = *smp[i].sm_lead; + if (sfirst[c] == -1) + { + sfirst[c] = i; +#ifdef FEAT_MBYTE + if (has_mbyte) + { + int n; + + /* Make sure all entries with this byte are following each + * other. Move the ones that are in the wrong position. Do + * keep the same ordering! */ + while (i + 1 < gap->ga_len + && (*smp[i + 1].sm_lead_w & 0xff) == c) + /* Skip over entry with same index byte. */ + ++i; + + for (n = 1; i + n < gap->ga_len; ++n) + if ((*smp[i + n].sm_lead_w & 0xff) == c) + { + salitem_T tsal; + + /* Move entry with same index byte after the entries + * we already found. */ + ++i; + --n; + tsal = smp[i + n]; + mch_memmove(smp + i + 1, smp + i, + sizeof(salitem_T) * n); + smp[i] = tsal; + } + } +#endif + } + } +} #ifdef FEAT_MBYTE /* @@ -2472,6 +2448,7 @@ did_set_spelllang(buf) int load_spf; int len; char_u *p; + int round; ga_init2(&ga, sizeof(langp_T), 2); clear_midword(buf); @@ -2572,12 +2549,26 @@ did_set_spelllang(buf) } } - /* - * Make sure the 'spellfile' file is loaded. It may be in 'runtimepath', - * then it's probably loaded above already. Otherwise load it here. - */ - if (load_spf) - { + /* round 1: load 'spellfile', if needed. + * round 2: load temp_wordlist, if possible. */ + for (round = 1; round <= 2; ++round) + { + if (round == 1) + { + /* Make sure the 'spellfile' file is loaded. It may be in + * 'runtimepath', then it's probably loaded above already. + * Otherwise load it here. */ + if (!load_spf) + continue; + } + else + { + if (temp_wordlist == NULL) + continue; + vim_snprintf((char *)spf_name, sizeof(spf_name), "%s.%s.spl", + temp_wordlist, spell_enc()); + } + /* Check if it was loaded already. */ for (lp = first_lang; lp != NULL; lp = lp->sl_next) if (fullpathcmp(spf_name, lp->sl_fname, FALSE) == FPC_SAME) @@ -2585,11 +2576,17 @@ did_set_spelllang(buf) if (lp == NULL) { /* Not loaded, try loading it now. The language name includes the - * region name, the region is ignored otherwise. */ - vim_strncpy(lang, gettail(buf->b_p_spf), MAXWLEN); - p = vim_strchr(lang, '.'); - if (p != NULL) - *p = NUL; /* truncate at ".encoding.add" */ + * region name, the region is ignored otherwise. for + * temp_wordlist use an arbitrary name. */ + if (round == 1) + { + vim_strncpy(lang, gettail(buf->b_p_spf), MAXWLEN); + p = vim_strchr(lang, '.'); + if (p != NULL) + *p = NUL; /* truncate at ".encoding.add" */ + } + else + STRCPY(lang, "temp wordlist"); lp = spell_load_file(spf_name, lang, NULL, TRUE); } if (lp != NULL && ga_grow(&ga, 1) == OK) @@ -2678,7 +2675,7 @@ use_midword(lp, buf) /* * Find the region "region[2]" in "rp" (points to "sl_regions"). * Each region is simply stored as the two characters of it's name. - * Returns the index if found, REGION_ALL if not found. + * Returns the index if found (first is 0), REGION_ALL if not found. */ static int find_region(rp, region) @@ -2780,6 +2777,13 @@ spell_free_all() slang_free(lp); } + if (temp_wordlist != NULL) + { + mch_remove(temp_wordlist); + vim_free(temp_wordlist); + temp_wordlist = NULL; + } + init_spell_chartab(); } # endif @@ -3967,6 +3971,7 @@ spell_read_wordfile(fname, spin) char_u rline[MAXLINELEN]; char_u *line; char_u *pc = NULL; + char_u *p; int l; int retval = OK; int did_word = FALSE; @@ -4035,13 +4040,9 @@ spell_read_wordfile(fname, spin) line = rline; } - flags = 0; - regionmask = spin->si_region; - if (*line == '/') { ++line; - if (STRNCMP(line, "encoding=", 9) == 0) { if (spin->si_conv.vc_type != CONV_NONE) @@ -4092,50 +4093,49 @@ spell_read_wordfile(fname, spin) continue; } - if (*line == '=') - { - /* keep-case word */ - flags |= WF_KEEPCAP; - ++line; - } - - if (*line == '!') - { - /* Bad, bad, wicked word. */ - flags |= WF_BANNED; - ++line; - } - else if (*line == '?') - { - /* Rare word. */ - flags |= WF_RARE; - ++line; - } - - if (VIM_ISDIGIT(*line)) - { - /* region number(s) */ - regionmask = 0; - while (VIM_ISDIGIT(*line)) + smsg((char_u *)_("/ line ignored in %s line %d: %s"), + fname, lnum, line - 1); + continue; + } + + flags = 0; + regionmask = spin->si_region; + + /* Check for flags and region after a slash. */ + p = vim_strchr(line, '/'); + if (p != NULL) + { + *p++ = NUL; + while (*p != NUL) + { + if (*p == '=') /* keep-case word */ + flags |= WF_KEEPCAP; + else if (*p == '!') /* Bad, bad, wicked word. */ + flags |= WF_BANNED; + else if (*p == '?') /* Rare word. */ + flags |= WF_RARE; + else if (VIM_ISDIGIT(*p)) /* region number(s) */ { - l = *line - '0'; + if ((flags & WF_REGION) == 0) /* first one */ + regionmask = 0; + flags |= WF_REGION; + + l = *p - '0'; if (l > spin->si_region_count) { smsg((char_u *)_("Invalid region nr in %s line %d: %s"), - fname, lnum, line); + fname, lnum, p); break; } regionmask |= 1 << (l - 1); - ++line; } - flags |= WF_REGION; - } - - if (flags == 0) - { - smsg((char_u *)_("/ line ignored in %s line %d: %s"), - fname, lnum, line); - continue; + else + { + smsg((char_u *)_("Unrecognized flags in %s line %d: %s"), + fname, lnum, p); + break; + } + ++p; } } @@ -5179,80 +5179,99 @@ mkspell(fcount, fnames, ascii, overwrite ex_spell(eap) exarg_T *eap; { - spell_add_word(eap->arg, STRLEN(eap->arg), eap->cmdidx == CMD_spellwrong); + spell_add_word(eap->arg, STRLEN(eap->arg), eap->cmdidx == CMD_spellwrong, + eap->forceit); } /* * Add "word[len]" to 'spellfile' as a good or bad word. */ void -spell_add_word(word, len, bad) +spell_add_word(word, len, bad, temp) char_u *word; int len; int bad; + int temp; /* "zG" and "zW": use temp word list */ { FILE *fd; buf_T *buf; int new_spf = FALSE; struct stat st; - - /* If 'spellfile' isn't set figure out a good default value. */ - if (*curbuf->b_p_spf == NUL) - { - init_spellfile(); - new_spf = TRUE; - } - - if (*curbuf->b_p_spf == NUL) - EMSG(_("E764: 'spellfile' is not set")); + char_u *fname; + + if (temp) /* use temp word list */ + { + if (temp_wordlist == NULL) + { + temp_wordlist = vim_tempname('s'); + if (temp_wordlist == NULL) + return; + } + fname = temp_wordlist; + } else { + /* If 'spellfile' isn't set figure out a good default value. */ + if (*curbuf->b_p_spf == NUL) + { + init_spellfile(); + new_spf = TRUE; + } + + if (*curbuf->b_p_spf == NUL) + { + EMSG(_("E764: 'spellfile' is not set")); + return; + } + /* 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)); + return; + } + + fname = curbuf->b_p_spf; + } + + fd = mch_fopen((char *)fname, "a"); + if (fd == NULL && new_spf) + { + /* We just initialized the 'spellfile' option and can't open the file. + * We may need to create the "spell" directory first. We already + * checked the runtime directory is writable in init_spellfile(). */ + STRCPY(NameBuff, fname); + *gettail_sep(NameBuff) = NUL; + if (mch_stat((char *)NameBuff, &st) < 0) + { + /* The directory doesn't exist. Try creating it and opening the + * file again. */ + vim_mkdir(NameBuff, 0755); + fd = mch_fopen((char *)fname, "a"); + } + } + + if (fd == NULL) + EMSG2(_(e_notopen), fname); + else + { + if (bad) + fprintf(fd, "%.*s/!\n", len, word); else - { - fd = mch_fopen((char *)curbuf->b_p_spf, "a"); - if (fd == NULL && new_spf) - { - /* We just initialized the 'spellfile' option and can't open - * the file. We may need to create the "spell" directory - * first. We already checked the runtime directory is - * writable in init_spellfile(). */ - STRCPY(NameBuff, curbuf->b_p_spf); - *gettail_sep(NameBuff) = NUL; - if (mch_stat((char *)NameBuff, &st) < 0) - { - /* The directory doesn't exist. Try creating it and - * opening the file again. */ - vim_mkdir(NameBuff, 0755); - 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, TRUE); - - /* If the .add file is edited somewhere, reload it. */ - if (buf != NULL) - buf_reload(buf); - - redraw_all_later(NOT_VALID); - } - } + fprintf(fd, "%.*s\n", len, word); + fclose(fd); + + /* Update the .add.spl file. */ + mkspell(1, &fname, FALSE, TRUE, TRUE); + + /* If the .add file is edited somewhere, reload it. */ + if (buf != NULL) + buf_reload(buf); + + redraw_all_later(NOT_VALID); } } @@ -5475,9 +5494,8 @@ set_spell_chartab(fol, low, upp) * Set the spell character tables from strings in the .spl file. */ static int -set_spell_charflags(flags, cnt, upp) +set_spell_charflags(flags, upp) char_u *flags; - int cnt; char_u *upp; { /* We build the new tables here first, so that we can compare with the @@ -5489,7 +5507,7 @@ set_spell_charflags(flags, cnt, upp) clear_spell_chartab(&new_st); - for (i = 0; i < cnt; ++i) + for (i = 0; flags[i] != NUL; ++i) { new_st.st_isw[i + 128] = (flags[i] & CF_WORD) != 0; new_st.st_isu[i + 128] = (flags[i] & CF_UPPER) != 0; @@ -9362,6 +9380,9 @@ ex_spelldump(eap) int depth; int n; int flags; + char_u *region_names = NULL; /* region names being used */ + int do_region = TRUE; /* dump region names and numbers */ + char_u *p; if (no_spell_checking()) return; @@ -9371,6 +9392,34 @@ ex_spelldump(eap) if (!bufempty() || !buf_valid(buf)) return; + /* Find out if we can support regions: All languages must support the same + * regions or none at all. */ + for (lp = LANGP_ENTRY(buf->b_langp, 0); lp->lp_slang != NULL; ++lp) + { + p = 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) + { + do_region = FALSE; /* region names are different */ + break; + } + } + } + + if (do_region && region_names != NULL) + { + vim_snprintf((char *)IObuff, IOSIZE, "/regions=%s", region_names); + ml_append(lnum++, IObuff, (colnr_T)0, FALSE); + } + else + do_region = FALSE; + + /* + * Loop over all files loaded for the entries in 'spelllang'. + */ for (lp = LANGP_ENTRY(buf->b_langp, 0); lp->lp_slang != NULL; ++lp) { slang = lp->lp_slang; @@ -9420,11 +9469,14 @@ ex_spelldump(eap) * Only use the word when the region matches. */ flags = (int)idxs[n]; if ((round == 2 || (flags & WF_KEEPCAP) == 0) - && ((flags & WF_REGION) == 0 - || (((unsigned)flags >> 8) + && (do_region + || (flags & WF_REGION) == 0 + || (((unsigned)flags >> 8) & lp->lp_region) != 0)) { word[depth] = NUL; + if (!do_region) + flags &= ~WF_REGION; /* Dump the basic word if there is no prefix or * when it's the first one. */ @@ -9470,7 +9522,8 @@ dump_word(word, round, flags, lnum) int keepcap = FALSE; char_u *p; char_u cword[MAXWLEN]; - char_u badword[MAXWLEN + 3]; + char_u badword[MAXWLEN + 10]; + int i; if (round == 1 && (flags & WF_CAPMASK) != 0) { @@ -9485,18 +9538,21 @@ dump_word(word, round, flags, lnum) keepcap = TRUE; } - /* Bad word is preceded by "/!" and some other - * flags. */ - if ((flags & (WF_BANNED | WF_RARE)) || keepcap) - { - STRCPY(badword, "/"); + /* Add flags and regions after a slash. */ + if ((flags & (WF_BANNED | WF_RARE | WF_REGION)) || keepcap) + { + STRCPY(badword, p); + STRCAT(badword, "/"); if (keepcap) STRCAT(badword, "="); if (flags & WF_BANNED) STRCAT(badword, "!"); else if (flags & WF_RARE) STRCAT(badword, "?"); - STRCAT(badword, p); + if (flags & WF_REGION) + for (i = 0; i < 7; ++i) + if (flags & (0x100 << i)) + sprintf((char *)badword + STRLEN(badword), "%d", i + 1); p = badword; }