summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2022-08-05 19:46:48 +0100
committerBram Moolenaar <Bram@vim.org>2022-08-05 19:46:48 +0100
commit213bbaf15afc628e5f83d1ae6526631ca8292292 (patch)
treef9b701acce08e848d8b8dde47946ab32a1b22b54
parentf4ba8bc47eb3c6b5899ef31d083b9b8f0d4ca456 (diff)
patch 9.0.0145: substitute that joins lines drops text propertiesv9.0.0145
Problem: Substitute that joins lines drops text properties. Solution: Move text properties of the last line to the new line.
-rw-r--r--src/ex_cmds.c66
-rw-r--r--src/proto/textprop.pro1
-rw-r--r--src/testdir/test_textprop.vim51
-rw-r--r--src/textprop.c24
-rw-r--r--src/version.c2
5 files changed, 132 insertions, 12 deletions
diff --git a/src/ex_cmds.c b/src/ex_cmds.c
index f41bc5f645..b020d61d08 100644
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -3709,6 +3709,9 @@ ex_substitute(exarg_T *eap)
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,6 +4052,7 @@ ex_substitute(exarg_T *eap)
#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,8 +4505,59 @@ ex_substitute(exarg_T *eap)
}
else
{
- p1 = ml_get(sub_firstlnum + nmatch - 1);
+ 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,7 +4763,10 @@ skip:
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,6 +4851,10 @@ skip:
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;
diff --git a/src/proto/textprop.pro b/src/proto/textprop.pro
index 0c580df3d1..3d2e68f79b 100644
--- a/src/proto/textprop.pro
+++ b/src/proto/textprop.pro
@@ -6,6 +6,7 @@ int prop_add_common(linenr_T start_lnum, colnr_T start_col, dict_T *dict, buf_T
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);
diff --git a/src/testdir/test_textprop.vim b/src/testdir/test_textprop.vim
index 4d41a15c5d..df06f38fe5 100644
--- a/src/testdir/test_textprop.vim
+++ b/src/testdir/test_textprop.vim
@@ -1363,15 +1363,18 @@ func Test_proptype_substitute2()
\ #{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))
+ 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,6 +1391,36 @@ func Test_proptype_substitute3()
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,
diff --git a/src/textprop.c b/src/textprop.c
index 86b0dbf883..9e643f7185 100644
--- a/src/textprop.c
+++ b/src/textprop.c
@@ -12,7 +12,6 @@
*
* 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?
@@ -683,6 +682,29 @@ set_text_props(linenr_T lnum, char_u *props, int len)
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)
{
diff --git a/src/version.c b/src/version.c
index aa3af5ccdd..6ca8bd08db 100644
--- a/src/version.c
+++ b/src/version.c
@@ -736,6 +736,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 145,
+/**/
144,
/**/
143,