blob: bf238d907cb2a66c8670f96a3116be129df243b4 [file] [log] [blame]
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001/* 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 * sign.c: functions for managing signs
12 */
13
14#include "vim.h"
15
16#if defined(FEAT_SIGNS) || defined(PROTO)
17
18/*
19 * Struct to hold the sign properties.
20 */
21typedef struct sign sign_T;
22
23struct sign
24{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +010025 sign_T *sn_next; // next sign in list
26 int sn_typenr; // type number of sign
27 char_u *sn_name; // name of sign
28 char_u *sn_icon; // name of pixmap
Bram Moolenaarbbea4702019-01-01 13:20:31 +010029# ifdef FEAT_SIGN_ICONS
Bram Moolenaar6b7b7192019-01-11 13:42:41 +010030 void *sn_image; // icon image
Bram Moolenaarbbea4702019-01-01 13:20:31 +010031# endif
Bram Moolenaar6b7b7192019-01-11 13:42:41 +010032 char_u *sn_text; // text used instead of pixmap
33 int sn_line_hl; // highlight ID for line
34 int sn_text_hl; // highlight ID for text
Bram Moolenaarbbea4702019-01-01 13:20:31 +010035};
36
37static sign_T *first_sign = NULL;
38static int next_sign_typenr = 1;
39
40static void sign_list_defined(sign_T *sp);
41static void sign_undefine(sign_T *sp, sign_T *sp_prev);
42
43static char *cmds[] = {
44 "define",
45# define SIGNCMD_DEFINE 0
46 "undefine",
47# define SIGNCMD_UNDEFINE 1
48 "list",
49# define SIGNCMD_LIST 2
50 "place",
51# define SIGNCMD_PLACE 3
52 "unplace",
53# define SIGNCMD_UNPLACE 4
54 "jump",
55# define SIGNCMD_JUMP 5
56 NULL
57# define SIGNCMD_LAST 6
58};
59
60static hashtab_T sg_table; // sign group (signgroup_T) hashtable
61static int next_sign_id = 1; // next sign id in the global group
62
63/*
64 * Initialize data needed for managing signs
65 */
66 void
67init_signs(void)
68{
69 hash_init(&sg_table); // sign group hash table
70}
71
72/*
73 * A new sign in group 'groupname' is added. If the group is not present,
74 * create it. Otherwise reference the group.
75 */
76 static signgroup_T *
77sign_group_ref(char_u *groupname)
78{
79 hash_T hash;
80 hashitem_T *hi;
81 signgroup_T *group;
82
83 hash = hash_hash(groupname);
84 hi = hash_lookup(&sg_table, groupname, hash);
85 if (HASHITEM_EMPTY(hi))
86 {
87 // new group
Bram Moolenaarc799fe22019-05-28 23:08:19 +020088 group = alloc(sizeof(signgroup_T) + STRLEN(groupname));
Bram Moolenaarbbea4702019-01-01 13:20:31 +010089 if (group == NULL)
90 return NULL;
91 STRCPY(group->sg_name, groupname);
92 group->refcount = 1;
93 group->next_sign_id = 1;
94 hash_add_item(&sg_table, hi, group->sg_name, hash);
95 }
96 else
97 {
98 // existing group
99 group = HI2SG(hi);
100 group->refcount++;
101 }
102
103 return group;
104}
105
106/*
107 * A sign in group 'groupname' is removed. If all the signs in this group are
108 * removed, then remove the group.
109 */
110 static void
111sign_group_unref(char_u *groupname)
112{
113 hashitem_T *hi;
114 signgroup_T *group;
115
116 hi = hash_find(&sg_table, groupname);
117 if (!HASHITEM_EMPTY(hi))
118 {
119 group = HI2SG(hi);
120 group->refcount--;
121 if (group->refcount == 0)
122 {
123 // All the signs in this group are removed
124 hash_remove(&sg_table, hi);
125 vim_free(group);
126 }
127 }
128}
129
130/*
131 * Returns TRUE if 'sign' is in 'group'.
132 * A sign can either be in the global group (sign->group == NULL)
133 * or in a named group. If 'group' is '*', then the sign is part of the group.
134 */
135 static int
136sign_in_group(signlist_T *sign, char_u *group)
137{
138 return ((group != NULL && STRCMP(group, "*") == 0)
139 || (group == NULL && sign->group == NULL)
140 || (group != NULL && sign->group != NULL
141 && STRCMP(group, sign->group->sg_name) == 0));
142}
143
144/*
145 * Get the next free sign identifier in the specified group
146 */
147 static int
148sign_group_get_next_signid(buf_T *buf, char_u *groupname)
149{
150 int id = 1;
151 signgroup_T *group = NULL;
152 signlist_T *sign;
153 hashitem_T *hi;
154 int found = FALSE;
155
156 if (groupname != NULL)
157 {
158 hi = hash_find(&sg_table, groupname);
159 if (HASHITEM_EMPTY(hi))
160 return id;
161 group = HI2SG(hi);
162 }
163
Bram Moolenaarb5443cc2019-01-15 20:19:40 +0100164 // Search for the next usable sign identifier
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100165 while (!found)
166 {
167 if (group == NULL)
168 id = next_sign_id++; // global group
169 else
170 id = group->next_sign_id++;
171
172 // Check whether this sign is already placed in the buffer
173 found = TRUE;
174 FOR_ALL_SIGNS_IN_BUF(buf, sign)
175 {
176 if (id == sign->id && sign_in_group(sign, groupname))
177 {
178 found = FALSE; // sign identifier is in use
179 break;
180 }
181 }
182 }
183
184 return id;
185}
186
187/*
188 * Insert a new sign into the signlist for buffer 'buf' between the 'prev' and
189 * 'next' signs.
190 */
191 static void
192insert_sign(
193 buf_T *buf, // buffer to store sign in
194 signlist_T *prev, // previous sign entry
195 signlist_T *next, // next sign entry
196 int id, // sign ID
197 char_u *group, // sign group; NULL for global group
198 int prio, // sign priority
199 linenr_T lnum, // line number which gets the mark
200 int typenr) // typenr of sign we are adding
201{
202 signlist_T *newsign;
203
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200204 newsign = lalloc_id(sizeof(signlist_T), FALSE, aid_insert_sign);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100205 if (newsign != NULL)
206 {
207 newsign->id = id;
208 newsign->lnum = lnum;
209 newsign->typenr = typenr;
210 if (group != NULL)
211 {
212 newsign->group = sign_group_ref(group);
213 if (newsign->group == NULL)
214 {
215 vim_free(newsign);
216 return;
217 }
218 }
219 else
220 newsign->group = NULL;
221 newsign->priority = prio;
222 newsign->next = next;
223 newsign->prev = prev;
224 if (next != NULL)
225 next->prev = newsign;
226
227 if (prev == NULL)
228 {
229 // When adding first sign need to redraw the windows to create the
230 // column for signs.
231 if (buf->b_signlist == NULL)
232 {
233 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200234 changed_line_abv_curs();
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100235 }
236
237 // first sign in signlist
238 buf->b_signlist = newsign;
239#ifdef FEAT_NETBEANS_INTG
240 if (netbeans_active())
241 buf->b_has_sign_column = TRUE;
242#endif
243 }
244 else
245 prev->next = newsign;
246 }
247}
248
249/*
250 * Insert a new sign sorted by line number and sign priority.
251 */
252 static void
253insert_sign_by_lnum_prio(
254 buf_T *buf, // buffer to store sign in
255 signlist_T *prev, // previous sign entry
256 int id, // sign ID
257 char_u *group, // sign group; NULL for global group
258 int prio, // sign priority
259 linenr_T lnum, // line number which gets the mark
260 int typenr) // typenr of sign we are adding
261{
262 signlist_T *sign;
263
264 // keep signs sorted by lnum and by priority: insert new sign at
265 // the proper position in the list for this lnum.
266 while (prev != NULL && prev->lnum == lnum && prev->priority <= prio)
267 prev = prev->prev;
268 if (prev == NULL)
269 sign = buf->b_signlist;
270 else
271 sign = prev->next;
272
273 insert_sign(buf, prev, sign, id, group, prio, lnum, typenr);
274}
275
276/*
Bram Moolenaar4e038572019-07-04 18:28:35 +0200277 * Lookup a sign by typenr. Returns NULL if sign is not found.
278 */
279 static sign_T *
280find_sign_by_typenr(int typenr)
281{
282 sign_T *sp;
283
284 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
285 if (sp->sn_typenr == typenr)
286 return sp;
287 return NULL;
288}
289
290/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100291 * Get the name of a sign by its typenr.
292 */
293 static char_u *
294sign_typenr2name(int typenr)
295{
296 sign_T *sp;
297
298 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
299 if (sp->sn_typenr == typenr)
300 return sp->sn_name;
301 return (char_u *)_("[Deleted]");
302}
303
304/*
305 * Return information about a sign in a Dict
306 */
307 static dict_T *
308sign_get_info(signlist_T *sign)
309{
310 dict_T *d;
311
312 if ((d = dict_alloc_id(aid_sign_getinfo)) == NULL)
313 return NULL;
314 dict_add_number(d, "id", sign->id);
315 dict_add_string(d, "group", (sign->group == NULL) ?
316 (char_u *)"" : sign->group->sg_name);
317 dict_add_number(d, "lnum", sign->lnum);
318 dict_add_string(d, "name", sign_typenr2name(sign->typenr));
319 dict_add_number(d, "priority", sign->priority);
320
321 return d;
322}
323
324/*
Bram Moolenaar64416122019-06-07 21:37:13 +0200325 * Sort the signs placed on the same line as "sign" by priority. Invoked after
326 * changing the priority of an already placed sign. Assumes the signs in the
327 * buffer are sorted by line number and priority.
328 */
329 static void
330sign_sort_by_prio_on_line(buf_T *buf, signlist_T *sign)
331{
332 signlist_T *p = NULL;
333
334 // If there is only one sign in the buffer or only one sign on the line or
335 // the sign is already sorted by priority, then return.
336 if ((sign->prev == NULL
337 || sign->prev->lnum != sign->lnum
338 || sign->prev->priority > sign->priority)
339 && (sign->next == NULL
340 || sign->next->lnum != sign->lnum
341 || sign->next->priority < sign->priority))
342 return;
343
344 // One or more signs on the same line as 'sign'
345 // Find a sign after which 'sign' should be inserted
346
347 // First search backward for a sign with higher priority on the same line
348 p = sign;
349 while (p->prev != NULL && p->prev->lnum == sign->lnum
350 && p->prev->priority <= sign->priority)
351 p = p->prev;
352
353 if (p == sign)
354 {
355 // Sign not found. Search forward for a sign with priority just before
356 // 'sign'.
357 p = sign->next;
358 while (p->next != NULL && p->next->lnum == sign->lnum
359 && p->next->priority > sign->priority)
360 p = p->next;
361 }
362
363 // Remove 'sign' from the list
364 if (buf->b_signlist == sign)
365 buf->b_signlist = sign->next;
366 if (sign->prev != NULL)
367 sign->prev->next = sign->next;
368 if (sign->next != NULL)
369 sign->next->prev = sign->prev;
370 sign->prev = NULL;
371 sign->next = NULL;
372
373 // Re-insert 'sign' at the right place
374 if (p->priority <= sign->priority)
375 {
376 // 'sign' has a higher priority and should be inserted before 'p'
377 sign->prev = p->prev;
378 sign->next = p;
379 p->prev = sign;
380 if (sign->prev != NULL)
381 sign->prev->next = sign;
382 if (buf->b_signlist == p)
383 buf->b_signlist = sign;
384 }
385 else
386 {
387 // 'sign' has a lower priority and should be inserted after 'p'
388 sign->prev = p;
389 sign->next = p->next;
390 p->next = sign;
391 if (sign->next != NULL)
392 sign->next->prev = sign;
393 }
394}
395
396/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100397 * Add the sign into the signlist. Find the right spot to do it though.
398 */
399 static void
400buf_addsign(
401 buf_T *buf, // buffer to store sign in
402 int id, // sign ID
403 char_u *groupname, // sign group
404 int prio, // sign priority
405 linenr_T lnum, // line number which gets the mark
406 int typenr) // typenr of sign we are adding
407{
408 signlist_T *sign; // a sign in the signlist
409 signlist_T *prev; // the previous sign
410
411 prev = NULL;
412 FOR_ALL_SIGNS_IN_BUF(buf, sign)
413 {
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100414 if (lnum == sign->lnum && id == sign->id
415 && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100416 {
417 // Update an existing sign
418 sign->typenr = typenr;
Bram Moolenaar58a7f872019-06-04 22:48:15 +0200419 sign->priority = prio;
Bram Moolenaar64416122019-06-07 21:37:13 +0200420 sign_sort_by_prio_on_line(buf, sign);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100421 return;
422 }
423 else if (lnum < sign->lnum)
424 {
425 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio,
426 lnum, typenr);
427 return;
428 }
429 prev = sign;
430 }
431
432 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr);
433 return;
434}
435
436/*
437 * For an existing, placed sign "markId" change the type to "typenr".
438 * Returns the line number of the sign, or zero if the sign is not found.
439 */
440 static linenr_T
441buf_change_sign_type(
442 buf_T *buf, // buffer to store sign in
443 int markId, // sign ID
444 char_u *group, // sign group
Bram Moolenaar809ce4d2019-07-13 21:21:40 +0200445 int typenr, // typenr of sign we are adding
446 int prio) // sign priority
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100447{
448 signlist_T *sign; // a sign in the signlist
449
450 FOR_ALL_SIGNS_IN_BUF(buf, sign)
451 {
452 if (sign->id == markId && sign_in_group(sign, group))
453 {
454 sign->typenr = typenr;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +0200455 sign->priority = prio;
456 sign_sort_by_prio_on_line(buf, sign);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100457 return sign->lnum;
458 }
459 }
460
461 return (linenr_T)0;
462}
463
464/*
Bram Moolenaar4e038572019-07-04 18:28:35 +0200465 * Return the attributes of the first sign placed on line 'lnum' in buffer
466 * 'buf'. Used when refreshing the screen. Returns TRUE if a sign is found on
467 * 'lnum', FALSE otherwise.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100468 */
469 int
Bram Moolenaar4e038572019-07-04 18:28:35 +0200470buf_get_signattrs(buf_T *buf, linenr_T lnum, sign_attrs_T *sattr)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100471{
Bram Moolenaar4e038572019-07-04 18:28:35 +0200472 signlist_T *sign;
473 sign_T *sp;
474
475 vim_memset(sattr, 0, sizeof(sign_attrs_T));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100476
477 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200478 {
479 if (sign->lnum > lnum)
480 // Signs are sorted by line number in the buffer. No need to check
481 // for signs after the specified line number 'lnum'.
482 break;
483
484 if (sign->lnum == lnum)
485 {
486 sattr->typenr = sign->typenr;
487 sp = find_sign_by_typenr(sign->typenr);
488 if (sp == NULL)
489 return FALSE;
490
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100491# ifdef FEAT_SIGN_ICONS
Bram Moolenaar4e038572019-07-04 18:28:35 +0200492 sattr->icon = sp->sn_image;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100493# endif
Bram Moolenaar4e038572019-07-04 18:28:35 +0200494 sattr->text = sp->sn_text;
495 if (sattr->text != NULL && sp->sn_text_hl > 0)
496 sattr->texthl = syn_id2attr(sp->sn_text_hl);
497 if (sp->sn_line_hl > 0)
498 sattr->linehl = syn_id2attr(sp->sn_line_hl);
499 return TRUE;
500 }
501 }
502 return FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100503}
504
505/*
506 * Delete sign 'id' in group 'group' from buffer 'buf'.
507 * If 'id' is zero, then delete all the signs in group 'group'. Otherwise
508 * delete only the specified sign.
509 * If 'group' is '*', then delete the sign in all the groups. If 'group' is
510 * NULL, then delete the sign in the global group. Otherwise delete the sign in
511 * the specified group.
512 * Returns the line number of the deleted sign. If multiple signs are deleted,
513 * then returns the line number of the last sign deleted.
514 */
515 linenr_T
516buf_delsign(
517 buf_T *buf, // buffer sign is stored in
518 linenr_T atlnum, // sign at this line, 0 - at any line
519 int id, // sign id
520 char_u *group) // sign group
521{
522 signlist_T **lastp; // pointer to pointer to current sign
523 signlist_T *sign; // a sign in a b_signlist
524 signlist_T *next; // the next sign in a b_signlist
525 linenr_T lnum; // line number whose sign was deleted
526
527 lastp = &buf->b_signlist;
528 lnum = 0;
529 for (sign = buf->b_signlist; sign != NULL; sign = next)
530 {
531 next = sign->next;
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100532 if ((id == 0 || sign->id == id)
533 && (atlnum == 0 || sign->lnum == atlnum)
534 && sign_in_group(sign, group))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100535
536 {
537 *lastp = next;
538 if (next != NULL)
539 next->prev = sign->prev;
540 lnum = sign->lnum;
541 if (sign->group != NULL)
542 sign_group_unref(sign->group->sg_name);
543 vim_free(sign);
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100544 redraw_buf_line_later(buf, lnum);
545
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100546 // Check whether only one sign needs to be deleted
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100547 // If deleting a sign with a specific identifier in a particular
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100548 // group or deleting any sign at a particular line number, delete
549 // only one sign.
550 if (group == NULL
551 || (*group != '*' && id != 0)
552 || (*group == '*' && atlnum != 0))
553 break;
554 }
555 else
556 lastp = &sign->next;
557 }
558
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100559 // When deleting the last sign the cursor position may change, because the
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100560 // sign columns no longer shows. And the 'signcolumn' may be hidden.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100561 if (buf->b_signlist == NULL)
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100562 {
563 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200564 changed_line_abv_curs();
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100565 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100566
567 return lnum;
568}
569
570
571/*
572 * Find the line number of the sign with the requested id in group 'group'. If
573 * the sign does not exist, return 0 as the line number. This will still let
574 * the correct file get loaded.
575 */
576 int
577buf_findsign(
578 buf_T *buf, // buffer to store sign in
579 int id, // sign ID
580 char_u *group) // sign group
581{
582 signlist_T *sign; // a sign in the signlist
583
584 FOR_ALL_SIGNS_IN_BUF(buf, sign)
585 if (sign->id == id && sign_in_group(sign, group))
586 return sign->lnum;
587
588 return 0;
589}
590
591/*
592 * Return the sign at line 'lnum' in buffer 'buf'. Returns NULL if a sign is
593 * not found at the line. If 'groupname' is NULL, searches in the global group.
594 */
595 static signlist_T *
596buf_getsign_at_line(
597 buf_T *buf, // buffer whose sign we are searching for
598 linenr_T lnum, // line number of sign
599 char_u *groupname) // sign group name
600{
601 signlist_T *sign; // a sign in the signlist
602
603 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200604 {
605 if (sign->lnum > lnum)
606 // Signs are sorted by line number in the buffer. No need to check
607 // for signs after the specified line number 'lnum'.
608 break;
609
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100610 if (sign->lnum == lnum && sign_in_group(sign, groupname))
611 return sign;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200612 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100613
614 return NULL;
615}
616
617/*
618 * Return the identifier of the sign at line number 'lnum' in buffer 'buf'.
619 */
620 int
621buf_findsign_id(
622 buf_T *buf, // buffer whose sign we are searching for
623 linenr_T lnum, // line number of sign
624 char_u *groupname) // sign group name
625{
626 signlist_T *sign; // a sign in the signlist
627
628 sign = buf_getsign_at_line(buf, lnum, groupname);
629 if (sign != NULL)
630 return sign->id;
631
632 return 0;
633}
634
635# if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
636/*
637 * See if a given type of sign exists on a specific line.
638 */
639 int
640buf_findsigntype_id(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100641 buf_T *buf, // buffer whose sign we are searching for
642 linenr_T lnum, // line number of sign
643 int typenr) // sign type number
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100644{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100645 signlist_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100646
647 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200648 {
649 if (sign->lnum > lnum)
650 // Signs are sorted by line number in the buffer. No need to check
651 // for signs after the specified line number 'lnum'.
652 break;
653
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100654 if (sign->lnum == lnum && sign->typenr == typenr)
655 return sign->id;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200656 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100657
658 return 0;
659}
660
661
662# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
663/*
664 * Return the number of icons on the given line.
665 */
666 int
667buf_signcount(buf_T *buf, linenr_T lnum)
668{
669 signlist_T *sign; // a sign in the signlist
670 int count = 0;
671
672 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200673 {
674 if (sign->lnum > lnum)
675 // Signs are sorted by line number in the buffer. No need to check
676 // for signs after the specified line number 'lnum'.
677 break;
678
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100679 if (sign->lnum == lnum)
680 if (sign_get_image(sign->typenr) != NULL)
681 count++;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200682 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100683
684 return count;
685}
686# endif /* FEAT_SIGN_ICONS */
687# endif /* FEAT_NETBEANS_INTG */
688
689/*
690 * Delete signs in group 'group' in buffer "buf". If 'group' is '*', then
691 * delete all the signs.
692 */
693 void
694buf_delete_signs(buf_T *buf, char_u *group)
695{
696 signlist_T *sign;
697 signlist_T **lastp; // pointer to pointer to current sign
698 signlist_T *next;
699
700 // When deleting the last sign need to redraw the windows to remove the
701 // sign column. Not when curwin is NULL (this means we're exiting).
702 if (buf->b_signlist != NULL && curwin != NULL)
703 {
704 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200705 changed_line_abv_curs();
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100706 }
707
708 lastp = &buf->b_signlist;
709 for (sign = buf->b_signlist; sign != NULL; sign = next)
710 {
711 next = sign->next;
712 if (sign_in_group(sign, group))
713 {
714 *lastp = next;
715 if (next != NULL)
716 next->prev = sign->prev;
717 if (sign->group != NULL)
718 sign_group_unref(sign->group->sg_name);
719 vim_free(sign);
720 }
721 else
722 lastp = &sign->next;
723 }
724}
725
726/*
727 * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers.
728 */
729 static void
730sign_list_placed(buf_T *rbuf, char_u *sign_group)
731{
732 buf_T *buf;
733 signlist_T *sign;
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100734 char lbuf[MSG_BUF_LEN];
735 char group[MSG_BUF_LEN];
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100736
Bram Moolenaar32526b32019-01-19 17:43:09 +0100737 msg_puts_title(_("\n--- Signs ---"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100738 msg_putchar('\n');
739 if (rbuf == NULL)
740 buf = firstbuf;
741 else
742 buf = rbuf;
743 while (buf != NULL && !got_int)
744 {
745 if (buf->b_signlist != NULL)
746 {
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100747 vim_snprintf(lbuf, MSG_BUF_LEN, _("Signs for %s:"), buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100748 msg_puts_attr(lbuf, HL_ATTR(HLF_D));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100749 msg_putchar('\n');
750 }
751 FOR_ALL_SIGNS_IN_BUF(buf, sign)
752 {
753 if (got_int)
754 break;
755 if (!sign_in_group(sign, sign_group))
756 continue;
757 if (sign->group != NULL)
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100758 vim_snprintf(group, MSG_BUF_LEN, _(" group=%s"),
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100759 sign->group->sg_name);
760 else
761 group[0] = '\0';
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100762 vim_snprintf(lbuf, MSG_BUF_LEN,
763 _(" line=%ld id=%d%s name=%s priority=%d"),
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100764 (long)sign->lnum, sign->id, group,
765 sign_typenr2name(sign->typenr), sign->priority);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100766 msg_puts(lbuf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100767 msg_putchar('\n');
768 }
769 if (rbuf != NULL)
770 break;
771 buf = buf->b_next;
772 }
773}
774
775/*
776 * Adjust a placed sign for inserted/deleted lines.
777 */
778 void
779sign_mark_adjust(
780 linenr_T line1,
781 linenr_T line2,
782 long amount,
783 long amount_after)
784{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100785 signlist_T *sign; // a sign in a b_signlist
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100786 linenr_T new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100787
788 FOR_ALL_SIGNS_IN_BUF(curbuf, sign)
789 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100790 // Ignore changes to lines after the sign
791 if (sign->lnum < line1)
792 continue;
793 new_lnum = sign->lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100794 if (sign->lnum >= line1 && sign->lnum <= line2)
795 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100796 if (amount != MAXLNUM)
797 new_lnum += amount;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100798 }
799 else if (sign->lnum > line2)
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100800 // Lines inserted or deleted before the sign
801 new_lnum += amount_after;
802
803 // If the new sign line number is past the last line in the buffer,
804 // then don't adjust the line number. Otherwise, it will always be past
805 // the last line and will not be visible.
806 if (new_lnum <= curbuf->b_ml.ml_line_count)
807 sign->lnum = new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100808 }
809}
810
811/*
812 * Find index of a ":sign" subcmd from its name.
813 * "*end_cmd" must be writable.
814 */
815 static int
816sign_cmd_idx(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100817 char_u *begin_cmd, // begin of sign subcmd
818 char_u *end_cmd) // just after sign subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100819{
820 int idx;
821 char save = *end_cmd;
822
823 *end_cmd = NUL;
824 for (idx = 0; ; ++idx)
825 if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0)
826 break;
827 *end_cmd = save;
828 return idx;
829}
830
831/*
832 * Find a sign by name. Also returns pointer to the previous sign.
833 */
834 static sign_T *
835sign_find(char_u *name, sign_T **sp_prev)
836{
837 sign_T *sp;
838
839 if (sp_prev != NULL)
840 *sp_prev = NULL;
841 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
842 {
843 if (STRCMP(sp->sn_name, name) == 0)
844 break;
845 if (sp_prev != NULL)
846 *sp_prev = sp;
847 }
848
849 return sp;
850}
851
852/*
Bram Moolenaar03142362019-01-18 22:01:42 +0100853 * Allocate a new sign
854 */
855 static sign_T *
856alloc_new_sign(char_u *name)
857{
858 sign_T *sp;
859 sign_T *lp;
860 int start = next_sign_typenr;
861
862 // Allocate a new sign.
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200863 sp = alloc_clear_id(sizeof(sign_T), aid_sign_define_by_name);
Bram Moolenaar03142362019-01-18 22:01:42 +0100864 if (sp == NULL)
865 return NULL;
866
867 // Check that next_sign_typenr is not already being used.
868 // This only happens after wrapping around. Hopefully
869 // another one got deleted and we can use its number.
870 for (lp = first_sign; lp != NULL; )
871 {
872 if (lp->sn_typenr == next_sign_typenr)
873 {
874 ++next_sign_typenr;
875 if (next_sign_typenr == MAX_TYPENR)
876 next_sign_typenr = 1;
877 if (next_sign_typenr == start)
878 {
879 vim_free(sp);
880 emsg(_("E612: Too many signs defined"));
881 return NULL;
882 }
883 lp = first_sign; // start all over
884 continue;
885 }
886 lp = lp->sn_next;
887 }
888
889 sp->sn_typenr = next_sign_typenr;
890 if (++next_sign_typenr == MAX_TYPENR)
891 next_sign_typenr = 1; // wrap around
892
893 sp->sn_name = vim_strsave(name);
894 if (sp->sn_name == NULL) // out of memory
895 {
896 vim_free(sp);
897 return NULL;
898 }
899
900 return sp;
901}
902
903/*
904 * Initialize the icon information for a new sign
905 */
906 static void
907sign_define_init_icon(sign_T *sp, char_u *icon)
908{
909 vim_free(sp->sn_icon);
910 sp->sn_icon = vim_strsave(icon);
911 backslash_halve(sp->sn_icon);
912# ifdef FEAT_SIGN_ICONS
913 if (gui.in_use)
914 {
915 out_flush();
916 if (sp->sn_image != NULL)
917 gui_mch_destroy_sign(sp->sn_image);
918 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
919 }
920# endif
921}
922
923/*
924 * Initialize the text for a new sign
925 */
926 static int
927sign_define_init_text(sign_T *sp, char_u *text)
928{
929 char_u *s;
930 char_u *endp;
931 int cells;
932 int len;
933
934 endp = text + (int)STRLEN(text);
935
936 // Remove backslashes so that it is possible to use a space.
937 for (s = text; s + 1 < endp; ++s)
938 if (*s == '\\')
939 {
940 STRMOVE(s, s + 1);
941 --endp;
942 }
943
944 // Count cells and check for non-printable chars
Bram Moolenaar03142362019-01-18 22:01:42 +0100945 if (has_mbyte)
946 {
947 cells = 0;
948 for (s = text; s < endp; s += (*mb_ptr2len)(s))
949 {
950 if (!vim_isprintc((*mb_ptr2char)(s)))
951 break;
952 cells += (*mb_ptr2cells)(s);
953 }
954 }
955 else
Bram Moolenaar03142362019-01-18 22:01:42 +0100956 {
957 for (s = text; s < endp; ++s)
958 if (!vim_isprintc(*s))
959 break;
960 cells = (int)(s - text);
961 }
962
963 // Currently sign text must be one or two display cells
964 if (s != endp || cells < 1 || cells > 2)
965 {
966 semsg(_("E239: Invalid sign text: %s"), text);
967 return FAIL;
968 }
969
970 vim_free(sp->sn_text);
971 // Allocate one byte more if we need to pad up
972 // with a space.
973 len = (int)(endp - text + ((cells == 1) ? 1 : 0));
974 sp->sn_text = vim_strnsave(text, len);
975
976 // For single character sign text, pad with a space.
977 if (sp->sn_text != NULL && cells == 1)
978 STRCPY(sp->sn_text + len - 1, " ");
979
980 return OK;
981}
982
983/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100984 * Define a new sign or update an existing sign
985 */
986 int
987sign_define_by_name(
988 char_u *name,
989 char_u *icon,
990 char_u *linehl,
991 char_u *text,
992 char_u *texthl)
993{
994 sign_T *sp_prev;
995 sign_T *sp;
996
997 sp = sign_find(name, &sp_prev);
998 if (sp == NULL)
999 {
Bram Moolenaar03142362019-01-18 22:01:42 +01001000 sp = alloc_new_sign(name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001001 if (sp == NULL)
1002 return FAIL;
1003
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001004 // add the new sign to the list of signs
1005 if (sp_prev == NULL)
1006 first_sign = sp;
1007 else
1008 sp_prev->sn_next = sp;
1009 }
1010
1011 // set values for a defined sign.
1012 if (icon != NULL)
Bram Moolenaar03142362019-01-18 22:01:42 +01001013 sign_define_init_icon(sp, icon);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001014
Bram Moolenaar03142362019-01-18 22:01:42 +01001015 if (text != NULL && (sign_define_init_text(sp, text) == FAIL))
1016 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001017
1018 if (linehl != NULL)
1019 sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl));
1020
1021 if (texthl != NULL)
1022 sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl));
1023
1024 return OK;
1025}
1026
1027/*
1028 * Free the sign specified by 'name'.
1029 */
1030 int
1031sign_undefine_by_name(char_u *name)
1032{
1033 sign_T *sp_prev;
1034 sign_T *sp;
1035
1036 sp = sign_find(name, &sp_prev);
1037 if (sp == NULL)
1038 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001039 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001040 return FAIL;
1041 }
1042 sign_undefine(sp, sp_prev);
1043
1044 return OK;
1045}
1046
1047/*
1048 * List the signs matching 'name'
1049 */
1050 static void
1051sign_list_by_name(char_u *name)
1052{
1053 sign_T *sp;
1054
1055 sp = sign_find(name, NULL);
1056 if (sp != NULL)
1057 sign_list_defined(sp);
1058 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001059 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001060}
1061
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001062 static void
1063may_force_numberwidth_recompute(buf_T *buf, int unplace)
1064{
1065 tabpage_T *tp;
1066 win_T *wp;
1067
1068 FOR_ALL_TAB_WINDOWS(tp, wp)
1069 if (wp->w_buffer == buf
1070 && (wp->w_p_nu || wp->w_p_rnu)
1071 && (unplace || wp->w_nrwidth_width < 2)
1072 && (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u'))
1073 wp->w_nrwidth_line_count = 0;
1074}
1075
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001076/*
Bram Moolenaar8144acb2019-01-14 23:08:18 +01001077 * Place a sign at the specified file location or update a sign.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001078 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001079 static int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001080sign_place(
1081 int *sign_id,
1082 char_u *sign_group,
1083 char_u *sign_name,
1084 buf_T *buf,
1085 linenr_T lnum,
1086 int prio)
1087{
1088 sign_T *sp;
1089
1090 // Check for reserved character '*' in group name
1091 if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0'))
1092 return FAIL;
1093
1094 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1095 if (STRCMP(sp->sn_name, sign_name) == 0)
1096 break;
1097 if (sp == NULL)
1098 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001099 semsg(_("E155: Unknown sign: %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001100 return FAIL;
1101 }
1102 if (*sign_id == 0)
1103 *sign_id = sign_group_get_next_signid(buf, sign_group);
1104
1105 if (lnum > 0)
1106 // ":sign place {id} line={lnum} name={name} file={fname}":
1107 // place a sign
1108 buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr);
1109 else
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02001110 // ":sign place {id} file={fname}": change sign type and/or priority
1111 lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr,
1112 prio);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001113 if (lnum > 0)
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001114 {
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001115 redraw_buf_line_later(buf, lnum);
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001116
1117 // When displaying signs in the 'number' column, if the width of the
1118 // number column is less than 2, then force recomputing the width.
1119 may_force_numberwidth_recompute(buf, FALSE);
1120 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001121 else
1122 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001123 semsg(_("E885: Not possible to change sign %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001124 return FAIL;
1125 }
1126
1127 return OK;
1128}
1129
1130/*
1131 * Unplace the specified sign
1132 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001133 static int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001134sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum)
1135{
1136 if (buf->b_signlist == NULL) // No signs in the buffer
1137 return OK;
1138
1139 if (sign_id == 0)
1140 {
1141 // Delete all the signs in the specified buffer
1142 redraw_buf_later(buf, NOT_VALID);
1143 buf_delete_signs(buf, sign_group);
1144 }
1145 else
1146 {
1147 linenr_T lnum;
1148
1149 // Delete only the specified signs
1150 lnum = buf_delsign(buf, atlnum, sign_id, sign_group);
1151 if (lnum == 0)
1152 return FAIL;
1153 }
1154
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001155 // When all the signs in a buffer are removed, force recomputing the
1156 // number column width (if enabled) in all the windows displaying the
1157 // buffer if 'signcolumn' is set to 'number' in that window.
1158 if (buf->b_signlist == NULL)
1159 may_force_numberwidth_recompute(buf, TRUE);
1160
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001161 return OK;
1162}
1163
1164/*
1165 * Unplace the sign at the current cursor line.
1166 */
1167 static void
1168sign_unplace_at_cursor(char_u *groupname)
1169{
1170 int id = -1;
1171
1172 id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname);
1173 if (id > 0)
1174 sign_unplace(id, groupname, curwin->w_buffer, curwin->w_cursor.lnum);
1175 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001176 emsg(_("E159: Missing sign number"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001177}
1178
1179/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001180 * Jump to a sign.
1181 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001182 static linenr_T
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001183sign_jump(int sign_id, char_u *sign_group, buf_T *buf)
1184{
1185 linenr_T lnum;
1186
1187 if ((lnum = buf_findsign(buf, sign_id, sign_group)) <= 0)
1188 {
Bram Moolenaarb5443cc2019-01-15 20:19:40 +01001189 semsg(_("E157: Invalid sign ID: %d"), sign_id);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001190 return -1;
1191 }
1192
1193 // goto a sign ...
1194 if (buf_jump_open_win(buf) != NULL)
1195 { // ... in a current window
1196 curwin->w_cursor.lnum = lnum;
1197 check_cursor_lnum();
1198 beginline(BL_WHITE);
1199 }
1200 else
1201 { // ... not currently in a window
1202 char_u *cmd;
1203
1204 if (buf->b_fname == NULL)
1205 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001206 emsg(_("E934: Cannot jump to a buffer that does not have a name"));
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001207 return -1;
1208 }
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02001209 cmd = alloc(STRLEN(buf->b_fname) + 25);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001210 if (cmd == NULL)
1211 return -1;
1212 sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname);
1213 do_cmdline_cmd(cmd);
1214 vim_free(cmd);
1215 }
1216# ifdef FEAT_FOLDING
1217 foldOpenCursor();
1218# endif
1219
1220 return lnum;
1221}
1222
1223/*
1224 * ":sign define {name} ..." command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001225 */
1226 static void
1227sign_define_cmd(char_u *sign_name, char_u *cmdline)
1228{
1229 char_u *arg;
1230 char_u *p = cmdline;
1231 char_u *icon = NULL;
1232 char_u *text = NULL;
1233 char_u *linehl = NULL;
1234 char_u *texthl = NULL;
1235 int failed = FALSE;
1236
1237 // set values for a defined sign.
1238 for (;;)
1239 {
1240 arg = skipwhite(p);
1241 if (*arg == NUL)
1242 break;
1243 p = skiptowhite_esc(arg);
1244 if (STRNCMP(arg, "icon=", 5) == 0)
1245 {
1246 arg += 5;
1247 icon = vim_strnsave(arg, (int)(p - arg));
1248 }
1249 else if (STRNCMP(arg, "text=", 5) == 0)
1250 {
1251 arg += 5;
1252 text = vim_strnsave(arg, (int)(p - arg));
1253 }
1254 else if (STRNCMP(arg, "linehl=", 7) == 0)
1255 {
1256 arg += 7;
1257 linehl = vim_strnsave(arg, (int)(p - arg));
1258 }
1259 else if (STRNCMP(arg, "texthl=", 7) == 0)
1260 {
1261 arg += 7;
1262 texthl = vim_strnsave(arg, (int)(p - arg));
1263 }
1264 else
1265 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001266 semsg(_(e_invarg2), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001267 failed = TRUE;
1268 break;
1269 }
1270 }
1271
1272 if (!failed)
1273 sign_define_by_name(sign_name, icon, linehl, text, texthl);
1274
1275 vim_free(icon);
1276 vim_free(text);
1277 vim_free(linehl);
1278 vim_free(texthl);
1279}
1280
1281/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001282 * ":sign place" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001283 */
1284 static void
1285sign_place_cmd(
1286 buf_T *buf,
1287 linenr_T lnum,
1288 char_u *sign_name,
1289 int id,
1290 char_u *group,
1291 int prio)
1292{
1293 if (id <= 0)
1294 {
1295 // List signs placed in a file/buffer
1296 // :sign place file={fname}
1297 // :sign place group={group} file={fname}
1298 // :sign place group=* file={fname}
1299 // :sign place buffer={nr}
1300 // :sign place group={group} buffer={nr}
1301 // :sign place group=* buffer={nr}
1302 // :sign place
1303 // :sign place group={group}
1304 // :sign place group=*
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001305 if (lnum >= 0 || sign_name != NULL
1306 || (group != NULL && *group == '\0'))
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001307 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001308 else
1309 sign_list_placed(buf, group);
1310 }
1311 else
1312 {
1313 // Place a new sign
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001314 if (sign_name == NULL || buf == NULL
1315 || (group != NULL && *group == '\0'))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001316 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001317 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001318 return;
1319 }
1320
1321 sign_place(&id, group, sign_name, buf, lnum, prio);
1322 }
1323}
1324
1325/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001326 * ":sign unplace" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001327 */
1328 static void
1329sign_unplace_cmd(
1330 buf_T *buf,
1331 linenr_T lnum,
1332 char_u *sign_name,
1333 int id,
1334 char_u *group)
1335{
1336 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0'))
1337 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001338 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001339 return;
1340 }
1341
1342 if (id == -2)
1343 {
1344 if (buf != NULL)
1345 // :sign unplace * file={fname}
1346 // :sign unplace * group={group} file={fname}
1347 // :sign unplace * group=* file={fname}
1348 // :sign unplace * buffer={nr}
1349 // :sign unplace * group={group} buffer={nr}
1350 // :sign unplace * group=* buffer={nr}
1351 sign_unplace(0, group, buf, 0);
1352 else
1353 // :sign unplace *
1354 // :sign unplace * group={group}
1355 // :sign unplace * group=*
1356 FOR_ALL_BUFFERS(buf)
1357 if (buf->b_signlist != NULL)
1358 buf_delete_signs(buf, group);
1359 }
1360 else
1361 {
1362 if (buf != NULL)
1363 // :sign unplace {id} file={fname}
1364 // :sign unplace {id} group={group} file={fname}
1365 // :sign unplace {id} group=* file={fname}
1366 // :sign unplace {id} buffer={nr}
1367 // :sign unplace {id} group={group} buffer={nr}
1368 // :sign unplace {id} group=* buffer={nr}
1369 sign_unplace(id, group, buf, 0);
1370 else
1371 {
1372 if (id == -1)
1373 {
1374 // :sign unplace group={group}
1375 // :sign unplace group=*
1376 sign_unplace_at_cursor(group);
1377 }
1378 else
1379 {
1380 // :sign unplace {id}
1381 // :sign unplace {id} group={group}
1382 // :sign unplace {id} group=*
1383 FOR_ALL_BUFFERS(buf)
1384 sign_unplace(id, group, buf, 0);
1385 }
1386 }
1387 }
1388}
1389
1390/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001391 * Jump to a placed sign commands:
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001392 * :sign jump {id} file={fname}
1393 * :sign jump {id} buffer={nr}
1394 * :sign jump {id} group={group} file={fname}
1395 * :sign jump {id} group={group} buffer={nr}
1396 */
1397 static void
1398sign_jump_cmd(
1399 buf_T *buf,
1400 linenr_T lnum,
1401 char_u *sign_name,
1402 int id,
1403 char_u *group)
1404{
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001405 if (sign_name == NULL && group == NULL && id == -1)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001406 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001407 emsg(_(e_argreq));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001408 return;
1409 }
1410
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001411 if (buf == NULL || (group != NULL && *group == '\0')
1412 || lnum >= 0 || sign_name != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001413 {
1414 // File or buffer is not specified or an empty group is used
1415 // or a line number or a sign name is specified.
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001416 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001417 return;
1418 }
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001419 (void)sign_jump(id, group, buf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001420}
1421
1422/*
1423 * Parse the command line arguments for the ":sign place", ":sign unplace" and
1424 * ":sign jump" commands.
1425 * The supported arguments are: line={lnum} name={name} group={group}
1426 * priority={prio} and file={fname} or buffer={nr}.
1427 */
1428 static int
1429parse_sign_cmd_args(
1430 int cmd,
1431 char_u *arg,
1432 char_u **sign_name,
1433 int *signid,
1434 char_u **group,
1435 int *prio,
1436 buf_T **buf,
1437 linenr_T *lnum)
1438{
1439 char_u *arg1;
1440 char_u *name;
1441 char_u *filename = NULL;
Bram Moolenaarb589f952019-01-07 22:10:00 +01001442 int lnum_arg = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001443
1444 // first arg could be placed sign id
1445 arg1 = arg;
1446 if (VIM_ISDIGIT(*arg))
1447 {
1448 *signid = getdigits(&arg);
1449 if (!VIM_ISWHITE(*arg) && *arg != NUL)
1450 {
1451 *signid = -1;
1452 arg = arg1;
1453 }
1454 else
1455 arg = skipwhite(arg);
1456 }
1457
1458 while (*arg != NUL)
1459 {
1460 if (STRNCMP(arg, "line=", 5) == 0)
1461 {
1462 arg += 5;
1463 *lnum = atoi((char *)arg);
1464 arg = skiptowhite(arg);
Bram Moolenaarb589f952019-01-07 22:10:00 +01001465 lnum_arg = TRUE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001466 }
1467 else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE)
1468 {
1469 if (*signid != -1)
1470 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001471 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001472 return FAIL;
1473 }
1474 *signid = -2;
1475 arg = skiptowhite(arg + 1);
1476 }
1477 else if (STRNCMP(arg, "name=", 5) == 0)
1478 {
1479 arg += 5;
1480 name = arg;
1481 arg = skiptowhite(arg);
1482 if (*arg != NUL)
1483 *arg++ = NUL;
1484 while (name[0] == '0' && name[1] != NUL)
1485 ++name;
1486 *sign_name = name;
1487 }
1488 else if (STRNCMP(arg, "group=", 6) == 0)
1489 {
1490 arg += 6;
1491 *group = arg;
1492 arg = skiptowhite(arg);
1493 if (*arg != NUL)
1494 *arg++ = NUL;
1495 }
1496 else if (STRNCMP(arg, "priority=", 9) == 0)
1497 {
1498 arg += 9;
1499 *prio = atoi((char *)arg);
1500 arg = skiptowhite(arg);
1501 }
1502 else if (STRNCMP(arg, "file=", 5) == 0)
1503 {
1504 arg += 5;
1505 filename = arg;
1506 *buf = buflist_findname_exp(arg);
1507 break;
1508 }
1509 else if (STRNCMP(arg, "buffer=", 7) == 0)
1510 {
1511 arg += 7;
1512 filename = arg;
1513 *buf = buflist_findnr((int)getdigits(&arg));
1514 if (*skipwhite(arg) != NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001515 emsg(_(e_trailing));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001516 break;
1517 }
1518 else
1519 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001520 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001521 return FAIL;
1522 }
1523 arg = skipwhite(arg);
1524 }
1525
1526 if (filename != NULL && *buf == NULL)
1527 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001528 semsg(_("E158: Invalid buffer name: %s"), filename);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001529 return FAIL;
1530 }
1531
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001532 // If the filename is not supplied for the sign place or the sign jump
1533 // command, then use the current buffer.
Bram Moolenaarb589f952019-01-07 22:10:00 +01001534 if (filename == NULL && ((cmd == SIGNCMD_PLACE && lnum_arg)
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001535 || cmd == SIGNCMD_JUMP))
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001536 *buf = curwin->w_buffer;
1537
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001538 return OK;
1539}
1540
1541/*
1542 * ":sign" command
1543 */
1544 void
1545ex_sign(exarg_T *eap)
1546{
1547 char_u *arg = eap->arg;
1548 char_u *p;
1549 int idx;
1550 sign_T *sp;
1551 buf_T *buf = NULL;
1552
1553 // Parse the subcommand.
1554 p = skiptowhite(arg);
1555 idx = sign_cmd_idx(arg, p);
1556 if (idx == SIGNCMD_LAST)
1557 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001558 semsg(_("E160: Unknown sign command: %s"), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001559 return;
1560 }
1561 arg = skipwhite(p);
1562
1563 if (idx <= SIGNCMD_LIST)
1564 {
1565 // Define, undefine or list signs.
1566 if (idx == SIGNCMD_LIST && *arg == NUL)
1567 {
1568 // ":sign list": list all defined signs
1569 for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next)
1570 sign_list_defined(sp);
1571 }
1572 else if (*arg == NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001573 emsg(_("E156: Missing sign name"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001574 else
1575 {
1576 char_u *name;
1577
1578 // Isolate the sign name. If it's a number skip leading zeroes,
1579 // so that "099" and "99" are the same sign. But keep "0".
1580 p = skiptowhite(arg);
1581 if (*p != NUL)
1582 *p++ = NUL;
1583 while (arg[0] == '0' && arg[1] != NUL)
1584 ++arg;
1585 name = vim_strsave(arg);
1586
1587 if (idx == SIGNCMD_DEFINE)
1588 sign_define_cmd(name, p);
1589 else if (idx == SIGNCMD_LIST)
1590 // ":sign list {name}"
1591 sign_list_by_name(name);
1592 else
1593 // ":sign undefine {name}"
1594 sign_undefine_by_name(name);
1595
1596 vim_free(name);
1597 return;
1598 }
1599 }
1600 else
1601 {
1602 int id = -1;
1603 linenr_T lnum = -1;
1604 char_u *sign_name = NULL;
1605 char_u *group = NULL;
1606 int prio = SIGN_DEF_PRIO;
1607
1608 // Parse command line arguments
1609 if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio,
1610 &buf, &lnum) == FAIL)
1611 return;
1612
1613 if (idx == SIGNCMD_PLACE)
1614 sign_place_cmd(buf, lnum, sign_name, id, group, prio);
1615 else if (idx == SIGNCMD_UNPLACE)
1616 sign_unplace_cmd(buf, lnum, sign_name, id, group);
1617 else if (idx == SIGNCMD_JUMP)
1618 sign_jump_cmd(buf, lnum, sign_name, id, group);
1619 }
1620}
1621
1622/*
1623 * Return information about a specified sign
1624 */
1625 static void
1626sign_getinfo(sign_T *sp, dict_T *retdict)
1627{
1628 char_u *p;
1629
1630 dict_add_string(retdict, "name", (char_u *)sp->sn_name);
1631 if (sp->sn_icon != NULL)
1632 dict_add_string(retdict, "icon", (char_u *)sp->sn_icon);
1633 if (sp->sn_text != NULL)
1634 dict_add_string(retdict, "text", (char_u *)sp->sn_text);
1635 if (sp->sn_line_hl > 0)
1636 {
1637 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1638 if (p == NULL)
1639 p = (char_u *)"NONE";
1640 dict_add_string(retdict, "linehl", (char_u *)p);
1641 }
1642 if (sp->sn_text_hl > 0)
1643 {
1644 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1645 if (p == NULL)
1646 p = (char_u *)"NONE";
1647 dict_add_string(retdict, "texthl", (char_u *)p);
1648 }
1649}
1650
1651/*
1652 * If 'name' is NULL, return a list of all the defined signs.
1653 * Otherwise, return information about the specified sign.
1654 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001655 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001656sign_getlist(char_u *name, list_T *retlist)
1657{
1658 sign_T *sp = first_sign;
1659 dict_T *dict;
1660
1661 if (name != NULL)
1662 {
1663 sp = sign_find(name, NULL);
1664 if (sp == NULL)
1665 return;
1666 }
1667
1668 for (; sp != NULL && !got_int; sp = sp->sn_next)
1669 {
1670 if ((dict = dict_alloc_id(aid_sign_getlist)) == NULL)
1671 return;
1672 if (list_append_dict(retlist, dict) == FAIL)
1673 return;
1674 sign_getinfo(sp, dict);
1675
1676 if (name != NULL) // handle only the specified sign
1677 break;
1678 }
1679}
1680
1681/*
1682 * Returns information about signs placed in a buffer as list of dicts.
1683 */
1684 void
1685get_buffer_signs(buf_T *buf, list_T *l)
1686{
1687 signlist_T *sign;
1688 dict_T *d;
1689
1690 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1691 {
1692 if ((d = sign_get_info(sign)) != NULL)
1693 list_append_dict(l, d);
1694 }
1695}
1696
1697/*
1698 * Return information about all the signs placed in a buffer
1699 */
1700 static void
1701sign_get_placed_in_buf(
1702 buf_T *buf,
1703 linenr_T lnum,
1704 int sign_id,
1705 char_u *sign_group,
1706 list_T *retlist)
1707{
1708 dict_T *d;
1709 list_T *l;
1710 signlist_T *sign;
1711 dict_T *sdict;
1712
1713 if ((d = dict_alloc_id(aid_sign_getplaced_dict)) == NULL)
1714 return;
1715 list_append_dict(retlist, d);
1716
1717 dict_add_number(d, "bufnr", (long)buf->b_fnum);
1718
1719 if ((l = list_alloc_id(aid_sign_getplaced_list)) == NULL)
1720 return;
1721 dict_add_list(d, "signs", l);
1722
1723 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1724 {
1725 if (!sign_in_group(sign, sign_group))
1726 continue;
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001727 if ((lnum == 0 && sign_id == 0)
1728 || (sign_id == 0 && lnum == sign->lnum)
1729 || (lnum == 0 && sign_id == sign->id)
1730 || (lnum == sign->lnum && sign_id == sign->id))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001731 {
1732 if ((sdict = sign_get_info(sign)) != NULL)
1733 list_append_dict(l, sdict);
1734 }
1735 }
1736}
1737
1738/*
1739 * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the
1740 * sign placed at the line number. If 'lnum' is zero, return all the signs
1741 * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers.
1742 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001743 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001744sign_get_placed(
1745 buf_T *buf,
1746 linenr_T lnum,
1747 int sign_id,
1748 char_u *sign_group,
1749 list_T *retlist)
1750{
1751 if (buf != NULL)
1752 sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist);
1753 else
1754 {
1755 FOR_ALL_BUFFERS(buf)
1756 {
1757 if (buf->b_signlist != NULL)
1758 sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist);
1759 }
1760 }
1761}
1762
1763# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1764/*
1765 * Allocate the icons. Called when the GUI has started. Allows defining
1766 * signs before it starts.
1767 */
1768 void
1769sign_gui_started(void)
1770{
1771 sign_T *sp;
1772
1773 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1774 if (sp->sn_icon != NULL)
1775 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
1776}
1777# endif
1778
1779/*
1780 * List one sign.
1781 */
1782 static void
1783sign_list_defined(sign_T *sp)
1784{
1785 char_u *p;
1786
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001787 smsg("sign %s", sp->sn_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001788 if (sp->sn_icon != NULL)
1789 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001790 msg_puts(" icon=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001791 msg_outtrans(sp->sn_icon);
1792# ifdef FEAT_SIGN_ICONS
1793 if (sp->sn_image == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001794 msg_puts(_(" (NOT FOUND)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001795# else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001796 msg_puts(_(" (not supported)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001797# endif
1798 }
1799 if (sp->sn_text != NULL)
1800 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001801 msg_puts(" text=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001802 msg_outtrans(sp->sn_text);
1803 }
1804 if (sp->sn_line_hl > 0)
1805 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001806 msg_puts(" linehl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001807 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1808 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001809 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001810 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001811 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001812 }
1813 if (sp->sn_text_hl > 0)
1814 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001815 msg_puts(" texthl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001816 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1817 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001818 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001819 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001820 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001821 }
1822}
1823
1824/*
1825 * Undefine a sign and free its memory.
1826 */
1827 static void
1828sign_undefine(sign_T *sp, sign_T *sp_prev)
1829{
1830 vim_free(sp->sn_name);
1831 vim_free(sp->sn_icon);
1832# ifdef FEAT_SIGN_ICONS
1833 if (sp->sn_image != NULL)
1834 {
1835 out_flush();
1836 gui_mch_destroy_sign(sp->sn_image);
1837 }
1838# endif
1839 vim_free(sp->sn_text);
1840 if (sp_prev == NULL)
1841 first_sign = sp->sn_next;
1842 else
1843 sp_prev->sn_next = sp->sn_next;
1844 vim_free(sp);
1845}
1846
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001847# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1848 void *
1849sign_get_image(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001850 int typenr) // the attribute which may have a sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001851{
1852 sign_T *sp;
1853
1854 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1855 if (sp->sn_typenr == typenr)
1856 return sp->sn_image;
1857 return NULL;
1858}
1859# endif
1860
1861/*
1862 * Undefine/free all signs.
1863 */
1864 void
1865free_signs(void)
1866{
1867 while (first_sign != NULL)
1868 sign_undefine(first_sign, NULL);
1869}
1870
1871# if defined(FEAT_CMDL_COMPL) || defined(PROTO)
1872static enum
1873{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001874 EXP_SUBCMD, // expand :sign sub-commands
1875 EXP_DEFINE, // expand :sign define {name} args
1876 EXP_PLACE, // expand :sign place {id} args
Bram Moolenaar3678f652019-02-17 14:50:25 +01001877 EXP_LIST, // expand :sign place args
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001878 EXP_UNPLACE, // expand :sign unplace"
Bram Moolenaar3678f652019-02-17 14:50:25 +01001879 EXP_SIGN_NAMES, // expand with name of placed signs
1880 EXP_SIGN_GROUPS // expand with name of placed sign groups
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001881} expand_what;
1882
1883/*
Bram Moolenaar3678f652019-02-17 14:50:25 +01001884 * Return the n'th sign name (used for command line completion)
1885 */
1886 static char_u *
1887get_nth_sign_name(int idx)
1888{
1889 int current_idx;
1890 sign_T *sp;
1891
1892 // Complete with name of signs already defined
1893 current_idx = 0;
1894 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1895 if (current_idx++ == idx)
1896 return sp->sn_name;
1897 return NULL;
1898}
1899
1900/*
1901 * Return the n'th sign group name (used for command line completion)
1902 */
1903 static char_u *
1904get_nth_sign_group_name(int idx)
1905{
1906 int current_idx;
1907 int todo;
1908 hashitem_T *hi;
1909 signgroup_T *group;
1910
1911 // Complete with name of sign groups already defined
1912 current_idx = 0;
1913 todo = (int)sg_table.ht_used;
1914 for (hi = sg_table.ht_array; todo > 0; ++hi)
1915 {
1916 if (!HASHITEM_EMPTY(hi))
1917 {
1918 --todo;
1919 if (current_idx++ == idx)
1920 {
1921 group = HI2SG(hi);
1922 return group->sg_name;
1923 }
1924 }
1925 }
1926 return NULL;
1927}
1928
1929/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001930 * Function given to ExpandGeneric() to obtain the sign command
1931 * expansion.
1932 */
1933 char_u *
1934get_sign_name(expand_T *xp UNUSED, int idx)
1935{
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001936 switch (expand_what)
1937 {
1938 case EXP_SUBCMD:
1939 return (char_u *)cmds[idx];
1940 case EXP_DEFINE:
1941 {
1942 char *define_arg[] =
1943 {
1944 "icon=", "linehl=", "text=", "texthl=", NULL
1945 };
1946 return (char_u *)define_arg[idx];
1947 }
1948 case EXP_PLACE:
1949 {
1950 char *place_arg[] =
1951 {
1952 "line=", "name=", "group=", "priority=", "file=",
1953 "buffer=", NULL
1954 };
1955 return (char_u *)place_arg[idx];
1956 }
Bram Moolenaar3678f652019-02-17 14:50:25 +01001957 case EXP_LIST:
1958 {
1959 char *list_arg[] =
1960 {
1961 "group=", "file=", "buffer=", NULL
1962 };
1963 return (char_u *)list_arg[idx];
1964 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001965 case EXP_UNPLACE:
1966 {
1967 char *unplace_arg[] = { "group=", "file=", "buffer=", NULL };
1968 return (char_u *)unplace_arg[idx];
1969 }
1970 case EXP_SIGN_NAMES:
Bram Moolenaar3678f652019-02-17 14:50:25 +01001971 return get_nth_sign_name(idx);
1972 case EXP_SIGN_GROUPS:
1973 return get_nth_sign_group_name(idx);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001974 default:
1975 return NULL;
1976 }
1977}
1978
1979/*
1980 * Handle command line completion for :sign command.
1981 */
1982 void
1983set_context_in_sign_cmd(expand_T *xp, char_u *arg)
1984{
1985 char_u *p;
1986 char_u *end_subcmd;
1987 char_u *last;
1988 int cmd_idx;
1989 char_u *begin_subcmd_args;
1990
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001991 // Default: expand subcommands.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001992 xp->xp_context = EXPAND_SIGN;
1993 expand_what = EXP_SUBCMD;
1994 xp->xp_pattern = arg;
1995
1996 end_subcmd = skiptowhite(arg);
1997 if (*end_subcmd == NUL)
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001998 // expand subcmd name
1999 // :sign {subcmd}<CTRL-D>
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002000 return;
2001
2002 cmd_idx = sign_cmd_idx(arg, end_subcmd);
2003
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002004 // :sign {subcmd} {subcmd_args}
2005 // |
2006 // begin_subcmd_args
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002007 begin_subcmd_args = skipwhite(end_subcmd);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002008
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002009 // expand last argument of subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002010
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002011 // :sign define {name} {args}...
2012 // |
2013 // p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002014
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002015 // Loop until reaching last argument.
Bram Moolenaar3678f652019-02-17 14:50:25 +01002016 p = begin_subcmd_args;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002017 do
2018 {
2019 p = skipwhite(p);
2020 last = p;
2021 p = skiptowhite(p);
2022 } while (*p != NUL);
2023
2024 p = vim_strchr(last, '=');
2025
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002026 // :sign define {name} {args}... {last}=
2027 // | |
2028 // last p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002029 if (p == NULL)
2030 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002031 // Expand last argument name (before equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002032 xp->xp_pattern = last;
2033 switch (cmd_idx)
2034 {
2035 case SIGNCMD_DEFINE:
2036 expand_what = EXP_DEFINE;
2037 break;
2038 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002039 // List placed signs
2040 if (VIM_ISDIGIT(*begin_subcmd_args))
2041 // :sign place {id} {args}...
2042 expand_what = EXP_PLACE;
2043 else
2044 // :sign place {args}...
2045 expand_what = EXP_LIST;
2046 break;
2047 case SIGNCMD_LIST:
2048 case SIGNCMD_UNDEFINE:
2049 // :sign list <CTRL-D>
2050 // :sign undefine <CTRL-D>
2051 expand_what = EXP_SIGN_NAMES;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002052 break;
2053 case SIGNCMD_JUMP:
2054 case SIGNCMD_UNPLACE:
2055 expand_what = EXP_UNPLACE;
2056 break;
2057 default:
2058 xp->xp_context = EXPAND_NOTHING;
2059 }
2060 }
2061 else
2062 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002063 // Expand last argument value (after equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002064 xp->xp_pattern = p + 1;
2065 switch (cmd_idx)
2066 {
2067 case SIGNCMD_DEFINE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002068 if (STRNCMP(last, "texthl", 6) == 0
2069 || STRNCMP(last, "linehl", 6) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002070 xp->xp_context = EXPAND_HIGHLIGHT;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002071 else if (STRNCMP(last, "icon", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002072 xp->xp_context = EXPAND_FILES;
2073 else
2074 xp->xp_context = EXPAND_NOTHING;
2075 break;
2076 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002077 if (STRNCMP(last, "name", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002078 expand_what = EXP_SIGN_NAMES;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002079 else if (STRNCMP(last, "group", 5) == 0)
2080 expand_what = EXP_SIGN_GROUPS;
2081 else if (STRNCMP(last, "file", 4) == 0)
2082 xp->xp_context = EXPAND_BUFFERS;
2083 else
2084 xp->xp_context = EXPAND_NOTHING;
2085 break;
2086 case SIGNCMD_UNPLACE:
2087 case SIGNCMD_JUMP:
2088 if (STRNCMP(last, "group", 5) == 0)
2089 expand_what = EXP_SIGN_GROUPS;
2090 else if (STRNCMP(last, "file", 4) == 0)
2091 xp->xp_context = EXPAND_BUFFERS;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002092 else
2093 xp->xp_context = EXPAND_NOTHING;
2094 break;
2095 default:
2096 xp->xp_context = EXPAND_NOTHING;
2097 }
2098 }
2099}
2100# endif
2101
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002102/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002103 * Define a sign using the attributes in 'dict'. Returns 0 on success and -1 on
2104 * failure.
2105 */
2106 static int
2107sign_define_from_dict(char_u *name_arg, dict_T *dict)
2108{
2109 char_u *name = NULL;
2110 char_u *icon = NULL;
2111 char_u *linehl = NULL;
2112 char_u *text = NULL;
2113 char_u *texthl = NULL;
2114 int retval = -1;
2115
2116 if (name_arg == NULL)
2117 {
2118 if (dict == NULL)
2119 return -1;
2120 name = dict_get_string(dict, (char_u *)"name", TRUE);
2121 }
2122 else
2123 name = vim_strsave(name_arg);
2124 if (name == NULL || name[0] == NUL)
2125 goto cleanup;
2126 if (dict != NULL)
2127 {
2128 icon = dict_get_string(dict, (char_u *)"icon", TRUE);
2129 linehl = dict_get_string(dict, (char_u *)"linehl", TRUE);
2130 text = dict_get_string(dict, (char_u *)"text", TRUE);
2131 texthl = dict_get_string(dict, (char_u *)"texthl", TRUE);
2132 }
2133
2134 if (sign_define_by_name(name, icon, linehl, text, texthl) == OK)
2135 retval = 0;
2136
2137cleanup:
2138 vim_free(name);
2139 vim_free(icon);
2140 vim_free(linehl);
2141 vim_free(text);
2142 vim_free(texthl);
2143
2144 return retval;
2145}
2146
2147/*
2148 * Define multiple signs using attributes from list 'l' and store the return
2149 * values in 'retlist'.
2150 */
2151 static void
2152sign_define_multiple(list_T *l, list_T *retlist)
2153{
2154 listitem_T *li;
2155 int retval;
2156
2157 for (li = l->lv_first; li != NULL; li = li->li_next)
2158 {
2159 retval = -1;
2160 if (li->li_tv.v_type == VAR_DICT)
2161 retval = sign_define_from_dict(NULL, li->li_tv.vval.v_dict);
2162 else
2163 emsg(_(e_dictreq));
2164 list_append_number(retlist, retval);
2165 }
2166}
2167
2168/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002169 * "sign_define()" function
2170 */
2171 void
2172f_sign_define(typval_T *argvars, typval_T *rettv)
2173{
2174 char_u *name;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002175
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002176 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2177 {
2178 // Define multiple signs
2179 if (rettv_list_alloc(rettv) != OK)
2180 return;
2181
2182 sign_define_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2183 return;
2184 }
2185
2186 // Define a single sign
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002187 rettv->vval.v_number = -1;
2188
2189 name = tv_get_string_chk(&argvars[0]);
2190 if (name == NULL)
2191 return;
2192
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002193 if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_DICT)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002194 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002195 emsg(_(e_dictreq));
2196 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002197 }
2198
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002199 rettv->vval.v_number = sign_define_from_dict(name,
2200 argvars[1].v_type == VAR_DICT ? argvars[1].vval.v_dict : NULL);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002201}
2202
2203/*
2204 * "sign_getdefined()" function
2205 */
2206 void
2207f_sign_getdefined(typval_T *argvars, typval_T *rettv)
2208{
2209 char_u *name = NULL;
2210
2211 if (rettv_list_alloc_id(rettv, aid_sign_getdefined) != OK)
2212 return;
2213
2214 if (argvars[0].v_type != VAR_UNKNOWN)
2215 name = tv_get_string(&argvars[0]);
2216
2217 sign_getlist(name, rettv->vval.v_list);
2218}
2219
2220/*
2221 * "sign_getplaced()" function
2222 */
2223 void
2224f_sign_getplaced(typval_T *argvars, typval_T *rettv)
2225{
2226 buf_T *buf = NULL;
2227 dict_T *dict;
2228 dictitem_T *di;
2229 linenr_T lnum = 0;
2230 int sign_id = 0;
2231 char_u *group = NULL;
2232 int notanum = FALSE;
2233
2234 if (rettv_list_alloc_id(rettv, aid_sign_getplaced) != OK)
2235 return;
2236
2237 if (argvars[0].v_type != VAR_UNKNOWN)
2238 {
2239 // get signs placed in the specified buffer
2240 buf = get_buf_arg(&argvars[0]);
2241 if (buf == NULL)
2242 return;
2243
2244 if (argvars[1].v_type != VAR_UNKNOWN)
2245 {
2246 if (argvars[1].v_type != VAR_DICT ||
2247 ((dict = argvars[1].vval.v_dict) == NULL))
2248 {
2249 emsg(_(e_dictreq));
2250 return;
2251 }
2252 if ((di = dict_find(dict, (char_u *)"lnum", -1)) != NULL)
2253 {
2254 // get signs placed at this line
2255 (void)tv_get_number_chk(&di->di_tv, &notanum);
2256 if (notanum)
2257 return;
2258 lnum = tv_get_lnum(&di->di_tv);
2259 }
2260 if ((di = dict_find(dict, (char_u *)"id", -1)) != NULL)
2261 {
2262 // get sign placed with this identifier
2263 sign_id = (int)tv_get_number_chk(&di->di_tv, &notanum);
2264 if (notanum)
2265 return;
2266 }
2267 if ((di = dict_find(dict, (char_u *)"group", -1)) != NULL)
2268 {
2269 group = tv_get_string_chk(&di->di_tv);
2270 if (group == NULL)
2271 return;
2272 if (*group == '\0') // empty string means global group
2273 group = NULL;
2274 }
2275 }
2276 }
2277
2278 sign_get_placed(buf, lnum, sign_id, group, rettv->vval.v_list);
2279}
2280
2281/*
2282 * "sign_jump()" function
2283 */
2284 void
2285f_sign_jump(typval_T *argvars, typval_T *rettv)
2286{
2287 int sign_id;
2288 char_u *sign_group = NULL;
2289 buf_T *buf;
2290 int notanum = FALSE;
2291
2292 rettv->vval.v_number = -1;
2293
2294 // Sign identifier
2295 sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
2296 if (notanum)
2297 return;
2298 if (sign_id <= 0)
2299 {
2300 emsg(_(e_invarg));
2301 return;
2302 }
2303
2304 // Sign group
2305 sign_group = tv_get_string_chk(&argvars[1]);
2306 if (sign_group == NULL)
2307 return;
2308 if (sign_group[0] == '\0')
2309 sign_group = NULL; // global sign group
2310 else
2311 {
2312 sign_group = vim_strsave(sign_group);
2313 if (sign_group == NULL)
2314 return;
2315 }
2316
2317 // Buffer to place the sign
2318 buf = get_buf_arg(&argvars[2]);
2319 if (buf == NULL)
2320 goto cleanup;
2321
2322 rettv->vval.v_number = sign_jump(sign_id, sign_group, buf);
2323
2324cleanup:
2325 vim_free(sign_group);
2326}
2327
2328/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002329 * Place a new sign using the values specified in dict 'dict'. Returns the sign
2330 * identifier if successfully placed, otherwise returns 0.
2331 */
2332 static int
2333sign_place_from_dict(
2334 typval_T *id_tv,
2335 typval_T *group_tv,
2336 typval_T *name_tv,
2337 typval_T *buf_tv,
2338 dict_T *dict)
2339{
2340 int sign_id = 0;
2341 char_u *group = NULL;
2342 char_u *sign_name = NULL;
2343 buf_T *buf = NULL;
2344 dictitem_T *di;
2345 linenr_T lnum = 0;
2346 int prio = SIGN_DEF_PRIO;
2347 int notanum = FALSE;
2348 int ret_sign_id = -1;
2349
2350 // sign identifier
2351 if (id_tv == NULL)
2352 {
2353 di = dict_find(dict, (char_u *)"id", -1);
2354 if (di != NULL)
2355 id_tv = &di->di_tv;
2356 }
2357 if (id_tv == NULL)
2358 sign_id = 0;
2359 else
2360 {
2361 sign_id = tv_get_number_chk(id_tv, &notanum);
2362 if (notanum)
2363 return -1;
2364 if (sign_id < 0)
2365 {
2366 emsg(_(e_invarg));
2367 return -1;
2368 }
2369 }
2370
2371 // sign group
2372 if (group_tv == NULL)
2373 {
2374 di = dict_find(dict, (char_u *)"group", -1);
2375 if (di != NULL)
2376 group_tv = &di->di_tv;
2377 }
2378 if (group_tv == NULL)
2379 group = NULL; // global group
2380 else
2381 {
2382 group = tv_get_string_chk(group_tv);
2383 if (group == NULL)
2384 goto cleanup;
2385 if (group[0] == '\0') // global sign group
2386 group = NULL;
2387 else
2388 {
2389 group = vim_strsave(group);
2390 if (group == NULL)
2391 return -1;
2392 }
2393 }
2394
2395 // sign name
2396 if (name_tv == NULL)
2397 {
2398 di = dict_find(dict, (char_u *)"name", -1);
2399 if (di != NULL)
2400 name_tv = &di->di_tv;
2401 }
2402 if (name_tv == NULL)
2403 goto cleanup;
2404 sign_name = tv_get_string_chk(name_tv);
2405 if (sign_name == NULL)
2406 goto cleanup;
2407
2408 // buffer to place the sign
2409 if (buf_tv == NULL)
2410 {
2411 di = dict_find(dict, (char_u *)"buffer", -1);
2412 if (di != NULL)
2413 buf_tv = &di->di_tv;
2414 }
2415 if (buf_tv == NULL)
2416 goto cleanup;
2417 buf = get_buf_arg(buf_tv);
2418 if (buf == NULL)
2419 goto cleanup;
2420
2421 // line number of the sign
2422 di = dict_find(dict, (char_u *)"lnum", -1);
2423 if (di != NULL)
2424 {
2425 lnum = (int)tv_get_number_chk(&di->di_tv, &notanum);
2426 if (notanum)
2427 goto cleanup;
2428 }
2429
2430 // sign priority
2431 di = dict_find(dict, (char_u *)"priority", -1);
2432 if (di != NULL)
2433 {
2434 prio = (int)tv_get_number_chk(&di->di_tv, &notanum);
2435 if (notanum)
2436 goto cleanup;
2437 }
2438
2439 if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK)
2440 ret_sign_id = sign_id;
2441
2442cleanup:
2443 vim_free(group);
2444
2445 return ret_sign_id;
2446}
2447
2448/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002449 * "sign_place()" function
2450 */
2451 void
2452f_sign_place(typval_T *argvars, typval_T *rettv)
2453{
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002454 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002455
2456 rettv->vval.v_number = -1;
2457
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002458 if (argvars[4].v_type != VAR_UNKNOWN
2459 && (argvars[4].v_type != VAR_DICT
2460 || ((dict = argvars[4].vval.v_dict) == NULL)))
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002461 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002462 emsg(_(e_dictreq));
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002463 return;
2464 }
2465
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002466 rettv->vval.v_number = sign_place_from_dict(&argvars[0], &argvars[1],
2467 &argvars[2], &argvars[3], dict);
2468}
2469
2470/*
2471 * "sign_placelist()" function. Place multiple signs.
2472 */
2473 void
2474f_sign_placelist(typval_T *argvars, typval_T *rettv)
2475{
2476 listitem_T *li;
2477 int sign_id;
2478
2479 if (rettv_list_alloc(rettv) != OK)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002480 return;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002481
2482 if (argvars[0].v_type != VAR_LIST)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002483 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002484 emsg(_(e_listreq));
2485 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002486 }
2487
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002488 // Process the List of sign attributes
2489 for (li = argvars[0].vval.v_list->lv_first; li != NULL; li = li->li_next)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002490 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002491 sign_id = -1;
2492 if (li->li_tv.v_type == VAR_DICT)
2493 sign_id = sign_place_from_dict(NULL, NULL, NULL, NULL,
2494 li->li_tv.vval.v_dict);
2495 else
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002496 emsg(_(e_dictreq));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002497 list_append_number(rettv->vval.v_list, sign_id);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002498 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002499}
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002500
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002501/*
2502 * Undefine multiple signs
2503 */
2504 static void
2505sign_undefine_multiple(list_T *l, list_T *retlist)
2506{
2507 char_u *name;
2508 listitem_T *li;
2509 int retval;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002510
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002511 for (li = l->lv_first; li != NULL; li = li->li_next)
2512 {
2513 retval = -1;
2514 name = tv_get_string_chk(&li->li_tv);
2515 if (name != NULL && (sign_undefine_by_name(name) == OK))
2516 retval = 0;
2517 list_append_number(retlist, retval);
2518 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002519}
2520
2521/*
2522 * "sign_undefine()" function
2523 */
2524 void
2525f_sign_undefine(typval_T *argvars, typval_T *rettv)
2526{
2527 char_u *name;
2528
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002529 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2530 {
2531 // Undefine multiple signs
2532 if (rettv_list_alloc(rettv) != OK)
2533 return;
2534
2535 sign_undefine_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2536 return;
2537 }
2538
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002539 rettv->vval.v_number = -1;
2540
2541 if (argvars[0].v_type == VAR_UNKNOWN)
2542 {
2543 // Free all the signs
2544 free_signs();
2545 rettv->vval.v_number = 0;
2546 }
2547 else
2548 {
2549 // Free only the specified sign
2550 name = tv_get_string_chk(&argvars[0]);
2551 if (name == NULL)
2552 return;
2553
2554 if (sign_undefine_by_name(name) == OK)
2555 rettv->vval.v_number = 0;
2556 }
2557}
2558
2559/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002560 * Unplace the sign with attributes specified in 'dict'. Returns 0 on success
2561 * and -1 on failure.
2562 */
2563 static int
2564sign_unplace_from_dict(typval_T *group_tv, dict_T *dict)
2565{
2566 dictitem_T *di;
2567 int sign_id = 0;
2568 buf_T *buf = NULL;
2569 char_u *group = NULL;
2570 int retval = -1;
2571
2572 // sign group
2573 if (group_tv != NULL)
2574 group = tv_get_string(group_tv);
2575 else
2576 group = dict_get_string(dict, (char_u *)"group", FALSE);
2577 if (group != NULL)
2578 {
2579 if (group[0] == '\0') // global sign group
2580 group = NULL;
2581 else
2582 {
2583 group = vim_strsave(group);
2584 if (group == NULL)
2585 return -1;
2586 }
2587 }
2588
2589 if (dict != NULL)
2590 {
2591 if ((di = dict_find(dict, (char_u *)"buffer", -1)) != NULL)
2592 {
2593 buf = get_buf_arg(&di->di_tv);
2594 if (buf == NULL)
2595 goto cleanup;
2596 }
2597 if (dict_find(dict, (char_u *)"id", -1) != NULL)
2598 {
2599 sign_id = dict_get_number(dict, (char_u *)"id");
2600 if (sign_id <= 0)
2601 {
2602 emsg(_(e_invarg));
2603 goto cleanup;
2604 }
2605 }
2606 }
2607
2608 if (buf == NULL)
2609 {
2610 // Delete the sign in all the buffers
2611 retval = 0;
2612 FOR_ALL_BUFFERS(buf)
2613 if (sign_unplace(sign_id, group, buf, 0) != OK)
2614 retval = -1;
2615 }
2616 else if (sign_unplace(sign_id, group, buf, 0) == OK)
2617 retval = 0;
2618
2619cleanup:
2620 vim_free(group);
2621
2622 return retval;
2623}
2624
2625/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002626 * "sign_unplace()" function
2627 */
2628 void
2629f_sign_unplace(typval_T *argvars, typval_T *rettv)
2630{
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002631 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002632
2633 rettv->vval.v_number = -1;
2634
2635 if (argvars[0].v_type != VAR_STRING)
2636 {
2637 emsg(_(e_invarg));
2638 return;
2639 }
2640
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002641 if (argvars[1].v_type != VAR_UNKNOWN)
2642 {
2643 if (argvars[1].v_type != VAR_DICT)
2644 {
2645 emsg(_(e_dictreq));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002646 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002647 }
2648 dict = argvars[1].vval.v_dict;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002649 }
2650
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002651 rettv->vval.v_number = sign_unplace_from_dict(&argvars[0], dict);
2652}
2653
2654/*
2655 * "sign_unplacelist()" function
2656 */
2657 void
2658f_sign_unplacelist(typval_T *argvars, typval_T *rettv)
2659{
2660 listitem_T *li;
2661 int retval;
2662
2663 if (rettv_list_alloc(rettv) != OK)
2664 return;
2665
2666 if (argvars[0].v_type != VAR_LIST)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002667 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002668 emsg(_(e_listreq));
2669 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002670 }
2671
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002672 for (li = argvars[0].vval.v_list->lv_first; li != NULL; li = li->li_next)
2673 {
2674 retval = -1;
2675 if (li->li_tv.v_type == VAR_DICT)
2676 retval = sign_unplace_from_dict(NULL, li->li_tv.vval.v_dict);
2677 else
2678 emsg(_(e_dictreq));
2679 list_append_number(rettv->vval.v_list, retval);
2680 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002681}
2682
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002683#endif /* FEAT_SIGNS */