Patch 8.2.0110
Problem:    prop_find() is not implemented.
Solution:   Implement prop_find(). (Ryan Hackett, closes #5421, closes #4970)
Files:      src/evalfunc.c, src/proto/textprop.pro,
            src/testdir/test_textprop.vim, src/textprop.c,
            runtime/doc/textprop.txt


*** ../vim-8.2.0109/src/evalfunc.c      2020-01-08 19:32:14.970527443 +0100
--- src/evalfunc.c      2020-01-10 19:51:35.852314531 +0100
***************
*** 620,625 ****
--- 620,626 ----
  #ifdef FEAT_PROP_POPUP
      {"prop_add",      3, 3, FEARG_1,    f_prop_add},
      {"prop_clear",    1, 3, FEARG_1,    f_prop_clear},
+     {"prop_find",     1, 2, FEARG_1,    f_prop_find},
      {"prop_list",     1, 2, FEARG_1,    f_prop_list},
      {"prop_remove",   1, 3, FEARG_1,    f_prop_remove},
      {"prop_type_add", 2, 2, FEARG_1,    f_prop_type_add},
*** ../vim-8.2.0109/src/proto/textprop.pro      2019-12-12 12:55:35.000000000 
+0100
--- src/proto/textprop.pro      2020-01-10 18:56:51.600928100 +0100
***************
*** 6,11 ****
--- 6,12 ----
  int find_visible_prop(win_T *wp, int type_id, int id, textprop_T *prop, 
linenr_T *found_lnum);
  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);
  void f_prop_list(typval_T *argvars, typval_T *rettv);
  void f_prop_remove(typval_T *argvars, typval_T *rettv);
  void f_prop_type_add(typval_T *argvars, typval_T *rettv);
*** ../vim-8.2.0109/src/testdir/test_textprop.vim       2020-01-09 
21:35:44.912474257 +0100
--- src/testdir/test_textprop.vim       2020-01-10 18:56:51.600928100 +0100
***************
*** 103,108 ****
--- 103,220 ----
        \ ]
  endfunc
  
+ func Test_prop_find()
+   new
+   call setline(1, ['one one one', 'twotwo', 'three', 'fourfour', 'five', 
'sixsix'])
+  
+   " Add two text props on lines 1 and 5, and one spanning lines 2 to 4. 
+   call prop_type_add('prop_name', {'highlight': 'Directory'})
+   call prop_add(1, 5, {'type': 'prop_name', 'id': 10, 'length': 3})
+   call prop_add(2, 4, {'type': 'prop_name', 'id': 11, 'end_lnum': 4, 
'end_col': 9})
+   call prop_add(5, 4, {'type': 'prop_name', 'id': 12, 'length': 1})
+ 
+   let expected = [
+     \ {'lnum': 1, 'col': 5, 'length': 3, 'id': 10, 'type': 'prop_name', 
'start': 1, 'end': 1},
+     \ {'lnum': 2, 'col': 4, 'id': 11, 'type': 'prop_name', 'start': 1, 'end': 
0},
+     \ {'lnum': 5, 'col': 4, 'length': 1, 'id': 12, 'type': 'prop_name', 
'start': 1, 'end': 1}
+     \ ]
+ 
+   " 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)
+ 
+   " With skipstart left at false (default), this should find the prop at line
+   " 5 col 4.
+   let result = prop_find({'type': 'prop_name', 'lnum': 5, 'col': 4}, 'b')
+   call assert_equal(expected[2], result)
+ 
+   " With skipstart set to true, this should skip the prop at line 5 col 4.
+   let result = prop_find({'type': 'prop_name', 'lnum': 5, 'col': 4, 
'skipstart': 1}, 'b')
+   unlet result.length
+   call assert_equal(expected[1], result)
+ 
+   " Search backwards from line 1 col 10 to find the prop on the same line.
+   let result = prop_find({'type': 'prop_name', 'lnum': 1, 'col': 10}, 'b')
+   call assert_equal(expected[0], result)
+ 
+   " 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)
+   let result = prop_find({'type': 'prop_name'}, 'b')
+   unlet result.length
+   call assert_equal(expected[1], result)
+ 
+   " with skipstart set to true, if the start position is anywhere between the
+   " start and end lines of a text prop (searching forward or backward), all 
lines
+   " of the prop will be skipped.
+   let result = prop_find({'type': 'prop_name', 'skipstart': 1}, 'b')
+   call assert_equal(expected[0], result)
+   let result = prop_find({'type': 'prop_name', 'skipstart': 1}, 'f')
+   call assert_equal(expected[2], result)
+ 
+   " Use skipstart to search through all props with type name 'prop_name'.
+   " First forward...
+   let lnum = 1
+   let col = 1
+   let i = 0
+   for exp in expected
+     let result = prop_find({'type': 'prop_name', 'lnum': lnum, 'col': col, 
'skipstart': 1}, 'f')
+     if !has_key(exp, "length")
+       unlet result.length
+     endif
+     call assert_equal(exp, result)
+     let lnum = result.lnum
+     let col = result.col
+     let i = i + 1
+   endfor
+ 
+   " ...then backwards.
+   let lnum = 6
+   let col = 4
+   let i = 2
+   while i >= 0
+     let result = prop_find({'type': 'prop_name', 'lnum': lnum, 'col': col, 
'skipstart': 1}, 'b')
+     if !has_key(expected[i], "length")
+       unlet result.length
+     endif
+     call assert_equal(expected[i], result)
+     let lnum = result.lnum
+     let col = result.col
+     let i = i - 1
+   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)
+ 
+   " Search for a prop with an unknown id.
+   let result = prop_find({'id': 999}, 'f')
+   call assert_equal({}, result)
+ 
+   " Search backwards from the proceeding position of the prop with id 11
+   " (at line num 2 col 4). This should return an empty dict.
+   let result = prop_find({'id': 11, 'lnum': 2, 'col': 3}, 'b')
+   call assert_equal({}, result)
+ 
+   " When lnum is given and col is omitted, use column 1.
+   let result = prop_find({'type': 'prop_name', 'lnum': 1}, 'f')
+   call assert_equal(expected[0], result)
+ 
+   call prop_clear(1,6)
+   call prop_type_delete('prop_name')
+ endfunc
+ 
  func Test_prop_add()
    new
    call AddPropTypes()
*** ../vim-8.2.0109/src/textprop.c      2020-01-04 16:13:44.408524644 +0100
--- src/textprop.c      2020-01-10 19:55:37.263273898 +0100
***************
*** 469,474 ****
--- 469,492 ----
  }
  
  /*
+  * Fill 'dict' with text properties in 'prop'.
+  */
+     static void
+ prop_fill_dict(dict_T *dict, textprop_T *prop, buf_T *buf)
+ {
+     proptype_T *pt;
+ 
+     dict_add_number(dict, "col", prop->tp_col);
+     dict_add_number(dict, "length", prop->tp_len);
+     dict_add_number(dict, "id", prop->tp_id);
+     dict_add_number(dict, "start", !(prop->tp_flags & TP_FLAG_CONT_PREV));
+     dict_add_number(dict, "end", !(prop->tp_flags & TP_FLAG_CONT_NEXT));
+     pt = text_prop_type_by_id(buf, prop->tp_type);
+     if (pt != NULL)
+       dict_add_string(dict, "type", pt->pt_name);
+ }
+ 
+ /*
   * Find a property type by ID in "buf" or globally.
   * Returns NULL if not found.
   */
***************
*** 537,542 ****
--- 555,744 ----
  }
  
  /*
+  * prop_find({props} [, {direction}])
+  */
+     void
+ f_prop_find(typval_T *argvars, typval_T *rettv)
+ {
+     pos_T       *cursor = &curwin->w_cursor;
+     dict_T      *dict;
+     buf_T       *buf = curbuf;
+     dictitem_T  *di;
+     int         lnum_start;
+     int         start_pos_has_prop = 0;
+     int         seen_end = 0;
+     int         id = -1;
+     int         type_id = -1;
+     int         skipstart = 0;
+     int         lnum = -1;
+     int         col = -1;
+     int         dir = 1;    // 1 = forward, -1 = backward
+ 
+     if (argvars[0].v_type != VAR_DICT || argvars[0].vval.v_dict == NULL)
+     {
+       emsg(_(e_invarg));
+       return;
+     }
+     dict = argvars[0].vval.v_dict;
+ 
+     if (get_bufnr_from_arg(&argvars[0], &buf) == FAIL)
+       return;
+     if (buf->b_ml.ml_mfp == NULL)
+       return;
+ 
+     if (argvars[1].v_type != VAR_UNKNOWN)
+     {
+       char_u      *dir_s = tv_get_string(&argvars[1]);
+ 
+       if (*dir_s == 'b')
+           dir = -1;
+       else if (*dir_s != 'f')
+       {
+           emsg(_(e_invarg));
+           return;
+       }
+     }
+ 
+     di = dict_find(dict, (char_u *)"lnum", -1);
+     if (di != NULL)
+       lnum = tv_get_number(&di->di_tv);
+ 
+     di = dict_find(dict, (char_u *)"col", -1);
+     if (di != NULL)
+       col = tv_get_number(&di->di_tv);
+ 
+     if (lnum == -1)
+     {
+       lnum = cursor->lnum;
+       col = cursor->col + 1;
+     }
+     else if (col == -1)
+       col = 1;
+ 
+     if (lnum < 1 || lnum > buf->b_ml.ml_line_count)
+     {
+       emsg(_(e_invrange));
+       return;
+     }
+ 
+     di = dict_find(dict, (char_u *)"skipstart", -1);
+     if (di != NULL)
+       skipstart = tv_get_number(&di->di_tv);
+ 
+     if (dict_find(dict, (char_u *)"id", -1) != NULL)
+       id = dict_get_number(dict, (char_u *)"id");
+     if (dict_find(dict, (char_u *)"type", -1))
+     {
+       char_u      *name = dict_get_string(dict, (char_u *)"type", FALSE);
+       proptype_T  *type = lookup_prop_type(name, buf);
+ 
+       if (type == NULL)
+           return;
+       type_id = type->pt_id;
+     }
+     if (id == -1 && type_id == -1)
+     {
+       emsg(_("E968: Need at least one of 'id' or 'type'"));
+       return;
+     }
+ 
+     lnum_start = lnum;
+ 
+     if (rettv_dict_alloc(rettv) == FAIL)
+       return;
+ 
+     while (1)
+     {
+       char_u  *text = ml_get_buf(buf, lnum, FALSE);
+       size_t  textlen = STRLEN(text) + 1;
+       int     count = (int)((buf->b_ml.ml_line_len - textlen)
+                                 / sizeof(textprop_T));
+       int         i;
+       textprop_T  prop;
+       int         prop_start;
+       int         prop_end;
+ 
+       for (i = 0; i < count; ++i)
+       {
+           mch_memmove(&prop, text + textlen + i * sizeof(textprop_T),
+                           sizeof(textprop_T));
+ 
+           if (prop.tp_id == id || prop.tp_type == type_id)
+           {
+               // Check if the starting position has text props.
+               if (lnum_start == lnum)
+               {
+                   if (col >= prop.tp_col
+                                      && (col <= prop.tp_col + prop.tp_len-1))
+                       start_pos_has_prop = 1;
+               }
+               else
+               {
+                   // Not at the first line of the search so adjust col to
+                   // indicate that we're continuing from prev/next line.
+                   if (dir < 0)
+                       col = buf->b_ml.ml_line_len;
+                   else
+                       col = 1;
+               }
+ 
+               prop_start = !(prop.tp_flags & TP_FLAG_CONT_PREV);
+               prop_end = !(prop.tp_flags & TP_FLAG_CONT_NEXT);
+               if (!prop_start && prop_end && dir > 0)
+                   seen_end = 1;
+ 
+               // Skip lines without the start flag.
+               if (!prop_start)
+               {
+                   // Always search backwards for start when search started
+                   // on a prop and we're not skipping.
+                   if (start_pos_has_prop && !skipstart)
+                       dir = -1;
+                   break;
+               }
+ 
+               // If skipstart is true, skip the prop at start pos (even if
+               // continued from another line).
+               if (start_pos_has_prop && skipstart && !seen_end)
+               {
+                   start_pos_has_prop = 0;
+                   break;
+               }
+ 
+               if (dir < 0)
+               {
+                   if (col < prop.tp_col)
+                       break;
+               }
+               else
+               {
+                   if (col > prop.tp_col + prop.tp_len-1)
+                       break;
+               }
+ 
+               prop_fill_dict(rettv->vval.v_dict, &prop, buf);
+               dict_add_number(rettv->vval.v_dict, "lnum", lnum);
+ 
+               return;
+           }
+       }
+ 
+       if (dir > 0)
+       {
+           if (lnum >= buf->b_ml.ml_line_count)
+               break;
+           lnum++;
+       }
+       else
+       {
+           if (lnum <= 1)
+               break;
+           lnum--;
+       }
+     }
+ }
+ 
+ /*
   * prop_list({lnum} [, {bufnr}])
   */
      void
***************
*** 564,570 ****
                                                         / sizeof(textprop_T));
        int         i;
        textprop_T  prop;
-       proptype_T  *pt;
  
        for (i = 0; i < count; ++i)
        {
--- 766,771 ----
***************
*** 574,588 ****
                break;
            mch_memmove(&prop, text + textlen + i * sizeof(textprop_T),
                                                           sizeof(textprop_T));
!           dict_add_number(d, "col", prop.tp_col);
!           dict_add_number(d, "length", prop.tp_len);
!           dict_add_number(d, "id", prop.tp_id);
!           dict_add_number(d, "start", !(prop.tp_flags & TP_FLAG_CONT_PREV));
!           dict_add_number(d, "end", !(prop.tp_flags & TP_FLAG_CONT_NEXT));
!           pt = text_prop_type_by_id(buf, prop.tp_type);
!           if (pt != NULL)
!               dict_add_string(d, "type", pt->pt_name);
! 
            list_append_dict(rettv->vval.v_list, d);
        }
      }
--- 775,781 ----
                break;
            mch_memmove(&prop, text + textlen + i * sizeof(textprop_T),
                                                           sizeof(textprop_T));
!           prop_fill_dict(d, &prop, buf);
            list_append_dict(rettv->vval.v_list, d);
        }
      }
*** ../vim-8.2.0109/runtime/doc/textprop.txt    2019-12-12 12:49:06.000000000 
+0100
--- runtime/doc/textprop.txt    2020-01-10 19:00:09.236227590 +0100
***************
*** 154,161 ****
                added to. When not found, the global property types are used.
                If not found an error is given.
  
-               See |text-properties| for information about text properties.
- 
                Can also be used as a |method|: >
                        GetLnum()->prop_add(col, props)
  
--- 154,159 ----
***************
*** 168,181 ****
                When {props} contains a "bufnr" item use this buffer,
                otherwise use the current buffer.
  
-               See |text-properties| for information about text properties.
- 
                Can also be used as a |method|: >
                        GetLnum()->prop_clear()
  <
                                                        *prop_find()*
  prop_find({props} [, {direction}])
-               {not implemented yet}
                Search for a text property as specified with {props}:
                   id           property with this ID
                   type         property with this type name
--- 166,176 ----
***************
*** 198,205 ****
                as with prop_list(), and additionally an "lnum" entry.
                If no match is found then an empty Dict is returned.
  
-               See |text-properties| for information about text properties.
- 
  
  prop_list({lnum} [, {props}])                         *prop_list()*
                Return a List with all text properties in line {lnum}.
--- 193,198 ----
***************
*** 223,230 ****
                When "end" is zero the property continues in the next line.
                The line break after this line is included.
  
-               See |text-properties| for information about text properties.
- 
                Can also be used as a |method|: >
                        GetLnum()->prop_list()
  <
--- 216,221 ----
***************
*** 248,255 ****
  
                Returns the number of properties that were removed.
  
-               See |text-properties| for information about text properties.
- 
                Can also be used as a |method|: >
                        GetProps()->prop_remove()
  
--- 239,244 ----
***************
*** 275,282 ****
                   end_incl     when TRUE inserts at the end position will be
                                included in the text property
  
-               See |text-properties| for information about text properties.
- 
                Can also be used as a |method|: >
                        GetPropName()->prop_type_add(props)
  
--- 264,269 ----
***************
*** 285,292 ****
                property with this name does not exist an error is given.
                The {props} argument is just like |prop_type_add()|.
  
-               See |text-properties| for information about text properties.
- 
                Can also be used as a |method|: >
                        GetPropName()->prop_type_change(props)
  
--- 272,277 ----
***************
*** 301,308 ****
  
                When text property type {name} is not found there is no error.
  
-               See |text-properties| for information about text properties.
- 
                Can also be used as a |method|: >
                        GetPropName()->prop_type_delete()
  
--- 286,291 ----
***************
*** 316,323 ****
                {props} can contain a "bufnr" item.  When it is given, use
                this buffer instead of the global property types.
  
-               See |text-properties| for information about text properties.
- 
                Can also be used as a |method|: >
                        GetPropName()->prop_type_get()
  
--- 299,304 ----
***************
*** 327,334 ****
                {props} can contain a "bufnr" item.  When it is given, use
                this buffer instead of the global property types.
  
-               See |text-properties| for information about text properties.
- 
  
  ==============================================================================
  3. When text changes                          *text-prop-changes*
--- 308,313 ----
*** ../vim-8.2.0109/src/version.c       2020-01-09 21:35:44.912474257 +0100
--- src/version.c       2020-01-10 18:58:23.020538339 +0100
***************
*** 744,745 ****
--- 744,747 ----
  {   /* Add new patch number below this line */
+ /**/
+     110,
  /**/

-- 
"Software is like sex... it's better when it's free."
                -- Linus Torvalds, initiator of the free Linux OS
Makes me wonder what FSF stands for...?

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            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/202001101857.00AIvHtv014183%40masaka.moolenaar.net.

Raspunde prin e-mail lui