Patch 8.2.3356
Problem:    Adding many text properties requires a lot of function calls.
Solution:   Add the prop_add_list() function. (Yegappan Lakshmanan,
            closes #8751)
Files:      runtime/doc/eval.txt, runtime/doc/textprop.txt,
            runtime/doc/usr_41.txt, src/evalfunc.c, src/proto/textprop.pro,
            src/testdir/test_textprop.vim, src/testdir/test_vim9_builtin.vim,
            src/textprop.c


*** ../vim-8.2.3355/runtime/doc/eval.txt        2021-08-10 10:23:22.280476685 
+0200
--- runtime/doc/eval.txt        2021-08-16 21:33:59.131967420 +0200
***************
*** 2784,2790 ****
  prompt_setcallback({buf}, {expr}) none        set prompt callback function
  prompt_setinterrupt({buf}, {text}) none       set prompt interrupt function
  prompt_setprompt({buf}, {text}) none  set prompt text
! prop_add({lnum}, {col}, {props})  none        add a text property
  prop_clear({lnum} [, {lnum-end} [, {props}]])
                                none    remove all text properties
  prop_find({props} [, {direction}])
--- 2801,2809 ----
  prompt_setcallback({buf}, {expr}) none        set prompt callback function
  prompt_setinterrupt({buf}, {text}) none       set prompt interrupt function
  prompt_setprompt({buf}, {text}) none  set prompt text
! prop_add({lnum}, {col}, {props})  none        add one text property
! prop_add_list({props}, [[{lnum}, {col}, {end-lnum}, {end-col}], ...])
!                               none    add multiple text properties
  prop_clear({lnum} [, {lnum-end} [, {props}]])
                                none    remove all text properties
  prop_find({props} [, {direction}])
*** ../vim-8.2.3355/runtime/doc/textprop.txt    2021-07-28 13:30:12.208929917 
+0200
--- runtime/doc/textprop.txt    2021-08-16 21:34:57.767789811 +0200
***************
*** 108,113 ****
--- 108,116 ----
  Manipulating text properties:
  
  prop_add({lnum}, {col}, {props})      add a text property
+ prop_add_list({props}, [[{lnum}, {col}, {end-lnum}, {end-col}], ...])
+                                       add a text property at multiple
+                                       positions.
  prop_clear({lnum} [, {lnum-end} [, {bufnr}]])
                                        remove all text properties
  prop_find({props} [, {direction}])    search for a text property
***************
*** 126,132 ****
                   length       length of text in bytes, can only be used
                                for a property that does not continue in
                                another line; can be zero
!                  end_lnum     line number for the end of text
                   end_col      column just after the text; not used when
                                "length" is present; when {col} and "end_col"
                                are equal, and "end_lnum" is omitted or equal
--- 129,135 ----
                   length       length of text in bytes, can only be used
                                for a property that does not continue in
                                another line; can be zero
!                  end_lnum     line number for the end of text (inclusive)
                   end_col      column just after the text; not used when
                                "length" is present; when {col} and "end_col"
                                are equal, and "end_lnum" is omitted or equal
***************
*** 158,163 ****
--- 161,195 ----
                Can also be used as a |method|: >
                        GetLnum()->prop_add(col, props)
  
+                                               *prop_add_list()*
+ prop_add_list({props}, [[{lnum}, {col}, {end-lnum}, {end-col}], ...])
+               Similar to prop_add(), but attaches a text property at
+               multiple positions in a buffer.
+ 
+               {props} is a dictionary with these fields:
+                  bufnr        buffer to add the property to; when omitted
+                               the current buffer is used
+                  id           user defined ID for the property; must be a
+                               number; when omitted zero is used
+                  type         name of the text property type
+               All fields except "type" are optional.
+ 
+               The second argument is a List of Lists where each list
+               specifies the starting and ending position of the text.  The
+               first two items {lnum} and {col} specify the starting position
+               of the text where the property will be attached and the last
+               two items {end-lnum} and {end-col} specify the position just
+               after the text.
+ 
+               Example:
+                       call prop_add_list(#{type: 'MyProp', id: 2},
+                                       \ [[1, 4, 1, 7],
+                                       \  [1, 15, 1, 20],
+                                       \  [2, 30, 3, 30]]
+ 
+               Can also be used as a |method|: >
+                       GetProp()->prop_add_list([[1, 1, 1, 2], [1, 4, 1, 8]])
+ 
  
  prop_clear({lnum} [, {lnum-end} [, {props}]])         *prop_clear()*
                Remove all text properties from line {lnum}.
*** ../vim-8.2.3355/runtime/doc/usr_41.txt      2021-08-08 14:41:48.719930705 
+0200
--- runtime/doc/usr_41.txt      2021-08-16 21:28:45.284918529 +0200
***************
*** 1153,1158 ****
--- 1161,1167 ----
  
  Text Properties:                              *text-property-functions*
        prop_add()              attach a property at a position
+       prop_add_list()         attach a property at multiple positions
        prop_clear()            remove all properties from a line or lines
        prop_find()             search for a property
        prop_list()             return a list of all properties in a line
*** ../vim-8.2.3355/src/evalfunc.c      2021-08-13 17:48:19.178894940 +0200
--- src/evalfunc.c      2021-08-16 21:28:45.288918517 +0200
***************
*** 708,713 ****
--- 708,714 ----
  static argcheck_T arg2_buffer_string[] = {arg_buffer, arg_string};
  static argcheck_T arg2_chan_or_job_dict[] = {arg_chan_or_job, arg_dict_any};
  static argcheck_T arg2_chan_or_job_string[] = {arg_chan_or_job, arg_string};
+ static argcheck_T arg2_dict_any_list_any[] = {arg_dict_any, arg_list_any};
  static argcheck_T arg2_dict_any_string_or_nr[] = {arg_dict_any, 
arg_string_or_nr};
  static argcheck_T arg2_dict_string[] = {arg_dict_any, arg_string};
  static argcheck_T arg2_float_or_nr[] = {arg_float_or_nr, arg_float_or_nr};
***************
*** 1740,1745 ****
--- 1741,1748 ----
                        ret_void,           JOB_FUNC(f_prompt_setprompt)},
      {"prop_add",      3, 3, FEARG_1,      arg3_number_number_dict,
                        ret_void,           PROP_FUNC(f_prop_add)},
+     {"prop_add_list", 2, 2, FEARG_1,      arg2_dict_any_list_any,
+                       ret_void,           PROP_FUNC(f_prop_add_list)},
      {"prop_clear",    1, 3, FEARG_1,      arg3_number_number_dict,
                        ret_void,           PROP_FUNC(f_prop_clear)},
      {"prop_find",     1, 2, FEARG_1,      arg2_dict_string,
*** ../vim-8.2.3355/src/proto/textprop.pro      2020-05-30 15:31:57.858700863 
+0200
--- src/proto/textprop.pro      2021-08-16 21:28:45.288918517 +0200
***************
*** 1,6 ****
--- 1,7 ----
  /* textprop.c */
  int find_prop_type_id(char_u *name, buf_T *buf);
  void f_prop_add(typval_T *argvars, typval_T *rettv);
+ void f_prop_add_list(typval_T *argvars, typval_T *rettv);
  void prop_add_common(linenr_T start_lnum, colnr_T start_col, dict_T *dict, 
buf_T *default_buf, typval_T *dict_arg);
  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);
*** ../vim-8.2.3355/src/testdir/test_textprop.vim       2021-08-15 
15:04:37.188080291 +0200
--- src/testdir/test_textprop.vim       2021-08-16 21:28:45.288918517 +0200
***************
*** 339,344 ****
--- 339,379 ----
    bwipe!
  endfunc
  
+ " Test for the prop_add_list() function
+ func Test_prop_add_list()
+   new
+   call AddPropTypes()
+   call setline(1, ['one one one', 'two two two', 'six six six', 'ten ten 
ten'])
+   call prop_add_list(#{type: 'one', id: 2},
+         \ [[1, 1, 1, 3], [2, 5, 2, 7], [3, 6, 4, 6]])
+   call assert_equal([#{id: 2, col: 1, type_bufnr: 0, end: 1, type: 'one',
+         \ length: 2, start: 1}], prop_list(1))
+   call assert_equal([#{id: 2, col: 5, type_bufnr: 0, end: 1, type: 'one',
+         \ length: 2, start: 1}], prop_list(2))
+   call assert_equal([#{id: 2, col: 6, type_bufnr: 0, end: 0, type: 'one',
+         \ length: 7, start: 1}], prop_list(3))
+   call assert_equal([#{id: 2, col: 1, type_bufnr: 0, end: 1, type: 'one',
+         \ length: 5, start: 0}], prop_list(4))
+   call assert_fails('call prop_add_list([1, 2], [[1, 1, 3]])', 'E1206:')
+   call assert_fails('call prop_add_list({}, {})', 'E1211:')
+   call assert_fails('call prop_add_list({}, [[1, 1, 3]])', 'E965:')
+   call assert_fails('call prop_add_list(#{type: "abc"}, [[1, 1, 1, 3]])', 
'E971:')
+   call assert_fails('call prop_add_list(#{type: "one"}, [[]])', 'E474:')
+   call assert_fails('call prop_add_list(#{type: "one"}, [[1, 1, 1, 1], {}])', 
'E714:')
+   call assert_fails('call prop_add_list(#{type: "one"}, [[1, 1, "a"]])', 
'E474:')
+   call assert_fails('call prop_add_list(#{type: "one"}, [[2, 2]])', 'E474:')
+   call assert_fails('call prop_add_list(#{type: "one"}, [[1, 1, 2], [2, 
2]])', 'E474:')
+   call assert_fails('call prop_add_list(#{type: "one"}, [[1, 1, 1, 2], [4, 1, 
5, 2]])', 'E966:')
+   call assert_fails('call prop_add_list(#{type: "one"}, [[3, 1, 1, 2]])', 
'E966:')
+   call assert_fails('call prop_add_list(#{type: "one"}, [[2, 2, 2, 2], [3, 
20, 3, 22]])', 'E964:')
+   call assert_fails('eval #{type: "one"}->prop_add_list([[2, 2, 2, 2], [3, 
20, 3, 22]])', 'E964:')
+   call assert_fails('call prop_add_list(test_null_dict(), [[2, 2, 2]])', 
'E965:')
+   call assert_fails('call prop_add_list(#{type: "one"}, test_null_list())', 
'E714:')
+   call assert_fails('call prop_add_list(#{type: "one"}, [test_null_list()])', 
'E714:')
+   call DeletePropTypes()
+   bw!
+ endfunc
+ 
  func Test_prop_remove()
    new
    call AddPropTypes()
*** ../vim-8.2.3355/src/testdir/test_vim9_builtin.vim   2021-08-09 
22:22:23.164468820 +0200
--- src/testdir/test_vim9_builtin.vim   2021-08-16 21:28:45.288918517 +0200
***************
*** 2364,2369 ****
--- 2364,2374 ----
    CheckDefAndScriptFailure2(['prop_add(1, 2, [])'], 'E1013: Argument 3: type 
mismatch, expected dict<any> but got list<unknown>', 'E1206: Dictionary 
required for argument 3')
  enddef
  
+ def Test_prop_add_list()
+   CheckDefAndScriptFailure2(['prop_add_list([], [])'], 'E1013: Argument 1: 
type mismatch, expected dict<any> but got list<unknown>', 'E1206: Dictionary 
required for argument 1')
+   CheckDefAndScriptFailure2(['prop_add_list({}, {})'], 'E1013: Argument 2: 
type mismatch, expected list<any> but got dict<unknown>', 'E1211: List required 
for argument 2')
+ enddef
+ 
  def Test_prop_clear()
    CheckDefAndScriptFailure2(['prop_clear("a")'], 'E1013: Argument 1: type 
mismatch, expected number but got string', 'E1210: Number required for argument 
1')
    CheckDefAndScriptFailure2(['prop_clear(1, "b")'], 'E1013: Argument 2: type 
mismatch, expected number but got string', 'E1210: Number required for argument 
2')
*** ../vim-8.2.3355/src/textprop.c      2021-08-01 21:30:08.893199555 +0200
--- src/textprop.c      2021-08-16 21:28:45.288918517 +0200
***************
*** 183,284 ****
  }
  
  /*
!  * Shared between prop_add() and popup_create().
!  * "dict_arg" is the function argument of a dict containing "bufnr".
!  * it is NULL for popup_create().
   */
!     void
! prop_add_common(
!       linenr_T    start_lnum,
!       colnr_T     start_col,
!       dict_T      *dict,
!       buf_T       *default_buf,
!       typval_T    *dict_arg)
  {
-     linenr_T  lnum;
-     linenr_T  end_lnum;
-     colnr_T   end_col;
-     char_u    *type_name;
      proptype_T        *type;
!     buf_T     *buf = default_buf;
!     int               id = 0;
!     char_u    *newtext;
      int               proplen;
-     size_t    textlen;
      char_u    *props = NULL;
      char_u    *newprops;
!     textprop_T        tmp_prop;
      int               i;
! 
!     if (dict == NULL || dict_find(dict, (char_u *)"type", -1) == NULL)
!     {
!       emsg(_("E965: missing property type name"));
!       return;
!     }
!     type_name = dict_get_string(dict, (char_u *)"type", FALSE);
! 
!     if (dict_find(dict, (char_u *)"end_lnum", -1) != NULL)
!     {
!       end_lnum = dict_get_number(dict, (char_u *)"end_lnum");
!       if (end_lnum < start_lnum)
!       {
!           semsg(_(e_invargval), "end_lnum");
!           return;
!       }
!     }
!     else
!       end_lnum = start_lnum;
! 
!     if (dict_find(dict, (char_u *)"length", -1) != NULL)
!     {
!       long length = dict_get_number(dict, (char_u *)"length");
! 
!       if (length < 0 || end_lnum > start_lnum)
!       {
!           semsg(_(e_invargval), "length");
!           return;
!       }
!       end_col = start_col + length;
!     }
!     else if (dict_find(dict, (char_u *)"end_col", -1) != NULL)
!     {
!       end_col = dict_get_number(dict, (char_u *)"end_col");
!       if (end_col <= 0)
!       {
!           semsg(_(e_invargval), "end_col");
!           return;
!       }
!     }
!     else if (start_lnum == end_lnum)
!       end_col = start_col;
!     else
!       end_col = 1;
! 
!     if (dict_find(dict, (char_u *)"id", -1) != NULL)
!       id = dict_get_number(dict, (char_u *)"id");
! 
!     if (dict_arg != NULL && get_bufnr_from_arg(dict_arg, &buf) == FAIL)
!       return;
  
      type = lookup_prop_type(type_name, buf);
      if (type == NULL)
!       return;
  
      if (start_lnum < 1 || start_lnum > buf->b_ml.ml_line_count)
      {
        semsg(_(e_invalid_lnum), (long)start_lnum);
!       return;
      }
      if (end_lnum < start_lnum || end_lnum > buf->b_ml.ml_line_count)
      {
        semsg(_(e_invalid_lnum), (long)end_lnum);
!       return;
      }
  
      if (buf->b_ml.ml_mfp == NULL)
      {
        emsg(_("E275: Cannot add text property to unloaded buffer"));
!       return;
      }
  
      for (lnum = start_lnum; lnum <= end_lnum; ++lnum)
--- 183,231 ----
  }
  
  /*
!  * Attach a text property 'type_name' to the text starting
!  * at [start_lnum, start_col] and ending at [end_lnum, end_col] in
!  * the buffer 'buf' and assign identifier 'id'.
   */
!     static int
! prop_add_one(
!       buf_T           *buf,
!       char_u          *type_name,
!       int             id,
!       linenr_T        start_lnum,
!       linenr_T        end_lnum,
!       colnr_T         start_col,
!       colnr_T         end_col)
  {
      proptype_T        *type;
!     linenr_T  lnum;
      int               proplen;
      char_u    *props = NULL;
      char_u    *newprops;
!     size_t    textlen;
!     char_u    *newtext;
      int               i;
!     textprop_T        tmp_prop;
  
      type = lookup_prop_type(type_name, buf);
      if (type == NULL)
!       return FAIL;
  
      if (start_lnum < 1 || start_lnum > buf->b_ml.ml_line_count)
      {
        semsg(_(e_invalid_lnum), (long)start_lnum);
!       return FAIL;
      }
      if (end_lnum < start_lnum || end_lnum > buf->b_ml.ml_line_count)
      {
        semsg(_(e_invalid_lnum), (long)end_lnum);
!       return FAIL;
      }
  
      if (buf->b_ml.ml_mfp == NULL)
      {
        emsg(_("E275: Cannot add text property to unloaded buffer"));
!       return FAIL;
      }
  
      for (lnum = start_lnum; lnum <= end_lnum; ++lnum)
***************
*** 297,303 ****
        if (col - 1 > (colnr_T)textlen)
        {
            semsg(_(e_invalid_col), (long)start_col);
!           return;
        }
  
        if (lnum == end_lnum)
--- 244,250 ----
        if (col - 1 > (colnr_T)textlen)
        {
            semsg(_(e_invalid_col), (long)start_col);
!           return FAIL;
        }
  
        if (lnum == end_lnum)
***************
*** 312,318 ****
        // Allocate the new line with space for the new property.
        newtext = alloc(buf->b_ml.ml_line_len + sizeof(textprop_T));
        if (newtext == NULL)
!           return;
        // Copy the text, including terminating NUL.
        mch_memmove(newtext, buf->b_ml.ml_line_ptr, textlen);
  
--- 259,265 ----
        // Allocate the new line with space for the new property.
        newtext = alloc(buf->b_ml.ml_line_len + sizeof(textprop_T));
        if (newtext == NULL)
!           return FAIL;
        // Copy the text, including terminating NUL.
        mch_memmove(newtext, buf->b_ml.ml_line_ptr, textlen);
  
***************
*** 351,358 ****
        buf->b_ml.ml_flags |= ML_LINE_DIRTY;
      }
  
-     buf->b_has_textprop = TRUE;  // this is never reset
      changed_lines_buf(buf, start_lnum, end_lnum + 1, 0);
      redraw_buf_later(buf, VALID);
  }
  
--- 298,453 ----
        buf->b_ml.ml_flags |= ML_LINE_DIRTY;
      }
  
      changed_lines_buf(buf, start_lnum, end_lnum + 1, 0);
+     return OK;
+ }
+ 
+ /*
+  * prop_add_list()
+  * First argument specifies the text property:
+  *   {'type': <str>, 'id': <num>, 'bufnr': <num>}
+  * Second argument is a List where each item is a List with the following
+  * entries: [lnum, start_col, end_col]
+  */
+     void
+ f_prop_add_list(typval_T *argvars, typval_T *rettv UNUSED)
+ {
+     dict_T    *dict;
+     char_u    *type_name;
+     buf_T     *buf = curbuf;
+     int               id = 0;
+     listitem_T        *li;
+     list_T    *pos_list;
+     linenr_T  start_lnum;
+     colnr_T   start_col;
+     linenr_T  end_lnum;
+     colnr_T   end_col;
+     int               error = FALSE;
+ 
+     if (check_for_dict_arg(argvars, 0) == FAIL
+           || check_for_list_arg(argvars, 1) == FAIL)
+       return;
+ 
+     if (argvars[1].vval.v_list == NULL)
+     {
+       emsg(_(e_listreq));
+       return;
+     }
+ 
+     dict = argvars[0].vval.v_dict;
+     if (dict == NULL || dict_find(dict, (char_u *)"type", -1) == NULL)
+     {
+       emsg(_("E965: missing property type name"));
+       return;
+     }
+     type_name = dict_get_string(dict, (char_u *)"type", FALSE);
+ 
+     if (dict_find(dict, (char_u *)"id", -1) != NULL)
+       id = dict_get_number(dict, (char_u *)"id");
+ 
+     if (get_bufnr_from_arg(&argvars[0], &buf) == FAIL)
+       return;
+ 
+     FOR_ALL_LIST_ITEMS(argvars[1].vval.v_list, li)
+     {
+       if (li->li_tv.v_type != VAR_LIST || li->li_tv.vval.v_list == NULL)
+       {
+           emsg(_(e_listreq));
+           return;
+       }
+ 
+       pos_list = li->li_tv.vval.v_list;
+       start_lnum = list_find_nr(pos_list, 0L, &error);
+       start_col = list_find_nr(pos_list, 1L, &error);
+       end_lnum = list_find_nr(pos_list, 2L, &error);
+       end_col = list_find_nr(pos_list, 3L, &error);
+       if (error || start_lnum <= 0 || start_col <= 0
+               || end_lnum <= 0 || end_col <= 0)
+       {
+           emsg(_(e_invarg));
+           return;
+       }
+       if (prop_add_one(buf, type_name, id, start_lnum, end_lnum,
+                                               start_col, end_col) == FAIL)
+           return;
+     }
+ 
+     buf->b_has_textprop = TRUE;  // this is never reset
+     redraw_buf_later(buf, VALID);
+ }
+ 
+ /*
+  * Shared between prop_add() and popup_create().
+  * "dict_arg" is the function argument of a dict containing "bufnr".
+  * it is NULL for popup_create().
+  */
+     void
+ prop_add_common(
+       linenr_T    start_lnum,
+       colnr_T     start_col,
+       dict_T      *dict,
+       buf_T       *default_buf,
+       typval_T    *dict_arg)
+ {
+     linenr_T  end_lnum;
+     colnr_T   end_col;
+     char_u    *type_name;
+     buf_T     *buf = default_buf;
+     int               id = 0;
+ 
+     if (dict == NULL || dict_find(dict, (char_u *)"type", -1) == NULL)
+     {
+       emsg(_("E965: missing property type name"));
+       return;
+     }
+     type_name = dict_get_string(dict, (char_u *)"type", FALSE);
+ 
+     if (dict_find(dict, (char_u *)"end_lnum", -1) != NULL)
+     {
+       end_lnum = dict_get_number(dict, (char_u *)"end_lnum");
+       if (end_lnum < start_lnum)
+       {
+           semsg(_(e_invargval), "end_lnum");
+           return;
+       }
+     }
+     else
+       end_lnum = start_lnum;
+ 
+     if (dict_find(dict, (char_u *)"length", -1) != NULL)
+     {
+       long length = dict_get_number(dict, (char_u *)"length");
+ 
+       if (length < 0 || end_lnum > start_lnum)
+       {
+           semsg(_(e_invargval), "length");
+           return;
+       }
+       end_col = start_col + length;
+     }
+     else if (dict_find(dict, (char_u *)"end_col", -1) != NULL)
+     {
+       end_col = dict_get_number(dict, (char_u *)"end_col");
+       if (end_col <= 0)
+       {
+           semsg(_(e_invargval), "end_col");
+           return;
+       }
+     }
+     else if (start_lnum == end_lnum)
+       end_col = start_col;
+     else
+       end_col = 1;
+ 
+     if (dict_find(dict, (char_u *)"id", -1) != NULL)
+       id = dict_get_number(dict, (char_u *)"id");
+ 
+     if (dict_arg != NULL && get_bufnr_from_arg(dict_arg, &buf) == FAIL)
+       return;
+ 
+     prop_add_one(buf, type_name, id, start_lnum, end_lnum, start_col, 
end_col);
+ 
+     buf->b_has_textprop = TRUE;  // this is never reset
      redraw_buf_later(buf, VALID);
  }
  
*** ../vim-8.2.3355/src/version.c       2021-08-16 21:15:28.215345122 +0200
--- src/version.c       2021-08-16 21:36:23.647529755 +0200
***************
*** 757,758 ****
--- 757,760 ----
  {   /* Add new patch number below this line */
+ /**/
+     3356,
  /**/

-- 
Q: Why does /dev/null accept only integers?
A: You can't sink a float.

 /// 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/202108161939.17GJdhRp173589%40masaka.moolenaar.net.

Raspunde prin e-mail lui