]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 8.2.0844: text properties crossing lines not handled correctly v8.2.0844
authorBram Moolenaar <Bram@vim.org>
Sat, 30 May 2020 12:46:52 +0000 (14:46 +0200)
committerBram Moolenaar <Bram@vim.org>
Sat, 30 May 2020 12:46:52 +0000 (14:46 +0200)
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)

src/memline.c
src/proto/memline.pro
src/structs.h
src/undo.c
src/version.c

index b409eb5553f0ca2a4b8fbf394f0d8acb7361c629..6d02119e556234792758a13970f38e199fa606ce 100644 (file)
@@ -243,7 +243,6 @@ static void set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf);
 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);
@@ -2618,6 +2617,9 @@ ml_line_alloced(void)
 }
 
 #ifdef FEAT_PROP_POPUP
+/*
+ * Add text properties that continue from the previous line.
+ */
     static void
 add_text_props_for_append(
            buf_T       *buf,
@@ -2657,15 +2659,17 @@ add_text_props_for_append(
        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));
+           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));
+                   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,8 +2687,7 @@ ml_append_int(
     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
+    int                flags)          // ML_APPEND_ flags
 {
     char_u     *line = line_arg;
     colnr_T    len = len_arg;
@@ -2716,7 +2719,7 @@ ml_append_int(
        len = (colnr_T)STRLEN(line) + 1;        // space needed for the text
 
 #ifdef FEAT_PROP_POPUP
-    if (curbuf->b_has_textprop && lnum > 0)
+    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,14 +2818,14 @@ ml_append_int(
         * copy the text into the block
         */
        mch_memmove((char *)dp + dp->db_index[db_idx + 1], line, (size_t)len);
-       if (mark)
+       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 (!newfile)
+       if (!(flags & ML_APPEND_NEW))
            buf->b_ml.ml_flags |= ML_LOCKED_POS;
     }
     else           // not enough space in data block
@@ -2891,7 +2894,8 @@ ml_append_int(
        }
 
        page_count = ((space_needed + HEADER_SIZE) + page_size - 1) / page_size;
-       if ((hp_new = ml_new_data(mfp, newfile, page_count)) == NULL)
+       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,7 +2931,7 @@ ml_append_int(
            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)
+           if (flags & ML_APPEND_MARK)
                dp_right->db_index[0] |= DB_MARKED;
 
            mch_memmove((char *)dp_right + dp_right->db_txt_start,
@@ -2968,7 +2972,7 @@ ml_append_int(
            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)
+           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,7 +3003,7 @@ ml_append_int(
         */
        if (lines_moved || in_left)
            buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
-       if (!newfile && db_idx >= 0 && in_left)
+       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,7 +3211,7 @@ ml_append_flush(
     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
+    int                flags)          // ML_APPEND_ flags
 {
     if (lnum > buf->b_ml.ml_line_count)
        return FAIL;  // lnum out of range
@@ -3224,7 +3228,7 @@ ml_append_flush(
        ml_flush_line(buf);
 #endif
 
-    return ml_append_int(buf, lnum, line, len, newfile, FALSE);
+    return ml_append_int(buf, lnum, line, len, flags);
 }
 
 /*
@@ -3232,7 +3236,7 @@ ml_append_flush(
  * "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
+ * "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().
@@ -3245,13 +3249,24 @@ ml_append(
     char_u     *line,          // text of the new line
     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, newfile);
+    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,7 +3282,7 @@ ml_append_buf(
 {
     if (buf->b_ml.ml_mfp == NULL)
        return FAIL;
-    return ml_append_flush(buf, lnum, line, len, newfile);
+    return ml_append_flush(buf, lnum, line, len, newfile ? ML_APPEND_NEW : 0);
 }
 #endif
 
@@ -3487,31 +3502,13 @@ adjust_text_props_for_delete(
 
 /*
  * 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.
+ * 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
  */
-    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)
+ml_delete_int(buf_T *buf, linenr_T lnum, int flags)
 {
     bhdr_T     *hp;
     memfile_T  *mfp;
@@ -3539,7 +3536,7 @@ ml_delete_int(buf_T *buf, linenr_T lnum, int message)
  */
     if (buf->b_ml.ml_line_count == 1)      // file becomes empty
     {
-       if (message
+       if ((flags & ML_DEL_MESSAGE)
 #ifdef FEAT_NETBEANS_INTG
                && !netbeansSuppressNoLines
 #endif
@@ -3586,7 +3583,7 @@ ml_delete_int(buf_T *buf, linenr_T lnum, int message)
 #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)
+    if (buf->b_has_textprop && !(flags & ML_DEL_UNDO))
     {
        size_t  textlen = STRLEN((char_u *)dp + line_start) + 1;
 
@@ -3698,6 +3695,40 @@ theend:
     return ret;
 }
 
+/*
+ * 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'
  */
@@ -3905,9 +3936,9 @@ ml_flush_line(buf_T *buf)
                 * 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);
+               (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);
index 113417042ee8f1e63dcc287aeda81ac705ebe356..de11868e51fa40611431901a588032b37ddb52e5 100644 (file)
@@ -22,10 +22,12 @@ char_u *ml_get_cursor(void);
 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);
index d6f9030c9e2c2bedcd290c976d2dcfba18e4d851..eb4f60ebb7fc523791560cf2d1f1f31d9df8e03f 100644 (file)
@@ -742,6 +742,15 @@ typedef struct memline
 #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.
index 4bbc0af0fb3d052cff6fd89bc2b65a9314cdd5d6..259de9ab2dcf290f3a43c7b79126c1941d488d75 100644 (file)
@@ -375,6 +375,27 @@ u_save_line(undoline_T *ul, linenr_T lnum)
     return ul->ul_line == NULL ? FAIL : OK;
 }
 
+/*
+ * 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.
@@ -450,6 +471,23 @@ u_savecommon(
     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,7 +2783,7 @@ u_undoredo(int undo)
                // dummy empty line will be inserted
                if (curbuf->b_ml.ml_line_count == 1)
                    empty_buffer = TRUE;
-               ml_delete(lnum, FALSE);
+               ml_delete_flags(lnum, ML_DEL_UNDO);
            }
        }
        else
@@ -2767,8 +2805,8 @@ u_undoredo(int undo)
                    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);
+                   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);
index 349ecbef62eb9df05f32e28ba88b423c18dec5db..2a33137dee99ba5a96af34b35e84d9c60da0d536 100644 (file)
@@ -746,6 +746,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    844,
 /**/
     843,
 /**/