comparison src/sign.c @ 15330:a6330a49e036 v8.1.0673

patch 8.1.0673: functionality for signs is spread out over several files commit https://github.com/vim/vim/commit/bbea47075cc4e7826e9f8c203e4272ba023ed7b0 Author: Bram Moolenaar <Bram@vim.org> Date: Tue Jan 1 13:20:31 2019 +0100 patch 8.1.0673: functionality for signs is spread out over several files Problem: Functionality for signs is spread out over several files. Solution: Move most of the sign functionality into sign.c. (Yegappan Lakshmanan, closes #3751)
author Bram Moolenaar <Bram@vim.org>
date Tue, 01 Jan 2019 13:30:09 +0100
parents
children 0d76dd701e49
comparison
equal deleted inserted replaced
15329:9a739a3d145e 15330:a6330a49e036
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 static hashtab_T sg_table; // sign group (signgroup_T) hashtable
61 static int next_sign_id = 1; // next sign id in the global group
62
63 /*
64 * Initialize data needed for managing signs
65 */
66 void
67 init_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 *
77 sign_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
88 group = (signgroup_T *)alloc(
89 (unsigned)(sizeof(signgroup_T) + STRLEN(groupname)));
90 if (group == NULL)
91 return NULL;
92 STRCPY(group->sg_name, groupname);
93 group->refcount = 1;
94 group->next_sign_id = 1;
95 hash_add_item(&sg_table, hi, group->sg_name, hash);
96 }
97 else
98 {
99 // existing group
100 group = HI2SG(hi);
101 group->refcount++;
102 }
103
104 return group;
105 }
106
107 /*
108 * A sign in group 'groupname' is removed. If all the signs in this group are
109 * removed, then remove the group.
110 */
111 static void
112 sign_group_unref(char_u *groupname)
113 {
114 hashitem_T *hi;
115 signgroup_T *group;
116
117 hi = hash_find(&sg_table, groupname);
118 if (!HASHITEM_EMPTY(hi))
119 {
120 group = HI2SG(hi);
121 group->refcount--;
122 if (group->refcount == 0)
123 {
124 // All the signs in this group are removed
125 hash_remove(&sg_table, hi);
126 vim_free(group);
127 }
128 }
129 }
130
131 /*
132 * Returns TRUE if 'sign' is in 'group'.
133 * A sign can either be in the global group (sign->group == NULL)
134 * or in a named group. If 'group' is '*', then the sign is part of the group.
135 */
136 static int
137 sign_in_group(signlist_T *sign, char_u *group)
138 {
139 return ((group != NULL && STRCMP(group, "*") == 0)
140 || (group == NULL && sign->group == NULL)
141 || (group != NULL && sign->group != NULL
142 && STRCMP(group, sign->group->sg_name) == 0));
143 }
144
145 /*
146 * Get the next free sign identifier in the specified group
147 */
148 static int
149 sign_group_get_next_signid(buf_T *buf, char_u *groupname)
150 {
151 int id = 1;
152 signgroup_T *group = NULL;
153 signlist_T *sign;
154 hashitem_T *hi;
155 int found = FALSE;
156
157 if (groupname != NULL)
158 {
159 hi = hash_find(&sg_table, groupname);
160 if (HASHITEM_EMPTY(hi))
161 return id;
162 group = HI2SG(hi);
163 }
164
165 // Search for the next usuable sign identifier
166 while (!found)
167 {
168 if (group == NULL)
169 id = next_sign_id++; // global group
170 else
171 id = group->next_sign_id++;
172
173 // Check whether this sign is already placed in the buffer
174 found = TRUE;
175 FOR_ALL_SIGNS_IN_BUF(buf, sign)
176 {
177 if (id == sign->id && sign_in_group(sign, groupname))
178 {
179 found = FALSE; // sign identifier is in use
180 break;
181 }
182 }
183 }
184
185 return id;
186 }
187
188 /*
189 * Insert a new sign into the signlist for buffer 'buf' between the 'prev' and
190 * 'next' signs.
191 */
192 static void
193 insert_sign(
194 buf_T *buf, // buffer to store sign in
195 signlist_T *prev, // previous sign entry
196 signlist_T *next, // next sign entry
197 int id, // sign ID
198 char_u *group, // sign group; NULL for global group
199 int prio, // sign priority
200 linenr_T lnum, // line number which gets the mark
201 int typenr) // typenr of sign we are adding
202 {
203 signlist_T *newsign;
204
205 newsign = (signlist_T *)lalloc_id((long_u)sizeof(signlist_T), FALSE,
206 aid_insert_sign);
207 if (newsign != NULL)
208 {
209 newsign->id = id;
210 newsign->lnum = lnum;
211 newsign->typenr = typenr;
212 if (group != NULL)
213 {
214 newsign->group = sign_group_ref(group);
215 if (newsign->group == NULL)
216 {
217 vim_free(newsign);
218 return;
219 }
220 }
221 else
222 newsign->group = NULL;
223 newsign->priority = prio;
224 newsign->next = next;
225 newsign->prev = prev;
226 if (next != NULL)
227 next->prev = newsign;
228
229 if (prev == NULL)
230 {
231 // When adding first sign need to redraw the windows to create the
232 // column for signs.
233 if (buf->b_signlist == NULL)
234 {
235 redraw_buf_later(buf, NOT_VALID);
236 changed_cline_bef_curs();
237 }
238
239 // first sign in signlist
240 buf->b_signlist = newsign;
241 #ifdef FEAT_NETBEANS_INTG
242 if (netbeans_active())
243 buf->b_has_sign_column = TRUE;
244 #endif
245 }
246 else
247 prev->next = newsign;
248 }
249 }
250
251 /*
252 * Insert a new sign sorted by line number and sign priority.
253 */
254 static void
255 insert_sign_by_lnum_prio(
256 buf_T *buf, // buffer to store sign in
257 signlist_T *prev, // previous sign entry
258 int id, // sign ID
259 char_u *group, // sign group; NULL for global group
260 int prio, // sign priority
261 linenr_T lnum, // line number which gets the mark
262 int typenr) // typenr of sign we are adding
263 {
264 signlist_T *sign;
265
266 // keep signs sorted by lnum and by priority: insert new sign at
267 // the proper position in the list for this lnum.
268 while (prev != NULL && prev->lnum == lnum && prev->priority <= prio)
269 prev = prev->prev;
270 if (prev == NULL)
271 sign = buf->b_signlist;
272 else
273 sign = prev->next;
274
275 insert_sign(buf, prev, sign, id, group, prio, lnum, typenr);
276 }
277
278 /*
279 * Get the name of a sign by its typenr.
280 */
281 static char_u *
282 sign_typenr2name(int typenr)
283 {
284 sign_T *sp;
285
286 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
287 if (sp->sn_typenr == typenr)
288 return sp->sn_name;
289 return (char_u *)_("[Deleted]");
290 }
291
292 /*
293 * Return information about a sign in a Dict
294 */
295 static dict_T *
296 sign_get_info(signlist_T *sign)
297 {
298 dict_T *d;
299
300 if ((d = dict_alloc_id(aid_sign_getinfo)) == NULL)
301 return NULL;
302 dict_add_number(d, "id", sign->id);
303 dict_add_string(d, "group", (sign->group == NULL) ?
304 (char_u *)"" : sign->group->sg_name);
305 dict_add_number(d, "lnum", sign->lnum);
306 dict_add_string(d, "name", sign_typenr2name(sign->typenr));
307 dict_add_number(d, "priority", sign->priority);
308
309 return d;
310 }
311
312 /*
313 * Add the sign into the signlist. Find the right spot to do it though.
314 */
315 static void
316 buf_addsign(
317 buf_T *buf, // buffer to store sign in
318 int id, // sign ID
319 char_u *groupname, // sign group
320 int prio, // sign priority
321 linenr_T lnum, // line number which gets the mark
322 int typenr) // typenr of sign we are adding
323 {
324 signlist_T *sign; // a sign in the signlist
325 signlist_T *prev; // the previous sign
326
327 prev = NULL;
328 FOR_ALL_SIGNS_IN_BUF(buf, sign)
329 {
330 if (lnum == sign->lnum && id == sign->id &&
331 sign_in_group(sign, groupname))
332 {
333 // Update an existing sign
334 sign->typenr = typenr;
335 return;
336 }
337 else if (lnum < sign->lnum)
338 {
339 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio,
340 lnum, typenr);
341 return;
342 }
343 prev = sign;
344 }
345
346 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr);
347 return;
348 }
349
350 /*
351 * For an existing, placed sign "markId" change the type to "typenr".
352 * Returns the line number of the sign, or zero if the sign is not found.
353 */
354 static linenr_T
355 buf_change_sign_type(
356 buf_T *buf, // buffer to store sign in
357 int markId, // sign ID
358 char_u *group, // sign group
359 int typenr) // typenr of sign we are adding
360 {
361 signlist_T *sign; // a sign in the signlist
362
363 FOR_ALL_SIGNS_IN_BUF(buf, sign)
364 {
365 if (sign->id == markId && sign_in_group(sign, group))
366 {
367 sign->typenr = typenr;
368 return sign->lnum;
369 }
370 }
371
372 return (linenr_T)0;
373 }
374
375 /*
376 * Return the type number of the sign at line number 'lnum' in buffer 'buf'
377 * which has the attribute specifed by 'type'. Returns 0 if a sign is not found
378 * at the line number or it doesn't have the specified attribute.
379 */
380 int
381 buf_getsigntype(
382 buf_T *buf,
383 linenr_T lnum,
384 int type) /* SIGN_ICON, SIGN_TEXT, SIGN_ANY, SIGN_LINEHL */
385 {
386 signlist_T *sign; /* a sign in a b_signlist */
387
388 FOR_ALL_SIGNS_IN_BUF(buf, sign)
389 if (sign->lnum == lnum
390 && (type == SIGN_ANY
391 # ifdef FEAT_SIGN_ICONS
392 || (type == SIGN_ICON
393 && sign_get_image(sign->typenr) != NULL)
394 # endif
395 || (type == SIGN_TEXT
396 && sign_get_text(sign->typenr) != NULL)
397 || (type == SIGN_LINEHL
398 && sign_get_attr(sign->typenr, TRUE) != 0)))
399 return sign->typenr;
400 return 0;
401 }
402
403 /*
404 * Delete sign 'id' in group 'group' from buffer 'buf'.
405 * If 'id' is zero, then delete all the signs in group 'group'. Otherwise
406 * delete only the specified sign.
407 * If 'group' is '*', then delete the sign in all the groups. If 'group' is
408 * NULL, then delete the sign in the global group. Otherwise delete the sign in
409 * the specified group.
410 * Returns the line number of the deleted sign. If multiple signs are deleted,
411 * then returns the line number of the last sign deleted.
412 */
413 linenr_T
414 buf_delsign(
415 buf_T *buf, // buffer sign is stored in
416 linenr_T atlnum, // sign at this line, 0 - at any line
417 int id, // sign id
418 char_u *group) // sign group
419 {
420 signlist_T **lastp; // pointer to pointer to current sign
421 signlist_T *sign; // a sign in a b_signlist
422 signlist_T *next; // the next sign in a b_signlist
423 linenr_T lnum; // line number whose sign was deleted
424
425 lastp = &buf->b_signlist;
426 lnum = 0;
427 for (sign = buf->b_signlist; sign != NULL; sign = next)
428 {
429 next = sign->next;
430 if ((id == 0 || sign->id == id) &&
431 (atlnum == 0 || sign->lnum == atlnum) &&
432 sign_in_group(sign, group))
433
434 {
435 *lastp = next;
436 if (next != NULL)
437 next->prev = sign->prev;
438 lnum = sign->lnum;
439 if (sign->group != NULL)
440 sign_group_unref(sign->group->sg_name);
441 vim_free(sign);
442 update_debug_sign(buf, lnum);
443 // Check whether only one sign needs to be deleted
444 // If deleting a sign with a specific identifer in a particular
445 // group or deleting any sign at a particular line number, delete
446 // only one sign.
447 if (group == NULL
448 || (*group != '*' && id != 0)
449 || (*group == '*' && atlnum != 0))
450 break;
451 }
452 else
453 lastp = &sign->next;
454 }
455
456 // When deleted the last sign need to redraw the windows to remove the
457 // sign column.
458 if (buf->b_signlist == NULL)
459 {
460 redraw_buf_later(buf, NOT_VALID);
461 changed_cline_bef_curs();
462 }
463
464 return lnum;
465 }
466
467
468 /*
469 * Find the line number of the sign with the requested id in group 'group'. If
470 * the sign does not exist, return 0 as the line number. This will still let
471 * the correct file get loaded.
472 */
473 int
474 buf_findsign(
475 buf_T *buf, // buffer to store sign in
476 int id, // sign ID
477 char_u *group) // sign group
478 {
479 signlist_T *sign; // a sign in the signlist
480
481 FOR_ALL_SIGNS_IN_BUF(buf, sign)
482 if (sign->id == id && sign_in_group(sign, group))
483 return sign->lnum;
484
485 return 0;
486 }
487
488 /*
489 * Return the sign at line 'lnum' in buffer 'buf'. Returns NULL if a sign is
490 * not found at the line. If 'groupname' is NULL, searches in the global group.
491 */
492 static signlist_T *
493 buf_getsign_at_line(
494 buf_T *buf, // buffer whose sign we are searching for
495 linenr_T lnum, // line number of sign
496 char_u *groupname) // sign group name
497 {
498 signlist_T *sign; // a sign in the signlist
499
500 FOR_ALL_SIGNS_IN_BUF(buf, sign)
501 if (sign->lnum == lnum && sign_in_group(sign, groupname))
502 return sign;
503
504 return NULL;
505 }
506
507 /*
508 * Return the identifier of the sign at line number 'lnum' in buffer 'buf'.
509 */
510 int
511 buf_findsign_id(
512 buf_T *buf, // buffer whose sign we are searching for
513 linenr_T lnum, // line number of sign
514 char_u *groupname) // sign group name
515 {
516 signlist_T *sign; // a sign in the signlist
517
518 sign = buf_getsign_at_line(buf, lnum, groupname);
519 if (sign != NULL)
520 return sign->id;
521
522 return 0;
523 }
524
525 # if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
526 /*
527 * See if a given type of sign exists on a specific line.
528 */
529 int
530 buf_findsigntype_id(
531 buf_T *buf, /* buffer whose sign we are searching for */
532 linenr_T lnum, /* line number of sign */
533 int typenr) /* sign type number */
534 {
535 signlist_T *sign; /* a sign in the signlist */
536
537 FOR_ALL_SIGNS_IN_BUF(buf, sign)
538 if (sign->lnum == lnum && sign->typenr == typenr)
539 return sign->id;
540
541 return 0;
542 }
543
544
545 # if defined(FEAT_SIGN_ICONS) || defined(PROTO)
546 /*
547 * Return the number of icons on the given line.
548 */
549 int
550 buf_signcount(buf_T *buf, linenr_T lnum)
551 {
552 signlist_T *sign; // a sign in the signlist
553 int count = 0;
554
555 FOR_ALL_SIGNS_IN_BUF(buf, sign)
556 if (sign->lnum == lnum)
557 if (sign_get_image(sign->typenr) != NULL)
558 count++;
559
560 return count;
561 }
562 # endif /* FEAT_SIGN_ICONS */
563 # endif /* FEAT_NETBEANS_INTG */
564
565 /*
566 * Delete signs in group 'group' in buffer "buf". If 'group' is '*', then
567 * delete all the signs.
568 */
569 void
570 buf_delete_signs(buf_T *buf, char_u *group)
571 {
572 signlist_T *sign;
573 signlist_T **lastp; // pointer to pointer to current sign
574 signlist_T *next;
575
576 // When deleting the last sign need to redraw the windows to remove the
577 // sign column. Not when curwin is NULL (this means we're exiting).
578 if (buf->b_signlist != NULL && curwin != NULL)
579 {
580 redraw_buf_later(buf, NOT_VALID);
581 changed_cline_bef_curs();
582 }
583
584 lastp = &buf->b_signlist;
585 for (sign = buf->b_signlist; sign != NULL; sign = next)
586 {
587 next = sign->next;
588 if (sign_in_group(sign, group))
589 {
590 *lastp = next;
591 if (next != NULL)
592 next->prev = sign->prev;
593 if (sign->group != NULL)
594 sign_group_unref(sign->group->sg_name);
595 vim_free(sign);
596 }
597 else
598 lastp = &sign->next;
599 }
600 }
601
602 /*
603 * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers.
604 */
605 static void
606 sign_list_placed(buf_T *rbuf, char_u *sign_group)
607 {
608 buf_T *buf;
609 signlist_T *sign;
610 char lbuf[BUFSIZ];
611 char group[BUFSIZ];
612
613 MSG_PUTS_TITLE(_("\n--- Signs ---"));
614 msg_putchar('\n');
615 if (rbuf == NULL)
616 buf = firstbuf;
617 else
618 buf = rbuf;
619 while (buf != NULL && !got_int)
620 {
621 if (buf->b_signlist != NULL)
622 {
623 vim_snprintf(lbuf, BUFSIZ, _("Signs for %s:"), buf->b_fname);
624 MSG_PUTS_ATTR(lbuf, HL_ATTR(HLF_D));
625 msg_putchar('\n');
626 }
627 FOR_ALL_SIGNS_IN_BUF(buf, sign)
628 {
629 if (got_int)
630 break;
631 if (!sign_in_group(sign, sign_group))
632 continue;
633 if (sign->group != NULL)
634 vim_snprintf(group, BUFSIZ, " group=%s",
635 sign->group->sg_name);
636 else
637 group[0] = '\0';
638 vim_snprintf(lbuf, BUFSIZ, _(" line=%ld id=%d%s name=%s "
639 "priority=%d"),
640 (long)sign->lnum, sign->id, group,
641 sign_typenr2name(sign->typenr), sign->priority);
642 MSG_PUTS(lbuf);
643 msg_putchar('\n');
644 }
645 if (rbuf != NULL)
646 break;
647 buf = buf->b_next;
648 }
649 }
650
651 /*
652 * Adjust a placed sign for inserted/deleted lines.
653 */
654 void
655 sign_mark_adjust(
656 linenr_T line1,
657 linenr_T line2,
658 long amount,
659 long amount_after)
660 {
661 signlist_T *sign; /* a sign in a b_signlist */
662
663 FOR_ALL_SIGNS_IN_BUF(curbuf, sign)
664 {
665 if (sign->lnum >= line1 && sign->lnum <= line2)
666 {
667 if (amount == MAXLNUM)
668 sign->lnum = line1;
669 else
670 sign->lnum += amount;
671 }
672 else if (sign->lnum > line2)
673 sign->lnum += amount_after;
674 }
675 }
676
677 /*
678 * Find index of a ":sign" subcmd from its name.
679 * "*end_cmd" must be writable.
680 */
681 static int
682 sign_cmd_idx(
683 char_u *begin_cmd, /* begin of sign subcmd */
684 char_u *end_cmd) /* just after sign subcmd */
685 {
686 int idx;
687 char save = *end_cmd;
688
689 *end_cmd = NUL;
690 for (idx = 0; ; ++idx)
691 if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0)
692 break;
693 *end_cmd = save;
694 return idx;
695 }
696
697 /*
698 * Find a sign by name. Also returns pointer to the previous sign.
699 */
700 static sign_T *
701 sign_find(char_u *name, sign_T **sp_prev)
702 {
703 sign_T *sp;
704
705 if (sp_prev != NULL)
706 *sp_prev = NULL;
707 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
708 {
709 if (STRCMP(sp->sn_name, name) == 0)
710 break;
711 if (sp_prev != NULL)
712 *sp_prev = sp;
713 }
714
715 return sp;
716 }
717
718 /*
719 * Define a new sign or update an existing sign
720 */
721 int
722 sign_define_by_name(
723 char_u *name,
724 char_u *icon,
725 char_u *linehl,
726 char_u *text,
727 char_u *texthl)
728 {
729 sign_T *sp_prev;
730 sign_T *sp;
731
732 sp = sign_find(name, &sp_prev);
733 if (sp == NULL)
734 {
735 sign_T *lp;
736 int start = next_sign_typenr;
737
738 // Allocate a new sign.
739 sp = (sign_T *)alloc_clear_id((unsigned)sizeof(sign_T),
740 aid_sign_define_by_name);
741 if (sp == NULL)
742 return FAIL;
743
744 // Check that next_sign_typenr is not already being used.
745 // This only happens after wrapping around. Hopefully
746 // another one got deleted and we can use its number.
747 for (lp = first_sign; lp != NULL; )
748 {
749 if (lp->sn_typenr == next_sign_typenr)
750 {
751 ++next_sign_typenr;
752 if (next_sign_typenr == MAX_TYPENR)
753 next_sign_typenr = 1;
754 if (next_sign_typenr == start)
755 {
756 vim_free(sp);
757 EMSG(_("E612: Too many signs defined"));
758 return FAIL;
759 }
760 lp = first_sign; // start all over
761 continue;
762 }
763 lp = lp->sn_next;
764 }
765
766 sp->sn_typenr = next_sign_typenr;
767 if (++next_sign_typenr == MAX_TYPENR)
768 next_sign_typenr = 1; // wrap around
769
770 sp->sn_name = vim_strsave(name);
771 if (sp->sn_name == NULL) // out of memory
772 {
773 vim_free(sp);
774 return FAIL;
775 }
776
777 // add the new sign to the list of signs
778 if (sp_prev == NULL)
779 first_sign = sp;
780 else
781 sp_prev->sn_next = sp;
782 }
783
784 // set values for a defined sign.
785 if (icon != NULL)
786 {
787 vim_free(sp->sn_icon);
788 sp->sn_icon = vim_strsave(icon);
789 backslash_halve(sp->sn_icon);
790 # ifdef FEAT_SIGN_ICONS
791 if (gui.in_use)
792 {
793 out_flush();
794 if (sp->sn_image != NULL)
795 gui_mch_destroy_sign(sp->sn_image);
796 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
797 }
798 # endif
799 }
800
801 if (text != NULL)
802 {
803 char_u *s;
804 char_u *endp;
805 int cells;
806 int len;
807
808 endp = text + (int)STRLEN(text);
809 for (s = text; s + 1 < endp; ++s)
810 if (*s == '\\')
811 {
812 // Remove a backslash, so that it is possible
813 // to use a space.
814 STRMOVE(s, s + 1);
815 --endp;
816 }
817 # ifdef FEAT_MBYTE
818 // Count cells and check for non-printable chars
819 if (has_mbyte)
820 {
821 cells = 0;
822 for (s = text; s < endp; s += (*mb_ptr2len)(s))
823 {
824 if (!vim_isprintc((*mb_ptr2char)(s)))
825 break;
826 cells += (*mb_ptr2cells)(s);
827 }
828 }
829 else
830 # endif
831 {
832 for (s = text; s < endp; ++s)
833 if (!vim_isprintc(*s))
834 break;
835 cells = (int)(s - text);
836 }
837 // Currently must be one or two display cells
838 if (s != endp || cells < 1 || cells > 2)
839 {
840 EMSG2(_("E239: Invalid sign text: %s"), text);
841 return FAIL;
842 }
843
844 vim_free(sp->sn_text);
845 // Allocate one byte more if we need to pad up
846 // with a space.
847 len = (int)(endp - text + ((cells == 1) ? 1 : 0));
848 sp->sn_text = vim_strnsave(text, len);
849
850 if (sp->sn_text != NULL && cells == 1)
851 STRCPY(sp->sn_text + len - 1, " ");
852 }
853
854 if (linehl != NULL)
855 sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl));
856
857 if (texthl != NULL)
858 sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl));
859
860 return OK;
861 }
862
863 /*
864 * Free the sign specified by 'name'.
865 */
866 int
867 sign_undefine_by_name(char_u *name)
868 {
869 sign_T *sp_prev;
870 sign_T *sp;
871
872 sp = sign_find(name, &sp_prev);
873 if (sp == NULL)
874 {
875 EMSG2(_("E155: Unknown sign: %s"), name);
876 return FAIL;
877 }
878 sign_undefine(sp, sp_prev);
879
880 return OK;
881 }
882
883 /*
884 * List the signs matching 'name'
885 */
886 static void
887 sign_list_by_name(char_u *name)
888 {
889 sign_T *sp;
890
891 sp = sign_find(name, NULL);
892 if (sp != NULL)
893 sign_list_defined(sp);
894 else
895 EMSG2(_("E155: Unknown sign: %s"), name);
896 }
897
898 /*
899 * Place a sign at the specifed file location or update a sign.
900 */
901 int
902 sign_place(
903 int *sign_id,
904 char_u *sign_group,
905 char_u *sign_name,
906 buf_T *buf,
907 linenr_T lnum,
908 int prio)
909 {
910 sign_T *sp;
911
912 // Check for reserved character '*' in group name
913 if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0'))
914 return FAIL;
915
916 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
917 if (STRCMP(sp->sn_name, sign_name) == 0)
918 break;
919 if (sp == NULL)
920 {
921 EMSG2(_("E155: Unknown sign: %s"), sign_name);
922 return FAIL;
923 }
924 if (*sign_id == 0)
925 *sign_id = sign_group_get_next_signid(buf, sign_group);
926
927 if (lnum > 0)
928 // ":sign place {id} line={lnum} name={name} file={fname}":
929 // place a sign
930 buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr);
931 else
932 // ":sign place {id} file={fname}": change sign type
933 lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr);
934 if (lnum > 0)
935 update_debug_sign(buf, lnum);
936 else
937 {
938 EMSG2(_("E885: Not possible to change sign %s"), sign_name);
939 return FAIL;
940 }
941
942 return OK;
943 }
944
945 /*
946 * Unplace the specified sign
947 */
948 int
949 sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum)
950 {
951 if (buf->b_signlist == NULL) // No signs in the buffer
952 return OK;
953
954 if (sign_id == 0)
955 {
956 // Delete all the signs in the specified buffer
957 redraw_buf_later(buf, NOT_VALID);
958 buf_delete_signs(buf, sign_group);
959 }
960 else
961 {
962 linenr_T lnum;
963
964 // Delete only the specified signs
965 lnum = buf_delsign(buf, atlnum, sign_id, sign_group);
966 if (lnum == 0)
967 return FAIL;
968 }
969
970 return OK;
971 }
972
973 /*
974 * Unplace the sign at the current cursor line.
975 */
976 static void
977 sign_unplace_at_cursor(char_u *groupname)
978 {
979 int id = -1;
980
981 id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname);
982 if (id > 0)
983 sign_unplace(id, groupname, curwin->w_buffer, curwin->w_cursor.lnum);
984 else
985 EMSG(_("E159: Missing sign number"));
986 }
987
988 /*
989 * sign define command
990 * ":sign define {name} ..."
991 */
992 static void
993 sign_define_cmd(char_u *sign_name, char_u *cmdline)
994 {
995 char_u *arg;
996 char_u *p = cmdline;
997 char_u *icon = NULL;
998 char_u *text = NULL;
999 char_u *linehl = NULL;
1000 char_u *texthl = NULL;
1001 int failed = FALSE;
1002
1003 // set values for a defined sign.
1004 for (;;)
1005 {
1006 arg = skipwhite(p);
1007 if (*arg == NUL)
1008 break;
1009 p = skiptowhite_esc(arg);
1010 if (STRNCMP(arg, "icon=", 5) == 0)
1011 {
1012 arg += 5;
1013 icon = vim_strnsave(arg, (int)(p - arg));
1014 }
1015 else if (STRNCMP(arg, "text=", 5) == 0)
1016 {
1017 arg += 5;
1018 text = vim_strnsave(arg, (int)(p - arg));
1019 }
1020 else if (STRNCMP(arg, "linehl=", 7) == 0)
1021 {
1022 arg += 7;
1023 linehl = vim_strnsave(arg, (int)(p - arg));
1024 }
1025 else if (STRNCMP(arg, "texthl=", 7) == 0)
1026 {
1027 arg += 7;
1028 texthl = vim_strnsave(arg, (int)(p - arg));
1029 }
1030 else
1031 {
1032 EMSG2(_(e_invarg2), arg);
1033 failed = TRUE;
1034 break;
1035 }
1036 }
1037
1038 if (!failed)
1039 sign_define_by_name(sign_name, icon, linehl, text, texthl);
1040
1041 vim_free(icon);
1042 vim_free(text);
1043 vim_free(linehl);
1044 vim_free(texthl);
1045 }
1046
1047 /*
1048 * :sign place command
1049 */
1050 static void
1051 sign_place_cmd(
1052 buf_T *buf,
1053 linenr_T lnum,
1054 char_u *sign_name,
1055 int id,
1056 char_u *group,
1057 int prio)
1058 {
1059 if (id <= 0)
1060 {
1061 // List signs placed in a file/buffer
1062 // :sign place file={fname}
1063 // :sign place group={group} file={fname}
1064 // :sign place group=* file={fname}
1065 // :sign place buffer={nr}
1066 // :sign place group={group} buffer={nr}
1067 // :sign place group=* buffer={nr}
1068 // :sign place
1069 // :sign place group={group}
1070 // :sign place group=*
1071 if (lnum >= 0 || sign_name != NULL ||
1072 (group != NULL && *group == '\0'))
1073 EMSG(_(e_invarg));
1074 else
1075 sign_list_placed(buf, group);
1076 }
1077 else
1078 {
1079 // Place a new sign
1080 if (sign_name == NULL || buf == NULL ||
1081 (group != NULL && *group == '\0'))
1082 {
1083 EMSG(_(e_invarg));
1084 return;
1085 }
1086
1087 sign_place(&id, group, sign_name, buf, lnum, prio);
1088 }
1089 }
1090
1091 /*
1092 * :sign unplace command
1093 */
1094 static void
1095 sign_unplace_cmd(
1096 buf_T *buf,
1097 linenr_T lnum,
1098 char_u *sign_name,
1099 int id,
1100 char_u *group)
1101 {
1102 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0'))
1103 {
1104 EMSG(_(e_invarg));
1105 return;
1106 }
1107
1108 if (id == -2)
1109 {
1110 if (buf != NULL)
1111 // :sign unplace * file={fname}
1112 // :sign unplace * group={group} file={fname}
1113 // :sign unplace * group=* file={fname}
1114 // :sign unplace * buffer={nr}
1115 // :sign unplace * group={group} buffer={nr}
1116 // :sign unplace * group=* buffer={nr}
1117 sign_unplace(0, group, buf, 0);
1118 else
1119 // :sign unplace *
1120 // :sign unplace * group={group}
1121 // :sign unplace * group=*
1122 FOR_ALL_BUFFERS(buf)
1123 if (buf->b_signlist != NULL)
1124 buf_delete_signs(buf, group);
1125 }
1126 else
1127 {
1128 if (buf != NULL)
1129 // :sign unplace {id} file={fname}
1130 // :sign unplace {id} group={group} file={fname}
1131 // :sign unplace {id} group=* file={fname}
1132 // :sign unplace {id} buffer={nr}
1133 // :sign unplace {id} group={group} buffer={nr}
1134 // :sign unplace {id} group=* buffer={nr}
1135 sign_unplace(id, group, buf, 0);
1136 else
1137 {
1138 if (id == -1)
1139 {
1140 // :sign unplace group={group}
1141 // :sign unplace group=*
1142 sign_unplace_at_cursor(group);
1143 }
1144 else
1145 {
1146 // :sign unplace {id}
1147 // :sign unplace {id} group={group}
1148 // :sign unplace {id} group=*
1149 FOR_ALL_BUFFERS(buf)
1150 sign_unplace(id, group, buf, 0);
1151 }
1152 }
1153 }
1154 }
1155
1156 /*
1157 * Jump to a placed sign
1158 * :sign jump {id} file={fname}
1159 * :sign jump {id} buffer={nr}
1160 * :sign jump {id} group={group} file={fname}
1161 * :sign jump {id} group={group} buffer={nr}
1162 */
1163 static void
1164 sign_jump_cmd(
1165 buf_T *buf,
1166 linenr_T lnum,
1167 char_u *sign_name,
1168 int id,
1169 char_u *group)
1170 {
1171 if (buf == NULL && sign_name == NULL && group == NULL && id == -1)
1172 {
1173 EMSG(_(e_argreq));
1174 return;
1175 }
1176
1177 if (buf == NULL || (group != NULL && *group == '\0') ||
1178 lnum >= 0 || sign_name != NULL)
1179 {
1180 // File or buffer is not specified or an empty group is used
1181 // or a line number or a sign name is specified.
1182 EMSG(_(e_invarg));
1183 return;
1184 }
1185
1186 if ((lnum = buf_findsign(buf, id, group)) <= 0)
1187 {
1188 EMSGN(_("E157: Invalid sign ID: %ld"), id);
1189 return;
1190 }
1191
1192 // goto a sign ...
1193 if (buf_jump_open_win(buf) != NULL)
1194 { // ... in a current window
1195 curwin->w_cursor.lnum = lnum;
1196 check_cursor_lnum();
1197 beginline(BL_WHITE);
1198 }
1199 else
1200 { // ... not currently in a window
1201 char_u *cmd;
1202
1203 if (buf->b_fname == NULL)
1204 {
1205 EMSG(_("E934: Cannot jump to a buffer that does not have a name"));
1206 return;
1207 }
1208 cmd = alloc((unsigned)STRLEN(buf->b_fname) + 25);
1209 if (cmd == NULL)
1210 return;
1211 sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname);
1212 do_cmdline_cmd(cmd);
1213 vim_free(cmd);
1214 }
1215 # ifdef FEAT_FOLDING
1216 foldOpenCursor();
1217 # endif
1218 }
1219
1220 /*
1221 * Parse the command line arguments for the ":sign place", ":sign unplace" and
1222 * ":sign jump" commands.
1223 * The supported arguments are: line={lnum} name={name} group={group}
1224 * priority={prio} and file={fname} or buffer={nr}.
1225 */
1226 static int
1227 parse_sign_cmd_args(
1228 int cmd,
1229 char_u *arg,
1230 char_u **sign_name,
1231 int *signid,
1232 char_u **group,
1233 int *prio,
1234 buf_T **buf,
1235 linenr_T *lnum)
1236 {
1237 char_u *arg1;
1238 char_u *name;
1239 char_u *filename = NULL;
1240
1241 // first arg could be placed sign id
1242 arg1 = arg;
1243 if (VIM_ISDIGIT(*arg))
1244 {
1245 *signid = getdigits(&arg);
1246 if (!VIM_ISWHITE(*arg) && *arg != NUL)
1247 {
1248 *signid = -1;
1249 arg = arg1;
1250 }
1251 else
1252 arg = skipwhite(arg);
1253 }
1254
1255 while (*arg != NUL)
1256 {
1257 if (STRNCMP(arg, "line=", 5) == 0)
1258 {
1259 arg += 5;
1260 *lnum = atoi((char *)arg);
1261 arg = skiptowhite(arg);
1262 }
1263 else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE)
1264 {
1265 if (*signid != -1)
1266 {
1267 EMSG(_(e_invarg));
1268 return FAIL;
1269 }
1270 *signid = -2;
1271 arg = skiptowhite(arg + 1);
1272 }
1273 else if (STRNCMP(arg, "name=", 5) == 0)
1274 {
1275 arg += 5;
1276 name = arg;
1277 arg = skiptowhite(arg);
1278 if (*arg != NUL)
1279 *arg++ = NUL;
1280 while (name[0] == '0' && name[1] != NUL)
1281 ++name;
1282 *sign_name = name;
1283 }
1284 else if (STRNCMP(arg, "group=", 6) == 0)
1285 {
1286 arg += 6;
1287 *group = arg;
1288 arg = skiptowhite(arg);
1289 if (*arg != NUL)
1290 *arg++ = NUL;
1291 }
1292 else if (STRNCMP(arg, "priority=", 9) == 0)
1293 {
1294 arg += 9;
1295 *prio = atoi((char *)arg);
1296 arg = skiptowhite(arg);
1297 }
1298 else if (STRNCMP(arg, "file=", 5) == 0)
1299 {
1300 arg += 5;
1301 filename = arg;
1302 *buf = buflist_findname_exp(arg);
1303 break;
1304 }
1305 else if (STRNCMP(arg, "buffer=", 7) == 0)
1306 {
1307 arg += 7;
1308 filename = arg;
1309 *buf = buflist_findnr((int)getdigits(&arg));
1310 if (*skipwhite(arg) != NUL)
1311 EMSG(_(e_trailing));
1312 break;
1313 }
1314 else
1315 {
1316 EMSG(_(e_invarg));
1317 return FAIL;
1318 }
1319 arg = skipwhite(arg);
1320 }
1321
1322 if (filename != NULL && *buf == NULL)
1323 {
1324 EMSG2(_("E158: Invalid buffer name: %s"), filename);
1325 return FAIL;
1326 }
1327
1328 return OK;
1329 }
1330
1331 /*
1332 * ":sign" command
1333 */
1334 void
1335 ex_sign(exarg_T *eap)
1336 {
1337 char_u *arg = eap->arg;
1338 char_u *p;
1339 int idx;
1340 sign_T *sp;
1341 buf_T *buf = NULL;
1342
1343 // Parse the subcommand.
1344 p = skiptowhite(arg);
1345 idx = sign_cmd_idx(arg, p);
1346 if (idx == SIGNCMD_LAST)
1347 {
1348 EMSG2(_("E160: Unknown sign command: %s"), arg);
1349 return;
1350 }
1351 arg = skipwhite(p);
1352
1353 if (idx <= SIGNCMD_LIST)
1354 {
1355 // Define, undefine or list signs.
1356 if (idx == SIGNCMD_LIST && *arg == NUL)
1357 {
1358 // ":sign list": list all defined signs
1359 for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next)
1360 sign_list_defined(sp);
1361 }
1362 else if (*arg == NUL)
1363 EMSG(_("E156: Missing sign name"));
1364 else
1365 {
1366 char_u *name;
1367
1368 // Isolate the sign name. If it's a number skip leading zeroes,
1369 // so that "099" and "99" are the same sign. But keep "0".
1370 p = skiptowhite(arg);
1371 if (*p != NUL)
1372 *p++ = NUL;
1373 while (arg[0] == '0' && arg[1] != NUL)
1374 ++arg;
1375 name = vim_strsave(arg);
1376
1377 if (idx == SIGNCMD_DEFINE)
1378 sign_define_cmd(name, p);
1379 else if (idx == SIGNCMD_LIST)
1380 // ":sign list {name}"
1381 sign_list_by_name(name);
1382 else
1383 // ":sign undefine {name}"
1384 sign_undefine_by_name(name);
1385
1386 vim_free(name);
1387 return;
1388 }
1389 }
1390 else
1391 {
1392 int id = -1;
1393 linenr_T lnum = -1;
1394 char_u *sign_name = NULL;
1395 char_u *group = NULL;
1396 int prio = SIGN_DEF_PRIO;
1397
1398 // Parse command line arguments
1399 if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio,
1400 &buf, &lnum) == FAIL)
1401 return;
1402
1403 if (idx == SIGNCMD_PLACE)
1404 sign_place_cmd(buf, lnum, sign_name, id, group, prio);
1405 else if (idx == SIGNCMD_UNPLACE)
1406 sign_unplace_cmd(buf, lnum, sign_name, id, group);
1407 else if (idx == SIGNCMD_JUMP)
1408 sign_jump_cmd(buf, lnum, sign_name, id, group);
1409 }
1410 }
1411
1412 /*
1413 * Return information about a specified sign
1414 */
1415 static void
1416 sign_getinfo(sign_T *sp, dict_T *retdict)
1417 {
1418 char_u *p;
1419
1420 dict_add_string(retdict, "name", (char_u *)sp->sn_name);
1421 if (sp->sn_icon != NULL)
1422 dict_add_string(retdict, "icon", (char_u *)sp->sn_icon);
1423 if (sp->sn_text != NULL)
1424 dict_add_string(retdict, "text", (char_u *)sp->sn_text);
1425 if (sp->sn_line_hl > 0)
1426 {
1427 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1428 if (p == NULL)
1429 p = (char_u *)"NONE";
1430 dict_add_string(retdict, "linehl", (char_u *)p);
1431 }
1432 if (sp->sn_text_hl > 0)
1433 {
1434 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1435 if (p == NULL)
1436 p = (char_u *)"NONE";
1437 dict_add_string(retdict, "texthl", (char_u *)p);
1438 }
1439 }
1440
1441 /*
1442 * If 'name' is NULL, return a list of all the defined signs.
1443 * Otherwise, return information about the specified sign.
1444 */
1445 void
1446 sign_getlist(char_u *name, list_T *retlist)
1447 {
1448 sign_T *sp = first_sign;
1449 dict_T *dict;
1450
1451 if (name != NULL)
1452 {
1453 sp = sign_find(name, NULL);
1454 if (sp == NULL)
1455 return;
1456 }
1457
1458 for (; sp != NULL && !got_int; sp = sp->sn_next)
1459 {
1460 if ((dict = dict_alloc_id(aid_sign_getlist)) == NULL)
1461 return;
1462 if (list_append_dict(retlist, dict) == FAIL)
1463 return;
1464 sign_getinfo(sp, dict);
1465
1466 if (name != NULL) // handle only the specified sign
1467 break;
1468 }
1469 }
1470
1471 /*
1472 * Returns information about signs placed in a buffer as list of dicts.
1473 */
1474 void
1475 get_buffer_signs(buf_T *buf, list_T *l)
1476 {
1477 signlist_T *sign;
1478 dict_T *d;
1479
1480 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1481 {
1482 if ((d = sign_get_info(sign)) != NULL)
1483 list_append_dict(l, d);
1484 }
1485 }
1486
1487 /*
1488 * Return information about all the signs placed in a buffer
1489 */
1490 static void
1491 sign_get_placed_in_buf(
1492 buf_T *buf,
1493 linenr_T lnum,
1494 int sign_id,
1495 char_u *sign_group,
1496 list_T *retlist)
1497 {
1498 dict_T *d;
1499 list_T *l;
1500 signlist_T *sign;
1501 dict_T *sdict;
1502
1503 if ((d = dict_alloc_id(aid_sign_getplaced_dict)) == NULL)
1504 return;
1505 list_append_dict(retlist, d);
1506
1507 dict_add_number(d, "bufnr", (long)buf->b_fnum);
1508
1509 if ((l = list_alloc_id(aid_sign_getplaced_list)) == NULL)
1510 return;
1511 dict_add_list(d, "signs", l);
1512
1513 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1514 {
1515 if (!sign_in_group(sign, sign_group))
1516 continue;
1517 if ((lnum == 0 && sign_id == 0) ||
1518 (sign_id == 0 && lnum == sign->lnum) ||
1519 (lnum == 0 && sign_id == sign->id) ||
1520 (lnum == sign->lnum && sign_id == sign->id))
1521 {
1522 if ((sdict = sign_get_info(sign)) != NULL)
1523 list_append_dict(l, sdict);
1524 }
1525 }
1526 }
1527
1528 /*
1529 * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the
1530 * sign placed at the line number. If 'lnum' is zero, return all the signs
1531 * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers.
1532 */
1533 void
1534 sign_get_placed(
1535 buf_T *buf,
1536 linenr_T lnum,
1537 int sign_id,
1538 char_u *sign_group,
1539 list_T *retlist)
1540 {
1541 if (buf != NULL)
1542 sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist);
1543 else
1544 {
1545 FOR_ALL_BUFFERS(buf)
1546 {
1547 if (buf->b_signlist != NULL)
1548 sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist);
1549 }
1550 }
1551 }
1552
1553 # if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1554 /*
1555 * Allocate the icons. Called when the GUI has started. Allows defining
1556 * signs before it starts.
1557 */
1558 void
1559 sign_gui_started(void)
1560 {
1561 sign_T *sp;
1562
1563 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1564 if (sp->sn_icon != NULL)
1565 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
1566 }
1567 # endif
1568
1569 /*
1570 * List one sign.
1571 */
1572 static void
1573 sign_list_defined(sign_T *sp)
1574 {
1575 char_u *p;
1576
1577 smsg((char_u *)"sign %s", sp->sn_name);
1578 if (sp->sn_icon != NULL)
1579 {
1580 MSG_PUTS(" icon=");
1581 msg_outtrans(sp->sn_icon);
1582 # ifdef FEAT_SIGN_ICONS
1583 if (sp->sn_image == NULL)
1584 MSG_PUTS(_(" (NOT FOUND)"));
1585 # else
1586 MSG_PUTS(_(" (not supported)"));
1587 # endif
1588 }
1589 if (sp->sn_text != NULL)
1590 {
1591 MSG_PUTS(" text=");
1592 msg_outtrans(sp->sn_text);
1593 }
1594 if (sp->sn_line_hl > 0)
1595 {
1596 MSG_PUTS(" linehl=");
1597 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1598 if (p == NULL)
1599 MSG_PUTS("NONE");
1600 else
1601 msg_puts(p);
1602 }
1603 if (sp->sn_text_hl > 0)
1604 {
1605 MSG_PUTS(" texthl=");
1606 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1607 if (p == NULL)
1608 MSG_PUTS("NONE");
1609 else
1610 msg_puts(p);
1611 }
1612 }
1613
1614 /*
1615 * Undefine a sign and free its memory.
1616 */
1617 static void
1618 sign_undefine(sign_T *sp, sign_T *sp_prev)
1619 {
1620 vim_free(sp->sn_name);
1621 vim_free(sp->sn_icon);
1622 # ifdef FEAT_SIGN_ICONS
1623 if (sp->sn_image != NULL)
1624 {
1625 out_flush();
1626 gui_mch_destroy_sign(sp->sn_image);
1627 }
1628 # endif
1629 vim_free(sp->sn_text);
1630 if (sp_prev == NULL)
1631 first_sign = sp->sn_next;
1632 else
1633 sp_prev->sn_next = sp->sn_next;
1634 vim_free(sp);
1635 }
1636
1637 /*
1638 * Get highlighting attribute for sign "typenr".
1639 * If "line" is TRUE: line highl, if FALSE: text highl.
1640 */
1641 int
1642 sign_get_attr(int typenr, int line)
1643 {
1644 sign_T *sp;
1645
1646 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1647 if (sp->sn_typenr == typenr)
1648 {
1649 if (line)
1650 {
1651 if (sp->sn_line_hl > 0)
1652 return syn_id2attr(sp->sn_line_hl);
1653 }
1654 else
1655 {
1656 if (sp->sn_text_hl > 0)
1657 return syn_id2attr(sp->sn_text_hl);
1658 }
1659 break;
1660 }
1661 return 0;
1662 }
1663
1664 /*
1665 * Get text mark for sign "typenr".
1666 * Returns NULL if there isn't one.
1667 */
1668 char_u *
1669 sign_get_text(int typenr)
1670 {
1671 sign_T *sp;
1672
1673 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1674 if (sp->sn_typenr == typenr)
1675 return sp->sn_text;
1676 return NULL;
1677 }
1678
1679 # if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1680 void *
1681 sign_get_image(
1682 int typenr) /* the attribute which may have a sign */
1683 {
1684 sign_T *sp;
1685
1686 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1687 if (sp->sn_typenr == typenr)
1688 return sp->sn_image;
1689 return NULL;
1690 }
1691 # endif
1692
1693 /*
1694 * Undefine/free all signs.
1695 */
1696 void
1697 free_signs(void)
1698 {
1699 while (first_sign != NULL)
1700 sign_undefine(first_sign, NULL);
1701 }
1702
1703 # if defined(FEAT_CMDL_COMPL) || defined(PROTO)
1704 static enum
1705 {
1706 EXP_SUBCMD, /* expand :sign sub-commands */
1707 EXP_DEFINE, /* expand :sign define {name} args */
1708 EXP_PLACE, /* expand :sign place {id} args */
1709 EXP_UNPLACE, /* expand :sign unplace" */
1710 EXP_SIGN_NAMES /* expand with name of placed signs */
1711 } expand_what;
1712
1713 /*
1714 * Function given to ExpandGeneric() to obtain the sign command
1715 * expansion.
1716 */
1717 char_u *
1718 get_sign_name(expand_T *xp UNUSED, int idx)
1719 {
1720 sign_T *sp;
1721 int current_idx;
1722
1723 switch (expand_what)
1724 {
1725 case EXP_SUBCMD:
1726 return (char_u *)cmds[idx];
1727 case EXP_DEFINE:
1728 {
1729 char *define_arg[] =
1730 {
1731 "icon=", "linehl=", "text=", "texthl=", NULL
1732 };
1733 return (char_u *)define_arg[idx];
1734 }
1735 case EXP_PLACE:
1736 {
1737 char *place_arg[] =
1738 {
1739 "line=", "name=", "group=", "priority=", "file=",
1740 "buffer=", NULL
1741 };
1742 return (char_u *)place_arg[idx];
1743 }
1744 case EXP_UNPLACE:
1745 {
1746 char *unplace_arg[] = { "group=", "file=", "buffer=", NULL };
1747 return (char_u *)unplace_arg[idx];
1748 }
1749 case EXP_SIGN_NAMES:
1750 /* Complete with name of signs already defined */
1751 current_idx = 0;
1752 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1753 if (current_idx++ == idx)
1754 return sp->sn_name;
1755 return NULL;
1756 default:
1757 return NULL;
1758 }
1759 }
1760
1761 /*
1762 * Handle command line completion for :sign command.
1763 */
1764 void
1765 set_context_in_sign_cmd(expand_T *xp, char_u *arg)
1766 {
1767 char_u *p;
1768 char_u *end_subcmd;
1769 char_u *last;
1770 int cmd_idx;
1771 char_u *begin_subcmd_args;
1772
1773 /* Default: expand subcommands. */
1774 xp->xp_context = EXPAND_SIGN;
1775 expand_what = EXP_SUBCMD;
1776 xp->xp_pattern = arg;
1777
1778 end_subcmd = skiptowhite(arg);
1779 if (*end_subcmd == NUL)
1780 /* expand subcmd name
1781 * :sign {subcmd}<CTRL-D>*/
1782 return;
1783
1784 cmd_idx = sign_cmd_idx(arg, end_subcmd);
1785
1786 /* :sign {subcmd} {subcmd_args}
1787 * |
1788 * begin_subcmd_args */
1789 begin_subcmd_args = skipwhite(end_subcmd);
1790 p = skiptowhite(begin_subcmd_args);
1791 if (*p == NUL)
1792 {
1793 /*
1794 * Expand first argument of subcmd when possible.
1795 * For ":jump {id}" and ":unplace {id}", we could
1796 * possibly expand the ids of all signs already placed.
1797 */
1798 xp->xp_pattern = begin_subcmd_args;
1799 switch (cmd_idx)
1800 {
1801 case SIGNCMD_LIST:
1802 case SIGNCMD_UNDEFINE:
1803 /* :sign list <CTRL-D>
1804 * :sign undefine <CTRL-D> */
1805 expand_what = EXP_SIGN_NAMES;
1806 break;
1807 default:
1808 xp->xp_context = EXPAND_NOTHING;
1809 }
1810 return;
1811 }
1812
1813 /* expand last argument of subcmd */
1814
1815 /* :sign define {name} {args}...
1816 * |
1817 * p */
1818
1819 /* Loop until reaching last argument. */
1820 do
1821 {
1822 p = skipwhite(p);
1823 last = p;
1824 p = skiptowhite(p);
1825 } while (*p != NUL);
1826
1827 p = vim_strchr(last, '=');
1828
1829 /* :sign define {name} {args}... {last}=
1830 * | |
1831 * last p */
1832 if (p == NULL)
1833 {
1834 /* Expand last argument name (before equal sign). */
1835 xp->xp_pattern = last;
1836 switch (cmd_idx)
1837 {
1838 case SIGNCMD_DEFINE:
1839 expand_what = EXP_DEFINE;
1840 break;
1841 case SIGNCMD_PLACE:
1842 expand_what = EXP_PLACE;
1843 break;
1844 case SIGNCMD_JUMP:
1845 case SIGNCMD_UNPLACE:
1846 expand_what = EXP_UNPLACE;
1847 break;
1848 default:
1849 xp->xp_context = EXPAND_NOTHING;
1850 }
1851 }
1852 else
1853 {
1854 /* Expand last argument value (after equal sign). */
1855 xp->xp_pattern = p + 1;
1856 switch (cmd_idx)
1857 {
1858 case SIGNCMD_DEFINE:
1859 if (STRNCMP(last, "texthl", p - last) == 0 ||
1860 STRNCMP(last, "linehl", p - last) == 0)
1861 xp->xp_context = EXPAND_HIGHLIGHT;
1862 else if (STRNCMP(last, "icon", p - last) == 0)
1863 xp->xp_context = EXPAND_FILES;
1864 else
1865 xp->xp_context = EXPAND_NOTHING;
1866 break;
1867 case SIGNCMD_PLACE:
1868 if (STRNCMP(last, "name", p - last) == 0)
1869 expand_what = EXP_SIGN_NAMES;
1870 else
1871 xp->xp_context = EXPAND_NOTHING;
1872 break;
1873 default:
1874 xp->xp_context = EXPAND_NOTHING;
1875 }
1876 }
1877 }
1878 # endif
1879
1880 #endif /* FEAT_SIGNS */