vim-patch:9.1.0418: Cannot move to previous/next rare word (#28822)

Problem:  Cannot move to previous/next rare word
          (Colin Kennedy)
Solution: Add the ]r and [r motions (Christ van Willegen)

fixes: vim/vim#14773
closes: vim/vim#14780

8e4c4c7d87

Co-authored-by: Christ van Willegen - van Noort <github.com@vanwillegen-vannoort.nl>
This commit is contained in:
zeertzjq 2024-05-18 07:09:05 +08:00 committed by GitHub
parent 5947f249f8
commit 62eb7e79a5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 95 additions and 11 deletions

View File

@ -51,6 +51,17 @@ To search for the next misspelled word:
*[S*
[S Like "]S" but search backwards.
*]r*
]r Move to next "rare" word after the cursor.
A count before the command can be used to repeat.
'wrapscan' applies.
*[r*
[r Like "]r" but search backwards, find the "rare"
word before the cursor. Doesn't recognize words
split over two lines, thus may stop at words that are
not highlighted as rare.
To add words to your own word list:

View File

@ -1415,7 +1415,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
pos_T pos = wp->w_cursor;
wp->w_cursor.lnum = lnum;
wp->w_cursor.col = linecol;
size_t len = spell_move_to(wp, FORWARD, true, true, &spell_hlf);
size_t len = spell_move_to(wp, FORWARD, SMT_ALL, true, &spell_hlf);
// spell_move_to() may call ml_get() and make "line" invalid
line = ml_get_buf(wp->w_buffer, lnum);

View File

@ -8308,7 +8308,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, EvalFuncData fptr
size_t len = 0;
if (argvars[0].v_type == VAR_UNKNOWN) {
// Find the start and length of the badly spelled word.
len = spell_move_to(curwin, FORWARD, true, true, &attr);
len = spell_move_to(curwin, FORWARD, SMT_ALL, true, &attr);
if (len != 0) {
word = get_cursor_pos_ptr();
curwin->w_set_curswant = true;

View File

@ -4570,7 +4570,7 @@ void free_insexpand_stuff(void)
static void spell_back_to_badword(void)
{
pos_T tpos = curwin->w_cursor;
spell_bad_len = spell_move_to(curwin, BACKWARD, true, true, NULL);
spell_bad_len = spell_move_to(curwin, BACKWARD, SMT_ALL, true, NULL);
if (curwin->w_cursor.col != tpos.col) {
start_arrow(&tpos);
}

View File

@ -2710,7 +2710,7 @@ static int nv_zg_zw(cmdarg_T *cap, int nchar)
// off this fails and find_ident_under_cursor() is
// used below.
emsg_off++;
len = spell_move_to(curwin, FORWARD, true, true, NULL);
len = spell_move_to(curwin, FORWARD, SMT_ALL, true, NULL);
emsg_off--;
if (len != 0 && curwin->w_cursor.col <= pos.col) {
ptr = ml_get_pos(&curwin->w_cursor);
@ -4272,12 +4272,15 @@ static void nv_brackets(cmdarg_T *cap)
cap->count1) == false) {
clearopbeep(cap->oap);
}
} else if (cap->nchar == 's' || cap->nchar == 'S') {
// "[s", "[S", "]s" and "]S": move to next spell error.
} else if (cap->nchar == 'r' || cap->nchar == 's' || cap->nchar == 'S') {
// "[r", "[s", "[S", "]r", "]s" and "]S": move to next spell error.
setpcmark();
for (n = 0; n < cap->count1; n++) {
if (spell_move_to(curwin, cap->cmdchar == ']' ? FORWARD : BACKWARD,
cap->nchar == 's', false, NULL) == 0) {
cap->nchar == 's'
? SMT_ALL
: cap->nchar == 'r' ? SMT_RARE : SMT_BAD,
false, NULL) == 0) {
clearopbeep(cap->oap);
break;
}

View File

@ -1290,11 +1290,11 @@ static inline bool can_syn_spell(win_T *wp, linenr_T lnum, int col)
/// to after badly spelled word before the cursor.
///
/// @param dir FORWARD or BACKWARD
/// @param allwords true for "[s"/"]s", false for "[S"/"]S"
/// @param behaviour Behaviour of the function
/// @param attrp return: attributes of bad word or NULL (only when "dir" is FORWARD)
///
/// @return 0 if not found, length of the badly spelled word otherwise.
size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *attrp)
size_t spell_move_to(win_T *wp, int dir, smt_T behaviour, bool curline, hlf_T *attrp)
{
pos_T found_pos;
size_t found_len = 0;
@ -1398,7 +1398,9 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
if (attr != HLF_COUNT) {
// We found a bad word. Check the attribute.
if (allwords || attr == HLF_SPB) {
if (behaviour == SMT_ALL
|| (behaviour == SMT_BAD && attr == HLF_SPB)
|| (behaviour == SMT_RARE && attr == HLF_SPR)) {
// When searching forward only accept a bad word after
// the cursor.
if (dir == BACKWARD

View File

@ -21,6 +21,13 @@ extern char *e_format;
extern char *repl_from;
extern char *repl_to;
/// Values for behaviour in spell_move_to
typedef enum {
SMT_ALL = 0, ///< Move to "all" words
SMT_BAD, ///< Move to "bad" words only
SMT_RARE, ///< Move to "rare" words only
} smt_T;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "spell.h.generated.h"
#endif

View File

@ -484,7 +484,7 @@ void spell_suggest(int count)
badlen = get_cursor_line_len() - curwin->w_cursor.col;
}
// Find the start of the badly spelled word.
} else if (spell_move_to(curwin, FORWARD, true, true, NULL) == 0
} else if (spell_move_to(curwin, FORWARD, SMT_ALL, true, NULL) == 0
|| curwin->w_cursor.col > prev_cursor.col) {
// No bad word or it starts after the cursor: use the word under the
// cursor.

View File

@ -0,0 +1,61 @@
" Test spell checking
source check.vim
CheckFeature spell
" Test spellbadword() with argument, specifically to move to "rare" words
" in normal mode.
func Test_spellrareword()
set spell
" Create a small word list to test that spellbadword('...')
" can return ['...', 'rare'].
let lines =<< trim END
foo
foobar/?
foobara/?
END
call writefile(lines, 'Xwords', 'D')
mkspell! Xwords.spl Xwords
set spelllang=Xwords.spl
call assert_equal(['foobar', 'rare'], spellbadword('foo foobar'))
new
call setline(1, ['foo', '', 'foo bar foo bar foobara foo foo foo foobar', '', 'End'])
set spell wrapscan
normal ]s
call assert_equal('foo', expand('<cword>'))
normal ]s
call assert_equal('bar', expand('<cword>'))
normal ]r
call assert_equal('foobara', expand('<cword>'))
normal ]r
call assert_equal('foobar', expand('<cword>'))
normal ]r
call assert_equal('foobara', expand('<cword>'))
normal 2]r
call assert_equal('foobara', expand('<cword>'))
normal [r
call assert_equal('foobar', expand('<cword>'))
normal [r
call assert_equal('foobara', expand('<cword>'))
normal [r
call assert_equal('foobar', expand('<cword>'))
normal 2[r
call assert_equal('foobar', expand('<cword>'))
bwipe!
set nospell
call delete('Xwords.spl')
set spelllang&
set spell&
" set 'encoding' to clear the word list
set encoding=utf-8
endfunc
" vim: shiftwidth=2 sts=2 expandtab