blob: ddf2ac6a2a7fd068055acdf55904e4f6e379f4aa [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 Moolenaar47ed5532019-08-08 20:49:14 +020088 group = alloc(offsetof(signgroup_T, sg_name) + STRLEN(groupname) + 1);
Bram Moolenaarbbea4702019-01-01 13:20:31 +010089 if (group == NULL)
90 return NULL;
91 STRCPY(group->sg_name, groupname);
Bram Moolenaar6656c2e2019-10-24 15:00:04 +020092 group->sg_refcount = 1;
93 group->sg_next_sign_id = 1;
Bram Moolenaarbbea4702019-01-01 13:20:31 +010094 hash_add_item(&sg_table, hi, group->sg_name, hash);
95 }
96 else
97 {
98 // existing group
99 group = HI2SG(hi);
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200100 group->sg_refcount++;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100101 }
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);
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200120 group->sg_refcount--;
121 if (group->sg_refcount == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100122 {
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'.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200132 * A sign can either be in the global group (sign->se_group == NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100133 * or in a named group. If 'group' is '*', then the sign is part of the group.
134 */
135 static int
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200136sign_in_group(sign_entry_T *sign, char_u *group)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100137{
138 return ((group != NULL && STRCMP(group, "*") == 0)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200139 || (group == NULL && sign->se_group == NULL)
140 || (group != NULL && sign->se_group != NULL
141 && STRCMP(group, sign->se_group->sg_name) == 0));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100142}
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;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200152 sign_entry_T *sign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100153 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
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200170 id = group->sg_next_sign_id++;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100171
172 // Check whether this sign is already placed in the buffer
173 found = TRUE;
174 FOR_ALL_SIGNS_IN_BUF(buf, sign)
175 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200176 if (id == sign->se_id && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100177 {
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
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200194 sign_entry_T *prev, // previous sign entry
195 sign_entry_T *next, // next sign entry
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100196 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{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200202 sign_entry_T *newsign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100203
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200204 newsign = lalloc_id(sizeof(sign_entry_T), FALSE, aid_insert_sign);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100205 if (newsign != NULL)
206 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200207 newsign->se_id = id;
208 newsign->se_lnum = lnum;
209 newsign->se_typenr = typenr;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100210 if (group != NULL)
211 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200212 newsign->se_group = sign_group_ref(group);
213 if (newsign->se_group == NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100214 {
215 vim_free(newsign);
216 return;
217 }
218 }
219 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200220 newsign->se_group = NULL;
221 newsign->se_priority = prio;
222 newsign->se_next = next;
223 newsign->se_prev = prev;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100224 if (next != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200225 next->se_prev = newsign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100226
227 if (prev == NULL)
228 {
229 // When adding first sign need to redraw the windows to create the
230 // column for signs.
231 if (buf->b_signlist == NULL)
232 {
233 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200234 changed_line_abv_curs();
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100235 }
236
237 // first sign in signlist
238 buf->b_signlist = newsign;
239#ifdef FEAT_NETBEANS_INTG
240 if (netbeans_active())
241 buf->b_has_sign_column = TRUE;
242#endif
243 }
244 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200245 prev->se_next = newsign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100246 }
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
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200255 sign_entry_T *prev, // previous sign entry
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100256 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{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200262 sign_entry_T *sign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100263
264 // keep signs sorted by lnum and by priority: insert new sign at
265 // the proper position in the list for this lnum.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200266 while (prev != NULL && prev->se_lnum == lnum && prev->se_priority <= prio)
267 prev = prev->se_prev;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100268 if (prev == NULL)
269 sign = buf->b_signlist;
270 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200271 sign = prev->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100272
273 insert_sign(buf, prev, sign, id, group, prio, lnum, typenr);
274}
275
276/*
Bram Moolenaar4e038572019-07-04 18:28:35 +0200277 * Lookup a sign by typenr. Returns NULL if sign is not found.
278 */
279 static sign_T *
280find_sign_by_typenr(int typenr)
281{
282 sign_T *sp;
283
284 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
285 if (sp->sn_typenr == typenr)
286 return sp;
287 return NULL;
288}
289
290/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100291 * Get the name of a sign by its typenr.
292 */
293 static char_u *
294sign_typenr2name(int typenr)
295{
296 sign_T *sp;
297
298 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
299 if (sp->sn_typenr == typenr)
300 return sp->sn_name;
301 return (char_u *)_("[Deleted]");
302}
303
304/*
305 * Return information about a sign in a Dict
306 */
307 static dict_T *
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200308sign_get_info(sign_entry_T *sign)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100309{
310 dict_T *d;
311
312 if ((d = dict_alloc_id(aid_sign_getinfo)) == NULL)
313 return NULL;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200314 dict_add_number(d, "id", sign->se_id);
315 dict_add_string(d, "group", (sign->se_group == NULL) ?
316 (char_u *)"" : sign->se_group->sg_name);
317 dict_add_number(d, "lnum", sign->se_lnum);
318 dict_add_string(d, "name", sign_typenr2name(sign->se_typenr));
319 dict_add_number(d, "priority", sign->se_priority);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100320
321 return d;
322}
323
324/*
Bram Moolenaar64416122019-06-07 21:37:13 +0200325 * Sort the signs placed on the same line as "sign" by priority. Invoked after
326 * changing the priority of an already placed sign. Assumes the signs in the
327 * buffer are sorted by line number and priority.
328 */
329 static void
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200330sign_sort_by_prio_on_line(buf_T *buf, sign_entry_T *sign)
Bram Moolenaar64416122019-06-07 21:37:13 +0200331{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200332 sign_entry_T *p = NULL;
Bram Moolenaar64416122019-06-07 21:37:13 +0200333
334 // If there is only one sign in the buffer or only one sign on the line or
335 // the sign is already sorted by priority, then return.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200336 if ((sign->se_prev == NULL
337 || sign->se_prev->se_lnum != sign->se_lnum
338 || sign->se_prev->se_priority > sign->se_priority)
339 && (sign->se_next == NULL
340 || sign->se_next->se_lnum != sign->se_lnum
341 || sign->se_next->se_priority < sign->se_priority))
Bram Moolenaar64416122019-06-07 21:37:13 +0200342 return;
343
344 // One or more signs on the same line as 'sign'
345 // Find a sign after which 'sign' should be inserted
346
347 // First search backward for a sign with higher priority on the same line
348 p = sign;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200349 while (p->se_prev != NULL && p->se_prev->se_lnum == sign->se_lnum
350 && p->se_prev->se_priority <= sign->se_priority)
351 p = p->se_prev;
Bram Moolenaar64416122019-06-07 21:37:13 +0200352
353 if (p == sign)
354 {
355 // Sign not found. Search forward for a sign with priority just before
356 // 'sign'.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200357 p = sign->se_next;
358 while (p->se_next != NULL && p->se_next->se_lnum == sign->se_lnum
359 && p->se_next->se_priority > sign->se_priority)
360 p = p->se_next;
Bram Moolenaar64416122019-06-07 21:37:13 +0200361 }
362
363 // Remove 'sign' from the list
364 if (buf->b_signlist == sign)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200365 buf->b_signlist = sign->se_next;
366 if (sign->se_prev != NULL)
367 sign->se_prev->se_next = sign->se_next;
368 if (sign->se_next != NULL)
369 sign->se_next->se_prev = sign->se_prev;
370 sign->se_prev = NULL;
371 sign->se_next = NULL;
Bram Moolenaar64416122019-06-07 21:37:13 +0200372
373 // Re-insert 'sign' at the right place
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200374 if (p->se_priority <= sign->se_priority)
Bram Moolenaar64416122019-06-07 21:37:13 +0200375 {
376 // 'sign' has a higher priority and should be inserted before 'p'
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200377 sign->se_prev = p->se_prev;
378 sign->se_next = p;
379 p->se_prev = sign;
380 if (sign->se_prev != NULL)
381 sign->se_prev->se_next = sign;
Bram Moolenaar64416122019-06-07 21:37:13 +0200382 if (buf->b_signlist == p)
383 buf->b_signlist = sign;
384 }
385 else
386 {
387 // 'sign' has a lower priority and should be inserted after 'p'
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200388 sign->se_prev = p;
389 sign->se_next = p->se_next;
390 p->se_next = sign;
391 if (sign->se_next != NULL)
392 sign->se_next->se_prev = sign;
Bram Moolenaar64416122019-06-07 21:37:13 +0200393 }
394}
395
396/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100397 * Add the sign into the signlist. Find the right spot to do it though.
398 */
399 static void
400buf_addsign(
401 buf_T *buf, // buffer to store sign in
402 int id, // sign ID
403 char_u *groupname, // sign group
404 int prio, // sign priority
405 linenr_T lnum, // line number which gets the mark
406 int typenr) // typenr of sign we are adding
407{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200408 sign_entry_T *sign; // a sign in the signlist
409 sign_entry_T *prev; // the previous sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100410
411 prev = NULL;
412 FOR_ALL_SIGNS_IN_BUF(buf, sign)
413 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200414 if (lnum == sign->se_lnum && id == sign->se_id
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100415 && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100416 {
417 // Update an existing sign
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200418 sign->se_typenr = typenr;
419 sign->se_priority = prio;
Bram Moolenaar64416122019-06-07 21:37:13 +0200420 sign_sort_by_prio_on_line(buf, sign);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100421 return;
422 }
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200423 else if (lnum < sign->se_lnum)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100424 {
425 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio,
426 lnum, typenr);
427 return;
428 }
429 prev = sign;
430 }
431
432 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr);
433 return;
434}
435
436/*
437 * For an existing, placed sign "markId" change the type to "typenr".
438 * Returns the line number of the sign, or zero if the sign is not found.
439 */
440 static linenr_T
441buf_change_sign_type(
442 buf_T *buf, // buffer to store sign in
443 int markId, // sign ID
444 char_u *group, // sign group
Bram Moolenaar809ce4d2019-07-13 21:21:40 +0200445 int typenr, // typenr of sign we are adding
446 int prio) // sign priority
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100447{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200448 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100449
450 FOR_ALL_SIGNS_IN_BUF(buf, sign)
451 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200452 if (sign->se_id == markId && sign_in_group(sign, group))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100453 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200454 sign->se_typenr = typenr;
455 sign->se_priority = prio;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +0200456 sign_sort_by_prio_on_line(buf, sign);
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200457 return sign->se_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100458 }
459 }
460
461 return (linenr_T)0;
462}
463
464/*
Bram Moolenaar4e038572019-07-04 18:28:35 +0200465 * Return the attributes of the first sign placed on line 'lnum' in buffer
466 * 'buf'. Used when refreshing the screen. Returns TRUE if a sign is found on
467 * 'lnum', FALSE otherwise.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100468 */
469 int
Bram Moolenaar4e038572019-07-04 18:28:35 +0200470buf_get_signattrs(buf_T *buf, linenr_T lnum, sign_attrs_T *sattr)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100471{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200472 sign_entry_T *sign;
473 sign_T *sp;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200474
475 vim_memset(sattr, 0, sizeof(sign_attrs_T));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100476
477 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200478 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200479 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200480 // Signs are sorted by line number in the buffer. No need to check
481 // for signs after the specified line number 'lnum'.
482 break;
483
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200484 if (sign->se_lnum == lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200485 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200486 sattr->sat_typenr = sign->se_typenr;
487 sp = find_sign_by_typenr(sign->se_typenr);
Bram Moolenaar4e038572019-07-04 18:28:35 +0200488 if (sp == NULL)
489 return FALSE;
490
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100491# ifdef FEAT_SIGN_ICONS
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200492 sattr->sat_icon = sp->sn_image;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100493# endif
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200494 sattr->sat_text = sp->sn_text;
495 if (sattr->sat_text != NULL && sp->sn_text_hl > 0)
496 sattr->sat_texthl = syn_id2attr(sp->sn_text_hl);
Bram Moolenaar4e038572019-07-04 18:28:35 +0200497 if (sp->sn_line_hl > 0)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200498 sattr->sat_linehl = syn_id2attr(sp->sn_line_hl);
Bram Moolenaar4e038572019-07-04 18:28:35 +0200499 return TRUE;
500 }
501 }
502 return FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100503}
504
505/*
506 * Delete sign 'id' in group 'group' from buffer 'buf'.
507 * If 'id' is zero, then delete all the signs in group 'group'. Otherwise
508 * delete only the specified sign.
509 * If 'group' is '*', then delete the sign in all the groups. If 'group' is
510 * NULL, then delete the sign in the global group. Otherwise delete the sign in
511 * the specified group.
512 * Returns the line number of the deleted sign. If multiple signs are deleted,
513 * then returns the line number of the last sign deleted.
514 */
515 linenr_T
516buf_delsign(
517 buf_T *buf, // buffer sign is stored in
518 linenr_T atlnum, // sign at this line, 0 - at any line
519 int id, // sign id
520 char_u *group) // sign group
521{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200522 sign_entry_T **lastp; // pointer to pointer to current sign
523 sign_entry_T *sign; // a sign in a b_signlist
524 sign_entry_T *next; // the next sign in a b_signlist
525 linenr_T lnum; // line number whose sign was deleted
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100526
527 lastp = &buf->b_signlist;
528 lnum = 0;
529 for (sign = buf->b_signlist; sign != NULL; sign = next)
530 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200531 next = sign->se_next;
532 if ((id == 0 || sign->se_id == id)
533 && (atlnum == 0 || sign->se_lnum == atlnum)
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100534 && sign_in_group(sign, group))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100535
536 {
537 *lastp = next;
538 if (next != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200539 next->se_prev = sign->se_prev;
540 lnum = sign->se_lnum;
541 if (sign->se_group != NULL)
542 sign_group_unref(sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100543 vim_free(sign);
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100544 redraw_buf_line_later(buf, lnum);
545
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100546 // Check whether only one sign needs to be deleted
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100547 // If deleting a sign with a specific identifier in a particular
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100548 // group or deleting any sign at a particular line number, delete
549 // only one sign.
550 if (group == NULL
551 || (*group != '*' && id != 0)
552 || (*group == '*' && atlnum != 0))
553 break;
554 }
555 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200556 lastp = &sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100557 }
558
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100559 // When deleting the last sign the cursor position may change, because the
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100560 // sign columns no longer shows. And the 'signcolumn' may be hidden.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100561 if (buf->b_signlist == NULL)
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100562 {
563 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200564 changed_line_abv_curs();
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100565 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100566
567 return lnum;
568}
569
570
571/*
572 * Find the line number of the sign with the requested id in group 'group'. If
573 * the sign does not exist, return 0 as the line number. This will still let
574 * the correct file get loaded.
575 */
576 int
577buf_findsign(
578 buf_T *buf, // buffer to store sign in
579 int id, // sign ID
580 char_u *group) // sign group
581{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200582 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100583
584 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200585 if (sign->se_id == id && sign_in_group(sign, group))
586 return sign->se_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100587
588 return 0;
589}
590
591/*
592 * Return the sign at line 'lnum' in buffer 'buf'. Returns NULL if a sign is
593 * not found at the line. If 'groupname' is NULL, searches in the global group.
594 */
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200595 static sign_entry_T *
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100596buf_getsign_at_line(
597 buf_T *buf, // buffer whose sign we are searching for
598 linenr_T lnum, // line number of sign
599 char_u *groupname) // sign group name
600{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200601 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100602
603 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200604 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200605 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200606 // Signs are sorted by line number in the buffer. No need to check
607 // for signs after the specified line number 'lnum'.
608 break;
609
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200610 if (sign->se_lnum == lnum && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100611 return sign;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200612 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100613
614 return NULL;
615}
616
617/*
618 * Return the identifier of the sign at line number 'lnum' in buffer 'buf'.
619 */
620 int
621buf_findsign_id(
622 buf_T *buf, // buffer whose sign we are searching for
623 linenr_T lnum, // line number of sign
624 char_u *groupname) // sign group name
625{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200626 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100627
628 sign = buf_getsign_at_line(buf, lnum, groupname);
629 if (sign != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200630 return sign->se_id;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100631
632 return 0;
633}
634
635# if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
636/*
637 * See if a given type of sign exists on a specific line.
638 */
639 int
640buf_findsigntype_id(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100641 buf_T *buf, // buffer whose sign we are searching for
642 linenr_T lnum, // line number of sign
643 int typenr) // sign type number
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100644{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200645 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100646
647 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200648 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200649 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200650 // Signs are sorted by line number in the buffer. No need to check
651 // for signs after the specified line number 'lnum'.
652 break;
653
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200654 if (sign->se_lnum == lnum && sign->se_typenr == typenr)
655 return sign->se_id;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200656 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100657
658 return 0;
659}
660
661
662# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
663/*
664 * Return the number of icons on the given line.
665 */
666 int
667buf_signcount(buf_T *buf, linenr_T lnum)
668{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200669 sign_entry_T *sign; // a sign in the signlist
670 int count = 0;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100671
672 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200673 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200674 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200675 // Signs are sorted by line number in the buffer. No need to check
676 // for signs after the specified line number 'lnum'.
677 break;
678
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200679 if (sign->se_lnum == lnum)
680 if (sign_get_image(sign->se_typenr) != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100681 count++;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200682 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100683
684 return count;
685}
686# endif /* FEAT_SIGN_ICONS */
687# endif /* FEAT_NETBEANS_INTG */
688
689/*
690 * Delete signs in group 'group' in buffer "buf". If 'group' is '*', then
691 * delete all the signs.
692 */
693 void
694buf_delete_signs(buf_T *buf, char_u *group)
695{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200696 sign_entry_T *sign;
697 sign_entry_T **lastp; // pointer to pointer to current sign
698 sign_entry_T *next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100699
700 // When deleting the last sign need to redraw the windows to remove the
701 // sign column. Not when curwin is NULL (this means we're exiting).
702 if (buf->b_signlist != NULL && curwin != NULL)
703 {
704 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200705 changed_line_abv_curs();
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100706 }
707
708 lastp = &buf->b_signlist;
709 for (sign = buf->b_signlist; sign != NULL; sign = next)
710 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200711 next = sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100712 if (sign_in_group(sign, group))
713 {
714 *lastp = next;
715 if (next != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200716 next->se_prev = sign->se_prev;
717 if (sign->se_group != NULL)
718 sign_group_unref(sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100719 vim_free(sign);
720 }
721 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200722 lastp = &sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100723 }
724}
725
726/*
727 * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers.
728 */
729 static void
730sign_list_placed(buf_T *rbuf, char_u *sign_group)
731{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200732 buf_T *buf;
733 sign_entry_T *sign;
734 char lbuf[MSG_BUF_LEN];
735 char group[MSG_BUF_LEN];
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100736
Bram Moolenaar32526b32019-01-19 17:43:09 +0100737 msg_puts_title(_("\n--- Signs ---"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100738 msg_putchar('\n');
739 if (rbuf == NULL)
740 buf = firstbuf;
741 else
742 buf = rbuf;
743 while (buf != NULL && !got_int)
744 {
745 if (buf->b_signlist != NULL)
746 {
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100747 vim_snprintf(lbuf, MSG_BUF_LEN, _("Signs for %s:"), buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100748 msg_puts_attr(lbuf, HL_ATTR(HLF_D));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100749 msg_putchar('\n');
750 }
751 FOR_ALL_SIGNS_IN_BUF(buf, sign)
752 {
753 if (got_int)
754 break;
755 if (!sign_in_group(sign, sign_group))
756 continue;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200757 if (sign->se_group != NULL)
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100758 vim_snprintf(group, MSG_BUF_LEN, _(" group=%s"),
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200759 sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100760 else
761 group[0] = '\0';
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100762 vim_snprintf(lbuf, MSG_BUF_LEN,
763 _(" line=%ld id=%d%s name=%s priority=%d"),
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200764 (long)sign->se_lnum, sign->se_id, group,
765 sign_typenr2name(sign->se_typenr), sign->se_priority);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100766 msg_puts(lbuf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100767 msg_putchar('\n');
768 }
769 if (rbuf != NULL)
770 break;
771 buf = buf->b_next;
772 }
773}
774
775/*
776 * Adjust a placed sign for inserted/deleted lines.
777 */
778 void
779sign_mark_adjust(
780 linenr_T line1,
781 linenr_T line2,
782 long amount,
783 long amount_after)
784{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200785 sign_entry_T *sign; // a sign in a b_signlist
786 linenr_T new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100787
788 FOR_ALL_SIGNS_IN_BUF(curbuf, sign)
789 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100790 // Ignore changes to lines after the sign
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200791 if (sign->se_lnum < line1)
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100792 continue;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200793 new_lnum = sign->se_lnum;
794 if (sign->se_lnum >= line1 && sign->se_lnum <= line2)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100795 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100796 if (amount != MAXLNUM)
797 new_lnum += amount;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100798 }
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200799 else if (sign->se_lnum > line2)
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100800 // Lines inserted or deleted before the sign
801 new_lnum += amount_after;
802
803 // If the new sign line number is past the last line in the buffer,
804 // then don't adjust the line number. Otherwise, it will always be past
805 // the last line and will not be visible.
806 if (new_lnum <= curbuf->b_ml.ml_line_count)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200807 sign->se_lnum = new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100808 }
809}
810
811/*
812 * Find index of a ":sign" subcmd from its name.
813 * "*end_cmd" must be writable.
814 */
815 static int
816sign_cmd_idx(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100817 char_u *begin_cmd, // begin of sign subcmd
818 char_u *end_cmd) // just after sign subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100819{
820 int idx;
821 char save = *end_cmd;
822
823 *end_cmd = NUL;
824 for (idx = 0; ; ++idx)
825 if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0)
826 break;
827 *end_cmd = save;
828 return idx;
829}
830
831/*
832 * Find a sign by name. Also returns pointer to the previous sign.
833 */
834 static sign_T *
835sign_find(char_u *name, sign_T **sp_prev)
836{
837 sign_T *sp;
838
839 if (sp_prev != NULL)
840 *sp_prev = NULL;
841 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
842 {
843 if (STRCMP(sp->sn_name, name) == 0)
844 break;
845 if (sp_prev != NULL)
846 *sp_prev = sp;
847 }
848
849 return sp;
850}
851
852/*
Bram Moolenaar03142362019-01-18 22:01:42 +0100853 * Allocate a new sign
854 */
855 static sign_T *
856alloc_new_sign(char_u *name)
857{
858 sign_T *sp;
859 sign_T *lp;
860 int start = next_sign_typenr;
861
862 // Allocate a new sign.
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200863 sp = alloc_clear_id(sizeof(sign_T), aid_sign_define_by_name);
Bram Moolenaar03142362019-01-18 22:01:42 +0100864 if (sp == NULL)
865 return NULL;
866
867 // Check that next_sign_typenr is not already being used.
868 // This only happens after wrapping around. Hopefully
869 // another one got deleted and we can use its number.
870 for (lp = first_sign; lp != NULL; )
871 {
872 if (lp->sn_typenr == next_sign_typenr)
873 {
874 ++next_sign_typenr;
875 if (next_sign_typenr == MAX_TYPENR)
876 next_sign_typenr = 1;
877 if (next_sign_typenr == start)
878 {
879 vim_free(sp);
880 emsg(_("E612: Too many signs defined"));
881 return NULL;
882 }
883 lp = first_sign; // start all over
884 continue;
885 }
886 lp = lp->sn_next;
887 }
888
889 sp->sn_typenr = next_sign_typenr;
890 if (++next_sign_typenr == MAX_TYPENR)
891 next_sign_typenr = 1; // wrap around
892
893 sp->sn_name = vim_strsave(name);
894 if (sp->sn_name == NULL) // out of memory
895 {
896 vim_free(sp);
897 return NULL;
898 }
899
900 return sp;
901}
902
903/*
904 * Initialize the icon information for a new sign
905 */
906 static void
907sign_define_init_icon(sign_T *sp, char_u *icon)
908{
909 vim_free(sp->sn_icon);
910 sp->sn_icon = vim_strsave(icon);
911 backslash_halve(sp->sn_icon);
912# ifdef FEAT_SIGN_ICONS
913 if (gui.in_use)
914 {
915 out_flush();
916 if (sp->sn_image != NULL)
917 gui_mch_destroy_sign(sp->sn_image);
918 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
919 }
920# endif
921}
922
923/*
924 * Initialize the text for a new sign
925 */
926 static int
927sign_define_init_text(sign_T *sp, char_u *text)
928{
929 char_u *s;
930 char_u *endp;
931 int cells;
932 int len;
933
934 endp = text + (int)STRLEN(text);
935
936 // Remove backslashes so that it is possible to use a space.
937 for (s = text; s + 1 < endp; ++s)
938 if (*s == '\\')
939 {
940 STRMOVE(s, s + 1);
941 --endp;
942 }
943
944 // Count cells and check for non-printable chars
Bram Moolenaar03142362019-01-18 22:01:42 +0100945 if (has_mbyte)
946 {
947 cells = 0;
948 for (s = text; s < endp; s += (*mb_ptr2len)(s))
949 {
950 if (!vim_isprintc((*mb_ptr2char)(s)))
951 break;
952 cells += (*mb_ptr2cells)(s);
953 }
954 }
955 else
Bram Moolenaar03142362019-01-18 22:01:42 +0100956 {
957 for (s = text; s < endp; ++s)
958 if (!vim_isprintc(*s))
959 break;
960 cells = (int)(s - text);
961 }
962
963 // Currently sign text must be one or two display cells
964 if (s != endp || cells < 1 || cells > 2)
965 {
966 semsg(_("E239: Invalid sign text: %s"), text);
967 return FAIL;
968 }
969
970 vim_free(sp->sn_text);
971 // Allocate one byte more if we need to pad up
972 // with a space.
973 len = (int)(endp - text + ((cells == 1) ? 1 : 0));
974 sp->sn_text = vim_strnsave(text, len);
975
976 // For single character sign text, pad with a space.
977 if (sp->sn_text != NULL && cells == 1)
978 STRCPY(sp->sn_text + len - 1, " ");
979
980 return OK;
981}
982
983/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100984 * Define a new sign or update an existing sign
985 */
986 int
987sign_define_by_name(
988 char_u *name,
989 char_u *icon,
990 char_u *linehl,
991 char_u *text,
992 char_u *texthl)
993{
994 sign_T *sp_prev;
995 sign_T *sp;
996
997 sp = sign_find(name, &sp_prev);
998 if (sp == NULL)
999 {
Bram Moolenaar03142362019-01-18 22:01:42 +01001000 sp = alloc_new_sign(name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001001 if (sp == NULL)
1002 return FAIL;
1003
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001004 // add the new sign to the list of signs
1005 if (sp_prev == NULL)
1006 first_sign = sp;
1007 else
1008 sp_prev->sn_next = sp;
1009 }
1010
1011 // set values for a defined sign.
1012 if (icon != NULL)
Bram Moolenaar03142362019-01-18 22:01:42 +01001013 sign_define_init_icon(sp, icon);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001014
Bram Moolenaar03142362019-01-18 22:01:42 +01001015 if (text != NULL && (sign_define_init_text(sp, text) == FAIL))
1016 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001017
1018 if (linehl != NULL)
1019 sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl));
1020
1021 if (texthl != NULL)
1022 sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl));
1023
1024 return OK;
1025}
1026
1027/*
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001028 * Return TRUE if sign "name" exists.
1029 */
1030 int
1031sign_exists_by_name(char_u *name)
1032{
1033 return sign_find(name, NULL) != NULL;
1034}
1035
1036/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001037 * Free the sign specified by 'name'.
1038 */
1039 int
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001040sign_undefine_by_name(char_u *name, int give_error)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001041{
1042 sign_T *sp_prev;
1043 sign_T *sp;
1044
1045 sp = sign_find(name, &sp_prev);
1046 if (sp == NULL)
1047 {
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001048 if (give_error)
1049 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001050 return FAIL;
1051 }
1052 sign_undefine(sp, sp_prev);
1053
1054 return OK;
1055}
1056
1057/*
1058 * List the signs matching 'name'
1059 */
1060 static void
1061sign_list_by_name(char_u *name)
1062{
1063 sign_T *sp;
1064
1065 sp = sign_find(name, NULL);
1066 if (sp != NULL)
1067 sign_list_defined(sp);
1068 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001069 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001070}
1071
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001072 static void
1073may_force_numberwidth_recompute(buf_T *buf, int unplace)
1074{
1075 tabpage_T *tp;
1076 win_T *wp;
1077
1078 FOR_ALL_TAB_WINDOWS(tp, wp)
1079 if (wp->w_buffer == buf
1080 && (wp->w_p_nu || wp->w_p_rnu)
1081 && (unplace || wp->w_nrwidth_width < 2)
1082 && (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u'))
1083 wp->w_nrwidth_line_count = 0;
1084}
1085
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001086/*
Bram Moolenaar8144acb2019-01-14 23:08:18 +01001087 * Place a sign at the specified file location or update a sign.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001088 */
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001089 int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001090sign_place(
1091 int *sign_id,
1092 char_u *sign_group,
1093 char_u *sign_name,
1094 buf_T *buf,
1095 linenr_T lnum,
1096 int prio)
1097{
1098 sign_T *sp;
1099
1100 // Check for reserved character '*' in group name
1101 if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0'))
1102 return FAIL;
1103
1104 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1105 if (STRCMP(sp->sn_name, sign_name) == 0)
1106 break;
1107 if (sp == NULL)
1108 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001109 semsg(_("E155: Unknown sign: %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001110 return FAIL;
1111 }
1112 if (*sign_id == 0)
1113 *sign_id = sign_group_get_next_signid(buf, sign_group);
1114
1115 if (lnum > 0)
1116 // ":sign place {id} line={lnum} name={name} file={fname}":
1117 // place a sign
1118 buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr);
1119 else
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02001120 // ":sign place {id} file={fname}": change sign type and/or priority
1121 lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr,
1122 prio);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001123 if (lnum > 0)
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001124 {
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001125 redraw_buf_line_later(buf, lnum);
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001126
1127 // When displaying signs in the 'number' column, if the width of the
1128 // number column is less than 2, then force recomputing the width.
1129 may_force_numberwidth_recompute(buf, FALSE);
1130 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001131 else
1132 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001133 semsg(_("E885: Not possible to change sign %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001134 return FAIL;
1135 }
1136
1137 return OK;
1138}
1139
1140/*
1141 * Unplace the specified sign
1142 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001143 static int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001144sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum)
1145{
1146 if (buf->b_signlist == NULL) // No signs in the buffer
1147 return OK;
1148
1149 if (sign_id == 0)
1150 {
1151 // Delete all the signs in the specified buffer
1152 redraw_buf_later(buf, NOT_VALID);
1153 buf_delete_signs(buf, sign_group);
1154 }
1155 else
1156 {
1157 linenr_T lnum;
1158
1159 // Delete only the specified signs
1160 lnum = buf_delsign(buf, atlnum, sign_id, sign_group);
1161 if (lnum == 0)
1162 return FAIL;
1163 }
1164
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001165 // When all the signs in a buffer are removed, force recomputing the
1166 // number column width (if enabled) in all the windows displaying the
1167 // buffer if 'signcolumn' is set to 'number' in that window.
1168 if (buf->b_signlist == NULL)
1169 may_force_numberwidth_recompute(buf, TRUE);
1170
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001171 return OK;
1172}
1173
1174/*
1175 * Unplace the sign at the current cursor line.
1176 */
1177 static void
1178sign_unplace_at_cursor(char_u *groupname)
1179{
1180 int id = -1;
1181
1182 id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname);
1183 if (id > 0)
1184 sign_unplace(id, groupname, curwin->w_buffer, curwin->w_cursor.lnum);
1185 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001186 emsg(_("E159: Missing sign number"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001187}
1188
1189/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001190 * Jump to a sign.
1191 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001192 static linenr_T
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001193sign_jump(int sign_id, char_u *sign_group, buf_T *buf)
1194{
1195 linenr_T lnum;
1196
1197 if ((lnum = buf_findsign(buf, sign_id, sign_group)) <= 0)
1198 {
Bram Moolenaarb5443cc2019-01-15 20:19:40 +01001199 semsg(_("E157: Invalid sign ID: %d"), sign_id);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001200 return -1;
1201 }
1202
1203 // goto a sign ...
1204 if (buf_jump_open_win(buf) != NULL)
1205 { // ... in a current window
1206 curwin->w_cursor.lnum = lnum;
1207 check_cursor_lnum();
1208 beginline(BL_WHITE);
1209 }
1210 else
1211 { // ... not currently in a window
1212 char_u *cmd;
1213
1214 if (buf->b_fname == NULL)
1215 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001216 emsg(_("E934: Cannot jump to a buffer that does not have a name"));
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001217 return -1;
1218 }
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02001219 cmd = alloc(STRLEN(buf->b_fname) + 25);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001220 if (cmd == NULL)
1221 return -1;
1222 sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname);
1223 do_cmdline_cmd(cmd);
1224 vim_free(cmd);
1225 }
1226# ifdef FEAT_FOLDING
1227 foldOpenCursor();
1228# endif
1229
1230 return lnum;
1231}
1232
1233/*
1234 * ":sign define {name} ..." command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001235 */
1236 static void
1237sign_define_cmd(char_u *sign_name, char_u *cmdline)
1238{
1239 char_u *arg;
1240 char_u *p = cmdline;
1241 char_u *icon = NULL;
1242 char_u *text = NULL;
1243 char_u *linehl = NULL;
1244 char_u *texthl = NULL;
1245 int failed = FALSE;
1246
1247 // set values for a defined sign.
1248 for (;;)
1249 {
1250 arg = skipwhite(p);
1251 if (*arg == NUL)
1252 break;
1253 p = skiptowhite_esc(arg);
1254 if (STRNCMP(arg, "icon=", 5) == 0)
1255 {
1256 arg += 5;
1257 icon = vim_strnsave(arg, (int)(p - arg));
1258 }
1259 else if (STRNCMP(arg, "text=", 5) == 0)
1260 {
1261 arg += 5;
1262 text = vim_strnsave(arg, (int)(p - arg));
1263 }
1264 else if (STRNCMP(arg, "linehl=", 7) == 0)
1265 {
1266 arg += 7;
1267 linehl = vim_strnsave(arg, (int)(p - arg));
1268 }
1269 else if (STRNCMP(arg, "texthl=", 7) == 0)
1270 {
1271 arg += 7;
1272 texthl = vim_strnsave(arg, (int)(p - arg));
1273 }
1274 else
1275 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001276 semsg(_(e_invarg2), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001277 failed = TRUE;
1278 break;
1279 }
1280 }
1281
1282 if (!failed)
1283 sign_define_by_name(sign_name, icon, linehl, text, texthl);
1284
1285 vim_free(icon);
1286 vim_free(text);
1287 vim_free(linehl);
1288 vim_free(texthl);
1289}
1290
1291/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001292 * ":sign place" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001293 */
1294 static void
1295sign_place_cmd(
1296 buf_T *buf,
1297 linenr_T lnum,
1298 char_u *sign_name,
1299 int id,
1300 char_u *group,
1301 int prio)
1302{
1303 if (id <= 0)
1304 {
1305 // List signs placed in a file/buffer
1306 // :sign place file={fname}
1307 // :sign place group={group} file={fname}
1308 // :sign place group=* file={fname}
1309 // :sign place buffer={nr}
1310 // :sign place group={group} buffer={nr}
1311 // :sign place group=* buffer={nr}
1312 // :sign place
1313 // :sign place group={group}
1314 // :sign place group=*
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001315 if (lnum >= 0 || sign_name != NULL
1316 || (group != NULL && *group == '\0'))
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001317 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001318 else
1319 sign_list_placed(buf, group);
1320 }
1321 else
1322 {
1323 // Place a new sign
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001324 if (sign_name == NULL || buf == NULL
1325 || (group != NULL && *group == '\0'))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001326 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001327 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001328 return;
1329 }
1330
1331 sign_place(&id, group, sign_name, buf, lnum, prio);
1332 }
1333}
1334
1335/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001336 * ":sign unplace" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001337 */
1338 static void
1339sign_unplace_cmd(
1340 buf_T *buf,
1341 linenr_T lnum,
1342 char_u *sign_name,
1343 int id,
1344 char_u *group)
1345{
1346 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0'))
1347 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001348 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001349 return;
1350 }
1351
1352 if (id == -2)
1353 {
1354 if (buf != NULL)
1355 // :sign unplace * file={fname}
1356 // :sign unplace * group={group} file={fname}
1357 // :sign unplace * group=* file={fname}
1358 // :sign unplace * buffer={nr}
1359 // :sign unplace * group={group} buffer={nr}
1360 // :sign unplace * group=* buffer={nr}
1361 sign_unplace(0, group, buf, 0);
1362 else
1363 // :sign unplace *
1364 // :sign unplace * group={group}
1365 // :sign unplace * group=*
1366 FOR_ALL_BUFFERS(buf)
1367 if (buf->b_signlist != NULL)
1368 buf_delete_signs(buf, group);
1369 }
1370 else
1371 {
1372 if (buf != NULL)
1373 // :sign unplace {id} file={fname}
1374 // :sign unplace {id} group={group} file={fname}
1375 // :sign unplace {id} group=* file={fname}
1376 // :sign unplace {id} buffer={nr}
1377 // :sign unplace {id} group={group} buffer={nr}
1378 // :sign unplace {id} group=* buffer={nr}
1379 sign_unplace(id, group, buf, 0);
1380 else
1381 {
1382 if (id == -1)
1383 {
1384 // :sign unplace group={group}
1385 // :sign unplace group=*
1386 sign_unplace_at_cursor(group);
1387 }
1388 else
1389 {
1390 // :sign unplace {id}
1391 // :sign unplace {id} group={group}
1392 // :sign unplace {id} group=*
1393 FOR_ALL_BUFFERS(buf)
1394 sign_unplace(id, group, buf, 0);
1395 }
1396 }
1397 }
1398}
1399
1400/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001401 * Jump to a placed sign commands:
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001402 * :sign jump {id} file={fname}
1403 * :sign jump {id} buffer={nr}
1404 * :sign jump {id} group={group} file={fname}
1405 * :sign jump {id} group={group} buffer={nr}
1406 */
1407 static void
1408sign_jump_cmd(
1409 buf_T *buf,
1410 linenr_T lnum,
1411 char_u *sign_name,
1412 int id,
1413 char_u *group)
1414{
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001415 if (sign_name == NULL && group == NULL && id == -1)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001416 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001417 emsg(_(e_argreq));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001418 return;
1419 }
1420
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001421 if (buf == NULL || (group != NULL && *group == '\0')
1422 || lnum >= 0 || sign_name != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001423 {
1424 // File or buffer is not specified or an empty group is used
1425 // or a line number or a sign name is specified.
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001426 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001427 return;
1428 }
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001429 (void)sign_jump(id, group, buf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001430}
1431
1432/*
1433 * Parse the command line arguments for the ":sign place", ":sign unplace" and
1434 * ":sign jump" commands.
1435 * The supported arguments are: line={lnum} name={name} group={group}
1436 * priority={prio} and file={fname} or buffer={nr}.
1437 */
1438 static int
1439parse_sign_cmd_args(
1440 int cmd,
1441 char_u *arg,
1442 char_u **sign_name,
1443 int *signid,
1444 char_u **group,
1445 int *prio,
1446 buf_T **buf,
1447 linenr_T *lnum)
1448{
1449 char_u *arg1;
1450 char_u *name;
1451 char_u *filename = NULL;
Bram Moolenaarb589f952019-01-07 22:10:00 +01001452 int lnum_arg = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001453
1454 // first arg could be placed sign id
1455 arg1 = arg;
1456 if (VIM_ISDIGIT(*arg))
1457 {
1458 *signid = getdigits(&arg);
1459 if (!VIM_ISWHITE(*arg) && *arg != NUL)
1460 {
1461 *signid = -1;
1462 arg = arg1;
1463 }
1464 else
1465 arg = skipwhite(arg);
1466 }
1467
1468 while (*arg != NUL)
1469 {
1470 if (STRNCMP(arg, "line=", 5) == 0)
1471 {
1472 arg += 5;
1473 *lnum = atoi((char *)arg);
1474 arg = skiptowhite(arg);
Bram Moolenaarb589f952019-01-07 22:10:00 +01001475 lnum_arg = TRUE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001476 }
1477 else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE)
1478 {
1479 if (*signid != -1)
1480 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001481 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001482 return FAIL;
1483 }
1484 *signid = -2;
1485 arg = skiptowhite(arg + 1);
1486 }
1487 else if (STRNCMP(arg, "name=", 5) == 0)
1488 {
1489 arg += 5;
1490 name = arg;
1491 arg = skiptowhite(arg);
1492 if (*arg != NUL)
1493 *arg++ = NUL;
1494 while (name[0] == '0' && name[1] != NUL)
1495 ++name;
1496 *sign_name = name;
1497 }
1498 else if (STRNCMP(arg, "group=", 6) == 0)
1499 {
1500 arg += 6;
1501 *group = arg;
1502 arg = skiptowhite(arg);
1503 if (*arg != NUL)
1504 *arg++ = NUL;
1505 }
1506 else if (STRNCMP(arg, "priority=", 9) == 0)
1507 {
1508 arg += 9;
1509 *prio = atoi((char *)arg);
1510 arg = skiptowhite(arg);
1511 }
1512 else if (STRNCMP(arg, "file=", 5) == 0)
1513 {
1514 arg += 5;
1515 filename = arg;
1516 *buf = buflist_findname_exp(arg);
1517 break;
1518 }
1519 else if (STRNCMP(arg, "buffer=", 7) == 0)
1520 {
1521 arg += 7;
1522 filename = arg;
1523 *buf = buflist_findnr((int)getdigits(&arg));
1524 if (*skipwhite(arg) != NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001525 emsg(_(e_trailing));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001526 break;
1527 }
1528 else
1529 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001530 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001531 return FAIL;
1532 }
1533 arg = skipwhite(arg);
1534 }
1535
1536 if (filename != NULL && *buf == NULL)
1537 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001538 semsg(_("E158: Invalid buffer name: %s"), filename);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001539 return FAIL;
1540 }
1541
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001542 // If the filename is not supplied for the sign place or the sign jump
1543 // command, then use the current buffer.
Bram Moolenaarb589f952019-01-07 22:10:00 +01001544 if (filename == NULL && ((cmd == SIGNCMD_PLACE && lnum_arg)
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001545 || cmd == SIGNCMD_JUMP))
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001546 *buf = curwin->w_buffer;
1547
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001548 return OK;
1549}
1550
1551/*
1552 * ":sign" command
1553 */
1554 void
1555ex_sign(exarg_T *eap)
1556{
1557 char_u *arg = eap->arg;
1558 char_u *p;
1559 int idx;
1560 sign_T *sp;
1561 buf_T *buf = NULL;
1562
1563 // Parse the subcommand.
1564 p = skiptowhite(arg);
1565 idx = sign_cmd_idx(arg, p);
1566 if (idx == SIGNCMD_LAST)
1567 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001568 semsg(_("E160: Unknown sign command: %s"), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001569 return;
1570 }
1571 arg = skipwhite(p);
1572
1573 if (idx <= SIGNCMD_LIST)
1574 {
1575 // Define, undefine or list signs.
1576 if (idx == SIGNCMD_LIST && *arg == NUL)
1577 {
1578 // ":sign list": list all defined signs
1579 for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next)
1580 sign_list_defined(sp);
1581 }
1582 else if (*arg == NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001583 emsg(_("E156: Missing sign name"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001584 else
1585 {
1586 char_u *name;
1587
1588 // Isolate the sign name. If it's a number skip leading zeroes,
1589 // so that "099" and "99" are the same sign. But keep "0".
1590 p = skiptowhite(arg);
1591 if (*p != NUL)
1592 *p++ = NUL;
1593 while (arg[0] == '0' && arg[1] != NUL)
1594 ++arg;
1595 name = vim_strsave(arg);
1596
1597 if (idx == SIGNCMD_DEFINE)
1598 sign_define_cmd(name, p);
1599 else if (idx == SIGNCMD_LIST)
1600 // ":sign list {name}"
1601 sign_list_by_name(name);
1602 else
1603 // ":sign undefine {name}"
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001604 sign_undefine_by_name(name, TRUE);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001605
1606 vim_free(name);
1607 return;
1608 }
1609 }
1610 else
1611 {
1612 int id = -1;
1613 linenr_T lnum = -1;
1614 char_u *sign_name = NULL;
1615 char_u *group = NULL;
1616 int prio = SIGN_DEF_PRIO;
1617
1618 // Parse command line arguments
1619 if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio,
1620 &buf, &lnum) == FAIL)
1621 return;
1622
1623 if (idx == SIGNCMD_PLACE)
1624 sign_place_cmd(buf, lnum, sign_name, id, group, prio);
1625 else if (idx == SIGNCMD_UNPLACE)
1626 sign_unplace_cmd(buf, lnum, sign_name, id, group);
1627 else if (idx == SIGNCMD_JUMP)
1628 sign_jump_cmd(buf, lnum, sign_name, id, group);
1629 }
1630}
1631
1632/*
1633 * Return information about a specified sign
1634 */
1635 static void
1636sign_getinfo(sign_T *sp, dict_T *retdict)
1637{
1638 char_u *p;
1639
1640 dict_add_string(retdict, "name", (char_u *)sp->sn_name);
1641 if (sp->sn_icon != NULL)
1642 dict_add_string(retdict, "icon", (char_u *)sp->sn_icon);
1643 if (sp->sn_text != NULL)
1644 dict_add_string(retdict, "text", (char_u *)sp->sn_text);
1645 if (sp->sn_line_hl > 0)
1646 {
1647 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1648 if (p == NULL)
1649 p = (char_u *)"NONE";
1650 dict_add_string(retdict, "linehl", (char_u *)p);
1651 }
1652 if (sp->sn_text_hl > 0)
1653 {
1654 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1655 if (p == NULL)
1656 p = (char_u *)"NONE";
1657 dict_add_string(retdict, "texthl", (char_u *)p);
1658 }
1659}
1660
1661/*
1662 * If 'name' is NULL, return a list of all the defined signs.
1663 * Otherwise, return information about the specified sign.
1664 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001665 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001666sign_getlist(char_u *name, list_T *retlist)
1667{
1668 sign_T *sp = first_sign;
1669 dict_T *dict;
1670
1671 if (name != NULL)
1672 {
1673 sp = sign_find(name, NULL);
1674 if (sp == NULL)
1675 return;
1676 }
1677
1678 for (; sp != NULL && !got_int; sp = sp->sn_next)
1679 {
1680 if ((dict = dict_alloc_id(aid_sign_getlist)) == NULL)
1681 return;
1682 if (list_append_dict(retlist, dict) == FAIL)
1683 return;
1684 sign_getinfo(sp, dict);
1685
1686 if (name != NULL) // handle only the specified sign
1687 break;
1688 }
1689}
1690
1691/*
1692 * Returns information about signs placed in a buffer as list of dicts.
1693 */
1694 void
1695get_buffer_signs(buf_T *buf, list_T *l)
1696{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001697 sign_entry_T *sign;
1698 dict_T *d;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001699
1700 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1701 {
1702 if ((d = sign_get_info(sign)) != NULL)
1703 list_append_dict(l, d);
1704 }
1705}
1706
1707/*
1708 * Return information about all the signs placed in a buffer
1709 */
1710 static void
1711sign_get_placed_in_buf(
1712 buf_T *buf,
1713 linenr_T lnum,
1714 int sign_id,
1715 char_u *sign_group,
1716 list_T *retlist)
1717{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001718 dict_T *d;
1719 list_T *l;
1720 sign_entry_T *sign;
1721 dict_T *sdict;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001722
1723 if ((d = dict_alloc_id(aid_sign_getplaced_dict)) == NULL)
1724 return;
1725 list_append_dict(retlist, d);
1726
1727 dict_add_number(d, "bufnr", (long)buf->b_fnum);
1728
1729 if ((l = list_alloc_id(aid_sign_getplaced_list)) == NULL)
1730 return;
1731 dict_add_list(d, "signs", l);
1732
1733 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1734 {
1735 if (!sign_in_group(sign, sign_group))
1736 continue;
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001737 if ((lnum == 0 && sign_id == 0)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001738 || (sign_id == 0 && lnum == sign->se_lnum)
1739 || (lnum == 0 && sign_id == sign->se_id)
1740 || (lnum == sign->se_lnum && sign_id == sign->se_id))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001741 {
1742 if ((sdict = sign_get_info(sign)) != NULL)
1743 list_append_dict(l, sdict);
1744 }
1745 }
1746}
1747
1748/*
1749 * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the
1750 * sign placed at the line number. If 'lnum' is zero, return all the signs
1751 * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers.
1752 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001753 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001754sign_get_placed(
1755 buf_T *buf,
1756 linenr_T lnum,
1757 int sign_id,
1758 char_u *sign_group,
1759 list_T *retlist)
1760{
1761 if (buf != NULL)
1762 sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist);
1763 else
1764 {
1765 FOR_ALL_BUFFERS(buf)
1766 {
1767 if (buf->b_signlist != NULL)
1768 sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist);
1769 }
1770 }
1771}
1772
1773# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1774/*
1775 * Allocate the icons. Called when the GUI has started. Allows defining
1776 * signs before it starts.
1777 */
1778 void
1779sign_gui_started(void)
1780{
1781 sign_T *sp;
1782
1783 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1784 if (sp->sn_icon != NULL)
1785 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
1786}
1787# endif
1788
1789/*
1790 * List one sign.
1791 */
1792 static void
1793sign_list_defined(sign_T *sp)
1794{
1795 char_u *p;
1796
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001797 smsg("sign %s", sp->sn_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001798 if (sp->sn_icon != NULL)
1799 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001800 msg_puts(" icon=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001801 msg_outtrans(sp->sn_icon);
1802# ifdef FEAT_SIGN_ICONS
1803 if (sp->sn_image == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001804 msg_puts(_(" (NOT FOUND)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001805# else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001806 msg_puts(_(" (not supported)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001807# endif
1808 }
1809 if (sp->sn_text != NULL)
1810 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001811 msg_puts(" text=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001812 msg_outtrans(sp->sn_text);
1813 }
1814 if (sp->sn_line_hl > 0)
1815 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001816 msg_puts(" linehl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001817 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1818 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001819 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001820 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001821 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001822 }
1823 if (sp->sn_text_hl > 0)
1824 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001825 msg_puts(" texthl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001826 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1827 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001828 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001829 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001830 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001831 }
1832}
1833
1834/*
1835 * Undefine a sign and free its memory.
1836 */
1837 static void
1838sign_undefine(sign_T *sp, sign_T *sp_prev)
1839{
1840 vim_free(sp->sn_name);
1841 vim_free(sp->sn_icon);
1842# ifdef FEAT_SIGN_ICONS
1843 if (sp->sn_image != NULL)
1844 {
1845 out_flush();
1846 gui_mch_destroy_sign(sp->sn_image);
1847 }
1848# endif
1849 vim_free(sp->sn_text);
1850 if (sp_prev == NULL)
1851 first_sign = sp->sn_next;
1852 else
1853 sp_prev->sn_next = sp->sn_next;
1854 vim_free(sp);
1855}
1856
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001857# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1858 void *
1859sign_get_image(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001860 int typenr) // the attribute which may have a sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001861{
1862 sign_T *sp;
1863
1864 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1865 if (sp->sn_typenr == typenr)
1866 return sp->sn_image;
1867 return NULL;
1868}
1869# endif
1870
1871/*
1872 * Undefine/free all signs.
1873 */
1874 void
1875free_signs(void)
1876{
1877 while (first_sign != NULL)
1878 sign_undefine(first_sign, NULL);
1879}
1880
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001881static enum
1882{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001883 EXP_SUBCMD, // expand :sign sub-commands
1884 EXP_DEFINE, // expand :sign define {name} args
1885 EXP_PLACE, // expand :sign place {id} args
Bram Moolenaar3678f652019-02-17 14:50:25 +01001886 EXP_LIST, // expand :sign place args
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001887 EXP_UNPLACE, // expand :sign unplace"
Bram Moolenaar3678f652019-02-17 14:50:25 +01001888 EXP_SIGN_NAMES, // expand with name of placed signs
1889 EXP_SIGN_GROUPS // expand with name of placed sign groups
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001890} expand_what;
1891
1892/*
Bram Moolenaar3678f652019-02-17 14:50:25 +01001893 * Return the n'th sign name (used for command line completion)
1894 */
1895 static char_u *
1896get_nth_sign_name(int idx)
1897{
1898 int current_idx;
1899 sign_T *sp;
1900
1901 // Complete with name of signs already defined
1902 current_idx = 0;
1903 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1904 if (current_idx++ == idx)
1905 return sp->sn_name;
1906 return NULL;
1907}
1908
1909/*
1910 * Return the n'th sign group name (used for command line completion)
1911 */
1912 static char_u *
1913get_nth_sign_group_name(int idx)
1914{
1915 int current_idx;
1916 int todo;
1917 hashitem_T *hi;
1918 signgroup_T *group;
1919
1920 // Complete with name of sign groups already defined
1921 current_idx = 0;
1922 todo = (int)sg_table.ht_used;
1923 for (hi = sg_table.ht_array; todo > 0; ++hi)
1924 {
1925 if (!HASHITEM_EMPTY(hi))
1926 {
1927 --todo;
1928 if (current_idx++ == idx)
1929 {
1930 group = HI2SG(hi);
1931 return group->sg_name;
1932 }
1933 }
1934 }
1935 return NULL;
1936}
1937
1938/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001939 * Function given to ExpandGeneric() to obtain the sign command
1940 * expansion.
1941 */
1942 char_u *
1943get_sign_name(expand_T *xp UNUSED, int idx)
1944{
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001945 switch (expand_what)
1946 {
1947 case EXP_SUBCMD:
1948 return (char_u *)cmds[idx];
1949 case EXP_DEFINE:
1950 {
1951 char *define_arg[] =
1952 {
1953 "icon=", "linehl=", "text=", "texthl=", NULL
1954 };
1955 return (char_u *)define_arg[idx];
1956 }
1957 case EXP_PLACE:
1958 {
1959 char *place_arg[] =
1960 {
1961 "line=", "name=", "group=", "priority=", "file=",
1962 "buffer=", NULL
1963 };
1964 return (char_u *)place_arg[idx];
1965 }
Bram Moolenaar3678f652019-02-17 14:50:25 +01001966 case EXP_LIST:
1967 {
1968 char *list_arg[] =
1969 {
1970 "group=", "file=", "buffer=", NULL
1971 };
1972 return (char_u *)list_arg[idx];
1973 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001974 case EXP_UNPLACE:
1975 {
1976 char *unplace_arg[] = { "group=", "file=", "buffer=", NULL };
1977 return (char_u *)unplace_arg[idx];
1978 }
1979 case EXP_SIGN_NAMES:
Bram Moolenaar3678f652019-02-17 14:50:25 +01001980 return get_nth_sign_name(idx);
1981 case EXP_SIGN_GROUPS:
1982 return get_nth_sign_group_name(idx);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001983 default:
1984 return NULL;
1985 }
1986}
1987
1988/*
1989 * Handle command line completion for :sign command.
1990 */
1991 void
1992set_context_in_sign_cmd(expand_T *xp, char_u *arg)
1993{
1994 char_u *p;
1995 char_u *end_subcmd;
1996 char_u *last;
1997 int cmd_idx;
1998 char_u *begin_subcmd_args;
1999
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002000 // Default: expand subcommands.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002001 xp->xp_context = EXPAND_SIGN;
2002 expand_what = EXP_SUBCMD;
2003 xp->xp_pattern = arg;
2004
2005 end_subcmd = skiptowhite(arg);
2006 if (*end_subcmd == NUL)
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002007 // expand subcmd name
2008 // :sign {subcmd}<CTRL-D>
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002009 return;
2010
2011 cmd_idx = sign_cmd_idx(arg, end_subcmd);
2012
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002013 // :sign {subcmd} {subcmd_args}
2014 // |
2015 // begin_subcmd_args
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002016 begin_subcmd_args = skipwhite(end_subcmd);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002017
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002018 // expand last argument of subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002019
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002020 // :sign define {name} {args}...
2021 // |
2022 // p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002023
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002024 // Loop until reaching last argument.
Bram Moolenaar3678f652019-02-17 14:50:25 +01002025 p = begin_subcmd_args;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002026 do
2027 {
2028 p = skipwhite(p);
2029 last = p;
2030 p = skiptowhite(p);
2031 } while (*p != NUL);
2032
2033 p = vim_strchr(last, '=');
2034
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002035 // :sign define {name} {args}... {last}=
2036 // | |
2037 // last p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002038 if (p == NULL)
2039 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002040 // Expand last argument name (before equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002041 xp->xp_pattern = last;
2042 switch (cmd_idx)
2043 {
2044 case SIGNCMD_DEFINE:
2045 expand_what = EXP_DEFINE;
2046 break;
2047 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002048 // List placed signs
2049 if (VIM_ISDIGIT(*begin_subcmd_args))
2050 // :sign place {id} {args}...
2051 expand_what = EXP_PLACE;
2052 else
2053 // :sign place {args}...
2054 expand_what = EXP_LIST;
2055 break;
2056 case SIGNCMD_LIST:
2057 case SIGNCMD_UNDEFINE:
2058 // :sign list <CTRL-D>
2059 // :sign undefine <CTRL-D>
2060 expand_what = EXP_SIGN_NAMES;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002061 break;
2062 case SIGNCMD_JUMP:
2063 case SIGNCMD_UNPLACE:
2064 expand_what = EXP_UNPLACE;
2065 break;
2066 default:
2067 xp->xp_context = EXPAND_NOTHING;
2068 }
2069 }
2070 else
2071 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002072 // Expand last argument value (after equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002073 xp->xp_pattern = p + 1;
2074 switch (cmd_idx)
2075 {
2076 case SIGNCMD_DEFINE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002077 if (STRNCMP(last, "texthl", 6) == 0
2078 || STRNCMP(last, "linehl", 6) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002079 xp->xp_context = EXPAND_HIGHLIGHT;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002080 else if (STRNCMP(last, "icon", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002081 xp->xp_context = EXPAND_FILES;
2082 else
2083 xp->xp_context = EXPAND_NOTHING;
2084 break;
2085 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002086 if (STRNCMP(last, "name", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002087 expand_what = EXP_SIGN_NAMES;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002088 else if (STRNCMP(last, "group", 5) == 0)
2089 expand_what = EXP_SIGN_GROUPS;
2090 else if (STRNCMP(last, "file", 4) == 0)
2091 xp->xp_context = EXPAND_BUFFERS;
2092 else
2093 xp->xp_context = EXPAND_NOTHING;
2094 break;
2095 case SIGNCMD_UNPLACE:
2096 case SIGNCMD_JUMP:
2097 if (STRNCMP(last, "group", 5) == 0)
2098 expand_what = EXP_SIGN_GROUPS;
2099 else if (STRNCMP(last, "file", 4) == 0)
2100 xp->xp_context = EXPAND_BUFFERS;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002101 else
2102 xp->xp_context = EXPAND_NOTHING;
2103 break;
2104 default:
2105 xp->xp_context = EXPAND_NOTHING;
2106 }
2107 }
2108}
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002109
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002110/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002111 * Define a sign using the attributes in 'dict'. Returns 0 on success and -1 on
2112 * failure.
2113 */
2114 static int
2115sign_define_from_dict(char_u *name_arg, dict_T *dict)
2116{
2117 char_u *name = NULL;
2118 char_u *icon = NULL;
2119 char_u *linehl = NULL;
2120 char_u *text = NULL;
2121 char_u *texthl = NULL;
2122 int retval = -1;
2123
2124 if (name_arg == NULL)
2125 {
2126 if (dict == NULL)
2127 return -1;
2128 name = dict_get_string(dict, (char_u *)"name", TRUE);
2129 }
2130 else
2131 name = vim_strsave(name_arg);
2132 if (name == NULL || name[0] == NUL)
2133 goto cleanup;
2134 if (dict != NULL)
2135 {
2136 icon = dict_get_string(dict, (char_u *)"icon", TRUE);
2137 linehl = dict_get_string(dict, (char_u *)"linehl", TRUE);
2138 text = dict_get_string(dict, (char_u *)"text", TRUE);
2139 texthl = dict_get_string(dict, (char_u *)"texthl", TRUE);
2140 }
2141
2142 if (sign_define_by_name(name, icon, linehl, text, texthl) == OK)
2143 retval = 0;
2144
2145cleanup:
2146 vim_free(name);
2147 vim_free(icon);
2148 vim_free(linehl);
2149 vim_free(text);
2150 vim_free(texthl);
2151
2152 return retval;
2153}
2154
2155/*
2156 * Define multiple signs using attributes from list 'l' and store the return
2157 * values in 'retlist'.
2158 */
2159 static void
2160sign_define_multiple(list_T *l, list_T *retlist)
2161{
2162 listitem_T *li;
2163 int retval;
2164
2165 for (li = l->lv_first; li != NULL; li = li->li_next)
2166 {
2167 retval = -1;
2168 if (li->li_tv.v_type == VAR_DICT)
2169 retval = sign_define_from_dict(NULL, li->li_tv.vval.v_dict);
2170 else
2171 emsg(_(e_dictreq));
2172 list_append_number(retlist, retval);
2173 }
2174}
2175
2176/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002177 * "sign_define()" function
2178 */
2179 void
2180f_sign_define(typval_T *argvars, typval_T *rettv)
2181{
2182 char_u *name;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002183
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002184 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2185 {
2186 // Define multiple signs
2187 if (rettv_list_alloc(rettv) != OK)
2188 return;
2189
2190 sign_define_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2191 return;
2192 }
2193
2194 // Define a single sign
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002195 rettv->vval.v_number = -1;
2196
2197 name = tv_get_string_chk(&argvars[0]);
2198 if (name == NULL)
2199 return;
2200
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002201 if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_DICT)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002202 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002203 emsg(_(e_dictreq));
2204 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002205 }
2206
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002207 rettv->vval.v_number = sign_define_from_dict(name,
2208 argvars[1].v_type == VAR_DICT ? argvars[1].vval.v_dict : NULL);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002209}
2210
2211/*
2212 * "sign_getdefined()" function
2213 */
2214 void
2215f_sign_getdefined(typval_T *argvars, typval_T *rettv)
2216{
2217 char_u *name = NULL;
2218
2219 if (rettv_list_alloc_id(rettv, aid_sign_getdefined) != OK)
2220 return;
2221
2222 if (argvars[0].v_type != VAR_UNKNOWN)
2223 name = tv_get_string(&argvars[0]);
2224
2225 sign_getlist(name, rettv->vval.v_list);
2226}
2227
2228/*
2229 * "sign_getplaced()" function
2230 */
2231 void
2232f_sign_getplaced(typval_T *argvars, typval_T *rettv)
2233{
2234 buf_T *buf = NULL;
2235 dict_T *dict;
2236 dictitem_T *di;
2237 linenr_T lnum = 0;
2238 int sign_id = 0;
2239 char_u *group = NULL;
2240 int notanum = FALSE;
2241
2242 if (rettv_list_alloc_id(rettv, aid_sign_getplaced) != OK)
2243 return;
2244
2245 if (argvars[0].v_type != VAR_UNKNOWN)
2246 {
2247 // get signs placed in the specified buffer
2248 buf = get_buf_arg(&argvars[0]);
2249 if (buf == NULL)
2250 return;
2251
2252 if (argvars[1].v_type != VAR_UNKNOWN)
2253 {
2254 if (argvars[1].v_type != VAR_DICT ||
2255 ((dict = argvars[1].vval.v_dict) == NULL))
2256 {
2257 emsg(_(e_dictreq));
2258 return;
2259 }
2260 if ((di = dict_find(dict, (char_u *)"lnum", -1)) != NULL)
2261 {
2262 // get signs placed at this line
2263 (void)tv_get_number_chk(&di->di_tv, &notanum);
2264 if (notanum)
2265 return;
2266 lnum = tv_get_lnum(&di->di_tv);
2267 }
2268 if ((di = dict_find(dict, (char_u *)"id", -1)) != NULL)
2269 {
2270 // get sign placed with this identifier
2271 sign_id = (int)tv_get_number_chk(&di->di_tv, &notanum);
2272 if (notanum)
2273 return;
2274 }
2275 if ((di = dict_find(dict, (char_u *)"group", -1)) != NULL)
2276 {
2277 group = tv_get_string_chk(&di->di_tv);
2278 if (group == NULL)
2279 return;
2280 if (*group == '\0') // empty string means global group
2281 group = NULL;
2282 }
2283 }
2284 }
2285
2286 sign_get_placed(buf, lnum, sign_id, group, rettv->vval.v_list);
2287}
2288
2289/*
2290 * "sign_jump()" function
2291 */
2292 void
2293f_sign_jump(typval_T *argvars, typval_T *rettv)
2294{
2295 int sign_id;
2296 char_u *sign_group = NULL;
2297 buf_T *buf;
2298 int notanum = FALSE;
2299
2300 rettv->vval.v_number = -1;
2301
2302 // Sign identifier
2303 sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
2304 if (notanum)
2305 return;
2306 if (sign_id <= 0)
2307 {
2308 emsg(_(e_invarg));
2309 return;
2310 }
2311
2312 // Sign group
2313 sign_group = tv_get_string_chk(&argvars[1]);
2314 if (sign_group == NULL)
2315 return;
2316 if (sign_group[0] == '\0')
2317 sign_group = NULL; // global sign group
2318 else
2319 {
2320 sign_group = vim_strsave(sign_group);
2321 if (sign_group == NULL)
2322 return;
2323 }
2324
2325 // Buffer to place the sign
2326 buf = get_buf_arg(&argvars[2]);
2327 if (buf == NULL)
2328 goto cleanup;
2329
2330 rettv->vval.v_number = sign_jump(sign_id, sign_group, buf);
2331
2332cleanup:
2333 vim_free(sign_group);
2334}
2335
2336/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002337 * Place a new sign using the values specified in dict 'dict'. Returns the sign
2338 * identifier if successfully placed, otherwise returns 0.
2339 */
2340 static int
2341sign_place_from_dict(
2342 typval_T *id_tv,
2343 typval_T *group_tv,
2344 typval_T *name_tv,
2345 typval_T *buf_tv,
2346 dict_T *dict)
2347{
2348 int sign_id = 0;
2349 char_u *group = NULL;
2350 char_u *sign_name = NULL;
2351 buf_T *buf = NULL;
2352 dictitem_T *di;
2353 linenr_T lnum = 0;
2354 int prio = SIGN_DEF_PRIO;
2355 int notanum = FALSE;
2356 int ret_sign_id = -1;
2357
2358 // sign identifier
2359 if (id_tv == NULL)
2360 {
2361 di = dict_find(dict, (char_u *)"id", -1);
2362 if (di != NULL)
2363 id_tv = &di->di_tv;
2364 }
2365 if (id_tv == NULL)
2366 sign_id = 0;
2367 else
2368 {
2369 sign_id = tv_get_number_chk(id_tv, &notanum);
2370 if (notanum)
2371 return -1;
2372 if (sign_id < 0)
2373 {
2374 emsg(_(e_invarg));
2375 return -1;
2376 }
2377 }
2378
2379 // sign group
2380 if (group_tv == NULL)
2381 {
2382 di = dict_find(dict, (char_u *)"group", -1);
2383 if (di != NULL)
2384 group_tv = &di->di_tv;
2385 }
2386 if (group_tv == NULL)
2387 group = NULL; // global group
2388 else
2389 {
2390 group = tv_get_string_chk(group_tv);
2391 if (group == NULL)
2392 goto cleanup;
2393 if (group[0] == '\0') // global sign group
2394 group = NULL;
2395 else
2396 {
2397 group = vim_strsave(group);
2398 if (group == NULL)
2399 return -1;
2400 }
2401 }
2402
2403 // sign name
2404 if (name_tv == NULL)
2405 {
2406 di = dict_find(dict, (char_u *)"name", -1);
2407 if (di != NULL)
2408 name_tv = &di->di_tv;
2409 }
2410 if (name_tv == NULL)
2411 goto cleanup;
2412 sign_name = tv_get_string_chk(name_tv);
2413 if (sign_name == NULL)
2414 goto cleanup;
2415
2416 // buffer to place the sign
2417 if (buf_tv == NULL)
2418 {
2419 di = dict_find(dict, (char_u *)"buffer", -1);
2420 if (di != NULL)
2421 buf_tv = &di->di_tv;
2422 }
2423 if (buf_tv == NULL)
2424 goto cleanup;
2425 buf = get_buf_arg(buf_tv);
2426 if (buf == NULL)
2427 goto cleanup;
2428
2429 // line number of the sign
2430 di = dict_find(dict, (char_u *)"lnum", -1);
2431 if (di != NULL)
2432 {
Bram Moolenaar42aff462019-08-21 13:20:29 +02002433 lnum = tv_get_lnum(&di->di_tv);
2434 if (lnum <= 0)
2435 {
2436 emsg(_(e_invarg));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002437 goto cleanup;
Bram Moolenaar42aff462019-08-21 13:20:29 +02002438 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002439 }
2440
2441 // sign priority
2442 di = dict_find(dict, (char_u *)"priority", -1);
2443 if (di != NULL)
2444 {
2445 prio = (int)tv_get_number_chk(&di->di_tv, &notanum);
2446 if (notanum)
2447 goto cleanup;
2448 }
2449
2450 if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK)
2451 ret_sign_id = sign_id;
2452
2453cleanup:
2454 vim_free(group);
2455
2456 return ret_sign_id;
2457}
2458
2459/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002460 * "sign_place()" function
2461 */
2462 void
2463f_sign_place(typval_T *argvars, typval_T *rettv)
2464{
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002465 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002466
2467 rettv->vval.v_number = -1;
2468
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002469 if (argvars[4].v_type != VAR_UNKNOWN
2470 && (argvars[4].v_type != VAR_DICT
2471 || ((dict = argvars[4].vval.v_dict) == NULL)))
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002472 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002473 emsg(_(e_dictreq));
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002474 return;
2475 }
2476
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002477 rettv->vval.v_number = sign_place_from_dict(&argvars[0], &argvars[1],
2478 &argvars[2], &argvars[3], dict);
2479}
2480
2481/*
2482 * "sign_placelist()" function. Place multiple signs.
2483 */
2484 void
2485f_sign_placelist(typval_T *argvars, typval_T *rettv)
2486{
2487 listitem_T *li;
2488 int sign_id;
2489
2490 if (rettv_list_alloc(rettv) != OK)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002491 return;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002492
2493 if (argvars[0].v_type != VAR_LIST)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002494 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002495 emsg(_(e_listreq));
2496 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002497 }
2498
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002499 // Process the List of sign attributes
2500 for (li = argvars[0].vval.v_list->lv_first; li != NULL; li = li->li_next)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002501 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002502 sign_id = -1;
2503 if (li->li_tv.v_type == VAR_DICT)
2504 sign_id = sign_place_from_dict(NULL, NULL, NULL, NULL,
2505 li->li_tv.vval.v_dict);
2506 else
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002507 emsg(_(e_dictreq));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002508 list_append_number(rettv->vval.v_list, sign_id);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002509 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002510}
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002511
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002512/*
2513 * Undefine multiple signs
2514 */
2515 static void
2516sign_undefine_multiple(list_T *l, list_T *retlist)
2517{
2518 char_u *name;
2519 listitem_T *li;
2520 int retval;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002521
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002522 for (li = l->lv_first; li != NULL; li = li->li_next)
2523 {
2524 retval = -1;
2525 name = tv_get_string_chk(&li->li_tv);
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002526 if (name != NULL && (sign_undefine_by_name(name, TRUE) == OK))
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002527 retval = 0;
2528 list_append_number(retlist, retval);
2529 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002530}
2531
2532/*
2533 * "sign_undefine()" function
2534 */
2535 void
2536f_sign_undefine(typval_T *argvars, typval_T *rettv)
2537{
2538 char_u *name;
2539
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002540 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2541 {
2542 // Undefine multiple signs
2543 if (rettv_list_alloc(rettv) != OK)
2544 return;
2545
2546 sign_undefine_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2547 return;
2548 }
2549
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002550 rettv->vval.v_number = -1;
2551
2552 if (argvars[0].v_type == VAR_UNKNOWN)
2553 {
2554 // Free all the signs
2555 free_signs();
2556 rettv->vval.v_number = 0;
2557 }
2558 else
2559 {
2560 // Free only the specified sign
2561 name = tv_get_string_chk(&argvars[0]);
2562 if (name == NULL)
2563 return;
2564
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002565 if (sign_undefine_by_name(name, TRUE) == OK)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002566 rettv->vval.v_number = 0;
2567 }
2568}
2569
2570/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002571 * Unplace the sign with attributes specified in 'dict'. Returns 0 on success
2572 * and -1 on failure.
2573 */
2574 static int
2575sign_unplace_from_dict(typval_T *group_tv, dict_T *dict)
2576{
2577 dictitem_T *di;
2578 int sign_id = 0;
2579 buf_T *buf = NULL;
2580 char_u *group = NULL;
2581 int retval = -1;
2582
2583 // sign group
2584 if (group_tv != NULL)
2585 group = tv_get_string(group_tv);
2586 else
2587 group = dict_get_string(dict, (char_u *)"group", FALSE);
2588 if (group != NULL)
2589 {
2590 if (group[0] == '\0') // global sign group
2591 group = NULL;
2592 else
2593 {
2594 group = vim_strsave(group);
2595 if (group == NULL)
2596 return -1;
2597 }
2598 }
2599
2600 if (dict != NULL)
2601 {
2602 if ((di = dict_find(dict, (char_u *)"buffer", -1)) != NULL)
2603 {
2604 buf = get_buf_arg(&di->di_tv);
2605 if (buf == NULL)
2606 goto cleanup;
2607 }
2608 if (dict_find(dict, (char_u *)"id", -1) != NULL)
2609 {
2610 sign_id = dict_get_number(dict, (char_u *)"id");
2611 if (sign_id <= 0)
2612 {
2613 emsg(_(e_invarg));
2614 goto cleanup;
2615 }
2616 }
2617 }
2618
2619 if (buf == NULL)
2620 {
2621 // Delete the sign in all the buffers
2622 retval = 0;
2623 FOR_ALL_BUFFERS(buf)
2624 if (sign_unplace(sign_id, group, buf, 0) != OK)
2625 retval = -1;
2626 }
2627 else if (sign_unplace(sign_id, group, buf, 0) == OK)
2628 retval = 0;
2629
2630cleanup:
2631 vim_free(group);
2632
2633 return retval;
2634}
2635
2636/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002637 * "sign_unplace()" function
2638 */
2639 void
2640f_sign_unplace(typval_T *argvars, typval_T *rettv)
2641{
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002642 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002643
2644 rettv->vval.v_number = -1;
2645
2646 if (argvars[0].v_type != VAR_STRING)
2647 {
2648 emsg(_(e_invarg));
2649 return;
2650 }
2651
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002652 if (argvars[1].v_type != VAR_UNKNOWN)
2653 {
2654 if (argvars[1].v_type != VAR_DICT)
2655 {
2656 emsg(_(e_dictreq));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002657 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002658 }
2659 dict = argvars[1].vval.v_dict;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002660 }
2661
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002662 rettv->vval.v_number = sign_unplace_from_dict(&argvars[0], dict);
2663}
2664
2665/*
2666 * "sign_unplacelist()" function
2667 */
2668 void
2669f_sign_unplacelist(typval_T *argvars, typval_T *rettv)
2670{
2671 listitem_T *li;
2672 int retval;
2673
2674 if (rettv_list_alloc(rettv) != OK)
2675 return;
2676
2677 if (argvars[0].v_type != VAR_LIST)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002678 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002679 emsg(_(e_listreq));
2680 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002681 }
2682
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002683 for (li = argvars[0].vval.v_list->lv_first; li != NULL; li = li->li_next)
2684 {
2685 retval = -1;
2686 if (li->li_tv.v_type == VAR_DICT)
2687 retval = sign_unplace_from_dict(NULL, li->li_tv.vval.v_dict);
2688 else
2689 emsg(_(e_dictreq));
2690 list_append_number(rettv->vval.v_list, retval);
2691 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002692}
2693
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002694#endif /* FEAT_SIGNS */