Mercurial > vim
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; |