vim-patch:9.2.0081: Failed "z=" does not reset 'nospell' setting (#38109)

Problem:  When z= fails due to no word being found, 'spelllang' being
          unset or a multiline visual selection, 'nospell' is not
          restored.
Solution: Jump to where the user configured value of 'spell' is restored
          instead of returning early (Luuk van Baal).

closes: vim/vim#19525

eba078fc47
This commit is contained in:
luukvbaal
2026-03-01 09:21:48 +01:00
committed by GitHub
parent 62135f5a57
commit 563f9ef799
3 changed files with 30 additions and 15 deletions

View File

@@ -439,11 +439,7 @@ int spell_check_sps(void)
void spell_suggest(int count) void spell_suggest(int count)
{ {
pos_T prev_cursor = curwin->w_cursor; pos_T prev_cursor = curwin->w_cursor;
char wcopy[MAXWLEN + 2];
suginfo_T sug;
suggest_T *stp;
bool mouse_used = false; bool mouse_used = false;
int selected = count;
int badlen = 0; int badlen = 0;
int msg_scroll_save = msg_scroll; int msg_scroll_save = msg_scroll;
const int wo_spell_save = curwin->w_p_spell; const int wo_spell_save = curwin->w_p_spell;
@@ -455,7 +451,7 @@ void spell_suggest(int count)
if (*curwin->w_s->b_p_spl == NUL) { if (*curwin->w_s->b_p_spl == NUL) {
emsg(_(e_no_spell)); emsg(_(e_no_spell));
return; goto skip;
} }
if (VIsual_active) { if (VIsual_active) {
@@ -463,7 +459,7 @@ void spell_suggest(int count)
// a multi-line selection. // a multi-line selection.
if (curwin->w_cursor.lnum != VIsual.lnum) { if (curwin->w_cursor.lnum != VIsual.lnum) {
vim_beep(kOptBoFlagSpell); vim_beep(kOptBoFlagSpell);
return; goto skip;
} }
badlen = (int)curwin->w_cursor.col - (int)VIsual.col; badlen = (int)curwin->w_cursor.col - (int)VIsual.col;
if (badlen < 0) { if (badlen < 0) {
@@ -481,11 +477,11 @@ void spell_suggest(int count)
// No bad word or it starts after the cursor: use the word under the // No bad word or it starts after the cursor: use the word under the
// cursor. // cursor.
curwin->w_cursor = prev_cursor; curwin->w_cursor = prev_cursor;
char *line = get_cursor_line_ptr(); char *curline = get_cursor_line_ptr();
char *p = line + curwin->w_cursor.col; char *p = curline + curwin->w_cursor.col;
// Backup to before start of word. // Backup to before start of word.
while (p > line && spell_iswordp_nmw(p, curwin)) { while (p > curline && spell_iswordp_nmw(p, curwin)) {
MB_PTR_BACK(line, p); MB_PTR_BACK(curline, p);
} }
// Forward to start of word. // Forward to start of word.
while (*p != NUL && !spell_iswordp_nmw(p, curwin)) { while (*p != NUL && !spell_iswordp_nmw(p, curwin)) {
@@ -494,9 +490,9 @@ void spell_suggest(int count)
if (!spell_iswordp_nmw(p, curwin)) { // No word found. if (!spell_iswordp_nmw(p, curwin)) { // No word found.
beep_flush(); beep_flush();
return; goto skip;
} }
curwin->w_cursor.col = (colnr_T)(p - line); curwin->w_cursor.col = (colnr_T)(p - curline);
} }
// Get the word and its length. // Get the word and its length.
@@ -510,10 +506,12 @@ void spell_suggest(int count)
// Get the list of suggestions. Limit to 'lines' - 2 or the number in // Get the list of suggestions. Limit to 'lines' - 2 or the number in
// 'spellsuggest', whatever is smaller. // 'spellsuggest', whatever is smaller.
suginfo_T sug;
int limit = MIN(sps_limit, Rows - 2); int limit = MIN(sps_limit, Rows - 2);
spell_find_suggest(line + curwin->w_cursor.col, badlen, &sug, limit, spell_find_suggest(line + curwin->w_cursor.col, badlen, &sug, limit,
true, need_cap, true); true, need_cap, true);
int selected = count;
msg_ext_set_kind("confirm"); msg_ext_set_kind("confirm");
if (GA_EMPTY(&sug.su_ga)) { if (GA_EMPTY(&sug.su_ga)) {
msg(_("Sorry, no suggestions"), 0); msg(_("Sorry, no suggestions"), 0);
@@ -543,10 +541,11 @@ void spell_suggest(int count)
msg_scroll = true; msg_scroll = true;
for (int i = 0; i < sug.su_ga.ga_len; i++) { for (int i = 0; i < sug.su_ga.ga_len; i++) {
stp = &SUG(sug.su_ga, i); suggest_T *stp = &SUG(sug.su_ga, i);
// The suggested word may replace only part of the bad word, add // The suggested word may replace only part of the bad word, add
// the not replaced part. But only when it's not getting too long. // the not replaced part. But only when it's not getting too long.
char wcopy[MAXWLEN + 2];
xstrlcpy(wcopy, stp->st_word, MAXWLEN + 1); xstrlcpy(wcopy, stp->st_word, MAXWLEN + 1);
int el = sug.su_badlen - stp->st_orglen; int el = sug.su_badlen - stp->st_orglen;
if (el > 0 && stp->st_wordlen + el <= MAXWLEN) { if (el > 0 && stp->st_wordlen + el <= MAXWLEN) {
@@ -609,7 +608,7 @@ void spell_suggest(int count)
XFREE_CLEAR(repl_from); XFREE_CLEAR(repl_from);
XFREE_CLEAR(repl_to); XFREE_CLEAR(repl_to);
stp = &SUG(sug.su_ga, selected - 1); suggest_T *stp = &SUG(sug.su_ga, selected - 1);
if (sug.su_badlen > stp->st_orglen) { if (sug.su_badlen > stp->st_orglen) {
// Replacing less than "su_badlen", append the remainder to // Replacing less than "su_badlen", append the remainder to
// repl_to. // repl_to.
@@ -649,6 +648,7 @@ void spell_suggest(int count)
spell_find_cleanup(&sug); spell_find_cleanup(&sug);
xfree(line); xfree(line);
skip:
curwin->w_p_spell = wo_spell_save; curwin->w_p_spell = wo_spell_save;
} }

View File

@@ -1579,4 +1579,18 @@ let g:test_data_aff_sal = [
\"SAL Z S", \"SAL Z S",
\ ] \ ]
func Test_suggest_spell_restore()
norm! z=
call assert_equal(0, &spell)
set spelllang=
sil! norm! z=
call assert_equal(0, &spell)
set spelllang=en
call setline(1, ['1','2'])
norm! vjz=
call assert_equal(0, &spell)
set spelllang&
bwipe!
endfunc
" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab

View File

@@ -809,13 +809,14 @@ endfunc
func Test_check_empty_line() func Test_check_empty_line()
" This was using freed memory " This was using freed memory
set spell
enew enew
spellgood! spellgood!
norm z= norm z=
norm yy norm yy
sil! norm P]svc sil! norm P]svc
norm P]s norm P]s
set spell&
bwipe! bwipe!
endfunc endfunc