blob: a67f3ec8cf8fb23928e0ffdc19b61fff0a6d1918 [file] [log] [blame]
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * sign.c: functions for managing signs
12 */
13
14#include "vim.h"
15
16#if defined(FEAT_SIGNS) || defined(PROTO)
17
18/*
19 * Struct to hold the sign properties.
20 */
21typedef struct sign sign_T;
22
23struct sign
24{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +010025 sign_T *sn_next; // next sign in list
26 int sn_typenr; // type number of sign
27 char_u *sn_name; // name of sign
28 char_u *sn_icon; // name of pixmap
Bram Moolenaarbbea4702019-01-01 13:20:31 +010029# ifdef FEAT_SIGN_ICONS
Bram Moolenaar6b7b7192019-01-11 13:42:41 +010030 void *sn_image; // icon image
Bram Moolenaarbbea4702019-01-01 13:20:31 +010031# endif
Bram Moolenaar6b7b7192019-01-11 13:42:41 +010032 char_u *sn_text; // text used instead of pixmap
33 int sn_line_hl; // highlight ID for line
34 int sn_text_hl; // highlight ID for text
Bram Moolenaarbbea4702019-01-01 13:20:31 +010035};
36
37static sign_T *first_sign = NULL;
38static int next_sign_typenr = 1;
39
40static void sign_list_defined(sign_T *sp);
41static void sign_undefine(sign_T *sp, sign_T *sp_prev);
42
43static char *cmds[] = {
44 "define",
45# define SIGNCMD_DEFINE 0
46 "undefine",
47# define SIGNCMD_UNDEFINE 1
48 "list",
49# define SIGNCMD_LIST 2
50 "place",
51# define SIGNCMD_PLACE 3
52 "unplace",
53# define SIGNCMD_UNPLACE 4
54 "jump",
55# define SIGNCMD_JUMP 5
56 NULL
57# define SIGNCMD_LAST 6
58};
59
60static hashtab_T sg_table; // sign group (signgroup_T) hashtable
61static int next_sign_id = 1; // next sign id in the global group
62
63/*
64 * Initialize data needed for managing signs
65 */
66 void
67init_signs(void)
68{
69 hash_init(&sg_table); // sign group hash table
70}
71
72/*
73 * A new sign in group 'groupname' is added. If the group is not present,
74 * create it. Otherwise reference the group.
75 */
76 static signgroup_T *
77sign_group_ref(char_u *groupname)
78{
79 hash_T hash;
80 hashitem_T *hi;
81 signgroup_T *group;
82
83 hash = hash_hash(groupname);
84 hi = hash_lookup(&sg_table, groupname, hash);
85 if (HASHITEM_EMPTY(hi))
86 {
87 // new group
Bram Moolenaarc799fe22019-05-28 23:08:19 +020088 group = alloc(sizeof(signgroup_T) + STRLEN(groupname));
Bram Moolenaarbbea4702019-01-01 13:20:31 +010089 if (group == NULL)
90 return NULL;
91 STRCPY(group->sg_name, groupname);
92 group->refcount = 1;
93 group->next_sign_id = 1;
94 hash_add_item(&sg_table, hi, group->sg_name, hash);
95 }
96 else
97 {
98 // existing group
99 group = HI2SG(hi);
100 group->refcount++;
101 }
102
103 return group;
104}
105
106/*
107 * A sign in group 'groupname' is removed. If all the signs in this group are
108 * removed, then remove the group.
109 */
110 static void
111sign_group_unref(char_u *groupname)
112{
113 hashitem_T *hi;
114 signgroup_T *group;
115
116 hi = hash_find(&sg_table, groupname);
117 if (!HASHITEM_EMPTY(hi))
118 {
119 group = HI2SG(hi);
120 group->refcount--;
121 if (group->refcount == 0)
122 {
123 // All the signs in this group are removed
124 hash_remove(&sg_table, hi);
125 vim_free(group);
126 }
127 }
128}
129
130/*
131 * Returns TRUE if 'sign' is in 'group'.
132 * A sign can either be in the global group (sign->group == NULL)
133 * or in a named group. If 'group' is '*', then the sign is part of the group.
134 */
135 static int
136sign_in_group(signlist_T *sign, char_u *group)
137{
138 return ((group != NULL && STRCMP(group, "*") == 0)
139 || (group == NULL && sign->group == NULL)
140 || (group != NULL && sign->group != NULL
141 && STRCMP(group, sign->group->sg_name) == 0));
142}
143
144/*
145 * Get the next free sign identifier in the specified group
146 */
147 static int
148sign_group_get_next_signid(buf_T *buf, char_u *groupname)
149{
150 int id = 1;
151 signgroup_T *group = NULL;
152 signlist_T *sign;
153 hashitem_T *hi;
154 int found = FALSE;
155
156 if (groupname != NULL)
157 {
158 hi = hash_find(&sg_table, groupname);
159 if (HASHITEM_EMPTY(hi))
160 return id;
161 group = HI2SG(hi);
162 }
163
Bram Moolenaarb5443cc2019-01-15 20:19:40 +0100164 // Search for the next usable sign identifier
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100165 while (!found)
166 {
167 if (group == NULL)
168 id = next_sign_id++; // global group
169 else
170 id = group->next_sign_id++;
171
172 // Check whether this sign is already placed in the buffer
173 found = TRUE;
174 FOR_ALL_SIGNS_IN_BUF(buf, sign)
175 {
176 if (id == sign->id && sign_in_group(sign, groupname))
177 {
178 found = FALSE; // sign identifier is in use
179 break;
180 }
181 }
182 }
183
184 return id;
185}
186
187/*
188 * Insert a new sign into the signlist for buffer 'buf' between the 'prev' and
189 * 'next' signs.
190 */
191 static void
192insert_sign(
193 buf_T *buf, // buffer to store sign in
194 signlist_T *prev, // previous sign entry
195 signlist_T *next, // next sign entry
196 int id, // sign ID
197 char_u *group, // sign group; NULL for global group
198 int prio, // sign priority
199 linenr_T lnum, // line number which gets the mark
200 int typenr) // typenr of sign we are adding
201{
202 signlist_T *newsign;
203
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200204 newsign = lalloc_id(sizeof(signlist_T), FALSE, aid_insert_sign);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100205 if (newsign != NULL)
206 {
207 newsign->id = id;
208 newsign->lnum = lnum;
209 newsign->typenr = typenr;
210 if (group != NULL)
211 {
212 newsign->group = sign_group_ref(group);
213 if (newsign->group == NULL)
214 {
215 vim_free(newsign);
216 return;
217 }
218 }
219 else
220 newsign->group = NULL;
221 newsign->priority = prio;
222 newsign->next = next;
223 newsign->prev = prev;
224 if (next != NULL)
225 next->prev = newsign;
226
227 if (prev == NULL)
228 {
229 // When adding first sign need to redraw the windows to create the
230 // column for signs.
231 if (buf->b_signlist == NULL)
232 {
233 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200234 changed_line_abv_curs();
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100235 }
236
237 // first sign in signlist
238 buf->b_signlist = newsign;
239#ifdef FEAT_NETBEANS_INTG
240 if (netbeans_active())
241 buf->b_has_sign_column = TRUE;
242#endif
243 }
244 else
245 prev->next = newsign;
246 }
247}
248
249/*
250 * Insert a new sign sorted by line number and sign priority.
251 */
252 static void
253insert_sign_by_lnum_prio(
254 buf_T *buf, // buffer to store sign in
255 signlist_T *prev, // previous sign entry
256 int id, // sign ID
257 char_u *group, // sign group; NULL for global group
258 int prio, // sign priority
259 linenr_T lnum, // line number which gets the mark
260 int typenr) // typenr of sign we are adding
261{
262 signlist_T *sign;
263
264 // keep signs sorted by lnum and by priority: insert new sign at
265 // the proper position in the list for this lnum.
266 while (prev != NULL && prev->lnum == lnum && prev->priority <= prio)
267 prev = prev->prev;
268 if (prev == NULL)
269 sign = buf->b_signlist;
270 else
271 sign = prev->next;
272
273 insert_sign(buf, prev, sign, id, group, prio, lnum, typenr);
274}
275
276/*
277 * Get the name of a sign by its typenr.
278 */
279 static char_u *
280sign_typenr2name(int typenr)
281{
282 sign_T *sp;
283
284 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
285 if (sp->sn_typenr == typenr)
286 return sp->sn_name;
287 return (char_u *)_("[Deleted]");
288}
289
290/*
291 * Return information about a sign in a Dict
292 */
293 static dict_T *
294sign_get_info(signlist_T *sign)
295{
296 dict_T *d;
297
298 if ((d = dict_alloc_id(aid_sign_getinfo)) == NULL)
299 return NULL;
300 dict_add_number(d, "id", sign->id);
301 dict_add_string(d, "group", (sign->group == NULL) ?
302 (char_u *)"" : sign->group->sg_name);
303 dict_add_number(d, "lnum", sign->lnum);
304 dict_add_string(d, "name", sign_typenr2name(sign->typenr));
305 dict_add_number(d, "priority", sign->priority);
306
307 return d;
308}
309
310/*
Bram Moolenaar64416122019-06-07 21:37:13 +0200311 * Sort the signs placed on the same line as "sign" by priority. Invoked after
312 * changing the priority of an already placed sign. Assumes the signs in the
313 * buffer are sorted by line number and priority.
314 */
315 static void
316sign_sort_by_prio_on_line(buf_T *buf, signlist_T *sign)
317{
318 signlist_T *p = NULL;
319
320 // If there is only one sign in the buffer or only one sign on the line or
321 // the sign is already sorted by priority, then return.
322 if ((sign->prev == NULL
323 || sign->prev->lnum != sign->lnum
324 || sign->prev->priority > sign->priority)
325 && (sign->next == NULL
326 || sign->next->lnum != sign->lnum
327 || sign->next->priority < sign->priority))
328 return;
329
330 // One or more signs on the same line as 'sign'
331 // Find a sign after which 'sign' should be inserted
332
333 // First search backward for a sign with higher priority on the same line
334 p = sign;
335 while (p->prev != NULL && p->prev->lnum == sign->lnum
336 && p->prev->priority <= sign->priority)
337 p = p->prev;
338
339 if (p == sign)
340 {
341 // Sign not found. Search forward for a sign with priority just before
342 // 'sign'.
343 p = sign->next;
344 while (p->next != NULL && p->next->lnum == sign->lnum
345 && p->next->priority > sign->priority)
346 p = p->next;
347 }
348
349 // Remove 'sign' from the list
350 if (buf->b_signlist == sign)
351 buf->b_signlist = sign->next;
352 if (sign->prev != NULL)
353 sign->prev->next = sign->next;
354 if (sign->next != NULL)
355 sign->next->prev = sign->prev;
356 sign->prev = NULL;
357 sign->next = NULL;
358
359 // Re-insert 'sign' at the right place
360 if (p->priority <= sign->priority)
361 {
362 // 'sign' has a higher priority and should be inserted before 'p'
363 sign->prev = p->prev;
364 sign->next = p;
365 p->prev = sign;
366 if (sign->prev != NULL)
367 sign->prev->next = sign;
368 if (buf->b_signlist == p)
369 buf->b_signlist = sign;
370 }
371 else
372 {
373 // 'sign' has a lower priority and should be inserted after 'p'
374 sign->prev = p;
375 sign->next = p->next;
376 p->next = sign;
377 if (sign->next != NULL)
378 sign->next->prev = sign;
379 }
380}
381
382/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100383 * Add the sign into the signlist. Find the right spot to do it though.
384 */
385 static void
386buf_addsign(
387 buf_T *buf, // buffer to store sign in
388 int id, // sign ID
389 char_u *groupname, // sign group
390 int prio, // sign priority
391 linenr_T lnum, // line number which gets the mark
392 int typenr) // typenr of sign we are adding
393{
394 signlist_T *sign; // a sign in the signlist
395 signlist_T *prev; // the previous sign
396
397 prev = NULL;
398 FOR_ALL_SIGNS_IN_BUF(buf, sign)
399 {
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100400 if (lnum == sign->lnum && id == sign->id
401 && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100402 {
403 // Update an existing sign
404 sign->typenr = typenr;
Bram Moolenaar58a7f872019-06-04 22:48:15 +0200405 sign->priority = prio;
Bram Moolenaar64416122019-06-07 21:37:13 +0200406 sign_sort_by_prio_on_line(buf, sign);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100407 return;
408 }
409 else if (lnum < sign->lnum)
410 {
411 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio,
412 lnum, typenr);
413 return;
414 }
415 prev = sign;
416 }
417
418 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr);
419 return;
420}
421
422/*
423 * For an existing, placed sign "markId" change the type to "typenr".
424 * Returns the line number of the sign, or zero if the sign is not found.
425 */
426 static linenr_T
427buf_change_sign_type(
428 buf_T *buf, // buffer to store sign in
429 int markId, // sign ID
430 char_u *group, // sign group
431 int typenr) // typenr of sign we are adding
432{
433 signlist_T *sign; // a sign in the signlist
434
435 FOR_ALL_SIGNS_IN_BUF(buf, sign)
436 {
437 if (sign->id == markId && sign_in_group(sign, group))
438 {
439 sign->typenr = typenr;
440 return sign->lnum;
441 }
442 }
443
444 return (linenr_T)0;
445}
446
447/*
448 * Return the type number of the sign at line number 'lnum' in buffer 'buf'
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100449 * which has the attribute specified by 'type'. Returns 0 if a sign is not
450 * found at the line number or it doesn't have the specified attribute.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100451 */
452 int
453buf_getsigntype(
454 buf_T *buf,
455 linenr_T lnum,
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100456 int type) // SIGN_ICON, SIGN_TEXT, SIGN_ANY, SIGN_LINEHL
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100457{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100458 signlist_T *sign; // a sign in a b_signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100459
460 FOR_ALL_SIGNS_IN_BUF(buf, sign)
461 if (sign->lnum == lnum
462 && (type == SIGN_ANY
463# ifdef FEAT_SIGN_ICONS
464 || (type == SIGN_ICON
465 && sign_get_image(sign->typenr) != NULL)
466# endif
467 || (type == SIGN_TEXT
468 && sign_get_text(sign->typenr) != NULL)
469 || (type == SIGN_LINEHL
470 && sign_get_attr(sign->typenr, TRUE) != 0)))
471 return sign->typenr;
472 return 0;
473}
474
475/*
476 * Delete sign 'id' in group 'group' from buffer 'buf'.
477 * If 'id' is zero, then delete all the signs in group 'group'. Otherwise
478 * delete only the specified sign.
479 * If 'group' is '*', then delete the sign in all the groups. If 'group' is
480 * NULL, then delete the sign in the global group. Otherwise delete the sign in
481 * the specified group.
482 * Returns the line number of the deleted sign. If multiple signs are deleted,
483 * then returns the line number of the last sign deleted.
484 */
485 linenr_T
486buf_delsign(
487 buf_T *buf, // buffer sign is stored in
488 linenr_T atlnum, // sign at this line, 0 - at any line
489 int id, // sign id
490 char_u *group) // sign group
491{
492 signlist_T **lastp; // pointer to pointer to current sign
493 signlist_T *sign; // a sign in a b_signlist
494 signlist_T *next; // the next sign in a b_signlist
495 linenr_T lnum; // line number whose sign was deleted
496
497 lastp = &buf->b_signlist;
498 lnum = 0;
499 for (sign = buf->b_signlist; sign != NULL; sign = next)
500 {
501 next = sign->next;
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100502 if ((id == 0 || sign->id == id)
503 && (atlnum == 0 || sign->lnum == atlnum)
504 && sign_in_group(sign, group))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100505
506 {
507 *lastp = next;
508 if (next != NULL)
509 next->prev = sign->prev;
510 lnum = sign->lnum;
511 if (sign->group != NULL)
512 sign_group_unref(sign->group->sg_name);
513 vim_free(sign);
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100514 redraw_buf_line_later(buf, lnum);
515
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100516 // Check whether only one sign needs to be deleted
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100517 // If deleting a sign with a specific identifier in a particular
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100518 // group or deleting any sign at a particular line number, delete
519 // only one sign.
520 if (group == NULL
521 || (*group != '*' && id != 0)
522 || (*group == '*' && atlnum != 0))
523 break;
524 }
525 else
526 lastp = &sign->next;
527 }
528
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100529 // When deleting the last sign the cursor position may change, because the
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100530 // sign columns no longer shows. And the 'signcolumn' may be hidden.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100531 if (buf->b_signlist == NULL)
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100532 {
533 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200534 changed_line_abv_curs();
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100535 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100536
537 return lnum;
538}
539
540
541/*
542 * Find the line number of the sign with the requested id in group 'group'. If
543 * the sign does not exist, return 0 as the line number. This will still let
544 * the correct file get loaded.
545 */
546 int
547buf_findsign(
548 buf_T *buf, // buffer to store sign in
549 int id, // sign ID
550 char_u *group) // sign group
551{
552 signlist_T *sign; // a sign in the signlist
553
554 FOR_ALL_SIGNS_IN_BUF(buf, sign)
555 if (sign->id == id && sign_in_group(sign, group))
556 return sign->lnum;
557
558 return 0;
559}
560
561/*
562 * Return the sign at line 'lnum' in buffer 'buf'. Returns NULL if a sign is
563 * not found at the line. If 'groupname' is NULL, searches in the global group.
564 */
565 static signlist_T *
566buf_getsign_at_line(
567 buf_T *buf, // buffer whose sign we are searching for
568 linenr_T lnum, // line number of sign
569 char_u *groupname) // sign group name
570{
571 signlist_T *sign; // a sign in the signlist
572
573 FOR_ALL_SIGNS_IN_BUF(buf, sign)
574 if (sign->lnum == lnum && sign_in_group(sign, groupname))
575 return sign;
576
577 return NULL;
578}
579
580/*
581 * Return the identifier of the sign at line number 'lnum' in buffer 'buf'.
582 */
583 int
584buf_findsign_id(
585 buf_T *buf, // buffer whose sign we are searching for
586 linenr_T lnum, // line number of sign
587 char_u *groupname) // sign group name
588{
589 signlist_T *sign; // a sign in the signlist
590
591 sign = buf_getsign_at_line(buf, lnum, groupname);
592 if (sign != NULL)
593 return sign->id;
594
595 return 0;
596}
597
598# if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
599/*
600 * See if a given type of sign exists on a specific line.
601 */
602 int
603buf_findsigntype_id(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100604 buf_T *buf, // buffer whose sign we are searching for
605 linenr_T lnum, // line number of sign
606 int typenr) // sign type number
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100607{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100608 signlist_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100609
610 FOR_ALL_SIGNS_IN_BUF(buf, sign)
611 if (sign->lnum == lnum && sign->typenr == typenr)
612 return sign->id;
613
614 return 0;
615}
616
617
618# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
619/*
620 * Return the number of icons on the given line.
621 */
622 int
623buf_signcount(buf_T *buf, linenr_T lnum)
624{
625 signlist_T *sign; // a sign in the signlist
626 int count = 0;
627
628 FOR_ALL_SIGNS_IN_BUF(buf, sign)
629 if (sign->lnum == lnum)
630 if (sign_get_image(sign->typenr) != NULL)
631 count++;
632
633 return count;
634}
635# endif /* FEAT_SIGN_ICONS */
636# endif /* FEAT_NETBEANS_INTG */
637
638/*
639 * Delete signs in group 'group' in buffer "buf". If 'group' is '*', then
640 * delete all the signs.
641 */
642 void
643buf_delete_signs(buf_T *buf, char_u *group)
644{
645 signlist_T *sign;
646 signlist_T **lastp; // pointer to pointer to current sign
647 signlist_T *next;
648
649 // When deleting the last sign need to redraw the windows to remove the
650 // sign column. Not when curwin is NULL (this means we're exiting).
651 if (buf->b_signlist != NULL && curwin != NULL)
652 {
653 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200654 changed_line_abv_curs();
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100655 }
656
657 lastp = &buf->b_signlist;
658 for (sign = buf->b_signlist; sign != NULL; sign = next)
659 {
660 next = sign->next;
661 if (sign_in_group(sign, group))
662 {
663 *lastp = next;
664 if (next != NULL)
665 next->prev = sign->prev;
666 if (sign->group != NULL)
667 sign_group_unref(sign->group->sg_name);
668 vim_free(sign);
669 }
670 else
671 lastp = &sign->next;
672 }
673}
674
675/*
676 * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers.
677 */
678 static void
679sign_list_placed(buf_T *rbuf, char_u *sign_group)
680{
681 buf_T *buf;
682 signlist_T *sign;
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100683 char lbuf[MSG_BUF_LEN];
684 char group[MSG_BUF_LEN];
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100685
Bram Moolenaar32526b32019-01-19 17:43:09 +0100686 msg_puts_title(_("\n--- Signs ---"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100687 msg_putchar('\n');
688 if (rbuf == NULL)
689 buf = firstbuf;
690 else
691 buf = rbuf;
692 while (buf != NULL && !got_int)
693 {
694 if (buf->b_signlist != NULL)
695 {
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100696 vim_snprintf(lbuf, MSG_BUF_LEN, _("Signs for %s:"), buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100697 msg_puts_attr(lbuf, HL_ATTR(HLF_D));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100698 msg_putchar('\n');
699 }
700 FOR_ALL_SIGNS_IN_BUF(buf, sign)
701 {
702 if (got_int)
703 break;
704 if (!sign_in_group(sign, sign_group))
705 continue;
706 if (sign->group != NULL)
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100707 vim_snprintf(group, MSG_BUF_LEN, _(" group=%s"),
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100708 sign->group->sg_name);
709 else
710 group[0] = '\0';
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100711 vim_snprintf(lbuf, MSG_BUF_LEN,
712 _(" line=%ld id=%d%s name=%s priority=%d"),
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100713 (long)sign->lnum, sign->id, group,
714 sign_typenr2name(sign->typenr), sign->priority);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100715 msg_puts(lbuf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100716 msg_putchar('\n');
717 }
718 if (rbuf != NULL)
719 break;
720 buf = buf->b_next;
721 }
722}
723
724/*
725 * Adjust a placed sign for inserted/deleted lines.
726 */
727 void
728sign_mark_adjust(
729 linenr_T line1,
730 linenr_T line2,
731 long amount,
732 long amount_after)
733{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100734 signlist_T *sign; // a sign in a b_signlist
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100735 linenr_T new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100736
737 FOR_ALL_SIGNS_IN_BUF(curbuf, sign)
738 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100739 // Ignore changes to lines after the sign
740 if (sign->lnum < line1)
741 continue;
742 new_lnum = sign->lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100743 if (sign->lnum >= line1 && sign->lnum <= line2)
744 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100745 if (amount != MAXLNUM)
746 new_lnum += amount;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100747 }
748 else if (sign->lnum > line2)
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100749 // Lines inserted or deleted before the sign
750 new_lnum += amount_after;
751
752 // If the new sign line number is past the last line in the buffer,
753 // then don't adjust the line number. Otherwise, it will always be past
754 // the last line and will not be visible.
755 if (new_lnum <= curbuf->b_ml.ml_line_count)
756 sign->lnum = new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100757 }
758}
759
760/*
761 * Find index of a ":sign" subcmd from its name.
762 * "*end_cmd" must be writable.
763 */
764 static int
765sign_cmd_idx(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100766 char_u *begin_cmd, // begin of sign subcmd
767 char_u *end_cmd) // just after sign subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100768{
769 int idx;
770 char save = *end_cmd;
771
772 *end_cmd = NUL;
773 for (idx = 0; ; ++idx)
774 if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0)
775 break;
776 *end_cmd = save;
777 return idx;
778}
779
780/*
781 * Find a sign by name. Also returns pointer to the previous sign.
782 */
783 static sign_T *
784sign_find(char_u *name, sign_T **sp_prev)
785{
786 sign_T *sp;
787
788 if (sp_prev != NULL)
789 *sp_prev = NULL;
790 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
791 {
792 if (STRCMP(sp->sn_name, name) == 0)
793 break;
794 if (sp_prev != NULL)
795 *sp_prev = sp;
796 }
797
798 return sp;
799}
800
801/*
Bram Moolenaar03142362019-01-18 22:01:42 +0100802 * Allocate a new sign
803 */
804 static sign_T *
805alloc_new_sign(char_u *name)
806{
807 sign_T *sp;
808 sign_T *lp;
809 int start = next_sign_typenr;
810
811 // Allocate a new sign.
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200812 sp = alloc_clear_id(sizeof(sign_T), aid_sign_define_by_name);
Bram Moolenaar03142362019-01-18 22:01:42 +0100813 if (sp == NULL)
814 return NULL;
815
816 // Check that next_sign_typenr is not already being used.
817 // This only happens after wrapping around. Hopefully
818 // another one got deleted and we can use its number.
819 for (lp = first_sign; lp != NULL; )
820 {
821 if (lp->sn_typenr == next_sign_typenr)
822 {
823 ++next_sign_typenr;
824 if (next_sign_typenr == MAX_TYPENR)
825 next_sign_typenr = 1;
826 if (next_sign_typenr == start)
827 {
828 vim_free(sp);
829 emsg(_("E612: Too many signs defined"));
830 return NULL;
831 }
832 lp = first_sign; // start all over
833 continue;
834 }
835 lp = lp->sn_next;
836 }
837
838 sp->sn_typenr = next_sign_typenr;
839 if (++next_sign_typenr == MAX_TYPENR)
840 next_sign_typenr = 1; // wrap around
841
842 sp->sn_name = vim_strsave(name);
843 if (sp->sn_name == NULL) // out of memory
844 {
845 vim_free(sp);
846 return NULL;
847 }
848
849 return sp;
850}
851
852/*
853 * Initialize the icon information for a new sign
854 */
855 static void
856sign_define_init_icon(sign_T *sp, char_u *icon)
857{
858 vim_free(sp->sn_icon);
859 sp->sn_icon = vim_strsave(icon);
860 backslash_halve(sp->sn_icon);
861# ifdef FEAT_SIGN_ICONS
862 if (gui.in_use)
863 {
864 out_flush();
865 if (sp->sn_image != NULL)
866 gui_mch_destroy_sign(sp->sn_image);
867 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
868 }
869# endif
870}
871
872/*
873 * Initialize the text for a new sign
874 */
875 static int
876sign_define_init_text(sign_T *sp, char_u *text)
877{
878 char_u *s;
879 char_u *endp;
880 int cells;
881 int len;
882
883 endp = text + (int)STRLEN(text);
884
885 // Remove backslashes so that it is possible to use a space.
886 for (s = text; s + 1 < endp; ++s)
887 if (*s == '\\')
888 {
889 STRMOVE(s, s + 1);
890 --endp;
891 }
892
893 // Count cells and check for non-printable chars
Bram Moolenaar03142362019-01-18 22:01:42 +0100894 if (has_mbyte)
895 {
896 cells = 0;
897 for (s = text; s < endp; s += (*mb_ptr2len)(s))
898 {
899 if (!vim_isprintc((*mb_ptr2char)(s)))
900 break;
901 cells += (*mb_ptr2cells)(s);
902 }
903 }
904 else
Bram Moolenaar03142362019-01-18 22:01:42 +0100905 {
906 for (s = text; s < endp; ++s)
907 if (!vim_isprintc(*s))
908 break;
909 cells = (int)(s - text);
910 }
911
912 // Currently sign text must be one or two display cells
913 if (s != endp || cells < 1 || cells > 2)
914 {
915 semsg(_("E239: Invalid sign text: %s"), text);
916 return FAIL;
917 }
918
919 vim_free(sp->sn_text);
920 // Allocate one byte more if we need to pad up
921 // with a space.
922 len = (int)(endp - text + ((cells == 1) ? 1 : 0));
923 sp->sn_text = vim_strnsave(text, len);
924
925 // For single character sign text, pad with a space.
926 if (sp->sn_text != NULL && cells == 1)
927 STRCPY(sp->sn_text + len - 1, " ");
928
929 return OK;
930}
931
932/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100933 * Define a new sign or update an existing sign
934 */
935 int
936sign_define_by_name(
937 char_u *name,
938 char_u *icon,
939 char_u *linehl,
940 char_u *text,
941 char_u *texthl)
942{
943 sign_T *sp_prev;
944 sign_T *sp;
945
946 sp = sign_find(name, &sp_prev);
947 if (sp == NULL)
948 {
Bram Moolenaar03142362019-01-18 22:01:42 +0100949 sp = alloc_new_sign(name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100950 if (sp == NULL)
951 return FAIL;
952
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100953 // add the new sign to the list of signs
954 if (sp_prev == NULL)
955 first_sign = sp;
956 else
957 sp_prev->sn_next = sp;
958 }
959
960 // set values for a defined sign.
961 if (icon != NULL)
Bram Moolenaar03142362019-01-18 22:01:42 +0100962 sign_define_init_icon(sp, icon);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100963
Bram Moolenaar03142362019-01-18 22:01:42 +0100964 if (text != NULL && (sign_define_init_text(sp, text) == FAIL))
965 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100966
967 if (linehl != NULL)
968 sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl));
969
970 if (texthl != NULL)
971 sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl));
972
973 return OK;
974}
975
976/*
977 * Free the sign specified by 'name'.
978 */
979 int
980sign_undefine_by_name(char_u *name)
981{
982 sign_T *sp_prev;
983 sign_T *sp;
984
985 sp = sign_find(name, &sp_prev);
986 if (sp == NULL)
987 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100988 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100989 return FAIL;
990 }
991 sign_undefine(sp, sp_prev);
992
993 return OK;
994}
995
996/*
997 * List the signs matching 'name'
998 */
999 static void
1000sign_list_by_name(char_u *name)
1001{
1002 sign_T *sp;
1003
1004 sp = sign_find(name, NULL);
1005 if (sp != NULL)
1006 sign_list_defined(sp);
1007 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001008 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001009}
1010
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001011 static void
1012may_force_numberwidth_recompute(buf_T *buf, int unplace)
1013{
1014 tabpage_T *tp;
1015 win_T *wp;
1016
1017 FOR_ALL_TAB_WINDOWS(tp, wp)
1018 if (wp->w_buffer == buf
1019 && (wp->w_p_nu || wp->w_p_rnu)
1020 && (unplace || wp->w_nrwidth_width < 2)
1021 && (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u'))
1022 wp->w_nrwidth_line_count = 0;
1023}
1024
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001025/*
Bram Moolenaar8144acb2019-01-14 23:08:18 +01001026 * Place a sign at the specified file location or update a sign.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001027 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001028 static int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001029sign_place(
1030 int *sign_id,
1031 char_u *sign_group,
1032 char_u *sign_name,
1033 buf_T *buf,
1034 linenr_T lnum,
1035 int prio)
1036{
1037 sign_T *sp;
1038
1039 // Check for reserved character '*' in group name
1040 if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0'))
1041 return FAIL;
1042
1043 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1044 if (STRCMP(sp->sn_name, sign_name) == 0)
1045 break;
1046 if (sp == NULL)
1047 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001048 semsg(_("E155: Unknown sign: %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001049 return FAIL;
1050 }
1051 if (*sign_id == 0)
1052 *sign_id = sign_group_get_next_signid(buf, sign_group);
1053
1054 if (lnum > 0)
1055 // ":sign place {id} line={lnum} name={name} file={fname}":
1056 // place a sign
1057 buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr);
1058 else
1059 // ":sign place {id} file={fname}": change sign type
1060 lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr);
1061 if (lnum > 0)
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001062 {
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001063 redraw_buf_line_later(buf, lnum);
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001064
1065 // When displaying signs in the 'number' column, if the width of the
1066 // number column is less than 2, then force recomputing the width.
1067 may_force_numberwidth_recompute(buf, FALSE);
1068 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001069 else
1070 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001071 semsg(_("E885: Not possible to change sign %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001072 return FAIL;
1073 }
1074
1075 return OK;
1076}
1077
1078/*
1079 * Unplace the specified sign
1080 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001081 static int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001082sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum)
1083{
1084 if (buf->b_signlist == NULL) // No signs in the buffer
1085 return OK;
1086
1087 if (sign_id == 0)
1088 {
1089 // Delete all the signs in the specified buffer
1090 redraw_buf_later(buf, NOT_VALID);
1091 buf_delete_signs(buf, sign_group);
1092 }
1093 else
1094 {
1095 linenr_T lnum;
1096
1097 // Delete only the specified signs
1098 lnum = buf_delsign(buf, atlnum, sign_id, sign_group);
1099 if (lnum == 0)
1100 return FAIL;
1101 }
1102
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001103 // When all the signs in a buffer are removed, force recomputing the
1104 // number column width (if enabled) in all the windows displaying the
1105 // buffer if 'signcolumn' is set to 'number' in that window.
1106 if (buf->b_signlist == NULL)
1107 may_force_numberwidth_recompute(buf, TRUE);
1108
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001109 return OK;
1110}
1111
1112/*
1113 * Unplace the sign at the current cursor line.
1114 */
1115 static void
1116sign_unplace_at_cursor(char_u *groupname)
1117{
1118 int id = -1;
1119
1120 id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname);
1121 if (id > 0)
1122 sign_unplace(id, groupname, curwin->w_buffer, curwin->w_cursor.lnum);
1123 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001124 emsg(_("E159: Missing sign number"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001125}
1126
1127/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001128 * Jump to a sign.
1129 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001130 static linenr_T
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001131sign_jump(int sign_id, char_u *sign_group, buf_T *buf)
1132{
1133 linenr_T lnum;
1134
1135 if ((lnum = buf_findsign(buf, sign_id, sign_group)) <= 0)
1136 {
Bram Moolenaarb5443cc2019-01-15 20:19:40 +01001137 semsg(_("E157: Invalid sign ID: %d"), sign_id);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001138 return -1;
1139 }
1140
1141 // goto a sign ...
1142 if (buf_jump_open_win(buf) != NULL)
1143 { // ... in a current window
1144 curwin->w_cursor.lnum = lnum;
1145 check_cursor_lnum();
1146 beginline(BL_WHITE);
1147 }
1148 else
1149 { // ... not currently in a window
1150 char_u *cmd;
1151
1152 if (buf->b_fname == NULL)
1153 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001154 emsg(_("E934: Cannot jump to a buffer that does not have a name"));
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001155 return -1;
1156 }
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02001157 cmd = alloc(STRLEN(buf->b_fname) + 25);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001158 if (cmd == NULL)
1159 return -1;
1160 sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname);
1161 do_cmdline_cmd(cmd);
1162 vim_free(cmd);
1163 }
1164# ifdef FEAT_FOLDING
1165 foldOpenCursor();
1166# endif
1167
1168 return lnum;
1169}
1170
1171/*
1172 * ":sign define {name} ..." command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001173 */
1174 static void
1175sign_define_cmd(char_u *sign_name, char_u *cmdline)
1176{
1177 char_u *arg;
1178 char_u *p = cmdline;
1179 char_u *icon = NULL;
1180 char_u *text = NULL;
1181 char_u *linehl = NULL;
1182 char_u *texthl = NULL;
1183 int failed = FALSE;
1184
1185 // set values for a defined sign.
1186 for (;;)
1187 {
1188 arg = skipwhite(p);
1189 if (*arg == NUL)
1190 break;
1191 p = skiptowhite_esc(arg);
1192 if (STRNCMP(arg, "icon=", 5) == 0)
1193 {
1194 arg += 5;
1195 icon = vim_strnsave(arg, (int)(p - arg));
1196 }
1197 else if (STRNCMP(arg, "text=", 5) == 0)
1198 {
1199 arg += 5;
1200 text = vim_strnsave(arg, (int)(p - arg));
1201 }
1202 else if (STRNCMP(arg, "linehl=", 7) == 0)
1203 {
1204 arg += 7;
1205 linehl = vim_strnsave(arg, (int)(p - arg));
1206 }
1207 else if (STRNCMP(arg, "texthl=", 7) == 0)
1208 {
1209 arg += 7;
1210 texthl = vim_strnsave(arg, (int)(p - arg));
1211 }
1212 else
1213 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001214 semsg(_(e_invarg2), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001215 failed = TRUE;
1216 break;
1217 }
1218 }
1219
1220 if (!failed)
1221 sign_define_by_name(sign_name, icon, linehl, text, texthl);
1222
1223 vim_free(icon);
1224 vim_free(text);
1225 vim_free(linehl);
1226 vim_free(texthl);
1227}
1228
1229/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001230 * ":sign place" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001231 */
1232 static void
1233sign_place_cmd(
1234 buf_T *buf,
1235 linenr_T lnum,
1236 char_u *sign_name,
1237 int id,
1238 char_u *group,
1239 int prio)
1240{
1241 if (id <= 0)
1242 {
1243 // List signs placed in a file/buffer
1244 // :sign place file={fname}
1245 // :sign place group={group} file={fname}
1246 // :sign place group=* file={fname}
1247 // :sign place buffer={nr}
1248 // :sign place group={group} buffer={nr}
1249 // :sign place group=* buffer={nr}
1250 // :sign place
1251 // :sign place group={group}
1252 // :sign place group=*
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001253 if (lnum >= 0 || sign_name != NULL
1254 || (group != NULL && *group == '\0'))
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001255 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001256 else
1257 sign_list_placed(buf, group);
1258 }
1259 else
1260 {
1261 // Place a new sign
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001262 if (sign_name == NULL || buf == NULL
1263 || (group != NULL && *group == '\0'))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001264 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001265 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001266 return;
1267 }
1268
1269 sign_place(&id, group, sign_name, buf, lnum, prio);
1270 }
1271}
1272
1273/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001274 * ":sign unplace" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001275 */
1276 static void
1277sign_unplace_cmd(
1278 buf_T *buf,
1279 linenr_T lnum,
1280 char_u *sign_name,
1281 int id,
1282 char_u *group)
1283{
1284 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0'))
1285 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001286 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001287 return;
1288 }
1289
1290 if (id == -2)
1291 {
1292 if (buf != NULL)
1293 // :sign unplace * file={fname}
1294 // :sign unplace * group={group} file={fname}
1295 // :sign unplace * group=* file={fname}
1296 // :sign unplace * buffer={nr}
1297 // :sign unplace * group={group} buffer={nr}
1298 // :sign unplace * group=* buffer={nr}
1299 sign_unplace(0, group, buf, 0);
1300 else
1301 // :sign unplace *
1302 // :sign unplace * group={group}
1303 // :sign unplace * group=*
1304 FOR_ALL_BUFFERS(buf)
1305 if (buf->b_signlist != NULL)
1306 buf_delete_signs(buf, group);
1307 }
1308 else
1309 {
1310 if (buf != NULL)
1311 // :sign unplace {id} file={fname}
1312 // :sign unplace {id} group={group} file={fname}
1313 // :sign unplace {id} group=* file={fname}
1314 // :sign unplace {id} buffer={nr}
1315 // :sign unplace {id} group={group} buffer={nr}
1316 // :sign unplace {id} group=* buffer={nr}
1317 sign_unplace(id, group, buf, 0);
1318 else
1319 {
1320 if (id == -1)
1321 {
1322 // :sign unplace group={group}
1323 // :sign unplace group=*
1324 sign_unplace_at_cursor(group);
1325 }
1326 else
1327 {
1328 // :sign unplace {id}
1329 // :sign unplace {id} group={group}
1330 // :sign unplace {id} group=*
1331 FOR_ALL_BUFFERS(buf)
1332 sign_unplace(id, group, buf, 0);
1333 }
1334 }
1335 }
1336}
1337
1338/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001339 * Jump to a placed sign commands:
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001340 * :sign jump {id} file={fname}
1341 * :sign jump {id} buffer={nr}
1342 * :sign jump {id} group={group} file={fname}
1343 * :sign jump {id} group={group} buffer={nr}
1344 */
1345 static void
1346sign_jump_cmd(
1347 buf_T *buf,
1348 linenr_T lnum,
1349 char_u *sign_name,
1350 int id,
1351 char_u *group)
1352{
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001353 if (sign_name == NULL && group == NULL && id == -1)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001354 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001355 emsg(_(e_argreq));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001356 return;
1357 }
1358
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001359 if (buf == NULL || (group != NULL && *group == '\0')
1360 || lnum >= 0 || sign_name != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001361 {
1362 // File or buffer is not specified or an empty group is used
1363 // or a line number or a sign name is specified.
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001364 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001365 return;
1366 }
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001367 (void)sign_jump(id, group, buf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001368}
1369
1370/*
1371 * Parse the command line arguments for the ":sign place", ":sign unplace" and
1372 * ":sign jump" commands.
1373 * The supported arguments are: line={lnum} name={name} group={group}
1374 * priority={prio} and file={fname} or buffer={nr}.
1375 */
1376 static int
1377parse_sign_cmd_args(
1378 int cmd,
1379 char_u *arg,
1380 char_u **sign_name,
1381 int *signid,
1382 char_u **group,
1383 int *prio,
1384 buf_T **buf,
1385 linenr_T *lnum)
1386{
1387 char_u *arg1;
1388 char_u *name;
1389 char_u *filename = NULL;
Bram Moolenaarb589f952019-01-07 22:10:00 +01001390 int lnum_arg = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001391
1392 // first arg could be placed sign id
1393 arg1 = arg;
1394 if (VIM_ISDIGIT(*arg))
1395 {
1396 *signid = getdigits(&arg);
1397 if (!VIM_ISWHITE(*arg) && *arg != NUL)
1398 {
1399 *signid = -1;
1400 arg = arg1;
1401 }
1402 else
1403 arg = skipwhite(arg);
1404 }
1405
1406 while (*arg != NUL)
1407 {
1408 if (STRNCMP(arg, "line=", 5) == 0)
1409 {
1410 arg += 5;
1411 *lnum = atoi((char *)arg);
1412 arg = skiptowhite(arg);
Bram Moolenaarb589f952019-01-07 22:10:00 +01001413 lnum_arg = TRUE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001414 }
1415 else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE)
1416 {
1417 if (*signid != -1)
1418 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001419 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001420 return FAIL;
1421 }
1422 *signid = -2;
1423 arg = skiptowhite(arg + 1);
1424 }
1425 else if (STRNCMP(arg, "name=", 5) == 0)
1426 {
1427 arg += 5;
1428 name = arg;
1429 arg = skiptowhite(arg);
1430 if (*arg != NUL)
1431 *arg++ = NUL;
1432 while (name[0] == '0' && name[1] != NUL)
1433 ++name;
1434 *sign_name = name;
1435 }
1436 else if (STRNCMP(arg, "group=", 6) == 0)
1437 {
1438 arg += 6;
1439 *group = arg;
1440 arg = skiptowhite(arg);
1441 if (*arg != NUL)
1442 *arg++ = NUL;
1443 }
1444 else if (STRNCMP(arg, "priority=", 9) == 0)
1445 {
1446 arg += 9;
1447 *prio = atoi((char *)arg);
1448 arg = skiptowhite(arg);
1449 }
1450 else if (STRNCMP(arg, "file=", 5) == 0)
1451 {
1452 arg += 5;
1453 filename = arg;
1454 *buf = buflist_findname_exp(arg);
1455 break;
1456 }
1457 else if (STRNCMP(arg, "buffer=", 7) == 0)
1458 {
1459 arg += 7;
1460 filename = arg;
1461 *buf = buflist_findnr((int)getdigits(&arg));
1462 if (*skipwhite(arg) != NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001463 emsg(_(e_trailing));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001464 break;
1465 }
1466 else
1467 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001468 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001469 return FAIL;
1470 }
1471 arg = skipwhite(arg);
1472 }
1473
1474 if (filename != NULL && *buf == NULL)
1475 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001476 semsg(_("E158: Invalid buffer name: %s"), filename);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001477 return FAIL;
1478 }
1479
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001480 // If the filename is not supplied for the sign place or the sign jump
1481 // command, then use the current buffer.
Bram Moolenaarb589f952019-01-07 22:10:00 +01001482 if (filename == NULL && ((cmd == SIGNCMD_PLACE && lnum_arg)
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001483 || cmd == SIGNCMD_JUMP))
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001484 *buf = curwin->w_buffer;
1485
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001486 return OK;
1487}
1488
1489/*
1490 * ":sign" command
1491 */
1492 void
1493ex_sign(exarg_T *eap)
1494{
1495 char_u *arg = eap->arg;
1496 char_u *p;
1497 int idx;
1498 sign_T *sp;
1499 buf_T *buf = NULL;
1500
1501 // Parse the subcommand.
1502 p = skiptowhite(arg);
1503 idx = sign_cmd_idx(arg, p);
1504 if (idx == SIGNCMD_LAST)
1505 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001506 semsg(_("E160: Unknown sign command: %s"), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001507 return;
1508 }
1509 arg = skipwhite(p);
1510
1511 if (idx <= SIGNCMD_LIST)
1512 {
1513 // Define, undefine or list signs.
1514 if (idx == SIGNCMD_LIST && *arg == NUL)
1515 {
1516 // ":sign list": list all defined signs
1517 for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next)
1518 sign_list_defined(sp);
1519 }
1520 else if (*arg == NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001521 emsg(_("E156: Missing sign name"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001522 else
1523 {
1524 char_u *name;
1525
1526 // Isolate the sign name. If it's a number skip leading zeroes,
1527 // so that "099" and "99" are the same sign. But keep "0".
1528 p = skiptowhite(arg);
1529 if (*p != NUL)
1530 *p++ = NUL;
1531 while (arg[0] == '0' && arg[1] != NUL)
1532 ++arg;
1533 name = vim_strsave(arg);
1534
1535 if (idx == SIGNCMD_DEFINE)
1536 sign_define_cmd(name, p);
1537 else if (idx == SIGNCMD_LIST)
1538 // ":sign list {name}"
1539 sign_list_by_name(name);
1540 else
1541 // ":sign undefine {name}"
1542 sign_undefine_by_name(name);
1543
1544 vim_free(name);
1545 return;
1546 }
1547 }
1548 else
1549 {
1550 int id = -1;
1551 linenr_T lnum = -1;
1552 char_u *sign_name = NULL;
1553 char_u *group = NULL;
1554 int prio = SIGN_DEF_PRIO;
1555
1556 // Parse command line arguments
1557 if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio,
1558 &buf, &lnum) == FAIL)
1559 return;
1560
1561 if (idx == SIGNCMD_PLACE)
1562 sign_place_cmd(buf, lnum, sign_name, id, group, prio);
1563 else if (idx == SIGNCMD_UNPLACE)
1564 sign_unplace_cmd(buf, lnum, sign_name, id, group);
1565 else if (idx == SIGNCMD_JUMP)
1566 sign_jump_cmd(buf, lnum, sign_name, id, group);
1567 }
1568}
1569
1570/*
1571 * Return information about a specified sign
1572 */
1573 static void
1574sign_getinfo(sign_T *sp, dict_T *retdict)
1575{
1576 char_u *p;
1577
1578 dict_add_string(retdict, "name", (char_u *)sp->sn_name);
1579 if (sp->sn_icon != NULL)
1580 dict_add_string(retdict, "icon", (char_u *)sp->sn_icon);
1581 if (sp->sn_text != NULL)
1582 dict_add_string(retdict, "text", (char_u *)sp->sn_text);
1583 if (sp->sn_line_hl > 0)
1584 {
1585 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1586 if (p == NULL)
1587 p = (char_u *)"NONE";
1588 dict_add_string(retdict, "linehl", (char_u *)p);
1589 }
1590 if (sp->sn_text_hl > 0)
1591 {
1592 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1593 if (p == NULL)
1594 p = (char_u *)"NONE";
1595 dict_add_string(retdict, "texthl", (char_u *)p);
1596 }
1597}
1598
1599/*
1600 * If 'name' is NULL, return a list of all the defined signs.
1601 * Otherwise, return information about the specified sign.
1602 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001603 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001604sign_getlist(char_u *name, list_T *retlist)
1605{
1606 sign_T *sp = first_sign;
1607 dict_T *dict;
1608
1609 if (name != NULL)
1610 {
1611 sp = sign_find(name, NULL);
1612 if (sp == NULL)
1613 return;
1614 }
1615
1616 for (; sp != NULL && !got_int; sp = sp->sn_next)
1617 {
1618 if ((dict = dict_alloc_id(aid_sign_getlist)) == NULL)
1619 return;
1620 if (list_append_dict(retlist, dict) == FAIL)
1621 return;
1622 sign_getinfo(sp, dict);
1623
1624 if (name != NULL) // handle only the specified sign
1625 break;
1626 }
1627}
1628
1629/*
1630 * Returns information about signs placed in a buffer as list of dicts.
1631 */
1632 void
1633get_buffer_signs(buf_T *buf, list_T *l)
1634{
1635 signlist_T *sign;
1636 dict_T *d;
1637
1638 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1639 {
1640 if ((d = sign_get_info(sign)) != NULL)
1641 list_append_dict(l, d);
1642 }
1643}
1644
1645/*
1646 * Return information about all the signs placed in a buffer
1647 */
1648 static void
1649sign_get_placed_in_buf(
1650 buf_T *buf,
1651 linenr_T lnum,
1652 int sign_id,
1653 char_u *sign_group,
1654 list_T *retlist)
1655{
1656 dict_T *d;
1657 list_T *l;
1658 signlist_T *sign;
1659 dict_T *sdict;
1660
1661 if ((d = dict_alloc_id(aid_sign_getplaced_dict)) == NULL)
1662 return;
1663 list_append_dict(retlist, d);
1664
1665 dict_add_number(d, "bufnr", (long)buf->b_fnum);
1666
1667 if ((l = list_alloc_id(aid_sign_getplaced_list)) == NULL)
1668 return;
1669 dict_add_list(d, "signs", l);
1670
1671 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1672 {
1673 if (!sign_in_group(sign, sign_group))
1674 continue;
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001675 if ((lnum == 0 && sign_id == 0)
1676 || (sign_id == 0 && lnum == sign->lnum)
1677 || (lnum == 0 && sign_id == sign->id)
1678 || (lnum == sign->lnum && sign_id == sign->id))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001679 {
1680 if ((sdict = sign_get_info(sign)) != NULL)
1681 list_append_dict(l, sdict);
1682 }
1683 }
1684}
1685
1686/*
1687 * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the
1688 * sign placed at the line number. If 'lnum' is zero, return all the signs
1689 * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers.
1690 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001691 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001692sign_get_placed(
1693 buf_T *buf,
1694 linenr_T lnum,
1695 int sign_id,
1696 char_u *sign_group,
1697 list_T *retlist)
1698{
1699 if (buf != NULL)
1700 sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist);
1701 else
1702 {
1703 FOR_ALL_BUFFERS(buf)
1704 {
1705 if (buf->b_signlist != NULL)
1706 sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist);
1707 }
1708 }
1709}
1710
1711# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1712/*
1713 * Allocate the icons. Called when the GUI has started. Allows defining
1714 * signs before it starts.
1715 */
1716 void
1717sign_gui_started(void)
1718{
1719 sign_T *sp;
1720
1721 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1722 if (sp->sn_icon != NULL)
1723 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
1724}
1725# endif
1726
1727/*
1728 * List one sign.
1729 */
1730 static void
1731sign_list_defined(sign_T *sp)
1732{
1733 char_u *p;
1734
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001735 smsg("sign %s", sp->sn_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001736 if (sp->sn_icon != NULL)
1737 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001738 msg_puts(" icon=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001739 msg_outtrans(sp->sn_icon);
1740# ifdef FEAT_SIGN_ICONS
1741 if (sp->sn_image == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001742 msg_puts(_(" (NOT FOUND)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001743# else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001744 msg_puts(_(" (not supported)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001745# endif
1746 }
1747 if (sp->sn_text != NULL)
1748 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001749 msg_puts(" text=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001750 msg_outtrans(sp->sn_text);
1751 }
1752 if (sp->sn_line_hl > 0)
1753 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001754 msg_puts(" linehl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001755 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1756 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001757 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001758 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001759 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001760 }
1761 if (sp->sn_text_hl > 0)
1762 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001763 msg_puts(" texthl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001764 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1765 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001766 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001767 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001768 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001769 }
1770}
1771
1772/*
1773 * Undefine a sign and free its memory.
1774 */
1775 static void
1776sign_undefine(sign_T *sp, sign_T *sp_prev)
1777{
1778 vim_free(sp->sn_name);
1779 vim_free(sp->sn_icon);
1780# ifdef FEAT_SIGN_ICONS
1781 if (sp->sn_image != NULL)
1782 {
1783 out_flush();
1784 gui_mch_destroy_sign(sp->sn_image);
1785 }
1786# endif
1787 vim_free(sp->sn_text);
1788 if (sp_prev == NULL)
1789 first_sign = sp->sn_next;
1790 else
1791 sp_prev->sn_next = sp->sn_next;
1792 vim_free(sp);
1793}
1794
1795/*
1796 * Get highlighting attribute for sign "typenr".
1797 * If "line" is TRUE: line highl, if FALSE: text highl.
1798 */
1799 int
1800sign_get_attr(int typenr, int line)
1801{
1802 sign_T *sp;
1803
1804 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1805 if (sp->sn_typenr == typenr)
1806 {
1807 if (line)
1808 {
1809 if (sp->sn_line_hl > 0)
1810 return syn_id2attr(sp->sn_line_hl);
1811 }
1812 else
1813 {
1814 if (sp->sn_text_hl > 0)
1815 return syn_id2attr(sp->sn_text_hl);
1816 }
1817 break;
1818 }
1819 return 0;
1820}
1821
1822/*
1823 * Get text mark for sign "typenr".
1824 * Returns NULL if there isn't one.
1825 */
1826 char_u *
1827sign_get_text(int typenr)
1828{
1829 sign_T *sp;
1830
1831 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1832 if (sp->sn_typenr == typenr)
1833 return sp->sn_text;
1834 return NULL;
1835}
1836
1837# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1838 void *
1839sign_get_image(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001840 int typenr) // the attribute which may have a sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001841{
1842 sign_T *sp;
1843
1844 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1845 if (sp->sn_typenr == typenr)
1846 return sp->sn_image;
1847 return NULL;
1848}
1849# endif
1850
1851/*
1852 * Undefine/free all signs.
1853 */
1854 void
1855free_signs(void)
1856{
1857 while (first_sign != NULL)
1858 sign_undefine(first_sign, NULL);
1859}
1860
1861# if defined(FEAT_CMDL_COMPL) || defined(PROTO)
1862static enum
1863{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001864 EXP_SUBCMD, // expand :sign sub-commands
1865 EXP_DEFINE, // expand :sign define {name} args
1866 EXP_PLACE, // expand :sign place {id} args
Bram Moolenaar3678f652019-02-17 14:50:25 +01001867 EXP_LIST, // expand :sign place args
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001868 EXP_UNPLACE, // expand :sign unplace"
Bram Moolenaar3678f652019-02-17 14:50:25 +01001869 EXP_SIGN_NAMES, // expand with name of placed signs
1870 EXP_SIGN_GROUPS // expand with name of placed sign groups
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001871} expand_what;
1872
1873/*
Bram Moolenaar3678f652019-02-17 14:50:25 +01001874 * Return the n'th sign name (used for command line completion)
1875 */
1876 static char_u *
1877get_nth_sign_name(int idx)
1878{
1879 int current_idx;
1880 sign_T *sp;
1881
1882 // Complete with name of signs already defined
1883 current_idx = 0;
1884 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1885 if (current_idx++ == idx)
1886 return sp->sn_name;
1887 return NULL;
1888}
1889
1890/*
1891 * Return the n'th sign group name (used for command line completion)
1892 */
1893 static char_u *
1894get_nth_sign_group_name(int idx)
1895{
1896 int current_idx;
1897 int todo;
1898 hashitem_T *hi;
1899 signgroup_T *group;
1900
1901 // Complete with name of sign groups already defined
1902 current_idx = 0;
1903 todo = (int)sg_table.ht_used;
1904 for (hi = sg_table.ht_array; todo > 0; ++hi)
1905 {
1906 if (!HASHITEM_EMPTY(hi))
1907 {
1908 --todo;
1909 if (current_idx++ == idx)
1910 {
1911 group = HI2SG(hi);
1912 return group->sg_name;
1913 }
1914 }
1915 }
1916 return NULL;
1917}
1918
1919/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001920 * Function given to ExpandGeneric() to obtain the sign command
1921 * expansion.
1922 */
1923 char_u *
1924get_sign_name(expand_T *xp UNUSED, int idx)
1925{
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001926 switch (expand_what)
1927 {
1928 case EXP_SUBCMD:
1929 return (char_u *)cmds[idx];
1930 case EXP_DEFINE:
1931 {
1932 char *define_arg[] =
1933 {
1934 "icon=", "linehl=", "text=", "texthl=", NULL
1935 };
1936 return (char_u *)define_arg[idx];
1937 }
1938 case EXP_PLACE:
1939 {
1940 char *place_arg[] =
1941 {
1942 "line=", "name=", "group=", "priority=", "file=",
1943 "buffer=", NULL
1944 };
1945 return (char_u *)place_arg[idx];
1946 }
Bram Moolenaar3678f652019-02-17 14:50:25 +01001947 case EXP_LIST:
1948 {
1949 char *list_arg[] =
1950 {
1951 "group=", "file=", "buffer=", NULL
1952 };
1953 return (char_u *)list_arg[idx];
1954 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001955 case EXP_UNPLACE:
1956 {
1957 char *unplace_arg[] = { "group=", "file=", "buffer=", NULL };
1958 return (char_u *)unplace_arg[idx];
1959 }
1960 case EXP_SIGN_NAMES:
Bram Moolenaar3678f652019-02-17 14:50:25 +01001961 return get_nth_sign_name(idx);
1962 case EXP_SIGN_GROUPS:
1963 return get_nth_sign_group_name(idx);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001964 default:
1965 return NULL;
1966 }
1967}
1968
1969/*
1970 * Handle command line completion for :sign command.
1971 */
1972 void
1973set_context_in_sign_cmd(expand_T *xp, char_u *arg)
1974{
1975 char_u *p;
1976 char_u *end_subcmd;
1977 char_u *last;
1978 int cmd_idx;
1979 char_u *begin_subcmd_args;
1980
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001981 // Default: expand subcommands.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001982 xp->xp_context = EXPAND_SIGN;
1983 expand_what = EXP_SUBCMD;
1984 xp->xp_pattern = arg;
1985
1986 end_subcmd = skiptowhite(arg);
1987 if (*end_subcmd == NUL)
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001988 // expand subcmd name
1989 // :sign {subcmd}<CTRL-D>
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001990 return;
1991
1992 cmd_idx = sign_cmd_idx(arg, end_subcmd);
1993
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001994 // :sign {subcmd} {subcmd_args}
1995 // |
1996 // begin_subcmd_args
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001997 begin_subcmd_args = skipwhite(end_subcmd);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001998
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001999 // expand last argument of subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002000
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002001 // :sign define {name} {args}...
2002 // |
2003 // p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002004
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002005 // Loop until reaching last argument.
Bram Moolenaar3678f652019-02-17 14:50:25 +01002006 p = begin_subcmd_args;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002007 do
2008 {
2009 p = skipwhite(p);
2010 last = p;
2011 p = skiptowhite(p);
2012 } while (*p != NUL);
2013
2014 p = vim_strchr(last, '=');
2015
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002016 // :sign define {name} {args}... {last}=
2017 // | |
2018 // last p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002019 if (p == NULL)
2020 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002021 // Expand last argument name (before equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002022 xp->xp_pattern = last;
2023 switch (cmd_idx)
2024 {
2025 case SIGNCMD_DEFINE:
2026 expand_what = EXP_DEFINE;
2027 break;
2028 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002029 // List placed signs
2030 if (VIM_ISDIGIT(*begin_subcmd_args))
2031 // :sign place {id} {args}...
2032 expand_what = EXP_PLACE;
2033 else
2034 // :sign place {args}...
2035 expand_what = EXP_LIST;
2036 break;
2037 case SIGNCMD_LIST:
2038 case SIGNCMD_UNDEFINE:
2039 // :sign list <CTRL-D>
2040 // :sign undefine <CTRL-D>
2041 expand_what = EXP_SIGN_NAMES;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002042 break;
2043 case SIGNCMD_JUMP:
2044 case SIGNCMD_UNPLACE:
2045 expand_what = EXP_UNPLACE;
2046 break;
2047 default:
2048 xp->xp_context = EXPAND_NOTHING;
2049 }
2050 }
2051 else
2052 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002053 // Expand last argument value (after equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002054 xp->xp_pattern = p + 1;
2055 switch (cmd_idx)
2056 {
2057 case SIGNCMD_DEFINE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002058 if (STRNCMP(last, "texthl", 6) == 0
2059 || STRNCMP(last, "linehl", 6) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002060 xp->xp_context = EXPAND_HIGHLIGHT;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002061 else if (STRNCMP(last, "icon", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002062 xp->xp_context = EXPAND_FILES;
2063 else
2064 xp->xp_context = EXPAND_NOTHING;
2065 break;
2066 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002067 if (STRNCMP(last, "name", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002068 expand_what = EXP_SIGN_NAMES;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002069 else if (STRNCMP(last, "group", 5) == 0)
2070 expand_what = EXP_SIGN_GROUPS;
2071 else if (STRNCMP(last, "file", 4) == 0)
2072 xp->xp_context = EXPAND_BUFFERS;
2073 else
2074 xp->xp_context = EXPAND_NOTHING;
2075 break;
2076 case SIGNCMD_UNPLACE:
2077 case SIGNCMD_JUMP:
2078 if (STRNCMP(last, "group", 5) == 0)
2079 expand_what = EXP_SIGN_GROUPS;
2080 else if (STRNCMP(last, "file", 4) == 0)
2081 xp->xp_context = EXPAND_BUFFERS;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002082 else
2083 xp->xp_context = EXPAND_NOTHING;
2084 break;
2085 default:
2086 xp->xp_context = EXPAND_NOTHING;
2087 }
2088 }
2089}
2090# endif
2091
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002092/*
2093 * "sign_define()" function
2094 */
2095 void
2096f_sign_define(typval_T *argvars, typval_T *rettv)
2097{
2098 char_u *name;
2099 dict_T *dict;
2100 char_u *icon = NULL;
2101 char_u *linehl = NULL;
2102 char_u *text = NULL;
2103 char_u *texthl = NULL;
2104
2105 rettv->vval.v_number = -1;
2106
2107 name = tv_get_string_chk(&argvars[0]);
2108 if (name == NULL)
2109 return;
2110
2111 if (argvars[1].v_type != VAR_UNKNOWN)
2112 {
2113 if (argvars[1].v_type != VAR_DICT)
2114 {
2115 emsg(_(e_dictreq));
2116 return;
2117 }
2118
2119 // sign attributes
2120 dict = argvars[1].vval.v_dict;
2121 if (dict_find(dict, (char_u *)"icon", -1) != NULL)
2122 icon = dict_get_string(dict, (char_u *)"icon", TRUE);
2123 if (dict_find(dict, (char_u *)"linehl", -1) != NULL)
2124 linehl = dict_get_string(dict, (char_u *)"linehl", TRUE);
2125 if (dict_find(dict, (char_u *)"text", -1) != NULL)
2126 text = dict_get_string(dict, (char_u *)"text", TRUE);
2127 if (dict_find(dict, (char_u *)"texthl", -1) != NULL)
2128 texthl = dict_get_string(dict, (char_u *)"texthl", TRUE);
2129 }
2130
2131 if (sign_define_by_name(name, icon, linehl, text, texthl) == OK)
2132 rettv->vval.v_number = 0;
2133
2134 vim_free(icon);
2135 vim_free(linehl);
2136 vim_free(text);
2137 vim_free(texthl);
2138}
2139
2140/*
2141 * "sign_getdefined()" function
2142 */
2143 void
2144f_sign_getdefined(typval_T *argvars, typval_T *rettv)
2145{
2146 char_u *name = NULL;
2147
2148 if (rettv_list_alloc_id(rettv, aid_sign_getdefined) != OK)
2149 return;
2150
2151 if (argvars[0].v_type != VAR_UNKNOWN)
2152 name = tv_get_string(&argvars[0]);
2153
2154 sign_getlist(name, rettv->vval.v_list);
2155}
2156
2157/*
2158 * "sign_getplaced()" function
2159 */
2160 void
2161f_sign_getplaced(typval_T *argvars, typval_T *rettv)
2162{
2163 buf_T *buf = NULL;
2164 dict_T *dict;
2165 dictitem_T *di;
2166 linenr_T lnum = 0;
2167 int sign_id = 0;
2168 char_u *group = NULL;
2169 int notanum = FALSE;
2170
2171 if (rettv_list_alloc_id(rettv, aid_sign_getplaced) != OK)
2172 return;
2173
2174 if (argvars[0].v_type != VAR_UNKNOWN)
2175 {
2176 // get signs placed in the specified buffer
2177 buf = get_buf_arg(&argvars[0]);
2178 if (buf == NULL)
2179 return;
2180
2181 if (argvars[1].v_type != VAR_UNKNOWN)
2182 {
2183 if (argvars[1].v_type != VAR_DICT ||
2184 ((dict = argvars[1].vval.v_dict) == NULL))
2185 {
2186 emsg(_(e_dictreq));
2187 return;
2188 }
2189 if ((di = dict_find(dict, (char_u *)"lnum", -1)) != NULL)
2190 {
2191 // get signs placed at this line
2192 (void)tv_get_number_chk(&di->di_tv, &notanum);
2193 if (notanum)
2194 return;
2195 lnum = tv_get_lnum(&di->di_tv);
2196 }
2197 if ((di = dict_find(dict, (char_u *)"id", -1)) != NULL)
2198 {
2199 // get sign placed with this identifier
2200 sign_id = (int)tv_get_number_chk(&di->di_tv, &notanum);
2201 if (notanum)
2202 return;
2203 }
2204 if ((di = dict_find(dict, (char_u *)"group", -1)) != NULL)
2205 {
2206 group = tv_get_string_chk(&di->di_tv);
2207 if (group == NULL)
2208 return;
2209 if (*group == '\0') // empty string means global group
2210 group = NULL;
2211 }
2212 }
2213 }
2214
2215 sign_get_placed(buf, lnum, sign_id, group, rettv->vval.v_list);
2216}
2217
2218/*
2219 * "sign_jump()" function
2220 */
2221 void
2222f_sign_jump(typval_T *argvars, typval_T *rettv)
2223{
2224 int sign_id;
2225 char_u *sign_group = NULL;
2226 buf_T *buf;
2227 int notanum = FALSE;
2228
2229 rettv->vval.v_number = -1;
2230
2231 // Sign identifier
2232 sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
2233 if (notanum)
2234 return;
2235 if (sign_id <= 0)
2236 {
2237 emsg(_(e_invarg));
2238 return;
2239 }
2240
2241 // Sign group
2242 sign_group = tv_get_string_chk(&argvars[1]);
2243 if (sign_group == NULL)
2244 return;
2245 if (sign_group[0] == '\0')
2246 sign_group = NULL; // global sign group
2247 else
2248 {
2249 sign_group = vim_strsave(sign_group);
2250 if (sign_group == NULL)
2251 return;
2252 }
2253
2254 // Buffer to place the sign
2255 buf = get_buf_arg(&argvars[2]);
2256 if (buf == NULL)
2257 goto cleanup;
2258
2259 rettv->vval.v_number = sign_jump(sign_id, sign_group, buf);
2260
2261cleanup:
2262 vim_free(sign_group);
2263}
2264
2265/*
2266 * "sign_place()" function
2267 */
2268 void
2269f_sign_place(typval_T *argvars, typval_T *rettv)
2270{
2271 int sign_id;
2272 char_u *group = NULL;
2273 char_u *sign_name;
2274 buf_T *buf;
2275 dict_T *dict;
2276 dictitem_T *di;
2277 linenr_T lnum = 0;
2278 int prio = SIGN_DEF_PRIO;
2279 int notanum = FALSE;
2280
2281 rettv->vval.v_number = -1;
2282
2283 // Sign identifier
2284 sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
2285 if (notanum)
2286 return;
2287 if (sign_id < 0)
2288 {
2289 emsg(_(e_invarg));
2290 return;
2291 }
2292
2293 // Sign group
2294 group = tv_get_string_chk(&argvars[1]);
2295 if (group == NULL)
2296 return;
2297 if (group[0] == '\0')
2298 group = NULL; // global sign group
2299 else
2300 {
2301 group = vim_strsave(group);
2302 if (group == NULL)
2303 return;
2304 }
2305
2306 // Sign name
2307 sign_name = tv_get_string_chk(&argvars[2]);
2308 if (sign_name == NULL)
2309 goto cleanup;
2310
2311 // Buffer to place the sign
2312 buf = get_buf_arg(&argvars[3]);
2313 if (buf == NULL)
2314 goto cleanup;
2315
2316 if (argvars[4].v_type != VAR_UNKNOWN)
2317 {
2318 if (argvars[4].v_type != VAR_DICT ||
2319 ((dict = argvars[4].vval.v_dict) == NULL))
2320 {
2321 emsg(_(e_dictreq));
2322 goto cleanup;
2323 }
2324
2325 // Line number where the sign is to be placed
2326 if ((di = dict_find(dict, (char_u *)"lnum", -1)) != NULL)
2327 {
2328 (void)tv_get_number_chk(&di->di_tv, &notanum);
2329 if (notanum)
2330 goto cleanup;
2331 lnum = tv_get_lnum(&di->di_tv);
2332 }
2333 if ((di = dict_find(dict, (char_u *)"priority", -1)) != NULL)
2334 {
2335 // Sign priority
2336 prio = (int)tv_get_number_chk(&di->di_tv, &notanum);
2337 if (notanum)
2338 goto cleanup;
2339 }
2340 }
2341
2342 if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK)
2343 rettv->vval.v_number = sign_id;
2344
2345cleanup:
2346 vim_free(group);
2347}
2348
2349/*
2350 * "sign_undefine()" function
2351 */
2352 void
2353f_sign_undefine(typval_T *argvars, typval_T *rettv)
2354{
2355 char_u *name;
2356
2357 rettv->vval.v_number = -1;
2358
2359 if (argvars[0].v_type == VAR_UNKNOWN)
2360 {
2361 // Free all the signs
2362 free_signs();
2363 rettv->vval.v_number = 0;
2364 }
2365 else
2366 {
2367 // Free only the specified sign
2368 name = tv_get_string_chk(&argvars[0]);
2369 if (name == NULL)
2370 return;
2371
2372 if (sign_undefine_by_name(name) == OK)
2373 rettv->vval.v_number = 0;
2374 }
2375}
2376
2377/*
2378 * "sign_unplace()" function
2379 */
2380 void
2381f_sign_unplace(typval_T *argvars, typval_T *rettv)
2382{
2383 dict_T *dict;
2384 dictitem_T *di;
2385 int sign_id = 0;
2386 buf_T *buf = NULL;
2387 char_u *group = NULL;
2388
2389 rettv->vval.v_number = -1;
2390
2391 if (argvars[0].v_type != VAR_STRING)
2392 {
2393 emsg(_(e_invarg));
2394 return;
2395 }
2396
2397 group = tv_get_string(&argvars[0]);
2398 if (group[0] == '\0')
2399 group = NULL; // global sign group
2400 else
2401 {
2402 group = vim_strsave(group);
2403 if (group == NULL)
2404 return;
2405 }
2406
2407 if (argvars[1].v_type != VAR_UNKNOWN)
2408 {
2409 if (argvars[1].v_type != VAR_DICT)
2410 {
2411 emsg(_(e_dictreq));
2412 goto cleanup;
2413 }
2414 dict = argvars[1].vval.v_dict;
2415
2416 if ((di = dict_find(dict, (char_u *)"buffer", -1)) != NULL)
2417 {
2418 buf = get_buf_arg(&di->di_tv);
2419 if (buf == NULL)
2420 goto cleanup;
2421 }
2422 if (dict_find(dict, (char_u *)"id", -1) != NULL)
2423 sign_id = dict_get_number(dict, (char_u *)"id");
2424 }
2425
2426 if (buf == NULL)
2427 {
2428 // Delete the sign in all the buffers
2429 FOR_ALL_BUFFERS(buf)
2430 if (sign_unplace(sign_id, group, buf, 0) == OK)
2431 rettv->vval.v_number = 0;
2432 }
2433 else
2434 {
2435 if (sign_unplace(sign_id, group, buf, 0) == OK)
2436 rettv->vval.v_number = 0;
2437 }
2438
2439cleanup:
2440 vim_free(group);
2441}
2442
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002443#endif /* FEAT_SIGNS */