comparison 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
comparison
equal deleted inserted replaced
26241:ae5abfa1efc8 26242:685206b54ecf
895 col = dir < 0 ? buf->b_ml.ml_line_len : 1; 895 col = dir < 0 ? buf->b_ml.ml_line_len : 1;
896 } 896 }
897 } 897 }
898 898
899 /* 899 /*
900 * Returns TRUE if 'type_or_id' is in the 'types_or_ids' list.
901 */
902 static int
903 prop_type_or_id_in_list(int *types_or_ids, int len, int type_or_id)
904 {
905 int i;
906
907 for (i = 0; i < len; i++)
908 if (types_or_ids[i] == type_or_id)
909 return TRUE;
910
911 return FALSE;
912 }
913
914 /*
915 * Return all the text properties in line 'lnum' in buffer 'buf' in 'retlist'.
916 * If 'prop_types' is not NULL, then return only the text properties with
917 * matching property type in the 'prop_types' array.
918 * If 'prop_ids' is not NULL, then return only the text properties with
919 * an identifier in the 'props_ids' array.
920 * If 'add_lnum' is TRUE, then add the line number also to the text property
921 * dictionary.
922 */
923 static void
924 get_props_in_line(
925 buf_T *buf,
926 linenr_T lnum,
927 int *prop_types,
928 int prop_types_len,
929 int *prop_ids,
930 int prop_ids_len,
931 list_T *retlist,
932 int add_lnum)
933 {
934 char_u *text = ml_get_buf(buf, lnum, FALSE);
935 size_t textlen = STRLEN(text) + 1;
936 int count;
937 int i;
938 textprop_T prop;
939
940 count = (int)((buf->b_ml.ml_line_len - textlen) / sizeof(textprop_T));
941 for (i = 0; i < count; ++i)
942 {
943 mch_memmove(&prop, text + textlen + i * sizeof(textprop_T),
944 sizeof(textprop_T));
945 if ((prop_types == NULL
946 || prop_type_or_id_in_list(prop_types, prop_types_len,
947 prop.tp_type))
948 && (prop_ids == NULL ||
949 prop_type_or_id_in_list(prop_ids, prop_ids_len,
950 prop.tp_id)))
951 {
952 dict_T *d = dict_alloc();
953
954 if (d == NULL)
955 break;
956 prop_fill_dict(d, &prop, buf);
957 if (add_lnum)
958 dict_add_number(d, "lnum", lnum);
959 list_append_dict(retlist, d);
960 }
961 }
962 }
963
964 /*
965 * Convert a List of property type names into an array of property type
966 * identifiers. Returns a pointer to the allocated array. Returns NULL on
967 * error. 'num_types' is set to the number of returned property types.
968 */
969 static int *
970 get_prop_types_from_names(list_T *l, buf_T *buf, int *num_types)
971 {
972 int *prop_types;
973 listitem_T *li;
974 int i;
975 char_u *name;
976 proptype_T *type;
977
978 *num_types = 0;
979
980 prop_types = ALLOC_MULT(int, list_len(l));
981 if (prop_types == NULL)
982 return NULL;
983
984 i = 0;
985 FOR_ALL_LIST_ITEMS(l, li)
986 {
987 if (li->li_tv.v_type != VAR_STRING)
988 {
989 emsg(_(e_stringreq));
990 goto errret;
991 }
992 name = li->li_tv.vval.v_string;
993 if (name == NULL)
994 goto errret;
995
996 type = lookup_prop_type(name, buf);
997 if (type == NULL)
998 goto errret;
999 prop_types[i++] = type->pt_id;
1000 }
1001
1002 *num_types = i;
1003 return prop_types;
1004
1005 errret:
1006 VIM_CLEAR(prop_types);
1007 return NULL;
1008 }
1009
1010 /*
1011 * Convert a List of property identifiers into an array of property
1012 * identifiers. Returns a pointer to the allocated array. Returns NULL on
1013 * error. 'num_ids' is set to the number of returned property identifiers.
1014 */
1015 static int *
1016 get_prop_ids_from_list(list_T *l, int *num_ids)
1017 {
1018 int *prop_ids;
1019 listitem_T *li;
1020 int i;
1021 int id;
1022 int error;
1023
1024 *num_ids = 0;
1025
1026 prop_ids = ALLOC_MULT(int, list_len(l));
1027 if (prop_ids == NULL)
1028 return NULL;
1029
1030 i = 0;
1031 FOR_ALL_LIST_ITEMS(l, li)
1032 {
1033 error = FALSE;
1034 id = tv_get_number_chk(&li->li_tv, &error);
1035 if (error)
1036 goto errret;
1037
1038 prop_ids[i++] = id;
1039 }
1040
1041 *num_ids = i;
1042 return prop_ids;
1043
1044 errret:
1045 VIM_CLEAR(prop_ids);
1046 return NULL;
1047 }
1048
1049 /*
900 * prop_list({lnum} [, {bufnr}]) 1050 * prop_list({lnum} [, {bufnr}])
901 */ 1051 */
902 void 1052 void
903 f_prop_list(typval_T *argvars, typval_T *rettv) 1053 f_prop_list(typval_T *argvars, typval_T *rettv)
904 { 1054 {
905 linenr_T lnum; 1055 linenr_T lnum;
906 buf_T *buf = curbuf; 1056 linenr_T start_lnum;
1057 linenr_T end_lnum;
1058 buf_T *buf = curbuf;
1059 int add_lnum = FALSE;
1060 int *prop_types = NULL;
1061 int prop_types_len = 0;
1062 int *prop_ids = NULL;
1063 int prop_ids_len = 0;
1064 list_T *l;
1065 dictitem_T *di;
907 1066
908 if (in_vim9script() 1067 if (in_vim9script()
909 && (check_for_number_arg(argvars, 0) == FAIL 1068 && (check_for_number_arg(argvars, 0) == FAIL
910 || check_for_opt_dict_arg(argvars, 1) == FAIL)) 1069 || check_for_opt_dict_arg(argvars, 1) == FAIL))
911 return; 1070 return;
912 1071
913 lnum = tv_get_number(&argvars[0]); 1072 if (rettv_list_alloc(rettv) != OK)
1073 return;
1074
1075 // default: get text properties on current line
1076 start_lnum = tv_get_number(&argvars[0]);
1077 end_lnum = start_lnum;
914 if (argvars[1].v_type != VAR_UNKNOWN) 1078 if (argvars[1].v_type != VAR_UNKNOWN)
915 { 1079 {
1080 dict_T *d;
1081
1082 if (argvars[1].v_type != VAR_DICT)
1083 {
1084 emsg(_(e_dictreq));
1085 return;
1086 }
1087 d = argvars[1].vval.v_dict;
1088
916 if (get_bufnr_from_arg(&argvars[1], &buf) == FAIL) 1089 if (get_bufnr_from_arg(&argvars[1], &buf) == FAIL)
917 return; 1090 return;
918 } 1091
919 if (lnum < 1 || lnum > buf->b_ml.ml_line_count) 1092 if (d != NULL && (di = dict_find(d, (char_u *)"end_lnum", -1)) != NULL)
920 { 1093 {
1094 if (di->di_tv.v_type != VAR_NUMBER)
1095 {
1096 emsg(_(e_numberreq));
1097 return;
1098 }
1099 end_lnum = tv_get_number(&di->di_tv);
1100 if (end_lnum < 0)
1101 // negative end_lnum is used as an offset from the last buffer
1102 // line
1103 end_lnum = buf->b_ml.ml_line_count + end_lnum + 1;
1104 else if (end_lnum > buf->b_ml.ml_line_count)
1105 end_lnum = buf->b_ml.ml_line_count;
1106 add_lnum = TRUE;
1107 }
1108 if (d != NULL && (di = dict_find(d, (char_u *)"types", -1)) != NULL)
1109 {
1110 if (di->di_tv.v_type != VAR_LIST)
1111 {
1112 emsg(_(e_listreq));
1113 return;
1114 }
1115
1116 l = di->di_tv.vval.v_list;
1117 if (l != NULL && list_len(l) > 0)
1118 {
1119 prop_types = get_prop_types_from_names(l, buf, &prop_types_len);
1120 if (prop_types == NULL)
1121 return;
1122 }
1123 }
1124 if (d != NULL && (di = dict_find(d, (char_u *)"ids", -1)) != NULL)
1125 {
1126 if (di->di_tv.v_type != VAR_LIST)
1127 {
1128 emsg(_(e_listreq));
1129 goto errret;
1130 }
1131
1132 l = di->di_tv.vval.v_list;
1133 if (l != NULL && list_len(l) > 0)
1134 {
1135 prop_ids = get_prop_ids_from_list(l, &prop_ids_len);
1136 if (prop_ids == NULL)
1137 goto errret;
1138 }
1139 }
1140 }
1141 if (start_lnum < 1 || start_lnum > buf->b_ml.ml_line_count
1142 || end_lnum < 1 || end_lnum < start_lnum)
921 emsg(_(e_invalid_range)); 1143 emsg(_(e_invalid_range));
922 return; 1144 else
923 } 1145 for (lnum = start_lnum; lnum <= end_lnum; lnum++)
924 1146 get_props_in_line(buf, lnum, prop_types, prop_types_len,
925 if (rettv_list_alloc(rettv) == OK) 1147 prop_ids, prop_ids_len,
926 { 1148 rettv->vval.v_list, add_lnum);
927 char_u *text = ml_get_buf(buf, lnum, FALSE); 1149
928 size_t textlen = STRLEN(text) + 1; 1150 errret:
929 int count = (int)((buf->b_ml.ml_line_len - textlen) 1151 VIM_CLEAR(prop_types);
930 / sizeof(textprop_T)); 1152 VIM_CLEAR(prop_ids);
931 int i;
932 textprop_T prop;
933
934 for (i = 0; i < count; ++i)
935 {
936 dict_T *d = dict_alloc();
937
938 if (d == NULL)
939 break;
940 mch_memmove(&prop, text + textlen + i * sizeof(textprop_T),
941 sizeof(textprop_T));
942 prop_fill_dict(d, &prop, buf);
943 list_append_dict(rettv->vval.v_list, d);
944 }
945 }
946 } 1153 }
947 1154
948 /* 1155 /*
949 * prop_remove({props} [, {lnum} [, {lnum_end}]]) 1156 * prop_remove({props} [, {lnum} [, {lnum_end}]])
950 */ 1157 */