blob: cf7581951c76c91fd0e064af53f331eeab3b7a35 [file] [log] [blame]
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * sign.c: functions for managing signs
12 */
13
14#include "vim.h"
15
16#if defined(FEAT_SIGNS) || defined(PROTO)
17
18/*
19 * Struct to hold the sign properties.
20 */
21typedef struct sign sign_T;
22
23struct sign
24{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +010025 sign_T *sn_next; // next sign in list
26 int sn_typenr; // type number of sign
27 char_u *sn_name; // name of sign
28 char_u *sn_icon; // name of pixmap
Bram Moolenaarbbea4702019-01-01 13:20:31 +010029# ifdef FEAT_SIGN_ICONS
Bram Moolenaar6b7b7192019-01-11 13:42:41 +010030 void *sn_image; // icon image
Bram Moolenaarbbea4702019-01-01 13:20:31 +010031# endif
Bram Moolenaar6b7b7192019-01-11 13:42:41 +010032 char_u *sn_text; // text used instead of pixmap
33 int sn_line_hl; // highlight ID for line
34 int sn_text_hl; // highlight ID for text
Bram Moolenaarbbea4702019-01-01 13:20:31 +010035};
36
37static sign_T *first_sign = NULL;
38static int next_sign_typenr = 1;
39
40static void sign_list_defined(sign_T *sp);
41static void sign_undefine(sign_T *sp, sign_T *sp_prev);
42
43static char *cmds[] = {
44 "define",
45# define SIGNCMD_DEFINE 0
46 "undefine",
47# define SIGNCMD_UNDEFINE 1
48 "list",
49# define SIGNCMD_LIST 2
50 "place",
51# define SIGNCMD_PLACE 3
52 "unplace",
53# define SIGNCMD_UNPLACE 4
54 "jump",
55# define SIGNCMD_JUMP 5
56 NULL
57# define SIGNCMD_LAST 6
58};
59
60static hashtab_T sg_table; // sign group (signgroup_T) hashtable
61static int next_sign_id = 1; // next sign id in the global group
62
63/*
64 * Initialize data needed for managing signs
65 */
66 void
67init_signs(void)
68{
69 hash_init(&sg_table); // sign group hash table
70}
71
72/*
73 * A new sign in group 'groupname' is added. If the group is not present,
74 * create it. Otherwise reference the group.
75 */
76 static signgroup_T *
77sign_group_ref(char_u *groupname)
78{
79 hash_T hash;
80 hashitem_T *hi;
81 signgroup_T *group;
82
83 hash = hash_hash(groupname);
84 hi = hash_lookup(&sg_table, groupname, hash);
85 if (HASHITEM_EMPTY(hi))
86 {
87 // new group
Bram Moolenaarc799fe22019-05-28 23:08:19 +020088 group = alloc(sizeof(signgroup_T) + STRLEN(groupname));
Bram Moolenaarbbea4702019-01-01 13:20:31 +010089 if (group == NULL)
90 return NULL;
91 STRCPY(group->sg_name, groupname);
92 group->refcount = 1;
93 group->next_sign_id = 1;
94 hash_add_item(&sg_table, hi, group->sg_name, hash);
95 }
96 else
97 {
98 // existing group
99 group = HI2SG(hi);
100 group->refcount++;
101 }
102
103 return group;
104}
105
106/*
107 * A sign in group 'groupname' is removed. If all the signs in this group are
108 * removed, then remove the group.
109 */
110 static void
111sign_group_unref(char_u *groupname)
112{
113 hashitem_T *hi;
114 signgroup_T *group;
115
116 hi = hash_find(&sg_table, groupname);
117 if (!HASHITEM_EMPTY(hi))
118 {
119 group = HI2SG(hi);
120 group->refcount--;
121 if (group->refcount == 0)
122 {
123 // All the signs in this group are removed
124 hash_remove(&sg_table, hi);
125 vim_free(group);
126 }
127 }
128}
129
130/*
131 * Returns TRUE if 'sign' is in 'group'.
132 * A sign can either be in the global group (sign->group == NULL)
133 * or in a named group. If 'group' is '*', then the sign is part of the group.
134 */
135 static int
136sign_in_group(signlist_T *sign, char_u *group)
137{
138 return ((group != NULL && STRCMP(group, "*") == 0)
139 || (group == NULL && sign->group == NULL)
140 || (group != NULL && sign->group != NULL
141 && STRCMP(group, sign->group->sg_name) == 0));
142}
143
144/*
145 * Get the next free sign identifier in the specified group
146 */
147 static int
148sign_group_get_next_signid(buf_T *buf, char_u *groupname)
149{
150 int id = 1;
151 signgroup_T *group = NULL;
152 signlist_T *sign;
153 hashitem_T *hi;
154 int found = FALSE;
155
156 if (groupname != NULL)
157 {
158 hi = hash_find(&sg_table, groupname);
159 if (HASHITEM_EMPTY(hi))
160 return id;
161 group = HI2SG(hi);
162 }
163
Bram Moolenaarb5443cc2019-01-15 20:19:40 +0100164 // Search for the next usable sign identifier
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100165 while (!found)
166 {
167 if (group == NULL)
168 id = next_sign_id++; // global group
169 else
170 id = group->next_sign_id++;
171
172 // Check whether this sign is already placed in the buffer
173 found = TRUE;
174 FOR_ALL_SIGNS_IN_BUF(buf, sign)
175 {
176 if (id == sign->id && sign_in_group(sign, groupname))
177 {
178 found = FALSE; // sign identifier is in use
179 break;
180 }
181 }
182 }
183
184 return id;
185}
186
187/*
188 * Insert a new sign into the signlist for buffer 'buf' between the 'prev' and
189 * 'next' signs.
190 */
191 static void
192insert_sign(
193 buf_T *buf, // buffer to store sign in
194 signlist_T *prev, // previous sign entry
195 signlist_T *next, // next sign entry
196 int id, // sign ID
197 char_u *group, // sign group; NULL for global group
198 int prio, // sign priority
199 linenr_T lnum, // line number which gets the mark
200 int typenr) // typenr of sign we are adding
201{
202 signlist_T *newsign;
203
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200204 newsign = lalloc_id(sizeof(signlist_T), FALSE, aid_insert_sign);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100205 if (newsign != NULL)
206 {
207 newsign->id = id;
208 newsign->lnum = lnum;
209 newsign->typenr = typenr;
210 if (group != NULL)
211 {
212 newsign->group = sign_group_ref(group);
213 if (newsign->group == NULL)
214 {
215 vim_free(newsign);
216 return;
217 }
218 }
219 else
220 newsign->group = NULL;
221 newsign->priority = prio;
222 newsign->next = next;
223 newsign->prev = prev;
224 if (next != NULL)
225 next->prev = newsign;
226
227 if (prev == NULL)
228 {
229 // When adding first sign need to redraw the windows to create the
230 // column for signs.
231 if (buf->b_signlist == NULL)
232 {
233 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200234 changed_line_abv_curs();
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100235 }
236
237 // first sign in signlist
238 buf->b_signlist = newsign;
239#ifdef FEAT_NETBEANS_INTG
240 if (netbeans_active())
241 buf->b_has_sign_column = TRUE;
242#endif
243 }
244 else
245 prev->next = newsign;
246 }
247}
248
249/*
250 * Insert a new sign sorted by line number and sign priority.
251 */
252 static void
253insert_sign_by_lnum_prio(
254 buf_T *buf, // buffer to store sign in
255 signlist_T *prev, // previous sign entry
256 int id, // sign ID
257 char_u *group, // sign group; NULL for global group
258 int prio, // sign priority
259 linenr_T lnum, // line number which gets the mark
260 int typenr) // typenr of sign we are adding
261{
262 signlist_T *sign;
263
264 // keep signs sorted by lnum and by priority: insert new sign at
265 // the proper position in the list for this lnum.
266 while (prev != NULL && prev->lnum == lnum && prev->priority <= prio)
267 prev = prev->prev;
268 if (prev == NULL)
269 sign = buf->b_signlist;
270 else
271 sign = prev->next;
272
273 insert_sign(buf, prev, sign, id, group, prio, lnum, typenr);
274}
275
276/*
Bram Moolenaar4e038572019-07-04 18:28:35 +0200277 * Lookup a sign by typenr. Returns NULL if sign is not found.
278 */
279 static sign_T *
280find_sign_by_typenr(int typenr)
281{
282 sign_T *sp;
283
284 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
285 if (sp->sn_typenr == typenr)
286 return sp;
287 return NULL;
288}
289
290/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100291 * Get the name of a sign by its typenr.
292 */
293 static char_u *
294sign_typenr2name(int typenr)
295{
296 sign_T *sp;
297
298 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
299 if (sp->sn_typenr == typenr)
300 return sp->sn_name;
301 return (char_u *)_("[Deleted]");
302}
303
304/*
305 * Return information about a sign in a Dict
306 */
307 static dict_T *
308sign_get_info(signlist_T *sign)
309{
310 dict_T *d;
311
312 if ((d = dict_alloc_id(aid_sign_getinfo)) == NULL)
313 return NULL;
314 dict_add_number(d, "id", sign->id);
315 dict_add_string(d, "group", (sign->group == NULL) ?
316 (char_u *)"" : sign->group->sg_name);
317 dict_add_number(d, "lnum", sign->lnum);
318 dict_add_string(d, "name", sign_typenr2name(sign->typenr));
319 dict_add_number(d, "priority", sign->priority);
320
321 return d;
322}
323
324/*
Bram Moolenaar64416122019-06-07 21:37:13 +0200325 * Sort the signs placed on the same line as "sign" by priority. Invoked after
326 * changing the priority of an already placed sign. Assumes the signs in the
327 * buffer are sorted by line number and priority.
328 */
329 static void
330sign_sort_by_prio_on_line(buf_T *buf, signlist_T *sign)
331{
332 signlist_T *p = NULL;
333
334 // If there is only one sign in the buffer or only one sign on the line or
335 // the sign is already sorted by priority, then return.
336 if ((sign->prev == NULL
337 || sign->prev->lnum != sign->lnum
338 || sign->prev->priority > sign->priority)
339 && (sign->next == NULL
340 || sign->next->lnum != sign->lnum
341 || sign->next->priority < sign->priority))
342 return;
343
344 // One or more signs on the same line as 'sign'
345 // Find a sign after which 'sign' should be inserted
346
347 // First search backward for a sign with higher priority on the same line
348 p = sign;
349 while (p->prev != NULL && p->prev->lnum == sign->lnum
350 && p->prev->priority <= sign->priority)
351 p = p->prev;
352
353 if (p == sign)
354 {
355 // Sign not found. Search forward for a sign with priority just before
356 // 'sign'.
357 p = sign->next;
358 while (p->next != NULL && p->next->lnum == sign->lnum
359 && p->next->priority > sign->priority)
360 p = p->next;
361 }
362
363 // Remove 'sign' from the list
364 if (buf->b_signlist == sign)
365 buf->b_signlist = sign->next;
366 if (sign->prev != NULL)
367 sign->prev->next = sign->next;
368 if (sign->next != NULL)
369 sign->next->prev = sign->prev;
370 sign->prev = NULL;
371 sign->next = NULL;
372
373 // Re-insert 'sign' at the right place
374 if (p->priority <= sign->priority)
375 {
376 // 'sign' has a higher priority and should be inserted before 'p'
377 sign->prev = p->prev;
378 sign->next = p;
379 p->prev = sign;
380 if (sign->prev != NULL)
381 sign->prev->next = sign;
382 if (buf->b_signlist == p)
383 buf->b_signlist = sign;
384 }
385 else
386 {
387 // 'sign' has a lower priority and should be inserted after 'p'
388 sign->prev = p;
389 sign->next = p->next;
390 p->next = sign;
391 if (sign->next != NULL)
392 sign->next->prev = sign;
393 }
394}
395
396/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100397 * Add the sign into the signlist. Find the right spot to do it though.
398 */
399 static void
400buf_addsign(
401 buf_T *buf, // buffer to store sign in
402 int id, // sign ID
403 char_u *groupname, // sign group
404 int prio, // sign priority
405 linenr_T lnum, // line number which gets the mark
406 int typenr) // typenr of sign we are adding
407{
408 signlist_T *sign; // a sign in the signlist
409 signlist_T *prev; // the previous sign
410
411 prev = NULL;
412 FOR_ALL_SIGNS_IN_BUF(buf, sign)
413 {
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100414 if (lnum == sign->lnum && id == sign->id
415 && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100416 {
417 // Update an existing sign
418 sign->typenr = typenr;
Bram Moolenaar58a7f872019-06-04 22:48:15 +0200419 sign->priority = prio;
Bram Moolenaar64416122019-06-07 21:37:13 +0200420 sign_sort_by_prio_on_line(buf, sign);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100421 return;
422 }
423 else if (lnum < sign->lnum)
424 {
425 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio,
426 lnum, typenr);
427 return;
428 }
429 prev = sign;
430 }
431
432 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr);
433 return;
434}
435
436/*
437 * For an existing, placed sign "markId" change the type to "typenr".
438 * Returns the line number of the sign, or zero if the sign is not found.
439 */
440 static linenr_T
441buf_change_sign_type(
442 buf_T *buf, // buffer to store sign in
443 int markId, // sign ID
444 char_u *group, // sign group
445 int typenr) // typenr of sign we are adding
446{
447 signlist_T *sign; // a sign in the signlist
448
449 FOR_ALL_SIGNS_IN_BUF(buf, sign)
450 {
451 if (sign->id == markId && sign_in_group(sign, group))
452 {
453 sign->typenr = typenr;
454 return sign->lnum;
455 }
456 }
457
458 return (linenr_T)0;
459}
460
461/*
Bram Moolenaar4e038572019-07-04 18:28:35 +0200462 * Return the attributes of the first sign placed on line 'lnum' in buffer
463 * 'buf'. Used when refreshing the screen. Returns TRUE if a sign is found on
464 * 'lnum', FALSE otherwise.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100465 */
466 int
Bram Moolenaar4e038572019-07-04 18:28:35 +0200467buf_get_signattrs(buf_T *buf, linenr_T lnum, sign_attrs_T *sattr)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100468{
Bram Moolenaar4e038572019-07-04 18:28:35 +0200469 signlist_T *sign;
470 sign_T *sp;
471
472 vim_memset(sattr, 0, sizeof(sign_attrs_T));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100473
474 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200475 {
476 if (sign->lnum > lnum)
477 // Signs are sorted by line number in the buffer. No need to check
478 // for signs after the specified line number 'lnum'.
479 break;
480
481 if (sign->lnum == lnum)
482 {
483 sattr->typenr = sign->typenr;
484 sp = find_sign_by_typenr(sign->typenr);
485 if (sp == NULL)
486 return FALSE;
487
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100488# ifdef FEAT_SIGN_ICONS
Bram Moolenaar4e038572019-07-04 18:28:35 +0200489 sattr->icon = sp->sn_image;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100490# endif
Bram Moolenaar4e038572019-07-04 18:28:35 +0200491 sattr->text = sp->sn_text;
492 if (sattr->text != NULL && sp->sn_text_hl > 0)
493 sattr->texthl = syn_id2attr(sp->sn_text_hl);
494 if (sp->sn_line_hl > 0)
495 sattr->linehl = syn_id2attr(sp->sn_line_hl);
496 return TRUE;
497 }
498 }
499 return FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100500}
501
502/*
503 * Delete sign 'id' in group 'group' from buffer 'buf'.
504 * If 'id' is zero, then delete all the signs in group 'group'. Otherwise
505 * delete only the specified sign.
506 * If 'group' is '*', then delete the sign in all the groups. If 'group' is
507 * NULL, then delete the sign in the global group. Otherwise delete the sign in
508 * the specified group.
509 * Returns the line number of the deleted sign. If multiple signs are deleted,
510 * then returns the line number of the last sign deleted.
511 */
512 linenr_T
513buf_delsign(
514 buf_T *buf, // buffer sign is stored in
515 linenr_T atlnum, // sign at this line, 0 - at any line
516 int id, // sign id
517 char_u *group) // sign group
518{
519 signlist_T **lastp; // pointer to pointer to current sign
520 signlist_T *sign; // a sign in a b_signlist
521 signlist_T *next; // the next sign in a b_signlist
522 linenr_T lnum; // line number whose sign was deleted
523
524 lastp = &buf->b_signlist;
525 lnum = 0;
526 for (sign = buf->b_signlist; sign != NULL; sign = next)
527 {
528 next = sign->next;
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100529 if ((id == 0 || sign->id == id)
530 && (atlnum == 0 || sign->lnum == atlnum)
531 && sign_in_group(sign, group))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100532
533 {
534 *lastp = next;
535 if (next != NULL)
536 next->prev = sign->prev;
537 lnum = sign->lnum;
538 if (sign->group != NULL)
539 sign_group_unref(sign->group->sg_name);
540 vim_free(sign);
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100541 redraw_buf_line_later(buf, lnum);
542
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100543 // Check whether only one sign needs to be deleted
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100544 // If deleting a sign with a specific identifier in a particular
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100545 // group or deleting any sign at a particular line number, delete
546 // only one sign.
547 if (group == NULL
548 || (*group != '*' && id != 0)
549 || (*group == '*' && atlnum != 0))
550 break;
551 }
552 else
553 lastp = &sign->next;
554 }
555
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100556 // When deleting the last sign the cursor position may change, because the
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100557 // sign columns no longer shows. And the 'signcolumn' may be hidden.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100558 if (buf->b_signlist == NULL)
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100559 {
560 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200561 changed_line_abv_curs();
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100562 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100563
564 return lnum;
565}
566
567
568/*
569 * Find the line number of the sign with the requested id in group 'group'. If
570 * the sign does not exist, return 0 as the line number. This will still let
571 * the correct file get loaded.
572 */
573 int
574buf_findsign(
575 buf_T *buf, // buffer to store sign in
576 int id, // sign ID
577 char_u *group) // sign group
578{
579 signlist_T *sign; // a sign in the signlist
580
581 FOR_ALL_SIGNS_IN_BUF(buf, sign)
582 if (sign->id == id && sign_in_group(sign, group))
583 return sign->lnum;
584
585 return 0;
586}
587
588/*
589 * Return the sign at line 'lnum' in buffer 'buf'. Returns NULL if a sign is
590 * not found at the line. If 'groupname' is NULL, searches in the global group.
591 */
592 static signlist_T *
593buf_getsign_at_line(
594 buf_T *buf, // buffer whose sign we are searching for
595 linenr_T lnum, // line number of sign
596 char_u *groupname) // sign group name
597{
598 signlist_T *sign; // a sign in the signlist
599
600 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200601 {
602 if (sign->lnum > lnum)
603 // Signs are sorted by line number in the buffer. No need to check
604 // for signs after the specified line number 'lnum'.
605 break;
606
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100607 if (sign->lnum == lnum && sign_in_group(sign, groupname))
608 return sign;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200609 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100610
611 return NULL;
612}
613
614/*
615 * Return the identifier of the sign at line number 'lnum' in buffer 'buf'.
616 */
617 int
618buf_findsign_id(
619 buf_T *buf, // buffer whose sign we are searching for
620 linenr_T lnum, // line number of sign
621 char_u *groupname) // sign group name
622{
623 signlist_T *sign; // a sign in the signlist
624
625 sign = buf_getsign_at_line(buf, lnum, groupname);
626 if (sign != NULL)
627 return sign->id;
628
629 return 0;
630}
631
632# if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
633/*
634 * See if a given type of sign exists on a specific line.
635 */
636 int
637buf_findsigntype_id(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100638 buf_T *buf, // buffer whose sign we are searching for
639 linenr_T lnum, // line number of sign
640 int typenr) // sign type number
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100641{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100642 signlist_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100643
644 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200645 {
646 if (sign->lnum > lnum)
647 // Signs are sorted by line number in the buffer. No need to check
648 // for signs after the specified line number 'lnum'.
649 break;
650
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100651 if (sign->lnum == lnum && sign->typenr == typenr)
652 return sign->id;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200653 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100654
655 return 0;
656}
657
658
659# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
660/*
661 * Return the number of icons on the given line.
662 */
663 int
664buf_signcount(buf_T *buf, linenr_T lnum)
665{
666 signlist_T *sign; // a sign in the signlist
667 int count = 0;
668
669 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200670 {
671 if (sign->lnum > lnum)
672 // Signs are sorted by line number in the buffer. No need to check
673 // for signs after the specified line number 'lnum'.
674 break;
675
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100676 if (sign->lnum == lnum)
677 if (sign_get_image(sign->typenr) != NULL)
678 count++;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200679 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100680
681 return count;
682}
683# endif /* FEAT_SIGN_ICONS */
684# endif /* FEAT_NETBEANS_INTG */
685
686/*
687 * Delete signs in group 'group' in buffer "buf". If 'group' is '*', then
688 * delete all the signs.
689 */
690 void
691buf_delete_signs(buf_T *buf, char_u *group)
692{
693 signlist_T *sign;
694 signlist_T **lastp; // pointer to pointer to current sign
695 signlist_T *next;
696
697 // When deleting the last sign need to redraw the windows to remove the
698 // sign column. Not when curwin is NULL (this means we're exiting).
699 if (buf->b_signlist != NULL && curwin != NULL)
700 {
701 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200702 changed_line_abv_curs();
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100703 }
704
705 lastp = &buf->b_signlist;
706 for (sign = buf->b_signlist; sign != NULL; sign = next)
707 {
708 next = sign->next;
709 if (sign_in_group(sign, group))
710 {
711 *lastp = next;
712 if (next != NULL)
713 next->prev = sign->prev;
714 if (sign->group != NULL)
715 sign_group_unref(sign->group->sg_name);
716 vim_free(sign);
717 }
718 else
719 lastp = &sign->next;
720 }
721}
722
723/*
724 * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers.
725 */
726 static void
727sign_list_placed(buf_T *rbuf, char_u *sign_group)
728{
729 buf_T *buf;
730 signlist_T *sign;
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100731 char lbuf[MSG_BUF_LEN];
732 char group[MSG_BUF_LEN];
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100733
Bram Moolenaar32526b32019-01-19 17:43:09 +0100734 msg_puts_title(_("\n--- Signs ---"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100735 msg_putchar('\n');
736 if (rbuf == NULL)
737 buf = firstbuf;
738 else
739 buf = rbuf;
740 while (buf != NULL && !got_int)
741 {
742 if (buf->b_signlist != NULL)
743 {
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100744 vim_snprintf(lbuf, MSG_BUF_LEN, _("Signs for %s:"), buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100745 msg_puts_attr(lbuf, HL_ATTR(HLF_D));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100746 msg_putchar('\n');
747 }
748 FOR_ALL_SIGNS_IN_BUF(buf, sign)
749 {
750 if (got_int)
751 break;
752 if (!sign_in_group(sign, sign_group))
753 continue;
754 if (sign->group != NULL)
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100755 vim_snprintf(group, MSG_BUF_LEN, _(" group=%s"),
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100756 sign->group->sg_name);
757 else
758 group[0] = '\0';
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100759 vim_snprintf(lbuf, MSG_BUF_LEN,
760 _(" line=%ld id=%d%s name=%s priority=%d"),
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100761 (long)sign->lnum, sign->id, group,
762 sign_typenr2name(sign->typenr), sign->priority);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100763 msg_puts(lbuf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100764 msg_putchar('\n');
765 }
766 if (rbuf != NULL)
767 break;
768 buf = buf->b_next;
769 }
770}
771
772/*
773 * Adjust a placed sign for inserted/deleted lines.
774 */
775 void
776sign_mark_adjust(
777 linenr_T line1,
778 linenr_T line2,
779 long amount,
780 long amount_after)
781{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100782 signlist_T *sign; // a sign in a b_signlist
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100783 linenr_T new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100784
785 FOR_ALL_SIGNS_IN_BUF(curbuf, sign)
786 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100787 // Ignore changes to lines after the sign
788 if (sign->lnum < line1)
789 continue;
790 new_lnum = sign->lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100791 if (sign->lnum >= line1 && sign->lnum <= line2)
792 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100793 if (amount != MAXLNUM)
794 new_lnum += amount;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100795 }
796 else if (sign->lnum > line2)
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100797 // Lines inserted or deleted before the sign
798 new_lnum += amount_after;
799
800 // If the new sign line number is past the last line in the buffer,
801 // then don't adjust the line number. Otherwise, it will always be past
802 // the last line and will not be visible.
803 if (new_lnum <= curbuf->b_ml.ml_line_count)
804 sign->lnum = new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100805 }
806}
807
808/*
809 * Find index of a ":sign" subcmd from its name.
810 * "*end_cmd" must be writable.
811 */
812 static int
813sign_cmd_idx(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100814 char_u *begin_cmd, // begin of sign subcmd
815 char_u *end_cmd) // just after sign subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100816{
817 int idx;
818 char save = *end_cmd;
819
820 *end_cmd = NUL;
821 for (idx = 0; ; ++idx)
822 if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0)
823 break;
824 *end_cmd = save;
825 return idx;
826}
827
828/*
829 * Find a sign by name. Also returns pointer to the previous sign.
830 */
831 static sign_T *
832sign_find(char_u *name, sign_T **sp_prev)
833{
834 sign_T *sp;
835
836 if (sp_prev != NULL)
837 *sp_prev = NULL;
838 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
839 {
840 if (STRCMP(sp->sn_name, name) == 0)
841 break;
842 if (sp_prev != NULL)
843 *sp_prev = sp;
844 }
845
846 return sp;
847}
848
849/*
Bram Moolenaar03142362019-01-18 22:01:42 +0100850 * Allocate a new sign
851 */
852 static sign_T *
853alloc_new_sign(char_u *name)
854{
855 sign_T *sp;
856 sign_T *lp;
857 int start = next_sign_typenr;
858
859 // Allocate a new sign.
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200860 sp = alloc_clear_id(sizeof(sign_T), aid_sign_define_by_name);
Bram Moolenaar03142362019-01-18 22:01:42 +0100861 if (sp == NULL)
862 return NULL;
863
864 // Check that next_sign_typenr is not already being used.
865 // This only happens after wrapping around. Hopefully
866 // another one got deleted and we can use its number.
867 for (lp = first_sign; lp != NULL; )
868 {
869 if (lp->sn_typenr == next_sign_typenr)
870 {
871 ++next_sign_typenr;
872 if (next_sign_typenr == MAX_TYPENR)
873 next_sign_typenr = 1;
874 if (next_sign_typenr == start)
875 {
876 vim_free(sp);
877 emsg(_("E612: Too many signs defined"));
878 return NULL;
879 }
880 lp = first_sign; // start all over
881 continue;
882 }
883 lp = lp->sn_next;
884 }
885
886 sp->sn_typenr = next_sign_typenr;
887 if (++next_sign_typenr == MAX_TYPENR)
888 next_sign_typenr = 1; // wrap around
889
890 sp->sn_name = vim_strsave(name);
891 if (sp->sn_name == NULL) // out of memory
892 {
893 vim_free(sp);
894 return NULL;
895 }
896
897 return sp;
898}
899
900/*
901 * Initialize the icon information for a new sign
902 */
903 static void
904sign_define_init_icon(sign_T *sp, char_u *icon)
905{
906 vim_free(sp->sn_icon);
907 sp->sn_icon = vim_strsave(icon);
908 backslash_halve(sp->sn_icon);
909# ifdef FEAT_SIGN_ICONS
910 if (gui.in_use)
911 {
912 out_flush();
913 if (sp->sn_image != NULL)
914 gui_mch_destroy_sign(sp->sn_image);
915 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
916 }
917# endif
918}
919
920/*
921 * Initialize the text for a new sign
922 */
923 static int
924sign_define_init_text(sign_T *sp, char_u *text)
925{
926 char_u *s;
927 char_u *endp;
928 int cells;
929 int len;
930
931 endp = text + (int)STRLEN(text);
932
933 // Remove backslashes so that it is possible to use a space.
934 for (s = text; s + 1 < endp; ++s)
935 if (*s == '\\')
936 {
937 STRMOVE(s, s + 1);
938 --endp;
939 }
940
941 // Count cells and check for non-printable chars
Bram Moolenaar03142362019-01-18 22:01:42 +0100942 if (has_mbyte)
943 {
944 cells = 0;
945 for (s = text; s < endp; s += (*mb_ptr2len)(s))
946 {
947 if (!vim_isprintc((*mb_ptr2char)(s)))
948 break;
949 cells += (*mb_ptr2cells)(s);
950 }
951 }
952 else
Bram Moolenaar03142362019-01-18 22:01:42 +0100953 {
954 for (s = text; s < endp; ++s)
955 if (!vim_isprintc(*s))
956 break;
957 cells = (int)(s - text);
958 }
959
960 // Currently sign text must be one or two display cells
961 if (s != endp || cells < 1 || cells > 2)
962 {
963 semsg(_("E239: Invalid sign text: %s"), text);
964 return FAIL;
965 }
966
967 vim_free(sp->sn_text);
968 // Allocate one byte more if we need to pad up
969 // with a space.
970 len = (int)(endp - text + ((cells == 1) ? 1 : 0));
971 sp->sn_text = vim_strnsave(text, len);
972
973 // For single character sign text, pad with a space.
974 if (sp->sn_text != NULL && cells == 1)
975 STRCPY(sp->sn_text + len - 1, " ");
976
977 return OK;
978}
979
980/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100981 * Define a new sign or update an existing sign
982 */
983 int
984sign_define_by_name(
985 char_u *name,
986 char_u *icon,
987 char_u *linehl,
988 char_u *text,
989 char_u *texthl)
990{
991 sign_T *sp_prev;
992 sign_T *sp;
993
994 sp = sign_find(name, &sp_prev);
995 if (sp == NULL)
996 {
Bram Moolenaar03142362019-01-18 22:01:42 +0100997 sp = alloc_new_sign(name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100998 if (sp == NULL)
999 return FAIL;
1000
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001001 // add the new sign to the list of signs
1002 if (sp_prev == NULL)
1003 first_sign = sp;
1004 else
1005 sp_prev->sn_next = sp;
1006 }
1007
1008 // set values for a defined sign.
1009 if (icon != NULL)
Bram Moolenaar03142362019-01-18 22:01:42 +01001010 sign_define_init_icon(sp, icon);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001011
Bram Moolenaar03142362019-01-18 22:01:42 +01001012 if (text != NULL && (sign_define_init_text(sp, text) == FAIL))
1013 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001014
1015 if (linehl != NULL)
1016 sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl));
1017
1018 if (texthl != NULL)
1019 sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl));
1020
1021 return OK;
1022}
1023
1024/*
1025 * Free the sign specified by 'name'.
1026 */
1027 int
1028sign_undefine_by_name(char_u *name)
1029{
1030 sign_T *sp_prev;
1031 sign_T *sp;
1032
1033 sp = sign_find(name, &sp_prev);
1034 if (sp == NULL)
1035 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001036 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001037 return FAIL;
1038 }
1039 sign_undefine(sp, sp_prev);
1040
1041 return OK;
1042}
1043
1044/*
1045 * List the signs matching 'name'
1046 */
1047 static void
1048sign_list_by_name(char_u *name)
1049{
1050 sign_T *sp;
1051
1052 sp = sign_find(name, NULL);
1053 if (sp != NULL)
1054 sign_list_defined(sp);
1055 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001056 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001057}
1058
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001059 static void
1060may_force_numberwidth_recompute(buf_T *buf, int unplace)
1061{
1062 tabpage_T *tp;
1063 win_T *wp;
1064
1065 FOR_ALL_TAB_WINDOWS(tp, wp)
1066 if (wp->w_buffer == buf
1067 && (wp->w_p_nu || wp->w_p_rnu)
1068 && (unplace || wp->w_nrwidth_width < 2)
1069 && (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u'))
1070 wp->w_nrwidth_line_count = 0;
1071}
1072
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001073/*
Bram Moolenaar8144acb2019-01-14 23:08:18 +01001074 * Place a sign at the specified file location or update a sign.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001075 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001076 static int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001077sign_place(
1078 int *sign_id,
1079 char_u *sign_group,
1080 char_u *sign_name,
1081 buf_T *buf,
1082 linenr_T lnum,
1083 int prio)
1084{
1085 sign_T *sp;
1086
1087 // Check for reserved character '*' in group name
1088 if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0'))
1089 return FAIL;
1090
1091 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1092 if (STRCMP(sp->sn_name, sign_name) == 0)
1093 break;
1094 if (sp == NULL)
1095 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001096 semsg(_("E155: Unknown sign: %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001097 return FAIL;
1098 }
1099 if (*sign_id == 0)
1100 *sign_id = sign_group_get_next_signid(buf, sign_group);
1101
1102 if (lnum > 0)
1103 // ":sign place {id} line={lnum} name={name} file={fname}":
1104 // place a sign
1105 buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr);
1106 else
1107 // ":sign place {id} file={fname}": change sign type
1108 lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr);
1109 if (lnum > 0)
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001110 {
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001111 redraw_buf_line_later(buf, lnum);
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001112
1113 // When displaying signs in the 'number' column, if the width of the
1114 // number column is less than 2, then force recomputing the width.
1115 may_force_numberwidth_recompute(buf, FALSE);
1116 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001117 else
1118 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001119 semsg(_("E885: Not possible to change sign %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001120 return FAIL;
1121 }
1122
1123 return OK;
1124}
1125
1126/*
1127 * Unplace the specified sign
1128 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001129 static int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001130sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum)
1131{
1132 if (buf->b_signlist == NULL) // No signs in the buffer
1133 return OK;
1134
1135 if (sign_id == 0)
1136 {
1137 // Delete all the signs in the specified buffer
1138 redraw_buf_later(buf, NOT_VALID);
1139 buf_delete_signs(buf, sign_group);
1140 }
1141 else
1142 {
1143 linenr_T lnum;
1144
1145 // Delete only the specified signs
1146 lnum = buf_delsign(buf, atlnum, sign_id, sign_group);
1147 if (lnum == 0)
1148 return FAIL;
1149 }
1150
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001151 // When all the signs in a buffer are removed, force recomputing the
1152 // number column width (if enabled) in all the windows displaying the
1153 // buffer if 'signcolumn' is set to 'number' in that window.
1154 if (buf->b_signlist == NULL)
1155 may_force_numberwidth_recompute(buf, TRUE);
1156
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001157 return OK;
1158}
1159
1160/*
1161 * Unplace the sign at the current cursor line.
1162 */
1163 static void
1164sign_unplace_at_cursor(char_u *groupname)
1165{
1166 int id = -1;
1167
1168 id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname);
1169 if (id > 0)
1170 sign_unplace(id, groupname, curwin->w_buffer, curwin->w_cursor.lnum);
1171 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001172 emsg(_("E159: Missing sign number"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001173}
1174
1175/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001176 * Jump to a sign.
1177 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001178 static linenr_T
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001179sign_jump(int sign_id, char_u *sign_group, buf_T *buf)
1180{
1181 linenr_T lnum;
1182
1183 if ((lnum = buf_findsign(buf, sign_id, sign_group)) <= 0)
1184 {
Bram Moolenaarb5443cc2019-01-15 20:19:40 +01001185 semsg(_("E157: Invalid sign ID: %d"), sign_id);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001186 return -1;
1187 }
1188
1189 // goto a sign ...
1190 if (buf_jump_open_win(buf) != NULL)
1191 { // ... in a current window
1192 curwin->w_cursor.lnum = lnum;
1193 check_cursor_lnum();
1194 beginline(BL_WHITE);
1195 }
1196 else
1197 { // ... not currently in a window
1198 char_u *cmd;
1199
1200 if (buf->b_fname == NULL)
1201 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001202 emsg(_("E934: Cannot jump to a buffer that does not have a name"));
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001203 return -1;
1204 }
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02001205 cmd = alloc(STRLEN(buf->b_fname) + 25);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001206 if (cmd == NULL)
1207 return -1;
1208 sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname);
1209 do_cmdline_cmd(cmd);
1210 vim_free(cmd);
1211 }
1212# ifdef FEAT_FOLDING
1213 foldOpenCursor();
1214# endif
1215
1216 return lnum;
1217}
1218
1219/*
1220 * ":sign define {name} ..." command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001221 */
1222 static void
1223sign_define_cmd(char_u *sign_name, char_u *cmdline)
1224{
1225 char_u *arg;
1226 char_u *p = cmdline;
1227 char_u *icon = NULL;
1228 char_u *text = NULL;
1229 char_u *linehl = NULL;
1230 char_u *texthl = NULL;
1231 int failed = FALSE;
1232
1233 // set values for a defined sign.
1234 for (;;)
1235 {
1236 arg = skipwhite(p);
1237 if (*arg == NUL)
1238 break;
1239 p = skiptowhite_esc(arg);
1240 if (STRNCMP(arg, "icon=", 5) == 0)
1241 {
1242 arg += 5;
1243 icon = vim_strnsave(arg, (int)(p - arg));
1244 }
1245 else if (STRNCMP(arg, "text=", 5) == 0)
1246 {
1247 arg += 5;
1248 text = vim_strnsave(arg, (int)(p - arg));
1249 }
1250 else if (STRNCMP(arg, "linehl=", 7) == 0)
1251 {
1252 arg += 7;
1253 linehl = vim_strnsave(arg, (int)(p - arg));
1254 }
1255 else if (STRNCMP(arg, "texthl=", 7) == 0)
1256 {
1257 arg += 7;
1258 texthl = vim_strnsave(arg, (int)(p - arg));
1259 }
1260 else
1261 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001262 semsg(_(e_invarg2), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001263 failed = TRUE;
1264 break;
1265 }
1266 }
1267
1268 if (!failed)
1269 sign_define_by_name(sign_name, icon, linehl, text, texthl);
1270
1271 vim_free(icon);
1272 vim_free(text);
1273 vim_free(linehl);
1274 vim_free(texthl);
1275}
1276
1277/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001278 * ":sign place" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001279 */
1280 static void
1281sign_place_cmd(
1282 buf_T *buf,
1283 linenr_T lnum,
1284 char_u *sign_name,
1285 int id,
1286 char_u *group,
1287 int prio)
1288{
1289 if (id <= 0)
1290 {
1291 // List signs placed in a file/buffer
1292 // :sign place file={fname}
1293 // :sign place group={group} file={fname}
1294 // :sign place group=* file={fname}
1295 // :sign place buffer={nr}
1296 // :sign place group={group} buffer={nr}
1297 // :sign place group=* buffer={nr}
1298 // :sign place
1299 // :sign place group={group}
1300 // :sign place group=*
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001301 if (lnum >= 0 || sign_name != NULL
1302 || (group != NULL && *group == '\0'))
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001303 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001304 else
1305 sign_list_placed(buf, group);
1306 }
1307 else
1308 {
1309 // Place a new sign
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001310 if (sign_name == NULL || buf == NULL
1311 || (group != NULL && *group == '\0'))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001312 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001313 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001314 return;
1315 }
1316
1317 sign_place(&id, group, sign_name, buf, lnum, prio);
1318 }
1319}
1320
1321/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001322 * ":sign unplace" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001323 */
1324 static void
1325sign_unplace_cmd(
1326 buf_T *buf,
1327 linenr_T lnum,
1328 char_u *sign_name,
1329 int id,
1330 char_u *group)
1331{
1332 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0'))
1333 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001334 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001335 return;
1336 }
1337
1338 if (id == -2)
1339 {
1340 if (buf != NULL)
1341 // :sign unplace * file={fname}
1342 // :sign unplace * group={group} file={fname}
1343 // :sign unplace * group=* file={fname}
1344 // :sign unplace * buffer={nr}
1345 // :sign unplace * group={group} buffer={nr}
1346 // :sign unplace * group=* buffer={nr}
1347 sign_unplace(0, group, buf, 0);
1348 else
1349 // :sign unplace *
1350 // :sign unplace * group={group}
1351 // :sign unplace * group=*
1352 FOR_ALL_BUFFERS(buf)
1353 if (buf->b_signlist != NULL)
1354 buf_delete_signs(buf, group);
1355 }
1356 else
1357 {
1358 if (buf != NULL)
1359 // :sign unplace {id} file={fname}
1360 // :sign unplace {id} group={group} file={fname}
1361 // :sign unplace {id} group=* file={fname}
1362 // :sign unplace {id} buffer={nr}
1363 // :sign unplace {id} group={group} buffer={nr}
1364 // :sign unplace {id} group=* buffer={nr}
1365 sign_unplace(id, group, buf, 0);
1366 else
1367 {
1368 if (id == -1)
1369 {
1370 // :sign unplace group={group}
1371 // :sign unplace group=*
1372 sign_unplace_at_cursor(group);
1373 }
1374 else
1375 {
1376 // :sign unplace {id}
1377 // :sign unplace {id} group={group}
1378 // :sign unplace {id} group=*
1379 FOR_ALL_BUFFERS(buf)
1380 sign_unplace(id, group, buf, 0);
1381 }
1382 }
1383 }
1384}
1385
1386/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001387 * Jump to a placed sign commands:
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001388 * :sign jump {id} file={fname}
1389 * :sign jump {id} buffer={nr}
1390 * :sign jump {id} group={group} file={fname}
1391 * :sign jump {id} group={group} buffer={nr}
1392 */
1393 static void
1394sign_jump_cmd(
1395 buf_T *buf,
1396 linenr_T lnum,
1397 char_u *sign_name,
1398 int id,
1399 char_u *group)
1400{
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001401 if (sign_name == NULL && group == NULL && id == -1)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001402 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001403 emsg(_(e_argreq));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001404 return;
1405 }
1406
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001407 if (buf == NULL || (group != NULL && *group == '\0')
1408 || lnum >= 0 || sign_name != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001409 {
1410 // File or buffer is not specified or an empty group is used
1411 // or a line number or a sign name is specified.
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001412 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001413 return;
1414 }
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001415 (void)sign_jump(id, group, buf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001416}
1417
1418/*
1419 * Parse the command line arguments for the ":sign place", ":sign unplace" and
1420 * ":sign jump" commands.
1421 * The supported arguments are: line={lnum} name={name} group={group}
1422 * priority={prio} and file={fname} or buffer={nr}.
1423 */
1424 static int
1425parse_sign_cmd_args(
1426 int cmd,
1427 char_u *arg,
1428 char_u **sign_name,
1429 int *signid,
1430 char_u **group,
1431 int *prio,
1432 buf_T **buf,
1433 linenr_T *lnum)
1434{
1435 char_u *arg1;
1436 char_u *name;
1437 char_u *filename = NULL;
Bram Moolenaarb589f952019-01-07 22:10:00 +01001438 int lnum_arg = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001439
1440 // first arg could be placed sign id
1441 arg1 = arg;
1442 if (VIM_ISDIGIT(*arg))
1443 {
1444 *signid = getdigits(&arg);
1445 if (!VIM_ISWHITE(*arg) && *arg != NUL)
1446 {
1447 *signid = -1;
1448 arg = arg1;
1449 }
1450 else
1451 arg = skipwhite(arg);
1452 }
1453
1454 while (*arg != NUL)
1455 {
1456 if (STRNCMP(arg, "line=", 5) == 0)
1457 {
1458 arg += 5;
1459 *lnum = atoi((char *)arg);
1460 arg = skiptowhite(arg);
Bram Moolenaarb589f952019-01-07 22:10:00 +01001461 lnum_arg = TRUE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001462 }
1463 else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE)
1464 {
1465 if (*signid != -1)
1466 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001467 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001468 return FAIL;
1469 }
1470 *signid = -2;
1471 arg = skiptowhite(arg + 1);
1472 }
1473 else if (STRNCMP(arg, "name=", 5) == 0)
1474 {
1475 arg += 5;
1476 name = arg;
1477 arg = skiptowhite(arg);
1478 if (*arg != NUL)
1479 *arg++ = NUL;
1480 while (name[0] == '0' && name[1] != NUL)
1481 ++name;
1482 *sign_name = name;
1483 }
1484 else if (STRNCMP(arg, "group=", 6) == 0)
1485 {
1486 arg += 6;
1487 *group = arg;
1488 arg = skiptowhite(arg);
1489 if (*arg != NUL)
1490 *arg++ = NUL;
1491 }
1492 else if (STRNCMP(arg, "priority=", 9) == 0)
1493 {
1494 arg += 9;
1495 *prio = atoi((char *)arg);
1496 arg = skiptowhite(arg);
1497 }
1498 else if (STRNCMP(arg, "file=", 5) == 0)
1499 {
1500 arg += 5;
1501 filename = arg;
1502 *buf = buflist_findname_exp(arg);
1503 break;
1504 }
1505 else if (STRNCMP(arg, "buffer=", 7) == 0)
1506 {
1507 arg += 7;
1508 filename = arg;
1509 *buf = buflist_findnr((int)getdigits(&arg));
1510 if (*skipwhite(arg) != NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001511 emsg(_(e_trailing));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001512 break;
1513 }
1514 else
1515 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001516 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001517 return FAIL;
1518 }
1519 arg = skipwhite(arg);
1520 }
1521
1522 if (filename != NULL && *buf == NULL)
1523 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001524 semsg(_("E158: Invalid buffer name: %s"), filename);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001525 return FAIL;
1526 }
1527
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001528 // If the filename is not supplied for the sign place or the sign jump
1529 // command, then use the current buffer.
Bram Moolenaarb589f952019-01-07 22:10:00 +01001530 if (filename == NULL && ((cmd == SIGNCMD_PLACE && lnum_arg)
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001531 || cmd == SIGNCMD_JUMP))
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001532 *buf = curwin->w_buffer;
1533
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001534 return OK;
1535}
1536
1537/*
1538 * ":sign" command
1539 */
1540 void
1541ex_sign(exarg_T *eap)
1542{
1543 char_u *arg = eap->arg;
1544 char_u *p;
1545 int idx;
1546 sign_T *sp;
1547 buf_T *buf = NULL;
1548
1549 // Parse the subcommand.
1550 p = skiptowhite(arg);
1551 idx = sign_cmd_idx(arg, p);
1552 if (idx == SIGNCMD_LAST)
1553 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001554 semsg(_("E160: Unknown sign command: %s"), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001555 return;
1556 }
1557 arg = skipwhite(p);
1558
1559 if (idx <= SIGNCMD_LIST)
1560 {
1561 // Define, undefine or list signs.
1562 if (idx == SIGNCMD_LIST && *arg == NUL)
1563 {
1564 // ":sign list": list all defined signs
1565 for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next)
1566 sign_list_defined(sp);
1567 }
1568 else if (*arg == NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001569 emsg(_("E156: Missing sign name"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001570 else
1571 {
1572 char_u *name;
1573
1574 // Isolate the sign name. If it's a number skip leading zeroes,
1575 // so that "099" and "99" are the same sign. But keep "0".
1576 p = skiptowhite(arg);
1577 if (*p != NUL)
1578 *p++ = NUL;
1579 while (arg[0] == '0' && arg[1] != NUL)
1580 ++arg;
1581 name = vim_strsave(arg);
1582
1583 if (idx == SIGNCMD_DEFINE)
1584 sign_define_cmd(name, p);
1585 else if (idx == SIGNCMD_LIST)
1586 // ":sign list {name}"
1587 sign_list_by_name(name);
1588 else
1589 // ":sign undefine {name}"
1590 sign_undefine_by_name(name);
1591
1592 vim_free(name);
1593 return;
1594 }
1595 }
1596 else
1597 {
1598 int id = -1;
1599 linenr_T lnum = -1;
1600 char_u *sign_name = NULL;
1601 char_u *group = NULL;
1602 int prio = SIGN_DEF_PRIO;
1603
1604 // Parse command line arguments
1605 if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio,
1606 &buf, &lnum) == FAIL)
1607 return;
1608
1609 if (idx == SIGNCMD_PLACE)
1610 sign_place_cmd(buf, lnum, sign_name, id, group, prio);
1611 else if (idx == SIGNCMD_UNPLACE)
1612 sign_unplace_cmd(buf, lnum, sign_name, id, group);
1613 else if (idx == SIGNCMD_JUMP)
1614 sign_jump_cmd(buf, lnum, sign_name, id, group);
1615 }
1616}
1617
1618/*
1619 * Return information about a specified sign
1620 */
1621 static void
1622sign_getinfo(sign_T *sp, dict_T *retdict)
1623{
1624 char_u *p;
1625
1626 dict_add_string(retdict, "name", (char_u *)sp->sn_name);
1627 if (sp->sn_icon != NULL)
1628 dict_add_string(retdict, "icon", (char_u *)sp->sn_icon);
1629 if (sp->sn_text != NULL)
1630 dict_add_string(retdict, "text", (char_u *)sp->sn_text);
1631 if (sp->sn_line_hl > 0)
1632 {
1633 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1634 if (p == NULL)
1635 p = (char_u *)"NONE";
1636 dict_add_string(retdict, "linehl", (char_u *)p);
1637 }
1638 if (sp->sn_text_hl > 0)
1639 {
1640 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1641 if (p == NULL)
1642 p = (char_u *)"NONE";
1643 dict_add_string(retdict, "texthl", (char_u *)p);
1644 }
1645}
1646
1647/*
1648 * If 'name' is NULL, return a list of all the defined signs.
1649 * Otherwise, return information about the specified sign.
1650 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001651 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001652sign_getlist(char_u *name, list_T *retlist)
1653{
1654 sign_T *sp = first_sign;
1655 dict_T *dict;
1656
1657 if (name != NULL)
1658 {
1659 sp = sign_find(name, NULL);
1660 if (sp == NULL)
1661 return;
1662 }
1663
1664 for (; sp != NULL && !got_int; sp = sp->sn_next)
1665 {
1666 if ((dict = dict_alloc_id(aid_sign_getlist)) == NULL)
1667 return;
1668 if (list_append_dict(retlist, dict) == FAIL)
1669 return;
1670 sign_getinfo(sp, dict);
1671
1672 if (name != NULL) // handle only the specified sign
1673 break;
1674 }
1675}
1676
1677/*
1678 * Returns information about signs placed in a buffer as list of dicts.
1679 */
1680 void
1681get_buffer_signs(buf_T *buf, list_T *l)
1682{
1683 signlist_T *sign;
1684 dict_T *d;
1685
1686 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1687 {
1688 if ((d = sign_get_info(sign)) != NULL)
1689 list_append_dict(l, d);
1690 }
1691}
1692
1693/*
1694 * Return information about all the signs placed in a buffer
1695 */
1696 static void
1697sign_get_placed_in_buf(
1698 buf_T *buf,
1699 linenr_T lnum,
1700 int sign_id,
1701 char_u *sign_group,
1702 list_T *retlist)
1703{
1704 dict_T *d;
1705 list_T *l;
1706 signlist_T *sign;
1707 dict_T *sdict;
1708
1709 if ((d = dict_alloc_id(aid_sign_getplaced_dict)) == NULL)
1710 return;
1711 list_append_dict(retlist, d);
1712
1713 dict_add_number(d, "bufnr", (long)buf->b_fnum);
1714
1715 if ((l = list_alloc_id(aid_sign_getplaced_list)) == NULL)
1716 return;
1717 dict_add_list(d, "signs", l);
1718
1719 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1720 {
1721 if (!sign_in_group(sign, sign_group))
1722 continue;
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001723 if ((lnum == 0 && sign_id == 0)
1724 || (sign_id == 0 && lnum == sign->lnum)
1725 || (lnum == 0 && sign_id == sign->id)
1726 || (lnum == sign->lnum && sign_id == sign->id))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001727 {
1728 if ((sdict = sign_get_info(sign)) != NULL)
1729 list_append_dict(l, sdict);
1730 }
1731 }
1732}
1733
1734/*
1735 * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the
1736 * sign placed at the line number. If 'lnum' is zero, return all the signs
1737 * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers.
1738 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001739 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001740sign_get_placed(
1741 buf_T *buf,
1742 linenr_T lnum,
1743 int sign_id,
1744 char_u *sign_group,
1745 list_T *retlist)
1746{
1747 if (buf != NULL)
1748 sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist);
1749 else
1750 {
1751 FOR_ALL_BUFFERS(buf)
1752 {
1753 if (buf->b_signlist != NULL)
1754 sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist);
1755 }
1756 }
1757}
1758
1759# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1760/*
1761 * Allocate the icons. Called when the GUI has started. Allows defining
1762 * signs before it starts.
1763 */
1764 void
1765sign_gui_started(void)
1766{
1767 sign_T *sp;
1768
1769 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1770 if (sp->sn_icon != NULL)
1771 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
1772}
1773# endif
1774
1775/*
1776 * List one sign.
1777 */
1778 static void
1779sign_list_defined(sign_T *sp)
1780{
1781 char_u *p;
1782
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001783 smsg("sign %s", sp->sn_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001784 if (sp->sn_icon != NULL)
1785 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001786 msg_puts(" icon=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001787 msg_outtrans(sp->sn_icon);
1788# ifdef FEAT_SIGN_ICONS
1789 if (sp->sn_image == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001790 msg_puts(_(" (NOT FOUND)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001791# else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001792 msg_puts(_(" (not supported)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001793# endif
1794 }
1795 if (sp->sn_text != NULL)
1796 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001797 msg_puts(" text=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001798 msg_outtrans(sp->sn_text);
1799 }
1800 if (sp->sn_line_hl > 0)
1801 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001802 msg_puts(" linehl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001803 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1804 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001805 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001806 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001807 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001808 }
1809 if (sp->sn_text_hl > 0)
1810 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001811 msg_puts(" texthl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001812 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1813 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001814 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001815 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001816 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001817 }
1818}
1819
1820/*
1821 * Undefine a sign and free its memory.
1822 */
1823 static void
1824sign_undefine(sign_T *sp, sign_T *sp_prev)
1825{
1826 vim_free(sp->sn_name);
1827 vim_free(sp->sn_icon);
1828# ifdef FEAT_SIGN_ICONS
1829 if (sp->sn_image != NULL)
1830 {
1831 out_flush();
1832 gui_mch_destroy_sign(sp->sn_image);
1833 }
1834# endif
1835 vim_free(sp->sn_text);
1836 if (sp_prev == NULL)
1837 first_sign = sp->sn_next;
1838 else
1839 sp_prev->sn_next = sp->sn_next;
1840 vim_free(sp);
1841}
1842
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001843# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1844 void *
1845sign_get_image(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001846 int typenr) // the attribute which may have a sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001847{
1848 sign_T *sp;
1849
1850 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1851 if (sp->sn_typenr == typenr)
1852 return sp->sn_image;
1853 return NULL;
1854}
1855# endif
1856
1857/*
1858 * Undefine/free all signs.
1859 */
1860 void
1861free_signs(void)
1862{
1863 while (first_sign != NULL)
1864 sign_undefine(first_sign, NULL);
1865}
1866
1867# if defined(FEAT_CMDL_COMPL) || defined(PROTO)
1868static enum
1869{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001870 EXP_SUBCMD, // expand :sign sub-commands
1871 EXP_DEFINE, // expand :sign define {name} args
1872 EXP_PLACE, // expand :sign place {id} args
Bram Moolenaar3678f652019-02-17 14:50:25 +01001873 EXP_LIST, // expand :sign place args
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001874 EXP_UNPLACE, // expand :sign unplace"
Bram Moolenaar3678f652019-02-17 14:50:25 +01001875 EXP_SIGN_NAMES, // expand with name of placed signs
1876 EXP_SIGN_GROUPS // expand with name of placed sign groups
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001877} expand_what;
1878
1879/*
Bram Moolenaar3678f652019-02-17 14:50:25 +01001880 * Return the n'th sign name (used for command line completion)
1881 */
1882 static char_u *
1883get_nth_sign_name(int idx)
1884{
1885 int current_idx;
1886 sign_T *sp;
1887
1888 // Complete with name of signs already defined
1889 current_idx = 0;
1890 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1891 if (current_idx++ == idx)
1892 return sp->sn_name;
1893 return NULL;
1894}
1895
1896/*
1897 * Return the n'th sign group name (used for command line completion)
1898 */
1899 static char_u *
1900get_nth_sign_group_name(int idx)
1901{
1902 int current_idx;
1903 int todo;
1904 hashitem_T *hi;
1905 signgroup_T *group;
1906
1907 // Complete with name of sign groups already defined
1908 current_idx = 0;
1909 todo = (int)sg_table.ht_used;
1910 for (hi = sg_table.ht_array; todo > 0; ++hi)
1911 {
1912 if (!HASHITEM_EMPTY(hi))
1913 {
1914 --todo;
1915 if (current_idx++ == idx)
1916 {
1917 group = HI2SG(hi);
1918 return group->sg_name;
1919 }
1920 }
1921 }
1922 return NULL;
1923}
1924
1925/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001926 * Function given to ExpandGeneric() to obtain the sign command
1927 * expansion.
1928 */
1929 char_u *
1930get_sign_name(expand_T *xp UNUSED, int idx)
1931{
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001932 switch (expand_what)
1933 {
1934 case EXP_SUBCMD:
1935 return (char_u *)cmds[idx];
1936 case EXP_DEFINE:
1937 {
1938 char *define_arg[] =
1939 {
1940 "icon=", "linehl=", "text=", "texthl=", NULL
1941 };
1942 return (char_u *)define_arg[idx];
1943 }
1944 case EXP_PLACE:
1945 {
1946 char *place_arg[] =
1947 {
1948 "line=", "name=", "group=", "priority=", "file=",
1949 "buffer=", NULL
1950 };
1951 return (char_u *)place_arg[idx];
1952 }
Bram Moolenaar3678f652019-02-17 14:50:25 +01001953 case EXP_LIST:
1954 {
1955 char *list_arg[] =
1956 {
1957 "group=", "file=", "buffer=", NULL
1958 };
1959 return (char_u *)list_arg[idx];
1960 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001961 case EXP_UNPLACE:
1962 {
1963 char *unplace_arg[] = { "group=", "file=", "buffer=", NULL };
1964 return (char_u *)unplace_arg[idx];
1965 }
1966 case EXP_SIGN_NAMES:
Bram Moolenaar3678f652019-02-17 14:50:25 +01001967 return get_nth_sign_name(idx);
1968 case EXP_SIGN_GROUPS:
1969 return get_nth_sign_group_name(idx);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001970 default:
1971 return NULL;
1972 }
1973}
1974
1975/*
1976 * Handle command line completion for :sign command.
1977 */
1978 void
1979set_context_in_sign_cmd(expand_T *xp, char_u *arg)
1980{
1981 char_u *p;
1982 char_u *end_subcmd;
1983 char_u *last;
1984 int cmd_idx;
1985 char_u *begin_subcmd_args;
1986
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001987 // Default: expand subcommands.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001988 xp->xp_context = EXPAND_SIGN;
1989 expand_what = EXP_SUBCMD;
1990 xp->xp_pattern = arg;
1991
1992 end_subcmd = skiptowhite(arg);
1993 if (*end_subcmd == NUL)
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001994 // expand subcmd name
1995 // :sign {subcmd}<CTRL-D>
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001996 return;
1997
1998 cmd_idx = sign_cmd_idx(arg, end_subcmd);
1999
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002000 // :sign {subcmd} {subcmd_args}
2001 // |
2002 // begin_subcmd_args
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002003 begin_subcmd_args = skipwhite(end_subcmd);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002004
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002005 // expand last argument of subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002006
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002007 // :sign define {name} {args}...
2008 // |
2009 // p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002010
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002011 // Loop until reaching last argument.
Bram Moolenaar3678f652019-02-17 14:50:25 +01002012 p = begin_subcmd_args;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002013 do
2014 {
2015 p = skipwhite(p);
2016 last = p;
2017 p = skiptowhite(p);
2018 } while (*p != NUL);
2019
2020 p = vim_strchr(last, '=');
2021
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002022 // :sign define {name} {args}... {last}=
2023 // | |
2024 // last p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002025 if (p == NULL)
2026 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002027 // Expand last argument name (before equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002028 xp->xp_pattern = last;
2029 switch (cmd_idx)
2030 {
2031 case SIGNCMD_DEFINE:
2032 expand_what = EXP_DEFINE;
2033 break;
2034 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002035 // List placed signs
2036 if (VIM_ISDIGIT(*begin_subcmd_args))
2037 // :sign place {id} {args}...
2038 expand_what = EXP_PLACE;
2039 else
2040 // :sign place {args}...
2041 expand_what = EXP_LIST;
2042 break;
2043 case SIGNCMD_LIST:
2044 case SIGNCMD_UNDEFINE:
2045 // :sign list <CTRL-D>
2046 // :sign undefine <CTRL-D>
2047 expand_what = EXP_SIGN_NAMES;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002048 break;
2049 case SIGNCMD_JUMP:
2050 case SIGNCMD_UNPLACE:
2051 expand_what = EXP_UNPLACE;
2052 break;
2053 default:
2054 xp->xp_context = EXPAND_NOTHING;
2055 }
2056 }
2057 else
2058 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002059 // Expand last argument value (after equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002060 xp->xp_pattern = p + 1;
2061 switch (cmd_idx)
2062 {
2063 case SIGNCMD_DEFINE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002064 if (STRNCMP(last, "texthl", 6) == 0
2065 || STRNCMP(last, "linehl", 6) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002066 xp->xp_context = EXPAND_HIGHLIGHT;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002067 else if (STRNCMP(last, "icon", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002068 xp->xp_context = EXPAND_FILES;
2069 else
2070 xp->xp_context = EXPAND_NOTHING;
2071 break;
2072 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002073 if (STRNCMP(last, "name", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002074 expand_what = EXP_SIGN_NAMES;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002075 else if (STRNCMP(last, "group", 5) == 0)
2076 expand_what = EXP_SIGN_GROUPS;
2077 else if (STRNCMP(last, "file", 4) == 0)
2078 xp->xp_context = EXPAND_BUFFERS;
2079 else
2080 xp->xp_context = EXPAND_NOTHING;
2081 break;
2082 case SIGNCMD_UNPLACE:
2083 case SIGNCMD_JUMP:
2084 if (STRNCMP(last, "group", 5) == 0)
2085 expand_what = EXP_SIGN_GROUPS;
2086 else if (STRNCMP(last, "file", 4) == 0)
2087 xp->xp_context = EXPAND_BUFFERS;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002088 else
2089 xp->xp_context = EXPAND_NOTHING;
2090 break;
2091 default:
2092 xp->xp_context = EXPAND_NOTHING;
2093 }
2094 }
2095}
2096# endif
2097
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002098/*
2099 * "sign_define()" function
2100 */
2101 void
2102f_sign_define(typval_T *argvars, typval_T *rettv)
2103{
2104 char_u *name;
2105 dict_T *dict;
2106 char_u *icon = NULL;
2107 char_u *linehl = NULL;
2108 char_u *text = NULL;
2109 char_u *texthl = NULL;
2110
2111 rettv->vval.v_number = -1;
2112
2113 name = tv_get_string_chk(&argvars[0]);
2114 if (name == NULL)
2115 return;
2116
2117 if (argvars[1].v_type != VAR_UNKNOWN)
2118 {
2119 if (argvars[1].v_type != VAR_DICT)
2120 {
2121 emsg(_(e_dictreq));
2122 return;
2123 }
2124
2125 // sign attributes
2126 dict = argvars[1].vval.v_dict;
2127 if (dict_find(dict, (char_u *)"icon", -1) != NULL)
2128 icon = dict_get_string(dict, (char_u *)"icon", TRUE);
2129 if (dict_find(dict, (char_u *)"linehl", -1) != NULL)
2130 linehl = dict_get_string(dict, (char_u *)"linehl", TRUE);
2131 if (dict_find(dict, (char_u *)"text", -1) != NULL)
2132 text = dict_get_string(dict, (char_u *)"text", TRUE);
2133 if (dict_find(dict, (char_u *)"texthl", -1) != NULL)
2134 texthl = dict_get_string(dict, (char_u *)"texthl", TRUE);
2135 }
2136
2137 if (sign_define_by_name(name, icon, linehl, text, texthl) == OK)
2138 rettv->vval.v_number = 0;
2139
2140 vim_free(icon);
2141 vim_free(linehl);
2142 vim_free(text);
2143 vim_free(texthl);
2144}
2145
2146/*
2147 * "sign_getdefined()" function
2148 */
2149 void
2150f_sign_getdefined(typval_T *argvars, typval_T *rettv)
2151{
2152 char_u *name = NULL;
2153
2154 if (rettv_list_alloc_id(rettv, aid_sign_getdefined) != OK)
2155 return;
2156
2157 if (argvars[0].v_type != VAR_UNKNOWN)
2158 name = tv_get_string(&argvars[0]);
2159
2160 sign_getlist(name, rettv->vval.v_list);
2161}
2162
2163/*
2164 * "sign_getplaced()" function
2165 */
2166 void
2167f_sign_getplaced(typval_T *argvars, typval_T *rettv)
2168{
2169 buf_T *buf = NULL;
2170 dict_T *dict;
2171 dictitem_T *di;
2172 linenr_T lnum = 0;
2173 int sign_id = 0;
2174 char_u *group = NULL;
2175 int notanum = FALSE;
2176
2177 if (rettv_list_alloc_id(rettv, aid_sign_getplaced) != OK)
2178 return;
2179
2180 if (argvars[0].v_type != VAR_UNKNOWN)
2181 {
2182 // get signs placed in the specified buffer
2183 buf = get_buf_arg(&argvars[0]);
2184 if (buf == NULL)
2185 return;
2186
2187 if (argvars[1].v_type != VAR_UNKNOWN)
2188 {
2189 if (argvars[1].v_type != VAR_DICT ||
2190 ((dict = argvars[1].vval.v_dict) == NULL))
2191 {
2192 emsg(_(e_dictreq));
2193 return;
2194 }
2195 if ((di = dict_find(dict, (char_u *)"lnum", -1)) != NULL)
2196 {
2197 // get signs placed at this line
2198 (void)tv_get_number_chk(&di->di_tv, &notanum);
2199 if (notanum)
2200 return;
2201 lnum = tv_get_lnum(&di->di_tv);
2202 }
2203 if ((di = dict_find(dict, (char_u *)"id", -1)) != NULL)
2204 {
2205 // get sign placed with this identifier
2206 sign_id = (int)tv_get_number_chk(&di->di_tv, &notanum);
2207 if (notanum)
2208 return;
2209 }
2210 if ((di = dict_find(dict, (char_u *)"group", -1)) != NULL)
2211 {
2212 group = tv_get_string_chk(&di->di_tv);
2213 if (group == NULL)
2214 return;
2215 if (*group == '\0') // empty string means global group
2216 group = NULL;
2217 }
2218 }
2219 }
2220
2221 sign_get_placed(buf, lnum, sign_id, group, rettv->vval.v_list);
2222}
2223
2224/*
2225 * "sign_jump()" function
2226 */
2227 void
2228f_sign_jump(typval_T *argvars, typval_T *rettv)
2229{
2230 int sign_id;
2231 char_u *sign_group = NULL;
2232 buf_T *buf;
2233 int notanum = FALSE;
2234
2235 rettv->vval.v_number = -1;
2236
2237 // Sign identifier
2238 sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
2239 if (notanum)
2240 return;
2241 if (sign_id <= 0)
2242 {
2243 emsg(_(e_invarg));
2244 return;
2245 }
2246
2247 // Sign group
2248 sign_group = tv_get_string_chk(&argvars[1]);
2249 if (sign_group == NULL)
2250 return;
2251 if (sign_group[0] == '\0')
2252 sign_group = NULL; // global sign group
2253 else
2254 {
2255 sign_group = vim_strsave(sign_group);
2256 if (sign_group == NULL)
2257 return;
2258 }
2259
2260 // Buffer to place the sign
2261 buf = get_buf_arg(&argvars[2]);
2262 if (buf == NULL)
2263 goto cleanup;
2264
2265 rettv->vval.v_number = sign_jump(sign_id, sign_group, buf);
2266
2267cleanup:
2268 vim_free(sign_group);
2269}
2270
2271/*
2272 * "sign_place()" function
2273 */
2274 void
2275f_sign_place(typval_T *argvars, typval_T *rettv)
2276{
2277 int sign_id;
2278 char_u *group = NULL;
2279 char_u *sign_name;
2280 buf_T *buf;
2281 dict_T *dict;
2282 dictitem_T *di;
2283 linenr_T lnum = 0;
2284 int prio = SIGN_DEF_PRIO;
2285 int notanum = FALSE;
2286
2287 rettv->vval.v_number = -1;
2288
2289 // Sign identifier
2290 sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
2291 if (notanum)
2292 return;
2293 if (sign_id < 0)
2294 {
2295 emsg(_(e_invarg));
2296 return;
2297 }
2298
2299 // Sign group
2300 group = tv_get_string_chk(&argvars[1]);
2301 if (group == NULL)
2302 return;
2303 if (group[0] == '\0')
2304 group = NULL; // global sign group
2305 else
2306 {
2307 group = vim_strsave(group);
2308 if (group == NULL)
2309 return;
2310 }
2311
2312 // Sign name
2313 sign_name = tv_get_string_chk(&argvars[2]);
2314 if (sign_name == NULL)
2315 goto cleanup;
2316
2317 // Buffer to place the sign
2318 buf = get_buf_arg(&argvars[3]);
2319 if (buf == NULL)
2320 goto cleanup;
2321
2322 if (argvars[4].v_type != VAR_UNKNOWN)
2323 {
2324 if (argvars[4].v_type != VAR_DICT ||
2325 ((dict = argvars[4].vval.v_dict) == NULL))
2326 {
2327 emsg(_(e_dictreq));
2328 goto cleanup;
2329 }
2330
2331 // Line number where the sign is to be placed
2332 if ((di = dict_find(dict, (char_u *)"lnum", -1)) != NULL)
2333 {
2334 (void)tv_get_number_chk(&di->di_tv, &notanum);
2335 if (notanum)
2336 goto cleanup;
2337 lnum = tv_get_lnum(&di->di_tv);
2338 }
2339 if ((di = dict_find(dict, (char_u *)"priority", -1)) != NULL)
2340 {
2341 // Sign priority
2342 prio = (int)tv_get_number_chk(&di->di_tv, &notanum);
2343 if (notanum)
2344 goto cleanup;
2345 }
2346 }
2347
2348 if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK)
2349 rettv->vval.v_number = sign_id;
2350
2351cleanup:
2352 vim_free(group);
2353}
2354
2355/*
2356 * "sign_undefine()" function
2357 */
2358 void
2359f_sign_undefine(typval_T *argvars, typval_T *rettv)
2360{
2361 char_u *name;
2362
2363 rettv->vval.v_number = -1;
2364
2365 if (argvars[0].v_type == VAR_UNKNOWN)
2366 {
2367 // Free all the signs
2368 free_signs();
2369 rettv->vval.v_number = 0;
2370 }
2371 else
2372 {
2373 // Free only the specified sign
2374 name = tv_get_string_chk(&argvars[0]);
2375 if (name == NULL)
2376 return;
2377
2378 if (sign_undefine_by_name(name) == OK)
2379 rettv->vval.v_number = 0;
2380 }
2381}
2382
2383/*
2384 * "sign_unplace()" function
2385 */
2386 void
2387f_sign_unplace(typval_T *argvars, typval_T *rettv)
2388{
2389 dict_T *dict;
2390 dictitem_T *di;
2391 int sign_id = 0;
2392 buf_T *buf = NULL;
2393 char_u *group = NULL;
2394
2395 rettv->vval.v_number = -1;
2396
2397 if (argvars[0].v_type != VAR_STRING)
2398 {
2399 emsg(_(e_invarg));
2400 return;
2401 }
2402
2403 group = tv_get_string(&argvars[0]);
2404 if (group[0] == '\0')
2405 group = NULL; // global sign group
2406 else
2407 {
2408 group = vim_strsave(group);
2409 if (group == NULL)
2410 return;
2411 }
2412
2413 if (argvars[1].v_type != VAR_UNKNOWN)
2414 {
2415 if (argvars[1].v_type != VAR_DICT)
2416 {
2417 emsg(_(e_dictreq));
2418 goto cleanup;
2419 }
2420 dict = argvars[1].vval.v_dict;
2421
2422 if ((di = dict_find(dict, (char_u *)"buffer", -1)) != NULL)
2423 {
2424 buf = get_buf_arg(&di->di_tv);
2425 if (buf == NULL)
2426 goto cleanup;
2427 }
2428 if (dict_find(dict, (char_u *)"id", -1) != NULL)
2429 sign_id = dict_get_number(dict, (char_u *)"id");
2430 }
2431
2432 if (buf == NULL)
2433 {
2434 // Delete the sign in all the buffers
2435 FOR_ALL_BUFFERS(buf)
2436 if (sign_unplace(sign_id, group, buf, 0) == OK)
2437 rettv->vval.v_number = 0;
2438 }
2439 else
2440 {
2441 if (sign_unplace(sign_id, group, buf, 0) == OK)
2442 rettv->vval.v_number = 0;
2443 }
2444
2445cleanup:
2446 vim_free(group);
2447}
2448
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002449#endif /* FEAT_SIGNS */