]> granicus.if.org Git - vim/commitdiff
patch 8.2.3356: adding many text properties requires a lot of function calls v8.2.3356
authorYegappan Lakshmanan <yegappan@yahoo.com>
Mon, 16 Aug 2021 19:39:09 +0000 (21:39 +0200)
committerBram Moolenaar <Bram@vim.org>
Mon, 16 Aug 2021 19:39:09 +0000 (21:39 +0200)
Problem:    Adding many text properties requires a lot of function calls.
Solution:   Add the prop_add_list() function. (Yegappan Lakshmanan,
            closes #8751)

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
src/version.c

index 31e901a54d29dfe202681c104ee3d77e5db3d097..db74f9c10b6e8afba6abeca2a4fdb59c94b3137f 100644 (file)
@@ -2801,7 +2801,9 @@ prompt_getprompt({buf})           String  get prompt text
 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_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}])
index 52191ae03a76c334aeff2c1114db52fa6ad5b925..c742293db427a89195e420159ef26cc5991320ad 100644 (file)
@@ -108,6 +108,9 @@ prop_type_list([{props}])           get list of property types
 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
@@ -158,6 +161,35 @@ prop_add({lnum}, {col}, {props})
                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}.
index 3a3a0fca789970c74eb60021dadc6e48b4fd9fb4..a5dde18c38f05b9d36b6fcb29d4e10e15add7b64 100644 (file)
@@ -1161,6 +1161,7 @@ Prompt Buffer:                                    *promptbuffer-functions*
 
 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
index 2b50798f4fe930f8b367877d0ebc10a7c01c2e83..4d73d40f12441cf0897029d36fb286b8250d0134 100644 (file)
@@ -708,6 +708,7 @@ static argcheck_T arg2_buffer_number[] = {arg_buffer, arg_number};
 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,6 +1741,8 @@ static funcentry_T global_functions[] =
                        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,
index 8a4a22fa0bc858c994f9d2de37a25b61f500a8af..72a900bfa8ec7d3d9ef5f426ad02ccf55b1d0bb0 100644 (file)
@@ -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);
index 6af47e36dcb83fc2e941fd2457456a0b821ccf24..373df6e6530438fe0a8e56f2d0ca55f87e7b0bbd 100644 (file)
@@ -339,6 +339,41 @@ func Test_prop_add()
   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()
index 5e45edbaeefa976f43e01a53cdffc15b1c41a2f2..00d021fe194ac07e9198537723dbbbded79b0967 100644 (file)
@@ -2364,6 +2364,11 @@ def Test_prop_add()
   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')
index 6c9d5c8c4f92ec64668706fc8316493bd8d0b8ec..26c7f52084169d1f2297caf7445242a2a6d452e3 100644 (file)
@@ -183,102 +183,49 @@ f_prop_add(typval_T *argvars, typval_T *rettv UNUSED)
 }
 
 /*
- * Shared between prop_add() and popup_create().
- * "dict_arg" is the function argument of a dict containing "bufnr".
- * it is NULL for popup_create().
+ * 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'.
  */
-    void
-prop_add_common(
-       linenr_T    start_lnum,
-       colnr_T     start_col,
-       dict_T      *dict,
-       buf_T       *default_buf,
-       typval_T    *dict_arg)
+    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)
 {
-    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;
+    linenr_T   lnum;
     int                proplen;
-    size_t     textlen;
     char_u     *props = NULL;
     char_u     *newprops;
-    textprop_T tmp_prop;
+    size_t     textlen;
+    char_u     *newtext;
     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;
+    textprop_T tmp_prop;
 
     type = lookup_prop_type(type_name, buf);
     if (type == NULL)
-       return;
+       return FAIL;
 
     if (start_lnum < 1 || start_lnum > buf->b_ml.ml_line_count)
     {
        semsg(_(e_invalid_lnum), (long)start_lnum);
-       return;
+       return FAIL;
     }
     if (end_lnum < start_lnum || end_lnum > buf->b_ml.ml_line_count)
     {
        semsg(_(e_invalid_lnum), (long)end_lnum);
-       return;
+       return FAIL;
     }
 
     if (buf->b_ml.ml_mfp == NULL)
     {
        emsg(_("E275: Cannot add text property to unloaded buffer"));
-       return;
+       return FAIL;
     }
 
     for (lnum = start_lnum; lnum <= end_lnum; ++lnum)
@@ -297,7 +244,7 @@ prop_add_common(
        if (col - 1 > (colnr_T)textlen)
        {
            semsg(_(e_invalid_col), (long)start_col);
-           return;
+           return FAIL;
        }
 
        if (lnum == end_lnum)
@@ -312,7 +259,7 @@ prop_add_common(
        // 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;
+           return FAIL;
        // Copy the text, including terminating NUL.
        mch_memmove(newtext, buf->b_ml.ml_line_ptr, textlen);
 
@@ -351,8 +298,156 @@ prop_add_common(
        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);
+    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);
 }
 
index 7f1f4961586fb616347f6a23350e2fdfadd59cb2..97a58e3c0219260a328998e5fc4000b865b17fa8 100644 (file)
@@ -755,6 +755,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    3356,
 /**/
     3355,
 /**/