# HG changeset patch # User Bram Moolenaar # Date 1565557207 -7200 # Node ID 3dbff5d375201cffef7a22341d0f8f53f18a2d65 # Parent fbdef42903ae741a70375e6e50e691887ee2afb3 patch 8.1.1838: there is :spellwrong and :spellgood but not :spellrare commit https://github.com/vim/vim/commit/08cc374dabd2a02785129fa1c0100f7745c244ad Author: Bram Moolenaar Date: Sun Aug 11 22:51:14 2019 +0200 patch 8.1.1838: there is :spellwrong and :spellgood but not :spellrare Problem: There is :spellwrong and :spellgood but not :spellrare. Solution: Add :spellrare. (Martin Tournoij, closes https://github.com/vim/vim/issues/4291) diff --git a/runtime/doc/spell.txt b/runtime/doc/spell.txt --- a/runtime/doc/spell.txt +++ b/runtime/doc/spell.txt @@ -121,6 +121,23 @@ zuG Undo |zW| and |zG|, remove the wor :spellw[rong]! {word} Add {word} as a wrong (bad) word to the internal word list, like with |zW|. + *:spellr* *:spellrare* +:[count]spellr[are] {word} + Add {word} as a rare word to 'spellfile', similar to + |zw|. Without count the first name is used, with + a count of two the second entry, etc. + + There are no normal mode commands to mark words as + rare as this is a fairly uncommon command and all + intuitive commands for this are already taken. If you + want you can add mappings with e.g.: > + nnoremap z? :exe ':spellrare ' . expand('') + nnoremap z/ :exe ':spellrare! ' . expand('') +< |:spellundo|, |zuw|, or |zuW| can be used to undo this. + +:spellr[rare]! {word} Add {word} as a rare word to the internal word + list, similar to |zW|. + :[count]spellu[ndo] {word} *:spellu* *:spellundo* Like |zuw|. [count] used as with |:spellgood|. diff --git a/src/ex_cmdidxs.h b/src/ex_cmdidxs.h --- a/src/ex_cmdidxs.h +++ b/src/ex_cmdidxs.h @@ -24,13 +24,13 @@ static const unsigned short cmdidxs1[26] /* q */ 358, /* r */ 361, /* s */ 381, - /* t */ 449, - /* u */ 494, - /* v */ 505, - /* w */ 523, - /* x */ 537, - /* y */ 547, - /* z */ 548 + /* t */ 450, + /* u */ 495, + /* v */ 506, + /* w */ 524, + /* x */ 538, + /* y */ 548, + /* z */ 549 }; /* @@ -59,7 +59,7 @@ static const unsigned char cmdidxs2[26][ /* p */ { 1, 0, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 0, 0, 16, 17, 26, 0, 27, 0, 28, 0 }, /* q */ { 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* r */ { 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 19, 0, 0, 0, 0 }, - /* s */ { 2, 6, 15, 0, 19, 23, 0, 25, 26, 0, 0, 29, 31, 35, 39, 41, 0, 49, 0, 50, 0, 62, 63, 0, 64, 0 }, + /* s */ { 2, 6, 15, 0, 19, 23, 0, 25, 26, 0, 0, 29, 31, 35, 39, 41, 0, 50, 0, 51, 0, 63, 64, 0, 65, 0 }, /* t */ { 2, 0, 19, 0, 24, 26, 0, 27, 0, 28, 0, 29, 33, 36, 38, 39, 0, 40, 42, 0, 43, 0, 0, 0, 0, 0 }, /* u */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* v */ { 0, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 9, 12, 0, 0, 0, 0, 15, 0, 16, 0, 0, 0, 0, 0 }, @@ -69,4 +69,4 @@ static const unsigned char cmdidxs2[26][ /* z */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; -static const int command_count = 561; +static const int command_count = 562; diff --git a/src/ex_cmds.h b/src/ex_cmds.h --- a/src/ex_cmds.h +++ b/src/ex_cmds.h @@ -1378,6 +1378,9 @@ EXCMD(CMD_split, "split", ex_splitview, EXCMD(CMD_spellgood, "spellgood", ex_spell, EX_BANG|EX_RANGE|EX_NEEDARG|EX_EXTRA|EX_TRLBAR, ADDR_OTHER), +EXCMD(CMD_spellrare, "spellrare", ex_spell, + EX_BANG|EX_RANGE|EX_NEEDARG|EX_EXTRA|EX_TRLBAR, + ADDR_OTHER), EXCMD(CMD_spelldump, "spelldump", ex_spelldump, EX_BANG|EX_TRLBAR, ADDR_NONE), diff --git a/src/normal.c b/src/normal.c --- a/src/normal.c +++ b/src/normal.c @@ -5127,7 +5127,8 @@ dozet: if (ptr == NULL && (len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0) return; - spell_add_word(ptr, len, nchar == 'w' || nchar == 'W', + spell_add_word(ptr, len, nchar == 'w' || nchar == 'W' + ? SPELL_ADD_BAD : SPELL_ADD_GOOD, (nchar == 'G' || nchar == 'W') ? 0 : (int)cap->count1, undo); diff --git a/src/proto/spellfile.pro b/src/proto/spellfile.pro --- a/src/proto/spellfile.pro +++ b/src/proto/spellfile.pro @@ -5,5 +5,5 @@ int spell_check_msm(void); void ex_mkspell(exarg_T *eap); void mkspell(int fcount, char_u **fnames, int ascii, int over_write, int added_word); void ex_spell(exarg_T *eap); -void spell_add_word(char_u *word, int len, int bad, int idx, int undo); +void spell_add_word(char_u *word, int len, int what, int idx, int undo); /* vim: set ft=c : */ diff --git a/src/spell.h b/src/spell.h --- a/src/spell.h +++ b/src/spell.h @@ -298,4 +298,8 @@ SPELL_EXTERN char e_format[] SPELL_INIT( SPELL_EXTERN spelltab_T spelltab; SPELL_EXTERN int did_set_spelltab; +// Values for "what" argument of spell_add_word() +#define SPELL_ADD_GOOD 0 +#define SPELL_ADD_BAD 1 +#define SPELL_ADD_RARE 2 #endif diff --git a/src/spellfile.c b/src/spellfile.c --- a/src/spellfile.c +++ b/src/spellfile.c @@ -6125,28 +6125,31 @@ spell_message(spellinfo_T *spin, char_u /* * ":[count]spellgood {word}" - * ":[count]spellwrong {word}" + * ":[count]spellwrong {word}" * ":[count]spellundo {word}" + * ":[count]spellrare {word}" */ void ex_spell(exarg_T *eap) { - spell_add_word(eap->arg, (int)STRLEN(eap->arg), eap->cmdidx == CMD_spellwrong, + spell_add_word(eap->arg, (int)STRLEN(eap->arg), + eap->cmdidx == CMD_spellwrong ? SPELL_ADD_BAD : + eap->cmdidx == CMD_spellrare ? SPELL_ADD_RARE : SPELL_ADD_GOOD, eap->forceit ? 0 : (int)eap->line2, eap->cmdidx == CMD_spellundo); } /* - * Add "word[len]" to 'spellfile' as a good or bad word. + * Add "word[len]" to 'spellfile' as a good, rare or bad word. */ void spell_add_word( char_u *word, int len, - int bad, - int idx, /* "zG" and "zW": zero, otherwise index in - 'spellfile' */ - int undo) /* TRUE for "zug", "zuG", "zuw" and "zuW" */ + int what, // SPELL_ADD_ values + int idx, // "zG" and "zW": zero, otherwise index in + // 'spellfile' + int undo) // TRUE for "zug", "zuG", "zuw" and "zuW" { FILE *fd = NULL; buf_T *buf = NULL; @@ -6213,7 +6216,7 @@ spell_add_word( fname = fnamebuf; } - if (bad || undo) + if (what == SPELL_ADD_BAD || undo) { /* When the word appears as good word we need to remove that one, * since its flags sort before the one with WF_BANNED. */ @@ -6280,8 +6283,10 @@ spell_add_word( semsg(_(e_notopen), fname); else { - if (bad) + if (what == SPELL_ADD_BAD) fprintf(fd, "%.*s/!\n", len, word); + else if (what == SPELL_ADD_RARE) + fprintf(fd, "%.*s/?\n", len, word); else fprintf(fd, "%.*s\n", len, word); fclose(fd); diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -236,6 +236,7 @@ NEW_TESTS = \ test_source \ test_source_utf8 \ test_spell \ + test_spellfile \ test_startup \ test_startup_utf8 \ test_stat \ @@ -411,6 +412,7 @@ NEW_TESTS_RES = \ test_sound.res \ test_source.res \ test_spell.res \ + test_spellfile.res \ test_startup.res \ test_stat.res \ test_substitute.res \ diff --git a/src/testdir/test_normal.vim b/src/testdir/test_normal.vim --- a/src/testdir/test_normal.vim +++ b/src/testdir/test_normal.vim @@ -1089,160 +1089,6 @@ func Test_normal18_z_fold() bw! endfunc -func Test_normal19_z_spell() - if !has("spell") || !has('syntax') - return - endif - new - call append(0, ['1 good', '2 goood', '3 goood']) - set spell spellfile=./Xspellfile.add spelllang=en - let oldlang=v:lang - lang C - - " Test for zg - 1 - norm! ]s - call assert_equal('2 goood', getline('.')) - norm! zg - 1 - let a=execute('unsilent :norm! ]s') - call assert_equal('1 good', getline('.')) - call assert_equal('search hit BOTTOM, continuing at TOP', a[1:]) - let cnt=readfile('./Xspellfile.add') - call assert_equal('goood', cnt[0]) - - " Test for zw - 2 - norm! $zw - 1 - norm! ]s - call assert_equal('2 goood', getline('.')) - let cnt=readfile('./Xspellfile.add') - call assert_equal('#oood', cnt[0]) - call assert_equal('goood/!', cnt[1]) - - " Test for zg in visual mode - let a=execute('unsilent :norm! V$zg') - call assert_equal("Word '2 goood' added to ./Xspellfile.add", a[1:]) - 1 - norm! ]s - call assert_equal('3 goood', getline('.')) - let cnt=readfile('./Xspellfile.add') - call assert_equal('2 goood', cnt[2]) - " Remove "2 good" from spellfile - 2 - let a=execute('unsilent norm! V$zw') - call assert_equal("Word '2 goood' added to ./Xspellfile.add", a[1:]) - let cnt=readfile('./Xspellfile.add') - call assert_equal('2 goood/!', cnt[3]) - - " Test for zG - let a=execute('unsilent norm! V$zG') - call assert_match("Word '2 goood' added to .*", a) - let fname=matchstr(a, 'to\s\+\zs\f\+$') - let cnt=readfile(fname) - call assert_equal('2 goood', cnt[0]) - - " Test for zW - let a=execute('unsilent norm! V$zW') - call assert_match("Word '2 goood' added to .*", a) - let cnt=readfile(fname) - call assert_equal('# goood', cnt[0]) - call assert_equal('2 goood/!', cnt[1]) - - " Test for zuW - let a=execute('unsilent norm! V$zuW') - call assert_match("Word '2 goood' removed from .*", a) - let cnt=readfile(fname) - call assert_equal('# goood', cnt[0]) - call assert_equal('# goood/!', cnt[1]) - - " Test for zuG - let a=execute('unsilent norm! $zG') - call assert_match("Word 'goood' added to .*", a) - let cnt=readfile(fname) - call assert_equal('# goood', cnt[0]) - call assert_equal('# goood/!', cnt[1]) - call assert_equal('goood', cnt[2]) - let a=execute('unsilent norm! $zuG') - let cnt=readfile(fname) - call assert_match("Word 'goood' removed from .*", a) - call assert_equal('# goood', cnt[0]) - call assert_equal('# goood/!', cnt[1]) - call assert_equal('#oood', cnt[2]) - " word not found in wordlist - let a=execute('unsilent norm! V$zuG') - let cnt=readfile(fname) - call assert_match("", a) - call assert_equal('# goood', cnt[0]) - call assert_equal('# goood/!', cnt[1]) - call assert_equal('#oood', cnt[2]) - - " Test for zug - call delete('./Xspellfile.add') - 2 - let a=execute('unsilent norm! $zg') - let cnt=readfile('./Xspellfile.add') - call assert_equal('goood', cnt[0]) - let a=execute('unsilent norm! $zug') - call assert_match("Word 'goood' removed from \./Xspellfile.add", a) - let cnt=readfile('./Xspellfile.add') - call assert_equal('#oood', cnt[0]) - " word not in wordlist - let a=execute('unsilent norm! V$zug') - call assert_match('', a) - let cnt=readfile('./Xspellfile.add') - call assert_equal('#oood', cnt[0]) - - " Test for zuw - call delete('./Xspellfile.add') - 2 - let a=execute('unsilent norm! Vzw') - let cnt=readfile('./Xspellfile.add') - call assert_equal('2 goood/!', cnt[0]) - let a=execute('unsilent norm! Vzuw') - call assert_match("Word '2 goood' removed from \./Xspellfile.add", a) - let cnt=readfile('./Xspellfile.add') - call assert_equal('# goood/!', cnt[0]) - " word not in wordlist - let a=execute('unsilent norm! $zug') - call assert_match('', a) - let cnt=readfile('./Xspellfile.add') - call assert_equal('# goood/!', cnt[0]) - - " add second entry to spellfile setting - set spellfile=./Xspellfile.add,./Xspellfile2.add - call delete('./Xspellfile.add') - 2 - let a=execute('unsilent norm! $2zg') - let cnt=readfile('./Xspellfile2.add') - call assert_match("Word 'goood' added to ./Xspellfile2.add", a) - call assert_equal('goood', cnt[0]) - - " Test for :spellgood! - let temp = execute(':spe!0/0') - call assert_match('Invalid region', temp) - let spellfile = matchstr(temp, 'Invalid region nr in \zs.*\ze line \d: 0') - call assert_equal(['# goood', '# goood/!', '#oood', '0/0'], readfile(spellfile)) - call delete(spellfile) - - " clean up - exe "lang" oldlang - call delete("./Xspellfile.add") - call delete("./Xspellfile2.add") - call delete("./Xspellfile.add.spl") - call delete("./Xspellfile2.add.spl") - - " zux -> no-op - 2 - norm! $zux - call assert_equal([], glob('Xspellfile.add',0,1)) - call assert_equal([], glob('Xspellfile2.add',0,1)) - - set spellfile= - bw! -endfunc - func Test_normal20_exmode() if !has("unix") " Reading from redirected file doesn't work on MS-Windows diff --git a/src/testdir/test_spellfile.vim b/src/testdir/test_spellfile.vim new file mode 100644 --- /dev/null +++ b/src/testdir/test_spellfile.vim @@ -0,0 +1,172 @@ +" Test for commands that operate on the spellfile. + +source shared.vim +source check.vim + +CheckFeature spell +CheckFeature syntax + +func Test_spell_normal() + new + call append(0, ['1 good', '2 goood', '3 goood']) + set spell spellfile=./Xspellfile.add spelllang=en + let oldlang=v:lang + lang C + + " Test for zg + 1 + norm! ]s + call assert_equal('2 goood', getline('.')) + norm! zg + 1 + let a=execute('unsilent :norm! ]s') + call assert_equal('1 good', getline('.')) + call assert_equal('search hit BOTTOM, continuing at TOP', a[1:]) + let cnt=readfile('./Xspellfile.add') + call assert_equal('goood', cnt[0]) + + " Test for zw + 2 + norm! $zw + 1 + norm! ]s + call assert_equal('2 goood', getline('.')) + let cnt=readfile('./Xspellfile.add') + call assert_equal('#oood', cnt[0]) + call assert_equal('goood/!', cnt[1]) + + " Test for :spellrare + spellrare rare + let cnt=readfile('./Xspellfile.add') + call assert_equal(['#oood', 'goood/!', 'rare/?'], cnt) + + " Make sure :spellundo works for rare words. + spellundo rare + let cnt=readfile('./Xspellfile.add') + call assert_equal(['#oood', 'goood/!', '#are/?'], cnt) + + " Test for zg in visual mode + let a=execute('unsilent :norm! V$zg') + call assert_equal("Word '2 goood' added to ./Xspellfile.add", a[1:]) + 1 + norm! ]s + call assert_equal('3 goood', getline('.')) + let cnt=readfile('./Xspellfile.add') + call assert_equal('2 goood', cnt[3]) + " Remove "2 good" from spellfile + 2 + let a=execute('unsilent norm! V$zw') + call assert_equal("Word '2 goood' added to ./Xspellfile.add", a[1:]) + let cnt=readfile('./Xspellfile.add') + call assert_equal('2 goood/!', cnt[4]) + + " Test for zG + let a=execute('unsilent norm! V$zG') + call assert_match("Word '2 goood' added to .*", a) + let fname=matchstr(a, 'to\s\+\zs\f\+$') + let cnt=readfile(fname) + call assert_equal('2 goood', cnt[0]) + + " Test for zW + let a=execute('unsilent norm! V$zW') + call assert_match("Word '2 goood' added to .*", a) + let cnt=readfile(fname) + call assert_equal('# goood', cnt[0]) + call assert_equal('2 goood/!', cnt[1]) + + " Test for zuW + let a=execute('unsilent norm! V$zuW') + call assert_match("Word '2 goood' removed from .*", a) + let cnt=readfile(fname) + call assert_equal('# goood', cnt[0]) + call assert_equal('# goood/!', cnt[1]) + + " Test for zuG + let a=execute('unsilent norm! $zG') + call assert_match("Word 'goood' added to .*", a) + let cnt=readfile(fname) + call assert_equal('# goood', cnt[0]) + call assert_equal('# goood/!', cnt[1]) + call assert_equal('goood', cnt[2]) + let a=execute('unsilent norm! $zuG') + let cnt=readfile(fname) + call assert_match("Word 'goood' removed from .*", a) + call assert_equal('# goood', cnt[0]) + call assert_equal('# goood/!', cnt[1]) + call assert_equal('#oood', cnt[2]) + " word not found in wordlist + let a=execute('unsilent norm! V$zuG') + let cnt=readfile(fname) + call assert_match("", a) + call assert_equal('# goood', cnt[0]) + call assert_equal('# goood/!', cnt[1]) + call assert_equal('#oood', cnt[2]) + + " Test for zug + call delete('./Xspellfile.add') + 2 + let a=execute('unsilent norm! $zg') + let cnt=readfile('./Xspellfile.add') + call assert_equal('goood', cnt[0]) + let a=execute('unsilent norm! $zug') + call assert_match("Word 'goood' removed from \./Xspellfile.add", a) + let cnt=readfile('./Xspellfile.add') + call assert_equal('#oood', cnt[0]) + " word not in wordlist + let a=execute('unsilent norm! V$zug') + call assert_match('', a) + let cnt=readfile('./Xspellfile.add') + call assert_equal('#oood', cnt[0]) + + " Test for zuw + call delete('./Xspellfile.add') + 2 + let a=execute('unsilent norm! Vzw') + let cnt=readfile('./Xspellfile.add') + call assert_equal('2 goood/!', cnt[0]) + let a=execute('unsilent norm! Vzuw') + call assert_match("Word '2 goood' removed from \./Xspellfile.add", a) + let cnt=readfile('./Xspellfile.add') + call assert_equal('# goood/!', cnt[0]) + " word not in wordlist + let a=execute('unsilent norm! $zug') + call assert_match('', a) + let cnt=readfile('./Xspellfile.add') + call assert_equal('# goood/!', cnt[0]) + + " add second entry to spellfile setting + set spellfile=./Xspellfile.add,./Xspellfile2.add + call delete('./Xspellfile.add') + 2 + let a=execute('unsilent norm! $2zg') + let cnt=readfile('./Xspellfile2.add') + call assert_match("Word 'goood' added to ./Xspellfile2.add", a) + call assert_equal('goood', cnt[0]) + + " Test for :spellgood! + let temp = execute(':spe!0/0') + call assert_match('Invalid region', temp) + let spellfile = matchstr(temp, 'Invalid region nr in \zs.*\ze line \d: 0') + call assert_equal(['# goood', '# goood/!', '#oood', '0/0'], readfile(spellfile)) + + " Test for :spellrare! + :spellrare! raare + call assert_equal(['# goood', '# goood/!', '#oood', '0/0', 'raare/?'], readfile(spellfile)) + call delete(spellfile) + + " clean up + exe "lang" oldlang + call delete("./Xspellfile.add") + call delete("./Xspellfile2.add") + call delete("./Xspellfile.add.spl") + call delete("./Xspellfile2.add.spl") + + " zux -> no-op + 2 + norm! $zux + call assert_equal([], glob('Xspellfile.add',0,1)) + call assert_equal([], glob('Xspellfile2.add',0,1)) + + set spellfile= + bw! +endfunc diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -770,6 +770,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1838, +/**/ 1837, /**/ 1836,