blob: e8288d628348f504e7be9d1cdb5226aacfe75634 [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);
234 changed_cline_bef_curs();
235 }
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 Moolenaarbbea4702019-01-01 13:20:31 +0100534 changed_cline_bef_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);
654 changed_cline_bef_curs();
655 }
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
1011/*
Bram Moolenaar8144acb2019-01-14 23:08:18 +01001012 * Place a sign at the specified file location or update a sign.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001013 */
1014 int
1015sign_place(
1016 int *sign_id,
1017 char_u *sign_group,
1018 char_u *sign_name,
1019 buf_T *buf,
1020 linenr_T lnum,
1021 int prio)
1022{
1023 sign_T *sp;
1024
1025 // Check for reserved character '*' in group name
1026 if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0'))
1027 return FAIL;
1028
1029 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1030 if (STRCMP(sp->sn_name, sign_name) == 0)
1031 break;
1032 if (sp == NULL)
1033 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001034 semsg(_("E155: Unknown sign: %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001035 return FAIL;
1036 }
1037 if (*sign_id == 0)
1038 *sign_id = sign_group_get_next_signid(buf, sign_group);
1039
1040 if (lnum > 0)
1041 // ":sign place {id} line={lnum} name={name} file={fname}":
1042 // place a sign
1043 buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr);
1044 else
1045 // ":sign place {id} file={fname}": change sign type
1046 lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr);
1047 if (lnum > 0)
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001048 redraw_buf_line_later(buf, lnum);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001049 else
1050 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001051 semsg(_("E885: Not possible to change sign %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001052 return FAIL;
1053 }
1054
1055 return OK;
1056}
1057
1058/*
1059 * Unplace the specified sign
1060 */
1061 int
1062sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum)
1063{
1064 if (buf->b_signlist == NULL) // No signs in the buffer
1065 return OK;
1066
1067 if (sign_id == 0)
1068 {
1069 // Delete all the signs in the specified buffer
1070 redraw_buf_later(buf, NOT_VALID);
1071 buf_delete_signs(buf, sign_group);
1072 }
1073 else
1074 {
1075 linenr_T lnum;
1076
1077 // Delete only the specified signs
1078 lnum = buf_delsign(buf, atlnum, sign_id, sign_group);
1079 if (lnum == 0)
1080 return FAIL;
1081 }
1082
1083 return OK;
1084}
1085
1086/*
1087 * Unplace the sign at the current cursor line.
1088 */
1089 static void
1090sign_unplace_at_cursor(char_u *groupname)
1091{
1092 int id = -1;
1093
1094 id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname);
1095 if (id > 0)
1096 sign_unplace(id, groupname, curwin->w_buffer, curwin->w_cursor.lnum);
1097 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001098 emsg(_("E159: Missing sign number"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001099}
1100
1101/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001102 * Jump to a sign.
1103 */
1104 linenr_T
1105sign_jump(int sign_id, char_u *sign_group, buf_T *buf)
1106{
1107 linenr_T lnum;
1108
1109 if ((lnum = buf_findsign(buf, sign_id, sign_group)) <= 0)
1110 {
Bram Moolenaarb5443cc2019-01-15 20:19:40 +01001111 semsg(_("E157: Invalid sign ID: %d"), sign_id);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001112 return -1;
1113 }
1114
1115 // goto a sign ...
1116 if (buf_jump_open_win(buf) != NULL)
1117 { // ... in a current window
1118 curwin->w_cursor.lnum = lnum;
1119 check_cursor_lnum();
1120 beginline(BL_WHITE);
1121 }
1122 else
1123 { // ... not currently in a window
1124 char_u *cmd;
1125
1126 if (buf->b_fname == NULL)
1127 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001128 emsg(_("E934: Cannot jump to a buffer that does not have a name"));
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001129 return -1;
1130 }
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02001131 cmd = alloc(STRLEN(buf->b_fname) + 25);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001132 if (cmd == NULL)
1133 return -1;
1134 sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname);
1135 do_cmdline_cmd(cmd);
1136 vim_free(cmd);
1137 }
1138# ifdef FEAT_FOLDING
1139 foldOpenCursor();
1140# endif
1141
1142 return lnum;
1143}
1144
1145/*
1146 * ":sign define {name} ..." command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001147 */
1148 static void
1149sign_define_cmd(char_u *sign_name, char_u *cmdline)
1150{
1151 char_u *arg;
1152 char_u *p = cmdline;
1153 char_u *icon = NULL;
1154 char_u *text = NULL;
1155 char_u *linehl = NULL;
1156 char_u *texthl = NULL;
1157 int failed = FALSE;
1158
1159 // set values for a defined sign.
1160 for (;;)
1161 {
1162 arg = skipwhite(p);
1163 if (*arg == NUL)
1164 break;
1165 p = skiptowhite_esc(arg);
1166 if (STRNCMP(arg, "icon=", 5) == 0)
1167 {
1168 arg += 5;
1169 icon = vim_strnsave(arg, (int)(p - arg));
1170 }
1171 else if (STRNCMP(arg, "text=", 5) == 0)
1172 {
1173 arg += 5;
1174 text = vim_strnsave(arg, (int)(p - arg));
1175 }
1176 else if (STRNCMP(arg, "linehl=", 7) == 0)
1177 {
1178 arg += 7;
1179 linehl = vim_strnsave(arg, (int)(p - arg));
1180 }
1181 else if (STRNCMP(arg, "texthl=", 7) == 0)
1182 {
1183 arg += 7;
1184 texthl = vim_strnsave(arg, (int)(p - arg));
1185 }
1186 else
1187 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001188 semsg(_(e_invarg2), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001189 failed = TRUE;
1190 break;
1191 }
1192 }
1193
1194 if (!failed)
1195 sign_define_by_name(sign_name, icon, linehl, text, texthl);
1196
1197 vim_free(icon);
1198 vim_free(text);
1199 vim_free(linehl);
1200 vim_free(texthl);
1201}
1202
1203/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001204 * ":sign place" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001205 */
1206 static void
1207sign_place_cmd(
1208 buf_T *buf,
1209 linenr_T lnum,
1210 char_u *sign_name,
1211 int id,
1212 char_u *group,
1213 int prio)
1214{
1215 if (id <= 0)
1216 {
1217 // List signs placed in a file/buffer
1218 // :sign place file={fname}
1219 // :sign place group={group} file={fname}
1220 // :sign place group=* file={fname}
1221 // :sign place buffer={nr}
1222 // :sign place group={group} buffer={nr}
1223 // :sign place group=* buffer={nr}
1224 // :sign place
1225 // :sign place group={group}
1226 // :sign place group=*
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001227 if (lnum >= 0 || sign_name != NULL
1228 || (group != NULL && *group == '\0'))
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001229 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001230 else
1231 sign_list_placed(buf, group);
1232 }
1233 else
1234 {
1235 // Place a new sign
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001236 if (sign_name == NULL || buf == NULL
1237 || (group != NULL && *group == '\0'))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001238 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001239 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001240 return;
1241 }
1242
1243 sign_place(&id, group, sign_name, buf, lnum, prio);
1244 }
1245}
1246
1247/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001248 * ":sign unplace" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001249 */
1250 static void
1251sign_unplace_cmd(
1252 buf_T *buf,
1253 linenr_T lnum,
1254 char_u *sign_name,
1255 int id,
1256 char_u *group)
1257{
1258 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0'))
1259 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001260 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001261 return;
1262 }
1263
1264 if (id == -2)
1265 {
1266 if (buf != NULL)
1267 // :sign unplace * file={fname}
1268 // :sign unplace * group={group} file={fname}
1269 // :sign unplace * group=* file={fname}
1270 // :sign unplace * buffer={nr}
1271 // :sign unplace * group={group} buffer={nr}
1272 // :sign unplace * group=* buffer={nr}
1273 sign_unplace(0, group, buf, 0);
1274 else
1275 // :sign unplace *
1276 // :sign unplace * group={group}
1277 // :sign unplace * group=*
1278 FOR_ALL_BUFFERS(buf)
1279 if (buf->b_signlist != NULL)
1280 buf_delete_signs(buf, group);
1281 }
1282 else
1283 {
1284 if (buf != NULL)
1285 // :sign unplace {id} file={fname}
1286 // :sign unplace {id} group={group} file={fname}
1287 // :sign unplace {id} group=* file={fname}
1288 // :sign unplace {id} buffer={nr}
1289 // :sign unplace {id} group={group} buffer={nr}
1290 // :sign unplace {id} group=* buffer={nr}
1291 sign_unplace(id, group, buf, 0);
1292 else
1293 {
1294 if (id == -1)
1295 {
1296 // :sign unplace group={group}
1297 // :sign unplace group=*
1298 sign_unplace_at_cursor(group);
1299 }
1300 else
1301 {
1302 // :sign unplace {id}
1303 // :sign unplace {id} group={group}
1304 // :sign unplace {id} group=*
1305 FOR_ALL_BUFFERS(buf)
1306 sign_unplace(id, group, buf, 0);
1307 }
1308 }
1309 }
1310}
1311
1312/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001313 * Jump to a placed sign commands:
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001314 * :sign jump {id} file={fname}
1315 * :sign jump {id} buffer={nr}
1316 * :sign jump {id} group={group} file={fname}
1317 * :sign jump {id} group={group} buffer={nr}
1318 */
1319 static void
1320sign_jump_cmd(
1321 buf_T *buf,
1322 linenr_T lnum,
1323 char_u *sign_name,
1324 int id,
1325 char_u *group)
1326{
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001327 if (sign_name == NULL && group == NULL && id == -1)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001328 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001329 emsg(_(e_argreq));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001330 return;
1331 }
1332
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001333 if (buf == NULL || (group != NULL && *group == '\0')
1334 || lnum >= 0 || sign_name != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001335 {
1336 // File or buffer is not specified or an empty group is used
1337 // or a line number or a sign name is specified.
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001338 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001339 return;
1340 }
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001341 (void)sign_jump(id, group, buf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001342}
1343
1344/*
1345 * Parse the command line arguments for the ":sign place", ":sign unplace" and
1346 * ":sign jump" commands.
1347 * The supported arguments are: line={lnum} name={name} group={group}
1348 * priority={prio} and file={fname} or buffer={nr}.
1349 */
1350 static int
1351parse_sign_cmd_args(
1352 int cmd,
1353 char_u *arg,
1354 char_u **sign_name,
1355 int *signid,
1356 char_u **group,
1357 int *prio,
1358 buf_T **buf,
1359 linenr_T *lnum)
1360{
1361 char_u *arg1;
1362 char_u *name;
1363 char_u *filename = NULL;
Bram Moolenaarb589f952019-01-07 22:10:00 +01001364 int lnum_arg = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001365
1366 // first arg could be placed sign id
1367 arg1 = arg;
1368 if (VIM_ISDIGIT(*arg))
1369 {
1370 *signid = getdigits(&arg);
1371 if (!VIM_ISWHITE(*arg) && *arg != NUL)
1372 {
1373 *signid = -1;
1374 arg = arg1;
1375 }
1376 else
1377 arg = skipwhite(arg);
1378 }
1379
1380 while (*arg != NUL)
1381 {
1382 if (STRNCMP(arg, "line=", 5) == 0)
1383 {
1384 arg += 5;
1385 *lnum = atoi((char *)arg);
1386 arg = skiptowhite(arg);
Bram Moolenaarb589f952019-01-07 22:10:00 +01001387 lnum_arg = TRUE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001388 }
1389 else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE)
1390 {
1391 if (*signid != -1)
1392 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001393 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001394 return FAIL;
1395 }
1396 *signid = -2;
1397 arg = skiptowhite(arg + 1);
1398 }
1399 else if (STRNCMP(arg, "name=", 5) == 0)
1400 {
1401 arg += 5;
1402 name = arg;
1403 arg = skiptowhite(arg);
1404 if (*arg != NUL)
1405 *arg++ = NUL;
1406 while (name[0] == '0' && name[1] != NUL)
1407 ++name;
1408 *sign_name = name;
1409 }
1410 else if (STRNCMP(arg, "group=", 6) == 0)
1411 {
1412 arg += 6;
1413 *group = arg;
1414 arg = skiptowhite(arg);
1415 if (*arg != NUL)
1416 *arg++ = NUL;
1417 }
1418 else if (STRNCMP(arg, "priority=", 9) == 0)
1419 {
1420 arg += 9;
1421 *prio = atoi((char *)arg);
1422 arg = skiptowhite(arg);
1423 }
1424 else if (STRNCMP(arg, "file=", 5) == 0)
1425 {
1426 arg += 5;
1427 filename = arg;
1428 *buf = buflist_findname_exp(arg);
1429 break;
1430 }
1431 else if (STRNCMP(arg, "buffer=", 7) == 0)
1432 {
1433 arg += 7;
1434 filename = arg;
1435 *buf = buflist_findnr((int)getdigits(&arg));
1436 if (*skipwhite(arg) != NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001437 emsg(_(e_trailing));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001438 break;
1439 }
1440 else
1441 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001442 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001443 return FAIL;
1444 }
1445 arg = skipwhite(arg);
1446 }
1447
1448 if (filename != NULL && *buf == NULL)
1449 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001450 semsg(_("E158: Invalid buffer name: %s"), filename);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001451 return FAIL;
1452 }
1453
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001454 // If the filename is not supplied for the sign place or the sign jump
1455 // command, then use the current buffer.
Bram Moolenaarb589f952019-01-07 22:10:00 +01001456 if (filename == NULL && ((cmd == SIGNCMD_PLACE && lnum_arg)
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001457 || cmd == SIGNCMD_JUMP))
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001458 *buf = curwin->w_buffer;
1459
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001460 return OK;
1461}
1462
1463/*
1464 * ":sign" command
1465 */
1466 void
1467ex_sign(exarg_T *eap)
1468{
1469 char_u *arg = eap->arg;
1470 char_u *p;
1471 int idx;
1472 sign_T *sp;
1473 buf_T *buf = NULL;
1474
1475 // Parse the subcommand.
1476 p = skiptowhite(arg);
1477 idx = sign_cmd_idx(arg, p);
1478 if (idx == SIGNCMD_LAST)
1479 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001480 semsg(_("E160: Unknown sign command: %s"), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001481 return;
1482 }
1483 arg = skipwhite(p);
1484
1485 if (idx <= SIGNCMD_LIST)
1486 {
1487 // Define, undefine or list signs.
1488 if (idx == SIGNCMD_LIST && *arg == NUL)
1489 {
1490 // ":sign list": list all defined signs
1491 for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next)
1492 sign_list_defined(sp);
1493 }
1494 else if (*arg == NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001495 emsg(_("E156: Missing sign name"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001496 else
1497 {
1498 char_u *name;
1499
1500 // Isolate the sign name. If it's a number skip leading zeroes,
1501 // so that "099" and "99" are the same sign. But keep "0".
1502 p = skiptowhite(arg);
1503 if (*p != NUL)
1504 *p++ = NUL;
1505 while (arg[0] == '0' && arg[1] != NUL)
1506 ++arg;
1507 name = vim_strsave(arg);
1508
1509 if (idx == SIGNCMD_DEFINE)
1510 sign_define_cmd(name, p);
1511 else if (idx == SIGNCMD_LIST)
1512 // ":sign list {name}"
1513 sign_list_by_name(name);
1514 else
1515 // ":sign undefine {name}"
1516 sign_undefine_by_name(name);
1517
1518 vim_free(name);
1519 return;
1520 }
1521 }
1522 else
1523 {
1524 int id = -1;
1525 linenr_T lnum = -1;
1526 char_u *sign_name = NULL;
1527 char_u *group = NULL;
1528 int prio = SIGN_DEF_PRIO;
1529
1530 // Parse command line arguments
1531 if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio,
1532 &buf, &lnum) == FAIL)
1533 return;
1534
1535 if (idx == SIGNCMD_PLACE)
1536 sign_place_cmd(buf, lnum, sign_name, id, group, prio);
1537 else if (idx == SIGNCMD_UNPLACE)
1538 sign_unplace_cmd(buf, lnum, sign_name, id, group);
1539 else if (idx == SIGNCMD_JUMP)
1540 sign_jump_cmd(buf, lnum, sign_name, id, group);
1541 }
1542}
1543
1544/*
1545 * Return information about a specified sign
1546 */
1547 static void
1548sign_getinfo(sign_T *sp, dict_T *retdict)
1549{
1550 char_u *p;
1551
1552 dict_add_string(retdict, "name", (char_u *)sp->sn_name);
1553 if (sp->sn_icon != NULL)
1554 dict_add_string(retdict, "icon", (char_u *)sp->sn_icon);
1555 if (sp->sn_text != NULL)
1556 dict_add_string(retdict, "text", (char_u *)sp->sn_text);
1557 if (sp->sn_line_hl > 0)
1558 {
1559 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1560 if (p == NULL)
1561 p = (char_u *)"NONE";
1562 dict_add_string(retdict, "linehl", (char_u *)p);
1563 }
1564 if (sp->sn_text_hl > 0)
1565 {
1566 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1567 if (p == NULL)
1568 p = (char_u *)"NONE";
1569 dict_add_string(retdict, "texthl", (char_u *)p);
1570 }
1571}
1572
1573/*
1574 * If 'name' is NULL, return a list of all the defined signs.
1575 * Otherwise, return information about the specified sign.
1576 */
1577 void
1578sign_getlist(char_u *name, list_T *retlist)
1579{
1580 sign_T *sp = first_sign;
1581 dict_T *dict;
1582
1583 if (name != NULL)
1584 {
1585 sp = sign_find(name, NULL);
1586 if (sp == NULL)
1587 return;
1588 }
1589
1590 for (; sp != NULL && !got_int; sp = sp->sn_next)
1591 {
1592 if ((dict = dict_alloc_id(aid_sign_getlist)) == NULL)
1593 return;
1594 if (list_append_dict(retlist, dict) == FAIL)
1595 return;
1596 sign_getinfo(sp, dict);
1597
1598 if (name != NULL) // handle only the specified sign
1599 break;
1600 }
1601}
1602
1603/*
1604 * Returns information about signs placed in a buffer as list of dicts.
1605 */
1606 void
1607get_buffer_signs(buf_T *buf, list_T *l)
1608{
1609 signlist_T *sign;
1610 dict_T *d;
1611
1612 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1613 {
1614 if ((d = sign_get_info(sign)) != NULL)
1615 list_append_dict(l, d);
1616 }
1617}
1618
1619/*
1620 * Return information about all the signs placed in a buffer
1621 */
1622 static void
1623sign_get_placed_in_buf(
1624 buf_T *buf,
1625 linenr_T lnum,
1626 int sign_id,
1627 char_u *sign_group,
1628 list_T *retlist)
1629{
1630 dict_T *d;
1631 list_T *l;
1632 signlist_T *sign;
1633 dict_T *sdict;
1634
1635 if ((d = dict_alloc_id(aid_sign_getplaced_dict)) == NULL)
1636 return;
1637 list_append_dict(retlist, d);
1638
1639 dict_add_number(d, "bufnr", (long)buf->b_fnum);
1640
1641 if ((l = list_alloc_id(aid_sign_getplaced_list)) == NULL)
1642 return;
1643 dict_add_list(d, "signs", l);
1644
1645 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1646 {
1647 if (!sign_in_group(sign, sign_group))
1648 continue;
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001649 if ((lnum == 0 && sign_id == 0)
1650 || (sign_id == 0 && lnum == sign->lnum)
1651 || (lnum == 0 && sign_id == sign->id)
1652 || (lnum == sign->lnum && sign_id == sign->id))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001653 {
1654 if ((sdict = sign_get_info(sign)) != NULL)
1655 list_append_dict(l, sdict);
1656 }
1657 }
1658}
1659
1660/*
1661 * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the
1662 * sign placed at the line number. If 'lnum' is zero, return all the signs
1663 * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers.
1664 */
1665 void
1666sign_get_placed(
1667 buf_T *buf,
1668 linenr_T lnum,
1669 int sign_id,
1670 char_u *sign_group,
1671 list_T *retlist)
1672{
1673 if (buf != NULL)
1674 sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist);
1675 else
1676 {
1677 FOR_ALL_BUFFERS(buf)
1678 {
1679 if (buf->b_signlist != NULL)
1680 sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist);
1681 }
1682 }
1683}
1684
1685# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1686/*
1687 * Allocate the icons. Called when the GUI has started. Allows defining
1688 * signs before it starts.
1689 */
1690 void
1691sign_gui_started(void)
1692{
1693 sign_T *sp;
1694
1695 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1696 if (sp->sn_icon != NULL)
1697 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
1698}
1699# endif
1700
1701/*
1702 * List one sign.
1703 */
1704 static void
1705sign_list_defined(sign_T *sp)
1706{
1707 char_u *p;
1708
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001709 smsg("sign %s", sp->sn_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001710 if (sp->sn_icon != NULL)
1711 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001712 msg_puts(" icon=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001713 msg_outtrans(sp->sn_icon);
1714# ifdef FEAT_SIGN_ICONS
1715 if (sp->sn_image == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001716 msg_puts(_(" (NOT FOUND)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001717# else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001718 msg_puts(_(" (not supported)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001719# endif
1720 }
1721 if (sp->sn_text != NULL)
1722 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001723 msg_puts(" text=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001724 msg_outtrans(sp->sn_text);
1725 }
1726 if (sp->sn_line_hl > 0)
1727 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001728 msg_puts(" linehl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001729 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1730 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001731 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001732 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001733 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001734 }
1735 if (sp->sn_text_hl > 0)
1736 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001737 msg_puts(" texthl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001738 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1739 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001740 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001741 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001742 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001743 }
1744}
1745
1746/*
1747 * Undefine a sign and free its memory.
1748 */
1749 static void
1750sign_undefine(sign_T *sp, sign_T *sp_prev)
1751{
1752 vim_free(sp->sn_name);
1753 vim_free(sp->sn_icon);
1754# ifdef FEAT_SIGN_ICONS
1755 if (sp->sn_image != NULL)
1756 {
1757 out_flush();
1758 gui_mch_destroy_sign(sp->sn_image);
1759 }
1760# endif
1761 vim_free(sp->sn_text);
1762 if (sp_prev == NULL)
1763 first_sign = sp->sn_next;
1764 else
1765 sp_prev->sn_next = sp->sn_next;
1766 vim_free(sp);
1767}
1768
1769/*
1770 * Get highlighting attribute for sign "typenr".
1771 * If "line" is TRUE: line highl, if FALSE: text highl.
1772 */
1773 int
1774sign_get_attr(int typenr, int line)
1775{
1776 sign_T *sp;
1777
1778 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1779 if (sp->sn_typenr == typenr)
1780 {
1781 if (line)
1782 {
1783 if (sp->sn_line_hl > 0)
1784 return syn_id2attr(sp->sn_line_hl);
1785 }
1786 else
1787 {
1788 if (sp->sn_text_hl > 0)
1789 return syn_id2attr(sp->sn_text_hl);
1790 }
1791 break;
1792 }
1793 return 0;
1794}
1795
1796/*
1797 * Get text mark for sign "typenr".
1798 * Returns NULL if there isn't one.
1799 */
1800 char_u *
1801sign_get_text(int typenr)
1802{
1803 sign_T *sp;
1804
1805 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1806 if (sp->sn_typenr == typenr)
1807 return sp->sn_text;
1808 return NULL;
1809}
1810
1811# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1812 void *
1813sign_get_image(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001814 int typenr) // the attribute which may have a sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001815{
1816 sign_T *sp;
1817
1818 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1819 if (sp->sn_typenr == typenr)
1820 return sp->sn_image;
1821 return NULL;
1822}
1823# endif
1824
1825/*
1826 * Undefine/free all signs.
1827 */
1828 void
1829free_signs(void)
1830{
1831 while (first_sign != NULL)
1832 sign_undefine(first_sign, NULL);
1833}
1834
1835# if defined(FEAT_CMDL_COMPL) || defined(PROTO)
1836static enum
1837{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001838 EXP_SUBCMD, // expand :sign sub-commands
1839 EXP_DEFINE, // expand :sign define {name} args
1840 EXP_PLACE, // expand :sign place {id} args
Bram Moolenaar3678f652019-02-17 14:50:25 +01001841 EXP_LIST, // expand :sign place args
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001842 EXP_UNPLACE, // expand :sign unplace"
Bram Moolenaar3678f652019-02-17 14:50:25 +01001843 EXP_SIGN_NAMES, // expand with name of placed signs
1844 EXP_SIGN_GROUPS // expand with name of placed sign groups
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001845} expand_what;
1846
1847/*
Bram Moolenaar3678f652019-02-17 14:50:25 +01001848 * Return the n'th sign name (used for command line completion)
1849 */
1850 static char_u *
1851get_nth_sign_name(int idx)
1852{
1853 int current_idx;
1854 sign_T *sp;
1855
1856 // Complete with name of signs already defined
1857 current_idx = 0;
1858 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1859 if (current_idx++ == idx)
1860 return sp->sn_name;
1861 return NULL;
1862}
1863
1864/*
1865 * Return the n'th sign group name (used for command line completion)
1866 */
1867 static char_u *
1868get_nth_sign_group_name(int idx)
1869{
1870 int current_idx;
1871 int todo;
1872 hashitem_T *hi;
1873 signgroup_T *group;
1874
1875 // Complete with name of sign groups already defined
1876 current_idx = 0;
1877 todo = (int)sg_table.ht_used;
1878 for (hi = sg_table.ht_array; todo > 0; ++hi)
1879 {
1880 if (!HASHITEM_EMPTY(hi))
1881 {
1882 --todo;
1883 if (current_idx++ == idx)
1884 {
1885 group = HI2SG(hi);
1886 return group->sg_name;
1887 }
1888 }
1889 }
1890 return NULL;
1891}
1892
1893/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001894 * Function given to ExpandGeneric() to obtain the sign command
1895 * expansion.
1896 */
1897 char_u *
1898get_sign_name(expand_T *xp UNUSED, int idx)
1899{
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001900 switch (expand_what)
1901 {
1902 case EXP_SUBCMD:
1903 return (char_u *)cmds[idx];
1904 case EXP_DEFINE:
1905 {
1906 char *define_arg[] =
1907 {
1908 "icon=", "linehl=", "text=", "texthl=", NULL
1909 };
1910 return (char_u *)define_arg[idx];
1911 }
1912 case EXP_PLACE:
1913 {
1914 char *place_arg[] =
1915 {
1916 "line=", "name=", "group=", "priority=", "file=",
1917 "buffer=", NULL
1918 };
1919 return (char_u *)place_arg[idx];
1920 }
Bram Moolenaar3678f652019-02-17 14:50:25 +01001921 case EXP_LIST:
1922 {
1923 char *list_arg[] =
1924 {
1925 "group=", "file=", "buffer=", NULL
1926 };
1927 return (char_u *)list_arg[idx];
1928 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001929 case EXP_UNPLACE:
1930 {
1931 char *unplace_arg[] = { "group=", "file=", "buffer=", NULL };
1932 return (char_u *)unplace_arg[idx];
1933 }
1934 case EXP_SIGN_NAMES:
Bram Moolenaar3678f652019-02-17 14:50:25 +01001935 return get_nth_sign_name(idx);
1936 case EXP_SIGN_GROUPS:
1937 return get_nth_sign_group_name(idx);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001938 default:
1939 return NULL;
1940 }
1941}
1942
1943/*
1944 * Handle command line completion for :sign command.
1945 */
1946 void
1947set_context_in_sign_cmd(expand_T *xp, char_u *arg)
1948{
1949 char_u *p;
1950 char_u *end_subcmd;
1951 char_u *last;
1952 int cmd_idx;
1953 char_u *begin_subcmd_args;
1954
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001955 // Default: expand subcommands.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001956 xp->xp_context = EXPAND_SIGN;
1957 expand_what = EXP_SUBCMD;
1958 xp->xp_pattern = arg;
1959
1960 end_subcmd = skiptowhite(arg);
1961 if (*end_subcmd == NUL)
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001962 // expand subcmd name
1963 // :sign {subcmd}<CTRL-D>
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001964 return;
1965
1966 cmd_idx = sign_cmd_idx(arg, end_subcmd);
1967
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001968 // :sign {subcmd} {subcmd_args}
1969 // |
1970 // begin_subcmd_args
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001971 begin_subcmd_args = skipwhite(end_subcmd);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001972
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001973 // expand last argument of subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001974
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001975 // :sign define {name} {args}...
1976 // |
1977 // p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001978
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001979 // Loop until reaching last argument.
Bram Moolenaar3678f652019-02-17 14:50:25 +01001980 p = begin_subcmd_args;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001981 do
1982 {
1983 p = skipwhite(p);
1984 last = p;
1985 p = skiptowhite(p);
1986 } while (*p != NUL);
1987
1988 p = vim_strchr(last, '=');
1989
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001990 // :sign define {name} {args}... {last}=
1991 // | |
1992 // last p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001993 if (p == NULL)
1994 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001995 // Expand last argument name (before equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001996 xp->xp_pattern = last;
1997 switch (cmd_idx)
1998 {
1999 case SIGNCMD_DEFINE:
2000 expand_what = EXP_DEFINE;
2001 break;
2002 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002003 // List placed signs
2004 if (VIM_ISDIGIT(*begin_subcmd_args))
2005 // :sign place {id} {args}...
2006 expand_what = EXP_PLACE;
2007 else
2008 // :sign place {args}...
2009 expand_what = EXP_LIST;
2010 break;
2011 case SIGNCMD_LIST:
2012 case SIGNCMD_UNDEFINE:
2013 // :sign list <CTRL-D>
2014 // :sign undefine <CTRL-D>
2015 expand_what = EXP_SIGN_NAMES;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002016 break;
2017 case SIGNCMD_JUMP:
2018 case SIGNCMD_UNPLACE:
2019 expand_what = EXP_UNPLACE;
2020 break;
2021 default:
2022 xp->xp_context = EXPAND_NOTHING;
2023 }
2024 }
2025 else
2026 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002027 // Expand last argument value (after equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002028 xp->xp_pattern = p + 1;
2029 switch (cmd_idx)
2030 {
2031 case SIGNCMD_DEFINE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002032 if (STRNCMP(last, "texthl", 6) == 0
2033 || STRNCMP(last, "linehl", 6) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002034 xp->xp_context = EXPAND_HIGHLIGHT;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002035 else if (STRNCMP(last, "icon", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002036 xp->xp_context = EXPAND_FILES;
2037 else
2038 xp->xp_context = EXPAND_NOTHING;
2039 break;
2040 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002041 if (STRNCMP(last, "name", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002042 expand_what = EXP_SIGN_NAMES;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002043 else if (STRNCMP(last, "group", 5) == 0)
2044 expand_what = EXP_SIGN_GROUPS;
2045 else if (STRNCMP(last, "file", 4) == 0)
2046 xp->xp_context = EXPAND_BUFFERS;
2047 else
2048 xp->xp_context = EXPAND_NOTHING;
2049 break;
2050 case SIGNCMD_UNPLACE:
2051 case SIGNCMD_JUMP:
2052 if (STRNCMP(last, "group", 5) == 0)
2053 expand_what = EXP_SIGN_GROUPS;
2054 else if (STRNCMP(last, "file", 4) == 0)
2055 xp->xp_context = EXPAND_BUFFERS;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002056 else
2057 xp->xp_context = EXPAND_NOTHING;
2058 break;
2059 default:
2060 xp->xp_context = EXPAND_NOTHING;
2061 }
2062 }
2063}
2064# endif
2065
2066#endif /* FEAT_SIGNS */