# HG changeset patch # User Bram Moolenaar # Date 1590843603 -7200 # Node ID e529690f27bc58d516ee2381591ecd8e55e054e0 # Parent 213a1f6ae87d296457f3c71c456a6f880a24947f patch 8.2.0844: text properties crossing lines not handled correctly Commit: https://github.com/vim/vim/commit/a9d4b84d97fb74061eeb42c1433e111fb58825dc Author: Bram Moolenaar Date: Sat May 30 14:46:52 2020 +0200 patch 8.2.0844: text properties crossing lines not handled correctly 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) diff --git a/src/memline.c b/src/memline.c --- a/src/memline.c +++ b/src/memline.c @@ -243,7 +243,6 @@ static void set_b0_dir_flag(ZERO_BL *b0p 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(). @@ -3246,12 +3250,23 @@ ml_append( 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, */ 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, #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; @@ -3699,6 +3696,40 @@ theend: } /* + * 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,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); diff --git a/src/proto/memline.pro b/src/proto/memline.pro --- a/src/proto/memline.pro +++ b/src/proto/memline.pro @@ -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); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -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. diff --git a/src/undo.c b/src/undo.c --- a/src/undo.c +++ b/src/undo.c @@ -376,6 +376,27 @@ u_save_line(undoline_T *ul, linenr_T lnu } /* + * 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,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); diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -747,6 +747,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 844, +/**/ 843, /**/ 842,