vim-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 vim/vim#5875)

ML_DEL_UNDO, ML_APPEND_UNDO are no-opt because textprop feature is N/A.

a9d4b84d97

Co-authored-by: Bram Moolenaar <Bram@vim.org>
This commit is contained in:
Jan Edmund Lazo
2025-08-16 17:59:58 -04:00
parent 09964b7ef0
commit ed012b817d
3 changed files with 79 additions and 41 deletions

View File

@@ -1973,12 +1973,10 @@ int ml_line_alloced(void)
/// @param lnum append after this line (can be 0)
/// @param line text of the new line
/// @param len length of line, including NUL, or 0
/// @param newfile flag, see above
/// @param mark mark the new line
/// @param flags ML_APPEND_ flags
///
/// @return FAIL for failure, OK otherwise
static int ml_append_int(buf_T *buf, linenr_T lnum, char *line, colnr_T len, bool newfile,
bool mark)
static int ml_append_int(buf_T *buf, linenr_T lnum, char *line, colnr_T len, int flags)
FUNC_ATTR_NONNULL_ARG(1)
{
// lnum out of range
@@ -2075,13 +2073,13 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char *line, colnr_T len, boo
// copy the text into the block
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
@@ -2135,7 +2133,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char *line, colnr_T len, boo
}
int page_count = ((space_needed + (int)HEADER_SIZE) + page_size - 1) / page_size;
hp_new = ml_new_data(mfp, newfile, page_count);
hp_new = ml_new_data(mfp, flags & ML_APPEND_NEW, page_count);
if (db_idx < 0) { // left block is new
hp_left = hp_new;
hp_right = hp;
@@ -2159,7 +2157,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char *line, colnr_T len, boo
dp_right->db_txt_start -= (unsigned)len;
dp_right->db_free -= (unsigned)len + (unsigned)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;
}
@@ -2192,7 +2190,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char *line, colnr_T len, boo
dp_left->db_txt_start -= (unsigned)len;
dp_left->db_free -= (unsigned)len + (unsigned)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;
}
memmove((char *)dp_left + dp_left->db_txt_start,
@@ -2221,7 +2219,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char *line, colnr_T len, boo
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);
@@ -2386,10 +2384,10 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char *line, colnr_T len, boo
/// @param lnum append after this line (can be 0)
/// @param line text of the new line
/// @param len length of line, including NUL, or 0
/// @param newfile flag, see above
/// @param flags ML_APPEND_ flags
///
/// @return FAIL for failure, OK otherwise
static int ml_append_flush(buf_T *buf, linenr_T lnum, char *line, colnr_T len, bool newfile)
static int ml_append_flush(buf_T *buf, linenr_T lnum, char *line, colnr_T len, int flags)
FUNC_ATTR_NONNULL_ARG(1)
{
if (lnum > buf->b_ml.ml_line_count) {
@@ -2400,14 +2398,14 @@ static int ml_append_flush(buf_T *buf, linenr_T lnum, char *line, colnr_T len, b
ml_flush_line(buf, false);
}
return ml_append_int(buf, lnum, line, len, newfile, false);
return ml_append_int(buf, lnum, line, len, flags);
}
/// Append a line after lnum (may be 0 to insert a line in front of the file).
/// "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().
@@ -2419,13 +2417,24 @@ static int ml_append_flush(buf_T *buf, linenr_T lnum, char *line, colnr_T len, b
///
/// @return FAIL for failure, OK otherwise
int ml_append(linenr_T lnum, char *line, colnr_T len, bool newfile)
{
return ml_append_flags(lnum, line, len, newfile ? ML_APPEND_NEW : 0);
}
/// @param lnum append after this line (can be 0)
/// @param line text of the new line
/// @param len length of new line, including nul, or 0
/// @param flags ML_APPEND_ values
///
/// @return FAIL for failure, OK otherwise
int ml_append_flags(linenr_T lnum, char *line, colnr_T len, int flags)
{
// 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);
}
/// Like ml_append() but for an arbitrary buffer. The buffer must already have
@@ -2442,7 +2451,7 @@ int ml_append_buf(buf_T *buf, linenr_T lnum, char *line, colnr_T len, bool newfi
return FAIL;
}
return ml_append_flush(buf, lnum, line, len, newfile);
return ml_append_flush(buf, lnum, line, len, newfile ? ML_APPEND_NEW : 0);
}
void ml_add_deleted_len(char *ptr, ssize_t len)
@@ -2530,24 +2539,6 @@ int ml_replace_buf(buf_T *buf, linenr_T lnum, char *line, bool copy, bool noallo
return OK;
}
/// Delete line `lnum` in the current buffer.
///
/// @note The caller of this function should probably also call
/// deleted_lines() after this.
///
/// @param message Show "--No lines in buffer--" message.
///
/// @return FAIL for failure, OK otherwise
int ml_delete(linenr_T lnum, bool message)
{
ml_flush_line(curbuf, false);
if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) {
return FAIL;
}
return ml_delete_int(curbuf, lnum, message);
}
/// Delete line `lnum` in buffer
///
/// @note The caller of this function should probably also call changed_lines() after this.
@@ -2556,12 +2547,20 @@ int ml_delete(linenr_T lnum, bool message)
///
/// @return FAIL for failure, OK otherwise
int ml_delete_buf(buf_T *buf, linenr_T lnum, bool message)
FUNC_ATTR_NONNULL_ALL
{
ml_flush_line(buf, false);
return ml_delete_int(buf, lnum, message);
}
static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message)
/// Delete line `lnum` in the current buffer.
///
/// @param flags ML_DEL_MESSAGE may give a "No lines in buffer" message.
/// 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)
FUNC_ATTR_NONNULL_ALL
{
if (lowest_marked && lowest_marked > lnum) {
lowest_marked--;
@@ -2569,7 +2568,7 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message)
// If the file becomes empty the last line is replaced by an empty line.
if (buf->b_ml.ml_line_count == 1) { // file becomes empty
if (message) {
if (flags & ML_DEL_MESSAGE) {
set_keep_msg(_(no_lines_msg), 0);
}
@@ -2685,6 +2684,32 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message)
return OK;
}
/// Delete line "lnum" in the current buffer.
///
/// @note The caller of this function should probably also call
/// deleted_lines() after this.
///
/// @param message true may give a "No lines in buffer" message.
///
/// @return FAIL for failure, OK otherwise
int ml_delete(linenr_T lnum, bool message)
{
return ml_delete_flags(lnum, message ? ML_DEL_MESSAGE : 0);
}
/// Like ml_delete() but using flags (see ml_delete_int()).
///
/// @return FAIL for failure, OK otherwise
int ml_delete_flags(linenr_T lnum, int flags)
{
ml_flush_line(curbuf, false);
if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) {
return FAIL;
}
return ml_delete_int(curbuf, lnum, flags);
}
/// set the B_MARKED flag for line 'lnum'
void ml_setmarked(linenr_T lnum)
{
@@ -2853,9 +2878,9 @@ static void ml_flush_line(buf_T *buf, bool noalloc)
// that has only one line.
// Don't forget to copy the mark!
// How about handling errors???
ml_append_int(buf, lnum, new_line, new_len, false,
(int)(dp->db_index[idx] & DB_MARKED));
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);
}
}
if (!noalloc) {

View File

@@ -10,3 +10,16 @@
/// LINEEMPTY() - return true if the line is empty
#define LINEEMPTY(p) (*ml_get(p) == NUL)
// Values for the flags argument of ml_delete_flags().
enum {
ML_DEL_MESSAGE = 1, // may give a "No lines in buffer" message
// ML_DEL_UNDO = 2, // called from undo
};
// Values for the flags argument of ml_append_int().
enum {
ML_APPEND_NEW = 1, // starting to edit a new file
ML_APPEND_MARK = 2, // mark the new line
// ML_APPEND_UNDO = 4, // called from undo
};

View File

@@ -2341,7 +2341,7 @@ static void u_undoredo(bool undo, bool do_buf_event)
if (curbuf->b_ml.ml_line_count == 1) {
empty_buffer = true;
}
ml_delete(lnum, false);
ml_delete(lnum); // ML_DEL_UNDO
}
} else {
newarray = NULL;
@@ -2360,7 +2360,7 @@ static void u_undoredo(bool undo, bool do_buf_event)
if (empty_buffer && lnum == 0) {
ml_replace(1, uep->ue_array[i], true);
} else {
ml_append(lnum, uep->ue_array[i], 0, false);
ml_append_flags(lnum, uep->ue_array[i], 0, 0); // ML_APPEND_UNDO
}
xfree(uep->ue_array[i]);
}