Mercurial > vim
comparison src/textprop.c @ 15138:9df130fd5e0d v8.1.0579
patch 8.1.0579: cannot attach properties to text
commit https://github.com/vim/vim/commit/98aefe7c3250bb5d4153b994f878594d1745424e
Author: Bram Moolenaar <Bram@vim.org>
Date: Thu Dec 13 22:20:09 2018 +0100
patch 8.1.0579: cannot attach properties to text
Problem: Cannot attach properties to text.
Solution: First part of adding text properties.
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Thu, 13 Dec 2018 22:30:08 +0100 |
parents | |
children | 7960bf50d345 |
comparison
equal
deleted
inserted
replaced
15137:44f47a35a3f4 | 15138:9df130fd5e0d |
---|---|
1 /* vi:set ts=8 sts=4 sw=4 noet: | |
2 * | |
3 * VIM - Vi IMproved by Bram Moolenaar | |
4 * | |
5 * Do ":help uganda" in Vim to read copying and usage conditions. | |
6 * Do ":help credits" in Vim to see a list of people who contributed. | |
7 * See README.txt for an overview of the Vim source code. | |
8 */ | |
9 | |
10 /* | |
11 * Text properties implementation. | |
12 * | |
13 * Text properties are attached to the text. They move with the text when | |
14 * text is inserted/deleted. | |
15 * | |
16 * Text properties have a user specified ID number, which can be unique. | |
17 * Text properties have a type, which can be used to specify highlighting. | |
18 * | |
19 * TODO: | |
20 * - Add an arrray for global_proptypes, to quickly lookup a proptype by ID | |
21 * - Add an arrray for b_proptypes, to quickly lookup a proptype by ID | |
22 * - adjust property column when text is inserted/deleted | |
23 * - support properties that continue over a line break | |
24 * - add mechanism to keep track of changed lines. | |
25 */ | |
26 | |
27 #include "vim.h" | |
28 | |
29 #if defined(FEAT_TEXT_PROP) || defined(PROTO) | |
30 | |
31 /* | |
32 * In a hashtable item "hi_key" points to "pt_name" in a proptype_T. | |
33 * This avoids adding a pointer to the hashtable item. | |
34 * PT2HIKEY() converts a proptype pointer to a hashitem key pointer. | |
35 * HIKEY2PT() converts a hashitem key pointer to a proptype pointer. | |
36 * HI2PT() converts a hashitem pointer to a proptype pointer. | |
37 */ | |
38 #define PT2HIKEY(p) ((p)->pt_name) | |
39 #define HIKEY2PT(p) ((proptype_T *)((p) - offsetof(proptype_T, pt_name))) | |
40 #define HI2PT(hi) HIKEY2PT((hi)->hi_key) | |
41 | |
42 // The global text property types. | |
43 static hashtab_T *global_proptypes = NULL; | |
44 | |
45 // The last used text property type ID. | |
46 static int proptype_id = 0; | |
47 | |
48 static char_u e_type_not_exist[] = N_("E971: Property type %s does not exist"); | |
49 static char_u e_invalid_col[] = N_("E964: Invalid column number: %ld"); | |
50 | |
51 /* | |
52 * Find a property type by name, return the hashitem. | |
53 * Returns NULL if the item can't be found. | |
54 */ | |
55 static hashitem_T * | |
56 find_prop_hi(char_u *name, buf_T *buf) | |
57 { | |
58 hashtab_T *ht; | |
59 hashitem_T *hi; | |
60 | |
61 if (*name == NUL) | |
62 return NULL; | |
63 if (buf == NULL) | |
64 ht = global_proptypes; | |
65 else | |
66 ht = buf->b_proptypes; | |
67 | |
68 if (ht == NULL) | |
69 return NULL; | |
70 hi = hash_find(ht, name); | |
71 if (HASHITEM_EMPTY(hi)) | |
72 return NULL; | |
73 return hi; | |
74 } | |
75 | |
76 /* | |
77 * Like find_prop_hi() but return the property type. | |
78 */ | |
79 static proptype_T * | |
80 find_prop(char_u *name, buf_T *buf) | |
81 { | |
82 hashitem_T *hi = find_prop_hi(name, buf); | |
83 | |
84 if (hi == NULL) | |
85 return NULL; | |
86 return HI2PT(hi); | |
87 } | |
88 | |
89 /* | |
90 * Lookup a property type by name. First in "buf" and when not found in the | |
91 * global types. | |
92 * When not found gives an error message and returns NULL. | |
93 */ | |
94 static proptype_T * | |
95 lookup_prop_type(char_u *name, buf_T *buf) | |
96 { | |
97 proptype_T *type = find_prop(name, buf); | |
98 | |
99 if (type == NULL) | |
100 type = find_prop(name, NULL); | |
101 if (type == NULL) | |
102 EMSG2(_(e_type_not_exist), name); | |
103 return type; | |
104 } | |
105 | |
106 /* | |
107 * Get an optional "bufnr" item from the dict in "arg". | |
108 * When the argument is not used or "bufnr" is not present then "buf" is | |
109 * unchanged. | |
110 * If "bufnr" is valid or not present return OK. | |
111 * When "arg" is not a dict or "bufnr" is invalide return FAIL. | |
112 */ | |
113 static int | |
114 get_bufnr_from_arg(typval_T *arg, buf_T **buf) | |
115 { | |
116 dictitem_T *di; | |
117 | |
118 if (arg->v_type != VAR_DICT) | |
119 { | |
120 EMSG(_(e_dictreq)); | |
121 return FAIL; | |
122 } | |
123 if (arg->vval.v_dict == NULL) | |
124 return OK; // NULL dict is like an empty dict | |
125 di = dict_find(arg->vval.v_dict, (char_u *)"bufnr", -1); | |
126 if (di != NULL) | |
127 { | |
128 *buf = get_buf_tv(&di->di_tv, FALSE); | |
129 if (*buf == NULL) | |
130 return FAIL; | |
131 } | |
132 return OK; | |
133 } | |
134 | |
135 /* | |
136 * prop_add({lnum}, {col}, {props}) | |
137 */ | |
138 void | |
139 f_prop_add(typval_T *argvars, typval_T *rettv UNUSED) | |
140 { | |
141 linenr_T lnum; | |
142 colnr_T col; | |
143 dict_T *dict; | |
144 colnr_T length = 1; | |
145 char_u *type_name; | |
146 proptype_T *type; | |
147 buf_T *buf = curbuf; | |
148 int id = 0; | |
149 char_u *newtext; | |
150 int proplen; | |
151 size_t textlen; | |
152 char_u *props; | |
153 char_u *newprops; | |
154 static textprop_T tmp_prop; // static to get it aligned. | |
155 int i; | |
156 | |
157 lnum = get_tv_number(&argvars[0]); | |
158 col = get_tv_number(&argvars[1]); | |
159 if (col < 1) | |
160 { | |
161 EMSGN(_(e_invalid_col), (long)col); | |
162 return; | |
163 } | |
164 if (argvars[2].v_type != VAR_DICT) | |
165 { | |
166 EMSG(_(e_dictreq)); | |
167 return; | |
168 } | |
169 dict = argvars[2].vval.v_dict; | |
170 | |
171 if (dict == NULL || dict_find(dict, (char_u *)"type", -1) == NULL) | |
172 { | |
173 EMSG(_("E965: missing property type name")); | |
174 return; | |
175 } | |
176 type_name = get_dict_string(dict, (char_u *)"type", FALSE); | |
177 | |
178 if (dict_find(dict, (char_u *)"end_lnum", -1) != NULL) | |
179 { | |
180 // TODO: handle end_lnum | |
181 EMSG("Sorry, end_lnum not supported yet"); | |
182 return; | |
183 } | |
184 | |
185 if (dict_find(dict, (char_u *)"length", -1) != NULL) | |
186 length = get_dict_number(dict, (char_u *)"length"); | |
187 else if (dict_find(dict, (char_u *)"end_col", -1) != NULL) | |
188 { | |
189 length = get_dict_number(dict, (char_u *)"end_col") - col; | |
190 if (length <= 0) | |
191 { | |
192 EMSG2(_(e_invargval), "end_col"); | |
193 return; | |
194 } | |
195 } | |
196 | |
197 if (dict_find(dict, (char_u *)"id", -1) != NULL) | |
198 id = get_dict_number(dict, (char_u *)"id"); | |
199 | |
200 if (get_bufnr_from_arg(&argvars[2], &buf) == FAIL) | |
201 return; | |
202 | |
203 type = lookup_prop_type(type_name, buf); | |
204 if (type == NULL) | |
205 return; | |
206 | |
207 if (lnum < 1 || lnum > buf->b_ml.ml_line_count) | |
208 { | |
209 EMSGN(_("E966: Invalid line number: %ld"), (long)lnum); | |
210 return; | |
211 } | |
212 | |
213 // Fetch the line to get the ml_line_len field updated. | |
214 proplen = get_text_props(buf, lnum, &props, TRUE); | |
215 | |
216 if (col >= (colnr_T)STRLEN(buf->b_ml.ml_line_ptr)) | |
217 { | |
218 EMSGN(_(e_invalid_col), (long)col); | |
219 return; | |
220 } | |
221 | |
222 // Allocate the new line with space for the new proprety. | |
223 newtext = alloc(buf->b_ml.ml_line_len + sizeof(textprop_T)); | |
224 if (newtext == NULL) | |
225 return; | |
226 // Copy the text, including terminating NUL. | |
227 textlen = buf->b_ml.ml_line_len - proplen * sizeof(textprop_T); | |
228 mch_memmove(newtext, buf->b_ml.ml_line_ptr, textlen); | |
229 | |
230 // Find the index where to insert the new property. | |
231 // Since the text properties are not aligned properly when stored with the | |
232 // text, we need to copy them as bytes before using it as a struct. | |
233 for (i = 0; i < proplen; ++i) | |
234 { | |
235 mch_memmove(&tmp_prop, props + i * sizeof(proptype_T), | |
236 sizeof(proptype_T)); | |
237 if (tmp_prop.tp_col >= col) | |
238 break; | |
239 } | |
240 newprops = newtext + textlen; | |
241 if (i > 0) | |
242 mch_memmove(newprops, props, sizeof(textprop_T) * i); | |
243 | |
244 tmp_prop.tp_col = col; | |
245 tmp_prop.tp_len = length; | |
246 tmp_prop.tp_id = id; | |
247 tmp_prop.tp_type = type->pt_id; | |
248 tmp_prop.tp_flags = 0; | |
249 mch_memmove(newprops + i * sizeof(textprop_T), &tmp_prop, | |
250 sizeof(textprop_T)); | |
251 | |
252 if (i < proplen) | |
253 mch_memmove(newprops + (i + 1) * sizeof(textprop_T), | |
254 props + i * sizeof(textprop_T), | |
255 sizeof(textprop_T) * (proplen - i)); | |
256 | |
257 if (buf->b_ml.ml_flags & ML_LINE_DIRTY) | |
258 vim_free(buf->b_ml.ml_line_ptr); | |
259 buf->b_ml.ml_line_ptr = newtext; | |
260 buf->b_ml.ml_line_len += sizeof(textprop_T); | |
261 buf->b_ml.ml_flags |= ML_LINE_DIRTY; | |
262 | |
263 redraw_buf_later(buf, NOT_VALID); | |
264 } | |
265 | |
266 /* | |
267 * Return TRUE if any text properties are defined globally or for buffer | |
268 * 'buf". | |
269 */ | |
270 int | |
271 has_any_text_properties(buf_T *buf) | |
272 { | |
273 return buf->b_proptypes != NULL || global_proptypes != NULL; | |
274 } | |
275 | |
276 /* | |
277 * Fetch the text properties for line "lnum" in buffer 'buf". | |
278 * Returns the number of text properties and, when non-zero, a pointer to the | |
279 * first one in "props" (note that it is not aligned, therefore the char_u | |
280 * pointer). | |
281 */ | |
282 int | |
283 get_text_props(buf_T *buf, linenr_T lnum, char_u **props, int will_change) | |
284 { | |
285 char_u *text; | |
286 size_t textlen; | |
287 size_t proplen; | |
288 | |
289 // Be quick when no text property types are defined. | |
290 if (!has_any_text_properties(buf)) | |
291 return 0; | |
292 | |
293 // Fetch the line to get the ml_line_len field updated. | |
294 text = ml_get_buf(buf, lnum, will_change); | |
295 textlen = STRLEN(text) + 1; | |
296 proplen = buf->b_ml.ml_line_len - textlen; | |
297 if (proplen % sizeof(textprop_T) != 0) | |
298 { | |
299 IEMSG(_("E967: text property info corrupted")); | |
300 return 0; | |
301 } | |
302 if (proplen > 0) | |
303 *props = text + textlen; | |
304 return proplen / sizeof(textprop_T); | |
305 } | |
306 | |
307 static proptype_T * | |
308 find_type_by_id(hashtab_T *ht, int id) | |
309 { | |
310 long todo; | |
311 hashitem_T *hi; | |
312 | |
313 if (ht == NULL) | |
314 return NULL; | |
315 | |
316 // TODO: Make this faster by keeping a list of types sorted on ID and use | |
317 // a binary search. | |
318 | |
319 todo = (long)ht->ht_used; | |
320 for (hi = ht->ht_array; todo > 0; ++hi) | |
321 { | |
322 if (!HASHITEM_EMPTY(hi)) | |
323 { | |
324 proptype_T *prop = HI2PT(hi); | |
325 | |
326 if (prop->pt_id == id) | |
327 return prop; | |
328 --todo; | |
329 } | |
330 } | |
331 return NULL; | |
332 } | |
333 | |
334 /* | |
335 * Find a property type by ID in "buf" or globally. | |
336 * Returns NULL if not found. | |
337 */ | |
338 proptype_T * | |
339 text_prop_type_by_id(buf_T *buf, int id) | |
340 { | |
341 proptype_T *type; | |
342 | |
343 type = find_type_by_id(buf->b_proptypes, id); | |
344 if (type == NULL) | |
345 type = find_type_by_id(global_proptypes, id); | |
346 return type; | |
347 } | |
348 | |
349 /* | |
350 * prop_clear({lnum} [, {lnum_end} [, {bufnr}]]) | |
351 */ | |
352 void | |
353 f_prop_clear(typval_T *argvars, typval_T *rettv UNUSED) | |
354 { | |
355 linenr_T start = get_tv_number(&argvars[0]); | |
356 linenr_T end = start; | |
357 linenr_T lnum; | |
358 buf_T *buf = curbuf; | |
359 | |
360 if (argvars[1].v_type != VAR_UNKNOWN) | |
361 { | |
362 end = get_tv_number(&argvars[1]); | |
363 if (argvars[2].v_type != VAR_UNKNOWN) | |
364 { | |
365 if (get_bufnr_from_arg(&argvars[2], &buf) == FAIL) | |
366 return; | |
367 } | |
368 } | |
369 if (start < 1 || end < 1) | |
370 { | |
371 EMSG(_(e_invrange)); | |
372 return; | |
373 } | |
374 | |
375 for (lnum = start; lnum <= end; ++lnum) | |
376 { | |
377 char_u *text; | |
378 size_t len; | |
379 | |
380 if (lnum > buf->b_ml.ml_line_count) | |
381 break; | |
382 text = ml_get_buf(buf, lnum, FALSE); | |
383 len = STRLEN(text) + 1; | |
384 if ((size_t)buf->b_ml.ml_line_len > len) | |
385 { | |
386 if (!(buf->b_ml.ml_flags & ML_LINE_DIRTY)) | |
387 { | |
388 char_u *newtext = vim_strsave(text); | |
389 | |
390 // need to allocate the line now | |
391 if (newtext == NULL) | |
392 return; | |
393 buf->b_ml.ml_line_ptr = newtext; | |
394 buf->b_ml.ml_flags |= ML_LINE_DIRTY; | |
395 } | |
396 buf->b_ml.ml_line_len = len; | |
397 } | |
398 } | |
399 redraw_buf_later(buf, NOT_VALID); | |
400 } | |
401 | |
402 /* | |
403 * prop_list({lnum} [, {bufnr}]) | |
404 */ | |
405 void | |
406 f_prop_list(typval_T *argvars, typval_T *rettv) | |
407 { | |
408 linenr_T lnum = get_tv_number(&argvars[0]); | |
409 buf_T *buf = curbuf; | |
410 | |
411 if (argvars[1].v_type != VAR_UNKNOWN) | |
412 { | |
413 if (get_bufnr_from_arg(&argvars[1], &buf) == FAIL) | |
414 return; | |
415 } | |
416 if (lnum < 1 || lnum > buf->b_ml.ml_line_count) | |
417 { | |
418 EMSG(_(e_invrange)); | |
419 return; | |
420 } | |
421 | |
422 if (rettv_list_alloc(rettv) == OK) | |
423 { | |
424 char_u *text = ml_get_buf(buf, lnum, FALSE); | |
425 size_t textlen = STRLEN(text) + 1; | |
426 int count = (buf->b_ml.ml_line_len - textlen) | |
427 / sizeof(textprop_T); | |
428 int i; | |
429 textprop_T prop; | |
430 proptype_T *pt; | |
431 | |
432 for (i = 0; i < count; ++i) | |
433 { | |
434 dict_T *d = dict_alloc(); | |
435 | |
436 if (d == NULL) | |
437 break; | |
438 mch_memmove(&prop, text + textlen + i * sizeof(textprop_T), | |
439 sizeof(textprop_T)); | |
440 dict_add_number(d, "col", prop.tp_col); | |
441 dict_add_number(d, "length", prop.tp_len); | |
442 dict_add_number(d, "id", prop.tp_id); | |
443 dict_add_number(d, "start", !(prop.tp_flags & TP_FLAG_CONT_PREV)); | |
444 dict_add_number(d, "end", !(prop.tp_flags & TP_FLAG_CONT_NEXT)); | |
445 pt = text_prop_type_by_id(buf, prop.tp_type); | |
446 if (pt != NULL) | |
447 dict_add_string(d, "type", pt->pt_name); | |
448 | |
449 list_append_dict(rettv->vval.v_list, d); | |
450 } | |
451 } | |
452 } | |
453 | |
454 /* | |
455 * prop_remove({props} [, {lnum} [, {lnum_end}]]) | |
456 */ | |
457 void | |
458 f_prop_remove(typval_T *argvars, typval_T *rettv) | |
459 { | |
460 linenr_T start = 1; | |
461 linenr_T end = 0; | |
462 linenr_T lnum; | |
463 dict_T *dict; | |
464 buf_T *buf = curbuf; | |
465 dictitem_T *di; | |
466 int do_all = FALSE; | |
467 int id = -1; | |
468 int type_id = -1; | |
469 | |
470 rettv->vval.v_number = 0; | |
471 if (argvars[0].v_type != VAR_DICT || argvars[0].vval.v_dict == NULL) | |
472 { | |
473 EMSG(_(e_invarg)); | |
474 return; | |
475 } | |
476 | |
477 if (argvars[1].v_type != VAR_UNKNOWN) | |
478 { | |
479 start = get_tv_number(&argvars[1]); | |
480 end = start; | |
481 if (argvars[2].v_type != VAR_UNKNOWN) | |
482 end = get_tv_number(&argvars[2]); | |
483 if (start < 1 || end < 1) | |
484 { | |
485 EMSG(_(e_invrange)); | |
486 return; | |
487 } | |
488 } | |
489 | |
490 dict = argvars[0].vval.v_dict; | |
491 di = dict_find(dict, (char_u *)"bufnr", -1); | |
492 if (di != NULL) | |
493 { | |
494 buf = get_buf_tv(&di->di_tv, FALSE); | |
495 if (buf == NULL) | |
496 return; | |
497 } | |
498 | |
499 di = dict_find(dict, (char_u*)"all", -1); | |
500 if (di != NULL) | |
501 do_all = get_dict_number(dict, (char_u *)"all"); | |
502 | |
503 if (dict_find(dict, (char_u *)"id", -1) != NULL) | |
504 id = get_dict_number(dict, (char_u *)"id"); | |
505 if (dict_find(dict, (char_u *)"type", -1)) | |
506 { | |
507 char_u *name = get_dict_string(dict, (char_u *)"type", FALSE); | |
508 proptype_T *type = lookup_prop_type(name, buf); | |
509 | |
510 if (type == NULL) | |
511 return; | |
512 type_id = type->pt_id; | |
513 } | |
514 if (id == -1 && type_id == -1) | |
515 { | |
516 EMSG(_("E968: Need at least one of 'id' or 'type'")); | |
517 return; | |
518 } | |
519 | |
520 if (end == 0) | |
521 end = buf->b_ml.ml_line_count; | |
522 for (lnum = start; lnum <= end; ++lnum) | |
523 { | |
524 char_u *text; | |
525 size_t len; | |
526 | |
527 if (lnum > buf->b_ml.ml_line_count) | |
528 break; | |
529 text = ml_get_buf(buf, lnum, FALSE); | |
530 len = STRLEN(text) + 1; | |
531 if ((size_t)buf->b_ml.ml_line_len > len) | |
532 { | |
533 static textprop_T textprop; // static because of alignment | |
534 unsigned idx; | |
535 | |
536 for (idx = 0; idx < (buf->b_ml.ml_line_len - len) | |
537 / sizeof(textprop_T); ++idx) | |
538 { | |
539 char_u *cur_prop = buf->b_ml.ml_line_ptr + len | |
540 + idx * sizeof(textprop_T); | |
541 size_t taillen; | |
542 | |
543 mch_memmove(&textprop, cur_prop, sizeof(textprop_T)); | |
544 if (textprop.tp_id == id || textprop.tp_type == type_id) | |
545 { | |
546 if (!(buf->b_ml.ml_flags & ML_LINE_DIRTY)) | |
547 { | |
548 char_u *newptr = alloc(buf->b_ml.ml_line_len); | |
549 | |
550 // need to allocate the line to be able to change it | |
551 if (newptr == NULL) | |
552 return; | |
553 mch_memmove(newptr, buf->b_ml.ml_line_ptr, | |
554 buf->b_ml.ml_line_len); | |
555 buf->b_ml.ml_line_ptr = newptr; | |
556 curbuf->b_ml.ml_flags |= ML_LINE_DIRTY; | |
557 } | |
558 | |
559 taillen = buf->b_ml.ml_line_len - len | |
560 - (idx + 1) * sizeof(textprop_T); | |
561 if (taillen > 0) | |
562 mch_memmove(cur_prop, cur_prop + sizeof(textprop_T), | |
563 taillen); | |
564 buf->b_ml.ml_line_len -= sizeof(textprop_T); | |
565 --idx; | |
566 | |
567 ++rettv->vval.v_number; | |
568 if (!do_all) | |
569 break; | |
570 } | |
571 } | |
572 } | |
573 } | |
574 redraw_buf_later(buf, NOT_VALID); | |
575 } | |
576 | |
577 /* | |
578 * Common for f_prop_type_add() and f_prop_type_change(). | |
579 */ | |
580 void | |
581 prop_type_set(typval_T *argvars, int add) | |
582 { | |
583 char_u *name; | |
584 buf_T *buf = NULL; | |
585 dict_T *dict; | |
586 dictitem_T *di; | |
587 proptype_T *prop; | |
588 | |
589 name = get_tv_string(&argvars[0]); | |
590 if (*name == NUL) | |
591 { | |
592 EMSG(_(e_invarg)); | |
593 return; | |
594 } | |
595 | |
596 if (get_bufnr_from_arg(&argvars[1], &buf) == FAIL) | |
597 return; | |
598 dict = argvars[1].vval.v_dict; | |
599 | |
600 prop = find_prop(name, buf); | |
601 if (add) | |
602 { | |
603 hashtab_T **htp; | |
604 | |
605 if (prop != NULL) | |
606 { | |
607 EMSG2(_("E969: Property type %s already defined"), name); | |
608 return; | |
609 } | |
610 prop = (proptype_T *)alloc_clear(sizeof(proptype_T) + STRLEN(name)); | |
611 if (prop == NULL) | |
612 return; | |
613 STRCPY(prop->pt_name, name); | |
614 prop->pt_id = ++proptype_id; | |
615 htp = buf == NULL ? &global_proptypes : &buf->b_proptypes; | |
616 if (*htp == NULL) | |
617 { | |
618 *htp = (hashtab_T *)alloc(sizeof(hashtab_T)); | |
619 if (*htp == NULL) | |
620 return; | |
621 hash_init(*htp); | |
622 } | |
623 hash_add(buf == NULL ? global_proptypes : buf->b_proptypes, | |
624 PT2HIKEY(prop)); | |
625 } | |
626 else | |
627 { | |
628 if (prop == NULL) | |
629 { | |
630 EMSG2(_(e_type_not_exist), name); | |
631 return; | |
632 } | |
633 } | |
634 | |
635 if (dict != NULL) | |
636 { | |
637 di = dict_find(dict, (char_u *)"highlight", -1); | |
638 if (di != NULL) | |
639 { | |
640 char_u *highlight; | |
641 int hl_id = 0; | |
642 | |
643 highlight = get_dict_string(dict, (char_u *)"highlight", TRUE); | |
644 if (highlight != NULL && *highlight != NUL) | |
645 hl_id = syn_name2id(highlight); | |
646 if (hl_id <= 0) | |
647 { | |
648 EMSG2(_("E970: Unknown highlight group name: '%s'"), | |
649 highlight == NULL ? (char_u *)"" : highlight); | |
650 return; | |
651 } | |
652 prop->pt_hl_id = hl_id; | |
653 } | |
654 | |
655 di = dict_find(dict, (char_u *)"priority", -1); | |
656 if (di != NULL) | |
657 prop->pt_priority = get_tv_number(&di->di_tv); | |
658 | |
659 di = dict_find(dict, (char_u *)"start_incl", -1); | |
660 if (di != NULL) | |
661 { | |
662 if (get_tv_number(&di->di_tv)) | |
663 prop->pt_flags |= PT_FLAG_INS_START_INCL; | |
664 else | |
665 prop->pt_flags &= ~PT_FLAG_INS_START_INCL; | |
666 } | |
667 | |
668 di = dict_find(dict, (char_u *)"end_incl", -1); | |
669 if (di != NULL) | |
670 { | |
671 if (get_tv_number(&di->di_tv)) | |
672 prop->pt_flags |= PT_FLAG_INS_END_INCL; | |
673 else | |
674 prop->pt_flags &= ~PT_FLAG_INS_END_INCL; | |
675 } | |
676 } | |
677 } | |
678 | |
679 /* | |
680 * prop_type_add({name}, {props}) | |
681 */ | |
682 void | |
683 f_prop_type_add(typval_T *argvars, typval_T *rettv UNUSED) | |
684 { | |
685 prop_type_set(argvars, TRUE); | |
686 } | |
687 | |
688 /* | |
689 * prop_type_change({name}, {props}) | |
690 */ | |
691 void | |
692 f_prop_type_change(typval_T *argvars, typval_T *rettv UNUSED) | |
693 { | |
694 prop_type_set(argvars, FALSE); | |
695 } | |
696 | |
697 /* | |
698 * prop_type_delete({name} [, {bufnr}]) | |
699 */ | |
700 void | |
701 f_prop_type_delete(typval_T *argvars, typval_T *rettv UNUSED) | |
702 { | |
703 char_u *name; | |
704 buf_T *buf = NULL; | |
705 hashitem_T *hi; | |
706 | |
707 name = get_tv_string(&argvars[0]); | |
708 if (*name == NUL) | |
709 { | |
710 EMSG(_(e_invarg)); | |
711 return; | |
712 } | |
713 | |
714 if (argvars[1].v_type != VAR_UNKNOWN) | |
715 { | |
716 if (get_bufnr_from_arg(&argvars[1], &buf) == FAIL) | |
717 return; | |
718 } | |
719 | |
720 hi = find_prop_hi(name, buf); | |
721 if (hi != NULL) | |
722 { | |
723 hashtab_T *ht; | |
724 | |
725 if (buf == NULL) | |
726 ht = global_proptypes; | |
727 else | |
728 ht = buf->b_proptypes; | |
729 hash_remove(ht, hi); | |
730 } | |
731 } | |
732 | |
733 /* | |
734 * prop_type_get({name} [, {bufnr}]) | |
735 */ | |
736 void | |
737 f_prop_type_get(typval_T *argvars, typval_T *rettv UNUSED) | |
738 { | |
739 char_u *name = get_tv_string(&argvars[0]); | |
740 | |
741 if (*name == NUL) | |
742 { | |
743 EMSG(_(e_invarg)); | |
744 return; | |
745 } | |
746 if (rettv_dict_alloc(rettv) == OK) | |
747 { | |
748 proptype_T *prop = NULL; | |
749 buf_T *buf = NULL; | |
750 | |
751 if (argvars[1].v_type != VAR_UNKNOWN) | |
752 { | |
753 if (get_bufnr_from_arg(&argvars[1], &buf) == FAIL) | |
754 return; | |
755 } | |
756 | |
757 prop = find_prop(name, buf); | |
758 if (prop != NULL) | |
759 { | |
760 dict_T *d = rettv->vval.v_dict; | |
761 | |
762 if (prop->pt_hl_id > 0) | |
763 dict_add_string(d, "highlight", syn_id2name(prop->pt_hl_id)); | |
764 dict_add_number(d, "priority", prop->pt_priority); | |
765 dict_add_number(d, "start_incl", | |
766 (prop->pt_flags & PT_FLAG_INS_START_INCL) ? 1 : 0); | |
767 dict_add_number(d, "end_incl", | |
768 (prop->pt_flags & PT_FLAG_INS_END_INCL) ? 1 : 0); | |
769 if (buf != NULL) | |
770 dict_add_number(d, "bufnr", buf->b_fnum); | |
771 } | |
772 } | |
773 } | |
774 | |
775 static void | |
776 list_types(hashtab_T *ht, list_T *l) | |
777 { | |
778 long todo; | |
779 hashitem_T *hi; | |
780 | |
781 todo = (long)ht->ht_used; | |
782 for (hi = ht->ht_array; todo > 0; ++hi) | |
783 { | |
784 if (!HASHITEM_EMPTY(hi)) | |
785 { | |
786 proptype_T *prop = HI2PT(hi); | |
787 | |
788 list_append_string(l, prop->pt_name, -1); | |
789 --todo; | |
790 } | |
791 } | |
792 } | |
793 | |
794 /* | |
795 * prop_type_list([{bufnr}]) | |
796 */ | |
797 void | |
798 f_prop_type_list(typval_T *argvars, typval_T *rettv UNUSED) | |
799 { | |
800 buf_T *buf = NULL; | |
801 | |
802 if (rettv_list_alloc(rettv) == OK) | |
803 { | |
804 if (argvars[0].v_type != VAR_UNKNOWN) | |
805 { | |
806 if (get_bufnr_from_arg(&argvars[0], &buf) == FAIL) | |
807 return; | |
808 } | |
809 if (buf == NULL) | |
810 { | |
811 if (global_proptypes != NULL) | |
812 list_types(global_proptypes, rettv->vval.v_list); | |
813 } | |
814 else if (buf->b_proptypes != NULL) | |
815 list_types(buf->b_proptypes, rettv->vval.v_list); | |
816 } | |
817 } | |
818 | |
819 /* | |
820 * Free all property types in "ht". | |
821 */ | |
822 static void | |
823 clear_ht_prop_types(hashtab_T *ht) | |
824 { | |
825 long todo; | |
826 hashitem_T *hi; | |
827 | |
828 if (ht == NULL) | |
829 return; | |
830 | |
831 todo = (long)ht->ht_used; | |
832 for (hi = ht->ht_array; todo > 0; ++hi) | |
833 { | |
834 if (!HASHITEM_EMPTY(hi)) | |
835 { | |
836 proptype_T *prop = HI2PT(hi); | |
837 | |
838 vim_free(prop); | |
839 --todo; | |
840 } | |
841 } | |
842 | |
843 hash_clear(ht); | |
844 vim_free(ht); | |
845 } | |
846 | |
847 #if defined(EXITFREE) || defined(PROTO) | |
848 /* | |
849 * Free all property types for "buf". | |
850 */ | |
851 void | |
852 clear_global_prop_types(void) | |
853 { | |
854 clear_ht_prop_types(global_proptypes); | |
855 global_proptypes = NULL; | |
856 } | |
857 #endif | |
858 | |
859 /* | |
860 * Free all property types for "buf". | |
861 */ | |
862 void | |
863 clear_buf_prop_types(buf_T *buf) | |
864 { | |
865 clear_ht_prop_types(buf->b_proptypes); | |
866 buf->b_proptypes = NULL; | |
867 } | |
868 | |
869 #endif // FEAT_TEXT_PROP |