comparison src/memline.c @ 20581:e529690f27bc v8.2.0844

patch 8.2.0844: text properties crossing lines not handled correctly Commit: https://github.com/vim/vim/commit/a9d4b84d97fb74061eeb42c1433e111fb58825dc Author: Bram Moolenaar <Bram@vim.org> 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)
author Bram Moolenaar <Bram@vim.org>
date Sat, 30 May 2020 15:00:03 +0200
parents a961efb326e5
children d067be761cd7
comparison
equal deleted inserted replaced
20580:213a1f6ae87d 20581:e529690f27bc
241 static void set_b0_fname(ZERO_BL *, buf_T *buf); 241 static void set_b0_fname(ZERO_BL *, buf_T *buf);
242 static void set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf); 242 static void set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf);
243 static void add_b0_fenc(ZERO_BL *b0p, buf_T *buf); 243 static void add_b0_fenc(ZERO_BL *b0p, buf_T *buf);
244 static time_t swapfile_info(char_u *); 244 static time_t swapfile_info(char_u *);
245 static int recov_file_names(char_u **, char_u *, int prepend_dot); 245 static int recov_file_names(char_u **, char_u *, int prepend_dot);
246 static int ml_delete_int(buf_T *, linenr_T, int);
247 static char_u *findswapname(buf_T *, char_u **, char_u *); 246 static char_u *findswapname(buf_T *, char_u **, char_u *);
248 static void ml_flush_line(buf_T *); 247 static void ml_flush_line(buf_T *);
249 static bhdr_T *ml_new_data(memfile_T *, int, int); 248 static bhdr_T *ml_new_data(memfile_T *, int, int);
250 static bhdr_T *ml_new_ptr(memfile_T *); 249 static bhdr_T *ml_new_ptr(memfile_T *);
251 static bhdr_T *ml_find_line(buf_T *, linenr_T, int); 250 static bhdr_T *ml_find_line(buf_T *, linenr_T, int);
2616 { 2615 {
2617 return (curbuf->b_ml.ml_flags & ML_LINE_DIRTY); 2616 return (curbuf->b_ml.ml_flags & ML_LINE_DIRTY);
2618 } 2617 }
2619 2618
2620 #ifdef FEAT_PROP_POPUP 2619 #ifdef FEAT_PROP_POPUP
2620 /*
2621 * Add text properties that continue from the previous line.
2622 */
2621 static void 2623 static void
2622 add_text_props_for_append( 2624 add_text_props_for_append(
2623 buf_T *buf, 2625 buf_T *buf,
2624 linenr_T lnum, 2626 linenr_T lnum,
2625 char_u **line, 2627 char_u **line,
2655 // Get the line above to find any props that continue in the next 2657 // Get the line above to find any props that continue in the next
2656 // line. 2658 // line.
2657 count = get_text_props(buf, lnum, &props, FALSE); 2659 count = get_text_props(buf, lnum, &props, FALSE);
2658 for (n = 0; n < count; ++n) 2660 for (n = 0; n < count; ++n)
2659 { 2661 {
2660 mch_memmove(&prop, props + n * sizeof(textprop_T), sizeof(textprop_T)); 2662 mch_memmove(&prop, props + n * sizeof(textprop_T),
2663 sizeof(textprop_T));
2661 if (prop.tp_flags & TP_FLAG_CONT_NEXT) 2664 if (prop.tp_flags & TP_FLAG_CONT_NEXT)
2662 { 2665 {
2663 if (round == 2) 2666 if (round == 2)
2664 { 2667 {
2665 prop.tp_flags |= TP_FLAG_CONT_PREV; 2668 prop.tp_flags |= TP_FLAG_CONT_PREV;
2666 prop.tp_col = 1; 2669 prop.tp_col = 1;
2667 prop.tp_len = *len; 2670 prop.tp_len = *len; // not exactly the right length
2668 mch_memmove(new_line + *len + new_prop_count * sizeof(textprop_T), &prop, sizeof(textprop_T)); 2671 mch_memmove(new_line + *len + new_prop_count
2672 * sizeof(textprop_T), &prop, sizeof(textprop_T));
2669 } 2673 }
2670 ++new_prop_count; 2674 ++new_prop_count;
2671 } 2675 }
2672 } 2676 }
2673 } 2677 }
2681 ml_append_int( 2685 ml_append_int(
2682 buf_T *buf, 2686 buf_T *buf,
2683 linenr_T lnum, // append after this line (can be 0) 2687 linenr_T lnum, // append after this line (can be 0)
2684 char_u *line_arg, // text of the new line 2688 char_u *line_arg, // text of the new line
2685 colnr_T len_arg, // length of line, including NUL, or 0 2689 colnr_T len_arg, // length of line, including NUL, or 0
2686 int newfile, // flag, see above 2690 int flags) // ML_APPEND_ flags
2687 int mark) // mark the new line
2688 { 2691 {
2689 char_u *line = line_arg; 2692 char_u *line = line_arg;
2690 colnr_T len = len_arg; 2693 colnr_T len = len_arg;
2691 int i; 2694 int i;
2692 int line_count; // number of indexes in current block 2695 int line_count; // number of indexes in current block
2714 2717
2715 if (len == 0) 2718 if (len == 0)
2716 len = (colnr_T)STRLEN(line) + 1; // space needed for the text 2719 len = (colnr_T)STRLEN(line) + 1; // space needed for the text
2717 2720
2718 #ifdef FEAT_PROP_POPUP 2721 #ifdef FEAT_PROP_POPUP
2719 if (curbuf->b_has_textprop && lnum > 0) 2722 if (curbuf->b_has_textprop && lnum > 0 && !(flags & ML_APPEND_UNDO))
2720 // Add text properties that continue from the previous line. 2723 // Add text properties that continue from the previous line.
2721 add_text_props_for_append(buf, lnum, &line, &len, &tofree); 2724 add_text_props_for_append(buf, lnum, &line, &len, &tofree);
2722 #endif 2725 #endif
2723 2726
2724 space_needed = len + INDEX_SIZE; // space needed for text + index 2727 space_needed = len + INDEX_SIZE; // space needed for text + index
2813 2816
2814 /* 2817 /*
2815 * copy the text into the block 2818 * copy the text into the block
2816 */ 2819 */
2817 mch_memmove((char *)dp + dp->db_index[db_idx + 1], line, (size_t)len); 2820 mch_memmove((char *)dp + dp->db_index[db_idx + 1], line, (size_t)len);
2818 if (mark) 2821 if (flags & ML_APPEND_MARK)
2819 dp->db_index[db_idx + 1] |= DB_MARKED; 2822 dp->db_index[db_idx + 1] |= DB_MARKED;
2820 2823
2821 /* 2824 /*
2822 * Mark the block dirty. 2825 * Mark the block dirty.
2823 */ 2826 */
2824 buf->b_ml.ml_flags |= ML_LOCKED_DIRTY; 2827 buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
2825 if (!newfile) 2828 if (!(flags & ML_APPEND_NEW))
2826 buf->b_ml.ml_flags |= ML_LOCKED_POS; 2829 buf->b_ml.ml_flags |= ML_LOCKED_POS;
2827 } 2830 }
2828 else // not enough space in data block 2831 else // not enough space in data block
2829 { 2832 {
2830 long line_count_left, line_count_right; 2833 long line_count_left, line_count_right;
2889 } 2892 }
2890 } 2893 }
2891 } 2894 }
2892 2895
2893 page_count = ((space_needed + HEADER_SIZE) + page_size - 1) / page_size; 2896 page_count = ((space_needed + HEADER_SIZE) + page_size - 1) / page_size;
2894 if ((hp_new = ml_new_data(mfp, newfile, page_count)) == NULL) 2897 if ((hp_new = ml_new_data(mfp, flags & ML_APPEND_NEW, page_count))
2898 == NULL)
2895 { 2899 {
2896 // correct line counts in pointer blocks 2900 // correct line counts in pointer blocks
2897 --(buf->b_ml.ml_locked_lineadd); 2901 --(buf->b_ml.ml_locked_lineadd);
2898 --(buf->b_ml.ml_locked_high); 2902 --(buf->b_ml.ml_locked_high);
2899 goto theend; 2903 goto theend;
2925 if (!in_left) 2929 if (!in_left)
2926 { 2930 {
2927 dp_right->db_txt_start -= len; 2931 dp_right->db_txt_start -= len;
2928 dp_right->db_free -= len + INDEX_SIZE; 2932 dp_right->db_free -= len + INDEX_SIZE;
2929 dp_right->db_index[0] = dp_right->db_txt_start; 2933 dp_right->db_index[0] = dp_right->db_txt_start;
2930 if (mark) 2934 if (flags & ML_APPEND_MARK)
2931 dp_right->db_index[0] |= DB_MARKED; 2935 dp_right->db_index[0] |= DB_MARKED;
2932 2936
2933 mch_memmove((char *)dp_right + dp_right->db_txt_start, 2937 mch_memmove((char *)dp_right + dp_right->db_txt_start,
2934 line, (size_t)len); 2938 line, (size_t)len);
2935 ++line_count_right; 2939 ++line_count_right;
2966 if (in_left) 2970 if (in_left)
2967 { 2971 {
2968 dp_left->db_txt_start -= len; 2972 dp_left->db_txt_start -= len;
2969 dp_left->db_free -= len + INDEX_SIZE; 2973 dp_left->db_free -= len + INDEX_SIZE;
2970 dp_left->db_index[line_count_left] = dp_left->db_txt_start; 2974 dp_left->db_index[line_count_left] = dp_left->db_txt_start;
2971 if (mark) 2975 if (flags & ML_APPEND_MARK)
2972 dp_left->db_index[line_count_left] |= DB_MARKED; 2976 dp_left->db_index[line_count_left] |= DB_MARKED;
2973 mch_memmove((char *)dp_left + dp_left->db_txt_start, 2977 mch_memmove((char *)dp_left + dp_left->db_txt_start,
2974 line, (size_t)len); 2978 line, (size_t)len);
2975 ++line_count_left; 2979 ++line_count_left;
2976 } 2980 }
2997 * The old one (hp, in ml_locked) gets a positive blocknumber if 3001 * The old one (hp, in ml_locked) gets a positive blocknumber if
2998 * we changed it and we are not editing a new file. 3002 * we changed it and we are not editing a new file.
2999 */ 3003 */
3000 if (lines_moved || in_left) 3004 if (lines_moved || in_left)
3001 buf->b_ml.ml_flags |= ML_LOCKED_DIRTY; 3005 buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
3002 if (!newfile && db_idx >= 0 && in_left) 3006 if (!(flags & ML_APPEND_NEW) && db_idx >= 0 && in_left)
3003 buf->b_ml.ml_flags |= ML_LOCKED_POS; 3007 buf->b_ml.ml_flags |= ML_LOCKED_POS;
3004 mf_put(mfp, hp_new, TRUE, FALSE); 3008 mf_put(mfp, hp_new, TRUE, FALSE);
3005 3009
3006 /* 3010 /*
3007 * flush the old data block 3011 * flush the old data block
3205 ml_append_flush( 3209 ml_append_flush(
3206 buf_T *buf, 3210 buf_T *buf,
3207 linenr_T lnum, // append after this line (can be 0) 3211 linenr_T lnum, // append after this line (can be 0)
3208 char_u *line, // text of the new line 3212 char_u *line, // text of the new line
3209 colnr_T len, // length of line, including NUL, or 0 3213 colnr_T len, // length of line, including NUL, or 0
3210 int newfile) // flag, see above 3214 int flags) // ML_APPEND_ flags
3211 { 3215 {
3212 if (lnum > buf->b_ml.ml_line_count) 3216 if (lnum > buf->b_ml.ml_line_count)
3213 return FAIL; // lnum out of range 3217 return FAIL; // lnum out of range
3214 3218
3215 if (buf->b_ml.ml_line_lnum != 0) 3219 if (buf->b_ml.ml_line_lnum != 0)
3222 may_invoke_listeners(buf, lnum + 1, lnum + 1, 1); 3226 may_invoke_listeners(buf, lnum + 1, lnum + 1, 1);
3223 if (buf->b_ml.ml_line_lnum != 0) 3227 if (buf->b_ml.ml_line_lnum != 0)
3224 ml_flush_line(buf); 3228 ml_flush_line(buf);
3225 #endif 3229 #endif
3226 3230
3227 return ml_append_int(buf, lnum, line, len, newfile, FALSE); 3231 return ml_append_int(buf, lnum, line, len, flags);
3228 } 3232 }
3229 3233
3230 /* 3234 /*
3231 * Append a line after lnum (may be 0 to insert a line in front of the file). 3235 * Append a line after lnum (may be 0 to insert a line in front of the file).
3232 * "line" does not need to be allocated, but can't be another line in a 3236 * "line" does not need to be allocated, but can't be another line in a
3233 * buffer, unlocking may make it invalid. 3237 * buffer, unlocking may make it invalid.
3234 * 3238 *
3235 * newfile: TRUE when starting to edit a new file, meaning that pe_old_lnum 3239 * "newfile": TRUE when starting to edit a new file, meaning that pe_old_lnum
3236 * will be set for recovery 3240 * will be set for recovery
3237 * Check: The caller of this function should probably also call 3241 * Check: The caller of this function should probably also call
3238 * appended_lines(). 3242 * appended_lines().
3239 * 3243 *
3240 * return FAIL for failure, OK otherwise 3244 * return FAIL for failure, OK otherwise
3244 linenr_T lnum, // append after this line (can be 0) 3248 linenr_T lnum, // append after this line (can be 0)
3245 char_u *line, // text of the new line 3249 char_u *line, // text of the new line
3246 colnr_T len, // length of new line, including NUL, or 0 3250 colnr_T len, // length of new line, including NUL, or 0
3247 int newfile) // flag, see above 3251 int newfile) // flag, see above
3248 { 3252 {
3253 return ml_append_flags(lnum, line, len, newfile ? ML_APPEND_NEW : 0);
3254 }
3255
3256 int
3257 ml_append_flags(
3258 linenr_T lnum, // append after this line (can be 0)
3259 char_u *line, // text of the new line
3260 colnr_T len, // length of new line, including NUL, or 0
3261 int flags) // ML_APPEND_ values
3262 {
3249 // When starting up, we might still need to create the memfile 3263 // When starting up, we might still need to create the memfile
3250 if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL) 3264 if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL)
3251 return FAIL; 3265 return FAIL;
3252 return ml_append_flush(curbuf, lnum, line, len, newfile); 3266 return ml_append_flush(curbuf, lnum, line, len, flags);
3253 } 3267 }
3268
3254 3269
3255 #if defined(FEAT_SPELL) || defined(FEAT_QUICKFIX) || defined(PROTO) 3270 #if defined(FEAT_SPELL) || defined(FEAT_QUICKFIX) || defined(PROTO)
3256 /* 3271 /*
3257 * Like ml_append() but for an arbitrary buffer. The buffer must already have 3272 * Like ml_append() but for an arbitrary buffer. The buffer must already have
3258 * a memline. 3273 * a memline.
3265 colnr_T len, // length of new line, including NUL, or 0 3280 colnr_T len, // length of new line, including NUL, or 0
3266 int newfile) // flag, see above 3281 int newfile) // flag, see above
3267 { 3282 {
3268 if (buf->b_ml.ml_mfp == NULL) 3283 if (buf->b_ml.ml_mfp == NULL)
3269 return FAIL; 3284 return FAIL;
3270 return ml_append_flush(buf, lnum, line, len, newfile); 3285 return ml_append_flush(buf, lnum, line, len, newfile ? ML_APPEND_NEW : 0);
3271 } 3286 }
3272 #endif 3287 #endif
3273 3288
3274 /* 3289 /*
3275 * Replace line lnum, with buffering, in current buffer. 3290 * Replace line lnum, with buffering, in current buffer.
3485 } 3500 }
3486 #endif 3501 #endif
3487 3502
3488 /* 3503 /*
3489 * Delete line "lnum" in the current buffer. 3504 * Delete line "lnum" in the current buffer.
3490 * When "message" is TRUE may give a "No lines in buffer" message. 3505 * When "flags" has ML_DEL_MESSAGE may give a "No lines in buffer" message.
3491 * 3506 * When "flags" has ML_DEL_UNDO this is called from undo.
3492 * Check: The caller of this function should probably also call
3493 * deleted_lines() after this.
3494 * 3507 *
3495 * return FAIL for failure, OK otherwise 3508 * return FAIL for failure, OK otherwise
3496 */ 3509 */
3497 int
3498 ml_delete(linenr_T lnum, int message)
3499 {
3500 ml_flush_line(curbuf);
3501 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count)
3502 return FAIL;
3503
3504 #ifdef FEAT_EVAL
3505 // When inserting above recorded changes: flush the changes before changing
3506 // the text.
3507 may_invoke_listeners(curbuf, lnum, lnum + 1, -1);
3508 #endif
3509
3510 return ml_delete_int(curbuf, lnum, message);
3511 }
3512
3513 static int 3510 static int
3514 ml_delete_int(buf_T *buf, linenr_T lnum, int message) 3511 ml_delete_int(buf_T *buf, linenr_T lnum, int flags)
3515 { 3512 {
3516 bhdr_T *hp; 3513 bhdr_T *hp;
3517 memfile_T *mfp; 3514 memfile_T *mfp;
3518 DATA_BL *dp; 3515 DATA_BL *dp;
3519 PTR_BL *pp; 3516 PTR_BL *pp;
3537 /* 3534 /*
3538 * If the file becomes empty the last line is replaced by an empty line. 3535 * If the file becomes empty the last line is replaced by an empty line.
3539 */ 3536 */
3540 if (buf->b_ml.ml_line_count == 1) // file becomes empty 3537 if (buf->b_ml.ml_line_count == 1) // file becomes empty
3541 { 3538 {
3542 if (message 3539 if ((flags & ML_DEL_MESSAGE)
3543 #ifdef FEAT_NETBEANS_INTG 3540 #ifdef FEAT_NETBEANS_INTG
3544 && !netbeansSuppressNoLines 3541 && !netbeansSuppressNoLines
3545 #endif 3542 #endif
3546 ) 3543 )
3547 set_keep_msg((char_u *)_(no_lines_msg), 0); 3544 set_keep_msg((char_u *)_(no_lines_msg), 0);
3584 netbeans_removed(buf, lnum, 0, (long)line_size); 3581 netbeans_removed(buf, lnum, 0, (long)line_size);
3585 #endif 3582 #endif
3586 #ifdef FEAT_PROP_POPUP 3583 #ifdef FEAT_PROP_POPUP
3587 // If there are text properties, make a copy, so that we can update 3584 // If there are text properties, make a copy, so that we can update
3588 // properties in preceding and following lines. 3585 // properties in preceding and following lines.
3589 if (buf->b_has_textprop) 3586 if (buf->b_has_textprop && !(flags & ML_DEL_UNDO))
3590 { 3587 {
3591 size_t textlen = STRLEN((char_u *)dp + line_start) + 1; 3588 size_t textlen = STRLEN((char_u *)dp + line_start) + 1;
3592 3589
3593 if ((long)textlen < line_size) 3590 if ((long)textlen < line_size)
3594 { 3591 {
3697 #endif 3694 #endif
3698 return ret; 3695 return ret;
3699 } 3696 }
3700 3697
3701 /* 3698 /*
3699 * Delete line "lnum" in the current buffer.
3700 * When "message" is TRUE may give a "No lines in buffer" message.
3701 *
3702 * Check: The caller of this function should probably also call
3703 * deleted_lines() after this.
3704 *
3705 * return FAIL for failure, OK otherwise
3706 */
3707 int
3708 ml_delete(linenr_T lnum, int message)
3709 {
3710 return ml_delete_flags(lnum, message ? ML_DEL_MESSAGE : 0);
3711 }
3712
3713 /*
3714 * Like ml_delete() but using flags (see ml_delete_int()).
3715 */
3716 int
3717 ml_delete_flags(linenr_T lnum, int flags)
3718 {
3719 ml_flush_line(curbuf);
3720 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count)
3721 return FAIL;
3722
3723 #ifdef FEAT_EVAL
3724 // When inserting above recorded changes: flush the changes before changing
3725 // the text.
3726 may_invoke_listeners(curbuf, lnum, lnum + 1, -1);
3727 #endif
3728
3729 return ml_delete_int(curbuf, lnum, flags);
3730 }
3731
3732 /*
3702 * set the DB_MARKED flag for line 'lnum' 3733 * set the DB_MARKED flag for line 'lnum'
3703 */ 3734 */
3704 void 3735 void
3705 ml_setmarked(linenr_T lnum) 3736 ml_setmarked(linenr_T lnum)
3706 { 3737 {
3903 * last line in a buffer, which causes trouble for a buffer 3934 * last line in a buffer, which causes trouble for a buffer
3904 * that has only one line. 3935 * that has only one line.
3905 * Don't forget to copy the mark! 3936 * Don't forget to copy the mark!
3906 */ 3937 */
3907 // How about handling errors??? 3938 // How about handling errors???
3908 (void)ml_append_int(buf, lnum, new_line, new_len, FALSE, 3939 (void)ml_append_int(buf, lnum, new_line, new_len,
3909 (dp->db_index[idx] & DB_MARKED)); 3940 (dp->db_index[idx] & DB_MARKED) ? ML_APPEND_MARK : 0);
3910 (void)ml_delete_int(buf, lnum, FALSE); 3941 (void)ml_delete_int(buf, lnum, 0);
3911 } 3942 }
3912 } 3943 }
3913 vim_free(new_line); 3944 vim_free(new_line);
3914 3945
3915 entered = FALSE; 3946 entered = FALSE;