To: vim_dev@googlegroups.com Subject: Patch 9.0.0145 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.0145 Problem: Substitute that joins lines drops text properties. Solution: Move text properties of the last line to the new line. Files: src/ex_cmds.c, src/textprop.c, src/proto/textprop.pro, src/testdir/test_textprop.vim *** ../vim-9.0.0144/src/ex_cmds.c 2022-07-30 16:54:01.863698294 +0100 --- src/ex_cmds.c 2022-08-05 19:22:50.993354784 +0100 *************** *** 3709,3714 **** --- 3709,3717 ---- int save_ma = 0; int save_sandbox = 0; #endif + #ifdef FEAT_PROP_POPUP + textprop_T *text_props = NULL; + #endif cmd = eap->arg; if (!global_busy) *************** *** 4049,4054 **** --- 4052,4058 ---- #ifdef FEAT_PROP_POPUP int apc_flags = APC_SAVE_FOR_UNDO | APC_SUBSTITUTE; colnr_T total_added = 0; + int text_prop_count = 0; #endif /* *************** *** 4501,4508 **** } else { ! p1 = ml_get(sub_firstlnum + nmatch - 1); nmatch_tl += nmatch - 1; } copy_len = regmatch.startpos[0].col - copycol; needed_len = copy_len + ((unsigned)STRLEN(p1) --- 4505,4563 ---- } else { ! linenr_T lastlnum = sub_firstlnum + nmatch - 1; ! #ifdef FEAT_PROP_POPUP ! if (curbuf->b_has_textprop) ! { ! char_u *prop_start; ! ! // Props in the first line may be shortened or deleted ! if (adjust_prop_columns(lnum, ! total_added + regmatch.startpos[0].col, ! -MAXCOL, apc_flags)) ! apc_flags &= ~APC_SAVE_FOR_UNDO; ! total_added -= (colnr_T)STRLEN( ! sub_firstline + regmatch.startpos[0].col); ! ! // Props in the last line may be moved or deleted ! if (adjust_prop_columns(lastlnum, ! 0, -regmatch.endpos[0].col, apc_flags)) ! // When text properties are changed, need to save ! // for undo first, unless done already. ! apc_flags &= ~APC_SAVE_FOR_UNDO; ! ! // Copy the text props of the last line, they will be ! // later appended to the changed line. ! text_prop_count = get_text_props(curbuf, lastlnum, ! &prop_start, FALSE); ! if (text_prop_count > 0) ! { ! // TODO: what when we already did this? ! vim_free(text_props); ! text_props = ALLOC_MULT(textprop_T, ! text_prop_count); ! if (text_props != NULL) ! { ! int pi; ! ! mch_memmove(text_props, prop_start, ! text_prop_count * sizeof(textprop_T)); ! // After joining the text prop columns will ! // increase. ! for (pi = 0; pi < text_prop_count; ++pi) ! text_props[pi].tp_col += ! regmatch.startpos[0].col + sublen - 1; ! } ! } ! } ! #endif ! p1 = ml_get(lastlnum); nmatch_tl += nmatch - 1; + #ifdef FEAT_PROP_POPUP + if (curbuf->b_has_textprop) + total_added += (colnr_T)STRLEN( + p1 + regmatch.endpos[0].col); + #endif } copy_len = regmatch.startpos[0].col - copycol; needed_len = copy_len + ((unsigned)STRLEN(p1) *************** *** 4708,4714 **** if (u_savesub(lnum) != OK) break; ml_replace(lnum, new_start, TRUE); ! if (nmatch_tl > 0) { /* --- 4763,4772 ---- if (u_savesub(lnum) != OK) break; ml_replace(lnum, new_start, TRUE); ! #ifdef FEAT_PROP_POPUP ! if (text_props != NULL) ! add_text_props(lnum, text_props, text_prop_count); ! #endif if (nmatch_tl > 0) { /* *************** *** 4793,4798 **** --- 4851,4860 ---- outofmem: vim_free(sub_firstline); // may have to free allocated copy of the line + #ifdef FEAT_PROP_POPUP + vim_free(text_props); + #endif + // ":s/pat//n" doesn't move the cursor if (subflags.do_count) curwin->w_cursor = old_cursor; *** ../vim-9.0.0144/src/textprop.c 2022-08-05 17:04:43.402914763 +0100 --- src/textprop.c 2022-08-05 19:45:38.890602955 +0100 *************** *** 12,18 **** * * TODO: * - Adjust text property column and length when text is inserted/deleted. - * -> a :substitute with a multi-line match * -> search for changed_bytes() from misc1.c * -> search for mark_col_adjust() * - Perhaps we only need TP_FLAG_CONT_NEXT and can drop TP_FLAG_CONT_PREV? --- 12,17 ---- *************** *** 683,688 **** --- 682,710 ---- curbuf->b_ml.ml_flags |= ML_LINE_DIRTY; } + /* + * Add "text_props" with "text_prop_count" text propertis to line "lnum". + */ + void + add_text_props(linenr_T lnum, textprop_T *text_props, int text_prop_count) + { + char_u *text; + char_u *newtext; + int proplen = text_prop_count * (int)sizeof(textprop_T); + + text = ml_get(lnum); + newtext = alloc(curbuf->b_ml.ml_line_len + proplen); + if (newtext == NULL) + return; + mch_memmove(newtext, text, curbuf->b_ml.ml_line_len); + mch_memmove(newtext + curbuf->b_ml.ml_line_len, text_props, proplen); + if (curbuf->b_ml.ml_flags & (ML_LINE_DIRTY | ML_ALLOCATED)) + vim_free(curbuf->b_ml.ml_line_ptr); + curbuf->b_ml.ml_line_ptr = newtext; + curbuf->b_ml.ml_line_len += proplen; + curbuf->b_ml.ml_flags |= ML_LINE_DIRTY; + } + static proptype_T * find_type_by_id(hashtab_T *ht, int id) { *** ../vim-9.0.0144/src/proto/textprop.pro 2022-08-01 22:18:30.483764954 +0100 --- src/proto/textprop.pro 2022-08-05 19:00:59.739298487 +0100 *************** *** 6,11 **** --- 6,12 ---- int get_text_props(buf_T *buf, linenr_T lnum, char_u **props, int will_change); int count_props(linenr_T lnum, int only_starting, int last_line); int find_visible_prop(win_T *wp, int type_id, int id, textprop_T *prop, linenr_T *found_lnum); + void add_text_props(linenr_T lnum, textprop_T *text_props, int text_prop_count); proptype_T *text_prop_type_by_id(buf_T *buf, int id); void f_prop_clear(typval_T *argvars, typval_T *rettv); void f_prop_find(typval_T *argvars, typval_T *rettv); *** ../vim-9.0.0144/src/testdir/test_textprop.vim 2022-08-05 17:04:43.406914758 +0100 --- src/testdir/test_textprop.vim 2022-08-05 19:42:41.538995550 +0100 *************** *** 1363,1377 **** \ #{type_bufnr: 0, id: 0, col: 50, end: 1, type: 'number', length: 4, start: 1}] " TODO ! return ! " Add some text in between ! %s/\s\+/ /g ! call assert_equal(expected, prop_list(1) + prop_list(2) + prop_list(3)) ! ! " remove some text ! :1s/[a-z]\{3\}//g ! let expected = [{'id': 0, 'col': 10, 'end': 1, 'type': 'number', 'length': 3, 'start': 1}] ! call assert_equal(expected, prop_list(1)) bwipe! endfunc --- 1363,1380 ---- \ #{type_bufnr: 0, id: 0, col: 50, end: 1, type: 'number', length: 4, start: 1}] " TODO ! if 0 ! " Add some text in between ! %s/\s\+/ /g ! call assert_equal(expected, prop_list(1) + prop_list(2) + prop_list(3)) ! ! " remove some text ! :1s/[a-z]\{3\}//g ! let expected = [{'id': 0, 'col': 10, 'end': 1, 'type': 'number', 'length': 3, 'start': 1}] ! call assert_equal(expected, prop_list(1)) ! endif ! ! call prop_type_delete('number') bwipe! endfunc *************** *** 1388,1393 **** --- 1391,1426 ---- bwipe! endfunc + func Test_proptype_substitute_join() + new + call setline(1, [ + \ 'This is some end', + \ 'start is highlighted end', + \ 'some is highlighted', + \ 'start is also highlighted']) + + call prop_type_add('number', {'highlight': 'ErrorMsg'}) + + call prop_add(1, 6, {'length': 2, 'type': 'number'}) + call prop_add(2, 7, {'length': 2, 'type': 'number'}) + call prop_add(3, 6, {'length': 2, 'type': 'number'}) + call prop_add(4, 7, {'length': 2, 'type': 'number'}) + " The highlighted "is" in line 1, 2 and 4 is kept and ajudsted. + " The highlighted "is" in line 3 is deleted. + let expected = [ + \ #{type_bufnr: 0, id: 0, col: 6, end: 1, type: 'number', length: 2, start: 1}, + \ #{type_bufnr: 0, id: 0, col: 21, end: 1, type: 'number', length: 2, start: 1}, + \ #{type_bufnr: 0, id: 0, col: 43, end: 1, type: 'number', length: 2, start: 1}] + + s/end\nstart/joined/ + s/end\n.*\nstart/joined/ + call assert_equal('This is some joined is highlighted joined is also highlighted', getline(1)) + call assert_equal(expected, prop_list(1)) + + call prop_type_delete('number') + bwipe! + endfunc + func SaveOptions() let d = #{tabstop: &tabstop, \ softtabstop: &softtabstop, *** ../vim-9.0.0144/src/version.c 2022-08-05 17:04:43.406914758 +0100 --- src/version.c 2022-08-05 19:43:04.402941494 +0100 *************** *** 737,738 **** --- 737,740 ---- { /* Add new patch number below this line */ + /**/ + 145, /**/ -- hundred-and-one symptoms of being an internet addict: 245. You use Real Audio to listen to a radio station from a distant city rather than turn on your stereo system. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///