blob: 134edf09f0b7a76cc32cc0f24db226cb455abce7 [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 Moolenaar4eb7dae2019-11-12 22:33:45 +0100470buf_get_signattrs(win_T *wp, 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 Moolenaar4eb7dae2019-11-12 22:33:45 +0100474 buf_T *buf = wp->w_buffer;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200475
476 vim_memset(sattr, 0, sizeof(sign_attrs_T));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100477
478 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200479 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200480 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200481 // Signs are sorted by line number in the buffer. No need to check
482 // for signs after the specified line number 'lnum'.
483 break;
484
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100485 if (sign->se_lnum == lnum
486# ifdef FEAT_TEXT_PROP
487 && sign_in_group(sign, (char_u *)"popupmenu")
488 == (WIN_IS_POPUP(wp) ? TRUE : FALSE)
489# endif
490 )
Bram Moolenaar4e038572019-07-04 18:28:35 +0200491 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200492 sattr->sat_typenr = sign->se_typenr;
493 sp = find_sign_by_typenr(sign->se_typenr);
Bram Moolenaar4e038572019-07-04 18:28:35 +0200494 if (sp == NULL)
495 return FALSE;
496
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100497# ifdef FEAT_SIGN_ICONS
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200498 sattr->sat_icon = sp->sn_image;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100499# endif
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200500 sattr->sat_text = sp->sn_text;
501 if (sattr->sat_text != NULL && sp->sn_text_hl > 0)
502 sattr->sat_texthl = syn_id2attr(sp->sn_text_hl);
Bram Moolenaar4e038572019-07-04 18:28:35 +0200503 if (sp->sn_line_hl > 0)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200504 sattr->sat_linehl = syn_id2attr(sp->sn_line_hl);
Bram Moolenaar4e038572019-07-04 18:28:35 +0200505 return TRUE;
506 }
507 }
508 return FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100509}
510
511/*
512 * Delete sign 'id' in group 'group' from buffer 'buf'.
513 * If 'id' is zero, then delete all the signs in group 'group'. Otherwise
514 * delete only the specified sign.
515 * If 'group' is '*', then delete the sign in all the groups. If 'group' is
516 * NULL, then delete the sign in the global group. Otherwise delete the sign in
517 * the specified group.
518 * Returns the line number of the deleted sign. If multiple signs are deleted,
519 * then returns the line number of the last sign deleted.
520 */
521 linenr_T
522buf_delsign(
523 buf_T *buf, // buffer sign is stored in
524 linenr_T atlnum, // sign at this line, 0 - at any line
525 int id, // sign id
526 char_u *group) // sign group
527{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200528 sign_entry_T **lastp; // pointer to pointer to current sign
529 sign_entry_T *sign; // a sign in a b_signlist
530 sign_entry_T *next; // the next sign in a b_signlist
531 linenr_T lnum; // line number whose sign was deleted
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100532
533 lastp = &buf->b_signlist;
534 lnum = 0;
535 for (sign = buf->b_signlist; sign != NULL; sign = next)
536 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200537 next = sign->se_next;
538 if ((id == 0 || sign->se_id == id)
539 && (atlnum == 0 || sign->se_lnum == atlnum)
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100540 && sign_in_group(sign, group))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100541
542 {
543 *lastp = next;
544 if (next != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200545 next->se_prev = sign->se_prev;
546 lnum = sign->se_lnum;
547 if (sign->se_group != NULL)
548 sign_group_unref(sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100549 vim_free(sign);
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100550 redraw_buf_line_later(buf, lnum);
551
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100552 // Check whether only one sign needs to be deleted
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100553 // If deleting a sign with a specific identifier in a particular
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100554 // group or deleting any sign at a particular line number, delete
555 // only one sign.
556 if (group == NULL
557 || (*group != '*' && id != 0)
558 || (*group == '*' && atlnum != 0))
559 break;
560 }
561 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200562 lastp = &sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100563 }
564
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100565 // When deleting the last sign the cursor position may change, because the
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100566 // sign columns no longer shows. And the 'signcolumn' may be hidden.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100567 if (buf->b_signlist == NULL)
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100568 {
569 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200570 changed_line_abv_curs();
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100571 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100572
573 return lnum;
574}
575
576
577/*
578 * Find the line number of the sign with the requested id in group 'group'. If
579 * the sign does not exist, return 0 as the line number. This will still let
580 * the correct file get loaded.
581 */
582 int
583buf_findsign(
584 buf_T *buf, // buffer to store sign in
585 int id, // sign ID
586 char_u *group) // sign group
587{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200588 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100589
590 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200591 if (sign->se_id == id && sign_in_group(sign, group))
592 return sign->se_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100593
594 return 0;
595}
596
597/*
598 * Return the sign at line 'lnum' in buffer 'buf'. Returns NULL if a sign is
599 * not found at the line. If 'groupname' is NULL, searches in the global group.
600 */
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200601 static sign_entry_T *
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100602buf_getsign_at_line(
603 buf_T *buf, // buffer whose sign we are searching for
604 linenr_T lnum, // line number of sign
605 char_u *groupname) // sign group name
606{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200607 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100608
609 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200610 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200611 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200612 // Signs are sorted by line number in the buffer. No need to check
613 // for signs after the specified line number 'lnum'.
614 break;
615
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200616 if (sign->se_lnum == lnum && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100617 return sign;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200618 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100619
620 return NULL;
621}
622
623/*
624 * Return the identifier of the sign at line number 'lnum' in buffer 'buf'.
625 */
626 int
627buf_findsign_id(
628 buf_T *buf, // buffer whose sign we are searching for
629 linenr_T lnum, // line number of sign
630 char_u *groupname) // sign group name
631{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200632 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100633
634 sign = buf_getsign_at_line(buf, lnum, groupname);
635 if (sign != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200636 return sign->se_id;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100637
638 return 0;
639}
640
641# if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
642/*
643 * See if a given type of sign exists on a specific line.
644 */
645 int
646buf_findsigntype_id(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100647 buf_T *buf, // buffer whose sign we are searching for
648 linenr_T lnum, // line number of sign
649 int typenr) // sign type number
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100650{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200651 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100652
653 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200654 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200655 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200656 // Signs are sorted by line number in the buffer. No need to check
657 // for signs after the specified line number 'lnum'.
658 break;
659
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200660 if (sign->se_lnum == lnum && sign->se_typenr == typenr)
661 return sign->se_id;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200662 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100663
664 return 0;
665}
666
667
668# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
669/*
670 * Return the number of icons on the given line.
671 */
672 int
673buf_signcount(buf_T *buf, linenr_T lnum)
674{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200675 sign_entry_T *sign; // a sign in the signlist
676 int count = 0;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100677
678 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200679 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200680 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200681 // Signs are sorted by line number in the buffer. No need to check
682 // for signs after the specified line number 'lnum'.
683 break;
684
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200685 if (sign->se_lnum == lnum)
686 if (sign_get_image(sign->se_typenr) != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100687 count++;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200688 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100689
690 return count;
691}
692# endif /* FEAT_SIGN_ICONS */
693# endif /* FEAT_NETBEANS_INTG */
694
695/*
696 * Delete signs in group 'group' in buffer "buf". If 'group' is '*', then
697 * delete all the signs.
698 */
699 void
700buf_delete_signs(buf_T *buf, char_u *group)
701{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200702 sign_entry_T *sign;
703 sign_entry_T **lastp; // pointer to pointer to current sign
704 sign_entry_T *next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100705
706 // When deleting the last sign need to redraw the windows to remove the
707 // sign column. Not when curwin is NULL (this means we're exiting).
708 if (buf->b_signlist != NULL && curwin != NULL)
709 {
710 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200711 changed_line_abv_curs();
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100712 }
713
714 lastp = &buf->b_signlist;
715 for (sign = buf->b_signlist; sign != NULL; sign = next)
716 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200717 next = sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100718 if (sign_in_group(sign, group))
719 {
720 *lastp = next;
721 if (next != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200722 next->se_prev = sign->se_prev;
723 if (sign->se_group != NULL)
724 sign_group_unref(sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100725 vim_free(sign);
726 }
727 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200728 lastp = &sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100729 }
730}
731
732/*
733 * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers.
734 */
735 static void
736sign_list_placed(buf_T *rbuf, char_u *sign_group)
737{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200738 buf_T *buf;
739 sign_entry_T *sign;
740 char lbuf[MSG_BUF_LEN];
741 char group[MSG_BUF_LEN];
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100742
Bram Moolenaar32526b32019-01-19 17:43:09 +0100743 msg_puts_title(_("\n--- Signs ---"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100744 msg_putchar('\n');
745 if (rbuf == NULL)
746 buf = firstbuf;
747 else
748 buf = rbuf;
749 while (buf != NULL && !got_int)
750 {
751 if (buf->b_signlist != NULL)
752 {
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100753 vim_snprintf(lbuf, MSG_BUF_LEN, _("Signs for %s:"), buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100754 msg_puts_attr(lbuf, HL_ATTR(HLF_D));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100755 msg_putchar('\n');
756 }
757 FOR_ALL_SIGNS_IN_BUF(buf, sign)
758 {
759 if (got_int)
760 break;
761 if (!sign_in_group(sign, sign_group))
762 continue;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200763 if (sign->se_group != NULL)
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100764 vim_snprintf(group, MSG_BUF_LEN, _(" group=%s"),
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200765 sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100766 else
767 group[0] = '\0';
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100768 vim_snprintf(lbuf, MSG_BUF_LEN,
769 _(" line=%ld id=%d%s name=%s priority=%d"),
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200770 (long)sign->se_lnum, sign->se_id, group,
771 sign_typenr2name(sign->se_typenr), sign->se_priority);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100772 msg_puts(lbuf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100773 msg_putchar('\n');
774 }
775 if (rbuf != NULL)
776 break;
777 buf = buf->b_next;
778 }
779}
780
781/*
782 * Adjust a placed sign for inserted/deleted lines.
783 */
784 void
785sign_mark_adjust(
786 linenr_T line1,
787 linenr_T line2,
788 long amount,
789 long amount_after)
790{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200791 sign_entry_T *sign; // a sign in a b_signlist
792 linenr_T new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100793
794 FOR_ALL_SIGNS_IN_BUF(curbuf, sign)
795 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100796 // Ignore changes to lines after the sign
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200797 if (sign->se_lnum < line1)
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100798 continue;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200799 new_lnum = sign->se_lnum;
800 if (sign->se_lnum >= line1 && sign->se_lnum <= line2)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100801 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100802 if (amount != MAXLNUM)
803 new_lnum += amount;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100804 }
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200805 else if (sign->se_lnum > line2)
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100806 // Lines inserted or deleted before the sign
807 new_lnum += amount_after;
808
809 // If the new sign line number is past the last line in the buffer,
810 // then don't adjust the line number. Otherwise, it will always be past
811 // the last line and will not be visible.
812 if (new_lnum <= curbuf->b_ml.ml_line_count)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200813 sign->se_lnum = new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100814 }
815}
816
817/*
818 * Find index of a ":sign" subcmd from its name.
819 * "*end_cmd" must be writable.
820 */
821 static int
822sign_cmd_idx(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100823 char_u *begin_cmd, // begin of sign subcmd
824 char_u *end_cmd) // just after sign subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100825{
826 int idx;
827 char save = *end_cmd;
828
829 *end_cmd = NUL;
830 for (idx = 0; ; ++idx)
831 if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0)
832 break;
833 *end_cmd = save;
834 return idx;
835}
836
837/*
838 * Find a sign by name. Also returns pointer to the previous sign.
839 */
840 static sign_T *
841sign_find(char_u *name, sign_T **sp_prev)
842{
843 sign_T *sp;
844
845 if (sp_prev != NULL)
846 *sp_prev = NULL;
847 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
848 {
849 if (STRCMP(sp->sn_name, name) == 0)
850 break;
851 if (sp_prev != NULL)
852 *sp_prev = sp;
853 }
854
855 return sp;
856}
857
858/*
Bram Moolenaar03142362019-01-18 22:01:42 +0100859 * Allocate a new sign
860 */
861 static sign_T *
862alloc_new_sign(char_u *name)
863{
864 sign_T *sp;
865 sign_T *lp;
866 int start = next_sign_typenr;
867
868 // Allocate a new sign.
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200869 sp = alloc_clear_id(sizeof(sign_T), aid_sign_define_by_name);
Bram Moolenaar03142362019-01-18 22:01:42 +0100870 if (sp == NULL)
871 return NULL;
872
873 // Check that next_sign_typenr is not already being used.
874 // This only happens after wrapping around. Hopefully
875 // another one got deleted and we can use its number.
876 for (lp = first_sign; lp != NULL; )
877 {
878 if (lp->sn_typenr == next_sign_typenr)
879 {
880 ++next_sign_typenr;
881 if (next_sign_typenr == MAX_TYPENR)
882 next_sign_typenr = 1;
883 if (next_sign_typenr == start)
884 {
885 vim_free(sp);
886 emsg(_("E612: Too many signs defined"));
887 return NULL;
888 }
889 lp = first_sign; // start all over
890 continue;
891 }
892 lp = lp->sn_next;
893 }
894
895 sp->sn_typenr = next_sign_typenr;
896 if (++next_sign_typenr == MAX_TYPENR)
897 next_sign_typenr = 1; // wrap around
898
899 sp->sn_name = vim_strsave(name);
900 if (sp->sn_name == NULL) // out of memory
901 {
902 vim_free(sp);
903 return NULL;
904 }
905
906 return sp;
907}
908
909/*
910 * Initialize the icon information for a new sign
911 */
912 static void
913sign_define_init_icon(sign_T *sp, char_u *icon)
914{
915 vim_free(sp->sn_icon);
916 sp->sn_icon = vim_strsave(icon);
917 backslash_halve(sp->sn_icon);
918# ifdef FEAT_SIGN_ICONS
919 if (gui.in_use)
920 {
921 out_flush();
922 if (sp->sn_image != NULL)
923 gui_mch_destroy_sign(sp->sn_image);
924 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
925 }
926# endif
927}
928
929/*
930 * Initialize the text for a new sign
931 */
932 static int
933sign_define_init_text(sign_T *sp, char_u *text)
934{
935 char_u *s;
936 char_u *endp;
937 int cells;
938 int len;
939
940 endp = text + (int)STRLEN(text);
941
942 // Remove backslashes so that it is possible to use a space.
943 for (s = text; s + 1 < endp; ++s)
944 if (*s == '\\')
945 {
946 STRMOVE(s, s + 1);
947 --endp;
948 }
949
950 // Count cells and check for non-printable chars
Bram Moolenaar03142362019-01-18 22:01:42 +0100951 if (has_mbyte)
952 {
953 cells = 0;
954 for (s = text; s < endp; s += (*mb_ptr2len)(s))
955 {
956 if (!vim_isprintc((*mb_ptr2char)(s)))
957 break;
958 cells += (*mb_ptr2cells)(s);
959 }
960 }
961 else
Bram Moolenaar03142362019-01-18 22:01:42 +0100962 {
963 for (s = text; s < endp; ++s)
964 if (!vim_isprintc(*s))
965 break;
966 cells = (int)(s - text);
967 }
968
969 // Currently sign text must be one or two display cells
970 if (s != endp || cells < 1 || cells > 2)
971 {
972 semsg(_("E239: Invalid sign text: %s"), text);
973 return FAIL;
974 }
975
976 vim_free(sp->sn_text);
977 // Allocate one byte more if we need to pad up
978 // with a space.
979 len = (int)(endp - text + ((cells == 1) ? 1 : 0));
980 sp->sn_text = vim_strnsave(text, len);
981
982 // For single character sign text, pad with a space.
983 if (sp->sn_text != NULL && cells == 1)
984 STRCPY(sp->sn_text + len - 1, " ");
985
986 return OK;
987}
988
989/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100990 * Define a new sign or update an existing sign
991 */
992 int
993sign_define_by_name(
994 char_u *name,
995 char_u *icon,
996 char_u *linehl,
997 char_u *text,
998 char_u *texthl)
999{
1000 sign_T *sp_prev;
1001 sign_T *sp;
1002
1003 sp = sign_find(name, &sp_prev);
1004 if (sp == NULL)
1005 {
Bram Moolenaar03142362019-01-18 22:01:42 +01001006 sp = alloc_new_sign(name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001007 if (sp == NULL)
1008 return FAIL;
1009
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001010 // add the new sign to the list of signs
1011 if (sp_prev == NULL)
1012 first_sign = sp;
1013 else
1014 sp_prev->sn_next = sp;
1015 }
1016
1017 // set values for a defined sign.
1018 if (icon != NULL)
Bram Moolenaar03142362019-01-18 22:01:42 +01001019 sign_define_init_icon(sp, icon);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001020
Bram Moolenaar03142362019-01-18 22:01:42 +01001021 if (text != NULL && (sign_define_init_text(sp, text) == FAIL))
1022 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001023
1024 if (linehl != NULL)
1025 sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl));
1026
1027 if (texthl != NULL)
1028 sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl));
1029
1030 return OK;
1031}
1032
1033/*
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001034 * Return TRUE if sign "name" exists.
1035 */
1036 int
1037sign_exists_by_name(char_u *name)
1038{
1039 return sign_find(name, NULL) != NULL;
1040}
1041
1042/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001043 * Free the sign specified by 'name'.
1044 */
1045 int
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001046sign_undefine_by_name(char_u *name, int give_error)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001047{
1048 sign_T *sp_prev;
1049 sign_T *sp;
1050
1051 sp = sign_find(name, &sp_prev);
1052 if (sp == NULL)
1053 {
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001054 if (give_error)
1055 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001056 return FAIL;
1057 }
1058 sign_undefine(sp, sp_prev);
1059
1060 return OK;
1061}
1062
1063/*
1064 * List the signs matching 'name'
1065 */
1066 static void
1067sign_list_by_name(char_u *name)
1068{
1069 sign_T *sp;
1070
1071 sp = sign_find(name, NULL);
1072 if (sp != NULL)
1073 sign_list_defined(sp);
1074 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001075 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001076}
1077
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001078 static void
1079may_force_numberwidth_recompute(buf_T *buf, int unplace)
1080{
1081 tabpage_T *tp;
1082 win_T *wp;
1083
1084 FOR_ALL_TAB_WINDOWS(tp, wp)
1085 if (wp->w_buffer == buf
1086 && (wp->w_p_nu || wp->w_p_rnu)
1087 && (unplace || wp->w_nrwidth_width < 2)
1088 && (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u'))
1089 wp->w_nrwidth_line_count = 0;
1090}
1091
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001092/*
Bram Moolenaar8144acb2019-01-14 23:08:18 +01001093 * Place a sign at the specified file location or update a sign.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001094 */
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001095 int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001096sign_place(
1097 int *sign_id,
1098 char_u *sign_group,
1099 char_u *sign_name,
1100 buf_T *buf,
1101 linenr_T lnum,
1102 int prio)
1103{
1104 sign_T *sp;
1105
1106 // Check for reserved character '*' in group name
1107 if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0'))
1108 return FAIL;
1109
1110 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1111 if (STRCMP(sp->sn_name, sign_name) == 0)
1112 break;
1113 if (sp == NULL)
1114 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001115 semsg(_("E155: Unknown sign: %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001116 return FAIL;
1117 }
1118 if (*sign_id == 0)
1119 *sign_id = sign_group_get_next_signid(buf, sign_group);
1120
1121 if (lnum > 0)
1122 // ":sign place {id} line={lnum} name={name} file={fname}":
1123 // place a sign
1124 buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr);
1125 else
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02001126 // ":sign place {id} file={fname}": change sign type and/or priority
1127 lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr,
1128 prio);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001129 if (lnum > 0)
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001130 {
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001131 redraw_buf_line_later(buf, lnum);
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001132
1133 // When displaying signs in the 'number' column, if the width of the
1134 // number column is less than 2, then force recomputing the width.
1135 may_force_numberwidth_recompute(buf, FALSE);
1136 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001137 else
1138 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001139 semsg(_("E885: Not possible to change sign %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001140 return FAIL;
1141 }
1142
1143 return OK;
1144}
1145
1146/*
1147 * Unplace the specified sign
1148 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001149 static int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001150sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum)
1151{
1152 if (buf->b_signlist == NULL) // No signs in the buffer
1153 return OK;
1154
1155 if (sign_id == 0)
1156 {
1157 // Delete all the signs in the specified buffer
1158 redraw_buf_later(buf, NOT_VALID);
1159 buf_delete_signs(buf, sign_group);
1160 }
1161 else
1162 {
1163 linenr_T lnum;
1164
1165 // Delete only the specified signs
1166 lnum = buf_delsign(buf, atlnum, sign_id, sign_group);
1167 if (lnum == 0)
1168 return FAIL;
1169 }
1170
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001171 // When all the signs in a buffer are removed, force recomputing the
1172 // number column width (if enabled) in all the windows displaying the
1173 // buffer if 'signcolumn' is set to 'number' in that window.
1174 if (buf->b_signlist == NULL)
1175 may_force_numberwidth_recompute(buf, TRUE);
1176
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001177 return OK;
1178}
1179
1180/*
1181 * Unplace the sign at the current cursor line.
1182 */
1183 static void
1184sign_unplace_at_cursor(char_u *groupname)
1185{
1186 int id = -1;
1187
1188 id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname);
1189 if (id > 0)
1190 sign_unplace(id, groupname, curwin->w_buffer, curwin->w_cursor.lnum);
1191 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001192 emsg(_("E159: Missing sign number"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001193}
1194
1195/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001196 * Jump to a sign.
1197 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001198 static linenr_T
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001199sign_jump(int sign_id, char_u *sign_group, buf_T *buf)
1200{
1201 linenr_T lnum;
1202
1203 if ((lnum = buf_findsign(buf, sign_id, sign_group)) <= 0)
1204 {
Bram Moolenaarb5443cc2019-01-15 20:19:40 +01001205 semsg(_("E157: Invalid sign ID: %d"), sign_id);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001206 return -1;
1207 }
1208
1209 // goto a sign ...
1210 if (buf_jump_open_win(buf) != NULL)
1211 { // ... in a current window
1212 curwin->w_cursor.lnum = lnum;
1213 check_cursor_lnum();
1214 beginline(BL_WHITE);
1215 }
1216 else
1217 { // ... not currently in a window
1218 char_u *cmd;
1219
1220 if (buf->b_fname == NULL)
1221 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001222 emsg(_("E934: Cannot jump to a buffer that does not have a name"));
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001223 return -1;
1224 }
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02001225 cmd = alloc(STRLEN(buf->b_fname) + 25);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001226 if (cmd == NULL)
1227 return -1;
1228 sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname);
1229 do_cmdline_cmd(cmd);
1230 vim_free(cmd);
1231 }
1232# ifdef FEAT_FOLDING
1233 foldOpenCursor();
1234# endif
1235
1236 return lnum;
1237}
1238
1239/*
1240 * ":sign define {name} ..." command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001241 */
1242 static void
1243sign_define_cmd(char_u *sign_name, char_u *cmdline)
1244{
1245 char_u *arg;
1246 char_u *p = cmdline;
1247 char_u *icon = NULL;
1248 char_u *text = NULL;
1249 char_u *linehl = NULL;
1250 char_u *texthl = NULL;
1251 int failed = FALSE;
1252
1253 // set values for a defined sign.
1254 for (;;)
1255 {
1256 arg = skipwhite(p);
1257 if (*arg == NUL)
1258 break;
1259 p = skiptowhite_esc(arg);
1260 if (STRNCMP(arg, "icon=", 5) == 0)
1261 {
1262 arg += 5;
1263 icon = vim_strnsave(arg, (int)(p - arg));
1264 }
1265 else if (STRNCMP(arg, "text=", 5) == 0)
1266 {
1267 arg += 5;
1268 text = vim_strnsave(arg, (int)(p - arg));
1269 }
1270 else if (STRNCMP(arg, "linehl=", 7) == 0)
1271 {
1272 arg += 7;
1273 linehl = vim_strnsave(arg, (int)(p - arg));
1274 }
1275 else if (STRNCMP(arg, "texthl=", 7) == 0)
1276 {
1277 arg += 7;
1278 texthl = vim_strnsave(arg, (int)(p - arg));
1279 }
1280 else
1281 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001282 semsg(_(e_invarg2), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001283 failed = TRUE;
1284 break;
1285 }
1286 }
1287
1288 if (!failed)
1289 sign_define_by_name(sign_name, icon, linehl, text, texthl);
1290
1291 vim_free(icon);
1292 vim_free(text);
1293 vim_free(linehl);
1294 vim_free(texthl);
1295}
1296
1297/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001298 * ":sign place" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001299 */
1300 static void
1301sign_place_cmd(
1302 buf_T *buf,
1303 linenr_T lnum,
1304 char_u *sign_name,
1305 int id,
1306 char_u *group,
1307 int prio)
1308{
1309 if (id <= 0)
1310 {
1311 // List signs placed in a file/buffer
1312 // :sign place file={fname}
1313 // :sign place group={group} file={fname}
1314 // :sign place group=* file={fname}
1315 // :sign place buffer={nr}
1316 // :sign place group={group} buffer={nr}
1317 // :sign place group=* buffer={nr}
1318 // :sign place
1319 // :sign place group={group}
1320 // :sign place group=*
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001321 if (lnum >= 0 || sign_name != NULL
1322 || (group != NULL && *group == '\0'))
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001323 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001324 else
1325 sign_list_placed(buf, group);
1326 }
1327 else
1328 {
1329 // Place a new sign
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001330 if (sign_name == NULL || buf == NULL
1331 || (group != NULL && *group == '\0'))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001332 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001333 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001334 return;
1335 }
1336
1337 sign_place(&id, group, sign_name, buf, lnum, prio);
1338 }
1339}
1340
1341/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001342 * ":sign unplace" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001343 */
1344 static void
1345sign_unplace_cmd(
1346 buf_T *buf,
1347 linenr_T lnum,
1348 char_u *sign_name,
1349 int id,
1350 char_u *group)
1351{
1352 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0'))
1353 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001354 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001355 return;
1356 }
1357
1358 if (id == -2)
1359 {
1360 if (buf != NULL)
1361 // :sign unplace * file={fname}
1362 // :sign unplace * group={group} file={fname}
1363 // :sign unplace * group=* file={fname}
1364 // :sign unplace * buffer={nr}
1365 // :sign unplace * group={group} buffer={nr}
1366 // :sign unplace * group=* buffer={nr}
1367 sign_unplace(0, group, buf, 0);
1368 else
1369 // :sign unplace *
1370 // :sign unplace * group={group}
1371 // :sign unplace * group=*
1372 FOR_ALL_BUFFERS(buf)
1373 if (buf->b_signlist != NULL)
1374 buf_delete_signs(buf, group);
1375 }
1376 else
1377 {
1378 if (buf != NULL)
1379 // :sign unplace {id} file={fname}
1380 // :sign unplace {id} group={group} file={fname}
1381 // :sign unplace {id} group=* file={fname}
1382 // :sign unplace {id} buffer={nr}
1383 // :sign unplace {id} group={group} buffer={nr}
1384 // :sign unplace {id} group=* buffer={nr}
1385 sign_unplace(id, group, buf, 0);
1386 else
1387 {
1388 if (id == -1)
1389 {
1390 // :sign unplace group={group}
1391 // :sign unplace group=*
1392 sign_unplace_at_cursor(group);
1393 }
1394 else
1395 {
1396 // :sign unplace {id}
1397 // :sign unplace {id} group={group}
1398 // :sign unplace {id} group=*
1399 FOR_ALL_BUFFERS(buf)
1400 sign_unplace(id, group, buf, 0);
1401 }
1402 }
1403 }
1404}
1405
1406/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001407 * Jump to a placed sign commands:
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001408 * :sign jump {id} file={fname}
1409 * :sign jump {id} buffer={nr}
1410 * :sign jump {id} group={group} file={fname}
1411 * :sign jump {id} group={group} buffer={nr}
1412 */
1413 static void
1414sign_jump_cmd(
1415 buf_T *buf,
1416 linenr_T lnum,
1417 char_u *sign_name,
1418 int id,
1419 char_u *group)
1420{
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001421 if (sign_name == NULL && group == NULL && id == -1)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001422 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001423 emsg(_(e_argreq));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001424 return;
1425 }
1426
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001427 if (buf == NULL || (group != NULL && *group == '\0')
1428 || lnum >= 0 || sign_name != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001429 {
1430 // File or buffer is not specified or an empty group is used
1431 // or a line number or a sign name is specified.
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001432 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001433 return;
1434 }
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001435 (void)sign_jump(id, group, buf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001436}
1437
1438/*
1439 * Parse the command line arguments for the ":sign place", ":sign unplace" and
1440 * ":sign jump" commands.
1441 * The supported arguments are: line={lnum} name={name} group={group}
1442 * priority={prio} and file={fname} or buffer={nr}.
1443 */
1444 static int
1445parse_sign_cmd_args(
1446 int cmd,
1447 char_u *arg,
1448 char_u **sign_name,
1449 int *signid,
1450 char_u **group,
1451 int *prio,
1452 buf_T **buf,
1453 linenr_T *lnum)
1454{
1455 char_u *arg1;
1456 char_u *name;
1457 char_u *filename = NULL;
Bram Moolenaarb589f952019-01-07 22:10:00 +01001458 int lnum_arg = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001459
1460 // first arg could be placed sign id
1461 arg1 = arg;
1462 if (VIM_ISDIGIT(*arg))
1463 {
1464 *signid = getdigits(&arg);
1465 if (!VIM_ISWHITE(*arg) && *arg != NUL)
1466 {
1467 *signid = -1;
1468 arg = arg1;
1469 }
1470 else
1471 arg = skipwhite(arg);
1472 }
1473
1474 while (*arg != NUL)
1475 {
1476 if (STRNCMP(arg, "line=", 5) == 0)
1477 {
1478 arg += 5;
1479 *lnum = atoi((char *)arg);
1480 arg = skiptowhite(arg);
Bram Moolenaarb589f952019-01-07 22:10:00 +01001481 lnum_arg = TRUE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001482 }
1483 else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE)
1484 {
1485 if (*signid != -1)
1486 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001487 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001488 return FAIL;
1489 }
1490 *signid = -2;
1491 arg = skiptowhite(arg + 1);
1492 }
1493 else if (STRNCMP(arg, "name=", 5) == 0)
1494 {
1495 arg += 5;
1496 name = arg;
1497 arg = skiptowhite(arg);
1498 if (*arg != NUL)
1499 *arg++ = NUL;
1500 while (name[0] == '0' && name[1] != NUL)
1501 ++name;
1502 *sign_name = name;
1503 }
1504 else if (STRNCMP(arg, "group=", 6) == 0)
1505 {
1506 arg += 6;
1507 *group = arg;
1508 arg = skiptowhite(arg);
1509 if (*arg != NUL)
1510 *arg++ = NUL;
1511 }
1512 else if (STRNCMP(arg, "priority=", 9) == 0)
1513 {
1514 arg += 9;
1515 *prio = atoi((char *)arg);
1516 arg = skiptowhite(arg);
1517 }
1518 else if (STRNCMP(arg, "file=", 5) == 0)
1519 {
1520 arg += 5;
1521 filename = arg;
1522 *buf = buflist_findname_exp(arg);
1523 break;
1524 }
1525 else if (STRNCMP(arg, "buffer=", 7) == 0)
1526 {
1527 arg += 7;
1528 filename = arg;
1529 *buf = buflist_findnr((int)getdigits(&arg));
1530 if (*skipwhite(arg) != NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001531 emsg(_(e_trailing));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001532 break;
1533 }
1534 else
1535 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001536 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001537 return FAIL;
1538 }
1539 arg = skipwhite(arg);
1540 }
1541
1542 if (filename != NULL && *buf == NULL)
1543 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001544 semsg(_("E158: Invalid buffer name: %s"), filename);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001545 return FAIL;
1546 }
1547
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001548 // If the filename is not supplied for the sign place or the sign jump
1549 // command, then use the current buffer.
Bram Moolenaarb589f952019-01-07 22:10:00 +01001550 if (filename == NULL && ((cmd == SIGNCMD_PLACE && lnum_arg)
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001551 || cmd == SIGNCMD_JUMP))
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001552 *buf = curwin->w_buffer;
1553
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001554 return OK;
1555}
1556
1557/*
1558 * ":sign" command
1559 */
1560 void
1561ex_sign(exarg_T *eap)
1562{
1563 char_u *arg = eap->arg;
1564 char_u *p;
1565 int idx;
1566 sign_T *sp;
1567 buf_T *buf = NULL;
1568
1569 // Parse the subcommand.
1570 p = skiptowhite(arg);
1571 idx = sign_cmd_idx(arg, p);
1572 if (idx == SIGNCMD_LAST)
1573 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001574 semsg(_("E160: Unknown sign command: %s"), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001575 return;
1576 }
1577 arg = skipwhite(p);
1578
1579 if (idx <= SIGNCMD_LIST)
1580 {
1581 // Define, undefine or list signs.
1582 if (idx == SIGNCMD_LIST && *arg == NUL)
1583 {
1584 // ":sign list": list all defined signs
1585 for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next)
1586 sign_list_defined(sp);
1587 }
1588 else if (*arg == NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001589 emsg(_("E156: Missing sign name"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001590 else
1591 {
1592 char_u *name;
1593
1594 // Isolate the sign name. If it's a number skip leading zeroes,
1595 // so that "099" and "99" are the same sign. But keep "0".
1596 p = skiptowhite(arg);
1597 if (*p != NUL)
1598 *p++ = NUL;
1599 while (arg[0] == '0' && arg[1] != NUL)
1600 ++arg;
1601 name = vim_strsave(arg);
1602
1603 if (idx == SIGNCMD_DEFINE)
1604 sign_define_cmd(name, p);
1605 else if (idx == SIGNCMD_LIST)
1606 // ":sign list {name}"
1607 sign_list_by_name(name);
1608 else
1609 // ":sign undefine {name}"
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001610 sign_undefine_by_name(name, TRUE);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001611
1612 vim_free(name);
1613 return;
1614 }
1615 }
1616 else
1617 {
1618 int id = -1;
1619 linenr_T lnum = -1;
1620 char_u *sign_name = NULL;
1621 char_u *group = NULL;
1622 int prio = SIGN_DEF_PRIO;
1623
1624 // Parse command line arguments
1625 if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio,
1626 &buf, &lnum) == FAIL)
1627 return;
1628
1629 if (idx == SIGNCMD_PLACE)
1630 sign_place_cmd(buf, lnum, sign_name, id, group, prio);
1631 else if (idx == SIGNCMD_UNPLACE)
1632 sign_unplace_cmd(buf, lnum, sign_name, id, group);
1633 else if (idx == SIGNCMD_JUMP)
1634 sign_jump_cmd(buf, lnum, sign_name, id, group);
1635 }
1636}
1637
1638/*
1639 * Return information about a specified sign
1640 */
1641 static void
1642sign_getinfo(sign_T *sp, dict_T *retdict)
1643{
1644 char_u *p;
1645
1646 dict_add_string(retdict, "name", (char_u *)sp->sn_name);
1647 if (sp->sn_icon != NULL)
1648 dict_add_string(retdict, "icon", (char_u *)sp->sn_icon);
1649 if (sp->sn_text != NULL)
1650 dict_add_string(retdict, "text", (char_u *)sp->sn_text);
1651 if (sp->sn_line_hl > 0)
1652 {
1653 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1654 if (p == NULL)
1655 p = (char_u *)"NONE";
1656 dict_add_string(retdict, "linehl", (char_u *)p);
1657 }
1658 if (sp->sn_text_hl > 0)
1659 {
1660 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1661 if (p == NULL)
1662 p = (char_u *)"NONE";
1663 dict_add_string(retdict, "texthl", (char_u *)p);
1664 }
1665}
1666
1667/*
1668 * If 'name' is NULL, return a list of all the defined signs.
1669 * Otherwise, return information about the specified sign.
1670 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001671 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001672sign_getlist(char_u *name, list_T *retlist)
1673{
1674 sign_T *sp = first_sign;
1675 dict_T *dict;
1676
1677 if (name != NULL)
1678 {
1679 sp = sign_find(name, NULL);
1680 if (sp == NULL)
1681 return;
1682 }
1683
1684 for (; sp != NULL && !got_int; sp = sp->sn_next)
1685 {
1686 if ((dict = dict_alloc_id(aid_sign_getlist)) == NULL)
1687 return;
1688 if (list_append_dict(retlist, dict) == FAIL)
1689 return;
1690 sign_getinfo(sp, dict);
1691
1692 if (name != NULL) // handle only the specified sign
1693 break;
1694 }
1695}
1696
1697/*
1698 * Returns information about signs placed in a buffer as list of dicts.
1699 */
1700 void
1701get_buffer_signs(buf_T *buf, list_T *l)
1702{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001703 sign_entry_T *sign;
1704 dict_T *d;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001705
1706 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1707 {
1708 if ((d = sign_get_info(sign)) != NULL)
1709 list_append_dict(l, d);
1710 }
1711}
1712
1713/*
1714 * Return information about all the signs placed in a buffer
1715 */
1716 static void
1717sign_get_placed_in_buf(
1718 buf_T *buf,
1719 linenr_T lnum,
1720 int sign_id,
1721 char_u *sign_group,
1722 list_T *retlist)
1723{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001724 dict_T *d;
1725 list_T *l;
1726 sign_entry_T *sign;
1727 dict_T *sdict;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001728
1729 if ((d = dict_alloc_id(aid_sign_getplaced_dict)) == NULL)
1730 return;
1731 list_append_dict(retlist, d);
1732
1733 dict_add_number(d, "bufnr", (long)buf->b_fnum);
1734
1735 if ((l = list_alloc_id(aid_sign_getplaced_list)) == NULL)
1736 return;
1737 dict_add_list(d, "signs", l);
1738
1739 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1740 {
1741 if (!sign_in_group(sign, sign_group))
1742 continue;
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001743 if ((lnum == 0 && sign_id == 0)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001744 || (sign_id == 0 && lnum == sign->se_lnum)
1745 || (lnum == 0 && sign_id == sign->se_id)
1746 || (lnum == sign->se_lnum && sign_id == sign->se_id))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001747 {
1748 if ((sdict = sign_get_info(sign)) != NULL)
1749 list_append_dict(l, sdict);
1750 }
1751 }
1752}
1753
1754/*
1755 * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the
1756 * sign placed at the line number. If 'lnum' is zero, return all the signs
1757 * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers.
1758 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001759 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001760sign_get_placed(
1761 buf_T *buf,
1762 linenr_T lnum,
1763 int sign_id,
1764 char_u *sign_group,
1765 list_T *retlist)
1766{
1767 if (buf != NULL)
1768 sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist);
1769 else
1770 {
1771 FOR_ALL_BUFFERS(buf)
1772 {
1773 if (buf->b_signlist != NULL)
1774 sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist);
1775 }
1776 }
1777}
1778
1779# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1780/*
1781 * Allocate the icons. Called when the GUI has started. Allows defining
1782 * signs before it starts.
1783 */
1784 void
1785sign_gui_started(void)
1786{
1787 sign_T *sp;
1788
1789 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1790 if (sp->sn_icon != NULL)
1791 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
1792}
1793# endif
1794
1795/*
1796 * List one sign.
1797 */
1798 static void
1799sign_list_defined(sign_T *sp)
1800{
1801 char_u *p;
1802
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001803 smsg("sign %s", sp->sn_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001804 if (sp->sn_icon != NULL)
1805 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001806 msg_puts(" icon=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001807 msg_outtrans(sp->sn_icon);
1808# ifdef FEAT_SIGN_ICONS
1809 if (sp->sn_image == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001810 msg_puts(_(" (NOT FOUND)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001811# else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001812 msg_puts(_(" (not supported)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001813# endif
1814 }
1815 if (sp->sn_text != NULL)
1816 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001817 msg_puts(" text=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001818 msg_outtrans(sp->sn_text);
1819 }
1820 if (sp->sn_line_hl > 0)
1821 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001822 msg_puts(" linehl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001823 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1824 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001825 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001826 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001827 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001828 }
1829 if (sp->sn_text_hl > 0)
1830 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001831 msg_puts(" texthl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001832 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1833 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001834 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001835 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001836 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001837 }
1838}
1839
1840/*
1841 * Undefine a sign and free its memory.
1842 */
1843 static void
1844sign_undefine(sign_T *sp, sign_T *sp_prev)
1845{
1846 vim_free(sp->sn_name);
1847 vim_free(sp->sn_icon);
1848# ifdef FEAT_SIGN_ICONS
1849 if (sp->sn_image != NULL)
1850 {
1851 out_flush();
1852 gui_mch_destroy_sign(sp->sn_image);
1853 }
1854# endif
1855 vim_free(sp->sn_text);
1856 if (sp_prev == NULL)
1857 first_sign = sp->sn_next;
1858 else
1859 sp_prev->sn_next = sp->sn_next;
1860 vim_free(sp);
1861}
1862
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001863# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1864 void *
1865sign_get_image(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001866 int typenr) // the attribute which may have a sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001867{
1868 sign_T *sp;
1869
1870 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1871 if (sp->sn_typenr == typenr)
1872 return sp->sn_image;
1873 return NULL;
1874}
1875# endif
1876
1877/*
1878 * Undefine/free all signs.
1879 */
1880 void
1881free_signs(void)
1882{
1883 while (first_sign != NULL)
1884 sign_undefine(first_sign, NULL);
1885}
1886
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001887static enum
1888{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001889 EXP_SUBCMD, // expand :sign sub-commands
1890 EXP_DEFINE, // expand :sign define {name} args
1891 EXP_PLACE, // expand :sign place {id} args
Bram Moolenaar3678f652019-02-17 14:50:25 +01001892 EXP_LIST, // expand :sign place args
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001893 EXP_UNPLACE, // expand :sign unplace"
Bram Moolenaar3678f652019-02-17 14:50:25 +01001894 EXP_SIGN_NAMES, // expand with name of placed signs
1895 EXP_SIGN_GROUPS // expand with name of placed sign groups
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001896} expand_what;
1897
1898/*
Bram Moolenaar3678f652019-02-17 14:50:25 +01001899 * Return the n'th sign name (used for command line completion)
1900 */
1901 static char_u *
1902get_nth_sign_name(int idx)
1903{
1904 int current_idx;
1905 sign_T *sp;
1906
1907 // Complete with name of signs already defined
1908 current_idx = 0;
1909 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1910 if (current_idx++ == idx)
1911 return sp->sn_name;
1912 return NULL;
1913}
1914
1915/*
1916 * Return the n'th sign group name (used for command line completion)
1917 */
1918 static char_u *
1919get_nth_sign_group_name(int idx)
1920{
1921 int current_idx;
1922 int todo;
1923 hashitem_T *hi;
1924 signgroup_T *group;
1925
1926 // Complete with name of sign groups already defined
1927 current_idx = 0;
1928 todo = (int)sg_table.ht_used;
1929 for (hi = sg_table.ht_array; todo > 0; ++hi)
1930 {
1931 if (!HASHITEM_EMPTY(hi))
1932 {
1933 --todo;
1934 if (current_idx++ == idx)
1935 {
1936 group = HI2SG(hi);
1937 return group->sg_name;
1938 }
1939 }
1940 }
1941 return NULL;
1942}
1943
1944/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001945 * Function given to ExpandGeneric() to obtain the sign command
1946 * expansion.
1947 */
1948 char_u *
1949get_sign_name(expand_T *xp UNUSED, int idx)
1950{
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001951 switch (expand_what)
1952 {
1953 case EXP_SUBCMD:
1954 return (char_u *)cmds[idx];
1955 case EXP_DEFINE:
1956 {
1957 char *define_arg[] =
1958 {
1959 "icon=", "linehl=", "text=", "texthl=", NULL
1960 };
1961 return (char_u *)define_arg[idx];
1962 }
1963 case EXP_PLACE:
1964 {
1965 char *place_arg[] =
1966 {
1967 "line=", "name=", "group=", "priority=", "file=",
1968 "buffer=", NULL
1969 };
1970 return (char_u *)place_arg[idx];
1971 }
Bram Moolenaar3678f652019-02-17 14:50:25 +01001972 case EXP_LIST:
1973 {
1974 char *list_arg[] =
1975 {
1976 "group=", "file=", "buffer=", NULL
1977 };
1978 return (char_u *)list_arg[idx];
1979 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001980 case EXP_UNPLACE:
1981 {
1982 char *unplace_arg[] = { "group=", "file=", "buffer=", NULL };
1983 return (char_u *)unplace_arg[idx];
1984 }
1985 case EXP_SIGN_NAMES:
Bram Moolenaar3678f652019-02-17 14:50:25 +01001986 return get_nth_sign_name(idx);
1987 case EXP_SIGN_GROUPS:
1988 return get_nth_sign_group_name(idx);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001989 default:
1990 return NULL;
1991 }
1992}
1993
1994/*
1995 * Handle command line completion for :sign command.
1996 */
1997 void
1998set_context_in_sign_cmd(expand_T *xp, char_u *arg)
1999{
2000 char_u *p;
2001 char_u *end_subcmd;
2002 char_u *last;
2003 int cmd_idx;
2004 char_u *begin_subcmd_args;
2005
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002006 // Default: expand subcommands.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002007 xp->xp_context = EXPAND_SIGN;
2008 expand_what = EXP_SUBCMD;
2009 xp->xp_pattern = arg;
2010
2011 end_subcmd = skiptowhite(arg);
2012 if (*end_subcmd == NUL)
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002013 // expand subcmd name
2014 // :sign {subcmd}<CTRL-D>
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002015 return;
2016
2017 cmd_idx = sign_cmd_idx(arg, end_subcmd);
2018
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002019 // :sign {subcmd} {subcmd_args}
2020 // |
2021 // begin_subcmd_args
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002022 begin_subcmd_args = skipwhite(end_subcmd);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002023
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002024 // expand last argument of subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002025
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002026 // :sign define {name} {args}...
2027 // |
2028 // p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002029
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002030 // Loop until reaching last argument.
Bram Moolenaar3678f652019-02-17 14:50:25 +01002031 p = begin_subcmd_args;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002032 do
2033 {
2034 p = skipwhite(p);
2035 last = p;
2036 p = skiptowhite(p);
2037 } while (*p != NUL);
2038
2039 p = vim_strchr(last, '=');
2040
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002041 // :sign define {name} {args}... {last}=
2042 // | |
2043 // last p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002044 if (p == NULL)
2045 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002046 // Expand last argument name (before equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002047 xp->xp_pattern = last;
2048 switch (cmd_idx)
2049 {
2050 case SIGNCMD_DEFINE:
2051 expand_what = EXP_DEFINE;
2052 break;
2053 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002054 // List placed signs
2055 if (VIM_ISDIGIT(*begin_subcmd_args))
2056 // :sign place {id} {args}...
2057 expand_what = EXP_PLACE;
2058 else
2059 // :sign place {args}...
2060 expand_what = EXP_LIST;
2061 break;
2062 case SIGNCMD_LIST:
2063 case SIGNCMD_UNDEFINE:
2064 // :sign list <CTRL-D>
2065 // :sign undefine <CTRL-D>
2066 expand_what = EXP_SIGN_NAMES;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002067 break;
2068 case SIGNCMD_JUMP:
2069 case SIGNCMD_UNPLACE:
2070 expand_what = EXP_UNPLACE;
2071 break;
2072 default:
2073 xp->xp_context = EXPAND_NOTHING;
2074 }
2075 }
2076 else
2077 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002078 // Expand last argument value (after equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002079 xp->xp_pattern = p + 1;
2080 switch (cmd_idx)
2081 {
2082 case SIGNCMD_DEFINE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002083 if (STRNCMP(last, "texthl", 6) == 0
2084 || STRNCMP(last, "linehl", 6) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002085 xp->xp_context = EXPAND_HIGHLIGHT;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002086 else if (STRNCMP(last, "icon", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002087 xp->xp_context = EXPAND_FILES;
2088 else
2089 xp->xp_context = EXPAND_NOTHING;
2090 break;
2091 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002092 if (STRNCMP(last, "name", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002093 expand_what = EXP_SIGN_NAMES;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002094 else if (STRNCMP(last, "group", 5) == 0)
2095 expand_what = EXP_SIGN_GROUPS;
2096 else if (STRNCMP(last, "file", 4) == 0)
2097 xp->xp_context = EXPAND_BUFFERS;
2098 else
2099 xp->xp_context = EXPAND_NOTHING;
2100 break;
2101 case SIGNCMD_UNPLACE:
2102 case SIGNCMD_JUMP:
2103 if (STRNCMP(last, "group", 5) == 0)
2104 expand_what = EXP_SIGN_GROUPS;
2105 else if (STRNCMP(last, "file", 4) == 0)
2106 xp->xp_context = EXPAND_BUFFERS;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002107 else
2108 xp->xp_context = EXPAND_NOTHING;
2109 break;
2110 default:
2111 xp->xp_context = EXPAND_NOTHING;
2112 }
2113 }
2114}
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002115
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002116/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002117 * Define a sign using the attributes in 'dict'. Returns 0 on success and -1 on
2118 * failure.
2119 */
2120 static int
2121sign_define_from_dict(char_u *name_arg, dict_T *dict)
2122{
2123 char_u *name = NULL;
2124 char_u *icon = NULL;
2125 char_u *linehl = NULL;
2126 char_u *text = NULL;
2127 char_u *texthl = NULL;
2128 int retval = -1;
2129
2130 if (name_arg == NULL)
2131 {
2132 if (dict == NULL)
2133 return -1;
2134 name = dict_get_string(dict, (char_u *)"name", TRUE);
2135 }
2136 else
2137 name = vim_strsave(name_arg);
2138 if (name == NULL || name[0] == NUL)
2139 goto cleanup;
2140 if (dict != NULL)
2141 {
2142 icon = dict_get_string(dict, (char_u *)"icon", TRUE);
2143 linehl = dict_get_string(dict, (char_u *)"linehl", TRUE);
2144 text = dict_get_string(dict, (char_u *)"text", TRUE);
2145 texthl = dict_get_string(dict, (char_u *)"texthl", TRUE);
2146 }
2147
2148 if (sign_define_by_name(name, icon, linehl, text, texthl) == OK)
2149 retval = 0;
2150
2151cleanup:
2152 vim_free(name);
2153 vim_free(icon);
2154 vim_free(linehl);
2155 vim_free(text);
2156 vim_free(texthl);
2157
2158 return retval;
2159}
2160
2161/*
2162 * Define multiple signs using attributes from list 'l' and store the return
2163 * values in 'retlist'.
2164 */
2165 static void
2166sign_define_multiple(list_T *l, list_T *retlist)
2167{
2168 listitem_T *li;
2169 int retval;
2170
2171 for (li = l->lv_first; li != NULL; li = li->li_next)
2172 {
2173 retval = -1;
2174 if (li->li_tv.v_type == VAR_DICT)
2175 retval = sign_define_from_dict(NULL, li->li_tv.vval.v_dict);
2176 else
2177 emsg(_(e_dictreq));
2178 list_append_number(retlist, retval);
2179 }
2180}
2181
2182/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002183 * "sign_define()" function
2184 */
2185 void
2186f_sign_define(typval_T *argvars, typval_T *rettv)
2187{
2188 char_u *name;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002189
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002190 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2191 {
2192 // Define multiple signs
2193 if (rettv_list_alloc(rettv) != OK)
2194 return;
2195
2196 sign_define_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2197 return;
2198 }
2199
2200 // Define a single sign
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002201 rettv->vval.v_number = -1;
2202
2203 name = tv_get_string_chk(&argvars[0]);
2204 if (name == NULL)
2205 return;
2206
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002207 if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_DICT)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002208 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002209 emsg(_(e_dictreq));
2210 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002211 }
2212
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002213 rettv->vval.v_number = sign_define_from_dict(name,
2214 argvars[1].v_type == VAR_DICT ? argvars[1].vval.v_dict : NULL);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002215}
2216
2217/*
2218 * "sign_getdefined()" function
2219 */
2220 void
2221f_sign_getdefined(typval_T *argvars, typval_T *rettv)
2222{
2223 char_u *name = NULL;
2224
2225 if (rettv_list_alloc_id(rettv, aid_sign_getdefined) != OK)
2226 return;
2227
2228 if (argvars[0].v_type != VAR_UNKNOWN)
2229 name = tv_get_string(&argvars[0]);
2230
2231 sign_getlist(name, rettv->vval.v_list);
2232}
2233
2234/*
2235 * "sign_getplaced()" function
2236 */
2237 void
2238f_sign_getplaced(typval_T *argvars, typval_T *rettv)
2239{
2240 buf_T *buf = NULL;
2241 dict_T *dict;
2242 dictitem_T *di;
2243 linenr_T lnum = 0;
2244 int sign_id = 0;
2245 char_u *group = NULL;
2246 int notanum = FALSE;
2247
2248 if (rettv_list_alloc_id(rettv, aid_sign_getplaced) != OK)
2249 return;
2250
2251 if (argvars[0].v_type != VAR_UNKNOWN)
2252 {
2253 // get signs placed in the specified buffer
2254 buf = get_buf_arg(&argvars[0]);
2255 if (buf == NULL)
2256 return;
2257
2258 if (argvars[1].v_type != VAR_UNKNOWN)
2259 {
2260 if (argvars[1].v_type != VAR_DICT ||
2261 ((dict = argvars[1].vval.v_dict) == NULL))
2262 {
2263 emsg(_(e_dictreq));
2264 return;
2265 }
2266 if ((di = dict_find(dict, (char_u *)"lnum", -1)) != NULL)
2267 {
2268 // get signs placed at this line
2269 (void)tv_get_number_chk(&di->di_tv, &notanum);
2270 if (notanum)
2271 return;
2272 lnum = tv_get_lnum(&di->di_tv);
2273 }
2274 if ((di = dict_find(dict, (char_u *)"id", -1)) != NULL)
2275 {
2276 // get sign placed with this identifier
2277 sign_id = (int)tv_get_number_chk(&di->di_tv, &notanum);
2278 if (notanum)
2279 return;
2280 }
2281 if ((di = dict_find(dict, (char_u *)"group", -1)) != NULL)
2282 {
2283 group = tv_get_string_chk(&di->di_tv);
2284 if (group == NULL)
2285 return;
2286 if (*group == '\0') // empty string means global group
2287 group = NULL;
2288 }
2289 }
2290 }
2291
2292 sign_get_placed(buf, lnum, sign_id, group, rettv->vval.v_list);
2293}
2294
2295/*
2296 * "sign_jump()" function
2297 */
2298 void
2299f_sign_jump(typval_T *argvars, typval_T *rettv)
2300{
2301 int sign_id;
2302 char_u *sign_group = NULL;
2303 buf_T *buf;
2304 int notanum = FALSE;
2305
2306 rettv->vval.v_number = -1;
2307
2308 // Sign identifier
2309 sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
2310 if (notanum)
2311 return;
2312 if (sign_id <= 0)
2313 {
2314 emsg(_(e_invarg));
2315 return;
2316 }
2317
2318 // Sign group
2319 sign_group = tv_get_string_chk(&argvars[1]);
2320 if (sign_group == NULL)
2321 return;
2322 if (sign_group[0] == '\0')
2323 sign_group = NULL; // global sign group
2324 else
2325 {
2326 sign_group = vim_strsave(sign_group);
2327 if (sign_group == NULL)
2328 return;
2329 }
2330
2331 // Buffer to place the sign
2332 buf = get_buf_arg(&argvars[2]);
2333 if (buf == NULL)
2334 goto cleanup;
2335
2336 rettv->vval.v_number = sign_jump(sign_id, sign_group, buf);
2337
2338cleanup:
2339 vim_free(sign_group);
2340}
2341
2342/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002343 * Place a new sign using the values specified in dict 'dict'. Returns the sign
2344 * identifier if successfully placed, otherwise returns 0.
2345 */
2346 static int
2347sign_place_from_dict(
2348 typval_T *id_tv,
2349 typval_T *group_tv,
2350 typval_T *name_tv,
2351 typval_T *buf_tv,
2352 dict_T *dict)
2353{
2354 int sign_id = 0;
2355 char_u *group = NULL;
2356 char_u *sign_name = NULL;
2357 buf_T *buf = NULL;
2358 dictitem_T *di;
2359 linenr_T lnum = 0;
2360 int prio = SIGN_DEF_PRIO;
2361 int notanum = FALSE;
2362 int ret_sign_id = -1;
2363
2364 // sign identifier
2365 if (id_tv == NULL)
2366 {
2367 di = dict_find(dict, (char_u *)"id", -1);
2368 if (di != NULL)
2369 id_tv = &di->di_tv;
2370 }
2371 if (id_tv == NULL)
2372 sign_id = 0;
2373 else
2374 {
2375 sign_id = tv_get_number_chk(id_tv, &notanum);
2376 if (notanum)
2377 return -1;
2378 if (sign_id < 0)
2379 {
2380 emsg(_(e_invarg));
2381 return -1;
2382 }
2383 }
2384
2385 // sign group
2386 if (group_tv == NULL)
2387 {
2388 di = dict_find(dict, (char_u *)"group", -1);
2389 if (di != NULL)
2390 group_tv = &di->di_tv;
2391 }
2392 if (group_tv == NULL)
2393 group = NULL; // global group
2394 else
2395 {
2396 group = tv_get_string_chk(group_tv);
2397 if (group == NULL)
2398 goto cleanup;
2399 if (group[0] == '\0') // global sign group
2400 group = NULL;
2401 else
2402 {
2403 group = vim_strsave(group);
2404 if (group == NULL)
2405 return -1;
2406 }
2407 }
2408
2409 // sign name
2410 if (name_tv == NULL)
2411 {
2412 di = dict_find(dict, (char_u *)"name", -1);
2413 if (di != NULL)
2414 name_tv = &di->di_tv;
2415 }
2416 if (name_tv == NULL)
2417 goto cleanup;
2418 sign_name = tv_get_string_chk(name_tv);
2419 if (sign_name == NULL)
2420 goto cleanup;
2421
2422 // buffer to place the sign
2423 if (buf_tv == NULL)
2424 {
2425 di = dict_find(dict, (char_u *)"buffer", -1);
2426 if (di != NULL)
2427 buf_tv = &di->di_tv;
2428 }
2429 if (buf_tv == NULL)
2430 goto cleanup;
2431 buf = get_buf_arg(buf_tv);
2432 if (buf == NULL)
2433 goto cleanup;
2434
2435 // line number of the sign
2436 di = dict_find(dict, (char_u *)"lnum", -1);
2437 if (di != NULL)
2438 {
Bram Moolenaar42aff462019-08-21 13:20:29 +02002439 lnum = tv_get_lnum(&di->di_tv);
2440 if (lnum <= 0)
2441 {
2442 emsg(_(e_invarg));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002443 goto cleanup;
Bram Moolenaar42aff462019-08-21 13:20:29 +02002444 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002445 }
2446
2447 // sign priority
2448 di = dict_find(dict, (char_u *)"priority", -1);
2449 if (di != NULL)
2450 {
2451 prio = (int)tv_get_number_chk(&di->di_tv, &notanum);
2452 if (notanum)
2453 goto cleanup;
2454 }
2455
2456 if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK)
2457 ret_sign_id = sign_id;
2458
2459cleanup:
2460 vim_free(group);
2461
2462 return ret_sign_id;
2463}
2464
2465/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002466 * "sign_place()" function
2467 */
2468 void
2469f_sign_place(typval_T *argvars, typval_T *rettv)
2470{
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002471 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002472
2473 rettv->vval.v_number = -1;
2474
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002475 if (argvars[4].v_type != VAR_UNKNOWN
2476 && (argvars[4].v_type != VAR_DICT
2477 || ((dict = argvars[4].vval.v_dict) == NULL)))
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002478 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002479 emsg(_(e_dictreq));
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002480 return;
2481 }
2482
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002483 rettv->vval.v_number = sign_place_from_dict(&argvars[0], &argvars[1],
2484 &argvars[2], &argvars[3], dict);
2485}
2486
2487/*
2488 * "sign_placelist()" function. Place multiple signs.
2489 */
2490 void
2491f_sign_placelist(typval_T *argvars, typval_T *rettv)
2492{
2493 listitem_T *li;
2494 int sign_id;
2495
2496 if (rettv_list_alloc(rettv) != OK)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002497 return;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002498
2499 if (argvars[0].v_type != VAR_LIST)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002500 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002501 emsg(_(e_listreq));
2502 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002503 }
2504
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002505 // Process the List of sign attributes
2506 for (li = argvars[0].vval.v_list->lv_first; li != NULL; li = li->li_next)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002507 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002508 sign_id = -1;
2509 if (li->li_tv.v_type == VAR_DICT)
2510 sign_id = sign_place_from_dict(NULL, NULL, NULL, NULL,
2511 li->li_tv.vval.v_dict);
2512 else
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002513 emsg(_(e_dictreq));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002514 list_append_number(rettv->vval.v_list, sign_id);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002515 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002516}
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002517
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002518/*
2519 * Undefine multiple signs
2520 */
2521 static void
2522sign_undefine_multiple(list_T *l, list_T *retlist)
2523{
2524 char_u *name;
2525 listitem_T *li;
2526 int retval;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002527
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002528 for (li = l->lv_first; li != NULL; li = li->li_next)
2529 {
2530 retval = -1;
2531 name = tv_get_string_chk(&li->li_tv);
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002532 if (name != NULL && (sign_undefine_by_name(name, TRUE) == OK))
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002533 retval = 0;
2534 list_append_number(retlist, retval);
2535 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002536}
2537
2538/*
2539 * "sign_undefine()" function
2540 */
2541 void
2542f_sign_undefine(typval_T *argvars, typval_T *rettv)
2543{
2544 char_u *name;
2545
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002546 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2547 {
2548 // Undefine multiple signs
2549 if (rettv_list_alloc(rettv) != OK)
2550 return;
2551
2552 sign_undefine_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2553 return;
2554 }
2555
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002556 rettv->vval.v_number = -1;
2557
2558 if (argvars[0].v_type == VAR_UNKNOWN)
2559 {
2560 // Free all the signs
2561 free_signs();
2562 rettv->vval.v_number = 0;
2563 }
2564 else
2565 {
2566 // Free only the specified sign
2567 name = tv_get_string_chk(&argvars[0]);
2568 if (name == NULL)
2569 return;
2570
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002571 if (sign_undefine_by_name(name, TRUE) == OK)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002572 rettv->vval.v_number = 0;
2573 }
2574}
2575
2576/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002577 * Unplace the sign with attributes specified in 'dict'. Returns 0 on success
2578 * and -1 on failure.
2579 */
2580 static int
2581sign_unplace_from_dict(typval_T *group_tv, dict_T *dict)
2582{
2583 dictitem_T *di;
2584 int sign_id = 0;
2585 buf_T *buf = NULL;
2586 char_u *group = NULL;
2587 int retval = -1;
2588
2589 // sign group
2590 if (group_tv != NULL)
2591 group = tv_get_string(group_tv);
2592 else
2593 group = dict_get_string(dict, (char_u *)"group", FALSE);
2594 if (group != NULL)
2595 {
2596 if (group[0] == '\0') // global sign group
2597 group = NULL;
2598 else
2599 {
2600 group = vim_strsave(group);
2601 if (group == NULL)
2602 return -1;
2603 }
2604 }
2605
2606 if (dict != NULL)
2607 {
2608 if ((di = dict_find(dict, (char_u *)"buffer", -1)) != NULL)
2609 {
2610 buf = get_buf_arg(&di->di_tv);
2611 if (buf == NULL)
2612 goto cleanup;
2613 }
2614 if (dict_find(dict, (char_u *)"id", -1) != NULL)
2615 {
2616 sign_id = dict_get_number(dict, (char_u *)"id");
2617 if (sign_id <= 0)
2618 {
2619 emsg(_(e_invarg));
2620 goto cleanup;
2621 }
2622 }
2623 }
2624
2625 if (buf == NULL)
2626 {
2627 // Delete the sign in all the buffers
2628 retval = 0;
2629 FOR_ALL_BUFFERS(buf)
2630 if (sign_unplace(sign_id, group, buf, 0) != OK)
2631 retval = -1;
2632 }
2633 else if (sign_unplace(sign_id, group, buf, 0) == OK)
2634 retval = 0;
2635
2636cleanup:
2637 vim_free(group);
2638
2639 return retval;
2640}
2641
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002642 sign_entry_T *
2643get_first_valid_sign(win_T *wp)
2644{
2645 sign_entry_T *sign = wp->w_buffer->b_signlist;
2646
2647# ifdef FEAT_TEXT_PROP
2648 while (sign != NULL && sign_in_group(sign, (char_u *)"popupmenu")
2649 == (WIN_IS_POPUP(wp) ? FALSE : TRUE))
2650 sign = sign->se_next;
2651# endif
2652 return sign;
2653}
2654
2655/*
2656 * Return TRUE when window "wp" has a column to draw signs in.
2657 */
2658 int
2659signcolumn_on(win_T *wp)
2660{
2661 // If 'signcolumn' is set to 'number', signs are displayed in the 'number'
2662 // column (if present). Otherwise signs are to be displayed in the sign
2663 // column.
2664 if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u')
2665 return get_first_valid_sign(wp) != NULL && !wp->w_p_nu && !wp->w_p_rnu;
2666
2667 if (*wp->w_p_scl == 'n')
2668 return FALSE;
2669 if (*wp->w_p_scl == 'y')
2670 return TRUE;
2671 return (get_first_valid_sign(wp) != NULL
2672# ifdef FEAT_NETBEANS_INTG
2673 || wp->w_buffer->b_has_sign_column
2674# endif
2675 );
2676}
2677
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002678/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002679 * "sign_unplace()" function
2680 */
2681 void
2682f_sign_unplace(typval_T *argvars, typval_T *rettv)
2683{
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002684 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002685
2686 rettv->vval.v_number = -1;
2687
2688 if (argvars[0].v_type != VAR_STRING)
2689 {
2690 emsg(_(e_invarg));
2691 return;
2692 }
2693
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002694 if (argvars[1].v_type != VAR_UNKNOWN)
2695 {
2696 if (argvars[1].v_type != VAR_DICT)
2697 {
2698 emsg(_(e_dictreq));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002699 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002700 }
2701 dict = argvars[1].vval.v_dict;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002702 }
2703
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002704 rettv->vval.v_number = sign_unplace_from_dict(&argvars[0], dict);
2705}
2706
2707/*
2708 * "sign_unplacelist()" function
2709 */
2710 void
2711f_sign_unplacelist(typval_T *argvars, typval_T *rettv)
2712{
2713 listitem_T *li;
2714 int retval;
2715
2716 if (rettv_list_alloc(rettv) != OK)
2717 return;
2718
2719 if (argvars[0].v_type != VAR_LIST)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002720 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002721 emsg(_(e_listreq));
2722 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002723 }
2724
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002725 for (li = argvars[0].vval.v_list->lv_first; li != NULL; li = li->li_next)
2726 {
2727 retval = -1;
2728 if (li->li_tv.v_type == VAR_DICT)
2729 retval = sign_unplace_from_dict(NULL, li->li_tv.vval.v_dict);
2730 else
2731 emsg(_(e_dictreq));
2732 list_append_number(rettv->vval.v_list, retval);
2733 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002734}
2735
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002736#endif /* FEAT_SIGNS */