blob: b89bea073d84495c46a10a3132df142f3e226cd2 [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/*
311 * Add the sign into the signlist. Find the right spot to do it though.
312 */
313 static void
314buf_addsign(
315 buf_T *buf, // buffer to store sign in
316 int id, // sign ID
317 char_u *groupname, // sign group
318 int prio, // sign priority
319 linenr_T lnum, // line number which gets the mark
320 int typenr) // typenr of sign we are adding
321{
322 signlist_T *sign; // a sign in the signlist
323 signlist_T *prev; // the previous sign
324
325 prev = NULL;
326 FOR_ALL_SIGNS_IN_BUF(buf, sign)
327 {
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100328 if (lnum == sign->lnum && id == sign->id
329 && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100330 {
331 // Update an existing sign
332 sign->typenr = typenr;
333 return;
334 }
335 else if (lnum < sign->lnum)
336 {
337 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio,
338 lnum, typenr);
339 return;
340 }
341 prev = sign;
342 }
343
344 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr);
345 return;
346}
347
348/*
349 * For an existing, placed sign "markId" change the type to "typenr".
350 * Returns the line number of the sign, or zero if the sign is not found.
351 */
352 static linenr_T
353buf_change_sign_type(
354 buf_T *buf, // buffer to store sign in
355 int markId, // sign ID
356 char_u *group, // sign group
357 int typenr) // typenr of sign we are adding
358{
359 signlist_T *sign; // a sign in the signlist
360
361 FOR_ALL_SIGNS_IN_BUF(buf, sign)
362 {
363 if (sign->id == markId && sign_in_group(sign, group))
364 {
365 sign->typenr = typenr;
366 return sign->lnum;
367 }
368 }
369
370 return (linenr_T)0;
371}
372
373/*
374 * Return the type number of the sign at line number 'lnum' in buffer 'buf'
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100375 * which has the attribute specified by 'type'. Returns 0 if a sign is not
376 * found at the line number or it doesn't have the specified attribute.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100377 */
378 int
379buf_getsigntype(
380 buf_T *buf,
381 linenr_T lnum,
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100382 int type) // SIGN_ICON, SIGN_TEXT, SIGN_ANY, SIGN_LINEHL
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100383{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100384 signlist_T *sign; // a sign in a b_signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100385
386 FOR_ALL_SIGNS_IN_BUF(buf, sign)
387 if (sign->lnum == lnum
388 && (type == SIGN_ANY
389# ifdef FEAT_SIGN_ICONS
390 || (type == SIGN_ICON
391 && sign_get_image(sign->typenr) != NULL)
392# endif
393 || (type == SIGN_TEXT
394 && sign_get_text(sign->typenr) != NULL)
395 || (type == SIGN_LINEHL
396 && sign_get_attr(sign->typenr, TRUE) != 0)))
397 return sign->typenr;
398 return 0;
399}
400
401/*
402 * Delete sign 'id' in group 'group' from buffer 'buf'.
403 * If 'id' is zero, then delete all the signs in group 'group'. Otherwise
404 * delete only the specified sign.
405 * If 'group' is '*', then delete the sign in all the groups. If 'group' is
406 * NULL, then delete the sign in the global group. Otherwise delete the sign in
407 * the specified group.
408 * Returns the line number of the deleted sign. If multiple signs are deleted,
409 * then returns the line number of the last sign deleted.
410 */
411 linenr_T
412buf_delsign(
413 buf_T *buf, // buffer sign is stored in
414 linenr_T atlnum, // sign at this line, 0 - at any line
415 int id, // sign id
416 char_u *group) // sign group
417{
418 signlist_T **lastp; // pointer to pointer to current sign
419 signlist_T *sign; // a sign in a b_signlist
420 signlist_T *next; // the next sign in a b_signlist
421 linenr_T lnum; // line number whose sign was deleted
422
423 lastp = &buf->b_signlist;
424 lnum = 0;
425 for (sign = buf->b_signlist; sign != NULL; sign = next)
426 {
427 next = sign->next;
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100428 if ((id == 0 || sign->id == id)
429 && (atlnum == 0 || sign->lnum == atlnum)
430 && sign_in_group(sign, group))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100431
432 {
433 *lastp = next;
434 if (next != NULL)
435 next->prev = sign->prev;
436 lnum = sign->lnum;
437 if (sign->group != NULL)
438 sign_group_unref(sign->group->sg_name);
439 vim_free(sign);
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100440 redraw_buf_line_later(buf, lnum);
441
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100442 // Check whether only one sign needs to be deleted
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100443 // If deleting a sign with a specific identifier in a particular
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100444 // group or deleting any sign at a particular line number, delete
445 // only one sign.
446 if (group == NULL
447 || (*group != '*' && id != 0)
448 || (*group == '*' && atlnum != 0))
449 break;
450 }
451 else
452 lastp = &sign->next;
453 }
454
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100455 // When deleting the last sign the cursor position may change, because the
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100456 // sign columns no longer shows. And the 'signcolumn' may be hidden.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100457 if (buf->b_signlist == NULL)
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100458 {
459 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100460 changed_cline_bef_curs();
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100461 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100462
463 return lnum;
464}
465
466
467/*
468 * Find the line number of the sign with the requested id in group 'group'. If
469 * the sign does not exist, return 0 as the line number. This will still let
470 * the correct file get loaded.
471 */
472 int
473buf_findsign(
474 buf_T *buf, // buffer to store sign in
475 int id, // sign ID
476 char_u *group) // sign group
477{
478 signlist_T *sign; // a sign in the signlist
479
480 FOR_ALL_SIGNS_IN_BUF(buf, sign)
481 if (sign->id == id && sign_in_group(sign, group))
482 return sign->lnum;
483
484 return 0;
485}
486
487/*
488 * Return the sign at line 'lnum' in buffer 'buf'. Returns NULL if a sign is
489 * not found at the line. If 'groupname' is NULL, searches in the global group.
490 */
491 static signlist_T *
492buf_getsign_at_line(
493 buf_T *buf, // buffer whose sign we are searching for
494 linenr_T lnum, // line number of sign
495 char_u *groupname) // sign group name
496{
497 signlist_T *sign; // a sign in the signlist
498
499 FOR_ALL_SIGNS_IN_BUF(buf, sign)
500 if (sign->lnum == lnum && sign_in_group(sign, groupname))
501 return sign;
502
503 return NULL;
504}
505
506/*
507 * Return the identifier of the sign at line number 'lnum' in buffer 'buf'.
508 */
509 int
510buf_findsign_id(
511 buf_T *buf, // buffer whose sign we are searching for
512 linenr_T lnum, // line number of sign
513 char_u *groupname) // sign group name
514{
515 signlist_T *sign; // a sign in the signlist
516
517 sign = buf_getsign_at_line(buf, lnum, groupname);
518 if (sign != NULL)
519 return sign->id;
520
521 return 0;
522}
523
524# if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
525/*
526 * See if a given type of sign exists on a specific line.
527 */
528 int
529buf_findsigntype_id(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100530 buf_T *buf, // buffer whose sign we are searching for
531 linenr_T lnum, // line number of sign
532 int typenr) // sign type number
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100533{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100534 signlist_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100535
536 FOR_ALL_SIGNS_IN_BUF(buf, sign)
537 if (sign->lnum == lnum && sign->typenr == typenr)
538 return sign->id;
539
540 return 0;
541}
542
543
544# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
545/*
546 * Return the number of icons on the given line.
547 */
548 int
549buf_signcount(buf_T *buf, linenr_T lnum)
550{
551 signlist_T *sign; // a sign in the signlist
552 int count = 0;
553
554 FOR_ALL_SIGNS_IN_BUF(buf, sign)
555 if (sign->lnum == lnum)
556 if (sign_get_image(sign->typenr) != NULL)
557 count++;
558
559 return count;
560}
561# endif /* FEAT_SIGN_ICONS */
562# endif /* FEAT_NETBEANS_INTG */
563
564/*
565 * Delete signs in group 'group' in buffer "buf". If 'group' is '*', then
566 * delete all the signs.
567 */
568 void
569buf_delete_signs(buf_T *buf, char_u *group)
570{
571 signlist_T *sign;
572 signlist_T **lastp; // pointer to pointer to current sign
573 signlist_T *next;
574
575 // When deleting the last sign need to redraw the windows to remove the
576 // sign column. Not when curwin is NULL (this means we're exiting).
577 if (buf->b_signlist != NULL && curwin != NULL)
578 {
579 redraw_buf_later(buf, NOT_VALID);
580 changed_cline_bef_curs();
581 }
582
583 lastp = &buf->b_signlist;
584 for (sign = buf->b_signlist; sign != NULL; sign = next)
585 {
586 next = sign->next;
587 if (sign_in_group(sign, group))
588 {
589 *lastp = next;
590 if (next != NULL)
591 next->prev = sign->prev;
592 if (sign->group != NULL)
593 sign_group_unref(sign->group->sg_name);
594 vim_free(sign);
595 }
596 else
597 lastp = &sign->next;
598 }
599}
600
601/*
602 * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers.
603 */
604 static void
605sign_list_placed(buf_T *rbuf, char_u *sign_group)
606{
607 buf_T *buf;
608 signlist_T *sign;
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100609 char lbuf[MSG_BUF_LEN];
610 char group[MSG_BUF_LEN];
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100611
Bram Moolenaar32526b32019-01-19 17:43:09 +0100612 msg_puts_title(_("\n--- Signs ---"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100613 msg_putchar('\n');
614 if (rbuf == NULL)
615 buf = firstbuf;
616 else
617 buf = rbuf;
618 while (buf != NULL && !got_int)
619 {
620 if (buf->b_signlist != NULL)
621 {
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100622 vim_snprintf(lbuf, MSG_BUF_LEN, _("Signs for %s:"), buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100623 msg_puts_attr(lbuf, HL_ATTR(HLF_D));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100624 msg_putchar('\n');
625 }
626 FOR_ALL_SIGNS_IN_BUF(buf, sign)
627 {
628 if (got_int)
629 break;
630 if (!sign_in_group(sign, sign_group))
631 continue;
632 if (sign->group != NULL)
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100633 vim_snprintf(group, MSG_BUF_LEN, _(" group=%s"),
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100634 sign->group->sg_name);
635 else
636 group[0] = '\0';
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100637 vim_snprintf(lbuf, MSG_BUF_LEN,
638 _(" line=%ld id=%d%s name=%s priority=%d"),
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100639 (long)sign->lnum, sign->id, group,
640 sign_typenr2name(sign->typenr), sign->priority);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100641 msg_puts(lbuf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100642 msg_putchar('\n');
643 }
644 if (rbuf != NULL)
645 break;
646 buf = buf->b_next;
647 }
648}
649
650/*
651 * Adjust a placed sign for inserted/deleted lines.
652 */
653 void
654sign_mark_adjust(
655 linenr_T line1,
656 linenr_T line2,
657 long amount,
658 long amount_after)
659{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100660 signlist_T *sign; // a sign in a b_signlist
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100661 linenr_T new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100662
663 FOR_ALL_SIGNS_IN_BUF(curbuf, sign)
664 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100665 // Ignore changes to lines after the sign
666 if (sign->lnum < line1)
667 continue;
668 new_lnum = sign->lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100669 if (sign->lnum >= line1 && sign->lnum <= line2)
670 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100671 if (amount != MAXLNUM)
672 new_lnum += amount;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100673 }
674 else if (sign->lnum > line2)
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100675 // Lines inserted or deleted before the sign
676 new_lnum += amount_after;
677
678 // If the new sign line number is past the last line in the buffer,
679 // then don't adjust the line number. Otherwise, it will always be past
680 // the last line and will not be visible.
681 if (new_lnum <= curbuf->b_ml.ml_line_count)
682 sign->lnum = new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100683 }
684}
685
686/*
687 * Find index of a ":sign" subcmd from its name.
688 * "*end_cmd" must be writable.
689 */
690 static int
691sign_cmd_idx(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100692 char_u *begin_cmd, // begin of sign subcmd
693 char_u *end_cmd) // just after sign subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100694{
695 int idx;
696 char save = *end_cmd;
697
698 *end_cmd = NUL;
699 for (idx = 0; ; ++idx)
700 if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0)
701 break;
702 *end_cmd = save;
703 return idx;
704}
705
706/*
707 * Find a sign by name. Also returns pointer to the previous sign.
708 */
709 static sign_T *
710sign_find(char_u *name, sign_T **sp_prev)
711{
712 sign_T *sp;
713
714 if (sp_prev != NULL)
715 *sp_prev = NULL;
716 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
717 {
718 if (STRCMP(sp->sn_name, name) == 0)
719 break;
720 if (sp_prev != NULL)
721 *sp_prev = sp;
722 }
723
724 return sp;
725}
726
727/*
Bram Moolenaar03142362019-01-18 22:01:42 +0100728 * Allocate a new sign
729 */
730 static sign_T *
731alloc_new_sign(char_u *name)
732{
733 sign_T *sp;
734 sign_T *lp;
735 int start = next_sign_typenr;
736
737 // Allocate a new sign.
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200738 sp = alloc_clear_id(sizeof(sign_T), aid_sign_define_by_name);
Bram Moolenaar03142362019-01-18 22:01:42 +0100739 if (sp == NULL)
740 return NULL;
741
742 // Check that next_sign_typenr is not already being used.
743 // This only happens after wrapping around. Hopefully
744 // another one got deleted and we can use its number.
745 for (lp = first_sign; lp != NULL; )
746 {
747 if (lp->sn_typenr == next_sign_typenr)
748 {
749 ++next_sign_typenr;
750 if (next_sign_typenr == MAX_TYPENR)
751 next_sign_typenr = 1;
752 if (next_sign_typenr == start)
753 {
754 vim_free(sp);
755 emsg(_("E612: Too many signs defined"));
756 return NULL;
757 }
758 lp = first_sign; // start all over
759 continue;
760 }
761 lp = lp->sn_next;
762 }
763
764 sp->sn_typenr = next_sign_typenr;
765 if (++next_sign_typenr == MAX_TYPENR)
766 next_sign_typenr = 1; // wrap around
767
768 sp->sn_name = vim_strsave(name);
769 if (sp->sn_name == NULL) // out of memory
770 {
771 vim_free(sp);
772 return NULL;
773 }
774
775 return sp;
776}
777
778/*
779 * Initialize the icon information for a new sign
780 */
781 static void
782sign_define_init_icon(sign_T *sp, char_u *icon)
783{
784 vim_free(sp->sn_icon);
785 sp->sn_icon = vim_strsave(icon);
786 backslash_halve(sp->sn_icon);
787# ifdef FEAT_SIGN_ICONS
788 if (gui.in_use)
789 {
790 out_flush();
791 if (sp->sn_image != NULL)
792 gui_mch_destroy_sign(sp->sn_image);
793 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
794 }
795# endif
796}
797
798/*
799 * Initialize the text for a new sign
800 */
801 static int
802sign_define_init_text(sign_T *sp, char_u *text)
803{
804 char_u *s;
805 char_u *endp;
806 int cells;
807 int len;
808
809 endp = text + (int)STRLEN(text);
810
811 // Remove backslashes so that it is possible to use a space.
812 for (s = text; s + 1 < endp; ++s)
813 if (*s == '\\')
814 {
815 STRMOVE(s, s + 1);
816 --endp;
817 }
818
819 // Count cells and check for non-printable chars
Bram Moolenaar03142362019-01-18 22:01:42 +0100820 if (has_mbyte)
821 {
822 cells = 0;
823 for (s = text; s < endp; s += (*mb_ptr2len)(s))
824 {
825 if (!vim_isprintc((*mb_ptr2char)(s)))
826 break;
827 cells += (*mb_ptr2cells)(s);
828 }
829 }
830 else
Bram Moolenaar03142362019-01-18 22:01:42 +0100831 {
832 for (s = text; s < endp; ++s)
833 if (!vim_isprintc(*s))
834 break;
835 cells = (int)(s - text);
836 }
837
838 // Currently sign text must be one or two display cells
839 if (s != endp || cells < 1 || cells > 2)
840 {
841 semsg(_("E239: Invalid sign text: %s"), text);
842 return FAIL;
843 }
844
845 vim_free(sp->sn_text);
846 // Allocate one byte more if we need to pad up
847 // with a space.
848 len = (int)(endp - text + ((cells == 1) ? 1 : 0));
849 sp->sn_text = vim_strnsave(text, len);
850
851 // For single character sign text, pad with a space.
852 if (sp->sn_text != NULL && cells == 1)
853 STRCPY(sp->sn_text + len - 1, " ");
854
855 return OK;
856}
857
858/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100859 * Define a new sign or update an existing sign
860 */
861 int
862sign_define_by_name(
863 char_u *name,
864 char_u *icon,
865 char_u *linehl,
866 char_u *text,
867 char_u *texthl)
868{
869 sign_T *sp_prev;
870 sign_T *sp;
871
872 sp = sign_find(name, &sp_prev);
873 if (sp == NULL)
874 {
Bram Moolenaar03142362019-01-18 22:01:42 +0100875 sp = alloc_new_sign(name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100876 if (sp == NULL)
877 return FAIL;
878
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100879 // add the new sign to the list of signs
880 if (sp_prev == NULL)
881 first_sign = sp;
882 else
883 sp_prev->sn_next = sp;
884 }
885
886 // set values for a defined sign.
887 if (icon != NULL)
Bram Moolenaar03142362019-01-18 22:01:42 +0100888 sign_define_init_icon(sp, icon);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100889
Bram Moolenaar03142362019-01-18 22:01:42 +0100890 if (text != NULL && (sign_define_init_text(sp, text) == FAIL))
891 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100892
893 if (linehl != NULL)
894 sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl));
895
896 if (texthl != NULL)
897 sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl));
898
899 return OK;
900}
901
902/*
903 * Free the sign specified by 'name'.
904 */
905 int
906sign_undefine_by_name(char_u *name)
907{
908 sign_T *sp_prev;
909 sign_T *sp;
910
911 sp = sign_find(name, &sp_prev);
912 if (sp == NULL)
913 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100914 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100915 return FAIL;
916 }
917 sign_undefine(sp, sp_prev);
918
919 return OK;
920}
921
922/*
923 * List the signs matching 'name'
924 */
925 static void
926sign_list_by_name(char_u *name)
927{
928 sign_T *sp;
929
930 sp = sign_find(name, NULL);
931 if (sp != NULL)
932 sign_list_defined(sp);
933 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100934 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100935}
936
937/*
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100938 * Place a sign at the specified file location or update a sign.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100939 */
940 int
941sign_place(
942 int *sign_id,
943 char_u *sign_group,
944 char_u *sign_name,
945 buf_T *buf,
946 linenr_T lnum,
947 int prio)
948{
949 sign_T *sp;
950
951 // Check for reserved character '*' in group name
952 if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0'))
953 return FAIL;
954
955 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
956 if (STRCMP(sp->sn_name, sign_name) == 0)
957 break;
958 if (sp == NULL)
959 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100960 semsg(_("E155: Unknown sign: %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100961 return FAIL;
962 }
963 if (*sign_id == 0)
964 *sign_id = sign_group_get_next_signid(buf, sign_group);
965
966 if (lnum > 0)
967 // ":sign place {id} line={lnum} name={name} file={fname}":
968 // place a sign
969 buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr);
970 else
971 // ":sign place {id} file={fname}": change sign type
972 lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr);
973 if (lnum > 0)
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100974 redraw_buf_line_later(buf, lnum);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100975 else
976 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100977 semsg(_("E885: Not possible to change sign %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100978 return FAIL;
979 }
980
981 return OK;
982}
983
984/*
985 * Unplace the specified sign
986 */
987 int
988sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum)
989{
990 if (buf->b_signlist == NULL) // No signs in the buffer
991 return OK;
992
993 if (sign_id == 0)
994 {
995 // Delete all the signs in the specified buffer
996 redraw_buf_later(buf, NOT_VALID);
997 buf_delete_signs(buf, sign_group);
998 }
999 else
1000 {
1001 linenr_T lnum;
1002
1003 // Delete only the specified signs
1004 lnum = buf_delsign(buf, atlnum, sign_id, sign_group);
1005 if (lnum == 0)
1006 return FAIL;
1007 }
1008
1009 return OK;
1010}
1011
1012/*
1013 * Unplace the sign at the current cursor line.
1014 */
1015 static void
1016sign_unplace_at_cursor(char_u *groupname)
1017{
1018 int id = -1;
1019
1020 id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname);
1021 if (id > 0)
1022 sign_unplace(id, groupname, curwin->w_buffer, curwin->w_cursor.lnum);
1023 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001024 emsg(_("E159: Missing sign number"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001025}
1026
1027/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001028 * Jump to a sign.
1029 */
1030 linenr_T
1031sign_jump(int sign_id, char_u *sign_group, buf_T *buf)
1032{
1033 linenr_T lnum;
1034
1035 if ((lnum = buf_findsign(buf, sign_id, sign_group)) <= 0)
1036 {
Bram Moolenaarb5443cc2019-01-15 20:19:40 +01001037 semsg(_("E157: Invalid sign ID: %d"), sign_id);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001038 return -1;
1039 }
1040
1041 // goto a sign ...
1042 if (buf_jump_open_win(buf) != NULL)
1043 { // ... in a current window
1044 curwin->w_cursor.lnum = lnum;
1045 check_cursor_lnum();
1046 beginline(BL_WHITE);
1047 }
1048 else
1049 { // ... not currently in a window
1050 char_u *cmd;
1051
1052 if (buf->b_fname == NULL)
1053 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001054 emsg(_("E934: Cannot jump to a buffer that does not have a name"));
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001055 return -1;
1056 }
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02001057 cmd = alloc(STRLEN(buf->b_fname) + 25);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001058 if (cmd == NULL)
1059 return -1;
1060 sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname);
1061 do_cmdline_cmd(cmd);
1062 vim_free(cmd);
1063 }
1064# ifdef FEAT_FOLDING
1065 foldOpenCursor();
1066# endif
1067
1068 return lnum;
1069}
1070
1071/*
1072 * ":sign define {name} ..." command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001073 */
1074 static void
1075sign_define_cmd(char_u *sign_name, char_u *cmdline)
1076{
1077 char_u *arg;
1078 char_u *p = cmdline;
1079 char_u *icon = NULL;
1080 char_u *text = NULL;
1081 char_u *linehl = NULL;
1082 char_u *texthl = NULL;
1083 int failed = FALSE;
1084
1085 // set values for a defined sign.
1086 for (;;)
1087 {
1088 arg = skipwhite(p);
1089 if (*arg == NUL)
1090 break;
1091 p = skiptowhite_esc(arg);
1092 if (STRNCMP(arg, "icon=", 5) == 0)
1093 {
1094 arg += 5;
1095 icon = vim_strnsave(arg, (int)(p - arg));
1096 }
1097 else if (STRNCMP(arg, "text=", 5) == 0)
1098 {
1099 arg += 5;
1100 text = vim_strnsave(arg, (int)(p - arg));
1101 }
1102 else if (STRNCMP(arg, "linehl=", 7) == 0)
1103 {
1104 arg += 7;
1105 linehl = vim_strnsave(arg, (int)(p - arg));
1106 }
1107 else if (STRNCMP(arg, "texthl=", 7) == 0)
1108 {
1109 arg += 7;
1110 texthl = vim_strnsave(arg, (int)(p - arg));
1111 }
1112 else
1113 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001114 semsg(_(e_invarg2), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001115 failed = TRUE;
1116 break;
1117 }
1118 }
1119
1120 if (!failed)
1121 sign_define_by_name(sign_name, icon, linehl, text, texthl);
1122
1123 vim_free(icon);
1124 vim_free(text);
1125 vim_free(linehl);
1126 vim_free(texthl);
1127}
1128
1129/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001130 * ":sign place" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001131 */
1132 static void
1133sign_place_cmd(
1134 buf_T *buf,
1135 linenr_T lnum,
1136 char_u *sign_name,
1137 int id,
1138 char_u *group,
1139 int prio)
1140{
1141 if (id <= 0)
1142 {
1143 // List signs placed in a file/buffer
1144 // :sign place file={fname}
1145 // :sign place group={group} file={fname}
1146 // :sign place group=* file={fname}
1147 // :sign place buffer={nr}
1148 // :sign place group={group} buffer={nr}
1149 // :sign place group=* buffer={nr}
1150 // :sign place
1151 // :sign place group={group}
1152 // :sign place group=*
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001153 if (lnum >= 0 || sign_name != NULL
1154 || (group != NULL && *group == '\0'))
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001155 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001156 else
1157 sign_list_placed(buf, group);
1158 }
1159 else
1160 {
1161 // Place a new sign
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001162 if (sign_name == NULL || buf == NULL
1163 || (group != NULL && *group == '\0'))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001164 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001165 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001166 return;
1167 }
1168
1169 sign_place(&id, group, sign_name, buf, lnum, prio);
1170 }
1171}
1172
1173/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001174 * ":sign unplace" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001175 */
1176 static void
1177sign_unplace_cmd(
1178 buf_T *buf,
1179 linenr_T lnum,
1180 char_u *sign_name,
1181 int id,
1182 char_u *group)
1183{
1184 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0'))
1185 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001186 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001187 return;
1188 }
1189
1190 if (id == -2)
1191 {
1192 if (buf != NULL)
1193 // :sign unplace * file={fname}
1194 // :sign unplace * group={group} file={fname}
1195 // :sign unplace * group=* file={fname}
1196 // :sign unplace * buffer={nr}
1197 // :sign unplace * group={group} buffer={nr}
1198 // :sign unplace * group=* buffer={nr}
1199 sign_unplace(0, group, buf, 0);
1200 else
1201 // :sign unplace *
1202 // :sign unplace * group={group}
1203 // :sign unplace * group=*
1204 FOR_ALL_BUFFERS(buf)
1205 if (buf->b_signlist != NULL)
1206 buf_delete_signs(buf, group);
1207 }
1208 else
1209 {
1210 if (buf != NULL)
1211 // :sign unplace {id} file={fname}
1212 // :sign unplace {id} group={group} file={fname}
1213 // :sign unplace {id} group=* file={fname}
1214 // :sign unplace {id} buffer={nr}
1215 // :sign unplace {id} group={group} buffer={nr}
1216 // :sign unplace {id} group=* buffer={nr}
1217 sign_unplace(id, group, buf, 0);
1218 else
1219 {
1220 if (id == -1)
1221 {
1222 // :sign unplace group={group}
1223 // :sign unplace group=*
1224 sign_unplace_at_cursor(group);
1225 }
1226 else
1227 {
1228 // :sign unplace {id}
1229 // :sign unplace {id} group={group}
1230 // :sign unplace {id} group=*
1231 FOR_ALL_BUFFERS(buf)
1232 sign_unplace(id, group, buf, 0);
1233 }
1234 }
1235 }
1236}
1237
1238/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001239 * Jump to a placed sign commands:
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001240 * :sign jump {id} file={fname}
1241 * :sign jump {id} buffer={nr}
1242 * :sign jump {id} group={group} file={fname}
1243 * :sign jump {id} group={group} buffer={nr}
1244 */
1245 static void
1246sign_jump_cmd(
1247 buf_T *buf,
1248 linenr_T lnum,
1249 char_u *sign_name,
1250 int id,
1251 char_u *group)
1252{
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001253 if (sign_name == NULL && group == NULL && id == -1)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001254 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001255 emsg(_(e_argreq));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001256 return;
1257 }
1258
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001259 if (buf == NULL || (group != NULL && *group == '\0')
1260 || lnum >= 0 || sign_name != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001261 {
1262 // File or buffer is not specified or an empty group is used
1263 // or a line number or a sign name is specified.
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001264 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001265 return;
1266 }
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001267 (void)sign_jump(id, group, buf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001268}
1269
1270/*
1271 * Parse the command line arguments for the ":sign place", ":sign unplace" and
1272 * ":sign jump" commands.
1273 * The supported arguments are: line={lnum} name={name} group={group}
1274 * priority={prio} and file={fname} or buffer={nr}.
1275 */
1276 static int
1277parse_sign_cmd_args(
1278 int cmd,
1279 char_u *arg,
1280 char_u **sign_name,
1281 int *signid,
1282 char_u **group,
1283 int *prio,
1284 buf_T **buf,
1285 linenr_T *lnum)
1286{
1287 char_u *arg1;
1288 char_u *name;
1289 char_u *filename = NULL;
Bram Moolenaarb589f952019-01-07 22:10:00 +01001290 int lnum_arg = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001291
1292 // first arg could be placed sign id
1293 arg1 = arg;
1294 if (VIM_ISDIGIT(*arg))
1295 {
1296 *signid = getdigits(&arg);
1297 if (!VIM_ISWHITE(*arg) && *arg != NUL)
1298 {
1299 *signid = -1;
1300 arg = arg1;
1301 }
1302 else
1303 arg = skipwhite(arg);
1304 }
1305
1306 while (*arg != NUL)
1307 {
1308 if (STRNCMP(arg, "line=", 5) == 0)
1309 {
1310 arg += 5;
1311 *lnum = atoi((char *)arg);
1312 arg = skiptowhite(arg);
Bram Moolenaarb589f952019-01-07 22:10:00 +01001313 lnum_arg = TRUE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001314 }
1315 else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE)
1316 {
1317 if (*signid != -1)
1318 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001319 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001320 return FAIL;
1321 }
1322 *signid = -2;
1323 arg = skiptowhite(arg + 1);
1324 }
1325 else if (STRNCMP(arg, "name=", 5) == 0)
1326 {
1327 arg += 5;
1328 name = arg;
1329 arg = skiptowhite(arg);
1330 if (*arg != NUL)
1331 *arg++ = NUL;
1332 while (name[0] == '0' && name[1] != NUL)
1333 ++name;
1334 *sign_name = name;
1335 }
1336 else if (STRNCMP(arg, "group=", 6) == 0)
1337 {
1338 arg += 6;
1339 *group = arg;
1340 arg = skiptowhite(arg);
1341 if (*arg != NUL)
1342 *arg++ = NUL;
1343 }
1344 else if (STRNCMP(arg, "priority=", 9) == 0)
1345 {
1346 arg += 9;
1347 *prio = atoi((char *)arg);
1348 arg = skiptowhite(arg);
1349 }
1350 else if (STRNCMP(arg, "file=", 5) == 0)
1351 {
1352 arg += 5;
1353 filename = arg;
1354 *buf = buflist_findname_exp(arg);
1355 break;
1356 }
1357 else if (STRNCMP(arg, "buffer=", 7) == 0)
1358 {
1359 arg += 7;
1360 filename = arg;
1361 *buf = buflist_findnr((int)getdigits(&arg));
1362 if (*skipwhite(arg) != NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001363 emsg(_(e_trailing));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001364 break;
1365 }
1366 else
1367 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001368 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001369 return FAIL;
1370 }
1371 arg = skipwhite(arg);
1372 }
1373
1374 if (filename != NULL && *buf == NULL)
1375 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001376 semsg(_("E158: Invalid buffer name: %s"), filename);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001377 return FAIL;
1378 }
1379
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001380 // If the filename is not supplied for the sign place or the sign jump
1381 // command, then use the current buffer.
Bram Moolenaarb589f952019-01-07 22:10:00 +01001382 if (filename == NULL && ((cmd == SIGNCMD_PLACE && lnum_arg)
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001383 || cmd == SIGNCMD_JUMP))
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001384 *buf = curwin->w_buffer;
1385
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001386 return OK;
1387}
1388
1389/*
1390 * ":sign" command
1391 */
1392 void
1393ex_sign(exarg_T *eap)
1394{
1395 char_u *arg = eap->arg;
1396 char_u *p;
1397 int idx;
1398 sign_T *sp;
1399 buf_T *buf = NULL;
1400
1401 // Parse the subcommand.
1402 p = skiptowhite(arg);
1403 idx = sign_cmd_idx(arg, p);
1404 if (idx == SIGNCMD_LAST)
1405 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001406 semsg(_("E160: Unknown sign command: %s"), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001407 return;
1408 }
1409 arg = skipwhite(p);
1410
1411 if (idx <= SIGNCMD_LIST)
1412 {
1413 // Define, undefine or list signs.
1414 if (idx == SIGNCMD_LIST && *arg == NUL)
1415 {
1416 // ":sign list": list all defined signs
1417 for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next)
1418 sign_list_defined(sp);
1419 }
1420 else if (*arg == NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001421 emsg(_("E156: Missing sign name"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001422 else
1423 {
1424 char_u *name;
1425
1426 // Isolate the sign name. If it's a number skip leading zeroes,
1427 // so that "099" and "99" are the same sign. But keep "0".
1428 p = skiptowhite(arg);
1429 if (*p != NUL)
1430 *p++ = NUL;
1431 while (arg[0] == '0' && arg[1] != NUL)
1432 ++arg;
1433 name = vim_strsave(arg);
1434
1435 if (idx == SIGNCMD_DEFINE)
1436 sign_define_cmd(name, p);
1437 else if (idx == SIGNCMD_LIST)
1438 // ":sign list {name}"
1439 sign_list_by_name(name);
1440 else
1441 // ":sign undefine {name}"
1442 sign_undefine_by_name(name);
1443
1444 vim_free(name);
1445 return;
1446 }
1447 }
1448 else
1449 {
1450 int id = -1;
1451 linenr_T lnum = -1;
1452 char_u *sign_name = NULL;
1453 char_u *group = NULL;
1454 int prio = SIGN_DEF_PRIO;
1455
1456 // Parse command line arguments
1457 if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio,
1458 &buf, &lnum) == FAIL)
1459 return;
1460
1461 if (idx == SIGNCMD_PLACE)
1462 sign_place_cmd(buf, lnum, sign_name, id, group, prio);
1463 else if (idx == SIGNCMD_UNPLACE)
1464 sign_unplace_cmd(buf, lnum, sign_name, id, group);
1465 else if (idx == SIGNCMD_JUMP)
1466 sign_jump_cmd(buf, lnum, sign_name, id, group);
1467 }
1468}
1469
1470/*
1471 * Return information about a specified sign
1472 */
1473 static void
1474sign_getinfo(sign_T *sp, dict_T *retdict)
1475{
1476 char_u *p;
1477
1478 dict_add_string(retdict, "name", (char_u *)sp->sn_name);
1479 if (sp->sn_icon != NULL)
1480 dict_add_string(retdict, "icon", (char_u *)sp->sn_icon);
1481 if (sp->sn_text != NULL)
1482 dict_add_string(retdict, "text", (char_u *)sp->sn_text);
1483 if (sp->sn_line_hl > 0)
1484 {
1485 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1486 if (p == NULL)
1487 p = (char_u *)"NONE";
1488 dict_add_string(retdict, "linehl", (char_u *)p);
1489 }
1490 if (sp->sn_text_hl > 0)
1491 {
1492 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1493 if (p == NULL)
1494 p = (char_u *)"NONE";
1495 dict_add_string(retdict, "texthl", (char_u *)p);
1496 }
1497}
1498
1499/*
1500 * If 'name' is NULL, return a list of all the defined signs.
1501 * Otherwise, return information about the specified sign.
1502 */
1503 void
1504sign_getlist(char_u *name, list_T *retlist)
1505{
1506 sign_T *sp = first_sign;
1507 dict_T *dict;
1508
1509 if (name != NULL)
1510 {
1511 sp = sign_find(name, NULL);
1512 if (sp == NULL)
1513 return;
1514 }
1515
1516 for (; sp != NULL && !got_int; sp = sp->sn_next)
1517 {
1518 if ((dict = dict_alloc_id(aid_sign_getlist)) == NULL)
1519 return;
1520 if (list_append_dict(retlist, dict) == FAIL)
1521 return;
1522 sign_getinfo(sp, dict);
1523
1524 if (name != NULL) // handle only the specified sign
1525 break;
1526 }
1527}
1528
1529/*
1530 * Returns information about signs placed in a buffer as list of dicts.
1531 */
1532 void
1533get_buffer_signs(buf_T *buf, list_T *l)
1534{
1535 signlist_T *sign;
1536 dict_T *d;
1537
1538 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1539 {
1540 if ((d = sign_get_info(sign)) != NULL)
1541 list_append_dict(l, d);
1542 }
1543}
1544
1545/*
1546 * Return information about all the signs placed in a buffer
1547 */
1548 static void
1549sign_get_placed_in_buf(
1550 buf_T *buf,
1551 linenr_T lnum,
1552 int sign_id,
1553 char_u *sign_group,
1554 list_T *retlist)
1555{
1556 dict_T *d;
1557 list_T *l;
1558 signlist_T *sign;
1559 dict_T *sdict;
1560
1561 if ((d = dict_alloc_id(aid_sign_getplaced_dict)) == NULL)
1562 return;
1563 list_append_dict(retlist, d);
1564
1565 dict_add_number(d, "bufnr", (long)buf->b_fnum);
1566
1567 if ((l = list_alloc_id(aid_sign_getplaced_list)) == NULL)
1568 return;
1569 dict_add_list(d, "signs", l);
1570
1571 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1572 {
1573 if (!sign_in_group(sign, sign_group))
1574 continue;
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001575 if ((lnum == 0 && sign_id == 0)
1576 || (sign_id == 0 && lnum == sign->lnum)
1577 || (lnum == 0 && sign_id == sign->id)
1578 || (lnum == sign->lnum && sign_id == sign->id))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001579 {
1580 if ((sdict = sign_get_info(sign)) != NULL)
1581 list_append_dict(l, sdict);
1582 }
1583 }
1584}
1585
1586/*
1587 * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the
1588 * sign placed at the line number. If 'lnum' is zero, return all the signs
1589 * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers.
1590 */
1591 void
1592sign_get_placed(
1593 buf_T *buf,
1594 linenr_T lnum,
1595 int sign_id,
1596 char_u *sign_group,
1597 list_T *retlist)
1598{
1599 if (buf != NULL)
1600 sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist);
1601 else
1602 {
1603 FOR_ALL_BUFFERS(buf)
1604 {
1605 if (buf->b_signlist != NULL)
1606 sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist);
1607 }
1608 }
1609}
1610
1611# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1612/*
1613 * Allocate the icons. Called when the GUI has started. Allows defining
1614 * signs before it starts.
1615 */
1616 void
1617sign_gui_started(void)
1618{
1619 sign_T *sp;
1620
1621 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1622 if (sp->sn_icon != NULL)
1623 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
1624}
1625# endif
1626
1627/*
1628 * List one sign.
1629 */
1630 static void
1631sign_list_defined(sign_T *sp)
1632{
1633 char_u *p;
1634
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001635 smsg("sign %s", sp->sn_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001636 if (sp->sn_icon != NULL)
1637 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001638 msg_puts(" icon=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001639 msg_outtrans(sp->sn_icon);
1640# ifdef FEAT_SIGN_ICONS
1641 if (sp->sn_image == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001642 msg_puts(_(" (NOT FOUND)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001643# else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001644 msg_puts(_(" (not supported)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001645# endif
1646 }
1647 if (sp->sn_text != NULL)
1648 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001649 msg_puts(" text=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001650 msg_outtrans(sp->sn_text);
1651 }
1652 if (sp->sn_line_hl > 0)
1653 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001654 msg_puts(" linehl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001655 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1656 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001657 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001658 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001659 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001660 }
1661 if (sp->sn_text_hl > 0)
1662 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001663 msg_puts(" texthl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001664 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1665 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001666 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001667 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001668 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001669 }
1670}
1671
1672/*
1673 * Undefine a sign and free its memory.
1674 */
1675 static void
1676sign_undefine(sign_T *sp, sign_T *sp_prev)
1677{
1678 vim_free(sp->sn_name);
1679 vim_free(sp->sn_icon);
1680# ifdef FEAT_SIGN_ICONS
1681 if (sp->sn_image != NULL)
1682 {
1683 out_flush();
1684 gui_mch_destroy_sign(sp->sn_image);
1685 }
1686# endif
1687 vim_free(sp->sn_text);
1688 if (sp_prev == NULL)
1689 first_sign = sp->sn_next;
1690 else
1691 sp_prev->sn_next = sp->sn_next;
1692 vim_free(sp);
1693}
1694
1695/*
1696 * Get highlighting attribute for sign "typenr".
1697 * If "line" is TRUE: line highl, if FALSE: text highl.
1698 */
1699 int
1700sign_get_attr(int typenr, int line)
1701{
1702 sign_T *sp;
1703
1704 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1705 if (sp->sn_typenr == typenr)
1706 {
1707 if (line)
1708 {
1709 if (sp->sn_line_hl > 0)
1710 return syn_id2attr(sp->sn_line_hl);
1711 }
1712 else
1713 {
1714 if (sp->sn_text_hl > 0)
1715 return syn_id2attr(sp->sn_text_hl);
1716 }
1717 break;
1718 }
1719 return 0;
1720}
1721
1722/*
1723 * Get text mark for sign "typenr".
1724 * Returns NULL if there isn't one.
1725 */
1726 char_u *
1727sign_get_text(int typenr)
1728{
1729 sign_T *sp;
1730
1731 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1732 if (sp->sn_typenr == typenr)
1733 return sp->sn_text;
1734 return NULL;
1735}
1736
1737# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1738 void *
1739sign_get_image(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001740 int typenr) // the attribute which may have a sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001741{
1742 sign_T *sp;
1743
1744 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1745 if (sp->sn_typenr == typenr)
1746 return sp->sn_image;
1747 return NULL;
1748}
1749# endif
1750
1751/*
1752 * Undefine/free all signs.
1753 */
1754 void
1755free_signs(void)
1756{
1757 while (first_sign != NULL)
1758 sign_undefine(first_sign, NULL);
1759}
1760
1761# if defined(FEAT_CMDL_COMPL) || defined(PROTO)
1762static enum
1763{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001764 EXP_SUBCMD, // expand :sign sub-commands
1765 EXP_DEFINE, // expand :sign define {name} args
1766 EXP_PLACE, // expand :sign place {id} args
Bram Moolenaar3678f652019-02-17 14:50:25 +01001767 EXP_LIST, // expand :sign place args
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001768 EXP_UNPLACE, // expand :sign unplace"
Bram Moolenaar3678f652019-02-17 14:50:25 +01001769 EXP_SIGN_NAMES, // expand with name of placed signs
1770 EXP_SIGN_GROUPS // expand with name of placed sign groups
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001771} expand_what;
1772
1773/*
Bram Moolenaar3678f652019-02-17 14:50:25 +01001774 * Return the n'th sign name (used for command line completion)
1775 */
1776 static char_u *
1777get_nth_sign_name(int idx)
1778{
1779 int current_idx;
1780 sign_T *sp;
1781
1782 // Complete with name of signs already defined
1783 current_idx = 0;
1784 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1785 if (current_idx++ == idx)
1786 return sp->sn_name;
1787 return NULL;
1788}
1789
1790/*
1791 * Return the n'th sign group name (used for command line completion)
1792 */
1793 static char_u *
1794get_nth_sign_group_name(int idx)
1795{
1796 int current_idx;
1797 int todo;
1798 hashitem_T *hi;
1799 signgroup_T *group;
1800
1801 // Complete with name of sign groups already defined
1802 current_idx = 0;
1803 todo = (int)sg_table.ht_used;
1804 for (hi = sg_table.ht_array; todo > 0; ++hi)
1805 {
1806 if (!HASHITEM_EMPTY(hi))
1807 {
1808 --todo;
1809 if (current_idx++ == idx)
1810 {
1811 group = HI2SG(hi);
1812 return group->sg_name;
1813 }
1814 }
1815 }
1816 return NULL;
1817}
1818
1819/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001820 * Function given to ExpandGeneric() to obtain the sign command
1821 * expansion.
1822 */
1823 char_u *
1824get_sign_name(expand_T *xp UNUSED, int idx)
1825{
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001826 switch (expand_what)
1827 {
1828 case EXP_SUBCMD:
1829 return (char_u *)cmds[idx];
1830 case EXP_DEFINE:
1831 {
1832 char *define_arg[] =
1833 {
1834 "icon=", "linehl=", "text=", "texthl=", NULL
1835 };
1836 return (char_u *)define_arg[idx];
1837 }
1838 case EXP_PLACE:
1839 {
1840 char *place_arg[] =
1841 {
1842 "line=", "name=", "group=", "priority=", "file=",
1843 "buffer=", NULL
1844 };
1845 return (char_u *)place_arg[idx];
1846 }
Bram Moolenaar3678f652019-02-17 14:50:25 +01001847 case EXP_LIST:
1848 {
1849 char *list_arg[] =
1850 {
1851 "group=", "file=", "buffer=", NULL
1852 };
1853 return (char_u *)list_arg[idx];
1854 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001855 case EXP_UNPLACE:
1856 {
1857 char *unplace_arg[] = { "group=", "file=", "buffer=", NULL };
1858 return (char_u *)unplace_arg[idx];
1859 }
1860 case EXP_SIGN_NAMES:
Bram Moolenaar3678f652019-02-17 14:50:25 +01001861 return get_nth_sign_name(idx);
1862 case EXP_SIGN_GROUPS:
1863 return get_nth_sign_group_name(idx);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001864 default:
1865 return NULL;
1866 }
1867}
1868
1869/*
1870 * Handle command line completion for :sign command.
1871 */
1872 void
1873set_context_in_sign_cmd(expand_T *xp, char_u *arg)
1874{
1875 char_u *p;
1876 char_u *end_subcmd;
1877 char_u *last;
1878 int cmd_idx;
1879 char_u *begin_subcmd_args;
1880
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001881 // Default: expand subcommands.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001882 xp->xp_context = EXPAND_SIGN;
1883 expand_what = EXP_SUBCMD;
1884 xp->xp_pattern = arg;
1885
1886 end_subcmd = skiptowhite(arg);
1887 if (*end_subcmd == NUL)
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001888 // expand subcmd name
1889 // :sign {subcmd}<CTRL-D>
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001890 return;
1891
1892 cmd_idx = sign_cmd_idx(arg, end_subcmd);
1893
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001894 // :sign {subcmd} {subcmd_args}
1895 // |
1896 // begin_subcmd_args
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001897 begin_subcmd_args = skipwhite(end_subcmd);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001898
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001899 // expand last argument of subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001900
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001901 // :sign define {name} {args}...
1902 // |
1903 // p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001904
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001905 // Loop until reaching last argument.
Bram Moolenaar3678f652019-02-17 14:50:25 +01001906 p = begin_subcmd_args;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001907 do
1908 {
1909 p = skipwhite(p);
1910 last = p;
1911 p = skiptowhite(p);
1912 } while (*p != NUL);
1913
1914 p = vim_strchr(last, '=');
1915
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001916 // :sign define {name} {args}... {last}=
1917 // | |
1918 // last p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001919 if (p == NULL)
1920 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001921 // Expand last argument name (before equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001922 xp->xp_pattern = last;
1923 switch (cmd_idx)
1924 {
1925 case SIGNCMD_DEFINE:
1926 expand_what = EXP_DEFINE;
1927 break;
1928 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01001929 // List placed signs
1930 if (VIM_ISDIGIT(*begin_subcmd_args))
1931 // :sign place {id} {args}...
1932 expand_what = EXP_PLACE;
1933 else
1934 // :sign place {args}...
1935 expand_what = EXP_LIST;
1936 break;
1937 case SIGNCMD_LIST:
1938 case SIGNCMD_UNDEFINE:
1939 // :sign list <CTRL-D>
1940 // :sign undefine <CTRL-D>
1941 expand_what = EXP_SIGN_NAMES;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001942 break;
1943 case SIGNCMD_JUMP:
1944 case SIGNCMD_UNPLACE:
1945 expand_what = EXP_UNPLACE;
1946 break;
1947 default:
1948 xp->xp_context = EXPAND_NOTHING;
1949 }
1950 }
1951 else
1952 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001953 // Expand last argument value (after equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001954 xp->xp_pattern = p + 1;
1955 switch (cmd_idx)
1956 {
1957 case SIGNCMD_DEFINE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01001958 if (STRNCMP(last, "texthl", 6) == 0
1959 || STRNCMP(last, "linehl", 6) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001960 xp->xp_context = EXPAND_HIGHLIGHT;
Bram Moolenaar3678f652019-02-17 14:50:25 +01001961 else if (STRNCMP(last, "icon", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001962 xp->xp_context = EXPAND_FILES;
1963 else
1964 xp->xp_context = EXPAND_NOTHING;
1965 break;
1966 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01001967 if (STRNCMP(last, "name", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001968 expand_what = EXP_SIGN_NAMES;
Bram Moolenaar3678f652019-02-17 14:50:25 +01001969 else if (STRNCMP(last, "group", 5) == 0)
1970 expand_what = EXP_SIGN_GROUPS;
1971 else if (STRNCMP(last, "file", 4) == 0)
1972 xp->xp_context = EXPAND_BUFFERS;
1973 else
1974 xp->xp_context = EXPAND_NOTHING;
1975 break;
1976 case SIGNCMD_UNPLACE:
1977 case SIGNCMD_JUMP:
1978 if (STRNCMP(last, "group", 5) == 0)
1979 expand_what = EXP_SIGN_GROUPS;
1980 else if (STRNCMP(last, "file", 4) == 0)
1981 xp->xp_context = EXPAND_BUFFERS;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001982 else
1983 xp->xp_context = EXPAND_NOTHING;
1984 break;
1985 default:
1986 xp->xp_context = EXPAND_NOTHING;
1987 }
1988 }
1989}
1990# endif
1991
1992#endif /* FEAT_SIGNS */