xref: /vim-8.2.3635/src/sign.c (revision ed37d9b3)
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  * 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  */
21 typedef struct sign sign_T;
22 
23 struct sign
24 {
25     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
29 # ifdef FEAT_SIGN_ICONS
30     void	*sn_image;	// icon image
31 # endif
32     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
35 };
36 
37 static sign_T	*first_sign = NULL;
38 static int	next_sign_typenr = 1;
39 
40 static void sign_list_defined(sign_T *sp);
41 static void sign_undefine(sign_T *sp, sign_T *sp_prev);
42 
43 static 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 
60 #define FOR_ALL_SIGNS(sp)	\
61     for ((sp) = first_sign; (sp) != NULL; (sp) = (sp)->sn_next)
62 
63 static hashtab_T	sg_table;	// sign group (signgroup_T) hashtable
64 static int		next_sign_id = 1; // next sign id in the global group
65 
66 /*
67  * Initialize data needed for managing signs
68  */
69     void
70 init_signs(void)
71 {
72     hash_init(&sg_table);		// sign group hash table
73 }
74 
75 /*
76  * A new sign in group 'groupname' is added. If the group is not present,
77  * create it. Otherwise reference the group.
78  */
79     static signgroup_T *
80 sign_group_ref(char_u *groupname)
81 {
82     hash_T		hash;
83     hashitem_T		*hi;
84     signgroup_T		*group;
85 
86     hash = hash_hash(groupname);
87     hi = hash_lookup(&sg_table, groupname, hash);
88     if (HASHITEM_EMPTY(hi))
89     {
90 	// new group
91 	group = alloc(offsetof(signgroup_T, sg_name) + STRLEN(groupname) + 1);
92 	if (group == NULL)
93 	    return NULL;
94 	STRCPY(group->sg_name, groupname);
95 	group->sg_refcount = 1;
96 	group->sg_next_sign_id = 1;
97 	hash_add_item(&sg_table, hi, group->sg_name, hash);
98     }
99     else
100     {
101 	// existing group
102 	group = HI2SG(hi);
103 	group->sg_refcount++;
104     }
105 
106     return group;
107 }
108 
109 /*
110  * A sign in group 'groupname' is removed. If all the signs in this group are
111  * removed, then remove the group.
112  */
113     static void
114 sign_group_unref(char_u *groupname)
115 {
116     hashitem_T		*hi;
117     signgroup_T		*group;
118 
119     hi = hash_find(&sg_table, groupname);
120     if (!HASHITEM_EMPTY(hi))
121     {
122 	group = HI2SG(hi);
123 	group->sg_refcount--;
124 	if (group->sg_refcount == 0)
125 	{
126 	    // All the signs in this group are removed
127 	    hash_remove(&sg_table, hi);
128 	    vim_free(group);
129 	}
130     }
131 }
132 
133 /*
134  * Returns TRUE if 'sign' is in 'group'.
135  * A sign can either be in the global group (sign->se_group == NULL)
136  * or in a named group. If 'group' is '*', then the sign is part of the group.
137  */
138     static int
139 sign_in_group(sign_entry_T *sign, char_u *group)
140 {
141     return ((group != NULL && STRCMP(group, "*") == 0)
142 	    || (group == NULL && sign->se_group == NULL)
143 	    || (group != NULL && sign->se_group != NULL
144 			      && STRCMP(group, sign->se_group->sg_name) == 0));
145 }
146 
147 /*
148  * Return TRUE if "sign" is to be displayed in window "wp".
149  * If the group name starts with "PopUp" it only shows in a popup window.
150  */
151     static int
152 sign_group_for_window(sign_entry_T *sign, win_T *wp)
153 {
154     int for_popup = sign->se_group != NULL
155 			&& STRNCMP("PopUp", sign->se_group->sg_name, 5) == 0;
156 
157     return WIN_IS_POPUP(wp) ? for_popup : !for_popup;
158 }
159 
160 /*
161  * Get the next free sign identifier in the specified group
162  */
163     static int
164 sign_group_get_next_signid(buf_T *buf, char_u *groupname)
165 {
166     int			id = 1;
167     signgroup_T		*group = NULL;
168     sign_entry_T	*sign;
169     hashitem_T		*hi;
170     int			found = FALSE;
171 
172     if (groupname != NULL)
173     {
174 	hi = hash_find(&sg_table, groupname);
175 	if (HASHITEM_EMPTY(hi))
176 	    return id;
177 	group = HI2SG(hi);
178     }
179 
180     // Search for the next usable sign identifier
181     while (!found)
182     {
183 	if (group == NULL)
184 	    id = next_sign_id++;		// global group
185 	else
186 	    id = group->sg_next_sign_id++;
187 
188 	// Check whether this sign is already placed in the buffer
189 	found = TRUE;
190 	FOR_ALL_SIGNS_IN_BUF(buf, sign)
191 	{
192 	    if (id == sign->se_id && sign_in_group(sign, groupname))
193 	    {
194 		found = FALSE;		// sign identifier is in use
195 		break;
196 	    }
197 	}
198     }
199 
200     return id;
201 }
202 
203 /*
204  * Insert a new sign into the signlist for buffer 'buf' between the 'prev' and
205  * 'next' signs.
206  */
207     static void
208 insert_sign(
209     buf_T	*buf,		// buffer to store sign in
210     sign_entry_T *prev,		// previous sign entry
211     sign_entry_T *next,		// next sign entry
212     int		id,		// sign ID
213     char_u	*group,		// sign group; NULL for global group
214     int		prio,		// sign priority
215     linenr_T	lnum,		// line number which gets the mark
216     int		typenr)		// typenr of sign we are adding
217 {
218     sign_entry_T *newsign;
219 
220     newsign = lalloc_id(sizeof(sign_entry_T), FALSE, aid_insert_sign);
221     if (newsign != NULL)
222     {
223 	newsign->se_id = id;
224 	newsign->se_lnum = lnum;
225 	newsign->se_typenr = typenr;
226 	if (group != NULL)
227 	{
228 	    newsign->se_group = sign_group_ref(group);
229 	    if (newsign->se_group == NULL)
230 	    {
231 		vim_free(newsign);
232 		return;
233 	    }
234 	}
235 	else
236 	    newsign->se_group = NULL;
237 	newsign->se_priority = prio;
238 	newsign->se_next = next;
239 	newsign->se_prev = prev;
240 	if (next != NULL)
241 	    next->se_prev = newsign;
242 
243 	if (prev == NULL)
244 	{
245 	    // When adding first sign need to redraw the windows to create the
246 	    // column for signs.
247 	    if (buf->b_signlist == NULL)
248 	    {
249 		redraw_buf_later(buf, NOT_VALID);
250 		changed_line_abv_curs();
251 	    }
252 
253 	    // first sign in signlist
254 	    buf->b_signlist = newsign;
255 #ifdef FEAT_NETBEANS_INTG
256 	    if (netbeans_active())
257 		buf->b_has_sign_column = TRUE;
258 #endif
259 	}
260 	else
261 	    prev->se_next = newsign;
262     }
263 }
264 
265 /*
266  * Insert a new sign sorted by line number and sign priority.
267  */
268     static void
269 insert_sign_by_lnum_prio(
270     buf_T	*buf,		// buffer to store sign in
271     sign_entry_T *prev,		// previous sign entry
272     int		id,		// sign ID
273     char_u	*group,		// sign group; NULL for global group
274     int		prio,		// sign priority
275     linenr_T	lnum,		// line number which gets the mark
276     int		typenr)		// typenr of sign we are adding
277 {
278     sign_entry_T	*sign;
279 
280     // keep signs sorted by lnum and by priority: insert new sign at
281     // the proper position in the list for this lnum.
282     while (prev != NULL && prev->se_lnum == lnum && prev->se_priority <= prio)
283 	prev = prev->se_prev;
284     if (prev == NULL)
285 	sign = buf->b_signlist;
286     else
287 	sign = prev->se_next;
288 
289     insert_sign(buf, prev, sign, id, group, prio, lnum, typenr);
290 }
291 
292 /*
293  * Lookup a sign by typenr. Returns NULL if sign is not found.
294  */
295     static sign_T *
296 find_sign_by_typenr(int typenr)
297 {
298     sign_T	*sp;
299 
300     FOR_ALL_SIGNS(sp)
301 	if (sp->sn_typenr == typenr)
302 	    return sp;
303     return NULL;
304 }
305 
306 /*
307  * Get the name of a sign by its typenr.
308  */
309     static char_u *
310 sign_typenr2name(int typenr)
311 {
312     sign_T	*sp;
313 
314     FOR_ALL_SIGNS(sp)
315 	if (sp->sn_typenr == typenr)
316 	    return sp->sn_name;
317     return (char_u *)_("[Deleted]");
318 }
319 
320 /*
321  * Return information about a sign in a Dict
322  */
323     static dict_T *
324 sign_get_info(sign_entry_T *sign)
325 {
326     dict_T	*d;
327 
328     if ((d = dict_alloc_id(aid_sign_getinfo)) == NULL)
329 	return NULL;
330     dict_add_number(d, "id", sign->se_id);
331     dict_add_string(d, "group", (sign->se_group == NULL) ?
332 				       (char_u *)"" : sign->se_group->sg_name);
333     dict_add_number(d, "lnum", sign->se_lnum);
334     dict_add_string(d, "name", sign_typenr2name(sign->se_typenr));
335     dict_add_number(d, "priority", sign->se_priority);
336 
337     return d;
338 }
339 
340 /*
341  * Sort the signs placed on the same line as "sign" by priority.  Invoked after
342  * changing the priority of an already placed sign.  Assumes the signs in the
343  * buffer are sorted by line number and priority.
344  */
345     static void
346 sign_sort_by_prio_on_line(buf_T *buf, sign_entry_T *sign)
347 {
348     sign_entry_T *p = NULL;
349 
350     // If there is only one sign in the buffer or only one sign on the line or
351     // the sign is already sorted by priority, then return.
352     if ((sign->se_prev == NULL
353 		|| sign->se_prev->se_lnum != sign->se_lnum
354 		|| sign->se_prev->se_priority > sign->se_priority)
355 	    && (sign->se_next == NULL
356 		|| sign->se_next->se_lnum != sign->se_lnum
357 		|| sign->se_next->se_priority < sign->se_priority))
358 	return;
359 
360     // One or more signs on the same line as 'sign'
361     // Find a sign after which 'sign' should be inserted
362 
363     // First search backward for a sign with higher priority on the same line
364     p = sign;
365     while (p->se_prev != NULL && p->se_prev->se_lnum == sign->se_lnum
366 			       && p->se_prev->se_priority <= sign->se_priority)
367 	p = p->se_prev;
368 
369     if (p == sign)
370     {
371 	// Sign not found. Search forward for a sign with priority just before
372 	// 'sign'.
373 	p = sign->se_next;
374 	while (p->se_next != NULL && p->se_next->se_lnum == sign->se_lnum
375 				&& p->se_next->se_priority > sign->se_priority)
376 	    p = p->se_next;
377     }
378 
379     // Remove 'sign' from the list
380     if (buf->b_signlist == sign)
381 	buf->b_signlist = sign->se_next;
382     if (sign->se_prev != NULL)
383 	sign->se_prev->se_next = sign->se_next;
384     if (sign->se_next != NULL)
385 	sign->se_next->se_prev = sign->se_prev;
386     sign->se_prev = NULL;
387     sign->se_next = NULL;
388 
389     // Re-insert 'sign' at the right place
390     if (p->se_priority <= sign->se_priority)
391     {
392 	// 'sign' has a higher priority and should be inserted before 'p'
393 	sign->se_prev = p->se_prev;
394 	sign->se_next = p;
395 	p->se_prev = sign;
396 	if (sign->se_prev != NULL)
397 	    sign->se_prev->se_next = sign;
398 	if (buf->b_signlist == p)
399 	    buf->b_signlist = sign;
400     }
401     else
402     {
403 	// 'sign' has a lower priority and should be inserted after 'p'
404 	sign->se_prev = p;
405 	sign->se_next = p->se_next;
406 	p->se_next = sign;
407 	if (sign->se_next != NULL)
408 	    sign->se_next->se_prev = sign;
409     }
410 }
411 
412 /*
413  * Add the sign into the signlist. Find the right spot to do it though.
414  */
415     static void
416 buf_addsign(
417     buf_T	*buf,		// buffer to store sign in
418     int		id,		// sign ID
419     char_u	*groupname,	// sign group
420     int		prio,		// sign priority
421     linenr_T	lnum,		// line number which gets the mark
422     int		typenr)		// typenr of sign we are adding
423 {
424     sign_entry_T	*sign;		// a sign in the signlist
425     sign_entry_T	*prev;		// the previous sign
426 
427     prev = NULL;
428     FOR_ALL_SIGNS_IN_BUF(buf, sign)
429     {
430 	if (lnum == sign->se_lnum && id == sign->se_id
431 		&& sign_in_group(sign, groupname))
432 	{
433 	    // Update an existing sign
434 	    sign->se_typenr = typenr;
435 	    sign->se_priority = prio;
436 	    sign_sort_by_prio_on_line(buf, sign);
437 	    return;
438 	}
439 	else if (lnum < sign->se_lnum)
440 	{
441 	    insert_sign_by_lnum_prio(buf, prev, id, groupname, prio,
442 								lnum, typenr);
443 	    return;
444 	}
445 	prev = sign;
446     }
447 
448     insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr);
449     return;
450 }
451 
452 /*
453  * For an existing, placed sign "markId" change the type to "typenr".
454  * Returns the line number of the sign, or zero if the sign is not found.
455  */
456     static linenr_T
457 buf_change_sign_type(
458     buf_T	*buf,		// buffer to store sign in
459     int		markId,		// sign ID
460     char_u	*group,		// sign group
461     int		typenr,		// typenr of sign we are adding
462     int		prio)		// sign priority
463 {
464     sign_entry_T	*sign;		// a sign in the signlist
465 
466     FOR_ALL_SIGNS_IN_BUF(buf, sign)
467     {
468 	if (sign->se_id == markId && sign_in_group(sign, group))
469 	{
470 	    sign->se_typenr = typenr;
471 	    sign->se_priority = prio;
472 	    sign_sort_by_prio_on_line(buf, sign);
473 	    return sign->se_lnum;
474 	}
475     }
476 
477     return (linenr_T)0;
478 }
479 
480 /*
481  * Return the attributes of the first sign placed on line 'lnum' in buffer
482  * 'buf'. Used when refreshing the screen. Returns TRUE if a sign is found on
483  * 'lnum', FALSE otherwise.
484  */
485     int
486 buf_get_signattrs(win_T *wp, linenr_T lnum, sign_attrs_T *sattr)
487 {
488     sign_entry_T	*sign;
489     sign_T		*sp;
490     buf_T		*buf = wp->w_buffer;
491 
492     CLEAR_POINTER(sattr);
493 
494     FOR_ALL_SIGNS_IN_BUF(buf, sign)
495     {
496 	if (sign->se_lnum > lnum)
497 	    // Signs are sorted by line number in the buffer. No need to check
498 	    // for signs after the specified line number 'lnum'.
499 	    break;
500 
501 	if (sign->se_lnum == lnum
502 # ifdef FEAT_PROP_POPUP
503 		&& sign_group_for_window(sign, wp)
504 # endif
505 		)
506 	{
507 	    sattr->sat_typenr = sign->se_typenr;
508 	    sp = find_sign_by_typenr(sign->se_typenr);
509 	    if (sp == NULL)
510 		return FALSE;
511 
512 # ifdef FEAT_SIGN_ICONS
513 	    sattr->sat_icon = sp->sn_image;
514 # endif
515 	    sattr->sat_text = sp->sn_text;
516 	    if (sattr->sat_text != NULL && sp->sn_text_hl > 0)
517 		sattr->sat_texthl = syn_id2attr(sp->sn_text_hl);
518 	    if (sp->sn_line_hl > 0)
519 		sattr->sat_linehl = syn_id2attr(sp->sn_line_hl);
520 
521 	    // If there is another sign next with the same priority, may
522 	    // combine the text and the line highlighting.
523 	    if (sign->se_next != NULL
524 		    && sign->se_next->se_priority == sign->se_priority
525 		    && sign->se_next->se_lnum == sign->se_lnum)
526 	    {
527 		sign_T	*next_sp = find_sign_by_typenr(sign->se_next->se_typenr);
528 
529 		if (next_sp != NULL)
530 		{
531 		    if (sattr->sat_icon == NULL && sattr->sat_text == NULL)
532 		    {
533 # ifdef FEAT_SIGN_ICONS
534 			sattr->sat_icon = next_sp->sn_image;
535 # endif
536 			sattr->sat_text = next_sp->sn_text;
537 		    }
538 		    if (sp->sn_text_hl <= 0 && next_sp->sn_text_hl > 0)
539 			sattr->sat_texthl = syn_id2attr(next_sp->sn_text_hl);
540 		    if (sp->sn_line_hl <= 0 && next_sp->sn_line_hl > 0)
541 			sattr->sat_linehl = syn_id2attr(next_sp->sn_line_hl);
542 		}
543 	    }
544 	    return TRUE;
545 	}
546     }
547     return FALSE;
548 }
549 
550 /*
551  * Delete sign 'id' in group 'group' from buffer 'buf'.
552  * If 'id' is zero, then delete all the signs in group 'group'. Otherwise
553  * delete only the specified sign.
554  * If 'group' is '*', then delete the sign in all the groups. If 'group' is
555  * NULL, then delete the sign in the global group. Otherwise delete the sign in
556  * the specified group.
557  * Returns the line number of the deleted sign. If multiple signs are deleted,
558  * then returns the line number of the last sign deleted.
559  */
560     linenr_T
561 buf_delsign(
562     buf_T	*buf,		// buffer sign is stored in
563     linenr_T	atlnum,		// sign at this line, 0 - at any line
564     int		id,		// sign id
565     char_u	*group)		// sign group
566 {
567     sign_entry_T	**lastp;	// pointer to pointer to current sign
568     sign_entry_T	*sign;		// a sign in a b_signlist
569     sign_entry_T	*next;		// the next sign in a b_signlist
570     linenr_T		lnum;		// line number whose sign was deleted
571 
572     lastp = &buf->b_signlist;
573     lnum = 0;
574     for (sign = buf->b_signlist; sign != NULL; sign = next)
575     {
576 	next = sign->se_next;
577 	if ((id == 0 || sign->se_id == id)
578 		&& (atlnum == 0 || sign->se_lnum == atlnum)
579 		&& sign_in_group(sign, group))
580 
581 	{
582 	    *lastp = next;
583 	    if (next != NULL)
584 		next->se_prev = sign->se_prev;
585 	    lnum = sign->se_lnum;
586 	    if (sign->se_group != NULL)
587 		sign_group_unref(sign->se_group->sg_name);
588 	    vim_free(sign);
589 	    redraw_buf_line_later(buf, lnum);
590 
591 	    // Check whether only one sign needs to be deleted
592 	    // If deleting a sign with a specific identifier in a particular
593 	    // group or deleting any sign at a particular line number, delete
594 	    // only one sign.
595 	    if (group == NULL
596 		    || (*group != '*' && id != 0)
597 		    || (*group == '*' && atlnum != 0))
598 		break;
599 	}
600 	else
601 	    lastp = &sign->se_next;
602     }
603 
604     // When deleting the last sign the cursor position may change, because the
605     // sign columns no longer shows.  And the 'signcolumn' may be hidden.
606     if (buf->b_signlist == NULL)
607     {
608 	redraw_buf_later(buf, NOT_VALID);
609 	changed_line_abv_curs();
610     }
611 
612     return lnum;
613 }
614 
615 
616 /*
617  * Find the line number of the sign with the requested id in group 'group'. If
618  * the sign does not exist, return 0 as the line number. This will still let
619  * the correct file get loaded.
620  */
621     int
622 buf_findsign(
623     buf_T	*buf,		// buffer to store sign in
624     int		id,		// sign ID
625     char_u	*group)		// sign group
626 {
627     sign_entry_T	*sign;		// a sign in the signlist
628 
629     FOR_ALL_SIGNS_IN_BUF(buf, sign)
630 	if (sign->se_id == id && sign_in_group(sign, group))
631 	    return sign->se_lnum;
632 
633     return 0;
634 }
635 
636 /*
637  * Return the sign at line 'lnum' in buffer 'buf'. Returns NULL if a sign is
638  * not found at the line. If 'groupname' is NULL, searches in the global group.
639  */
640     static sign_entry_T *
641 buf_getsign_at_line(
642     buf_T	*buf,		// buffer whose sign we are searching for
643     linenr_T	lnum,		// line number of sign
644     char_u	*groupname)	// sign group name
645 {
646     sign_entry_T	*sign;		// a sign in the signlist
647 
648     FOR_ALL_SIGNS_IN_BUF(buf, sign)
649     {
650 	if (sign->se_lnum > lnum)
651 	    // Signs are sorted by line number in the buffer. No need to check
652 	    // for signs after the specified line number 'lnum'.
653 	    break;
654 
655 	if (sign->se_lnum == lnum && sign_in_group(sign, groupname))
656 	    return sign;
657     }
658 
659     return NULL;
660 }
661 
662 /*
663  * Return the identifier of the sign at line number 'lnum' in buffer 'buf'.
664  */
665     int
666 buf_findsign_id(
667     buf_T	*buf,		// buffer whose sign we are searching for
668     linenr_T	lnum,		// line number of sign
669     char_u	*groupname)	// sign group name
670 {
671     sign_entry_T	*sign;		// a sign in the signlist
672 
673     sign = buf_getsign_at_line(buf, lnum, groupname);
674     if (sign != NULL)
675 	return sign->se_id;
676 
677     return 0;
678 }
679 
680 # if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
681 /*
682  * See if a given type of sign exists on a specific line.
683  */
684     int
685 buf_findsigntype_id(
686     buf_T	*buf,		// buffer whose sign we are searching for
687     linenr_T	lnum,		// line number of sign
688     int		typenr)		// sign type number
689 {
690     sign_entry_T	*sign;		// a sign in the signlist
691 
692     FOR_ALL_SIGNS_IN_BUF(buf, sign)
693     {
694 	if (sign->se_lnum > lnum)
695 	    // Signs are sorted by line number in the buffer. No need to check
696 	    // for signs after the specified line number 'lnum'.
697 	    break;
698 
699 	if (sign->se_lnum == lnum && sign->se_typenr == typenr)
700 	    return sign->se_id;
701     }
702 
703     return 0;
704 }
705 
706 
707 #  if defined(FEAT_SIGN_ICONS) || defined(PROTO)
708 /*
709  * Return the number of icons on the given line.
710  */
711     int
712 buf_signcount(buf_T *buf, linenr_T lnum)
713 {
714     sign_entry_T	*sign;		// a sign in the signlist
715     int			count = 0;
716 
717     FOR_ALL_SIGNS_IN_BUF(buf, sign)
718     {
719 	if (sign->se_lnum > lnum)
720 	    // Signs are sorted by line number in the buffer. No need to check
721 	    // for signs after the specified line number 'lnum'.
722 	    break;
723 
724 	if (sign->se_lnum == lnum)
725 	    if (sign_get_image(sign->se_typenr) != NULL)
726 		count++;
727     }
728 
729     return count;
730 }
731 #  endif // FEAT_SIGN_ICONS
732 # endif // FEAT_NETBEANS_INTG
733 
734 /*
735  * Delete signs in group 'group' in buffer "buf". If 'group' is '*', then
736  * delete all the signs.
737  */
738     void
739 buf_delete_signs(buf_T *buf, char_u *group)
740 {
741     sign_entry_T	*sign;
742     sign_entry_T	**lastp;	// pointer to pointer to current sign
743     sign_entry_T	*next;
744 
745     // When deleting the last sign need to redraw the windows to remove the
746     // sign column. Not when curwin is NULL (this means we're exiting).
747     if (buf->b_signlist != NULL && curwin != NULL)
748     {
749 	redraw_buf_later(buf, NOT_VALID);
750 	changed_line_abv_curs();
751     }
752 
753     lastp = &buf->b_signlist;
754     for (sign = buf->b_signlist; sign != NULL; sign = next)
755     {
756 	next = sign->se_next;
757 	if (sign_in_group(sign, group))
758 	{
759 	    *lastp = next;
760 	    if (next != NULL)
761 		next->se_prev = sign->se_prev;
762 	    if (sign->se_group != NULL)
763 		sign_group_unref(sign->se_group->sg_name);
764 	    vim_free(sign);
765 	}
766 	else
767 	    lastp = &sign->se_next;
768     }
769 }
770 
771 /*
772  * List placed signs for "rbuf".  If "rbuf" is NULL do it for all buffers.
773  */
774     static void
775 sign_list_placed(buf_T *rbuf, char_u *sign_group)
776 {
777     buf_T		*buf;
778     sign_entry_T	*sign;
779     char		lbuf[MSG_BUF_LEN];
780     char		group[MSG_BUF_LEN];
781 
782     msg_puts_title(_("\n--- Signs ---"));
783     msg_putchar('\n');
784     if (rbuf == NULL)
785 	buf = firstbuf;
786     else
787 	buf = rbuf;
788     while (buf != NULL && !got_int)
789     {
790 	if (buf->b_signlist != NULL)
791 	{
792 	    vim_snprintf(lbuf, MSG_BUF_LEN, _("Signs for %s:"), buf->b_fname);
793 	    msg_puts_attr(lbuf, HL_ATTR(HLF_D));
794 	    msg_putchar('\n');
795 	}
796 	FOR_ALL_SIGNS_IN_BUF(buf, sign)
797 	{
798 	    if (got_int)
799 		break;
800 	    if (!sign_in_group(sign, sign_group))
801 		continue;
802 	    if (sign->se_group != NULL)
803 		vim_snprintf(group, MSG_BUF_LEN, _("  group=%s"),
804 						      sign->se_group->sg_name);
805 	    else
806 		group[0] = '\0';
807 	    vim_snprintf(lbuf, MSG_BUF_LEN,
808 			   _("    line=%ld  id=%d%s  name=%s  priority=%d"),
809 			   (long)sign->se_lnum, sign->se_id, group,
810 			 sign_typenr2name(sign->se_typenr), sign->se_priority);
811 	    msg_puts(lbuf);
812 	    msg_putchar('\n');
813 	}
814 	if (rbuf != NULL)
815 	    break;
816 	buf = buf->b_next;
817     }
818 }
819 
820 /*
821  * Adjust a placed sign for inserted/deleted lines.
822  */
823     void
824 sign_mark_adjust(
825     linenr_T	line1,
826     linenr_T	line2,
827     long	amount,
828     long	amount_after)
829 {
830     sign_entry_T	*sign;		// a sign in a b_signlist
831     linenr_T		new_lnum;
832 
833     FOR_ALL_SIGNS_IN_BUF(curbuf, sign)
834     {
835 	// Ignore changes to lines after the sign
836 	if (sign->se_lnum < line1)
837 	    continue;
838 	new_lnum = sign->se_lnum;
839 	if (sign->se_lnum >= line1 && sign->se_lnum <= line2)
840 	{
841 	    if (amount != MAXLNUM)
842 		new_lnum += amount;
843 	}
844 	else if (sign->se_lnum > line2)
845 	    // Lines inserted or deleted before the sign
846 	    new_lnum += amount_after;
847 
848 	// If the new sign line number is past the last line in the buffer,
849 	// then don't adjust the line number. Otherwise, it will always be past
850 	// the last line and will not be visible.
851 	if (new_lnum <= curbuf->b_ml.ml_line_count)
852 	    sign->se_lnum = new_lnum;
853     }
854 }
855 
856 /*
857  * Find index of a ":sign" subcmd from its name.
858  * "*end_cmd" must be writable.
859  */
860     static int
861 sign_cmd_idx(
862     char_u	*begin_cmd,	// begin of sign subcmd
863     char_u	*end_cmd)	// just after sign subcmd
864 {
865     int		idx;
866     char	save = *end_cmd;
867 
868     *end_cmd = NUL;
869     for (idx = 0; ; ++idx)
870 	if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0)
871 	    break;
872     *end_cmd = save;
873     return idx;
874 }
875 
876 /*
877  * Find a sign by name. Also returns pointer to the previous sign.
878  */
879     static sign_T *
880 sign_find(char_u *name, sign_T **sp_prev)
881 {
882     sign_T *sp;
883 
884     if (sp_prev != NULL)
885 	*sp_prev = NULL;
886     FOR_ALL_SIGNS(sp)
887     {
888 	if (STRCMP(sp->sn_name, name) == 0)
889 	    break;
890 	if (sp_prev != NULL)
891 	    *sp_prev = sp;
892     }
893 
894     return sp;
895 }
896 
897 /*
898  * Allocate a new sign
899  */
900     static sign_T *
901 alloc_new_sign(char_u *name)
902 {
903     sign_T	*sp;
904     sign_T	*lp;
905     int	start = next_sign_typenr;
906 
907     // Allocate a new sign.
908     sp = alloc_clear_id(sizeof(sign_T), aid_sign_define_by_name);
909     if (sp == NULL)
910 	return NULL;
911 
912     // Check that next_sign_typenr is not already being used.
913     // This only happens after wrapping around.  Hopefully
914     // another one got deleted and we can use its number.
915     for (lp = first_sign; lp != NULL; )
916     {
917 	if (lp->sn_typenr == next_sign_typenr)
918 	{
919 	    ++next_sign_typenr;
920 	    if (next_sign_typenr == MAX_TYPENR)
921 		next_sign_typenr = 1;
922 	    if (next_sign_typenr == start)
923 	    {
924 		vim_free(sp);
925 		emsg(_("E612: Too many signs defined"));
926 		return NULL;
927 	    }
928 	    lp = first_sign;  // start all over
929 	    continue;
930 	}
931 	lp = lp->sn_next;
932     }
933 
934     sp->sn_typenr = next_sign_typenr;
935     if (++next_sign_typenr == MAX_TYPENR)
936 	next_sign_typenr = 1; // wrap around
937 
938     sp->sn_name = vim_strsave(name);
939     if (sp->sn_name == NULL)  // out of memory
940     {
941 	vim_free(sp);
942 	return NULL;
943     }
944 
945     return sp;
946 }
947 
948 /*
949  * Initialize the icon information for a new sign
950  */
951     static void
952 sign_define_init_icon(sign_T *sp, char_u *icon)
953 {
954     vim_free(sp->sn_icon);
955     sp->sn_icon = vim_strsave(icon);
956     backslash_halve(sp->sn_icon);
957 # ifdef FEAT_SIGN_ICONS
958     if (gui.in_use)
959     {
960 	out_flush();
961 	if (sp->sn_image != NULL)
962 	    gui_mch_destroy_sign(sp->sn_image);
963 	sp->sn_image = gui_mch_register_sign(sp->sn_icon);
964     }
965 # endif
966 }
967 
968 /*
969  * Initialize the text for a new sign
970  */
971     static int
972 sign_define_init_text(sign_T *sp, char_u *text)
973 {
974     char_u	*s;
975     char_u	*endp;
976     int		cells;
977     int		len;
978 
979     endp = text + (int)STRLEN(text);
980 
981     // Remove backslashes so that it is possible to use a space.
982     for (s = text; s + 1 < endp; ++s)
983 	if (*s == '\\')
984 	{
985 	    STRMOVE(s, s + 1);
986 	    --endp;
987 	}
988 
989     // Count cells and check for non-printable chars
990     if (has_mbyte)
991     {
992 	cells = 0;
993 	for (s = text; s < endp; s += (*mb_ptr2len)(s))
994 	{
995 	    if (!vim_isprintc((*mb_ptr2char)(s)))
996 		break;
997 	    cells += (*mb_ptr2cells)(s);
998 	}
999     }
1000     else
1001     {
1002 	for (s = text; s < endp; ++s)
1003 	    if (!vim_isprintc(*s))
1004 		break;
1005 	cells = (int)(s - text);
1006     }
1007 
1008     // Currently sign text must be one or two display cells
1009     if (s != endp || cells < 1 || cells > 2)
1010     {
1011 	semsg(_("E239: Invalid sign text: %s"), text);
1012 	return FAIL;
1013     }
1014 
1015     vim_free(sp->sn_text);
1016     // Allocate one byte more if we need to pad up
1017     // with a space.
1018     len = (int)(endp - text + ((cells == 1) ? 1 : 0));
1019     sp->sn_text = vim_strnsave(text, len);
1020 
1021     // For single character sign text, pad with a space.
1022     if (sp->sn_text != NULL && cells == 1)
1023 	STRCPY(sp->sn_text + len - 1, " ");
1024 
1025     return OK;
1026 }
1027 
1028 /*
1029  * Define a new sign or update an existing sign
1030  */
1031     int
1032 sign_define_by_name(
1033 	char_u	*name,
1034 	char_u	*icon,
1035 	char_u	*linehl,
1036 	char_u	*text,
1037 	char_u	*texthl)
1038 {
1039     sign_T	*sp_prev;
1040     sign_T	*sp;
1041 
1042     sp = sign_find(name, &sp_prev);
1043     if (sp == NULL)
1044     {
1045 	sp = alloc_new_sign(name);
1046 	if (sp == NULL)
1047 	    return FAIL;
1048 
1049 	// add the new sign to the list of signs
1050 	if (sp_prev == NULL)
1051 	    first_sign = sp;
1052 	else
1053 	    sp_prev->sn_next = sp;
1054     }
1055     else
1056     {
1057 	win_T *wp;
1058 
1059 	// Signs may already exist, a redraw is needed in windows with a
1060 	// non-empty sign list.
1061 	FOR_ALL_WINDOWS(wp)
1062 	    if (wp->w_buffer->b_signlist != NULL)
1063 		redraw_buf_later(wp->w_buffer, NOT_VALID);
1064     }
1065 
1066     // set values for a defined sign.
1067     if (icon != NULL)
1068 	sign_define_init_icon(sp, icon);
1069 
1070     if (text != NULL && (sign_define_init_text(sp, text) == FAIL))
1071 	return FAIL;
1072 
1073     if (linehl != NULL)
1074 	sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl));
1075 
1076     if (texthl != NULL)
1077 	sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl));
1078 
1079     return OK;
1080 }
1081 
1082 /*
1083  * Return TRUE if sign "name" exists.
1084  */
1085     int
1086 sign_exists_by_name(char_u *name)
1087 {
1088     return sign_find(name, NULL) != NULL;
1089 }
1090 
1091 /*
1092  * Free the sign specified by 'name'.
1093  */
1094     int
1095 sign_undefine_by_name(char_u *name, int give_error)
1096 {
1097     sign_T	*sp_prev;
1098     sign_T	*sp;
1099 
1100     sp = sign_find(name, &sp_prev);
1101     if (sp == NULL)
1102     {
1103 	if (give_error)
1104 	    semsg(_("E155: Unknown sign: %s"), name);
1105 	return FAIL;
1106     }
1107     sign_undefine(sp, sp_prev);
1108 
1109     return OK;
1110 }
1111 
1112 /*
1113  * List the signs matching 'name'
1114  */
1115     static void
1116 sign_list_by_name(char_u *name)
1117 {
1118     sign_T	*sp;
1119 
1120     sp = sign_find(name, NULL);
1121     if (sp != NULL)
1122 	sign_list_defined(sp);
1123     else
1124 	semsg(_("E155: Unknown sign: %s"), name);
1125 }
1126 
1127     static void
1128 may_force_numberwidth_recompute(buf_T *buf, int unplace)
1129 {
1130     tabpage_T	*tp;
1131     win_T		*wp;
1132 
1133     FOR_ALL_TAB_WINDOWS(tp, wp)
1134 	if (wp->w_buffer == buf
1135 		&& (wp->w_p_nu || wp->w_p_rnu)
1136 		&& (unplace || wp->w_nrwidth_width < 2)
1137 		&& (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u'))
1138 	    wp->w_nrwidth_line_count = 0;
1139 }
1140 
1141 /*
1142  * Place a sign at the specified file location or update a sign.
1143  */
1144     int
1145 sign_place(
1146 	int		*sign_id,
1147 	char_u		*sign_group,
1148 	char_u		*sign_name,
1149 	buf_T		*buf,
1150 	linenr_T	lnum,
1151 	int		prio)
1152 {
1153     sign_T	*sp;
1154 
1155     // Check for reserved character '*' in group name
1156     if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0'))
1157 	return FAIL;
1158 
1159     FOR_ALL_SIGNS(sp)
1160 	if (STRCMP(sp->sn_name, sign_name) == 0)
1161 	    break;
1162     if (sp == NULL)
1163     {
1164 	semsg(_("E155: Unknown sign: %s"), sign_name);
1165 	return FAIL;
1166     }
1167     if (*sign_id == 0)
1168 	*sign_id = sign_group_get_next_signid(buf, sign_group);
1169 
1170     if (lnum > 0)
1171 	// ":sign place {id} line={lnum} name={name} file={fname}":
1172 	// place a sign
1173 	buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr);
1174     else
1175 	// ":sign place {id} file={fname}": change sign type and/or priority
1176 	lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr,
1177 								prio);
1178     if (lnum > 0)
1179     {
1180 	redraw_buf_line_later(buf, lnum);
1181 
1182 	// When displaying signs in the 'number' column, if the width of the
1183 	// number column is less than 2, then force recomputing the width.
1184 	may_force_numberwidth_recompute(buf, FALSE);
1185     }
1186     else
1187     {
1188 	semsg(_("E885: Not possible to change sign %s"), sign_name);
1189 	return FAIL;
1190     }
1191 
1192     return OK;
1193 }
1194 
1195 /*
1196  * Unplace the specified sign
1197  */
1198     static int
1199 sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum)
1200 {
1201     if (buf->b_signlist == NULL)	// No signs in the buffer
1202 	return OK;
1203 
1204     if (sign_id == 0)
1205     {
1206 	// Delete all the signs in the specified buffer
1207 	redraw_buf_later(buf, NOT_VALID);
1208 	buf_delete_signs(buf, sign_group);
1209     }
1210     else
1211     {
1212 	linenr_T	lnum;
1213 
1214 	// Delete only the specified signs
1215 	lnum = buf_delsign(buf, atlnum, sign_id, sign_group);
1216 	if (lnum == 0)
1217 	    return FAIL;
1218     }
1219 
1220     // When all the signs in a buffer are removed, force recomputing the
1221     // number column width (if enabled) in all the windows displaying the
1222     // buffer if 'signcolumn' is set to 'number' in that window.
1223     if (buf->b_signlist == NULL)
1224 	may_force_numberwidth_recompute(buf, TRUE);
1225 
1226     return OK;
1227 }
1228 
1229 /*
1230  * Unplace the sign at the current cursor line.
1231  */
1232     static void
1233 sign_unplace_at_cursor(char_u *groupname)
1234 {
1235     int		id = -1;
1236 
1237     id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname);
1238     if (id > 0)
1239 	sign_unplace(id, groupname, curwin->w_buffer, curwin->w_cursor.lnum);
1240     else
1241 	emsg(_("E159: Missing sign number"));
1242 }
1243 
1244 /*
1245  * Jump to a sign.
1246  */
1247     static linenr_T
1248 sign_jump(int sign_id, char_u *sign_group, buf_T *buf)
1249 {
1250     linenr_T	lnum;
1251 
1252     if ((lnum = buf_findsign(buf, sign_id, sign_group)) <= 0)
1253     {
1254 	semsg(_("E157: Invalid sign ID: %d"), sign_id);
1255 	return -1;
1256     }
1257 
1258     // goto a sign ...
1259     if (buf_jump_open_win(buf) != NULL)
1260     {			// ... in a current window
1261 	curwin->w_cursor.lnum = lnum;
1262 	check_cursor_lnum();
1263 	beginline(BL_WHITE);
1264     }
1265     else
1266     {			// ... not currently in a window
1267 	char_u	*cmd;
1268 
1269 	if (buf->b_fname == NULL)
1270 	{
1271 	    emsg(_("E934: Cannot jump to a buffer that does not have a name"));
1272 	    return -1;
1273 	}
1274 	cmd = alloc(STRLEN(buf->b_fname) + 25);
1275 	if (cmd == NULL)
1276 	    return -1;
1277 	sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname);
1278 	do_cmdline_cmd(cmd);
1279 	vim_free(cmd);
1280     }
1281 # ifdef FEAT_FOLDING
1282     foldOpenCursor();
1283 # endif
1284 
1285     return lnum;
1286 }
1287 
1288 /*
1289  * ":sign define {name} ..." command
1290  */
1291     static void
1292 sign_define_cmd(char_u *sign_name, char_u *cmdline)
1293 {
1294     char_u	*arg;
1295     char_u	*p = cmdline;
1296     char_u	*icon = NULL;
1297     char_u	*text = NULL;
1298     char_u	*linehl = NULL;
1299     char_u	*texthl = NULL;
1300     int failed = FALSE;
1301 
1302     // set values for a defined sign.
1303     for (;;)
1304     {
1305 	arg = skipwhite(p);
1306 	if (*arg == NUL)
1307 	    break;
1308 	p = skiptowhite_esc(arg);
1309 	if (STRNCMP(arg, "icon=", 5) == 0)
1310 	{
1311 	    arg += 5;
1312 	    icon = vim_strnsave(arg, (int)(p - arg));
1313 	}
1314 	else if (STRNCMP(arg, "text=", 5) == 0)
1315 	{
1316 	    arg += 5;
1317 	    text = vim_strnsave(arg, (int)(p - arg));
1318 	}
1319 	else if (STRNCMP(arg, "linehl=", 7) == 0)
1320 	{
1321 	    arg += 7;
1322 	    linehl = vim_strnsave(arg, (int)(p - arg));
1323 	}
1324 	else if (STRNCMP(arg, "texthl=", 7) == 0)
1325 	{
1326 	    arg += 7;
1327 	    texthl = vim_strnsave(arg, (int)(p - arg));
1328 	}
1329 	else
1330 	{
1331 	    semsg(_(e_invarg2), arg);
1332 	    failed = TRUE;
1333 	    break;
1334 	}
1335     }
1336 
1337     if (!failed)
1338 	sign_define_by_name(sign_name, icon, linehl, text, texthl);
1339 
1340     vim_free(icon);
1341     vim_free(text);
1342     vim_free(linehl);
1343     vim_free(texthl);
1344 }
1345 
1346 /*
1347  * ":sign place" command
1348  */
1349     static void
1350 sign_place_cmd(
1351 	buf_T		*buf,
1352 	linenr_T	lnum,
1353 	char_u		*sign_name,
1354 	int		id,
1355 	char_u		*group,
1356 	int		prio)
1357 {
1358     if (id <= 0)
1359     {
1360 	// List signs placed in a file/buffer
1361 	//   :sign place file={fname}
1362 	//   :sign place group={group} file={fname}
1363 	//   :sign place group=* file={fname}
1364 	//   :sign place buffer={nr}
1365 	//   :sign place group={group} buffer={nr}
1366 	//   :sign place group=* buffer={nr}
1367 	//   :sign place
1368 	//   :sign place group={group}
1369 	//   :sign place group=*
1370 	if (lnum >= 0 || sign_name != NULL
1371 		|| (group != NULL && *group == '\0'))
1372 	    emsg(_(e_invarg));
1373 	else
1374 	    sign_list_placed(buf, group);
1375     }
1376     else
1377     {
1378 	// Place a new sign
1379 	if (sign_name == NULL || buf == NULL
1380 		|| (group != NULL && *group == '\0'))
1381 	{
1382 	    emsg(_(e_invarg));
1383 	    return;
1384 	}
1385 
1386 	sign_place(&id, group, sign_name, buf, lnum, prio);
1387     }
1388 }
1389 
1390 /*
1391  * ":sign unplace" command
1392  */
1393     static void
1394 sign_unplace_cmd(
1395 	buf_T		*buf,
1396 	linenr_T	lnum,
1397 	char_u		*sign_name,
1398 	int		id,
1399 	char_u		*group)
1400 {
1401     if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0'))
1402     {
1403 	emsg(_(e_invarg));
1404 	return;
1405     }
1406 
1407     if (id == -2)
1408     {
1409 	if (buf != NULL)
1410 	    // :sign unplace * file={fname}
1411 	    // :sign unplace * group={group} file={fname}
1412 	    // :sign unplace * group=* file={fname}
1413 	    // :sign unplace * buffer={nr}
1414 	    // :sign unplace * group={group} buffer={nr}
1415 	    // :sign unplace * group=* buffer={nr}
1416 	    sign_unplace(0, group, buf, 0);
1417 	else
1418 	    // :sign unplace *
1419 	    // :sign unplace * group={group}
1420 	    // :sign unplace * group=*
1421 	    FOR_ALL_BUFFERS(buf)
1422 		if (buf->b_signlist != NULL)
1423 		    buf_delete_signs(buf, group);
1424     }
1425     else
1426     {
1427 	if (buf != NULL)
1428 	    // :sign unplace {id} file={fname}
1429 	    // :sign unplace {id} group={group} file={fname}
1430 	    // :sign unplace {id} group=* file={fname}
1431 	    // :sign unplace {id} buffer={nr}
1432 	    // :sign unplace {id} group={group} buffer={nr}
1433 	    // :sign unplace {id} group=* buffer={nr}
1434 	    sign_unplace(id, group, buf, 0);
1435 	else
1436 	{
1437 	    if (id == -1)
1438 	    {
1439 		// :sign unplace group={group}
1440 		// :sign unplace group=*
1441 		sign_unplace_at_cursor(group);
1442 	    }
1443 	    else
1444 	    {
1445 		// :sign unplace {id}
1446 		// :sign unplace {id} group={group}
1447 		// :sign unplace {id} group=*
1448 		FOR_ALL_BUFFERS(buf)
1449 		    sign_unplace(id, group, buf, 0);
1450 	    }
1451 	}
1452     }
1453 }
1454 
1455 /*
1456  * Jump to a placed sign commands:
1457  *   :sign jump {id} file={fname}
1458  *   :sign jump {id} buffer={nr}
1459  *   :sign jump {id} group={group} file={fname}
1460  *   :sign jump {id} group={group} buffer={nr}
1461  */
1462     static void
1463 sign_jump_cmd(
1464 	buf_T		*buf,
1465 	linenr_T	lnum,
1466 	char_u		*sign_name,
1467 	int		id,
1468 	char_u		*group)
1469 {
1470     if (sign_name == NULL && group == NULL && id == -1)
1471     {
1472 	emsg(_(e_argreq));
1473 	return;
1474     }
1475 
1476     if (buf == NULL || (group != NULL && *group == '\0')
1477 					     || lnum >= 0 || sign_name != NULL)
1478     {
1479 	// File or buffer is not specified or an empty group is used
1480 	// or a line number or a sign name is specified.
1481 	emsg(_(e_invarg));
1482 	return;
1483     }
1484     (void)sign_jump(id, group, buf);
1485 }
1486 
1487 /*
1488  * Parse the command line arguments for the ":sign place", ":sign unplace" and
1489  * ":sign jump" commands.
1490  * The supported arguments are: line={lnum} name={name} group={group}
1491  * priority={prio} and file={fname} or buffer={nr}.
1492  */
1493     static int
1494 parse_sign_cmd_args(
1495 	int	    cmd,
1496 	char_u	    *arg,
1497 	char_u	    **sign_name,
1498 	int	    *signid,
1499 	char_u	    **group,
1500 	int	    *prio,
1501 	buf_T	    **buf,
1502 	linenr_T    *lnum)
1503 {
1504     char_u	*arg1;
1505     char_u	*name;
1506     char_u	*filename = NULL;
1507     int		lnum_arg = FALSE;
1508 
1509     // first arg could be placed sign id
1510     arg1 = arg;
1511     if (VIM_ISDIGIT(*arg))
1512     {
1513 	*signid = getdigits(&arg);
1514 	if (!VIM_ISWHITE(*arg) && *arg != NUL)
1515 	{
1516 	    *signid = -1;
1517 	    arg = arg1;
1518 	}
1519 	else
1520 	    arg = skipwhite(arg);
1521     }
1522 
1523     while (*arg != NUL)
1524     {
1525 	if (STRNCMP(arg, "line=", 5) == 0)
1526 	{
1527 	    arg += 5;
1528 	    *lnum = atoi((char *)arg);
1529 	    arg = skiptowhite(arg);
1530 	    lnum_arg = TRUE;
1531 	}
1532 	else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE)
1533 	{
1534 	    if (*signid != -1)
1535 	    {
1536 		emsg(_(e_invarg));
1537 		return FAIL;
1538 	    }
1539 	    *signid = -2;
1540 	    arg = skiptowhite(arg + 1);
1541 	}
1542 	else if (STRNCMP(arg, "name=", 5) == 0)
1543 	{
1544 	    arg += 5;
1545 	    name = arg;
1546 	    arg = skiptowhite(arg);
1547 	    if (*arg != NUL)
1548 		*arg++ = NUL;
1549 	    while (name[0] == '0' && name[1] != NUL)
1550 		++name;
1551 	    *sign_name = name;
1552 	}
1553 	else if (STRNCMP(arg, "group=", 6) == 0)
1554 	{
1555 	    arg += 6;
1556 	    *group = arg;
1557 	    arg = skiptowhite(arg);
1558 	    if (*arg != NUL)
1559 		*arg++ = NUL;
1560 	}
1561 	else if (STRNCMP(arg, "priority=", 9) == 0)
1562 	{
1563 	    arg += 9;
1564 	    *prio = atoi((char *)arg);
1565 	    arg = skiptowhite(arg);
1566 	}
1567 	else if (STRNCMP(arg, "file=", 5) == 0)
1568 	{
1569 	    arg += 5;
1570 	    filename = arg;
1571 	    *buf = buflist_findname_exp(arg);
1572 	    break;
1573 	}
1574 	else if (STRNCMP(arg, "buffer=", 7) == 0)
1575 	{
1576 	    arg += 7;
1577 	    filename = arg;
1578 	    *buf = buflist_findnr((int)getdigits(&arg));
1579 	    if (*skipwhite(arg) != NUL)
1580 		emsg(_(e_trailing));
1581 	    break;
1582 	}
1583 	else
1584 	{
1585 	    emsg(_(e_invarg));
1586 	    return FAIL;
1587 	}
1588 	arg = skipwhite(arg);
1589     }
1590 
1591     if (filename != NULL && *buf == NULL)
1592     {
1593 	semsg(_("E158: Invalid buffer name: %s"), filename);
1594 	return FAIL;
1595     }
1596 
1597     // If the filename is not supplied for the sign place or the sign jump
1598     // command, then use the current buffer.
1599     if (filename == NULL && ((cmd == SIGNCMD_PLACE && lnum_arg)
1600 						       || cmd == SIGNCMD_JUMP))
1601 	*buf = curwin->w_buffer;
1602 
1603     return OK;
1604 }
1605 
1606 /*
1607  * ":sign" command
1608  */
1609     void
1610 ex_sign(exarg_T *eap)
1611 {
1612     char_u	*arg = eap->arg;
1613     char_u	*p;
1614     int		idx;
1615     sign_T	*sp;
1616     buf_T	*buf = NULL;
1617 
1618     // Parse the subcommand.
1619     p = skiptowhite(arg);
1620     idx = sign_cmd_idx(arg, p);
1621     if (idx == SIGNCMD_LAST)
1622     {
1623 	semsg(_("E160: Unknown sign command: %s"), arg);
1624 	return;
1625     }
1626     arg = skipwhite(p);
1627 
1628     if (idx <= SIGNCMD_LIST)
1629     {
1630 	// Define, undefine or list signs.
1631 	if (idx == SIGNCMD_LIST && *arg == NUL)
1632 	{
1633 	    // ":sign list": list all defined signs
1634 	    for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next)
1635 		sign_list_defined(sp);
1636 	}
1637 	else if (*arg == NUL)
1638 	    emsg(_("E156: Missing sign name"));
1639 	else
1640 	{
1641 	    char_u	*name;
1642 
1643 	    // Isolate the sign name.  If it's a number skip leading zeroes,
1644 	    // so that "099" and "99" are the same sign.  But keep "0".
1645 	    p = skiptowhite(arg);
1646 	    if (*p != NUL)
1647 		*p++ = NUL;
1648 	    while (arg[0] == '0' && arg[1] != NUL)
1649 		++arg;
1650 	    name = vim_strsave(arg);
1651 
1652 	    if (idx == SIGNCMD_DEFINE)
1653 		sign_define_cmd(name, p);
1654 	    else if (idx == SIGNCMD_LIST)
1655 		// ":sign list {name}"
1656 		sign_list_by_name(name);
1657 	    else
1658 		// ":sign undefine {name}"
1659 		sign_undefine_by_name(name, TRUE);
1660 
1661 	    vim_free(name);
1662 	    return;
1663 	}
1664     }
1665     else
1666     {
1667 	int		id = -1;
1668 	linenr_T	lnum = -1;
1669 	char_u		*sign_name = NULL;
1670 	char_u		*group = NULL;
1671 	int		prio = SIGN_DEF_PRIO;
1672 
1673 	// Parse command line arguments
1674 	if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio,
1675 							  &buf, &lnum) == FAIL)
1676 	    return;
1677 
1678 	if (idx == SIGNCMD_PLACE)
1679 	    sign_place_cmd(buf, lnum, sign_name, id, group, prio);
1680 	else if (idx == SIGNCMD_UNPLACE)
1681 	    sign_unplace_cmd(buf, lnum, sign_name, id, group);
1682 	else if (idx == SIGNCMD_JUMP)
1683 	    sign_jump_cmd(buf, lnum, sign_name, id, group);
1684     }
1685 }
1686 
1687 /*
1688  * Return information about a specified sign
1689  */
1690     static void
1691 sign_getinfo(sign_T *sp, dict_T *retdict)
1692 {
1693     char_u	*p;
1694 
1695     dict_add_string(retdict, "name", (char_u *)sp->sn_name);
1696     if (sp->sn_icon != NULL)
1697 	dict_add_string(retdict, "icon", (char_u *)sp->sn_icon);
1698     if (sp->sn_text != NULL)
1699 	dict_add_string(retdict, "text", (char_u *)sp->sn_text);
1700     if (sp->sn_line_hl > 0)
1701     {
1702 	p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1703 	if (p == NULL)
1704 	    p = (char_u *)"NONE";
1705 	dict_add_string(retdict, "linehl", (char_u *)p);
1706     }
1707     if (sp->sn_text_hl > 0)
1708     {
1709 	p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1710 	if (p == NULL)
1711 	    p = (char_u *)"NONE";
1712 	dict_add_string(retdict, "texthl", (char_u *)p);
1713     }
1714 }
1715 
1716 /*
1717  * If 'name' is NULL, return a list of all the defined signs.
1718  * Otherwise, return information about the specified sign.
1719  */
1720     static void
1721 sign_getlist(char_u *name, list_T *retlist)
1722 {
1723     sign_T	*sp = first_sign;
1724     dict_T	*dict;
1725 
1726     if (name != NULL)
1727     {
1728 	sp = sign_find(name, NULL);
1729 	if (sp == NULL)
1730 	    return;
1731     }
1732 
1733     for (; sp != NULL && !got_int; sp = sp->sn_next)
1734     {
1735 	if ((dict = dict_alloc_id(aid_sign_getlist)) == NULL)
1736 	    return;
1737 	if (list_append_dict(retlist, dict) == FAIL)
1738 	    return;
1739 	sign_getinfo(sp, dict);
1740 
1741 	if (name != NULL)	    // handle only the specified sign
1742 	    break;
1743     }
1744 }
1745 
1746 /*
1747  * Returns information about signs placed in a buffer as list of dicts.
1748  */
1749     void
1750 get_buffer_signs(buf_T *buf, list_T *l)
1751 {
1752     sign_entry_T	*sign;
1753     dict_T		*d;
1754 
1755     FOR_ALL_SIGNS_IN_BUF(buf, sign)
1756     {
1757 	if ((d = sign_get_info(sign)) != NULL)
1758 	    list_append_dict(l, d);
1759     }
1760 }
1761 
1762 /*
1763  * Return information about all the signs placed in a buffer
1764  */
1765     static void
1766 sign_get_placed_in_buf(
1767 	buf_T		*buf,
1768 	linenr_T	lnum,
1769 	int		sign_id,
1770 	char_u		*sign_group,
1771 	list_T		*retlist)
1772 {
1773     dict_T		*d;
1774     list_T		*l;
1775     sign_entry_T	*sign;
1776     dict_T		*sdict;
1777 
1778     if ((d = dict_alloc_id(aid_sign_getplaced_dict)) == NULL)
1779 	return;
1780     list_append_dict(retlist, d);
1781 
1782     dict_add_number(d, "bufnr", (long)buf->b_fnum);
1783 
1784     if ((l = list_alloc_id(aid_sign_getplaced_list)) == NULL)
1785 	return;
1786     dict_add_list(d, "signs", l);
1787 
1788     FOR_ALL_SIGNS_IN_BUF(buf, sign)
1789     {
1790 	if (!sign_in_group(sign, sign_group))
1791 	    continue;
1792 	if ((lnum == 0 && sign_id == 0)
1793 		|| (sign_id == 0 && lnum == sign->se_lnum)
1794 		|| (lnum == 0 && sign_id == sign->se_id)
1795 		|| (lnum == sign->se_lnum && sign_id == sign->se_id))
1796 	{
1797 	    if ((sdict = sign_get_info(sign)) != NULL)
1798 		list_append_dict(l, sdict);
1799 	}
1800     }
1801 }
1802 
1803 /*
1804  * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the
1805  * sign placed at the line number. If 'lnum' is zero, return all the signs
1806  * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers.
1807  */
1808     static void
1809 sign_get_placed(
1810 	buf_T		*buf,
1811 	linenr_T	lnum,
1812 	int		sign_id,
1813 	char_u		*sign_group,
1814 	list_T		*retlist)
1815 {
1816     if (buf != NULL)
1817 	sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist);
1818     else
1819     {
1820 	FOR_ALL_BUFFERS(buf)
1821 	    if (buf->b_signlist != NULL)
1822 		sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist);
1823     }
1824 }
1825 
1826 # if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1827 /*
1828  * Allocate the icons.  Called when the GUI has started.  Allows defining
1829  * signs before it starts.
1830  */
1831     void
1832 sign_gui_started(void)
1833 {
1834     sign_T	*sp;
1835 
1836     FOR_ALL_SIGNS(sp)
1837 	if (sp->sn_icon != NULL)
1838 	    sp->sn_image = gui_mch_register_sign(sp->sn_icon);
1839 }
1840 # endif
1841 
1842 /*
1843  * List one sign.
1844  */
1845     static void
1846 sign_list_defined(sign_T *sp)
1847 {
1848     char_u	*p;
1849 
1850     smsg("sign %s", sp->sn_name);
1851     if (sp->sn_icon != NULL)
1852     {
1853 	msg_puts(" icon=");
1854 	msg_outtrans(sp->sn_icon);
1855 # ifdef FEAT_SIGN_ICONS
1856 	if (sp->sn_image == NULL)
1857 	    msg_puts(_(" (NOT FOUND)"));
1858 # else
1859 	msg_puts(_(" (not supported)"));
1860 # endif
1861     }
1862     if (sp->sn_text != NULL)
1863     {
1864 	msg_puts(" text=");
1865 	msg_outtrans(sp->sn_text);
1866     }
1867     if (sp->sn_line_hl > 0)
1868     {
1869 	msg_puts(" linehl=");
1870 	p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1871 	if (p == NULL)
1872 	    msg_puts("NONE");
1873 	else
1874 	    msg_puts((char *)p);
1875     }
1876     if (sp->sn_text_hl > 0)
1877     {
1878 	msg_puts(" texthl=");
1879 	p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1880 	if (p == NULL)
1881 	    msg_puts("NONE");
1882 	else
1883 	    msg_puts((char *)p);
1884     }
1885 }
1886 
1887 /*
1888  * Undefine a sign and free its memory.
1889  */
1890     static void
1891 sign_undefine(sign_T *sp, sign_T *sp_prev)
1892 {
1893     vim_free(sp->sn_name);
1894     vim_free(sp->sn_icon);
1895 # ifdef FEAT_SIGN_ICONS
1896     if (sp->sn_image != NULL)
1897     {
1898 	out_flush();
1899 	gui_mch_destroy_sign(sp->sn_image);
1900     }
1901 # endif
1902     vim_free(sp->sn_text);
1903     if (sp_prev == NULL)
1904 	first_sign = sp->sn_next;
1905     else
1906 	sp_prev->sn_next = sp->sn_next;
1907     vim_free(sp);
1908 }
1909 
1910 # if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1911     void *
1912 sign_get_image(
1913     int		typenr)		// the attribute which may have a sign
1914 {
1915     sign_T	*sp;
1916 
1917     FOR_ALL_SIGNS(sp)
1918 	if (sp->sn_typenr == typenr)
1919 	    return sp->sn_image;
1920     return NULL;
1921 }
1922 # endif
1923 
1924 /*
1925  * Undefine/free all signs.
1926  */
1927     void
1928 free_signs(void)
1929 {
1930     while (first_sign != NULL)
1931 	sign_undefine(first_sign, NULL);
1932 }
1933 
1934 static enum
1935 {
1936     EXP_SUBCMD,		// expand :sign sub-commands
1937     EXP_DEFINE,		// expand :sign define {name} args
1938     EXP_PLACE,		// expand :sign place {id} args
1939     EXP_LIST,		// expand :sign place args
1940     EXP_UNPLACE,	// expand :sign unplace"
1941     EXP_SIGN_NAMES,	// expand with name of placed signs
1942     EXP_SIGN_GROUPS	// expand with name of placed sign groups
1943 } expand_what;
1944 
1945 /*
1946  * Return the n'th sign name (used for command line completion)
1947  */
1948     static char_u *
1949 get_nth_sign_name(int idx)
1950 {
1951     int		current_idx;
1952     sign_T	*sp;
1953 
1954     // Complete with name of signs already defined
1955     current_idx = 0;
1956     FOR_ALL_SIGNS(sp)
1957 	if (current_idx++ == idx)
1958 	    return sp->sn_name;
1959     return NULL;
1960 }
1961 
1962 /*
1963  * Return the n'th sign group name (used for command line completion)
1964  */
1965     static char_u *
1966 get_nth_sign_group_name(int idx)
1967 {
1968     int		current_idx;
1969     int		todo;
1970     hashitem_T	*hi;
1971     signgroup_T	*group;
1972 
1973     // Complete with name of sign groups already defined
1974     current_idx = 0;
1975     todo = (int)sg_table.ht_used;
1976     for (hi = sg_table.ht_array; todo > 0; ++hi)
1977     {
1978 	if (!HASHITEM_EMPTY(hi))
1979 	{
1980 	    --todo;
1981 	    if (current_idx++ == idx)
1982 	    {
1983 		group = HI2SG(hi);
1984 		return group->sg_name;
1985 	    }
1986 	}
1987     }
1988     return NULL;
1989 }
1990 
1991 /*
1992  * Function given to ExpandGeneric() to obtain the sign command
1993  * expansion.
1994  */
1995     char_u *
1996 get_sign_name(expand_T *xp UNUSED, int idx)
1997 {
1998     switch (expand_what)
1999     {
2000     case EXP_SUBCMD:
2001 	return (char_u *)cmds[idx];
2002     case EXP_DEFINE:
2003 	{
2004 	    char *define_arg[] =
2005 	    {
2006 		"icon=", "linehl=", "text=", "texthl=", NULL
2007 	    };
2008 	    return (char_u *)define_arg[idx];
2009 	}
2010     case EXP_PLACE:
2011 	{
2012 	    char *place_arg[] =
2013 	    {
2014 		"line=", "name=", "group=", "priority=", "file=",
2015 		"buffer=", NULL
2016 	    };
2017 	    return (char_u *)place_arg[idx];
2018 	}
2019     case EXP_LIST:
2020 	{
2021 	    char *list_arg[] =
2022 	    {
2023 		"group=", "file=", "buffer=", NULL
2024 	    };
2025 	    return (char_u *)list_arg[idx];
2026 	}
2027     case EXP_UNPLACE:
2028 	{
2029 	    char *unplace_arg[] = { "group=", "file=", "buffer=", NULL };
2030 	    return (char_u *)unplace_arg[idx];
2031 	}
2032     case EXP_SIGN_NAMES:
2033 	return get_nth_sign_name(idx);
2034     case EXP_SIGN_GROUPS:
2035 	return get_nth_sign_group_name(idx);
2036     default:
2037 	return NULL;
2038     }
2039 }
2040 
2041 /*
2042  * Handle command line completion for :sign command.
2043  */
2044     void
2045 set_context_in_sign_cmd(expand_T *xp, char_u *arg)
2046 {
2047     char_u	*p;
2048     char_u	*end_subcmd;
2049     char_u	*last;
2050     int		cmd_idx;
2051     char_u	*begin_subcmd_args;
2052 
2053     // Default: expand subcommands.
2054     xp->xp_context = EXPAND_SIGN;
2055     expand_what = EXP_SUBCMD;
2056     xp->xp_pattern = arg;
2057 
2058     end_subcmd = skiptowhite(arg);
2059     if (*end_subcmd == NUL)
2060 	// expand subcmd name
2061 	// :sign {subcmd}<CTRL-D>
2062 	return;
2063 
2064     cmd_idx = sign_cmd_idx(arg, end_subcmd);
2065 
2066     // :sign {subcmd} {subcmd_args}
2067     //		      |
2068     //		      begin_subcmd_args
2069     begin_subcmd_args = skipwhite(end_subcmd);
2070 
2071     // expand last argument of subcmd
2072 
2073     // :sign define {name} {args}...
2074     //		    |
2075     //		    p
2076 
2077     // Loop until reaching last argument.
2078     p = begin_subcmd_args;
2079     do
2080     {
2081 	p = skipwhite(p);
2082 	last = p;
2083 	p = skiptowhite(p);
2084     } while (*p != NUL);
2085 
2086     p = vim_strchr(last, '=');
2087 
2088     // :sign define {name} {args}... {last}=
2089     //				     |	   |
2090     //				  last	   p
2091     if (p == NULL)
2092     {
2093 	// Expand last argument name (before equal sign).
2094 	xp->xp_pattern = last;
2095 	switch (cmd_idx)
2096 	{
2097 	    case SIGNCMD_DEFINE:
2098 		expand_what = EXP_DEFINE;
2099 		break;
2100 	    case SIGNCMD_PLACE:
2101 		// List placed signs
2102 		if (VIM_ISDIGIT(*begin_subcmd_args))
2103 		    //   :sign place {id} {args}...
2104 		    expand_what = EXP_PLACE;
2105 		else
2106 		    //   :sign place {args}...
2107 		    expand_what = EXP_LIST;
2108 		break;
2109 	    case SIGNCMD_LIST:
2110 	    case SIGNCMD_UNDEFINE:
2111 		// :sign list <CTRL-D>
2112 		// :sign undefine <CTRL-D>
2113 		expand_what = EXP_SIGN_NAMES;
2114 		break;
2115 	    case SIGNCMD_JUMP:
2116 	    case SIGNCMD_UNPLACE:
2117 		expand_what = EXP_UNPLACE;
2118 		break;
2119 	    default:
2120 		xp->xp_context = EXPAND_NOTHING;
2121 	}
2122     }
2123     else
2124     {
2125 	// Expand last argument value (after equal sign).
2126 	xp->xp_pattern = p + 1;
2127 	switch (cmd_idx)
2128 	{
2129 	    case SIGNCMD_DEFINE:
2130 		if (STRNCMP(last, "texthl", 6) == 0
2131 			|| STRNCMP(last, "linehl", 6) == 0)
2132 		    xp->xp_context = EXPAND_HIGHLIGHT;
2133 		else if (STRNCMP(last, "icon", 4) == 0)
2134 		    xp->xp_context = EXPAND_FILES;
2135 		else
2136 		    xp->xp_context = EXPAND_NOTHING;
2137 		break;
2138 	    case SIGNCMD_PLACE:
2139 		if (STRNCMP(last, "name", 4) == 0)
2140 		    expand_what = EXP_SIGN_NAMES;
2141 		else if (STRNCMP(last, "group", 5) == 0)
2142 		    expand_what = EXP_SIGN_GROUPS;
2143 		else if (STRNCMP(last, "file", 4) == 0)
2144 		    xp->xp_context = EXPAND_BUFFERS;
2145 		else
2146 		    xp->xp_context = EXPAND_NOTHING;
2147 		break;
2148 	    case SIGNCMD_UNPLACE:
2149 	    case SIGNCMD_JUMP:
2150 		if (STRNCMP(last, "group", 5) == 0)
2151 		    expand_what = EXP_SIGN_GROUPS;
2152 		else if (STRNCMP(last, "file", 4) == 0)
2153 		    xp->xp_context = EXPAND_BUFFERS;
2154 		else
2155 		    xp->xp_context = EXPAND_NOTHING;
2156 		break;
2157 	    default:
2158 		xp->xp_context = EXPAND_NOTHING;
2159 	}
2160     }
2161 }
2162 
2163 /*
2164  * Define a sign using the attributes in 'dict'. Returns 0 on success and -1 on
2165  * failure.
2166  */
2167     static int
2168 sign_define_from_dict(char_u *name_arg, dict_T *dict)
2169 {
2170     char_u	*name = NULL;
2171     char_u	*icon = NULL;
2172     char_u	*linehl = NULL;
2173     char_u	*text = NULL;
2174     char_u	*texthl = NULL;
2175     int		retval = -1;
2176 
2177     if (name_arg == NULL)
2178     {
2179 	if (dict == NULL)
2180 	    return -1;
2181 	name = dict_get_string(dict, (char_u *)"name", TRUE);
2182     }
2183     else
2184 	name = vim_strsave(name_arg);
2185     if (name == NULL || name[0] == NUL)
2186 	goto cleanup;
2187     if (dict != NULL)
2188     {
2189 	icon = dict_get_string(dict, (char_u *)"icon", TRUE);
2190 	linehl = dict_get_string(dict, (char_u *)"linehl", TRUE);
2191 	text = dict_get_string(dict, (char_u *)"text", TRUE);
2192 	texthl = dict_get_string(dict, (char_u *)"texthl", TRUE);
2193     }
2194 
2195     if (sign_define_by_name(name, icon, linehl, text, texthl) == OK)
2196 	retval = 0;
2197 
2198 cleanup:
2199     vim_free(name);
2200     vim_free(icon);
2201     vim_free(linehl);
2202     vim_free(text);
2203     vim_free(texthl);
2204 
2205     return retval;
2206 }
2207 
2208 /*
2209  * Define multiple signs using attributes from list 'l' and store the return
2210  * values in 'retlist'.
2211  */
2212     static void
2213 sign_define_multiple(list_T *l, list_T *retlist)
2214 {
2215     listitem_T	*li;
2216     int		retval;
2217 
2218     FOR_ALL_LIST_ITEMS(l, li)
2219     {
2220 	retval = -1;
2221 	if (li->li_tv.v_type == VAR_DICT)
2222 	    retval = sign_define_from_dict(NULL, li->li_tv.vval.v_dict);
2223 	else
2224 	    emsg(_(e_dictreq));
2225 	list_append_number(retlist, retval);
2226     }
2227 }
2228 
2229 /*
2230  * "sign_define()" function
2231  */
2232     void
2233 f_sign_define(typval_T *argvars, typval_T *rettv)
2234 {
2235     char_u	*name;
2236 
2237     if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2238     {
2239 	// Define multiple signs
2240 	if (rettv_list_alloc(rettv) != OK)
2241 	    return;
2242 
2243 	sign_define_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2244 	return;
2245     }
2246 
2247     // Define a single sign
2248     rettv->vval.v_number = -1;
2249 
2250     name = tv_get_string_chk(&argvars[0]);
2251     if (name == NULL)
2252 	return;
2253 
2254     if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_DICT)
2255     {
2256 	emsg(_(e_dictreq));
2257 	return;
2258     }
2259 
2260     rettv->vval.v_number = sign_define_from_dict(name,
2261 	    argvars[1].v_type == VAR_DICT ? argvars[1].vval.v_dict : NULL);
2262 }
2263 
2264 /*
2265  * "sign_getdefined()" function
2266  */
2267     void
2268 f_sign_getdefined(typval_T *argvars, typval_T *rettv)
2269 {
2270     char_u	*name = NULL;
2271 
2272     if (rettv_list_alloc_id(rettv, aid_sign_getdefined) != OK)
2273 	return;
2274 
2275     if (argvars[0].v_type != VAR_UNKNOWN)
2276 	name = tv_get_string(&argvars[0]);
2277 
2278     sign_getlist(name, rettv->vval.v_list);
2279 }
2280 
2281 /*
2282  * "sign_getplaced()" function
2283  */
2284     void
2285 f_sign_getplaced(typval_T *argvars, typval_T *rettv)
2286 {
2287     buf_T	*buf = NULL;
2288     dict_T	*dict;
2289     dictitem_T	*di;
2290     linenr_T	lnum = 0;
2291     int		sign_id = 0;
2292     char_u	*group = NULL;
2293     int		notanum = FALSE;
2294 
2295     if (rettv_list_alloc_id(rettv, aid_sign_getplaced) != OK)
2296 	return;
2297 
2298     if (argvars[0].v_type != VAR_UNKNOWN)
2299     {
2300 	// get signs placed in the specified buffer
2301 	buf = get_buf_arg(&argvars[0]);
2302 	if (buf == NULL)
2303 	    return;
2304 
2305 	if (argvars[1].v_type != VAR_UNKNOWN)
2306 	{
2307 	    if (argvars[1].v_type != VAR_DICT ||
2308 				((dict = argvars[1].vval.v_dict) == NULL))
2309 	    {
2310 		emsg(_(e_dictreq));
2311 		return;
2312 	    }
2313 	    if ((di = dict_find(dict, (char_u *)"lnum", -1)) != NULL)
2314 	    {
2315 		// get signs placed at this line
2316 		(void)tv_get_number_chk(&di->di_tv, &notanum);
2317 		if (notanum)
2318 		    return;
2319 		lnum = tv_get_lnum(&di->di_tv);
2320 	    }
2321 	    if ((di = dict_find(dict, (char_u *)"id", -1)) != NULL)
2322 	    {
2323 		// get sign placed with this identifier
2324 		sign_id = (int)tv_get_number_chk(&di->di_tv, &notanum);
2325 		if (notanum)
2326 		    return;
2327 	    }
2328 	    if ((di = dict_find(dict, (char_u *)"group", -1)) != NULL)
2329 	    {
2330 		group = tv_get_string_chk(&di->di_tv);
2331 		if (group == NULL)
2332 		    return;
2333 		if (*group == '\0')	// empty string means global group
2334 		    group = NULL;
2335 	    }
2336 	}
2337     }
2338 
2339     sign_get_placed(buf, lnum, sign_id, group, rettv->vval.v_list);
2340 }
2341 
2342 /*
2343  * "sign_jump()" function
2344  */
2345     void
2346 f_sign_jump(typval_T *argvars, typval_T *rettv)
2347 {
2348     int		sign_id;
2349     char_u	*sign_group = NULL;
2350     buf_T	*buf;
2351     int		notanum = FALSE;
2352 
2353     rettv->vval.v_number = -1;
2354 
2355     // Sign identifier
2356     sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
2357     if (notanum)
2358 	return;
2359     if (sign_id <= 0)
2360     {
2361 	emsg(_(e_invarg));
2362 	return;
2363     }
2364 
2365     // Sign group
2366     sign_group = tv_get_string_chk(&argvars[1]);
2367     if (sign_group == NULL)
2368 	return;
2369     if (sign_group[0] == '\0')
2370 	sign_group = NULL;			// global sign group
2371     else
2372     {
2373 	sign_group = vim_strsave(sign_group);
2374 	if (sign_group == NULL)
2375 	    return;
2376     }
2377 
2378     // Buffer to place the sign
2379     buf = get_buf_arg(&argvars[2]);
2380     if (buf == NULL)
2381 	goto cleanup;
2382 
2383     rettv->vval.v_number = sign_jump(sign_id, sign_group, buf);
2384 
2385 cleanup:
2386     vim_free(sign_group);
2387 }
2388 
2389 /*
2390  * Place a new sign using the values specified in dict 'dict'. Returns the sign
2391  * identifier if successfully placed, otherwise returns 0.
2392  */
2393     static int
2394 sign_place_from_dict(
2395 	typval_T	*id_tv,
2396 	typval_T	*group_tv,
2397 	typval_T	*name_tv,
2398 	typval_T	*buf_tv,
2399 	dict_T		*dict)
2400 {
2401     int		sign_id = 0;
2402     char_u	*group = NULL;
2403     char_u	*sign_name = NULL;
2404     buf_T	*buf = NULL;
2405     dictitem_T	*di;
2406     linenr_T	lnum = 0;
2407     int		prio = SIGN_DEF_PRIO;
2408     int		notanum = FALSE;
2409     int		ret_sign_id = -1;
2410 
2411     // sign identifier
2412     if (id_tv == NULL)
2413     {
2414 	di = dict_find(dict, (char_u *)"id", -1);
2415 	if (di != NULL)
2416 	    id_tv = &di->di_tv;
2417     }
2418     if (id_tv == NULL)
2419 	sign_id = 0;
2420     else
2421     {
2422 	sign_id = tv_get_number_chk(id_tv, &notanum);
2423 	if (notanum)
2424 	    return -1;
2425 	if (sign_id < 0)
2426 	{
2427 	    emsg(_(e_invarg));
2428 	    return -1;
2429 	}
2430     }
2431 
2432     // sign group
2433     if (group_tv == NULL)
2434     {
2435 	di = dict_find(dict, (char_u *)"group", -1);
2436 	if (di != NULL)
2437 	    group_tv = &di->di_tv;
2438     }
2439     if (group_tv == NULL)
2440 	group = NULL;				// global group
2441     else
2442     {
2443 	group = tv_get_string_chk(group_tv);
2444 	if (group == NULL)
2445 	    goto cleanup;
2446 	if (group[0] == '\0')			// global sign group
2447 	    group = NULL;
2448 	else
2449 	{
2450 	    group = vim_strsave(group);
2451 	    if (group == NULL)
2452 		return -1;
2453 	}
2454     }
2455 
2456     // sign name
2457     if (name_tv == NULL)
2458     {
2459 	di = dict_find(dict, (char_u *)"name", -1);
2460 	if (di != NULL)
2461 	    name_tv = &di->di_tv;
2462     }
2463     if (name_tv == NULL)
2464 	goto cleanup;
2465     sign_name = tv_get_string_chk(name_tv);
2466     if (sign_name == NULL)
2467 	goto cleanup;
2468 
2469     // buffer to place the sign
2470     if (buf_tv == NULL)
2471     {
2472 	di = dict_find(dict, (char_u *)"buffer", -1);
2473 	if (di != NULL)
2474 	    buf_tv = &di->di_tv;
2475     }
2476     if (buf_tv == NULL)
2477 	goto cleanup;
2478     buf = get_buf_arg(buf_tv);
2479     if (buf == NULL)
2480 	goto cleanup;
2481 
2482     // line number of the sign
2483     di = dict_find(dict, (char_u *)"lnum", -1);
2484     if (di != NULL)
2485     {
2486 	lnum = tv_get_lnum(&di->di_tv);
2487 	if (lnum <= 0)
2488 	{
2489 	    emsg(_(e_invarg));
2490 	    goto cleanup;
2491 	}
2492     }
2493 
2494     // sign priority
2495     di = dict_find(dict, (char_u *)"priority", -1);
2496     if (di != NULL)
2497     {
2498 	prio = (int)tv_get_number_chk(&di->di_tv, &notanum);
2499 	if (notanum)
2500 	    goto cleanup;
2501     }
2502 
2503     if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK)
2504 	ret_sign_id = sign_id;
2505 
2506 cleanup:
2507     vim_free(group);
2508 
2509     return ret_sign_id;
2510 }
2511 
2512 /*
2513  * "sign_place()" function
2514  */
2515     void
2516 f_sign_place(typval_T *argvars, typval_T *rettv)
2517 {
2518     dict_T	*dict = NULL;
2519 
2520     rettv->vval.v_number = -1;
2521 
2522     if (argvars[4].v_type != VAR_UNKNOWN
2523 	    && (argvars[4].v_type != VAR_DICT
2524 		|| ((dict = argvars[4].vval.v_dict) == NULL)))
2525     {
2526 	emsg(_(e_dictreq));
2527 	return;
2528     }
2529 
2530     rettv->vval.v_number = sign_place_from_dict(&argvars[0], &argvars[1],
2531 					&argvars[2], &argvars[3], dict);
2532 }
2533 
2534 /*
2535  * "sign_placelist()" function.  Place multiple signs.
2536  */
2537     void
2538 f_sign_placelist(typval_T *argvars, typval_T *rettv)
2539 {
2540     listitem_T	*li;
2541     int		sign_id;
2542 
2543     if (rettv_list_alloc(rettv) != OK)
2544 	return;
2545 
2546     if (argvars[0].v_type != VAR_LIST)
2547     {
2548 	emsg(_(e_listreq));
2549 	return;
2550     }
2551 
2552     // Process the List of sign attributes
2553     FOR_ALL_LIST_ITEMS(argvars[0].vval.v_list, li)
2554     {
2555 	sign_id = -1;
2556 	if (li->li_tv.v_type == VAR_DICT)
2557 	    sign_id = sign_place_from_dict(NULL, NULL, NULL, NULL,
2558 						li->li_tv.vval.v_dict);
2559 	else
2560 	    emsg(_(e_dictreq));
2561 	list_append_number(rettv->vval.v_list, sign_id);
2562     }
2563 }
2564 
2565 /*
2566  * Undefine multiple signs
2567  */
2568     static void
2569 sign_undefine_multiple(list_T *l, list_T *retlist)
2570 {
2571     char_u	*name;
2572     listitem_T	*li;
2573     int		retval;
2574 
2575     FOR_ALL_LIST_ITEMS(l, li)
2576     {
2577 	retval = -1;
2578 	name = tv_get_string_chk(&li->li_tv);
2579 	if (name != NULL && (sign_undefine_by_name(name, TRUE) == OK))
2580 	    retval = 0;
2581 	list_append_number(retlist, retval);
2582     }
2583 }
2584 
2585 /*
2586  * "sign_undefine()" function
2587  */
2588     void
2589 f_sign_undefine(typval_T *argvars, typval_T *rettv)
2590 {
2591     char_u *name;
2592 
2593     if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2594     {
2595 	// Undefine multiple signs
2596 	if (rettv_list_alloc(rettv) != OK)
2597 	    return;
2598 
2599 	sign_undefine_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2600 	return;
2601     }
2602 
2603     rettv->vval.v_number = -1;
2604 
2605     if (argvars[0].v_type == VAR_UNKNOWN)
2606     {
2607 	// Free all the signs
2608 	free_signs();
2609 	rettv->vval.v_number = 0;
2610     }
2611     else
2612     {
2613 	// Free only the specified sign
2614 	name = tv_get_string_chk(&argvars[0]);
2615 	if (name == NULL)
2616 	    return;
2617 
2618 	if (sign_undefine_by_name(name, TRUE) == OK)
2619 	    rettv->vval.v_number = 0;
2620     }
2621 }
2622 
2623 /*
2624  * Unplace the sign with attributes specified in 'dict'. Returns 0 on success
2625  * and -1 on failure.
2626  */
2627     static int
2628 sign_unplace_from_dict(typval_T *group_tv, dict_T *dict)
2629 {
2630     dictitem_T	*di;
2631     int		sign_id = 0;
2632     buf_T	*buf = NULL;
2633     char_u	*group = NULL;
2634     int		retval = -1;
2635 
2636     // sign group
2637     if (group_tv != NULL)
2638 	group = tv_get_string(group_tv);
2639     else
2640 	group = dict_get_string(dict, (char_u *)"group", FALSE);
2641     if (group != NULL)
2642     {
2643 	if (group[0] == '\0')			// global sign group
2644 	    group = NULL;
2645 	else
2646 	{
2647 	    group = vim_strsave(group);
2648 	    if (group == NULL)
2649 		return -1;
2650 	}
2651     }
2652 
2653     if (dict != NULL)
2654     {
2655 	if ((di = dict_find(dict, (char_u *)"buffer", -1)) != NULL)
2656 	{
2657 	    buf = get_buf_arg(&di->di_tv);
2658 	    if (buf == NULL)
2659 		goto cleanup;
2660 	}
2661 	if (dict_find(dict, (char_u *)"id", -1) != NULL)
2662 	{
2663 	    sign_id = dict_get_number(dict, (char_u *)"id");
2664 	    if (sign_id <= 0)
2665 	    {
2666 		emsg(_(e_invarg));
2667 		goto cleanup;
2668 	    }
2669 	}
2670     }
2671 
2672     if (buf == NULL)
2673     {
2674 	// Delete the sign in all the buffers
2675 	retval = 0;
2676 	FOR_ALL_BUFFERS(buf)
2677 	    if (sign_unplace(sign_id, group, buf, 0) != OK)
2678 		retval = -1;
2679     }
2680     else if (sign_unplace(sign_id, group, buf, 0) == OK)
2681 	retval = 0;
2682 
2683 cleanup:
2684     vim_free(group);
2685 
2686     return retval;
2687 }
2688 
2689     sign_entry_T *
2690 get_first_valid_sign(win_T *wp)
2691 {
2692     sign_entry_T *sign = wp->w_buffer->b_signlist;
2693 
2694 # ifdef FEAT_PROP_POPUP
2695     while (sign != NULL && !sign_group_for_window(sign, wp))
2696 	sign = sign->se_next;
2697 # endif
2698     return sign;
2699 }
2700 
2701 /*
2702  * Return TRUE when window "wp" has a column to draw signs in.
2703  */
2704      int
2705 signcolumn_on(win_T *wp)
2706 {
2707     // If 'signcolumn' is set to 'number', signs are displayed in the 'number'
2708     // column (if present). Otherwise signs are to be displayed in the sign
2709     // column.
2710     if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u')
2711 	return get_first_valid_sign(wp) != NULL && !wp->w_p_nu && !wp->w_p_rnu;
2712 
2713     if (*wp->w_p_scl == 'n')
2714 	return FALSE;
2715     if (*wp->w_p_scl == 'y')
2716 	return TRUE;
2717     return (get_first_valid_sign(wp) != NULL
2718 # ifdef FEAT_NETBEANS_INTG
2719 			|| wp->w_buffer->b_has_sign_column
2720 # endif
2721 		    );
2722 }
2723 
2724 /*
2725  * "sign_unplace()" function
2726  */
2727     void
2728 f_sign_unplace(typval_T *argvars, typval_T *rettv)
2729 {
2730     dict_T	*dict = NULL;
2731 
2732     rettv->vval.v_number = -1;
2733 
2734     if (argvars[0].v_type != VAR_STRING)
2735     {
2736 	emsg(_(e_invarg));
2737 	return;
2738     }
2739 
2740     if (argvars[1].v_type != VAR_UNKNOWN)
2741     {
2742 	if (argvars[1].v_type != VAR_DICT)
2743 	{
2744 	    emsg(_(e_dictreq));
2745 	    return;
2746 	}
2747 	dict = argvars[1].vval.v_dict;
2748     }
2749 
2750     rettv->vval.v_number = sign_unplace_from_dict(&argvars[0], dict);
2751 }
2752 
2753 /*
2754  * "sign_unplacelist()" function
2755  */
2756     void
2757 f_sign_unplacelist(typval_T *argvars, typval_T *rettv)
2758 {
2759     listitem_T	*li;
2760     int		retval;
2761 
2762     if (rettv_list_alloc(rettv) != OK)
2763 	return;
2764 
2765     if (argvars[0].v_type != VAR_LIST)
2766     {
2767 	emsg(_(e_listreq));
2768 	return;
2769     }
2770 
2771     FOR_ALL_LIST_ITEMS(argvars[0].vval.v_list, li)
2772     {
2773 	retval = -1;
2774 	if (li->li_tv.v_type == VAR_DICT)
2775 	    retval = sign_unplace_from_dict(NULL, li->li_tv.vval.v_dict);
2776 	else
2777 	    emsg(_(e_dictreq));
2778 	list_append_number(rettv->vval.v_list, retval);
2779     }
2780 }
2781 
2782 #endif // FEAT_SIGNS
2783