Patch 9.0.0233
Problem:    Removing multiple text properties takes many calls.
Solution:   Pass a list to prop_remove(). (Ben Jackson, closes #10945)
Files:      runtime/doc/textprop.txt, src/textprop.c, src/errors.h,
            src/testdir/test_textprop.vim


*** ../vim-9.0.0232/runtime/doc/textprop.txt    2022-08-05 17:04:43.402914763 
+0100
--- runtime/doc/textprop.txt    2022-08-20 20:54:00.459011754 +0100
***************
*** 143,154 ****
                                zero is used
                   text         text to be displayed before {col}, or after the
                                line if {col} is zero
                   text_align   when "text" is present and {col} is zero
                                specifies where to display the text:
                                   after   after the end of the line
!                                  right   right aligned in the window
                                   below   in the next screen line
!                               When omitted "after" is used.
                   text_wrap    when "text" is present and {col} is zero,
                                specifies what happens if the text doesn't
                                fit:
--- 143,158 ----
                                zero is used
                   text         text to be displayed before {col}, or after the
                                line if {col} is zero
+                                                       *E1294*
                   text_align   when "text" is present and {col} is zero
                                specifies where to display the text:
                                   after   after the end of the line
!                                  right   right aligned in the window (unless
!                                          the text wraps to the next screen
!                                          line)
                                   below   in the next screen line
!                               When omitted "after" is used.  Only one
!                               "right" property can fit in earch line.
                   text_wrap    when "text" is present and {col} is zero,
                                specifies what happens if the text doesn't
                                fit:
***************
*** 186,194 ****
                buffer line, the cursor cannot be placed on it.  A mouse click
                in the text will move the cursor to the first character after
                the text, or the last character of the line.
                A negative "id" will be chosen and is returned.  Once a
-               Any Tab in the text will be changed to a space (Rationale:
-               otherwise the size of the text is difficult to compute).
                property with "text" has been added for a buffer then using a
                negative "id" for any other property will give an error:
                *E1293*
--- 190,199 ----
                buffer line, the cursor cannot be placed on it.  A mouse click
                in the text will move the cursor to the first character after
                the text, or the last character of the line.
+               Any Tab and other control character in the text will be
+               changed to a space (Rationale: otherwise the size of the text
+               is difficult to compute).
                A negative "id" will be chosen and is returned.  Once a
                property with "text" has been added for a buffer then using a
                negative "id" for any other property will give an error:
                *E1293*
***************
*** 347,357 ****
                {props} is a dictionary with these fields:
                   id           remove text properties with this ID
                   type         remove text properties with this type name
!                  both         "id" and "type" must both match
                   bufnr        use this buffer instead of the current one
                   all          when TRUE remove all matching text properties,
                                not just the first one
!               A property matches when either "id" or "type" matches.
                If buffer "bufnr" does not exist you get an error message.
                If buffer "bufnr" is not loaded then nothing happens.
  
--- 352,367 ----
                {props} is a dictionary with these fields:
                   id           remove text properties with this ID
                   type         remove text properties with this type name
!                  types        remove text properties with type names in this
!                               List
!                  both         "id" and "type"/"types" must both match
                   bufnr        use this buffer instead of the current one
                   all          when TRUE remove all matching text properties,
                                not just the first one
!               Only one of "type" and "types" may be supplied. *E1295*
! 
!               A property matches when either "id" or one of the supplied
!               types matches.
                If buffer "bufnr" does not exist you get an error message.
                If buffer "bufnr" is not loaded then nothing happens.
  
*** ../vim-9.0.0232/src/textprop.c      2022-08-15 15:54:45.710375583 +0100
--- src/textprop.c      2022-08-20 20:47:49.903395746 +0100
***************
*** 1010,1016 ****
      }
      if (both && (!id_found || type_id == -1))
      {
!       emsg(_(e_need_id_and_type_with_both));
        return;
      }
  
--- 1010,1016 ----
      }
      if (both && (!id_found || type_id == -1))
      {
!       emsg(_(e_need_id_and_type_or_types_with_both));
        return;
      }
  
***************
*** 1378,1384 ****
      buf_T     *buf = curbuf;
      int               do_all;
      int               id = -MAXCOL;
!     int               type_id = -1;
      int               both;
      int               did_remove_text = FALSE;
  
--- 1378,1386 ----
      buf_T     *buf = curbuf;
      int               do_all;
      int               id = -MAXCOL;
!     int               type_id = -1;       // for a single "type"
!     int               *type_ids = NULL;   // array, for a list of "types", 
allocated
!     int               num_type_ids = 0;   // number of elements in "type_ids"
      int               both;
      int               did_remove_text = FALSE;
  
***************
*** 1420,1425 ****
--- 1422,1430 ----
  
      if (dict_has_key(dict, "id"))
        id = dict_get_number(dict, "id");
+ 
+     // if a specific type was supplied "type": check that (and ignore "types".
+     // Otherwise check against the list of "types".
      if (dict_has_key(dict, "type"))
      {
        char_u      *name = dict_get_string(dict, "type", FALSE);
***************
*** 1429,1445 ****
            return;
        type_id = type->pt_id;
      }
      both = dict_get_bool(dict, "both", FALSE);
  
!     if (id == -MAXCOL && type_id == -1)
      {
        emsg(_(e_need_at_least_one_of_id_or_type));
!       return;
      }
!     if (both && (id == -MAXCOL || type_id == -1))
      {
!       emsg(_(e_need_id_and_type_with_both));
!       return;
      }
  
      if (end == 0)
--- 1434,1481 ----
            return;
        type_id = type->pt_id;
      }
+     if (dict_has_key(dict, "types"))
+     {
+       typval_T types;
+       listitem_T *li = NULL;
+ 
+       dict_get_tv(dict, "types", &types);
+       if (types.v_type == VAR_LIST && types.vval.v_list->lv_len > 0)
+       {
+           type_ids = alloc( sizeof(int) * types.vval.v_list->lv_len );
+ 
+           FOR_ALL_LIST_ITEMS(types.vval.v_list, li)
+           {
+               proptype_T *prop_type;
+ 
+               if (li->li_tv.v_type != VAR_STRING)
+                   continue;
+ 
+               prop_type = lookup_prop_type(li->li_tv.vval.v_string, buf);
+ 
+               if (!prop_type)
+                   goto cleanup_prop_remove;
+ 
+               type_ids[num_type_ids++] = prop_type->pt_id;
+           }
+       }
+     }
      both = dict_get_bool(dict, "both", FALSE);
  
!     if (id == -MAXCOL && (type_id == -1 && num_type_ids == 0))
      {
        emsg(_(e_need_at_least_one_of_id_or_type));
!       goto cleanup_prop_remove;
      }
!     if (both && (id == -MAXCOL || (type_id == -1 && num_type_ids == 0)))
      {
!       emsg(_(e_need_id_and_type_or_types_with_both));
!       goto cleanup_prop_remove;
!     }
!     if (type_id != -1 && num_type_ids > 0)
!     {
!       emsg(_(e_cannot_specify_both_type_and_types));
!       goto cleanup_prop_remove;
      }
  
      if (end == 0)
***************
*** 1464,1473 ****
                char_u *cur_prop = buf->b_ml.ml_line_ptr + len
                                                    + idx * sizeof(textprop_T);
                size_t  taillen;
  
                mch_memmove(&textprop, cur_prop, sizeof(textprop_T));
!               if (both ? textprop.tp_id == id && textprop.tp_type == type_id
!                        : textprop.tp_id == id || textprop.tp_type == type_id)
                {
                    if (!(buf->b_ml.ml_flags & ML_LINE_DIRTY))
                    {
--- 1500,1525 ----
                char_u *cur_prop = buf->b_ml.ml_line_ptr + len
                                                    + idx * sizeof(textprop_T);
                size_t  taillen;
+               int matches_id = 0;
+               int matches_type = 0;
  
                mch_memmove(&textprop, cur_prop, sizeof(textprop_T));
! 
!               matches_id = textprop.tp_id == id;
!               if (num_type_ids > 0)
!               {
!                   int idx2;
! 
!                   for (idx2 = 0; !matches_type && idx2 < num_type_ids; ++idx2)
!                       matches_type = textprop.tp_type == type_ids[idx2];
!               }
!               else
!               {
!                   matches_type = textprop.tp_type == type_id;
!               }
! 
!               if (both ? matches_id && matches_type
!                        : matches_id || matches_type)
                {
                    if (!(buf->b_ml.ml_flags & ML_LINE_DIRTY))
                    {
***************
*** 1475,1481 ****
  
                        // need to allocate the line to be able to change it
                        if (newptr == NULL)
!                           return;
                        mch_memmove(newptr, buf->b_ml.ml_line_ptr,
                                                        buf->b_ml.ml_line_len);
                        if (buf->b_ml.ml_flags & ML_ALLOCATED)
--- 1527,1533 ----
  
                        // need to allocate the line to be able to change it
                        if (newptr == NULL)
!                           goto cleanup_prop_remove;
                        mch_memmove(newptr, buf->b_ml.ml_line_ptr,
                                                        buf->b_ml.ml_line_len);
                        if (buf->b_ml.ml_flags & ML_ALLOCATED)
***************
*** 1537,1542 ****
--- 1589,1597 ----
                         && ((char_u **)gap->ga_data)[gap->ga_len - 1] == NULL)
            --gap->ga_len;
      }
+ 
+ cleanup_prop_remove:
+     vim_free(type_ids);
  }
  
  /*
*** ../vim-9.0.0232/src/errors.h        2022-08-20 12:07:55.098022792 +0100
--- src/errors.h        2022-08-20 20:40:30.507911267 +0100
***************
*** 2208,2215 ****
        INIT(= N_("E859: Failed to convert returned python object to a Vim 
value"));
  #endif
  #ifdef FEAT_PROP_POPUP
! EXTERN char e_need_id_and_type_with_both[]
!       INIT(= N_("E860: Need 'id' and 'type' with 'both'"));
  # ifdef FEAT_TERMINAL
  EXTERN char e_cannot_open_second_popup_with_terminal[]
        INIT(= N_("E861: Cannot open a second popup with a terminal"));
--- 2208,2215 ----
        INIT(= N_("E859: Failed to convert returned python object to a Vim 
value"));
  #endif
  #ifdef FEAT_PROP_POPUP
! EXTERN char e_need_id_and_type_or_types_with_both[]
!       INIT(= N_("E860: Need 'id' and 'type' or 'types' with 'both'"));
  # ifdef FEAT_TERMINAL
  EXTERN char e_cannot_open_second_popup_with_terminal[]
        INIT(= N_("E861: Cannot open a second popup with a terminal"));
***************
*** 3316,3318 ****
--- 3316,3322 ----
  EXTERN char e_can_only_use_text_align_when_column_is_zero[]
        INIT(= N_("E1294: Can only use text_align when column is zero"));
  #endif
+ #ifdef FEAT_PROP_POPUP
+ EXTERN char e_cannot_specify_both_type_and_types[]
+       INIT(= N_("E1295: Cannot specify both 'type' and 'types'"));
+ #endif
*** ../vim-9.0.0232/src/testdir/test_textprop.vim       2022-08-15 
15:54:45.710375583 +0100
--- src/testdir/test_textprop.vim       2022-08-20 20:44:18.895631843 +0100
***************
*** 161,167 ****
      \ ]
  
    " Starting at line 5 col 1 this should find the prop at line 5 col 4.
!   call cursor(5,1)
    let result = prop_find({'type': 'prop_name'}, 'f')
    call assert_equal(expected[2], result)
  
--- 161,167 ----
      \ ]
  
    " Starting at line 5 col 1 this should find the prop at line 5 col 4.
!   call cursor(5, 1)
    let result = prop_find({'type': 'prop_name'}, 'f')
    call assert_equal(expected[2], result)
  
***************
*** 182,188 ****
    " with skipstart set to false, if the start position is anywhere between the
    " start and end lines of a text prop (searching forward or backward), the
    " result should be the prop on the first line (the line with 'start' set to 
1).
!   call cursor(3,1)
    let result = prop_find({'type': 'prop_name'}, 'f')
    unlet result.length
    call assert_equal(expected[1], result)
--- 182,188 ----
    " with skipstart set to false, if the start position is anywhere between the
    " start and end lines of a text prop (searching forward or backward), the
    " result should be the prop on the first line (the line with 'start' set to 
1).
!   call cursor(3, 1)
    let result = prop_find({'type': 'prop_name'}, 'f')
    unlet result.length
    call assert_equal(expected[1], result)
***************
*** 230,241 ****
    endwhile
  
    " Starting from line 6 col 1 search backwards for prop with id 10.
!   call cursor(6,1)
    let result = prop_find({'id': 10, 'skipstart': 1}, 'b')
    call assert_equal(expected[0], result)
  
    " Starting from line 1 col 1 search forwards for prop with id 12.
!   call cursor(1,1)
    let result = prop_find({'id': 12}, 'f')
    call assert_equal(expected[2], result)
  
--- 230,241 ----
    endwhile
  
    " Starting from line 6 col 1 search backwards for prop with id 10.
!   call cursor(6, 1)
    let result = prop_find({'id': 10, 'skipstart': 1}, 'b')
    call assert_equal(expected[0], result)
  
    " Starting from line 1 col 1 search forwards for prop with id 12.
!   call cursor(1, 1)
    let result = prop_find({'id': 12}, 'f')
    call assert_equal(expected[2], result)
  
***************
*** 426,431 ****
--- 426,462 ----
  
    call DeletePropTypes()
    bwipe!
+ 
+   new
+   call AddPropTypes()
+   call SetupPropsInFirstLine()
+   let props = Get_expected_props() " [whole, one, two, three]
+   call assert_equal(props, prop_list(1))
+ 
+   " remove one by types
+   call assert_equal(1, prop_remove({'types': ['one', 'two', 'three']}, 1))
+   unlet props[1] " [whole, two, three]
+   call assert_equal(props, prop_list(1))
+ 
+   " remove 'all' by types
+   call assert_equal(2, prop_remove({'types': ['three', 'whole'], 'all': 1}, 
1))
+   unlet props[0] " [two, three]
+   unlet props[1] " [three]
+   call assert_equal(props, prop_list(1))
+ 
+   " remove none by types
+   call assert_equal(0, prop_remove({'types': ['three', 'whole'], 'all': 1}, 
1))
+   call assert_equal(props, prop_list(1))
+ 
+   " no types
+   call assert_fails("call prop_remove({'types': []}, 1)", 'E968:')
+   call assert_fails("call prop_remove({'types': ['not_a_real_type']}, 1)", 
'E971:')
+ 
+   " only one of types and type can be supplied
+   call assert_fails("call prop_remove({'type': 'one', 'types': ['three'], 
'all': 1}, 1)", 'E1295:')
+ 
+   call DeletePropTypes()
+   bwipe!
  endfunc
  
  def Test_prop_add_vim9()
***************
*** 1396,1402 ****
  func Test_textprop_invalid_highlight()
    call assert_fails("call prop_type_add('dni', {'highlight': 
'DoesNotExist'})", 'E970:')
    new
!   call setline(1, ['asdf','asdf'])
    call prop_add(1, 1, {'length': 4, 'type': 'dni'})
    redraw
    bwipe!
--- 1427,1433 ----
  func Test_textprop_invalid_highlight()
    call assert_fails("call prop_type_add('dni', {'highlight': 
'DoesNotExist'})", 'E970:')
    new
!   call setline(1, ['asdf', 'asdf'])
    call prop_add(1, 1, {'length': 4, 'type': 'dni'})
    redraw
    bwipe!
***************
*** 2207,2213 ****
      call prop_add(1, col, #{type: 'misspell', length: 2})
    endfor
  
!   call cursor(1,18)
    let expected = [
      \ #{lnum: 1, id: 0, col: 14, end: 1, type: 'misspell', type_bufnr: 0, 
length: 2, start: 1},
      \ #{lnum: 1, id: 0, col: 24, end: 1, type: 'misspell', type_bufnr: 0, 
length: 2, start: 1}
--- 2238,2244 ----
      call prop_add(1, col, #{type: 'misspell', length: 2})
    endfor
  
!   call cursor(1, 18)
    let expected = [
      \ #{lnum: 1, id: 0, col: 14, end: 1, type: 'misspell', type_bufnr: 0, 
length: 2, start: 1},
      \ #{lnum: 1, id: 0, col: 24, end: 1, type: 'misspell', type_bufnr: 0, 
length: 2, start: 1}
***************
*** 2310,2316 ****
    call assert_equal(lines, getline(1, '$'))
    let expected = [
        \ {'lnum': 1, 'id': 0, 'col': 4, 'type_bufnr': 0, 'end': 0, 'type': 
'one',
!       \ 'length': 4 ,'start': 1},
        \ {'lnum': 2, 'id': 0, 'col': 1, 'type_bufnr': 0, 'end': 0, 'type': 
'one',
        \ 'length': 7, 'start': 0},
        \ {'lnum': 3, 'id': 0, 'col': 1, 'type_bufnr': 0, 'end': 0, 'type': 
'one',
--- 2341,2347 ----
    call assert_equal(lines, getline(1, '$'))
    let expected = [
        \ {'lnum': 1, 'id': 0, 'col': 4, 'type_bufnr': 0, 'end': 0, 'type': 
'one',
!       \ 'length': 4 , 'start': 1},
        \ {'lnum': 2, 'id': 0, 'col': 1, 'type_bufnr': 0, 'end': 0, 'type': 
'one',
        \ 'length': 7, 'start': 0},
        \ {'lnum': 3, 'id': 0, 'col': 1, 'type_bufnr': 0, 'end': 0, 'type': 
'one',
*** ../vim-9.0.0232/src/version.c       2022-08-20 20:09:10.598693210 +0100
--- src/version.c       2022-08-20 20:38:36.820063346 +0100
***************
*** 733,734 ****
--- 733,736 ----
  {   /* Add new patch number below this line */
+ /**/
+     233,
  /**/

-- 
Be nice to your kids...  they'll be the ones choosing your nursing home.

 /// 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/20220820195527.7E2261C0ADA%40moolenaar.net.

Raspunde prin e-mail lui