Patch 9.0.1578
Problem:    SpellCap highlight not always updated when needed.
Solution:   Handle updating line below closed fold and other situations where
            only part of the window is redrawn. (Luuk van Baal, closes #12428,
            closes #12420)
Files:      src/drawline.c, src/drawscreen.c, src/spell.c,
            src/proto/spell.pro, src/spellsuggest.c,
            src/testdir/test_spell.vim, src/testdir/dumps/Test_spell_6.dump,
            src/testdir/dumps/Test_spell_7.dump,
            src/testdir/dumps/Test_spell_8.dump


*** ../vim-9.0.1577/src/drawline.c      2023-05-02 20:52:32.047787527 +0100
--- src/drawline.c      2023-05-25 17:08:43.287047551 +0100
***************
*** 1062,1068 ****
      linenr_T  lnum,
      int               startrow,
      int               endrow,
!     int               nochange UNUSED,        // not updating for changed text
      int               number_only)            // only update the number column
  {
      winlinevars_T     wlv;            // variables passed between functions
--- 1062,1068 ----
      linenr_T  lnum,
      int               startrow,
      int               endrow,
!     int               mod_top UNUSED,         // top line updated for changed 
text
      int               number_only)            // only update the number column
  {
      winlinevars_T     wlv;            // variables passed between functions
***************
*** 1314,1324 ****
  
            // When there was a sentence end in the previous line may require a
            // word starting with capital in this line.  In line 1 always check
!           // the first word.
!           if (lnum != capcol_lnum)
!               cap_col = -1;
!           if (lnum == 1)
                cap_col = 0;
            capcol_lnum = 0;
        }
  #endif
--- 1314,1331 ----
  
            // When there was a sentence end in the previous line may require a
            // word starting with capital in this line.  In line 1 always check
!           // the first word.  Also check for sentence end in the line above
!           // when updating the first row in a window, the top line with
!           // changed text in a window, or if the previous line is folded.
!           if (lnum == 1
!                   || ((startrow == 0 || mod_top == lnum
! #ifdef FEAT_FOLDING
!                       || hasFoldingWin(wp, lnum - 1, NULL, NULL, TRUE, NULL)
! #endif
!                       ) && check_need_cap(wp, lnum, 0)))
                cap_col = 0;
+           else if (lnum != capcol_lnum)
+               cap_col = -1;
            capcol_lnum = 0;
        }
  #endif
***************
*** 2787,2793 ****
                            p = prev_ptr;
                        cap_col -= (int)(prev_ptr - line);
                        len = spell_check(wp, p, &spell_hlf, &cap_col,
!                                                                   nochange);
                        word_end = v + len;
  
                        // In Insert mode only highlight a word that
--- 2794,2800 ----
                            p = prev_ptr;
                        cap_col -= (int)(prev_ptr - line);
                        len = spell_check(wp, p, &spell_hlf, &cap_col,
!                                                               mod_top == 0);
                        word_end = v + len;
  
                        // In Insert mode only highlight a word that
*** ../vim-9.0.1577/src/drawscreen.c    2023-05-06 12:39:58.736971487 +0100
--- src/drawscreen.c    2023-05-25 17:08:43.287047551 +0100
***************
*** 2487,2494 ****
  #endif
  
                // Display one line.
!               row = win_line(wp, lnum, srow, wp->w_height,
!                                                         mod_top == 0, FALSE);
  
  #ifdef FEAT_FOLDING
                wp->w_lines[idx].wl_folded = FALSE;
--- 2487,2493 ----
  #endif
  
                // Display one line.
!               row = win_line(wp, lnum, srow, wp->w_height, mod_top, FALSE);
  
  #ifdef FEAT_FOLDING
                wp->w_lines[idx].wl_folded = FALSE;
***************
*** 2535,2541 ****
                    fold_line(wp, fold_count, &win_foldinfo, lnum, row);
                else
  #endif
!                   (void)win_line(wp, lnum, srow, wp->w_height, TRUE, TRUE);
            }
  
            // This line does not need to be drawn, advance to the next one.
--- 2534,2540 ----
                    fold_line(wp, fold_count, &win_foldinfo, lnum, row);
                else
  #endif
!                   (void)win_line(wp, lnum, srow, wp->w_height, mod_top, TRUE);
            }
  
            // This line does not need to be drawn, advance to the next one.
*** ../vim-9.0.1577/src/spell.c 2023-02-20 12:16:33.336269408 +0000
--- src/spell.c 2023-05-25 17:08:43.291047554 +0100
***************
*** 1342,1348 ****
        {
            // For spellbadword(): check if first word needs a capital.
            col = getwhitecols(line);
!           if (check_need_cap(lnum, col))
                capcol = col;
  
            // Need to get the line again, may have looked at the previous
--- 1342,1348 ----
        {
            // For spellbadword(): check if first word needs a capital.
            col = getwhitecols(line);
!           if (check_need_cap(curwin, lnum, col))
                capcol = col;
  
            // Need to get the line again, may have looked at the previous
***************
*** 2815,2838 ****
  
  /*
   * Check if the word at line "lnum" column "col" is required to start with a
!  * capital.  This uses 'spellcapcheck' of the current buffer.
   */
      int
! check_need_cap(linenr_T lnum, colnr_T col)
  {
      int               need_cap = FALSE;
!     char_u    *line;
      char_u    *line_copy = NULL;
!     char_u    *p;
!     colnr_T   endcol;
!     regmatch_T        regmatch;
  
!     if (curwin->w_s->b_cap_prog == NULL)
!       return FALSE;
! 
!     line = ml_get_curline();
!     endcol = 0;
!     if (getwhitecols(line) >= (int)col)
      {
        // At start of line, check if previous line is empty or sentence
        // ends there.
--- 2815,2834 ----
  
  /*
   * Check if the word at line "lnum" column "col" is required to start with a
!  * capital.  This uses 'spellcapcheck' of the buffer in window "wp".
   */
      int
! check_need_cap(win_T *wp, linenr_T lnum, colnr_T col)
  {
+     if (wp->w_s->b_cap_prog == NULL)
+       return FALSE;
+ 
      int               need_cap = FALSE;
!     char_u    *line = col ? ml_get_buf(wp->w_buffer, lnum, FALSE) : NULL;
      char_u    *line_copy = NULL;
!     colnr_T   endcol = 0;
  
!     if (col == 0 || getwhitecols(line) >= col)
      {
        // At start of line, check if previous line is empty or sentence
        // ends there.
***************
*** 2840,2852 ****
            need_cap = TRUE;
        else
        {
!           line = ml_get(lnum - 1);
            if (*skipwhite(line) == NUL)
                need_cap = TRUE;
            else
            {
                // Append a space in place of the line break.
                line_copy = concat_str(line, (char_u *)" ");
                line = line_copy;
                endcol = (colnr_T)STRLEN(line);
            }
--- 2836,2851 ----
            need_cap = TRUE;
        else
        {
!           line = ml_get_buf(wp->w_buffer, lnum - 1, FALSE);
            if (*skipwhite(line) == NUL)
                need_cap = TRUE;
            else
            {
                // Append a space in place of the line break.
                line_copy = concat_str(line, (char_u *)" ");
+               if (line_copy == NULL)
+                   return FALSE;
+ 
                line = line_copy;
                endcol = (colnr_T)STRLEN(line);
            }
***************
*** 2858,2870 ****
      if (endcol > 0)
      {
        // Check if sentence ends before the bad word.
!       regmatch.regprog = curwin->w_s->b_cap_prog;
        regmatch.rm_ic = FALSE;
!       p = line + endcol;
        for (;;)
        {
            MB_PTR_BACK(line, p);
!           if (p == line || spell_iswordp_nmw(p, curwin))
                break;
            if (vim_regexec(&regmatch, p, 0)
                                         && regmatch.endp[0] == line + endcol)
--- 2857,2870 ----
      if (endcol > 0)
      {
        // Check if sentence ends before the bad word.
!       regmatch_T      regmatch;
!       regmatch.regprog = wp->w_s->b_cap_prog;
        regmatch.rm_ic = FALSE;
!       char_u *p = line + endcol;
        for (;;)
        {
            MB_PTR_BACK(line, p);
!           if (p == line || spell_iswordp_nmw(p, wp))
                break;
            if (vim_regexec(&regmatch, p, 0)
                                         && regmatch.endp[0] == line + endcol)
***************
*** 2873,2879 ****
                break;
            }
        }
!       curwin->w_s->b_cap_prog = regmatch.regprog;
      }
  
      vim_free(line_copy);
--- 2873,2879 ----
                break;
            }
        }
!       wp->w_s->b_cap_prog = regmatch.regprog;
      }
  
      vim_free(line_copy);
***************
*** 4340,4346 ****
      void
  spell_expand_check_cap(colnr_T col)
  {
!     spell_expand_need_cap = check_need_cap(curwin->w_cursor.lnum, col);
  }
  
  /*
--- 4340,4346 ----
      void
  spell_expand_check_cap(colnr_T col)
  {
!     spell_expand_need_cap = check_need_cap(curwin, curwin->w_cursor.lnum, 
col);
  }
  
  /*
*** ../vim-9.0.1577/src/proto/spell.pro 2023-02-20 12:16:33.336269408 +0000
--- src/proto/spell.pro 2023-05-25 17:11:52.183303177 +0100
***************
*** 28,34 ****
  int spell_iswordp(char_u *p, win_T *wp);
  int spell_iswordp_nmw(char_u *p, win_T *wp);
  int spell_casefold(win_T *wp, char_u *str, int len, char_u *buf, int buflen);
! int check_need_cap(linenr_T lnum, colnr_T col);
  void ex_spellrepall(exarg_T *eap);
  void onecap_copy(char_u *word, char_u *wcopy, int upper);
  void allcap_copy(char_u *word, char_u *wcopy);
--- 28,34 ----
  int spell_iswordp(char_u *p, win_T *wp);
  int spell_iswordp_nmw(char_u *p, win_T *wp);
  int spell_casefold(win_T *wp, char_u *str, int len, char_u *buf, int buflen);
! int check_need_cap(win_T *wp, linenr_T lnum, colnr_T col);
  void ex_spellrepall(exarg_T *eap);
  void onecap_copy(char_u *word, char_u *wcopy, int upper);
  void allcap_copy(char_u *word, char_u *wcopy);
*** ../vim-9.0.1577/src/spellsuggest.c  2023-03-07 17:13:47.313107772 +0000
--- src/spellsuggest.c  2023-05-25 17:08:43.291047554 +0100
***************
*** 538,544 ****
      // Get the word and its length.
  
      // Figure out if the word should be capitalised.
!     need_cap = check_need_cap(curwin->w_cursor.lnum, curwin->w_cursor.col);
  
      // Make a copy of current line since autocommands may free the line.
      line = vim_strsave(ml_get_curline());
--- 538,545 ----
      // Get the word and its length.
  
      // Figure out if the word should be capitalised.
!     need_cap = check_need_cap(curwin, curwin->w_cursor.lnum,
!                                                       curwin->w_cursor.col);
  
      // Make a copy of current line since autocommands may free the line.
      line = vim_strsave(ml_get_curline());
*** ../vim-9.0.1577/src/testdir/test_spell.vim  2022-11-14 20:52:11.277268383 
+0000
--- src/testdir/test_spell.vim  2023-05-25 17:08:43.291047554 +0100
***************
*** 999,1011 ****
    call VerifyScreenDump(buf, 'Test_spell_3', {})
  
    " Deleting a full stop removes missing Cap in next line
!   call term_sendkeys(buf, "5Gddk$x")
    call VerifyScreenDump(buf, 'Test_spell_4', {})
  
    " Undo also updates the next line (go to command line to remove message)
    call term_sendkeys(buf, "u:\<Esc>")
    call VerifyScreenDump(buf, 'Test_spell_5', {})
  
    " clean up
    call StopVimInTerminal(buf)
  endfunc
--- 999,1024 ----
    call VerifyScreenDump(buf, 'Test_spell_3', {})
  
    " Deleting a full stop removes missing Cap in next line
!   call term_sendkeys(buf, "5Gdd\<C-L>k$x")
    call VerifyScreenDump(buf, 'Test_spell_4', {})
  
    " Undo also updates the next line (go to command line to remove message)
    call term_sendkeys(buf, "u:\<Esc>")
    call VerifyScreenDump(buf, 'Test_spell_5', {})
  
+   " Folding an empty line does not remove Cap in next line
+   call term_sendkeys(buf, "uzfk:\<Esc>")
+   call VerifyScreenDump(buf, 'Test_spell_6', {})
+ 
+   " Folding the end of a sentence does not remove Cap in next line
+   " and editing a line does not remove Cap in current line
+   call term_sendkeys(buf, "Jzfkk$x")
+   call VerifyScreenDump(buf, 'Test_spell_7', {})
+ 
+   " Cap is correctly applied in the first row of a window
+   call term_sendkeys(buf, "\<C-E>\<C-L>")
+   call VerifyScreenDump(buf, 'Test_spell_8', {})
+ 
    " clean up
    call StopVimInTerminal(buf)
  endfunc
*** ../vim-9.0.1577/src/testdir/dumps/Test_spell_6.dump 2023-05-25 
17:14:07.015468776 +0100
--- src/testdir/dumps/Test_spell_6.dump 2023-05-25 17:08:43.291047554 +0100
***************
*** 0 ****
--- 1,8 ----
+ | +0&#ffffff0@2|T|h|i|s| |l|i|n|e| |h|a|s| |a| |s+0&#ffd7d7255|e|p|l@1| 
+0&#ffffff0|e|r@1|o|r|.| |a+0&#5fd7ff255|n|d| +0&#ffffff0|m|i|s@1|i|n|g| 
|c|a|p|s| |a|n|d| |t|r|a|i|l|i|n|g| |s|p|a|c|e|s|.| @5
+ |a+0&#5fd7ff255|n|o|t|h|e|r| +0&#ffffff0|m|i|s@1|i|n|g| |c|a|p| |h|e|r|e|.| 
@49
+ |N|o|t| @71
+ >++0#0000e05#a8a8a8255|-@1| @1|2| |l|i|n|e|s|:| |a|n|d| |h|e|r|e|.|-@51
+ |a+0#0000000#5fd7ff255|n|d| +0&#ffffff0|h|e|r|e|.| @65
+ |~+0#4040ff13&| @73
+ |~| @73
+ | +0#0000000&@56|4|,|4|-|1| @8|A|l@1| 
*** ../vim-9.0.1577/src/testdir/dumps/Test_spell_7.dump 2023-05-25 
17:14:07.019468782 +0100
--- src/testdir/dumps/Test_spell_7.dump 2023-05-25 17:08:43.291047554 +0100
***************
*** 0 ****
--- 1,8 ----
+ | +0&#ffffff0@2|T|h|i|s| |l|i|n|e| |h|a|s| |a| |s+0&#ffd7d7255|e|p|l@1| 
+0&#ffffff0|e|r@1|o|r|.| |a+0&#5fd7ff255|n|d| +0&#ffffff0|m|i|s@1|i|n|g| 
|c|a|p|s| |a|n|d| |t|r|a|i|l|i|n|g| |s|p|a|c|e|s|.| @5
+ |a+0&#5fd7ff255|n|o|t|h|e|r| +0&#ffffff0|m|i|s@1|i|n|g| |c|a|p| |h|e|r>e| @50
+ |++0#0000e05#a8a8a8255|-@1| @1|2| |l|i|n|e|s|:| |N|o|t|-@57
+ |a+0#0000000#5fd7ff255|n|d| +0&#ffffff0|h|e|r|e|.| @65
+ |~+0#4040ff13&| @73
+ |~| @73
+ |~| @73
+ | +0#0000000&@56|2|,|2|4| @9|A|l@1| 
*** ../vim-9.0.1577/src/testdir/dumps/Test_spell_8.dump 2023-05-25 
17:14:07.023468785 +0100
--- src/testdir/dumps/Test_spell_8.dump 2023-05-25 17:08:43.291047554 +0100
***************
*** 0 ****
--- 1,8 ----
+ |a+0&#5fd7ff255|n|o|t|h|e|r| +0&#ffffff0|m|i|s@1|i|n|g| |c|a|p| |h|e|r|e| @50
+ |++0#0000e05#a8a8a8255|-@1| @1|2| |l|i|n|e|s|:| |N|o|t|-@57
+ |a+0#0000000#5fd7ff255|n|d| +0&#ffffff0|h|e|r|e>.| @65
+ |~+0#4040ff13&| @73
+ |~| @73
+ |~| @73
+ |~| @73
+ | +0#0000000&@56|5|,|9| @10|B|o|t| 
*** ../vim-9.0.1577/src/version.c       2023-05-25 16:43:23.758752677 +0100
--- src/version.c       2023-05-25 17:11:13.279249455 +0100
***************
*** 697,698 ****
--- 697,700 ----
  {   /* Add new patch number below this line */
+ /**/
+     1578,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
78. You find yourself dialing IP numbers on the phone.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20230525161512.4869A1C0634%40moolenaar.net.

Raspunde prin e-mail lui