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 -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20220805184731.399971C0EC3%40moolenaar.net.

Raspunde prin e-mail lui