blob: 9766d4e75dd6bd12f08537759c8350f45e68d614 [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 Moolenaar51e14382019-05-25 20:21:28 +020088 group = (signgroup_T *)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 Moolenaar18a4ba22019-05-24 19:39:03 +0200204 newsign = (signlist_T *)lalloc_id(sizeof(signlist_T), FALSE,
205 aid_insert_sign);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100206 if (newsign != NULL)
207 {
208 newsign->id = id;
209 newsign->lnum = lnum;
210 newsign->typenr = typenr;
211 if (group != NULL)
212 {
213 newsign->group = sign_group_ref(group);
214 if (newsign->group == NULL)
215 {
216 vim_free(newsign);
217 return;
218 }
219 }
220 else
221 newsign->group = NULL;
222 newsign->priority = prio;
223 newsign->next = next;
224 newsign->prev = prev;
225 if (next != NULL)
226 next->prev = newsign;
227
228 if (prev == NULL)
229 {
230 // When adding first sign need to redraw the windows to create the
231 // column for signs.
232 if (buf->b_signlist == NULL)
233 {
234 redraw_buf_later(buf, NOT_VALID);
235 changed_cline_bef_curs();
236 }
237
238 // first sign in signlist
239 buf->b_signlist = newsign;
240#ifdef FEAT_NETBEANS_INTG
241 if (netbeans_active())
242 buf->b_has_sign_column = TRUE;
243#endif
244 }
245 else
246 prev->next = newsign;
247 }
248}
249
250/*
251 * Insert a new sign sorted by line number and sign priority.
252 */
253 static void
254insert_sign_by_lnum_prio(
255 buf_T *buf, // buffer to store sign in
256 signlist_T *prev, // previous sign entry
257 int id, // sign ID
258 char_u *group, // sign group; NULL for global group
259 int prio, // sign priority
260 linenr_T lnum, // line number which gets the mark
261 int typenr) // typenr of sign we are adding
262{
263 signlist_T *sign;
264
265 // keep signs sorted by lnum and by priority: insert new sign at
266 // the proper position in the list for this lnum.
267 while (prev != NULL && prev->lnum == lnum && prev->priority <= prio)
268 prev = prev->prev;
269 if (prev == NULL)
270 sign = buf->b_signlist;
271 else
272 sign = prev->next;
273
274 insert_sign(buf, prev, sign, id, group, prio, lnum, typenr);
275}
276
277/*
278 * Get the name of a sign by its typenr.
279 */
280 static char_u *
281sign_typenr2name(int typenr)
282{
283 sign_T *sp;
284
285 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
286 if (sp->sn_typenr == typenr)
287 return sp->sn_name;
288 return (char_u *)_("[Deleted]");
289}
290
291/*
292 * Return information about a sign in a Dict
293 */
294 static dict_T *
295sign_get_info(signlist_T *sign)
296{
297 dict_T *d;
298
299 if ((d = dict_alloc_id(aid_sign_getinfo)) == NULL)
300 return NULL;
301 dict_add_number(d, "id", sign->id);
302 dict_add_string(d, "group", (sign->group == NULL) ?
303 (char_u *)"" : sign->group->sg_name);
304 dict_add_number(d, "lnum", sign->lnum);
305 dict_add_string(d, "name", sign_typenr2name(sign->typenr));
306 dict_add_number(d, "priority", sign->priority);
307
308 return d;
309}
310
311/*
312 * Add the sign into the signlist. Find the right spot to do it though.
313 */
314 static void
315buf_addsign(
316 buf_T *buf, // buffer to store sign in
317 int id, // sign ID
318 char_u *groupname, // sign group
319 int prio, // sign priority
320 linenr_T lnum, // line number which gets the mark
321 int typenr) // typenr of sign we are adding
322{
323 signlist_T *sign; // a sign in the signlist
324 signlist_T *prev; // the previous sign
325
326 prev = NULL;
327 FOR_ALL_SIGNS_IN_BUF(buf, sign)
328 {
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100329 if (lnum == sign->lnum && id == sign->id
330 && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100331 {
332 // Update an existing sign
333 sign->typenr = typenr;
334 return;
335 }
336 else if (lnum < sign->lnum)
337 {
338 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio,
339 lnum, typenr);
340 return;
341 }
342 prev = sign;
343 }
344
345 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr);
346 return;
347}
348
349/*
350 * For an existing, placed sign "markId" change the type to "typenr".
351 * Returns the line number of the sign, or zero if the sign is not found.
352 */
353 static linenr_T
354buf_change_sign_type(
355 buf_T *buf, // buffer to store sign in
356 int markId, // sign ID
357 char_u *group, // sign group
358 int typenr) // typenr of sign we are adding
359{
360 signlist_T *sign; // a sign in the signlist
361
362 FOR_ALL_SIGNS_IN_BUF(buf, sign)
363 {
364 if (sign->id == markId && sign_in_group(sign, group))
365 {
366 sign->typenr = typenr;
367 return sign->lnum;
368 }
369 }
370
371 return (linenr_T)0;
372}
373
374/*
375 * Return the type number of the sign at line number 'lnum' in buffer 'buf'
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100376 * which has the attribute specified by 'type'. Returns 0 if a sign is not
377 * found at the line number or it doesn't have the specified attribute.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100378 */
379 int
380buf_getsigntype(
381 buf_T *buf,
382 linenr_T lnum,
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100383 int type) // SIGN_ICON, SIGN_TEXT, SIGN_ANY, SIGN_LINEHL
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100384{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100385 signlist_T *sign; // a sign in a b_signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100386
387 FOR_ALL_SIGNS_IN_BUF(buf, sign)
388 if (sign->lnum == lnum
389 && (type == SIGN_ANY
390# ifdef FEAT_SIGN_ICONS
391 || (type == SIGN_ICON
392 && sign_get_image(sign->typenr) != NULL)
393# endif
394 || (type == SIGN_TEXT
395 && sign_get_text(sign->typenr) != NULL)
396 || (type == SIGN_LINEHL
397 && sign_get_attr(sign->typenr, TRUE) != 0)))
398 return sign->typenr;
399 return 0;
400}
401
402/*
403 * Delete sign 'id' in group 'group' from buffer 'buf'.
404 * If 'id' is zero, then delete all the signs in group 'group'. Otherwise
405 * delete only the specified sign.
406 * If 'group' is '*', then delete the sign in all the groups. If 'group' is
407 * NULL, then delete the sign in the global group. Otherwise delete the sign in
408 * the specified group.
409 * Returns the line number of the deleted sign. If multiple signs are deleted,
410 * then returns the line number of the last sign deleted.
411 */
412 linenr_T
413buf_delsign(
414 buf_T *buf, // buffer sign is stored in
415 linenr_T atlnum, // sign at this line, 0 - at any line
416 int id, // sign id
417 char_u *group) // sign group
418{
419 signlist_T **lastp; // pointer to pointer to current sign
420 signlist_T *sign; // a sign in a b_signlist
421 signlist_T *next; // the next sign in a b_signlist
422 linenr_T lnum; // line number whose sign was deleted
423
424 lastp = &buf->b_signlist;
425 lnum = 0;
426 for (sign = buf->b_signlist; sign != NULL; sign = next)
427 {
428 next = sign->next;
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100429 if ((id == 0 || sign->id == id)
430 && (atlnum == 0 || sign->lnum == atlnum)
431 && sign_in_group(sign, group))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100432
433 {
434 *lastp = next;
435 if (next != NULL)
436 next->prev = sign->prev;
437 lnum = sign->lnum;
438 if (sign->group != NULL)
439 sign_group_unref(sign->group->sg_name);
440 vim_free(sign);
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100441 redraw_buf_line_later(buf, lnum);
442
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100443 // Check whether only one sign needs to be deleted
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100444 // If deleting a sign with a specific identifier in a particular
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100445 // group or deleting any sign at a particular line number, delete
446 // only one sign.
447 if (group == NULL
448 || (*group != '*' && id != 0)
449 || (*group == '*' && atlnum != 0))
450 break;
451 }
452 else
453 lastp = &sign->next;
454 }
455
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100456 // When deleting the last sign the cursor position may change, because the
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100457 // sign columns no longer shows. And the 'signcolumn' may be hidden.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100458 if (buf->b_signlist == NULL)
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100459 {
460 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100461 changed_cline_bef_curs();
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100462 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100463
464 return lnum;
465}
466
467
468/*
469 * Find the line number of the sign with the requested id in group 'group'. If
470 * the sign does not exist, return 0 as the line number. This will still let
471 * the correct file get loaded.
472 */
473 int
474buf_findsign(
475 buf_T *buf, // buffer to store sign in
476 int id, // sign ID
477 char_u *group) // sign group
478{
479 signlist_T *sign; // a sign in the signlist
480
481 FOR_ALL_SIGNS_IN_BUF(buf, sign)
482 if (sign->id == id && sign_in_group(sign, group))
483 return sign->lnum;
484
485 return 0;
486}
487
488/*
489 * Return the sign at line 'lnum' in buffer 'buf'. Returns NULL if a sign is
490 * not found at the line. If 'groupname' is NULL, searches in the global group.
491 */
492 static signlist_T *
493buf_getsign_at_line(
494 buf_T *buf, // buffer whose sign we are searching for
495 linenr_T lnum, // line number of sign
496 char_u *groupname) // sign group name
497{
498 signlist_T *sign; // a sign in the signlist
499
500 FOR_ALL_SIGNS_IN_BUF(buf, sign)
501 if (sign->lnum == lnum && sign_in_group(sign, groupname))
502 return sign;
503
504 return NULL;
505}
506
507/*
508 * Return the identifier of the sign at line number 'lnum' in buffer 'buf'.
509 */
510 int
511buf_findsign_id(
512 buf_T *buf, // buffer whose sign we are searching for
513 linenr_T lnum, // line number of sign
514 char_u *groupname) // sign group name
515{
516 signlist_T *sign; // a sign in the signlist
517
518 sign = buf_getsign_at_line(buf, lnum, groupname);
519 if (sign != NULL)
520 return sign->id;
521
522 return 0;
523}
524
525# if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
526/*
527 * See if a given type of sign exists on a specific line.
528 */
529 int
530buf_findsigntype_id(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100531 buf_T *buf, // buffer whose sign we are searching for
532 linenr_T lnum, // line number of sign
533 int typenr) // sign type number
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100534{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100535 signlist_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100536
537 FOR_ALL_SIGNS_IN_BUF(buf, sign)
538 if (sign->lnum == lnum && sign->typenr == typenr)
539 return sign->id;
540
541 return 0;
542}
543
544
545# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
546/*
547 * Return the number of icons on the given line.
548 */
549 int
550buf_signcount(buf_T *buf, linenr_T lnum)
551{
552 signlist_T *sign; // a sign in the signlist
553 int count = 0;
554
555 FOR_ALL_SIGNS_IN_BUF(buf, sign)
556 if (sign->lnum == lnum)
557 if (sign_get_image(sign->typenr) != NULL)
558 count++;
559
560 return count;
561}
562# endif /* FEAT_SIGN_ICONS */
563# endif /* FEAT_NETBEANS_INTG */
564
565/*
566 * Delete signs in group 'group' in buffer "buf". If 'group' is '*', then
567 * delete all the signs.
568 */
569 void
570buf_delete_signs(buf_T *buf, char_u *group)
571{
572 signlist_T *sign;
573 signlist_T **lastp; // pointer to pointer to current sign
574 signlist_T *next;
575
576 // When deleting the last sign need to redraw the windows to remove the
577 // sign column. Not when curwin is NULL (this means we're exiting).
578 if (buf->b_signlist != NULL && curwin != NULL)
579 {
580 redraw_buf_later(buf, NOT_VALID);
581 changed_cline_bef_curs();
582 }
583
584 lastp = &buf->b_signlist;
585 for (sign = buf->b_signlist; sign != NULL; sign = next)
586 {
587 next = sign->next;
588 if (sign_in_group(sign, group))
589 {
590 *lastp = next;
591 if (next != NULL)
592 next->prev = sign->prev;
593 if (sign->group != NULL)
594 sign_group_unref(sign->group->sg_name);
595 vim_free(sign);
596 }
597 else
598 lastp = &sign->next;
599 }
600}
601
602/*
603 * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers.
604 */
605 static void
606sign_list_placed(buf_T *rbuf, char_u *sign_group)
607{
608 buf_T *buf;
609 signlist_T *sign;
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100610 char lbuf[MSG_BUF_LEN];
611 char group[MSG_BUF_LEN];
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100612
Bram Moolenaar32526b32019-01-19 17:43:09 +0100613 msg_puts_title(_("\n--- Signs ---"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100614 msg_putchar('\n');
615 if (rbuf == NULL)
616 buf = firstbuf;
617 else
618 buf = rbuf;
619 while (buf != NULL && !got_int)
620 {
621 if (buf->b_signlist != NULL)
622 {
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100623 vim_snprintf(lbuf, MSG_BUF_LEN, _("Signs for %s:"), buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100624 msg_puts_attr(lbuf, HL_ATTR(HLF_D));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100625 msg_putchar('\n');
626 }
627 FOR_ALL_SIGNS_IN_BUF(buf, sign)
628 {
629 if (got_int)
630 break;
631 if (!sign_in_group(sign, sign_group))
632 continue;
633 if (sign->group != NULL)
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100634 vim_snprintf(group, MSG_BUF_LEN, _(" group=%s"),
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100635 sign->group->sg_name);
636 else
637 group[0] = '\0';
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100638 vim_snprintf(lbuf, MSG_BUF_LEN,
639 _(" line=%ld id=%d%s name=%s priority=%d"),
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100640 (long)sign->lnum, sign->id, group,
641 sign_typenr2name(sign->typenr), sign->priority);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100642 msg_puts(lbuf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100643 msg_putchar('\n');
644 }
645 if (rbuf != NULL)
646 break;
647 buf = buf->b_next;
648 }
649}
650
651/*
652 * Adjust a placed sign for inserted/deleted lines.
653 */
654 void
655sign_mark_adjust(
656 linenr_T line1,
657 linenr_T line2,
658 long amount,
659 long amount_after)
660{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100661 signlist_T *sign; // a sign in a b_signlist
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100662 linenr_T new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100663
664 FOR_ALL_SIGNS_IN_BUF(curbuf, sign)
665 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100666 // Ignore changes to lines after the sign
667 if (sign->lnum < line1)
668 continue;
669 new_lnum = sign->lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100670 if (sign->lnum >= line1 && sign->lnum <= line2)
671 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100672 if (amount != MAXLNUM)
673 new_lnum += amount;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100674 }
675 else if (sign->lnum > line2)
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100676 // Lines inserted or deleted before the sign
677 new_lnum += amount_after;
678
679 // If the new sign line number is past the last line in the buffer,
680 // then don't adjust the line number. Otherwise, it will always be past
681 // the last line and will not be visible.
682 if (new_lnum <= curbuf->b_ml.ml_line_count)
683 sign->lnum = new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100684 }
685}
686
687/*
688 * Find index of a ":sign" subcmd from its name.
689 * "*end_cmd" must be writable.
690 */
691 static int
692sign_cmd_idx(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100693 char_u *begin_cmd, // begin of sign subcmd
694 char_u *end_cmd) // just after sign subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100695{
696 int idx;
697 char save = *end_cmd;
698
699 *end_cmd = NUL;
700 for (idx = 0; ; ++idx)
701 if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0)
702 break;
703 *end_cmd = save;
704 return idx;
705}
706
707/*
708 * Find a sign by name. Also returns pointer to the previous sign.
709 */
710 static sign_T *
711sign_find(char_u *name, sign_T **sp_prev)
712{
713 sign_T *sp;
714
715 if (sp_prev != NULL)
716 *sp_prev = NULL;
717 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
718 {
719 if (STRCMP(sp->sn_name, name) == 0)
720 break;
721 if (sp_prev != NULL)
722 *sp_prev = sp;
723 }
724
725 return sp;
726}
727
728/*
Bram Moolenaar03142362019-01-18 22:01:42 +0100729 * Allocate a new sign
730 */
731 static sign_T *
732alloc_new_sign(char_u *name)
733{
734 sign_T *sp;
735 sign_T *lp;
736 int start = next_sign_typenr;
737
738 // Allocate a new sign.
Bram Moolenaar51e14382019-05-25 20:21:28 +0200739 sp = (sign_T *)alloc_clear_id(sizeof(sign_T), aid_sign_define_by_name);
Bram Moolenaar03142362019-01-18 22:01:42 +0100740 if (sp == NULL)
741 return NULL;
742
743 // Check that next_sign_typenr is not already being used.
744 // This only happens after wrapping around. Hopefully
745 // another one got deleted and we can use its number.
746 for (lp = first_sign; lp != NULL; )
747 {
748 if (lp->sn_typenr == next_sign_typenr)
749 {
750 ++next_sign_typenr;
751 if (next_sign_typenr == MAX_TYPENR)
752 next_sign_typenr = 1;
753 if (next_sign_typenr == start)
754 {
755 vim_free(sp);
756 emsg(_("E612: Too many signs defined"));
757 return NULL;
758 }
759 lp = first_sign; // start all over
760 continue;
761 }
762 lp = lp->sn_next;
763 }
764
765 sp->sn_typenr = next_sign_typenr;
766 if (++next_sign_typenr == MAX_TYPENR)
767 next_sign_typenr = 1; // wrap around
768
769 sp->sn_name = vim_strsave(name);
770 if (sp->sn_name == NULL) // out of memory
771 {
772 vim_free(sp);
773 return NULL;
774 }
775
776 return sp;
777}
778
779/*
780 * Initialize the icon information for a new sign
781 */
782 static void
783sign_define_init_icon(sign_T *sp, char_u *icon)
784{
785 vim_free(sp->sn_icon);
786 sp->sn_icon = vim_strsave(icon);
787 backslash_halve(sp->sn_icon);
788# ifdef FEAT_SIGN_ICONS
789 if (gui.in_use)
790 {
791 out_flush();
792 if (sp->sn_image != NULL)
793 gui_mch_destroy_sign(sp->sn_image);
794 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
795 }
796# endif
797}
798
799/*
800 * Initialize the text for a new sign
801 */
802 static int
803sign_define_init_text(sign_T *sp, char_u *text)
804{
805 char_u *s;
806 char_u *endp;
807 int cells;
808 int len;
809
810 endp = text + (int)STRLEN(text);
811
812 // Remove backslashes so that it is possible to use a space.
813 for (s = text; s + 1 < endp; ++s)
814 if (*s == '\\')
815 {
816 STRMOVE(s, s + 1);
817 --endp;
818 }
819
820 // Count cells and check for non-printable chars
Bram Moolenaar03142362019-01-18 22:01:42 +0100821 if (has_mbyte)
822 {
823 cells = 0;
824 for (s = text; s < endp; s += (*mb_ptr2len)(s))
825 {
826 if (!vim_isprintc((*mb_ptr2char)(s)))
827 break;
828 cells += (*mb_ptr2cells)(s);
829 }
830 }
831 else
Bram Moolenaar03142362019-01-18 22:01:42 +0100832 {
833 for (s = text; s < endp; ++s)
834 if (!vim_isprintc(*s))
835 break;
836 cells = (int)(s - text);
837 }
838
839 // Currently sign text must be one or two display cells
840 if (s != endp || cells < 1 || cells > 2)
841 {
842 semsg(_("E239: Invalid sign text: %s"), text);
843 return FAIL;
844 }
845
846 vim_free(sp->sn_text);
847 // Allocate one byte more if we need to pad up
848 // with a space.
849 len = (int)(endp - text + ((cells == 1) ? 1 : 0));
850 sp->sn_text = vim_strnsave(text, len);
851
852 // For single character sign text, pad with a space.
853 if (sp->sn_text != NULL && cells == 1)
854 STRCPY(sp->sn_text + len - 1, " ");
855
856 return OK;
857}
858
859/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100860 * Define a new sign or update an existing sign
861 */
862 int
863sign_define_by_name(
864 char_u *name,
865 char_u *icon,
866 char_u *linehl,
867 char_u *text,
868 char_u *texthl)
869{
870 sign_T *sp_prev;
871 sign_T *sp;
872
873 sp = sign_find(name, &sp_prev);
874 if (sp == NULL)
875 {
Bram Moolenaar03142362019-01-18 22:01:42 +0100876 sp = alloc_new_sign(name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100877 if (sp == NULL)
878 return FAIL;
879
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100880 // add the new sign to the list of signs
881 if (sp_prev == NULL)
882 first_sign = sp;
883 else
884 sp_prev->sn_next = sp;
885 }
886
887 // set values for a defined sign.
888 if (icon != NULL)
Bram Moolenaar03142362019-01-18 22:01:42 +0100889 sign_define_init_icon(sp, icon);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100890
Bram Moolenaar03142362019-01-18 22:01:42 +0100891 if (text != NULL && (sign_define_init_text(sp, text) == FAIL))
892 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100893
894 if (linehl != NULL)
895 sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl));
896
897 if (texthl != NULL)
898 sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl));
899
900 return OK;
901}
902
903/*
904 * Free the sign specified by 'name'.
905 */
906 int
907sign_undefine_by_name(char_u *name)
908{
909 sign_T *sp_prev;
910 sign_T *sp;
911
912 sp = sign_find(name, &sp_prev);
913 if (sp == NULL)
914 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100915 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100916 return FAIL;
917 }
918 sign_undefine(sp, sp_prev);
919
920 return OK;
921}
922
923/*
924 * List the signs matching 'name'
925 */
926 static void
927sign_list_by_name(char_u *name)
928{
929 sign_T *sp;
930
931 sp = sign_find(name, NULL);
932 if (sp != NULL)
933 sign_list_defined(sp);
934 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100935 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100936}
937
938/*
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100939 * Place a sign at the specified file location or update a sign.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100940 */
941 int
942sign_place(
943 int *sign_id,
944 char_u *sign_group,
945 char_u *sign_name,
946 buf_T *buf,
947 linenr_T lnum,
948 int prio)
949{
950 sign_T *sp;
951
952 // Check for reserved character '*' in group name
953 if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0'))
954 return FAIL;
955
956 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
957 if (STRCMP(sp->sn_name, sign_name) == 0)
958 break;
959 if (sp == NULL)
960 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100961 semsg(_("E155: Unknown sign: %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100962 return FAIL;
963 }
964 if (*sign_id == 0)
965 *sign_id = sign_group_get_next_signid(buf, sign_group);
966
967 if (lnum > 0)
968 // ":sign place {id} line={lnum} name={name} file={fname}":
969 // place a sign
970 buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr);
971 else
972 // ":sign place {id} file={fname}": change sign type
973 lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr);
974 if (lnum > 0)
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100975 redraw_buf_line_later(buf, lnum);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100976 else
977 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100978 semsg(_("E885: Not possible to change sign %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100979 return FAIL;
980 }
981
982 return OK;
983}
984
985/*
986 * Unplace the specified sign
987 */
988 int
989sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum)
990{
991 if (buf->b_signlist == NULL) // No signs in the buffer
992 return OK;
993
994 if (sign_id == 0)
995 {
996 // Delete all the signs in the specified buffer
997 redraw_buf_later(buf, NOT_VALID);
998 buf_delete_signs(buf, sign_group);
999 }
1000 else
1001 {
1002 linenr_T lnum;
1003
1004 // Delete only the specified signs
1005 lnum = buf_delsign(buf, atlnum, sign_id, sign_group);
1006 if (lnum == 0)
1007 return FAIL;
1008 }
1009
1010 return OK;
1011}
1012
1013/*
1014 * Unplace the sign at the current cursor line.
1015 */
1016 static void
1017sign_unplace_at_cursor(char_u *groupname)
1018{
1019 int id = -1;
1020
1021 id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname);
1022 if (id > 0)
1023 sign_unplace(id, groupname, curwin->w_buffer, curwin->w_cursor.lnum);
1024 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001025 emsg(_("E159: Missing sign number"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001026}
1027
1028/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001029 * Jump to a sign.
1030 */
1031 linenr_T
1032sign_jump(int sign_id, char_u *sign_group, buf_T *buf)
1033{
1034 linenr_T lnum;
1035
1036 if ((lnum = buf_findsign(buf, sign_id, sign_group)) <= 0)
1037 {
Bram Moolenaarb5443cc2019-01-15 20:19:40 +01001038 semsg(_("E157: Invalid sign ID: %d"), sign_id);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001039 return -1;
1040 }
1041
1042 // goto a sign ...
1043 if (buf_jump_open_win(buf) != NULL)
1044 { // ... in a current window
1045 curwin->w_cursor.lnum = lnum;
1046 check_cursor_lnum();
1047 beginline(BL_WHITE);
1048 }
1049 else
1050 { // ... not currently in a window
1051 char_u *cmd;
1052
1053 if (buf->b_fname == NULL)
1054 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001055 emsg(_("E934: Cannot jump to a buffer that does not have a name"));
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001056 return -1;
1057 }
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02001058 cmd = alloc(STRLEN(buf->b_fname) + 25);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001059 if (cmd == NULL)
1060 return -1;
1061 sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname);
1062 do_cmdline_cmd(cmd);
1063 vim_free(cmd);
1064 }
1065# ifdef FEAT_FOLDING
1066 foldOpenCursor();
1067# endif
1068
1069 return lnum;
1070}
1071
1072/*
1073 * ":sign define {name} ..." command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001074 */
1075 static void
1076sign_define_cmd(char_u *sign_name, char_u *cmdline)
1077{
1078 char_u *arg;
1079 char_u *p = cmdline;
1080 char_u *icon = NULL;
1081 char_u *text = NULL;
1082 char_u *linehl = NULL;
1083 char_u *texthl = NULL;
1084 int failed = FALSE;
1085
1086 // set values for a defined sign.
1087 for (;;)
1088 {
1089 arg = skipwhite(p);
1090 if (*arg == NUL)
1091 break;
1092 p = skiptowhite_esc(arg);
1093 if (STRNCMP(arg, "icon=", 5) == 0)
1094 {
1095 arg += 5;
1096 icon = vim_strnsave(arg, (int)(p - arg));
1097 }
1098 else if (STRNCMP(arg, "text=", 5) == 0)
1099 {
1100 arg += 5;
1101 text = vim_strnsave(arg, (int)(p - arg));
1102 }
1103 else if (STRNCMP(arg, "linehl=", 7) == 0)
1104 {
1105 arg += 7;
1106 linehl = vim_strnsave(arg, (int)(p - arg));
1107 }
1108 else if (STRNCMP(arg, "texthl=", 7) == 0)
1109 {
1110 arg += 7;
1111 texthl = vim_strnsave(arg, (int)(p - arg));
1112 }
1113 else
1114 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001115 semsg(_(e_invarg2), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001116 failed = TRUE;
1117 break;
1118 }
1119 }
1120
1121 if (!failed)
1122 sign_define_by_name(sign_name, icon, linehl, text, texthl);
1123
1124 vim_free(icon);
1125 vim_free(text);
1126 vim_free(linehl);
1127 vim_free(texthl);
1128}
1129
1130/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001131 * ":sign place" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001132 */
1133 static void
1134sign_place_cmd(
1135 buf_T *buf,
1136 linenr_T lnum,
1137 char_u *sign_name,
1138 int id,
1139 char_u *group,
1140 int prio)
1141{
1142 if (id <= 0)
1143 {
1144 // List signs placed in a file/buffer
1145 // :sign place file={fname}
1146 // :sign place group={group} file={fname}
1147 // :sign place group=* file={fname}
1148 // :sign place buffer={nr}
1149 // :sign place group={group} buffer={nr}
1150 // :sign place group=* buffer={nr}
1151 // :sign place
1152 // :sign place group={group}
1153 // :sign place group=*
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001154 if (lnum >= 0 || sign_name != NULL
1155 || (group != NULL && *group == '\0'))
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001156 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001157 else
1158 sign_list_placed(buf, group);
1159 }
1160 else
1161 {
1162 // Place a new sign
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001163 if (sign_name == NULL || buf == NULL
1164 || (group != NULL && *group == '\0'))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001165 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001166 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001167 return;
1168 }
1169
1170 sign_place(&id, group, sign_name, buf, lnum, prio);
1171 }
1172}
1173
1174/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001175 * ":sign unplace" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001176 */
1177 static void
1178sign_unplace_cmd(
1179 buf_T *buf,
1180 linenr_T lnum,
1181 char_u *sign_name,
1182 int id,
1183 char_u *group)
1184{
1185 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0'))
1186 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001187 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001188 return;
1189 }
1190
1191 if (id == -2)
1192 {
1193 if (buf != NULL)
1194 // :sign unplace * file={fname}
1195 // :sign unplace * group={group} file={fname}
1196 // :sign unplace * group=* file={fname}
1197 // :sign unplace * buffer={nr}
1198 // :sign unplace * group={group} buffer={nr}
1199 // :sign unplace * group=* buffer={nr}
1200 sign_unplace(0, group, buf, 0);
1201 else
1202 // :sign unplace *
1203 // :sign unplace * group={group}
1204 // :sign unplace * group=*
1205 FOR_ALL_BUFFERS(buf)
1206 if (buf->b_signlist != NULL)
1207 buf_delete_signs(buf, group);
1208 }
1209 else
1210 {
1211 if (buf != NULL)
1212 // :sign unplace {id} file={fname}
1213 // :sign unplace {id} group={group} file={fname}
1214 // :sign unplace {id} group=* file={fname}
1215 // :sign unplace {id} buffer={nr}
1216 // :sign unplace {id} group={group} buffer={nr}
1217 // :sign unplace {id} group=* buffer={nr}
1218 sign_unplace(id, group, buf, 0);
1219 else
1220 {
1221 if (id == -1)
1222 {
1223 // :sign unplace group={group}
1224 // :sign unplace group=*
1225 sign_unplace_at_cursor(group);
1226 }
1227 else
1228 {
1229 // :sign unplace {id}
1230 // :sign unplace {id} group={group}
1231 // :sign unplace {id} group=*
1232 FOR_ALL_BUFFERS(buf)
1233 sign_unplace(id, group, buf, 0);
1234 }
1235 }
1236 }
1237}
1238
1239/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001240 * Jump to a placed sign commands:
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001241 * :sign jump {id} file={fname}
1242 * :sign jump {id} buffer={nr}
1243 * :sign jump {id} group={group} file={fname}
1244 * :sign jump {id} group={group} buffer={nr}
1245 */
1246 static void
1247sign_jump_cmd(
1248 buf_T *buf,
1249 linenr_T lnum,
1250 char_u *sign_name,
1251 int id,
1252 char_u *group)
1253{
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001254 if (sign_name == NULL && group == NULL && id == -1)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001255 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001256 emsg(_(e_argreq));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001257 return;
1258 }
1259
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001260 if (buf == NULL || (group != NULL && *group == '\0')
1261 || lnum >= 0 || sign_name != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001262 {
1263 // File or buffer is not specified or an empty group is used
1264 // or a line number or a sign name is specified.
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001265 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001266 return;
1267 }
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001268 (void)sign_jump(id, group, buf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001269}
1270
1271/*
1272 * Parse the command line arguments for the ":sign place", ":sign unplace" and
1273 * ":sign jump" commands.
1274 * The supported arguments are: line={lnum} name={name} group={group}
1275 * priority={prio} and file={fname} or buffer={nr}.
1276 */
1277 static int
1278parse_sign_cmd_args(
1279 int cmd,
1280 char_u *arg,
1281 char_u **sign_name,
1282 int *signid,
1283 char_u **group,
1284 int *prio,
1285 buf_T **buf,
1286 linenr_T *lnum)
1287{
1288 char_u *arg1;
1289 char_u *name;
1290 char_u *filename = NULL;
Bram Moolenaarb589f952019-01-07 22:10:00 +01001291 int lnum_arg = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001292
1293 // first arg could be placed sign id
1294 arg1 = arg;
1295 if (VIM_ISDIGIT(*arg))
1296 {
1297 *signid = getdigits(&arg);
1298 if (!VIM_ISWHITE(*arg) && *arg != NUL)
1299 {
1300 *signid = -1;
1301 arg = arg1;
1302 }
1303 else
1304 arg = skipwhite(arg);
1305 }
1306
1307 while (*arg != NUL)
1308 {
1309 if (STRNCMP(arg, "line=", 5) == 0)
1310 {
1311 arg += 5;
1312 *lnum = atoi((char *)arg);
1313 arg = skiptowhite(arg);
Bram Moolenaarb589f952019-01-07 22:10:00 +01001314 lnum_arg = TRUE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001315 }
1316 else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE)
1317 {
1318 if (*signid != -1)
1319 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001320 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001321 return FAIL;
1322 }
1323 *signid = -2;
1324 arg = skiptowhite(arg + 1);
1325 }
1326 else if (STRNCMP(arg, "name=", 5) == 0)
1327 {
1328 arg += 5;
1329 name = arg;
1330 arg = skiptowhite(arg);
1331 if (*arg != NUL)
1332 *arg++ = NUL;
1333 while (name[0] == '0' && name[1] != NUL)
1334 ++name;
1335 *sign_name = name;
1336 }
1337 else if (STRNCMP(arg, "group=", 6) == 0)
1338 {
1339 arg += 6;
1340 *group = arg;
1341 arg = skiptowhite(arg);
1342 if (*arg != NUL)
1343 *arg++ = NUL;
1344 }
1345 else if (STRNCMP(arg, "priority=", 9) == 0)
1346 {
1347 arg += 9;
1348 *prio = atoi((char *)arg);
1349 arg = skiptowhite(arg);
1350 }
1351 else if (STRNCMP(arg, "file=", 5) == 0)
1352 {
1353 arg += 5;
1354 filename = arg;
1355 *buf = buflist_findname_exp(arg);
1356 break;
1357 }
1358 else if (STRNCMP(arg, "buffer=", 7) == 0)
1359 {
1360 arg += 7;
1361 filename = arg;
1362 *buf = buflist_findnr((int)getdigits(&arg));
1363 if (*skipwhite(arg) != NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001364 emsg(_(e_trailing));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001365 break;
1366 }
1367 else
1368 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001369 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001370 return FAIL;
1371 }
1372 arg = skipwhite(arg);
1373 }
1374
1375 if (filename != NULL && *buf == NULL)
1376 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001377 semsg(_("E158: Invalid buffer name: %s"), filename);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001378 return FAIL;
1379 }
1380
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001381 // If the filename is not supplied for the sign place or the sign jump
1382 // command, then use the current buffer.
Bram Moolenaarb589f952019-01-07 22:10:00 +01001383 if (filename == NULL && ((cmd == SIGNCMD_PLACE && lnum_arg)
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001384 || cmd == SIGNCMD_JUMP))
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001385 *buf = curwin->w_buffer;
1386
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001387 return OK;
1388}
1389
1390/*
1391 * ":sign" command
1392 */
1393 void
1394ex_sign(exarg_T *eap)
1395{
1396 char_u *arg = eap->arg;
1397 char_u *p;
1398 int idx;
1399 sign_T *sp;
1400 buf_T *buf = NULL;
1401
1402 // Parse the subcommand.
1403 p = skiptowhite(arg);
1404 idx = sign_cmd_idx(arg, p);
1405 if (idx == SIGNCMD_LAST)
1406 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001407 semsg(_("E160: Unknown sign command: %s"), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001408 return;
1409 }
1410 arg = skipwhite(p);
1411
1412 if (idx <= SIGNCMD_LIST)
1413 {
1414 // Define, undefine or list signs.
1415 if (idx == SIGNCMD_LIST && *arg == NUL)
1416 {
1417 // ":sign list": list all defined signs
1418 for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next)
1419 sign_list_defined(sp);
1420 }
1421 else if (*arg == NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001422 emsg(_("E156: Missing sign name"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001423 else
1424 {
1425 char_u *name;
1426
1427 // Isolate the sign name. If it's a number skip leading zeroes,
1428 // so that "099" and "99" are the same sign. But keep "0".
1429 p = skiptowhite(arg);
1430 if (*p != NUL)
1431 *p++ = NUL;
1432 while (arg[0] == '0' && arg[1] != NUL)
1433 ++arg;
1434 name = vim_strsave(arg);
1435
1436 if (idx == SIGNCMD_DEFINE)
1437 sign_define_cmd(name, p);
1438 else if (idx == SIGNCMD_LIST)
1439 // ":sign list {name}"
1440 sign_list_by_name(name);
1441 else
1442 // ":sign undefine {name}"
1443 sign_undefine_by_name(name);
1444
1445 vim_free(name);
1446 return;
1447 }
1448 }
1449 else
1450 {
1451 int id = -1;
1452 linenr_T lnum = -1;
1453 char_u *sign_name = NULL;
1454 char_u *group = NULL;
1455 int prio = SIGN_DEF_PRIO;
1456
1457 // Parse command line arguments
1458 if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio,
1459 &buf, &lnum) == FAIL)
1460 return;
1461
1462 if (idx == SIGNCMD_PLACE)
1463 sign_place_cmd(buf, lnum, sign_name, id, group, prio);
1464 else if (idx == SIGNCMD_UNPLACE)
1465 sign_unplace_cmd(buf, lnum, sign_name, id, group);
1466 else if (idx == SIGNCMD_JUMP)
1467 sign_jump_cmd(buf, lnum, sign_name, id, group);
1468 }
1469}
1470
1471/*
1472 * Return information about a specified sign
1473 */
1474 static void
1475sign_getinfo(sign_T *sp, dict_T *retdict)
1476{
1477 char_u *p;
1478
1479 dict_add_string(retdict, "name", (char_u *)sp->sn_name);
1480 if (sp->sn_icon != NULL)
1481 dict_add_string(retdict, "icon", (char_u *)sp->sn_icon);
1482 if (sp->sn_text != NULL)
1483 dict_add_string(retdict, "text", (char_u *)sp->sn_text);
1484 if (sp->sn_line_hl > 0)
1485 {
1486 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1487 if (p == NULL)
1488 p = (char_u *)"NONE";
1489 dict_add_string(retdict, "linehl", (char_u *)p);
1490 }
1491 if (sp->sn_text_hl > 0)
1492 {
1493 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1494 if (p == NULL)
1495 p = (char_u *)"NONE";
1496 dict_add_string(retdict, "texthl", (char_u *)p);
1497 }
1498}
1499
1500/*
1501 * If 'name' is NULL, return a list of all the defined signs.
1502 * Otherwise, return information about the specified sign.
1503 */
1504 void
1505sign_getlist(char_u *name, list_T *retlist)
1506{
1507 sign_T *sp = first_sign;
1508 dict_T *dict;
1509
1510 if (name != NULL)
1511 {
1512 sp = sign_find(name, NULL);
1513 if (sp == NULL)
1514 return;
1515 }
1516
1517 for (; sp != NULL && !got_int; sp = sp->sn_next)
1518 {
1519 if ((dict = dict_alloc_id(aid_sign_getlist)) == NULL)
1520 return;
1521 if (list_append_dict(retlist, dict) == FAIL)
1522 return;
1523 sign_getinfo(sp, dict);
1524
1525 if (name != NULL) // handle only the specified sign
1526 break;
1527 }
1528}
1529
1530/*
1531 * Returns information about signs placed in a buffer as list of dicts.
1532 */
1533 void
1534get_buffer_signs(buf_T *buf, list_T *l)
1535{
1536 signlist_T *sign;
1537 dict_T *d;
1538
1539 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1540 {
1541 if ((d = sign_get_info(sign)) != NULL)
1542 list_append_dict(l, d);
1543 }
1544}
1545
1546/*
1547 * Return information about all the signs placed in a buffer
1548 */
1549 static void
1550sign_get_placed_in_buf(
1551 buf_T *buf,
1552 linenr_T lnum,
1553 int sign_id,
1554 char_u *sign_group,
1555 list_T *retlist)
1556{
1557 dict_T *d;
1558 list_T *l;
1559 signlist_T *sign;
1560 dict_T *sdict;
1561
1562 if ((d = dict_alloc_id(aid_sign_getplaced_dict)) == NULL)
1563 return;
1564 list_append_dict(retlist, d);
1565
1566 dict_add_number(d, "bufnr", (long)buf->b_fnum);
1567
1568 if ((l = list_alloc_id(aid_sign_getplaced_list)) == NULL)
1569 return;
1570 dict_add_list(d, "signs", l);
1571
1572 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1573 {
1574 if (!sign_in_group(sign, sign_group))
1575 continue;
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001576 if ((lnum == 0 && sign_id == 0)
1577 || (sign_id == 0 && lnum == sign->lnum)
1578 || (lnum == 0 && sign_id == sign->id)
1579 || (lnum == sign->lnum && sign_id == sign->id))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001580 {
1581 if ((sdict = sign_get_info(sign)) != NULL)
1582 list_append_dict(l, sdict);
1583 }
1584 }
1585}
1586
1587/*
1588 * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the
1589 * sign placed at the line number. If 'lnum' is zero, return all the signs
1590 * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers.
1591 */
1592 void
1593sign_get_placed(
1594 buf_T *buf,
1595 linenr_T lnum,
1596 int sign_id,
1597 char_u *sign_group,
1598 list_T *retlist)
1599{
1600 if (buf != NULL)
1601 sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist);
1602 else
1603 {
1604 FOR_ALL_BUFFERS(buf)
1605 {
1606 if (buf->b_signlist != NULL)
1607 sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist);
1608 }
1609 }
1610}
1611
1612# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1613/*
1614 * Allocate the icons. Called when the GUI has started. Allows defining
1615 * signs before it starts.
1616 */
1617 void
1618sign_gui_started(void)
1619{
1620 sign_T *sp;
1621
1622 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1623 if (sp->sn_icon != NULL)
1624 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
1625}
1626# endif
1627
1628/*
1629 * List one sign.
1630 */
1631 static void
1632sign_list_defined(sign_T *sp)
1633{
1634 char_u *p;
1635
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001636 smsg("sign %s", sp->sn_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001637 if (sp->sn_icon != NULL)
1638 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001639 msg_puts(" icon=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001640 msg_outtrans(sp->sn_icon);
1641# ifdef FEAT_SIGN_ICONS
1642 if (sp->sn_image == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001643 msg_puts(_(" (NOT FOUND)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001644# else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001645 msg_puts(_(" (not supported)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001646# endif
1647 }
1648 if (sp->sn_text != NULL)
1649 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001650 msg_puts(" text=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001651 msg_outtrans(sp->sn_text);
1652 }
1653 if (sp->sn_line_hl > 0)
1654 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001655 msg_puts(" linehl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001656 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1657 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001658 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001659 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001660 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001661 }
1662 if (sp->sn_text_hl > 0)
1663 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001664 msg_puts(" texthl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001665 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1666 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001667 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001668 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001669 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001670 }
1671}
1672
1673/*
1674 * Undefine a sign and free its memory.
1675 */
1676 static void
1677sign_undefine(sign_T *sp, sign_T *sp_prev)
1678{
1679 vim_free(sp->sn_name);
1680 vim_free(sp->sn_icon);
1681# ifdef FEAT_SIGN_ICONS
1682 if (sp->sn_image != NULL)
1683 {
1684 out_flush();
1685 gui_mch_destroy_sign(sp->sn_image);
1686 }
1687# endif
1688 vim_free(sp->sn_text);
1689 if (sp_prev == NULL)
1690 first_sign = sp->sn_next;
1691 else
1692 sp_prev->sn_next = sp->sn_next;
1693 vim_free(sp);
1694}
1695
1696/*
1697 * Get highlighting attribute for sign "typenr".
1698 * If "line" is TRUE: line highl, if FALSE: text highl.
1699 */
1700 int
1701sign_get_attr(int typenr, int line)
1702{
1703 sign_T *sp;
1704
1705 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1706 if (sp->sn_typenr == typenr)
1707 {
1708 if (line)
1709 {
1710 if (sp->sn_line_hl > 0)
1711 return syn_id2attr(sp->sn_line_hl);
1712 }
1713 else
1714 {
1715 if (sp->sn_text_hl > 0)
1716 return syn_id2attr(sp->sn_text_hl);
1717 }
1718 break;
1719 }
1720 return 0;
1721}
1722
1723/*
1724 * Get text mark for sign "typenr".
1725 * Returns NULL if there isn't one.
1726 */
1727 char_u *
1728sign_get_text(int typenr)
1729{
1730 sign_T *sp;
1731
1732 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1733 if (sp->sn_typenr == typenr)
1734 return sp->sn_text;
1735 return NULL;
1736}
1737
1738# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1739 void *
1740sign_get_image(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001741 int typenr) // the attribute which may have a sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001742{
1743 sign_T *sp;
1744
1745 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1746 if (sp->sn_typenr == typenr)
1747 return sp->sn_image;
1748 return NULL;
1749}
1750# endif
1751
1752/*
1753 * Undefine/free all signs.
1754 */
1755 void
1756free_signs(void)
1757{
1758 while (first_sign != NULL)
1759 sign_undefine(first_sign, NULL);
1760}
1761
1762# if defined(FEAT_CMDL_COMPL) || defined(PROTO)
1763static enum
1764{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001765 EXP_SUBCMD, // expand :sign sub-commands
1766 EXP_DEFINE, // expand :sign define {name} args
1767 EXP_PLACE, // expand :sign place {id} args
Bram Moolenaar3678f652019-02-17 14:50:25 +01001768 EXP_LIST, // expand :sign place args
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001769 EXP_UNPLACE, // expand :sign unplace"
Bram Moolenaar3678f652019-02-17 14:50:25 +01001770 EXP_SIGN_NAMES, // expand with name of placed signs
1771 EXP_SIGN_GROUPS // expand with name of placed sign groups
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001772} expand_what;
1773
1774/*
Bram Moolenaar3678f652019-02-17 14:50:25 +01001775 * Return the n'th sign name (used for command line completion)
1776 */
1777 static char_u *
1778get_nth_sign_name(int idx)
1779{
1780 int current_idx;
1781 sign_T *sp;
1782
1783 // Complete with name of signs already defined
1784 current_idx = 0;
1785 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1786 if (current_idx++ == idx)
1787 return sp->sn_name;
1788 return NULL;
1789}
1790
1791/*
1792 * Return the n'th sign group name (used for command line completion)
1793 */
1794 static char_u *
1795get_nth_sign_group_name(int idx)
1796{
1797 int current_idx;
1798 int todo;
1799 hashitem_T *hi;
1800 signgroup_T *group;
1801
1802 // Complete with name of sign groups already defined
1803 current_idx = 0;
1804 todo = (int)sg_table.ht_used;
1805 for (hi = sg_table.ht_array; todo > 0; ++hi)
1806 {
1807 if (!HASHITEM_EMPTY(hi))
1808 {
1809 --todo;
1810 if (current_idx++ == idx)
1811 {
1812 group = HI2SG(hi);
1813 return group->sg_name;
1814 }
1815 }
1816 }
1817 return NULL;
1818}
1819
1820/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001821 * Function given to ExpandGeneric() to obtain the sign command
1822 * expansion.
1823 */
1824 char_u *
1825get_sign_name(expand_T *xp UNUSED, int idx)
1826{
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001827 switch (expand_what)
1828 {
1829 case EXP_SUBCMD:
1830 return (char_u *)cmds[idx];
1831 case EXP_DEFINE:
1832 {
1833 char *define_arg[] =
1834 {
1835 "icon=", "linehl=", "text=", "texthl=", NULL
1836 };
1837 return (char_u *)define_arg[idx];
1838 }
1839 case EXP_PLACE:
1840 {
1841 char *place_arg[] =
1842 {
1843 "line=", "name=", "group=", "priority=", "file=",
1844 "buffer=", NULL
1845 };
1846 return (char_u *)place_arg[idx];
1847 }
Bram Moolenaar3678f652019-02-17 14:50:25 +01001848 case EXP_LIST:
1849 {
1850 char *list_arg[] =
1851 {
1852 "group=", "file=", "buffer=", NULL
1853 };
1854 return (char_u *)list_arg[idx];
1855 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001856 case EXP_UNPLACE:
1857 {
1858 char *unplace_arg[] = { "group=", "file=", "buffer=", NULL };
1859 return (char_u *)unplace_arg[idx];
1860 }
1861 case EXP_SIGN_NAMES:
Bram Moolenaar3678f652019-02-17 14:50:25 +01001862 return get_nth_sign_name(idx);
1863 case EXP_SIGN_GROUPS:
1864 return get_nth_sign_group_name(idx);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001865 default:
1866 return NULL;
1867 }
1868}
1869
1870/*
1871 * Handle command line completion for :sign command.
1872 */
1873 void
1874set_context_in_sign_cmd(expand_T *xp, char_u *arg)
1875{
1876 char_u *p;
1877 char_u *end_subcmd;
1878 char_u *last;
1879 int cmd_idx;
1880 char_u *begin_subcmd_args;
1881
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001882 // Default: expand subcommands.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001883 xp->xp_context = EXPAND_SIGN;
1884 expand_what = EXP_SUBCMD;
1885 xp->xp_pattern = arg;
1886
1887 end_subcmd = skiptowhite(arg);
1888 if (*end_subcmd == NUL)
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001889 // expand subcmd name
1890 // :sign {subcmd}<CTRL-D>
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001891 return;
1892
1893 cmd_idx = sign_cmd_idx(arg, end_subcmd);
1894
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001895 // :sign {subcmd} {subcmd_args}
1896 // |
1897 // begin_subcmd_args
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001898 begin_subcmd_args = skipwhite(end_subcmd);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001899
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001900 // expand last argument of subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001901
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001902 // :sign define {name} {args}...
1903 // |
1904 // p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001905
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001906 // Loop until reaching last argument.
Bram Moolenaar3678f652019-02-17 14:50:25 +01001907 p = begin_subcmd_args;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001908 do
1909 {
1910 p = skipwhite(p);
1911 last = p;
1912 p = skiptowhite(p);
1913 } while (*p != NUL);
1914
1915 p = vim_strchr(last, '=');
1916
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001917 // :sign define {name} {args}... {last}=
1918 // | |
1919 // last p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001920 if (p == NULL)
1921 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001922 // Expand last argument name (before equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001923 xp->xp_pattern = last;
1924 switch (cmd_idx)
1925 {
1926 case SIGNCMD_DEFINE:
1927 expand_what = EXP_DEFINE;
1928 break;
1929 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01001930 // List placed signs
1931 if (VIM_ISDIGIT(*begin_subcmd_args))
1932 // :sign place {id} {args}...
1933 expand_what = EXP_PLACE;
1934 else
1935 // :sign place {args}...
1936 expand_what = EXP_LIST;
1937 break;
1938 case SIGNCMD_LIST:
1939 case SIGNCMD_UNDEFINE:
1940 // :sign list <CTRL-D>
1941 // :sign undefine <CTRL-D>
1942 expand_what = EXP_SIGN_NAMES;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001943 break;
1944 case SIGNCMD_JUMP:
1945 case SIGNCMD_UNPLACE:
1946 expand_what = EXP_UNPLACE;
1947 break;
1948 default:
1949 xp->xp_context = EXPAND_NOTHING;
1950 }
1951 }
1952 else
1953 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001954 // Expand last argument value (after equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001955 xp->xp_pattern = p + 1;
1956 switch (cmd_idx)
1957 {
1958 case SIGNCMD_DEFINE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01001959 if (STRNCMP(last, "texthl", 6) == 0
1960 || STRNCMP(last, "linehl", 6) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001961 xp->xp_context = EXPAND_HIGHLIGHT;
Bram Moolenaar3678f652019-02-17 14:50:25 +01001962 else if (STRNCMP(last, "icon", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001963 xp->xp_context = EXPAND_FILES;
1964 else
1965 xp->xp_context = EXPAND_NOTHING;
1966 break;
1967 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01001968 if (STRNCMP(last, "name", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001969 expand_what = EXP_SIGN_NAMES;
Bram Moolenaar3678f652019-02-17 14:50:25 +01001970 else if (STRNCMP(last, "group", 5) == 0)
1971 expand_what = EXP_SIGN_GROUPS;
1972 else if (STRNCMP(last, "file", 4) == 0)
1973 xp->xp_context = EXPAND_BUFFERS;
1974 else
1975 xp->xp_context = EXPAND_NOTHING;
1976 break;
1977 case SIGNCMD_UNPLACE:
1978 case SIGNCMD_JUMP:
1979 if (STRNCMP(last, "group", 5) == 0)
1980 expand_what = EXP_SIGN_GROUPS;
1981 else if (STRNCMP(last, "file", 4) == 0)
1982 xp->xp_context = EXPAND_BUFFERS;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001983 else
1984 xp->xp_context = EXPAND_NOTHING;
1985 break;
1986 default:
1987 xp->xp_context = EXPAND_NOTHING;
1988 }
1989 }
1990}
1991# endif
1992
1993#endif /* FEAT_SIGNS */