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.