summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorYegappan Lakshmanan <yegappan@yahoo.com>2021-11-23 11:46:32 +0000
committerBram Moolenaar <Bram@vim.org>2021-11-23 11:46:32 +0000
commite021662f39b38ef7cf27e13850d0ce6890e48376 (patch)
tree4923c14f11926925612243326af7232f5b0f663a /src
parent04b568b38f848293e1ae0e680685280151acb386 (diff)
patch 8.2.3652: can only get text properties one line at a timev8.2.3652
Problem: Can only get text properties one line at a time. Solution: Add options to prop_list() to use a range of lines and filter by types. (Yegappan Lakshmanan, closes #9138)
Diffstat (limited to 'src')
-rw-r--r--src/testdir/test_textprop.vim151
-rw-r--r--src/textprop.c257
-rw-r--r--src/version.c2
3 files changed, 384 insertions, 26 deletions
diff --git a/src/testdir/test_textprop.vim b/src/testdir/test_textprop.vim
index 4b58784693..4a12b63d69 100644
--- a/src/testdir/test_textprop.vim
+++ b/src/testdir/test_textprop.vim
@@ -5,6 +5,7 @@ source check.vim
CheckFeature textprop
source screendump.vim
+source vim9.vim
func Test_proptype_global()
call prop_type_add('comment', {'highlight': 'Directory', 'priority': 123, 'start_incl': 1, 'end_incl': 1})
@@ -1645,6 +1646,154 @@ def Test_prop_bufnr_zero()
endtry
enddef
-
+" Tests for the prop_list() function
+func Test_prop_list()
+ let lines =<< trim END
+ new
+ call AddPropTypes()
+ call setline(1, repeat([repeat('a', 60)], 10))
+ call prop_add(1, 4, {'type': 'one', 'id': 5, 'end_col': 6})
+ call prop_add(1, 5, {'type': 'two', 'id': 10, 'end_col': 7})
+ call prop_add(3, 12, {'type': 'one', 'id': 20, 'end_col': 14})
+ call prop_add(3, 13, {'type': 'two', 'id': 10, 'end_col': 15})
+ call prop_add(5, 20, {'type': 'one', 'id': 10, 'end_col': 22})
+ call prop_add(5, 21, {'type': 'two', 'id': 20, 'end_col': 23})
+ call assert_equal([
+ \ {'id': 5, 'col': 4, 'type_bufnr': 0, 'end': 1,
+ \ 'type': 'one', 'length': 2, 'start': 1},
+ \ {'id': 10, 'col': 5, 'type_bufnr': 0, 'end': 1,
+ \ 'type': 'two', 'length': 2, 'start': 1}], prop_list(1))
+ #" text properties between a few lines
+ call assert_equal([
+ \ {'lnum': 3, 'id': 20, 'col': 12, 'type_bufnr': 0, 'end': 1,
+ \ 'type': 'one', 'length': 2, 'start': 1},
+ \ {'lnum': 3, 'id': 10, 'col': 13, 'type_bufnr': 0, 'end': 1,
+ \ 'type': 'two', 'length': 2, 'start': 1},
+ \ {'lnum': 5, 'id': 10, 'col': 20, 'type_bufnr': 0, 'end': 1,
+ \ 'type': 'one', 'length': 2, 'start': 1},
+ \ {'lnum': 5, 'id': 20, 'col': 21, 'type_bufnr': 0, 'end': 1,
+ \ 'type': 'two', 'length': 2, 'start': 1}],
+ \ prop_list(2, {'end_lnum': 5}))
+ #" text properties across all the lines
+ call assert_equal([
+ \ {'lnum': 1, 'id': 5, 'col': 4, 'type_bufnr': 0, 'end': 1,
+ \ 'type': 'one', 'length': 2, 'start': 1},
+ \ {'lnum': 3, 'id': 20, 'col': 12, 'type_bufnr': 0, 'end': 1,
+ \ 'type': 'one', 'length': 2, 'start': 1},
+ \ {'lnum': 5, 'id': 10, 'col': 20, 'type_bufnr': 0, 'end': 1,
+ \ 'type': 'one', 'length': 2, 'start': 1}],
+ \ prop_list(1, {'types': ['one'], 'end_lnum': -1}))
+ #" text properties with the specified identifier
+ call assert_equal([
+ \ {'lnum': 3, 'id': 20, 'col': 12, 'type_bufnr': 0, 'end': 1,
+ \ 'type': 'one', 'length': 2, 'start': 1},
+ \ {'lnum': 5, 'id': 20, 'col': 21, 'type_bufnr': 0, 'end': 1,
+ \ 'type': 'two', 'length': 2, 'start': 1}],
+ \ prop_list(1, {'ids': [20], 'end_lnum': 10}))
+ #" text properties of the specified type and id
+ call assert_equal([
+ \ {'lnum': 1, 'id': 10, 'col': 5, 'type_bufnr': 0, 'end': 1,
+ \ 'type': 'two', 'length': 2, 'start': 1},
+ \ {'lnum': 3, 'id': 10, 'col': 13, 'type_bufnr': 0, 'end': 1,
+ \ 'type': 'two', 'length': 2, 'start': 1}],
+ \ prop_list(1, {'types': ['two'], 'ids': [10], 'end_lnum': 20}))
+ call assert_equal([], prop_list(1, {'ids': [40, 50], 'end_lnum': 10}))
+ call assert_equal([], prop_list(6, {'end_lnum': 10}))
+ call assert_equal([], prop_list(2, {'end_lnum': 2}))
+ #" error cases
+ call assert_fails("echo prop_list(1, {'end_lnum': -20})", 'E16:')
+ call assert_fails("echo prop_list(4, {'end_lnum': 2})", 'E16:')
+ call assert_fails("echo prop_list(1, {'end_lnum': '$'})", 'E889:')
+ call assert_fails("echo prop_list(1, {'types': ['blue'], 'end_lnum': 10})",
+ \ 'E971:')
+ call assert_fails("echo prop_list(1, {'types': ['one', 'blue'],
+ \ 'end_lnum': 10})", 'E971:')
+ call assert_fails("echo prop_list(1, {'types': ['one', 10],
+ \ 'end_lnum': 10})", 'E928:')
+ call assert_fails("echo prop_list(1, {'types': ['']})", 'E971:')
+ call assert_equal([], prop_list(2, {'types': []}))
+ call assert_equal([], prop_list(2, {'types': test_null_list()}))
+ call assert_fails("call prop_list(1, {'types': {}})", 'E714:')
+ call assert_fails("call prop_list(1, {'types': 'one'})", 'E714:')
+ call assert_equal([], prop_list(2, {'types': ['one'],
+ \ 'ids': test_null_list()}))
+ call assert_equal([], prop_list(2, {'types': ['one'], 'ids': []}))
+ call assert_fails("call prop_list(1, {'types': ['one'], 'ids': {}})",
+ \ 'E714:')
+ call assert_fails("call prop_list(1, {'types': ['one'], 'ids': 10})",
+ \ 'E714:')
+ call assert_fails("call prop_list(1, {'types': ['one'], 'ids': [[]]})",
+ \ 'E745:')
+ call assert_fails("call prop_list(1, {'types': ['one'], 'ids': [10, []]})",
+ \ 'E745:')
+
+ #" get text properties from a non-current buffer
+ wincmd w
+ call assert_equal([
+ \ {'lnum': 1, 'id': 5, 'col': 4, 'type_bufnr': 0, 'end': 1,
+ \ 'type': 'one', 'length': 2, 'start': 1},
+ \ {'lnum': 1, 'id': 10, 'col': 5, 'type_bufnr': 0, 'end': 1,
+ \ 'type': 'two', 'length': 2, 'start': 1},
+ \ {'lnum': 3, 'id': 20, 'col': 12, 'type_bufnr': 0, 'end': 1,
+ \ 'type': 'one', 'length': 2, 'start': 1},
+ \ {'lnum': 3, 'id': 10, 'col': 13, 'type_bufnr': 0, 'end': 1,
+ \ 'type': 'two', 'length': 2, 'start': 1}],
+ \ prop_list(1, {'bufnr': winbufnr(1), 'end_lnum': 4}))
+ wincmd w
+
+ #" get text properties after clearing all the properties
+ call prop_clear(1, line('$'))
+ call assert_equal([], prop_list(1, {'end_lnum': 10}))
+
+ call prop_add(2, 4, {'type': 'one', 'id': 5, 'end_col': 6})
+ call prop_add(2, 4, {'type': 'two', 'id': 10, 'end_col': 6})
+ call prop_add(2, 4, {'type': 'three', 'id': 15, 'end_col': 6})
+ #" get text properties with a list of types
+ call assert_equal([
+ \ {'id': 10, 'col': 4, 'type_bufnr': 0, 'end': 1,
+ \ 'type': 'two', 'length': 2, 'start': 1},
+ \ {'id': 5, 'col': 4, 'type_bufnr': 0, 'end': 1,
+ \ 'type': 'one', 'length': 2, 'start': 1}],
+ \ prop_list(2, {'types': ['one', 'two']}))
+ call assert_equal([
+ \ {'id': 15, 'col': 4, 'type_bufnr': 0, 'end': 1,
+ \ 'type': 'three', 'length': 2, 'start': 1},
+ \ {'id': 5, 'col': 4, 'type_bufnr': 0, 'end': 1,
+ \ 'type': 'one', 'length': 2, 'start': 1}],
+ \ prop_list(2, {'types': ['one', 'three']}))
+ #" get text properties with a list of identifiers
+ call assert_equal([
+ \ {'id': 10, 'col': 4, 'type_bufnr': 0, 'end': 1,
+ \ 'type': 'two', 'length': 2, 'start': 1},
+ \ {'id': 5, 'col': 4, 'type_bufnr': 0, 'end': 1,
+ \ 'type': 'one', 'length': 2, 'start': 1}],
+ \ prop_list(2, {'ids': [5, 10, 20]}))
+ call prop_clear(1, line('$'))
+ call assert_equal([], prop_list(2, {'types': ['one', 'two']}))
+ call assert_equal([], prop_list(2, {'ids': [5, 10, 20]}))
+
+ #" get text properties from a hidden buffer
+ edit! Xaaa
+ call setline(1, repeat([repeat('b', 60)], 10))
+ call prop_add(1, 4, {'type': 'one', 'id': 5, 'end_col': 6})
+ call prop_add(4, 8, {'type': 'two', 'id': 10, 'end_col': 10})
+ VAR bnr = bufnr()
+ hide edit Xbbb
+ call assert_equal([
+ \ {'lnum': 1, 'id': 5, 'col': 4, 'type_bufnr': 0, 'end': 1,
+ \ 'type': 'one', 'length': 2, 'start': 1},
+ \ {'lnum': 4, 'id': 10, 'col': 8, 'type_bufnr': 0, 'end': 1,
+ \ 'type': 'two', 'length': 2, 'start': 1}],
+ \ prop_list(1, {'bufnr': bnr,
+ \ 'types': ['one', 'two'], 'ids': [5, 10], 'end_lnum': -1}))
+ #" get text properties from an unloaded buffer
+ bunload! Xaaa
+ call assert_equal([], prop_list(1, {'bufnr': bnr, 'end_lnum': -1}))
+
+ call DeletePropTypes()
+ :%bw!
+ END
+ call CheckLegacyAndVim9Success(lines)
+endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/textprop.c b/src/textprop.c
index 26c7f52084..beb82a35b5 100644
--- a/src/textprop.c
+++ b/src/textprop.c
@@ -897,52 +897,259 @@ f_prop_find(typval_T *argvars, typval_T *rettv)
}
/*
+ * Returns TRUE if 'type_or_id' is in the 'types_or_ids' list.
+ */
+ static int
+prop_type_or_id_in_list(int *types_or_ids, int len, int type_or_id)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ if (types_or_ids[i] == type_or_id)
+ return TRUE;
+
+ return FALSE;
+}
+
+/*
+ * Return all the text properties in line 'lnum' in buffer 'buf' in 'retlist'.
+ * If 'prop_types' is not NULL, then return only the text properties with
+ * matching property type in the 'prop_types' array.
+ * If 'prop_ids' is not NULL, then return only the text properties with
+ * an identifier in the 'props_ids' array.
+ * If 'add_lnum' is TRUE, then add the line number also to the text property
+ * dictionary.
+ */
+ static void
+get_props_in_line(
+ buf_T *buf,
+ linenr_T lnum,
+ int *prop_types,
+ int prop_types_len,
+ int *prop_ids,
+ int prop_ids_len,
+ list_T *retlist,
+ int add_lnum)
+{
+ char_u *text = ml_get_buf(buf, lnum, FALSE);
+ size_t textlen = STRLEN(text) + 1;
+ int count;
+ int i;
+ textprop_T prop;
+
+ count = (int)((buf->b_ml.ml_line_len - textlen) / sizeof(textprop_T));
+ for (i = 0; i < count; ++i)
+ {
+ mch_memmove(&prop, text + textlen + i * sizeof(textprop_T),
+ sizeof(textprop_T));
+ if ((prop_types == NULL
+ || prop_type_or_id_in_list(prop_types, prop_types_len,
+ prop.tp_type))
+ && (prop_ids == NULL ||
+ prop_type_or_id_in_list(prop_ids, prop_ids_len,
+ prop.tp_id)))
+ {
+ dict_T *d = dict_alloc();
+
+ if (d == NULL)
+ break;
+ prop_fill_dict(d, &prop, buf);
+ if (add_lnum)
+ dict_add_number(d, "lnum", lnum);
+ list_append_dict(retlist, d);
+ }
+ }
+}
+
+/*
+ * Convert a List of property type names into an array of property type
+ * identifiers. Returns a pointer to the allocated array. Returns NULL on
+ * error. 'num_types' is set to the number of returned property types.
+ */
+ static int *
+get_prop_types_from_names(list_T *l, buf_T *buf, int *num_types)
+{
+ int *prop_types;
+ listitem_T *li;
+ int i;
+ char_u *name;
+ proptype_T *type;
+
+ *num_types = 0;
+
+ prop_types = ALLOC_MULT(int, list_len(l));
+ if (prop_types == NULL)
+ return NULL;
+
+ i = 0;
+ FOR_ALL_LIST_ITEMS(l, li)
+ {
+ if (li->li_tv.v_type != VAR_STRING)
+ {
+ emsg(_(e_stringreq));
+ goto errret;
+ }
+ name = li->li_tv.vval.v_string;
+ if (name == NULL)
+ goto errret;
+
+ type = lookup_prop_type(name, buf);
+ if (type == NULL)
+ goto errret;
+ prop_types[i++] = type->pt_id;
+ }
+
+ *num_types = i;
+ return prop_types;
+
+errret:
+ VIM_CLEAR(prop_types);
+ return NULL;
+}
+
+/*
+ * Convert a List of property identifiers into an array of property
+ * identifiers. Returns a pointer to the allocated array. Returns NULL on
+ * error. 'num_ids' is set to the number of returned property identifiers.
+ */
+ static int *
+get_prop_ids_from_list(list_T *l, int *num_ids)
+{
+ int *prop_ids;
+ listitem_T *li;
+ int i;
+ int id;
+ int error;
+
+ *num_ids = 0;
+
+ prop_ids = ALLOC_MULT(int, list_len(l));
+ if (prop_ids == NULL)
+ return NULL;
+
+ i = 0;
+ FOR_ALL_LIST_ITEMS(l, li)
+ {
+ error = FALSE;
+ id = tv_get_number_chk(&li->li_tv, &error);
+ if (error)
+ goto errret;
+
+ prop_ids[i++] = id;
+ }
+
+ *num_ids = i;
+ return prop_ids;
+
+errret:
+ VIM_CLEAR(prop_ids);
+ return NULL;
+}
+
+/*
* prop_list({lnum} [, {bufnr}])
*/
void
f_prop_list(typval_T *argvars, typval_T *rettv)
{
- linenr_T lnum;
- buf_T *buf = curbuf;
+ linenr_T lnum;
+ linenr_T start_lnum;
+ linenr_T end_lnum;
+ buf_T *buf = curbuf;
+ int add_lnum = FALSE;
+ int *prop_types = NULL;
+ int prop_types_len = 0;
+ int *prop_ids = NULL;
+ int prop_ids_len = 0;
+ list_T *l;
+ dictitem_T *di;
if (in_vim9script()
&& (check_for_number_arg(argvars, 0) == FAIL
|| check_for_opt_dict_arg(argvars, 1) == FAIL))
return;
- lnum = tv_get_number(&argvars[0]);
+ if (rettv_list_alloc(rettv) != OK)
+ return;
+
+ // default: get text properties on current line
+ start_lnum = tv_get_number(&argvars[0]);
+ end_lnum = start_lnum;
if (argvars[1].v_type != VAR_UNKNOWN)
{
+ dict_T *d;
+
+ if (argvars[1].v_type != VAR_DICT)
+ {
+ emsg(_(e_dictreq));
+ return;
+ }
+ d = argvars[1].vval.v_dict;
+
if (get_bufnr_from_arg(&argvars[1], &buf) == FAIL)
return;
- }
- if (lnum < 1 || lnum > buf->b_ml.ml_line_count)
- {
- emsg(_(e_invalid_range));
- return;
- }
- if (rettv_list_alloc(rettv) == OK)
- {
- 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;
+ if (d != NULL && (di = dict_find(d, (char_u *)"end_lnum", -1)) != NULL)
+ {
+ if (di->di_tv.v_type != VAR_NUMBER)
+ {
+ emsg(_(e_numberreq));
+ return;
+ }
+ end_lnum = tv_get_number(&di->di_tv);
+ if (end_lnum < 0)
+ // negative end_lnum is used as an offset from the last buffer
+ // line
+ end_lnum = buf->b_ml.ml_line_count + end_lnum + 1;
+ else if (end_lnum > buf->b_ml.ml_line_count)
+ end_lnum = buf->b_ml.ml_line_count;
+ add_lnum = TRUE;
+ }
+ if (d != NULL && (di = dict_find(d, (char_u *)"types", -1)) != NULL)
+ {
+ if (di->di_tv.v_type != VAR_LIST)
+ {
+ emsg(_(e_listreq));
+ return;
+ }
- for (i = 0; i < count; ++i)
+ l = di->di_tv.vval.v_list;
+ if (l != NULL && list_len(l) > 0)
+ {
+ prop_types = get_prop_types_from_names(l, buf, &prop_types_len);
+ if (prop_types == NULL)
+ return;
+ }
+ }
+ if (d != NULL && (di = dict_find(d, (char_u *)"ids", -1)) != NULL)
{
- dict_T *d = dict_alloc();
+ if (di->di_tv.v_type != VAR_LIST)
+ {
+ emsg(_(e_listreq));
+ goto errret;
+ }
- if (d == NULL)
- 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);
+ l = di->di_tv.vval.v_list;
+ if (l != NULL && list_len(l) > 0)
+ {
+ prop_ids = get_prop_ids_from_list(l, &prop_ids_len);
+ if (prop_ids == NULL)
+ goto errret;
+ }
}
}
+ if (start_lnum < 1 || start_lnum > buf->b_ml.ml_line_count
+ || end_lnum < 1 || end_lnum < start_lnum)
+ emsg(_(e_invalid_range));
+ else
+ for (lnum = start_lnum; lnum <= end_lnum; lnum++)
+ get_props_in_line(buf, lnum, prop_types, prop_types_len,
+ prop_ids, prop_ids_len,
+ rettv->vval.v_list, add_lnum);
+
+errret:
+ VIM_CLEAR(prop_types);
+ VIM_CLEAR(prop_ids);
}
/*
diff --git a/src/version.c b/src/version.c
index f2bcb190f3..0644ba2226 100644
--- a/src/version.c
+++ b/src/version.c
@@ -758,6 +758,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 3652,
+/**/
3651,
/**/
3650,