Mercurial > vim
diff src/textprop.c @ 26242:685206b54ecf v8.2.3652
patch 8.2.3652: can only get text properties one line at a time
Commit: https://github.com/vim/vim/commit/e021662f39b38ef7cf27e13850d0ce6890e48376
Author: Yegappan Lakshmanan <yegappan@yahoo.com>
Date: Tue Nov 23 11:46:32 2021 +0000
patch 8.2.3652: can only get text properties one line at a time
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)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Tue, 23 Nov 2021 13:00:05 +0100 |
parents | 78ef12e0ce8c |
children | 2df40c348c70 |
line wrap: on
line diff
--- a/src/textprop.c +++ b/src/textprop.c @@ -897,52 +897,259 @@ f_prop_find(typval_T *argvars, typval_T } /* + * 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); } /*