Patch 8.2.0844
Problem:    Text properties crossing lines not handled correctly.
Solution:   When saving for undo include an extra line when needed and do not
            adjust properties when undoing. (Axel Forsman, closes #5875)
Files:      src/memline.c, src/proto/memline.pro, src/undo.c, src/structs.h


*** ../vim-8.2.0843/src/memline.c       2020-02-14 13:21:55.646197062 +0100
--- src/memline.c       2020-05-30 14:21:38.307387444 +0200
***************
*** 243,249 ****
  static void add_b0_fenc(ZERO_BL *b0p, buf_T *buf);
  static time_t swapfile_info(char_u *);
  static int recov_file_names(char_u **, char_u *, int prepend_dot);
- static int ml_delete_int(buf_T *, linenr_T, int);
  static char_u *findswapname(buf_T *, char_u **, char_u *);
  static void ml_flush_line(buf_T *);
  static bhdr_T *ml_new_data(memfile_T *, int, int);
--- 243,248 ----
***************
*** 2618,2623 ****
--- 2617,2625 ----
  }
  
  #ifdef FEAT_PROP_POPUP
+ /*
+  * Add text properties that continue from the previous line.
+  */
      static void
  add_text_props_for_append(
            buf_T       *buf,
***************
*** 2657,2671 ****
        count = get_text_props(buf, lnum, &props, FALSE);
        for (n = 0; n < count; ++n)
        {
!           mch_memmove(&prop, props + n * sizeof(textprop_T), 
sizeof(textprop_T));
            if (prop.tp_flags & TP_FLAG_CONT_NEXT)
            {
                if (round == 2)
                {
                    prop.tp_flags |= TP_FLAG_CONT_PREV;
                    prop.tp_col = 1;
!                   prop.tp_len = *len;
!                   mch_memmove(new_line + *len + new_prop_count * 
sizeof(textprop_T), &prop, sizeof(textprop_T));
                }
                ++new_prop_count;
            }
--- 2659,2675 ----
        count = get_text_props(buf, lnum, &props, FALSE);
        for (n = 0; n < count; ++n)
        {
!           mch_memmove(&prop, props + n * sizeof(textprop_T),
!                                                          sizeof(textprop_T));
            if (prop.tp_flags & TP_FLAG_CONT_NEXT)
            {
                if (round == 2)
                {
                    prop.tp_flags |= TP_FLAG_CONT_PREV;
                    prop.tp_col = 1;
!                   prop.tp_len = *len;  // not exactly the right length
!                   mch_memmove(new_line + *len + new_prop_count
!                             * sizeof(textprop_T), &prop, sizeof(textprop_T));
                }
                ++new_prop_count;
            }
***************
*** 2683,2690 ****
      linenr_T  lnum,           // append after this line (can be 0)
      char_u    *line_arg,      // text of the new line
      colnr_T   len_arg,        // length of line, including NUL, or 0
!     int               newfile,        // flag, see above
!     int               mark)           // mark the new line
  {
      char_u    *line = line_arg;
      colnr_T   len = len_arg;
--- 2687,2693 ----
      linenr_T  lnum,           // append after this line (can be 0)
      char_u    *line_arg,      // text of the new line
      colnr_T   len_arg,        // length of line, including NUL, or 0
!     int               flags)          // ML_APPEND_ flags
  {
      char_u    *line = line_arg;
      colnr_T   len = len_arg;
***************
*** 2716,2722 ****
        len = (colnr_T)STRLEN(line) + 1;        // space needed for the text
  
  #ifdef FEAT_PROP_POPUP
!     if (curbuf->b_has_textprop && lnum > 0)
        // Add text properties that continue from the previous line.
        add_text_props_for_append(buf, lnum, &line, &len, &tofree);
  #endif
--- 2719,2725 ----
        len = (colnr_T)STRLEN(line) + 1;        // space needed for the text
  
  #ifdef FEAT_PROP_POPUP
!     if (curbuf->b_has_textprop && lnum > 0 && !(flags & ML_APPEND_UNDO))
        // Add text properties that continue from the previous line.
        add_text_props_for_append(buf, lnum, &line, &len, &tofree);
  #endif
***************
*** 2815,2828 ****
         * copy the text into the block
         */
        mch_memmove((char *)dp + dp->db_index[db_idx + 1], line, (size_t)len);
!       if (mark)
            dp->db_index[db_idx + 1] |= DB_MARKED;
  
        /*
         * Mark the block dirty.
         */
        buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
!       if (!newfile)
            buf->b_ml.ml_flags |= ML_LOCKED_POS;
      }
      else          // not enough space in data block
--- 2818,2831 ----
         * copy the text into the block
         */
        mch_memmove((char *)dp + dp->db_index[db_idx + 1], line, (size_t)len);
!       if (flags & ML_APPEND_MARK)
            dp->db_index[db_idx + 1] |= DB_MARKED;
  
        /*
         * Mark the block dirty.
         */
        buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
!       if (!(flags & ML_APPEND_NEW))
            buf->b_ml.ml_flags |= ML_LOCKED_POS;
      }
      else          // not enough space in data block
***************
*** 2891,2897 ****
        }
  
        page_count = ((space_needed + HEADER_SIZE) + page_size - 1) / page_size;
!       if ((hp_new = ml_new_data(mfp, newfile, page_count)) == NULL)
        {
                        // correct line counts in pointer blocks
            --(buf->b_ml.ml_locked_lineadd);
--- 2894,2901 ----
        }
  
        page_count = ((space_needed + HEADER_SIZE) + page_size - 1) / page_size;
!       if ((hp_new = ml_new_data(mfp, flags & ML_APPEND_NEW, page_count))
!                                                                      == NULL)
        {
                        // correct line counts in pointer blocks
            --(buf->b_ml.ml_locked_lineadd);
***************
*** 2927,2933 ****
            dp_right->db_txt_start -= len;
            dp_right->db_free -= len + INDEX_SIZE;
            dp_right->db_index[0] = dp_right->db_txt_start;
!           if (mark)
                dp_right->db_index[0] |= DB_MARKED;
  
            mch_memmove((char *)dp_right + dp_right->db_txt_start,
--- 2931,2937 ----
            dp_right->db_txt_start -= len;
            dp_right->db_free -= len + INDEX_SIZE;
            dp_right->db_index[0] = dp_right->db_txt_start;
!           if (flags & ML_APPEND_MARK)
                dp_right->db_index[0] |= DB_MARKED;
  
            mch_memmove((char *)dp_right + dp_right->db_txt_start,
***************
*** 2968,2974 ****
            dp_left->db_txt_start -= len;
            dp_left->db_free -= len + INDEX_SIZE;
            dp_left->db_index[line_count_left] = dp_left->db_txt_start;
!           if (mark)
                dp_left->db_index[line_count_left] |= DB_MARKED;
            mch_memmove((char *)dp_left + dp_left->db_txt_start,
                                                           line, (size_t)len);
--- 2972,2978 ----
            dp_left->db_txt_start -= len;
            dp_left->db_free -= len + INDEX_SIZE;
            dp_left->db_index[line_count_left] = dp_left->db_txt_start;
!           if (flags & ML_APPEND_MARK)
                dp_left->db_index[line_count_left] |= DB_MARKED;
            mch_memmove((char *)dp_left + dp_left->db_txt_start,
                                                           line, (size_t)len);
***************
*** 2999,3005 ****
         */
        if (lines_moved || in_left)
            buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
!       if (!newfile && db_idx >= 0 && in_left)
            buf->b_ml.ml_flags |= ML_LOCKED_POS;
        mf_put(mfp, hp_new, TRUE, FALSE);
  
--- 3003,3009 ----
         */
        if (lines_moved || in_left)
            buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
!       if (!(flags & ML_APPEND_NEW) && db_idx >= 0 && in_left)
            buf->b_ml.ml_flags |= ML_LOCKED_POS;
        mf_put(mfp, hp_new, TRUE, FALSE);
  
***************
*** 3207,3213 ****
      linenr_T  lnum,           // append after this line (can be 0)
      char_u    *line,          // text of the new line
      colnr_T   len,            // length of line, including NUL, or 0
!     int               newfile)        // flag, see above
  {
      if (lnum > buf->b_ml.ml_line_count)
        return FAIL;  // lnum out of range
--- 3211,3217 ----
      linenr_T  lnum,           // append after this line (can be 0)
      char_u    *line,          // text of the new line
      colnr_T   len,            // length of line, including NUL, or 0
!     int               flags)          // ML_APPEND_ flags
  {
      if (lnum > buf->b_ml.ml_line_count)
        return FAIL;  // lnum out of range
***************
*** 3224,3230 ****
        ml_flush_line(buf);
  #endif
  
!     return ml_append_int(buf, lnum, line, len, newfile, FALSE);
  }
  
  /*
--- 3228,3234 ----
        ml_flush_line(buf);
  #endif
  
!     return ml_append_int(buf, lnum, line, len, flags);
  }
  
  /*
***************
*** 3232,3238 ****
   * "line" does not need to be allocated, but can't be another line in a
   * buffer, unlocking may make it invalid.
   *
!  *   newfile: TRUE when starting to edit a new file, meaning that pe_old_lnum
   *            will be set for recovery
   * Check: The caller of this function should probably also call
   * appended_lines().
--- 3236,3242 ----
   * "line" does not need to be allocated, but can't be another line in a
   * buffer, unlocking may make it invalid.
   *
!  * "newfile": TRUE when starting to edit a new file, meaning that pe_old_lnum
   *            will be set for recovery
   * Check: The caller of this function should probably also call
   * appended_lines().
***************
*** 3246,3257 ****
      colnr_T   len,            // length of new line, including NUL, or 0
      int               newfile)        // flag, see above
  {
      // When starting up, we might still need to create the memfile
      if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL)
        return FAIL;
!     return ml_append_flush(curbuf, lnum, line, len, newfile);
  }
  
  #if defined(FEAT_SPELL) || defined(FEAT_QUICKFIX) || defined(PROTO)
  /*
   * Like ml_append() but for an arbitrary buffer.  The buffer must already have
--- 3250,3272 ----
      colnr_T   len,            // length of new line, including NUL, or 0
      int               newfile)        // flag, see above
  {
+     return ml_append_flags(lnum, line, len, newfile ? ML_APPEND_NEW : 0);
+ }
+ 
+     int
+ ml_append_flags(
+     linenr_T  lnum,           // append after this line (can be 0)
+     char_u    *line,          // text of the new line
+     colnr_T   len,            // length of new line, including NUL, or 0
+     int               flags)          // ML_APPEND_ values
+ {
      // When starting up, we might still need to create the memfile
      if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL)
        return FAIL;
!     return ml_append_flush(curbuf, lnum, line, len, flags);
  }
  
+ 
  #if defined(FEAT_SPELL) || defined(FEAT_QUICKFIX) || defined(PROTO)
  /*
   * Like ml_append() but for an arbitrary buffer.  The buffer must already have
***************
*** 3267,3273 ****
  {
      if (buf->b_ml.ml_mfp == NULL)
        return FAIL;
!     return ml_append_flush(buf, lnum, line, len, newfile);
  }
  #endif
  
--- 3282,3288 ----
  {
      if (buf->b_ml.ml_mfp == NULL)
        return FAIL;
!     return ml_append_flush(buf, lnum, line, len, newfile ? ML_APPEND_NEW : 0);
  }
  #endif
  
***************
*** 3487,3517 ****
  
  /*
   * Delete line "lnum" in the current buffer.
!  * When "message" is TRUE may give a "No lines in buffer" message.
!  *
!  * Check: The caller of this function should probably also call
!  * deleted_lines() after this.
   *
   * return FAIL for failure, OK otherwise
   */
-     int
- ml_delete(linenr_T lnum, int message)
- {
-     ml_flush_line(curbuf);
-     if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count)
-       return FAIL;
- 
- #ifdef FEAT_EVAL
-     // When inserting above recorded changes: flush the changes before 
changing
-     // the text.
-     may_invoke_listeners(curbuf, lnum, lnum + 1, -1);
- #endif
- 
-     return ml_delete_int(curbuf, lnum, message);
- }
- 
      static int
! ml_delete_int(buf_T *buf, linenr_T lnum, int message)
  {
      bhdr_T    *hp;
      memfile_T *mfp;
--- 3502,3514 ----
  
  /*
   * Delete line "lnum" in the current buffer.
!  * When "flags" has ML_DEL_MESSAGE may give a "No lines in buffer" message.
!  * When "flags" has ML_DEL_UNDO this is called from undo.
   *
   * return FAIL for failure, OK otherwise
   */
      static int
! ml_delete_int(buf_T *buf, linenr_T lnum, int flags)
  {
      bhdr_T    *hp;
      memfile_T *mfp;
***************
*** 3539,3545 ****
   */
      if (buf->b_ml.ml_line_count == 1)     // file becomes empty
      {
!       if (message
  #ifdef FEAT_NETBEANS_INTG
                && !netbeansSuppressNoLines
  #endif
--- 3536,3542 ----
   */
      if (buf->b_ml.ml_line_count == 1)     // file becomes empty
      {
!       if ((flags & ML_DEL_MESSAGE)
  #ifdef FEAT_NETBEANS_INTG
                && !netbeansSuppressNoLines
  #endif
***************
*** 3586,3592 ****
  #ifdef FEAT_PROP_POPUP
      // If there are text properties, make a copy, so that we can update
      // properties in preceding and following lines.
!     if (buf->b_has_textprop)
      {
        size_t  textlen = STRLEN((char_u *)dp + line_start) + 1;
  
--- 3583,3589 ----
  #ifdef FEAT_PROP_POPUP
      // If there are text properties, make a copy, so that we can update
      // properties in preceding and following lines.
!     if (buf->b_has_textprop && !(flags & ML_DEL_UNDO))
      {
        size_t  textlen = STRLEN((char_u *)dp + line_start) + 1;
  
***************
*** 3699,3704 ****
--- 3696,3735 ----
  }
  
  /*
+  * Delete line "lnum" in the current buffer.
+  * When "message" is TRUE may give a "No lines in buffer" message.
+  *
+  * Check: The caller of this function should probably also call
+  * deleted_lines() after this.
+  *
+  * return FAIL for failure, OK otherwise
+  */
+     int
+ ml_delete(linenr_T lnum, int message)
+ {
+     return ml_delete_flags(lnum, message ? ML_DEL_MESSAGE : 0);
+ }
+ 
+ /*
+  * Like ml_delete() but using flags (see ml_delete_int()).
+  */
+     int
+ ml_delete_flags(linenr_T lnum, int flags)
+ {
+     ml_flush_line(curbuf);
+     if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count)
+       return FAIL;
+ 
+ #ifdef FEAT_EVAL
+     // When inserting above recorded changes: flush the changes before 
changing
+     // the text.
+     may_invoke_listeners(curbuf, lnum, lnum + 1, -1);
+ #endif
+ 
+     return ml_delete_int(curbuf, lnum, flags);
+ }
+ 
+ /*
   * set the DB_MARKED flag for line 'lnum'
   */
      void
***************
*** 3905,3913 ****
                 * Don't forget to copy the mark!
                 */
                // How about handling errors???
!               (void)ml_append_int(buf, lnum, new_line, new_len, FALSE,
!                                            (dp->db_index[idx] & DB_MARKED));
!               (void)ml_delete_int(buf, lnum, FALSE);
            }
        }
        vim_free(new_line);
--- 3936,3944 ----
                 * Don't forget to copy the mark!
                 */
                // How about handling errors???
!               (void)ml_append_int(buf, lnum, new_line, new_len,
!                        (dp->db_index[idx] & DB_MARKED) ? ML_APPEND_MARK : 0);
!               (void)ml_delete_int(buf, lnum, 0);
            }
        }
        vim_free(new_line);
*** ../vim-8.2.0843/src/proto/memline.pro       2020-02-14 13:21:55.646197062 
+0100
--- src/proto/memline.pro       2020-05-30 14:26:25.222250641 +0200
***************
*** 22,31 ****
--- 22,33 ----
  char_u *ml_get_buf(buf_T *buf, linenr_T lnum, int will_change);
  int ml_line_alloced(void);
  int ml_append(linenr_T lnum, char_u *line, colnr_T len, int newfile);
+ int ml_append_flags(linenr_T lnum, char_u *line, colnr_T len, int flags);
  int ml_append_buf(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, int 
newfile);
  int ml_replace(linenr_T lnum, char_u *line, int copy);
  int ml_replace_len(linenr_T lnum, char_u *line_arg, colnr_T len_arg, int 
has_props, int copy);
  int ml_delete(linenr_T lnum, int message);
+ int ml_delete_flags(linenr_T lnum, int flags);
  void ml_setmarked(linenr_T lnum);
  linenr_T ml_firstmarked(void);
  void ml_clearmarked(void);
*** ../vim-8.2.0843/src/undo.c  2020-04-30 22:29:36.626024141 +0200
--- src/undo.c  2020-05-30 14:26:17.670280596 +0200
***************
*** 376,381 ****
--- 376,402 ----
  }
  
  /*
+  * return TRUE if line "lnum" has text property "flags".
+  */
+     static int
+ has_prop_w_flags(linenr_T lnum, int flags)
+ {
+     char_u  *props;
+     int           i;
+     int           proplen = get_text_props(curbuf, lnum, &props, FALSE);
+ 
+     for (i = 0; i < proplen; ++i)
+     {
+       textprop_T prop;
+ 
+       mch_memmove(&prop, props + i * sizeof prop, sizeof prop);
+       if (prop.tp_flags & flags)
+           return TRUE;
+     }
+     return FALSE;
+ }
+ 
+ /*
   * Common code for various ways to save text before a change.
   * "top" is the line above the first changed line.
   * "bot" is the line below the last changed line.
***************
*** 450,455 ****
--- 471,493 ----
      u_check(FALSE);
  #endif
  
+ #ifdef FEAT_PROP_POPUP
+     // Include the line above if a text property continues from it.
+     // Include the line below if a text property continues to it.
+     if (bot - top > 1)
+     {
+       if (top > 0 && has_prop_w_flags(top + 1, TP_FLAG_CONT_PREV))
+           --top;
+       if (bot <= curbuf->b_ml.ml_line_count
+                              && has_prop_w_flags(bot - 1, TP_FLAG_CONT_NEXT))
+       {
+           ++bot;
+           if (newbot != 0)
+               ++newbot;
+       }
+     }
+ #endif
+ 
      size = bot - top - 1;
  
      /*
***************
*** 2745,2751 ****
                // dummy empty line will be inserted
                if (curbuf->b_ml.ml_line_count == 1)
                    empty_buffer = TRUE;
!               ml_delete(lnum, FALSE);
            }
        }
        else
--- 2783,2789 ----
                // dummy empty line will be inserted
                if (curbuf->b_ml.ml_line_count == 1)
                    empty_buffer = TRUE;
!               ml_delete_flags(lnum, ML_DEL_UNDO);
            }
        }
        else
***************
*** 2767,2774 ****
                    ml_replace_len((linenr_T)1, uep->ue_array[i].ul_line,
                                          uep->ue_array[i].ul_len, TRUE, TRUE);
                else
!                   ml_append(lnum, uep->ue_array[i].ul_line,
!                                     (colnr_T)uep->ue_array[i].ul_len, FALSE);
                vim_free(uep->ue_array[i].ul_line);
            }
            vim_free((char_u *)uep->ue_array);
--- 2805,2812 ----
                    ml_replace_len((linenr_T)1, uep->ue_array[i].ul_line,
                                          uep->ue_array[i].ul_len, TRUE, TRUE);
                else
!                   ml_append_flags(lnum, uep->ue_array[i].ul_line,
!                            (colnr_T)uep->ue_array[i].ul_len, ML_APPEND_UNDO);
                vim_free(uep->ue_array[i].ul_line);
            }
            vim_free((char_u *)uep->ue_array);
*** ../vim-8.2.0843/src/structs.h       2020-05-25 22:36:46.629735032 +0200
--- src/structs.h       2020-05-30 14:19:49.331818479 +0200
***************
*** 742,747 ****
--- 742,756 ----
  #endif
  } memline_T;
  
+ // Values for the flags argument of ml_delete_flags().
+ #define ML_DEL_MESSAGE            1   // may give a "No lines in buffer" 
message
+ #define ML_DEL_UNDO       2   // called from undo, do not update textprops
+ 
+ // Values for the flags argument of ml_append_int().
+ #define ML_APPEND_NEW     1   // starting to edit a new file
+ #define ML_APPEND_MARK            2   // mark the new line
+ #define ML_APPEND_UNDO            4   // called from undo
+ 
  
  /*
   * Structure defining text properties.  These stick with the text.
*** ../vim-8.2.0843/src/version.c       2020-05-30 13:15:11.098875009 +0200
--- src/version.c       2020-05-30 14:46:21.329275749 +0200
***************
*** 748,749 ****
--- 748,751 ----
  {   /* Add new patch number below this line */
+ /**/
+     844,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
214. Your MCI "Circle of Friends" are all Hayes-compatible.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            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/202005301247.04UClNPf318871%40masaka.moolenaar.net.

Raspunde prin e-mail lui