]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.0.0145: substitute that joins lines drops text properties v9.0.0145
authorBram Moolenaar <Bram@vim.org>
Fri, 5 Aug 2022 18:46:48 +0000 (19:46 +0100)
committerBram Moolenaar <Bram@vim.org>
Fri, 5 Aug 2022 18:46:48 +0000 (19:46 +0100)
Problem:    Substitute that joins lines drops text properties.
Solution:   Move text properties of the last line to the new line.

src/ex_cmds.c
src/proto/textprop.pro
src/testdir/test_textprop.vim
src/textprop.c
src/version.c

index f41bc5f6459091983cca13433b526bc288528f00..b020d61d0895226c4833f0887c9204e72af5110f 100644 (file)
@@ -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;
index 0c580df3d1597ad9eb4f4594bc41c6f37d6c3da4..3d2e68f79bd924035d5c8475829f8fd68bd629cc 100644 (file)
@@ -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);
index 4d41a15c5dab5c082be30f352479483e1a974f85..df06f38fe523d95d41d0e44371e0c6ff7cfef256 100644 (file)
@@ -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,
index 86b0dbf883d57c0c9b620c5f2a74b2b5bc06de53..9e643f718544268d8692a8591b0f484114cc507c 100644 (file)
@@ -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)
 {
index aa3af5ccdd9aeceffb7006f48fc1116bb6fcc3f3..6ca8bd08db511e5c68e8f3e89c816bf0181510c1 100644 (file)
@@ -735,6 +735,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    145,
 /**/
     144,
 /**/