summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2005-06-28 23:23:32 +0000
committerBram Moolenaar <Bram@vim.org>2005-06-28 23:23:32 +0000
commita1ba811ac90665fa347eb58929e22681e1c99669 (patch)
tree45a7feec13df8a3b390133aaf417d6d026b62cbb
parent9a50b1bf21cee8ba77306e319d88c4e2eeb024c0 (diff)
downloadvim-git-a1ba811ac90665fa347eb58929e22681e1c99669.tar.gz
updated for version 7.0096
-rw-r--r--runtime/doc/index.txt3
-rw-r--r--runtime/syntax/ld.vim81
-rw-r--r--src/README.txt5
-rw-r--r--src/ex_docmd.c4
-rw-r--r--src/spell.c852
5 files changed, 872 insertions, 73 deletions
diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt
index 0976c4132..c03dc7b07 100644
--- a/runtime/doc/index.txt
+++ b/runtime/doc/index.txt
@@ -1,4 +1,4 @@
-*index.txt* For Vim version 7.0aa. Last change: 2005 Jun 23
+*index.txt* For Vim version 7.0aa. Last change: 2005 Jun 28
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -1359,6 +1359,7 @@ The commands are sorted on the non-optional part of their name.
|:source| :so[urce] read Vim or Ex commands from a file
|:spelldump| :spelld[ump] split window and fill with all correct words
|:spellgood| :spe[llgood] add good word for spelling
+|:spellrepall| :spellr[epall] replace all bad words like last |z?|
|:spellwrong| :spellw[rong] add spelling mistake
|:split| :sp[lit] split current window
|:sprevious| :spr[evious] split window and go to previous file in the
diff --git a/runtime/syntax/ld.vim b/runtime/syntax/ld.vim
new file mode 100644
index 000000000..c1a00d9c4
--- /dev/null
+++ b/runtime/syntax/ld.vim
@@ -0,0 +1,81 @@
+" Vim syntax file
+" Language: ld(1) script
+" Maintainer: Nikolai Weibull <nikolai+work.vim@bitwi.se>
+" Latest Revision: 2005-06-28
+
+if exists("b:current_syntax")
+ finish
+endif
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+syn keyword ldTodo contained TODO FIXME XXX NOTE
+
+syn region ldComment start='/\*' end='\*/' contains=ldTodo,@Spell
+
+syn region ldFileName start=+"+ end=+"+
+
+syn keyword ldPreProc SECTIONS MEMORY OVERLAY PHDRS VERSION
+syn match ldPreProc '\<VERS_\d\+\.\d\+'
+
+syn keyword ldFunction ABSOLUTE ADDR ALIGN BLOCK DATA_SEGMENT_ALIGN
+ \ DATA_SEGMENT_END DATA_SEGMENT_RELRO_END DEFINED
+ \ LOADADDR MAX MIN NEXT SIZEOF SIZEOF_HEADERS
+ \ sizeof_headers
+
+syn keyword ldKeyword ENTRY INCLUDE INPUT GROUP OUTPUT
+ \ SEARCH_DIR STARTUP OUTPUT_FORMAT TARGET
+ \ ASSERT EXTERN FORCE_COMMON_ALLOCATION
+ \ INHIBIT_COMMON_ALLOCATION NOCROSSREFS OUTPUT_ARCH
+ \ PROVIDE EXCLUDE_FILE SORT KEEP FILL
+ \ CREATE_OBJECT_SYMBOLS CONSTRUCTORS SUBALIGN
+ \ FILEHDR AT __asm__ ABSOLUTE
+
+syn keyword ldDataType BYTE SHORT LONG QUAD SQUAD
+syn keyword ldOutputType NOLOAD DSECT COPY INFO OVERLAY
+syn keyword ldPTType PT_NULL PT_LOAD PT_DYNAMIC PT_INTERP
+ \ PT_NOTE PT_SHLIB PT_PHDR
+
+syn keyword ldSpecial COMMON
+syn match ldSpecial '/DISCARD/'
+
+syn keyword ldIdentifier ORIGIN LENGTH
+
+syn match ldSpecSections '\.'
+syn match ldSections '\.\S\+'
+syn match ldSpecSections '\.\%(text\|data\|bss\|symver\)\>'
+
+syn match ldNumber display '\<0[xX]\x\+\>'
+syn match ldNumber display '\d\+[KM]\>' contains=ldNumberMult
+syn match ldNumberMult display '[KM]\>'
+syn match ldOctal contained display '\<0\o\+\>'
+ \ contains=ldOctalZero
+syn match ldOctalZero contained display '\<0'
+syn match ldOctalError contained display '\<0\o*[89]\d*\>'
+
+
+hi def link ldTodo Todo
+hi def link ldComment Comment
+hi def link ldFileName String
+hi def link ldPreProc PreProc
+hi def link ldFunction Identifier
+hi def link ldKeyword Keyword
+hi def link ldType Type
+hi def link ldDataType ldType
+hi def link ldOutputType ldType
+hi def link ldPTType ldType
+hi def link ldSpecial Special
+hi def link ldIdentifier Identifier
+hi def link ldSections Constant
+hi def link ldSpecSections Special
+hi def link ldNumber Number
+hi def link ldNumberMult PreProc
+hi def link ldOctal ldNumber
+hi def link ldOctalZero PreProc
+hi def link ldOctalError Error
+
+let b:current_syntax = "ld"
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
diff --git a/src/README.txt b/src/README.txt
index 05b864fa7..2ebbda005 100644
--- a/src/README.txt
+++ b/src/README.txt
@@ -24,16 +24,19 @@ Most code can be found in a file with an obvious name (incomplete list):
fold.c folding
getchar.c getting characters and key mapping
mark.c marks
+ mbyte.c multy-byte character handling
memfile.c storing lines for buffers in a swapfile
memline.c storing lines for buffers in memory
menu.c menus
message.c (error) messages
- mbyte.c multy-byte character handling
ops.c handling operators ("d", "y", "p")
option.c options
quickfix.c quickfix commands (":make", ":cn")
+ regexp.c pattern matching
screen.c updating the windows
search.c pattern searching
+ spell.c spell checking
+ syntax.c syntax and other highlighting
tag.c tags
term.c terminal handling, termcap codes
undo.c undo and redo
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index 75d2c9411..b2cc7f917 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -227,6 +227,7 @@ static void ex_popup __ARGS((exarg_T *eap));
# define ex_spell ex_ni
# define ex_mkspell ex_ni
# define ex_spelldump ex_ni
+# define ex_spellrepall ex_ni
#endif
#ifndef FEAT_MZSCHEME
# define ex_mzscheme ex_script_ni
@@ -4266,7 +4267,8 @@ expand_filename(eap, cmdlinep, errormsgp)
if (vim_strchr(eap->arg, '$') != NULL
|| vim_strchr(eap->arg, '~') != NULL)
{
- expand_env_esc(eap->arg, NameBuff, MAXPATHL, TRUE);
+ expand_env_esc(eap->arg, NameBuff, MAXPATHL,
+ TRUE, NULL);
has_wildcards = mch_has_wildcard(NameBuff);
p = NameBuff;
}
diff --git a/src/spell.c b/src/spell.c
index 71ed843d1..c4efed275 100644
--- a/src/spell.c
+++ b/src/spell.c
@@ -270,6 +270,11 @@ typedef struct salitem_S
char_u *sm_oneoff; /* letters from () or NULL */
char_u *sm_rules; /* rules like ^, $, priority */
char_u *sm_to; /* replacement. */
+#ifdef FEAT_MBYTE
+ int *sm_lead_w; /* wide character copy of "sm_lead" */
+ int *sm_oneoff_w; /* wide character copy of "sm_oneoff" */
+ int *sm_to_w; /* wide character copy of "sm_to" */
+#endif
} salitem_T;
/*
@@ -412,6 +417,7 @@ typedef struct suggest_S
#define SCORE_INSDUP 66 /* insert a duplicate character */
#define SCORE_NONWORD 103 /* change non-word to word char */
+#define SCORE_FILE 30 /* suggestion from a file */
#define SCORE_MAXINIT 350 /* Initial maximum score: higher == slower.
* 350 allows for about three changes. */
@@ -558,6 +564,9 @@ static void spell_load_lang __ARGS((char_u *lang));
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));
+#ifdef FEAT_MBYTE
+static int *mb_str2wide __ARGS((char_u *s));
+#endif
static idx_T read_tree __ARGS((FILE *fd, char_u *byts, idx_T *idxs, int maxidx, int startidx, int prefixtree, int maxprefcondnr));
static int find_region __ARGS((char_u *rp, char_u *region));
static int captype __ARGS((char_u *word, char_u *end));
@@ -567,6 +576,11 @@ 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));
static void spell_find_suggest __ARGS((char_u *badptr, suginfo_T *su, int maxcount, int banbadword));
+#ifdef FEAT_EVAL
+static void spell_suggest_expr __ARGS((suginfo_T *su, char_u *expr));
+#endif
+static void spell_suggest_file __ARGS((suginfo_T *su, char_u *fname));
+static void spell_suggest_intern __ARGS((suginfo_T *su));
static void spell_find_cleanup __ARGS((suginfo_T *su));
static void onecap_copy __ARGS((char_u *word, char_u *wcopy, int upper));
static void allcap_copy __ARGS((char_u *word, char_u *wcopy));
@@ -588,6 +602,9 @@ static void free_banned __ARGS((suginfo_T *su));
static void rescore_suggestions __ARGS((suginfo_T *su));
static int cleanup_suggestions __ARGS((garray_T *gap, int maxscore, int keep));
static void spell_soundfold __ARGS((slang_T *slang, char_u *inword, char_u *res));
+#ifdef FEAT_MBYTE
+static void spell_soundfold_w __ARGS((slang_T *slang, char_u *inword, char_u *res));
+#endif
static int soundalike_score __ARGS((char_u *goodsound, char_u *badsound));
static int spell_edit_score __ARGS((char_u *badword, char_u *goodword));
static void dump_word __ARGS((char_u *word, int round, int flags, linenr_T lnum));
@@ -1951,6 +1968,31 @@ formerr:
for (i = 0; i < ccnt; ++i)
*p++ = getc(fd); /* <salto> */
*p++ = NUL;
+
+#ifdef FEAT_MBYTE
+ if (has_mbyte)
+ {
+ /* convert the multi-byte strings to wide char strings */
+ smp->sm_lead_w = mb_str2wide(smp->sm_lead);
+ smp->sm_leadlen = mb_charlen(smp->sm_lead);
+ if (smp->sm_oneoff == NULL)
+ smp->sm_oneoff_w = NULL;
+ else
+ smp->sm_oneoff_w = mb_str2wide(smp->sm_oneoff);
+ smp->sm_to_w = mb_str2wide(smp->sm_to);
+ if (smp->sm_lead_w == NULL
+ || (smp->sm_oneoff_w == NULL && smp->sm_oneoff != NULL)
+ || smp->sm_to_w == NULL)
+ {
+ vim_free(smp->sm_lead);
+ vim_free(smp->sm_to);
+ vim_free(smp->sm_lead_w);
+ vim_free(smp->sm_oneoff_w);
+ vim_free(smp->sm_to_w);
+ goto endFAIL;
+ }
+ }
+#endif
}
/* Fill the first-index table. */
@@ -1960,8 +2002,44 @@ formerr:
for (i = 0; i < gap->ga_len; ++i)
{
smp = &((salitem_T *)gap->ga_data)[i];
- if (first[*smp->sm_lead] == -1)
- first[*smp->sm_lead] = i;
+#ifdef FEAT_MBYTE
+ if (has_mbyte)
+ /* Use the lowest byte of the first character. */
+ c = *smp->sm_lead_w & 0xff;
+ else
+#endif
+ c = *smp->sm_lead;
+ if (first[c] == -1)
+ {
+ first[c] = i;
+#ifdef FEAT_MBYTE
+ if (has_mbyte)
+ {
+ int j;
+
+ /* Make sure all entries with this byte are following each
+ * other. Move the ones down that are in the wrong position.
+ * Do keep the right sequence. */
+ while (i + 1 < gap->ga_len && (*smp[1].sm_lead_w & 0xff) == c)
+ {
+ ++i;
+ ++smp;
+ }
+ for (j = 1; i + j < gap->ga_len; ++j)
+ if ((*smp[j].sm_lead_w & 0xff) == c)
+ {
+ salitem_T tsal;
+
+ ++i;
+ ++smp;
+ --j;
+ tsal = smp[j];
+ mch_memmove(smp + 1, smp, sizeof(salitem_T) * j);
+ *smp = tsal;
+ }
+ }
+#endif
+ }
}
cnt = (getc(fd) << 8) + getc(fd); /* <maplen> */
@@ -2048,6 +2126,30 @@ endOK:
return lp;
}
+#ifdef FEAT_MBYTE
+/*
+ * Turn a multi-byte string into a wide character string.
+ * Return it in allocated memory (NULL for out-of-memory)
+ */
+ static int *
+mb_str2wide(s)
+ char_u *s;
+{
+ int *res;
+ char_u *p;
+ int i = 0;
+
+ res = (int *)alloc(sizeof(int) * (mb_charlen(s) + 1));
+ if (res != NULL)
+ {
+ for (p = s; *p != NUL; )
+ res[i++] = mb_ptr2char_adv(&p);
+ res[i] = NUL;
+ }
+ return res;
+}
+#endif
+
/*
* Read one row of siblings from the spell file and store it in the byte array
* "byts" and index array "idxs". Recursively read the children.
@@ -5200,6 +5302,35 @@ spell_iswordp(p)
return spelltab.st_isw[spell_ismw[*p] ? p[1] : p[0]];
}
+#ifdef FEAT_MBYTE
+/*
+ * Return TRUE if "p" points to a word character.
+ * Wide version of spell_iswordp().
+ */
+ static int
+spell_iswordp_w(p)
+ int *p;
+{
+ int *s;
+
+ if (*p < 256 ? spell_ismw[*p] : (spell_ismw_mb != NULL
+ && vim_strchr(spell_ismw_mb, *p) != NULL))
+ s = p + 1;
+ else
+ s = p;
+
+ if (mb_char2len(*s) > 1)
+ {
+ if (enc_utf8)
+ return utf_class(*s) >= 2;
+ if (enc_dbcs)
+ return dbcs_class((unsigned)*s >> 8, *s & 0xff) >= 2;
+ return 0;
+ }
+ return spelltab.st_isw[*s];
+}
+#endif
+
/*
* Write the table with prefix conditions to the .spl file.
*/
@@ -5318,6 +5449,59 @@ spell_casefold(str, len, buf, buflen)
return OK;
}
+#define SPS_BEST 1
+#define SPS_FAST 2
+#define SPS_DOUBLE 4
+
+static int sps_flags = SPS_BEST;
+
+/*
+ * Check the 'spellsuggest' option. Return FAIL if it's wrong.
+ * Sets "sps_flags".
+ */
+ int
+spell_check_sps()
+{
+ char_u *p;
+ char_u buf[MAXPATHL];
+ int f;
+
+ sps_flags = 0;
+
+ for (p = p_sps; *p != NUL; )
+ {
+ copy_option_part(&p, buf, MAXPATHL, ",");
+
+ f = 0;
+ if (STRCMP(buf, "best") == 0)
+ f = SPS_BEST;
+ else if (STRCMP(buf, "fast") == 0)
+ f = SPS_FAST;
+ else if (STRCMP(buf, "double") == 0)
+ f = SPS_DOUBLE;
+ else if (STRNCMP(buf, "expr:", 5) != 0
+ && STRNCMP(buf, "file:", 5) != 0)
+ f = -1;
+
+ if (f == -1 || (sps_flags != 0 && f != 0))
+ {
+ sps_flags = SPS_BEST;
+ return FAIL;
+ }
+ if (f != 0)
+ sps_flags = f;
+ }
+
+ if (sps_flags == 0)
+ sps_flags = SPS_BEST;
+
+ return OK;
+}
+
+/* Remember what "z?" replaced. */
+static char_u *repl_from = NULL;
+static char_u *repl_to = NULL;
+
/*
* "z?": Find badly spelled word under or after the cursor.
* Give suggestions for the properly spelled word.
@@ -5333,6 +5517,7 @@ spell_suggest()
int c;
suginfo_T sug;
suggest_T *stp;
+ int mouse_used;
/* Find the start of the badly spelled word. */
if (spell_move_to(FORWARD, TRUE, TRUE) == FAIL
@@ -5371,8 +5556,14 @@ spell_suggest()
MSG(_("Sorry, no suggestions"));
else
{
+ vim_free(repl_from);
+ repl_from = NULL;
+ vim_free(repl_to);
+ repl_to = NULL;
+
/* List the suggestions. */
msg_start();
+ lines_left = Rows; /* avoid more prompt */
vim_snprintf((char *)IObuff, IOSIZE, _("Change \"%.*s\" to:"),
sug.su_badlen, sug.su_badptr);
msg_puts(IObuff);
@@ -5415,16 +5606,22 @@ spell_suggest()
msg_advance(30);
msg_puts(IObuff);
}
- lines_left = 3; /* avoid more prompt */
msg_putchar('\n');
}
/* Ask for choice. */
- i = prompt_for_number();
+ i = prompt_for_number(&mouse_used);
+ if (mouse_used)
+ i -= lines_left;
+
if (i > 0 && i <= sug.su_ga.ga_len && u_save_cursor() == OK)
{
- /* Replace the word. */
+ /* Save the from and to text for :spellrepall. */
stp = &SUG(sug.su_ga, i - 1);
+ repl_from = vim_strnsave(sug.su_badptr, stp->st_orglen);
+ repl_to = vim_strsave(stp->st_word);
+
+ /* Replace the word. */
p = alloc(STRLEN(line) - stp->st_orglen + STRLEN(stp->st_word) + 1);
if (p != NULL)
{
@@ -5451,6 +5648,69 @@ spell_suggest()
}
/*
+ * ":spellrepall"
+ */
+/*ARGSUSED*/
+ void
+ex_spellrepall(eap)
+ exarg_T *eap;
+{
+ pos_T pos = curwin->w_cursor;
+ char_u *frompat;
+ int addlen;
+ char_u *line;
+ char_u *p;
+ int didone = FALSE;
+ int save_ws = p_ws;
+
+ if (repl_from == NULL || repl_to == NULL)
+ {
+ EMSG(_("E752: No previous spell replacement"));
+ return;
+ }
+ addlen = STRLEN(repl_to) - STRLEN(repl_from);
+
+ frompat = alloc(STRLEN(repl_from) + 7);
+ if (frompat == NULL)
+ return;
+ sprintf((char *)frompat, "\\V\\<%s\\>", repl_from);
+ p_ws = FALSE;
+
+ curwin->w_cursor.lnum = 0;
+ while (!got_int)
+ {
+ if (do_search(NULL, '/', frompat, 1L, SEARCH_KEEP) == 0
+ || u_save_cursor() == FAIL)
+ break;
+
+ /* Only replace when the right word isn't there yet. This happens
+ * when changing "etc" to "etc.". */
+ line = ml_get_curline();
+ if (addlen <= 0 || STRNCMP(line + curwin->w_cursor.col,
+ repl_to, STRLEN(repl_to)) != 0)
+ {
+ p = alloc(STRLEN(line) + addlen + 1);
+ if (p == NULL)
+ break;
+ mch_memmove(p, line, curwin->w_cursor.col);
+ STRCPY(p + curwin->w_cursor.col, repl_to);
+ STRCAT(p, line + curwin->w_cursor.col + STRLEN(repl_from));
+ ml_replace(curwin->w_cursor.lnum, p, FALSE);
+ changed_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col);
+ didone = TRUE;
+ }
+ curwin->w_cursor.col += STRLEN(repl_to);
+ }
+
+ p_ws = save_ws;
+ curwin->w_cursor = pos;
+ vim_free(frompat);
+
+ if (!didone)
+ EMSG2(_("E753: Not found: %s"), repl_from);
+}
+
+/*
* Find spell suggestions for "word". Return them in the growarray "*gap" as
* a list of allocated strings.
*/
@@ -5505,6 +5765,13 @@ spell_find_suggest(badptr, su, maxcount, banbadword)
int banbadword; /* don't include badword in suggestions */
{
int attr;
+ char_u buf[MAXPATHL];
+ char_u *p;
+ int do_combine = FALSE;
+ char_u *sps_copy;
+#ifdef FEAT_EVAL
+ static int expr_busy = FALSE;
+#endif
/*
* Set the info in "*su".
@@ -5519,6 +5786,7 @@ spell_find_suggest(badptr, su, maxcount, banbadword)
su->su_badptr = badptr;
su->su_badlen = spell_check(curwin, su->su_badptr, &attr);
su->su_maxcount = maxcount;
+ su->su_maxscore = SCORE_MAXINIT;
if (su->su_badlen >= MAXWLEN)
su->su_badlen = MAXWLEN - 1; /* just in case */
@@ -5532,13 +5800,157 @@ spell_find_suggest(badptr, su, maxcount, banbadword)
if (banbadword)
add_banned(su, su->su_badword);
+ /* Make a copy of 'spellsuggest', because the expression may change it. */
+ sps_copy = vim_strsave(p_sps);
+ if (sps_copy == NULL)
+ return;
+
+ /* Loop over the items in 'spellsuggest'. */
+ for (p = sps_copy; *p != NUL; )
+ {
+ copy_option_part(&p, buf, MAXPATHL, ",");
+
+ if (STRNCMP(buf, "expr:", 5) == 0)
+ {
+#ifdef FEAT_EVAL
+ /* Evaluate an expression. Skip this when called recursively
+ * (using spellsuggest() in the expression). */
+ if (!expr_busy)
+ {
+ expr_busy = TRUE;
+ spell_suggest_expr(su, buf + 5);
+ expr_busy = FALSE;
+ }
+#endif
+ }
+ else if (STRNCMP(buf, "file:", 5) == 0)
+ /* Use list of suggestions in a file. */
+ spell_suggest_file(su, buf + 5);
+ else
+ {
+ /* Use internal method. */
+ spell_suggest_intern(su);
+ if (sps_flags & SPS_DOUBLE)
+ do_combine = TRUE;
+ }
+ }
+
+ vim_free(sps_copy);
+
+ if (do_combine)
+ /* Combine the two list of suggestions. This must be done last,
+ * because sorting changes the order again. */
+ score_combine(su);
+}
+
+#ifdef FEAT_EVAL
+/*
+ * Find suggestions by evaluating expression "expr".
+ */
+ static void
+spell_suggest_expr(su, expr)
+ suginfo_T *su;
+ char_u *expr;
+{
+ list_T *list;
+ listitem_T *li;
+ int score;
+ char_u *p;
+
+ /* The work is split up in a few parts to avoid having to export
+ * suginfo_T.
+ * First evaluate the expression and get the resulting list. */
+ list = eval_spell_expr(su->su_badword, expr);
+ if (list != NULL)
+ {
+ /* Loop over the items in the list. */
+ for (li = list->lv_first; li != NULL; li = li->li_next)
+ if (li->li_tv.v_type == VAR_LIST)
+ {
+ /* Get the word and the score from the items. */
+ score = get_spellword(li->li_tv.vval.v_list, &p);
+ if (score >= 0)
+ add_suggestion(su, &su->su_ga, p,
+ su->su_badlen, score, 0, TRUE);
+ }
+ list_unref(list);
+ }
+
+ /* Sort the suggestions and truncate at "maxcount". */
+ (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount);
+}
+#endif
+
+/*
+ * Find suggestions a file "fname".
+ */
+ static void
+spell_suggest_file(su, fname)
+ suginfo_T *su;
+ char_u *fname;
+{
+ FILE *fd;
+ char_u line[MAXWLEN * 2];
+ char_u *p;
+ int len;
+ char_u cword[MAXWLEN];
+
+ /* Open the file. */
+ fd = mch_fopen((char *)fname, "r");
+ if (fd == NULL)
+ {
+ EMSG2(_(e_notopen), fname);
+ return;
+ }
+
+ /* Read it line by line. */
+ while (!vim_fgets(line, MAXWLEN * 2, fd) && !got_int)
+ {
+ line_breakcheck();
+
+ p = vim_strchr(line, '/');
+ if (p == NULL)
+ continue; /* No Tab found, just skip the line. */
+ *p++ = NUL;
+ if (STRICMP(su->su_badword, line) == 0)
+ {
+ /* Match! Isolate the good word, until CR or NL. */
+ for (len = 0; p[len] >= ' '; ++len)
+ ;
+ p[len] = NUL;
+
+ /* If the suggestion doesn't have specific case duplicate the case
+ * of the bad word. */
+ if (captype(p, NULL) == 0)
+ {
+ make_case_word(p, cword, su->su_badflags);
+ p = cword;
+ }
+
+ add_suggestion(su, &su->su_ga, p, su->su_badlen,
+ SCORE_FILE, 0, TRUE);
+ }
+ }
+
+ fclose(fd);
+
+ /* Sort the suggestions and truncate at "maxcount". */
+ (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount);
+}
+
+/*
+ * Find suggestions for the internal method indicated by "sps_flags".
+ */
+ static void
+spell_suggest_intern(su)
+ suginfo_T *su;
+{
/*
* 1. Try special cases, such as repeating a word: "the the" -> "the".
*
* Set a maximum score to limit the combination of operations that is
* tried.
*/
- su->su_maxscore = SCORE_MAXINIT;
suggest_try_special(su);
/*
@@ -5574,19 +5986,14 @@ spell_find_suggest(badptr, su, maxcount, banbadword)
got_int = FALSE;
}
- if (sps_flags & SPS_DOUBLE)
- {
- /* Combine the two list of suggestions. */
- score_combine(su);
- }
- else if (su->su_ga.ga_len != 0)
+ if ((sps_flags & SPS_DOUBLE) == 0 && su->su_ga.ga_len != 0)
{
if (sps_flags & SPS_BEST)
/* Adjust the word score for how it sounds like. */
rescore_suggestions(su);
/* Sort the suggestions and truncate at "maxcount". */
- (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, maxcount);
+ (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount);
}
}
@@ -5859,7 +6266,8 @@ suggest_try_change(su)
add_banned(su, preword + prewordlen);
break;
}
- if (was_banned(su, preword + prewordlen))
+ if (was_banned(su, preword + prewordlen)
+ || was_banned(su, preword))
break;
newscore = 0;
@@ -7271,6 +7679,16 @@ add_suggestion(su, gap, goodword, badlen, score, altscore, had_bonus)
else
p = NULL;
}
+ else if (i < 0)
+ {
+ /* When replacing part of the word check that we actually change
+ * something. For "the the" a suggestion can be replacing the first
+ * "the" with itself, since "the" wasn't banned. */
+ if (badlen == STRLEN(goodword)
+ && STRNCMP(su->su_badword, goodword, badlen) == 0)
+ return;
+ }
+
if (score <= su->su_maxscore)
{
@@ -7468,6 +7886,38 @@ cleanup_suggestions(gap, maxscore, keep)
return maxscore;
}
+#if defined(FEAT_EVAL) || defined(PROTO)
+/*
+ * Soundfold a string, for soundfold().
+ * Result is in allocated memory, NULL for an error.
+ */
+ char_u *
+eval_soundfold(word)
+ char_u *word;
+{
+ langp_T *lp;
+ char_u fword[MAXWLEN];
+ char_u sound[MAXWLEN];
+
+ if (curwin->w_p_spell && *curbuf->b_p_spl != NUL)
+ /* Use the sound-folding of the first language that supports it. */
+ for (lp = LANGP_ENTRY(curwin->w_buffer->b_langp, 0);
+ lp->lp_slang != NULL; ++lp)
+ if (lp->lp_slang->sl_sal.ga_len > 0)
+ {
+ /* word most be case-folded first. */
+ (void)spell_casefold(word, STRLEN(word), fword, MAXWLEN);
+
+ /* soundfold the word */
+ spell_soundfold(lp->lp_slang, fword, sound);
+ return vim_strsave(sound);
+ }
+
+ /* No language with sound folding, return word as-is. */
+ return vim_strsave(word);
+}
+#endif
+
/*
* Turn "inword" into its sound-a-like equivalent in "res[MAXWLEN]".
*/
@@ -7479,10 +7929,6 @@ spell_soundfold(slang, inword, res)
{
salitem_T *smp;
char_u word[MAXWLEN];
-#ifdef FEAT_MBYTE
- int l;
- int found_mbyte = FALSE;
-#endif
char_u *s;
char_u *t;
char_u *pf;
@@ -7497,6 +7943,15 @@ spell_soundfold(slang, inword, res)
int p0 = -333;
int c0;
+#ifdef FEAT_MBYTE
+ if (has_mbyte)
+ {
+ /* Call the multi-byte version of this. */
+ spell_soundfold_w(slang, inword, res);
+ return;
+ }
+#endif
+
/* Remove accents, if wanted. We actually remove all non-word characters.
* But keep white space. */
if (slang->sl_rem_accents)
@@ -7509,20 +7964,6 @@ spell_soundfold(slang, inword, res)
*t++ = ' ';
s = skipwhite(s);
}
-#ifdef FEAT_MBYTE
- else if (has_mbyte)
- {
- l = mb_ptr2len_check(s);
- if (spell_iswordp(s))
- {
- mch_memmove(t, s, l);
- t += l;
- if (l > 1)
- found_mbyte = TRUE;
- }
- s += l;
- }
-#endif
else
{
if (spell_iswordp(s))
@@ -7533,35 +7974,13 @@ spell_soundfold(slang, inword, res)
*t = NUL;
}
else
- {
-#ifdef FEAT_MBYTE
- if (has_mbyte)
- for (s = inword; *s != NUL; s += l)
- if ((l = mb_ptr2len_check(s)) > 1)
- {
- found_mbyte = TRUE;
- break;
- }
-#endif
STRCPY(word, inword);
- }
-
-#ifdef FEAT_MBYTE
- /* If there are multi-byte characters in the word return it as-is, because
- * the following won't work. */
- if (found_mbyte)
- {
- STRCPY(res, word);
- return;
- }
-#endif
smp = (salitem_T *)slang->sl_sal.ga_data;
/*
* This comes from Aspell phonet.cpp. Converted from C++ to C.
* Changed to keep spaces.
- * TODO: support for multi-byte chars.
*/
i = reslen = z = 0;
while ((c = word[i]) != NUL)
@@ -7723,7 +8142,7 @@ spell_soundfold(slang, inword, res)
z0 = 1;
z = 1;
k0 = 0;
- while (*s != NUL && word[i+k0] != NUL)
+ while (*s != NUL && word[i + k0] != NUL)
{
word[i + k0] = *s;
k0++;
@@ -7744,10 +8163,7 @@ spell_soundfold(slang, inword, res)
while (*s != NUL && s[1] != NUL && reslen < MAXWLEN)
{
if (reslen == 0 || res[reslen - 1] != *s)
- {
- res[reslen] = *s;
- reslen++;
- }
+ res[reslen++] = *s;
s++;
}
/* new "actual letter" */
@@ -7755,10 +8171,7 @@ spell_soundfold(slang, inword, res)
if (strstr((char *)pf, "^^") != NULL)
{
if (c != NUL)
- {
- res[reslen] = c;
- reslen++;
- }
+ res[reslen++] = c;
mch_memmove(word, word + i + 1,
STRLEN(word + i + 1) + 1);
i = 0;
@@ -7780,11 +8193,8 @@ spell_soundfold(slang, inword, res)
if (k && !p0 && reslen < MAXWLEN && c != NUL
&& (!slang->sl_collapse || reslen == 0
|| res[reslen - 1] != c))
- {
/* condense only double letters */
- res[reslen] = c;
- reslen++;
- }
+ res[reslen++] = c;
i++;
z = 0;
@@ -7795,6 +8205,308 @@ spell_soundfold(slang, inword, res)
res[reslen] = NUL;
}
+#ifdef FEAT_MBYTE
+/*
+ * Turn "inword" into its sound-a-like equivalent in "res[MAXWLEN]".
+ * Multi-byte version of spell_soundfold().
+ */
+ static void
+spell_soundfold_w(slang, inword, res)
+ slang_T *slang;
+ char_u *inword;
+ char_u *res;
+{
+ salitem_T *smp;
+ int word[MAXWLEN];
+ int wres[MAXWLEN];
+ int l;
+ char_u *s;
+ int *ws;
+ char_u *t;
+ int *pf;
+ int i, j, z;
+ int reslen;
+ int n, k = 0;
+ int z0;
+ int k0;
+ int n0;
+ int c;
+ int pri;
+ int p0 = -333;
+ int c0;
+ int did_white = FALSE;
+
+ /*
+ * Convert the multi-byte string to a wide-character string.
+ * Remove accents, if wanted. We actually remove all non-word characters.
+ * But keep white space.
+ */
+ n = 0;
+ for (s = inword; *s != NUL; )
+ {
+ t = s;
+ c = mb_ptr2char_adv(&s);
+ if (slang->sl_rem_accents)
+ {
+ if (enc_utf8 ? utf_class(c) == 0 : vim_iswhite(c))
+ {
+ if (did_white)
+ continue;
+ c = ' ';
+ did_white = TRUE;
+ }
+ else
+ {
+ did_white = FALSE;
+ if (!spell_iswordp(t))
+ continue;
+ }
+ }
+ word[n++] = c;
+ }
+ word[n] = NUL;
+
+ smp = (salitem_T *)slang->sl_sal.ga_data;
+
+ /*
+ * This comes from Aspell phonet.cpp.
+ * Converted from C++ to C. Added support for multi-byte chars.
+ * Changed to keep spaces.
+ */
+ i = reslen = z = 0;
+ while ((c = word[i]) != NUL)
+ {
+ /* Start with the first rule that has the character in the word. */
+ n = slang->sl_sal_first[c & 0xff];
+ z0 = 0;
+
+ if (n >= 0)
+ {
+ /* check all rules for the same letter */
+ for (; ((ws = smp[n].sm_lead_w)[0] & 0xff) == (c & 0xff); ++n)
+ {
+ /* Quickly skip entries that don't match the word. Most
+ * entries are less then three chars, optimize for that. */
+ k = smp[n].sm_leadlen;
+ if (k > 1)
+ {
+ if (word[i + 1] != ws[1])
+ continue;
+ if (k > 2)
+ {
+ for (j = 2; j < k; ++j)
+ if (word[i + j] != ws[j])
+ break;
+ if (j < k)
+ continue;
+ }
+ }
+
+ if ((pf = smp[n].sm_oneoff_w) != NULL)
+ {
+ /* Check for match with one of the chars in "sm_oneoff". */
+ while (*pf != NUL && *pf != word[i + k])
+ ++pf;
+ if (*pf == NUL)
+ continue;
+ ++k;
+ }
+ s = smp[n].sm_rules;
+ pri = 5; /* default priority */
+
+ p0 = *s;
+ k0 = k;
+ while (*s == '-' && k > 1)
+ {
+ k--;
+ s++;
+ }
+ if (*s == '<')
+ s++;
+ if (VIM_ISDIGIT(*s))
+ {
+ /* determine priority */
+ pri = *s - '0';
+ s++;
+ }
+ if (*s == '^' && *(s + 1) == '^')
+ s++;
+
+ if (*s == NUL
+ || (*s == '^'
+ && (i == 0 || !(word[i - 1] == ' '
+ || spell_iswordp_w(word + i - 1)))
+ && (*(s + 1) != '$'
+ || (!spell_iswordp_w(word + i + k0))))
+ || (*s == '$' && i > 0
+ && spell_iswordp_w(word + i - 1)
+ && (!spell_iswordp_w(word + i + k0))))
+ {
+ /* search for followup rules, if: */
+ /* followup and k > 1 and NO '-' in searchstring */
+ c0 = word[i + k - 1];
+ n0 = slang->sl_sal_first[c0 & 0xff];
+
+ if (slang->sl_followup && k > 1 && n0 >= 0
+ && p0 != '-' && word[i + k] != NUL)
+ {
+ /* test follow-up rule for "word[i + k]" */
+ for ( ; ((ws = smp[n0].sm_lead_w)[0] & 0xff)
+ == (c0 & 0xff); ++n0)
+ {
+ /* Quickly skip entries that don't match the word.
+ * */
+ k0 = smp[n0].sm_leadlen;
+ if (k0 > 1)
+ {
+ if (word[i + k] != ws[1])
+ continue;
+ if (k0 > 2)
+ {
+ pf = word + i + k + 1;
+ for (j = 2; j < k0; ++j)
+ if (*pf++ != ws[j])
+ break;
+ if (j < k0)
+ continue;
+ }
+ }
+ k0 += k - 1;
+
+ if ((pf = smp[n0].sm_oneoff_w) != NULL)
+ {
+ /* Check for match with one of the chars in
+ * "sm_oneoff". */
+ while (*pf != NUL && *pf != word[i + k0])
+ ++pf;
+ if (*pf == NUL)
+ continue;
+ ++k0;
+ }
+
+ p0 = 5;
+ s = smp[n0].sm_rules;
+ while (*s == '-')
+ {
+ /* "k0" gets NOT reduced because
+ * "if (k0 == k)" */
+ s++;
+ }
+ if (*s == '<')
+ s++;
+ if (VIM_ISDIGIT(*s))
+ {
+ p0 = *s - '0';
+ s++;
+ }
+
+ if (*s == NUL
+ /* *s == '^' cuts */
+ || (*s == '$'
+ && !spell_iswordp_w(word + i + k0)))
+ {
+ if (k0 == k)
+ /* this is just a piece of the string */
+ continue;
+
+ if (p0 < pri)
+ /* priority too low */
+ continue;
+ /* rule fits; stop search */
+ break;
+ }
+ }
+
+ if (p0 >= pri && (smp[n0].sm_lead_w[0] & 0xff)
+ == (c0 & 0xff))
+ continue;
+ }
+
+ /* replace string */
+ ws = smp[n].sm_to_w;
+ 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 != NUL && (wres[reslen - 1] == c
+ || wres[reslen - 1] == *ws))
+ reslen--;
+ z0 = 1;
+ z = 1;
+ k0 = 0;
+ while (*ws != NUL && word[i + k0] != NUL)
+ {
+ word[i + k0] = *ws;
+ k0++;
+ ws++;
+ }
+ if (k > k0)
+ mch_memmove(word + i + k0, word + i + k,
+ sizeof(int) * (STRLEN(word + i + k) + 1));
+
+ /* new "actual letter" */
+ c = word[i];
+ }
+ else
+ {
+ /* no '<' rule used */
+ i += k - 1;
+ z = 0;
+ while (*ws != NUL && ws[1] != NUL && reslen < MAXWLEN)
+ {
+ if (reslen == 0 || wres[reslen - 1] != *ws)
+ wres[reslen++] = *ws;
+ ws++;
+ }
+ /* new "actual letter" */
+ c = *ws;
+ if (strstr((char *)s, "^^") != NULL)
+ {
+ if (c != NUL)
+ wres[reslen++] = c;
+ mch_memmove(word, word + i + 1,
+ sizeof(int) * (STRLEN(word + i + 1) + 1));
+ i = 0;
+ z0 = 1;
+ }
+ }
+ break;
+ }
+ }
+ }
+ else if (vim_iswhite(c))
+ {
+ c = ' ';
+ k = 1;
+ }
+
+ if (z0 == 0)
+ {
+ if (k && !p0 && reslen < MAXWLEN && c != NUL
+ && (!slang->sl_collapse || reslen == 0
+ || wres[reslen - 1] != c))
+ /* condense only double letters */
+ wres[reslen++] = c;
+
+ i++;
+ z = 0;
+ k = 0;
+ }
+ }
+
+ /* Convert wide characters in "wres" to a multi-byte string in "res". */
+ l = 0;
+ for (n = 0; n < reslen; ++n)
+ {
+ l += mb_char2bytes(wres[n], res + l);
+ if (l + MB_MAXBYTES > MAXWLEN)
+ break;
+ }
+ res[l] = NUL;
+}
+#endif
+
/*
* Compute a score for two sound-a-like words.
* This permits up to two inserts/deletes/swaps/etc. to keep things fast.
@@ -8023,7 +8735,7 @@ spell_edit_score(badword, goodword)
char_u *goodword;
{
int *cnt;
- int badlen, goodlen;
+ int badlen, goodlen; /* lenghts including NUL */
int j, i;
int t;
int bc, gc;
@@ -8289,8 +9001,8 @@ dump_word(word, round, flags, lnum)
}
/*
- * Find matching prefixes for "word". Prepend each to "word" and append
- * a line to the buffer.
+ * For ":spelldump": Find matching prefixes for "word". Prepend each to
+ * "word" and append a line to the buffer.
* Return the updated line number.
*/
static linenr_T