blob: f3a1f9df9e916eb5f0579fb53c0c76147f003fb3 [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
Bram Moolenaar72570732019-11-30 14:21:53 +0100141 && STRCMP(group, sign->se_group->sg_name) == 0));
142}
143
144/*
145 * Return TRUE if "sign" is to be displayed in window "wp".
146 * If the group name starts with "PopUp" it only shows in a popup window.
147 */
148 static int
149sign_group_for_window(sign_entry_T *sign, win_T *wp)
150{
151 int for_popup = sign->se_group != NULL
152 && STRNCMP("PopUp", sign->se_group->sg_name, 5) == 0;
153
154 return WIN_IS_POPUP(wp) ? for_popup : !for_popup;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100155}
156
157/*
158 * Get the next free sign identifier in the specified group
159 */
160 static int
161sign_group_get_next_signid(buf_T *buf, char_u *groupname)
162{
163 int id = 1;
164 signgroup_T *group = NULL;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200165 sign_entry_T *sign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100166 hashitem_T *hi;
167 int found = FALSE;
168
169 if (groupname != NULL)
170 {
171 hi = hash_find(&sg_table, groupname);
172 if (HASHITEM_EMPTY(hi))
173 return id;
174 group = HI2SG(hi);
175 }
176
Bram Moolenaarb5443cc2019-01-15 20:19:40 +0100177 // Search for the next usable sign identifier
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100178 while (!found)
179 {
180 if (group == NULL)
181 id = next_sign_id++; // global group
182 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200183 id = group->sg_next_sign_id++;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100184
185 // Check whether this sign is already placed in the buffer
186 found = TRUE;
187 FOR_ALL_SIGNS_IN_BUF(buf, sign)
188 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200189 if (id == sign->se_id && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100190 {
191 found = FALSE; // sign identifier is in use
192 break;
193 }
194 }
195 }
196
197 return id;
198}
199
200/*
201 * Insert a new sign into the signlist for buffer 'buf' between the 'prev' and
202 * 'next' signs.
203 */
204 static void
205insert_sign(
206 buf_T *buf, // buffer to store sign in
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200207 sign_entry_T *prev, // previous sign entry
208 sign_entry_T *next, // next sign entry
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100209 int id, // sign ID
210 char_u *group, // sign group; NULL for global group
211 int prio, // sign priority
212 linenr_T lnum, // line number which gets the mark
213 int typenr) // typenr of sign we are adding
214{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200215 sign_entry_T *newsign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100216
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200217 newsign = lalloc_id(sizeof(sign_entry_T), FALSE, aid_insert_sign);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100218 if (newsign != NULL)
219 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200220 newsign->se_id = id;
221 newsign->se_lnum = lnum;
222 newsign->se_typenr = typenr;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100223 if (group != NULL)
224 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200225 newsign->se_group = sign_group_ref(group);
226 if (newsign->se_group == NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100227 {
228 vim_free(newsign);
229 return;
230 }
231 }
232 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200233 newsign->se_group = NULL;
234 newsign->se_priority = prio;
235 newsign->se_next = next;
236 newsign->se_prev = prev;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100237 if (next != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200238 next->se_prev = newsign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100239
240 if (prev == NULL)
241 {
242 // When adding first sign need to redraw the windows to create the
243 // column for signs.
244 if (buf->b_signlist == NULL)
245 {
246 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200247 changed_line_abv_curs();
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100248 }
249
250 // first sign in signlist
251 buf->b_signlist = newsign;
252#ifdef FEAT_NETBEANS_INTG
253 if (netbeans_active())
254 buf->b_has_sign_column = TRUE;
255#endif
256 }
257 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200258 prev->se_next = newsign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100259 }
260}
261
262/*
263 * Insert a new sign sorted by line number and sign priority.
264 */
265 static void
266insert_sign_by_lnum_prio(
267 buf_T *buf, // buffer to store sign in
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200268 sign_entry_T *prev, // previous sign entry
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100269 int id, // sign ID
270 char_u *group, // sign group; NULL for global group
271 int prio, // sign priority
272 linenr_T lnum, // line number which gets the mark
273 int typenr) // typenr of sign we are adding
274{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200275 sign_entry_T *sign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100276
277 // keep signs sorted by lnum and by priority: insert new sign at
278 // the proper position in the list for this lnum.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200279 while (prev != NULL && prev->se_lnum == lnum && prev->se_priority <= prio)
280 prev = prev->se_prev;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100281 if (prev == NULL)
282 sign = buf->b_signlist;
283 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200284 sign = prev->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100285
286 insert_sign(buf, prev, sign, id, group, prio, lnum, typenr);
287}
288
289/*
Bram Moolenaar4e038572019-07-04 18:28:35 +0200290 * Lookup a sign by typenr. Returns NULL if sign is not found.
291 */
292 static sign_T *
293find_sign_by_typenr(int typenr)
294{
295 sign_T *sp;
296
297 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
298 if (sp->sn_typenr == typenr)
299 return sp;
300 return NULL;
301}
302
303/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100304 * Get the name of a sign by its typenr.
305 */
306 static char_u *
307sign_typenr2name(int typenr)
308{
309 sign_T *sp;
310
311 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
312 if (sp->sn_typenr == typenr)
313 return sp->sn_name;
314 return (char_u *)_("[Deleted]");
315}
316
317/*
318 * Return information about a sign in a Dict
319 */
320 static dict_T *
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200321sign_get_info(sign_entry_T *sign)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100322{
323 dict_T *d;
324
325 if ((d = dict_alloc_id(aid_sign_getinfo)) == NULL)
326 return NULL;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200327 dict_add_number(d, "id", sign->se_id);
328 dict_add_string(d, "group", (sign->se_group == NULL) ?
329 (char_u *)"" : sign->se_group->sg_name);
330 dict_add_number(d, "lnum", sign->se_lnum);
331 dict_add_string(d, "name", sign_typenr2name(sign->se_typenr));
332 dict_add_number(d, "priority", sign->se_priority);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100333
334 return d;
335}
336
337/*
Bram Moolenaar64416122019-06-07 21:37:13 +0200338 * Sort the signs placed on the same line as "sign" by priority. Invoked after
339 * changing the priority of an already placed sign. Assumes the signs in the
340 * buffer are sorted by line number and priority.
341 */
342 static void
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200343sign_sort_by_prio_on_line(buf_T *buf, sign_entry_T *sign)
Bram Moolenaar64416122019-06-07 21:37:13 +0200344{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200345 sign_entry_T *p = NULL;
Bram Moolenaar64416122019-06-07 21:37:13 +0200346
347 // If there is only one sign in the buffer or only one sign on the line or
348 // the sign is already sorted by priority, then return.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200349 if ((sign->se_prev == NULL
350 || sign->se_prev->se_lnum != sign->se_lnum
351 || sign->se_prev->se_priority > sign->se_priority)
352 && (sign->se_next == NULL
353 || sign->se_next->se_lnum != sign->se_lnum
354 || sign->se_next->se_priority < sign->se_priority))
Bram Moolenaar64416122019-06-07 21:37:13 +0200355 return;
356
357 // One or more signs on the same line as 'sign'
358 // Find a sign after which 'sign' should be inserted
359
360 // First search backward for a sign with higher priority on the same line
361 p = sign;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200362 while (p->se_prev != NULL && p->se_prev->se_lnum == sign->se_lnum
363 && p->se_prev->se_priority <= sign->se_priority)
364 p = p->se_prev;
Bram Moolenaar64416122019-06-07 21:37:13 +0200365
366 if (p == sign)
367 {
368 // Sign not found. Search forward for a sign with priority just before
369 // 'sign'.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200370 p = sign->se_next;
371 while (p->se_next != NULL && p->se_next->se_lnum == sign->se_lnum
372 && p->se_next->se_priority > sign->se_priority)
373 p = p->se_next;
Bram Moolenaar64416122019-06-07 21:37:13 +0200374 }
375
376 // Remove 'sign' from the list
377 if (buf->b_signlist == sign)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200378 buf->b_signlist = sign->se_next;
379 if (sign->se_prev != NULL)
380 sign->se_prev->se_next = sign->se_next;
381 if (sign->se_next != NULL)
382 sign->se_next->se_prev = sign->se_prev;
383 sign->se_prev = NULL;
384 sign->se_next = NULL;
Bram Moolenaar64416122019-06-07 21:37:13 +0200385
386 // Re-insert 'sign' at the right place
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200387 if (p->se_priority <= sign->se_priority)
Bram Moolenaar64416122019-06-07 21:37:13 +0200388 {
389 // 'sign' has a higher priority and should be inserted before 'p'
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200390 sign->se_prev = p->se_prev;
391 sign->se_next = p;
392 p->se_prev = sign;
393 if (sign->se_prev != NULL)
394 sign->se_prev->se_next = sign;
Bram Moolenaar64416122019-06-07 21:37:13 +0200395 if (buf->b_signlist == p)
396 buf->b_signlist = sign;
397 }
398 else
399 {
400 // 'sign' has a lower priority and should be inserted after 'p'
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200401 sign->se_prev = p;
402 sign->se_next = p->se_next;
403 p->se_next = sign;
404 if (sign->se_next != NULL)
405 sign->se_next->se_prev = sign;
Bram Moolenaar64416122019-06-07 21:37:13 +0200406 }
407}
408
409/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100410 * Add the sign into the signlist. Find the right spot to do it though.
411 */
412 static void
413buf_addsign(
414 buf_T *buf, // buffer to store sign in
415 int id, // sign ID
416 char_u *groupname, // sign group
417 int prio, // sign priority
418 linenr_T lnum, // line number which gets the mark
419 int typenr) // typenr of sign we are adding
420{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200421 sign_entry_T *sign; // a sign in the signlist
422 sign_entry_T *prev; // the previous sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100423
424 prev = NULL;
425 FOR_ALL_SIGNS_IN_BUF(buf, sign)
426 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200427 if (lnum == sign->se_lnum && id == sign->se_id
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100428 && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100429 {
430 // Update an existing sign
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200431 sign->se_typenr = typenr;
432 sign->se_priority = prio;
Bram Moolenaar64416122019-06-07 21:37:13 +0200433 sign_sort_by_prio_on_line(buf, sign);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100434 return;
435 }
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200436 else if (lnum < sign->se_lnum)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100437 {
438 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio,
439 lnum, typenr);
440 return;
441 }
442 prev = sign;
443 }
444
445 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr);
446 return;
447}
448
449/*
450 * For an existing, placed sign "markId" change the type to "typenr".
451 * Returns the line number of the sign, or zero if the sign is not found.
452 */
453 static linenr_T
454buf_change_sign_type(
455 buf_T *buf, // buffer to store sign in
456 int markId, // sign ID
457 char_u *group, // sign group
Bram Moolenaar809ce4d2019-07-13 21:21:40 +0200458 int typenr, // typenr of sign we are adding
459 int prio) // sign priority
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100460{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200461 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100462
463 FOR_ALL_SIGNS_IN_BUF(buf, sign)
464 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200465 if (sign->se_id == markId && sign_in_group(sign, group))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100466 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200467 sign->se_typenr = typenr;
468 sign->se_priority = prio;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +0200469 sign_sort_by_prio_on_line(buf, sign);
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200470 return sign->se_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100471 }
472 }
473
474 return (linenr_T)0;
475}
476
477/*
Bram Moolenaar4e038572019-07-04 18:28:35 +0200478 * Return the attributes of the first sign placed on line 'lnum' in buffer
479 * 'buf'. Used when refreshing the screen. Returns TRUE if a sign is found on
480 * 'lnum', FALSE otherwise.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100481 */
482 int
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100483buf_get_signattrs(win_T *wp, linenr_T lnum, sign_attrs_T *sattr)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100484{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200485 sign_entry_T *sign;
486 sign_T *sp;
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100487 buf_T *buf = wp->w_buffer;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200488
489 vim_memset(sattr, 0, sizeof(sign_attrs_T));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100490
491 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200492 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200493 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200494 // Signs are sorted by line number in the buffer. No need to check
495 // for signs after the specified line number 'lnum'.
496 break;
497
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100498 if (sign->se_lnum == lnum
499# ifdef FEAT_TEXT_PROP
Bram Moolenaar72570732019-11-30 14:21:53 +0100500 && sign_group_for_window(sign, wp)
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100501# endif
502 )
Bram Moolenaar4e038572019-07-04 18:28:35 +0200503 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200504 sattr->sat_typenr = sign->se_typenr;
505 sp = find_sign_by_typenr(sign->se_typenr);
Bram Moolenaar4e038572019-07-04 18:28:35 +0200506 if (sp == NULL)
507 return FALSE;
508
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100509# ifdef FEAT_SIGN_ICONS
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200510 sattr->sat_icon = sp->sn_image;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100511# endif
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200512 sattr->sat_text = sp->sn_text;
513 if (sattr->sat_text != NULL && sp->sn_text_hl > 0)
514 sattr->sat_texthl = syn_id2attr(sp->sn_text_hl);
Bram Moolenaar4e038572019-07-04 18:28:35 +0200515 if (sp->sn_line_hl > 0)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200516 sattr->sat_linehl = syn_id2attr(sp->sn_line_hl);
Bram Moolenaar4e038572019-07-04 18:28:35 +0200517 return TRUE;
518 }
519 }
520 return FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100521}
522
523/*
524 * Delete sign 'id' in group 'group' from buffer 'buf'.
525 * If 'id' is zero, then delete all the signs in group 'group'. Otherwise
526 * delete only the specified sign.
527 * If 'group' is '*', then delete the sign in all the groups. If 'group' is
528 * NULL, then delete the sign in the global group. Otherwise delete the sign in
529 * the specified group.
530 * Returns the line number of the deleted sign. If multiple signs are deleted,
531 * then returns the line number of the last sign deleted.
532 */
533 linenr_T
534buf_delsign(
535 buf_T *buf, // buffer sign is stored in
536 linenr_T atlnum, // sign at this line, 0 - at any line
537 int id, // sign id
538 char_u *group) // sign group
539{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200540 sign_entry_T **lastp; // pointer to pointer to current sign
541 sign_entry_T *sign; // a sign in a b_signlist
542 sign_entry_T *next; // the next sign in a b_signlist
543 linenr_T lnum; // line number whose sign was deleted
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100544
545 lastp = &buf->b_signlist;
546 lnum = 0;
547 for (sign = buf->b_signlist; sign != NULL; sign = next)
548 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200549 next = sign->se_next;
550 if ((id == 0 || sign->se_id == id)
551 && (atlnum == 0 || sign->se_lnum == atlnum)
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100552 && sign_in_group(sign, group))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100553
554 {
555 *lastp = next;
556 if (next != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200557 next->se_prev = sign->se_prev;
558 lnum = sign->se_lnum;
559 if (sign->se_group != NULL)
560 sign_group_unref(sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100561 vim_free(sign);
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100562 redraw_buf_line_later(buf, lnum);
563
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100564 // Check whether only one sign needs to be deleted
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100565 // If deleting a sign with a specific identifier in a particular
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100566 // group or deleting any sign at a particular line number, delete
567 // only one sign.
568 if (group == NULL
569 || (*group != '*' && id != 0)
570 || (*group == '*' && atlnum != 0))
571 break;
572 }
573 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200574 lastp = &sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100575 }
576
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100577 // When deleting the last sign the cursor position may change, because the
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100578 // sign columns no longer shows. And the 'signcolumn' may be hidden.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100579 if (buf->b_signlist == NULL)
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100580 {
581 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200582 changed_line_abv_curs();
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100583 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100584
585 return lnum;
586}
587
588
589/*
590 * Find the line number of the sign with the requested id in group 'group'. If
591 * the sign does not exist, return 0 as the line number. This will still let
592 * the correct file get loaded.
593 */
594 int
595buf_findsign(
596 buf_T *buf, // buffer to store sign in
597 int id, // sign ID
598 char_u *group) // sign group
599{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200600 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100601
602 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200603 if (sign->se_id == id && sign_in_group(sign, group))
604 return sign->se_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100605
606 return 0;
607}
608
609/*
610 * Return the sign at line 'lnum' in buffer 'buf'. Returns NULL if a sign is
611 * not found at the line. If 'groupname' is NULL, searches in the global group.
612 */
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200613 static sign_entry_T *
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100614buf_getsign_at_line(
615 buf_T *buf, // buffer whose sign we are searching for
616 linenr_T lnum, // line number of sign
617 char_u *groupname) // sign group name
618{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200619 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100620
621 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200622 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200623 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200624 // Signs are sorted by line number in the buffer. No need to check
625 // for signs after the specified line number 'lnum'.
626 break;
627
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200628 if (sign->se_lnum == lnum && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100629 return sign;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200630 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100631
632 return NULL;
633}
634
635/*
636 * Return the identifier of the sign at line number 'lnum' in buffer 'buf'.
637 */
638 int
639buf_findsign_id(
640 buf_T *buf, // buffer whose sign we are searching for
641 linenr_T lnum, // line number of sign
642 char_u *groupname) // sign group name
643{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200644 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100645
646 sign = buf_getsign_at_line(buf, lnum, groupname);
647 if (sign != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200648 return sign->se_id;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100649
650 return 0;
651}
652
653# if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
654/*
655 * See if a given type of sign exists on a specific line.
656 */
657 int
658buf_findsigntype_id(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100659 buf_T *buf, // buffer whose sign we are searching for
660 linenr_T lnum, // line number of sign
661 int typenr) // sign type number
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100662{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200663 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100664
665 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200666 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200667 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200668 // Signs are sorted by line number in the buffer. No need to check
669 // for signs after the specified line number 'lnum'.
670 break;
671
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200672 if (sign->se_lnum == lnum && sign->se_typenr == typenr)
673 return sign->se_id;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200674 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100675
676 return 0;
677}
678
679
680# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
681/*
682 * Return the number of icons on the given line.
683 */
684 int
685buf_signcount(buf_T *buf, linenr_T lnum)
686{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200687 sign_entry_T *sign; // a sign in the signlist
688 int count = 0;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100689
690 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200691 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200692 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200693 // Signs are sorted by line number in the buffer. No need to check
694 // for signs after the specified line number 'lnum'.
695 break;
696
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200697 if (sign->se_lnum == lnum)
698 if (sign_get_image(sign->se_typenr) != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100699 count++;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200700 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100701
702 return count;
703}
704# endif /* FEAT_SIGN_ICONS */
705# endif /* FEAT_NETBEANS_INTG */
706
707/*
708 * Delete signs in group 'group' in buffer "buf". If 'group' is '*', then
709 * delete all the signs.
710 */
711 void
712buf_delete_signs(buf_T *buf, char_u *group)
713{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200714 sign_entry_T *sign;
715 sign_entry_T **lastp; // pointer to pointer to current sign
716 sign_entry_T *next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100717
718 // When deleting the last sign need to redraw the windows to remove the
719 // sign column. Not when curwin is NULL (this means we're exiting).
720 if (buf->b_signlist != NULL && curwin != NULL)
721 {
722 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200723 changed_line_abv_curs();
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100724 }
725
726 lastp = &buf->b_signlist;
727 for (sign = buf->b_signlist; sign != NULL; sign = next)
728 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200729 next = sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100730 if (sign_in_group(sign, group))
731 {
732 *lastp = next;
733 if (next != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200734 next->se_prev = sign->se_prev;
735 if (sign->se_group != NULL)
736 sign_group_unref(sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100737 vim_free(sign);
738 }
739 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200740 lastp = &sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100741 }
742}
743
744/*
745 * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers.
746 */
747 static void
748sign_list_placed(buf_T *rbuf, char_u *sign_group)
749{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200750 buf_T *buf;
751 sign_entry_T *sign;
752 char lbuf[MSG_BUF_LEN];
753 char group[MSG_BUF_LEN];
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100754
Bram Moolenaar32526b32019-01-19 17:43:09 +0100755 msg_puts_title(_("\n--- Signs ---"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100756 msg_putchar('\n');
757 if (rbuf == NULL)
758 buf = firstbuf;
759 else
760 buf = rbuf;
761 while (buf != NULL && !got_int)
762 {
763 if (buf->b_signlist != NULL)
764 {
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100765 vim_snprintf(lbuf, MSG_BUF_LEN, _("Signs for %s:"), buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100766 msg_puts_attr(lbuf, HL_ATTR(HLF_D));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100767 msg_putchar('\n');
768 }
769 FOR_ALL_SIGNS_IN_BUF(buf, sign)
770 {
771 if (got_int)
772 break;
773 if (!sign_in_group(sign, sign_group))
774 continue;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200775 if (sign->se_group != NULL)
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100776 vim_snprintf(group, MSG_BUF_LEN, _(" group=%s"),
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200777 sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100778 else
779 group[0] = '\0';
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100780 vim_snprintf(lbuf, MSG_BUF_LEN,
781 _(" line=%ld id=%d%s name=%s priority=%d"),
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200782 (long)sign->se_lnum, sign->se_id, group,
783 sign_typenr2name(sign->se_typenr), sign->se_priority);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100784 msg_puts(lbuf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100785 msg_putchar('\n');
786 }
787 if (rbuf != NULL)
788 break;
789 buf = buf->b_next;
790 }
791}
792
793/*
794 * Adjust a placed sign for inserted/deleted lines.
795 */
796 void
797sign_mark_adjust(
798 linenr_T line1,
799 linenr_T line2,
800 long amount,
801 long amount_after)
802{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200803 sign_entry_T *sign; // a sign in a b_signlist
804 linenr_T new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100805
806 FOR_ALL_SIGNS_IN_BUF(curbuf, sign)
807 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100808 // Ignore changes to lines after the sign
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200809 if (sign->se_lnum < line1)
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100810 continue;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200811 new_lnum = sign->se_lnum;
812 if (sign->se_lnum >= line1 && sign->se_lnum <= line2)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100813 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100814 if (amount != MAXLNUM)
815 new_lnum += amount;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100816 }
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200817 else if (sign->se_lnum > line2)
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100818 // Lines inserted or deleted before the sign
819 new_lnum += amount_after;
820
821 // If the new sign line number is past the last line in the buffer,
822 // then don't adjust the line number. Otherwise, it will always be past
823 // the last line and will not be visible.
824 if (new_lnum <= curbuf->b_ml.ml_line_count)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200825 sign->se_lnum = new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100826 }
827}
828
829/*
830 * Find index of a ":sign" subcmd from its name.
831 * "*end_cmd" must be writable.
832 */
833 static int
834sign_cmd_idx(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100835 char_u *begin_cmd, // begin of sign subcmd
836 char_u *end_cmd) // just after sign subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100837{
838 int idx;
839 char save = *end_cmd;
840
841 *end_cmd = NUL;
842 for (idx = 0; ; ++idx)
843 if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0)
844 break;
845 *end_cmd = save;
846 return idx;
847}
848
849/*
850 * Find a sign by name. Also returns pointer to the previous sign.
851 */
852 static sign_T *
853sign_find(char_u *name, sign_T **sp_prev)
854{
855 sign_T *sp;
856
857 if (sp_prev != NULL)
858 *sp_prev = NULL;
859 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
860 {
861 if (STRCMP(sp->sn_name, name) == 0)
862 break;
863 if (sp_prev != NULL)
864 *sp_prev = sp;
865 }
866
867 return sp;
868}
869
870/*
Bram Moolenaar03142362019-01-18 22:01:42 +0100871 * Allocate a new sign
872 */
873 static sign_T *
874alloc_new_sign(char_u *name)
875{
876 sign_T *sp;
877 sign_T *lp;
878 int start = next_sign_typenr;
879
880 // Allocate a new sign.
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200881 sp = alloc_clear_id(sizeof(sign_T), aid_sign_define_by_name);
Bram Moolenaar03142362019-01-18 22:01:42 +0100882 if (sp == NULL)
883 return NULL;
884
885 // Check that next_sign_typenr is not already being used.
886 // This only happens after wrapping around. Hopefully
887 // another one got deleted and we can use its number.
888 for (lp = first_sign; lp != NULL; )
889 {
890 if (lp->sn_typenr == next_sign_typenr)
891 {
892 ++next_sign_typenr;
893 if (next_sign_typenr == MAX_TYPENR)
894 next_sign_typenr = 1;
895 if (next_sign_typenr == start)
896 {
897 vim_free(sp);
898 emsg(_("E612: Too many signs defined"));
899 return NULL;
900 }
901 lp = first_sign; // start all over
902 continue;
903 }
904 lp = lp->sn_next;
905 }
906
907 sp->sn_typenr = next_sign_typenr;
908 if (++next_sign_typenr == MAX_TYPENR)
909 next_sign_typenr = 1; // wrap around
910
911 sp->sn_name = vim_strsave(name);
912 if (sp->sn_name == NULL) // out of memory
913 {
914 vim_free(sp);
915 return NULL;
916 }
917
918 return sp;
919}
920
921/*
922 * Initialize the icon information for a new sign
923 */
924 static void
925sign_define_init_icon(sign_T *sp, char_u *icon)
926{
927 vim_free(sp->sn_icon);
928 sp->sn_icon = vim_strsave(icon);
929 backslash_halve(sp->sn_icon);
930# ifdef FEAT_SIGN_ICONS
931 if (gui.in_use)
932 {
933 out_flush();
934 if (sp->sn_image != NULL)
935 gui_mch_destroy_sign(sp->sn_image);
936 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
937 }
938# endif
939}
940
941/*
942 * Initialize the text for a new sign
943 */
944 static int
945sign_define_init_text(sign_T *sp, char_u *text)
946{
947 char_u *s;
948 char_u *endp;
949 int cells;
950 int len;
951
952 endp = text + (int)STRLEN(text);
953
954 // Remove backslashes so that it is possible to use a space.
955 for (s = text; s + 1 < endp; ++s)
956 if (*s == '\\')
957 {
958 STRMOVE(s, s + 1);
959 --endp;
960 }
961
962 // Count cells and check for non-printable chars
Bram Moolenaar03142362019-01-18 22:01:42 +0100963 if (has_mbyte)
964 {
965 cells = 0;
966 for (s = text; s < endp; s += (*mb_ptr2len)(s))
967 {
968 if (!vim_isprintc((*mb_ptr2char)(s)))
969 break;
970 cells += (*mb_ptr2cells)(s);
971 }
972 }
973 else
Bram Moolenaar03142362019-01-18 22:01:42 +0100974 {
975 for (s = text; s < endp; ++s)
976 if (!vim_isprintc(*s))
977 break;
978 cells = (int)(s - text);
979 }
980
981 // Currently sign text must be one or two display cells
982 if (s != endp || cells < 1 || cells > 2)
983 {
984 semsg(_("E239: Invalid sign text: %s"), text);
985 return FAIL;
986 }
987
988 vim_free(sp->sn_text);
989 // Allocate one byte more if we need to pad up
990 // with a space.
991 len = (int)(endp - text + ((cells == 1) ? 1 : 0));
992 sp->sn_text = vim_strnsave(text, len);
993
994 // For single character sign text, pad with a space.
995 if (sp->sn_text != NULL && cells == 1)
996 STRCPY(sp->sn_text + len - 1, " ");
997
998 return OK;
999}
1000
1001/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001002 * Define a new sign or update an existing sign
1003 */
1004 int
1005sign_define_by_name(
1006 char_u *name,
1007 char_u *icon,
1008 char_u *linehl,
1009 char_u *text,
1010 char_u *texthl)
1011{
1012 sign_T *sp_prev;
1013 sign_T *sp;
1014
1015 sp = sign_find(name, &sp_prev);
1016 if (sp == NULL)
1017 {
Bram Moolenaar03142362019-01-18 22:01:42 +01001018 sp = alloc_new_sign(name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001019 if (sp == NULL)
1020 return FAIL;
1021
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001022 // add the new sign to the list of signs
1023 if (sp_prev == NULL)
1024 first_sign = sp;
1025 else
1026 sp_prev->sn_next = sp;
1027 }
1028
1029 // set values for a defined sign.
1030 if (icon != NULL)
Bram Moolenaar03142362019-01-18 22:01:42 +01001031 sign_define_init_icon(sp, icon);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001032
Bram Moolenaar03142362019-01-18 22:01:42 +01001033 if (text != NULL && (sign_define_init_text(sp, text) == FAIL))
1034 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001035
1036 if (linehl != NULL)
1037 sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl));
1038
1039 if (texthl != NULL)
1040 sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl));
1041
1042 return OK;
1043}
1044
1045/*
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001046 * Return TRUE if sign "name" exists.
1047 */
1048 int
1049sign_exists_by_name(char_u *name)
1050{
1051 return sign_find(name, NULL) != NULL;
1052}
1053
1054/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001055 * Free the sign specified by 'name'.
1056 */
1057 int
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001058sign_undefine_by_name(char_u *name, int give_error)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001059{
1060 sign_T *sp_prev;
1061 sign_T *sp;
1062
1063 sp = sign_find(name, &sp_prev);
1064 if (sp == NULL)
1065 {
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001066 if (give_error)
1067 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001068 return FAIL;
1069 }
1070 sign_undefine(sp, sp_prev);
1071
1072 return OK;
1073}
1074
1075/*
1076 * List the signs matching 'name'
1077 */
1078 static void
1079sign_list_by_name(char_u *name)
1080{
1081 sign_T *sp;
1082
1083 sp = sign_find(name, NULL);
1084 if (sp != NULL)
1085 sign_list_defined(sp);
1086 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001087 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001088}
1089
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001090 static void
1091may_force_numberwidth_recompute(buf_T *buf, int unplace)
1092{
1093 tabpage_T *tp;
1094 win_T *wp;
1095
1096 FOR_ALL_TAB_WINDOWS(tp, wp)
1097 if (wp->w_buffer == buf
1098 && (wp->w_p_nu || wp->w_p_rnu)
1099 && (unplace || wp->w_nrwidth_width < 2)
1100 && (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u'))
1101 wp->w_nrwidth_line_count = 0;
1102}
1103
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001104/*
Bram Moolenaar8144acb2019-01-14 23:08:18 +01001105 * Place a sign at the specified file location or update a sign.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001106 */
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001107 int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001108sign_place(
1109 int *sign_id,
1110 char_u *sign_group,
1111 char_u *sign_name,
1112 buf_T *buf,
1113 linenr_T lnum,
1114 int prio)
1115{
1116 sign_T *sp;
1117
1118 // Check for reserved character '*' in group name
1119 if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0'))
1120 return FAIL;
1121
1122 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1123 if (STRCMP(sp->sn_name, sign_name) == 0)
1124 break;
1125 if (sp == NULL)
1126 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001127 semsg(_("E155: Unknown sign: %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001128 return FAIL;
1129 }
1130 if (*sign_id == 0)
1131 *sign_id = sign_group_get_next_signid(buf, sign_group);
1132
1133 if (lnum > 0)
1134 // ":sign place {id} line={lnum} name={name} file={fname}":
1135 // place a sign
1136 buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr);
1137 else
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02001138 // ":sign place {id} file={fname}": change sign type and/or priority
1139 lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr,
1140 prio);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001141 if (lnum > 0)
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001142 {
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001143 redraw_buf_line_later(buf, lnum);
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001144
1145 // When displaying signs in the 'number' column, if the width of the
1146 // number column is less than 2, then force recomputing the width.
1147 may_force_numberwidth_recompute(buf, FALSE);
1148 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001149 else
1150 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001151 semsg(_("E885: Not possible to change sign %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001152 return FAIL;
1153 }
1154
1155 return OK;
1156}
1157
1158/*
1159 * Unplace the specified sign
1160 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001161 static int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001162sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum)
1163{
1164 if (buf->b_signlist == NULL) // No signs in the buffer
1165 return OK;
1166
1167 if (sign_id == 0)
1168 {
1169 // Delete all the signs in the specified buffer
1170 redraw_buf_later(buf, NOT_VALID);
1171 buf_delete_signs(buf, sign_group);
1172 }
1173 else
1174 {
1175 linenr_T lnum;
1176
1177 // Delete only the specified signs
1178 lnum = buf_delsign(buf, atlnum, sign_id, sign_group);
1179 if (lnum == 0)
1180 return FAIL;
1181 }
1182
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001183 // When all the signs in a buffer are removed, force recomputing the
1184 // number column width (if enabled) in all the windows displaying the
1185 // buffer if 'signcolumn' is set to 'number' in that window.
1186 if (buf->b_signlist == NULL)
1187 may_force_numberwidth_recompute(buf, TRUE);
1188
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001189 return OK;
1190}
1191
1192/*
1193 * Unplace the sign at the current cursor line.
1194 */
1195 static void
1196sign_unplace_at_cursor(char_u *groupname)
1197{
1198 int id = -1;
1199
1200 id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname);
1201 if (id > 0)
1202 sign_unplace(id, groupname, curwin->w_buffer, curwin->w_cursor.lnum);
1203 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001204 emsg(_("E159: Missing sign number"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001205}
1206
1207/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001208 * Jump to a sign.
1209 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001210 static linenr_T
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001211sign_jump(int sign_id, char_u *sign_group, buf_T *buf)
1212{
1213 linenr_T lnum;
1214
1215 if ((lnum = buf_findsign(buf, sign_id, sign_group)) <= 0)
1216 {
Bram Moolenaarb5443cc2019-01-15 20:19:40 +01001217 semsg(_("E157: Invalid sign ID: %d"), sign_id);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001218 return -1;
1219 }
1220
1221 // goto a sign ...
1222 if (buf_jump_open_win(buf) != NULL)
1223 { // ... in a current window
1224 curwin->w_cursor.lnum = lnum;
1225 check_cursor_lnum();
1226 beginline(BL_WHITE);
1227 }
1228 else
1229 { // ... not currently in a window
1230 char_u *cmd;
1231
1232 if (buf->b_fname == NULL)
1233 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001234 emsg(_("E934: Cannot jump to a buffer that does not have a name"));
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001235 return -1;
1236 }
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02001237 cmd = alloc(STRLEN(buf->b_fname) + 25);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001238 if (cmd == NULL)
1239 return -1;
1240 sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname);
1241 do_cmdline_cmd(cmd);
1242 vim_free(cmd);
1243 }
1244# ifdef FEAT_FOLDING
1245 foldOpenCursor();
1246# endif
1247
1248 return lnum;
1249}
1250
1251/*
1252 * ":sign define {name} ..." command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001253 */
1254 static void
1255sign_define_cmd(char_u *sign_name, char_u *cmdline)
1256{
1257 char_u *arg;
1258 char_u *p = cmdline;
1259 char_u *icon = NULL;
1260 char_u *text = NULL;
1261 char_u *linehl = NULL;
1262 char_u *texthl = NULL;
1263 int failed = FALSE;
1264
1265 // set values for a defined sign.
1266 for (;;)
1267 {
1268 arg = skipwhite(p);
1269 if (*arg == NUL)
1270 break;
1271 p = skiptowhite_esc(arg);
1272 if (STRNCMP(arg, "icon=", 5) == 0)
1273 {
1274 arg += 5;
1275 icon = vim_strnsave(arg, (int)(p - arg));
1276 }
1277 else if (STRNCMP(arg, "text=", 5) == 0)
1278 {
1279 arg += 5;
1280 text = vim_strnsave(arg, (int)(p - arg));
1281 }
1282 else if (STRNCMP(arg, "linehl=", 7) == 0)
1283 {
1284 arg += 7;
1285 linehl = vim_strnsave(arg, (int)(p - arg));
1286 }
1287 else if (STRNCMP(arg, "texthl=", 7) == 0)
1288 {
1289 arg += 7;
1290 texthl = vim_strnsave(arg, (int)(p - arg));
1291 }
1292 else
1293 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001294 semsg(_(e_invarg2), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001295 failed = TRUE;
1296 break;
1297 }
1298 }
1299
1300 if (!failed)
1301 sign_define_by_name(sign_name, icon, linehl, text, texthl);
1302
1303 vim_free(icon);
1304 vim_free(text);
1305 vim_free(linehl);
1306 vim_free(texthl);
1307}
1308
1309/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001310 * ":sign place" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001311 */
1312 static void
1313sign_place_cmd(
1314 buf_T *buf,
1315 linenr_T lnum,
1316 char_u *sign_name,
1317 int id,
1318 char_u *group,
1319 int prio)
1320{
1321 if (id <= 0)
1322 {
1323 // List signs placed in a file/buffer
1324 // :sign place file={fname}
1325 // :sign place group={group} file={fname}
1326 // :sign place group=* file={fname}
1327 // :sign place buffer={nr}
1328 // :sign place group={group} buffer={nr}
1329 // :sign place group=* buffer={nr}
1330 // :sign place
1331 // :sign place group={group}
1332 // :sign place group=*
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001333 if (lnum >= 0 || sign_name != NULL
1334 || (group != NULL && *group == '\0'))
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001335 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001336 else
1337 sign_list_placed(buf, group);
1338 }
1339 else
1340 {
1341 // Place a new sign
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001342 if (sign_name == NULL || buf == NULL
1343 || (group != NULL && *group == '\0'))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001344 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001345 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001346 return;
1347 }
1348
1349 sign_place(&id, group, sign_name, buf, lnum, prio);
1350 }
1351}
1352
1353/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001354 * ":sign unplace" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001355 */
1356 static void
1357sign_unplace_cmd(
1358 buf_T *buf,
1359 linenr_T lnum,
1360 char_u *sign_name,
1361 int id,
1362 char_u *group)
1363{
1364 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0'))
1365 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001366 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001367 return;
1368 }
1369
1370 if (id == -2)
1371 {
1372 if (buf != NULL)
1373 // :sign unplace * file={fname}
1374 // :sign unplace * group={group} file={fname}
1375 // :sign unplace * group=* file={fname}
1376 // :sign unplace * buffer={nr}
1377 // :sign unplace * group={group} buffer={nr}
1378 // :sign unplace * group=* buffer={nr}
1379 sign_unplace(0, group, buf, 0);
1380 else
1381 // :sign unplace *
1382 // :sign unplace * group={group}
1383 // :sign unplace * group=*
1384 FOR_ALL_BUFFERS(buf)
1385 if (buf->b_signlist != NULL)
1386 buf_delete_signs(buf, group);
1387 }
1388 else
1389 {
1390 if (buf != NULL)
1391 // :sign unplace {id} file={fname}
1392 // :sign unplace {id} group={group} file={fname}
1393 // :sign unplace {id} group=* file={fname}
1394 // :sign unplace {id} buffer={nr}
1395 // :sign unplace {id} group={group} buffer={nr}
1396 // :sign unplace {id} group=* buffer={nr}
1397 sign_unplace(id, group, buf, 0);
1398 else
1399 {
1400 if (id == -1)
1401 {
1402 // :sign unplace group={group}
1403 // :sign unplace group=*
1404 sign_unplace_at_cursor(group);
1405 }
1406 else
1407 {
1408 // :sign unplace {id}
1409 // :sign unplace {id} group={group}
1410 // :sign unplace {id} group=*
1411 FOR_ALL_BUFFERS(buf)
1412 sign_unplace(id, group, buf, 0);
1413 }
1414 }
1415 }
1416}
1417
1418/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001419 * Jump to a placed sign commands:
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001420 * :sign jump {id} file={fname}
1421 * :sign jump {id} buffer={nr}
1422 * :sign jump {id} group={group} file={fname}
1423 * :sign jump {id} group={group} buffer={nr}
1424 */
1425 static void
1426sign_jump_cmd(
1427 buf_T *buf,
1428 linenr_T lnum,
1429 char_u *sign_name,
1430 int id,
1431 char_u *group)
1432{
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001433 if (sign_name == NULL && group == NULL && id == -1)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001434 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001435 emsg(_(e_argreq));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001436 return;
1437 }
1438
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001439 if (buf == NULL || (group != NULL && *group == '\0')
1440 || lnum >= 0 || sign_name != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001441 {
1442 // File or buffer is not specified or an empty group is used
1443 // or a line number or a sign name is specified.
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001444 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001445 return;
1446 }
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001447 (void)sign_jump(id, group, buf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001448}
1449
1450/*
1451 * Parse the command line arguments for the ":sign place", ":sign unplace" and
1452 * ":sign jump" commands.
1453 * The supported arguments are: line={lnum} name={name} group={group}
1454 * priority={prio} and file={fname} or buffer={nr}.
1455 */
1456 static int
1457parse_sign_cmd_args(
1458 int cmd,
1459 char_u *arg,
1460 char_u **sign_name,
1461 int *signid,
1462 char_u **group,
1463 int *prio,
1464 buf_T **buf,
1465 linenr_T *lnum)
1466{
1467 char_u *arg1;
1468 char_u *name;
1469 char_u *filename = NULL;
Bram Moolenaarb589f952019-01-07 22:10:00 +01001470 int lnum_arg = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001471
1472 // first arg could be placed sign id
1473 arg1 = arg;
1474 if (VIM_ISDIGIT(*arg))
1475 {
1476 *signid = getdigits(&arg);
1477 if (!VIM_ISWHITE(*arg) && *arg != NUL)
1478 {
1479 *signid = -1;
1480 arg = arg1;
1481 }
1482 else
1483 arg = skipwhite(arg);
1484 }
1485
1486 while (*arg != NUL)
1487 {
1488 if (STRNCMP(arg, "line=", 5) == 0)
1489 {
1490 arg += 5;
1491 *lnum = atoi((char *)arg);
1492 arg = skiptowhite(arg);
Bram Moolenaarb589f952019-01-07 22:10:00 +01001493 lnum_arg = TRUE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001494 }
1495 else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE)
1496 {
1497 if (*signid != -1)
1498 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001499 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001500 return FAIL;
1501 }
1502 *signid = -2;
1503 arg = skiptowhite(arg + 1);
1504 }
1505 else if (STRNCMP(arg, "name=", 5) == 0)
1506 {
1507 arg += 5;
1508 name = arg;
1509 arg = skiptowhite(arg);
1510 if (*arg != NUL)
1511 *arg++ = NUL;
1512 while (name[0] == '0' && name[1] != NUL)
1513 ++name;
1514 *sign_name = name;
1515 }
1516 else if (STRNCMP(arg, "group=", 6) == 0)
1517 {
1518 arg += 6;
1519 *group = arg;
1520 arg = skiptowhite(arg);
1521 if (*arg != NUL)
1522 *arg++ = NUL;
1523 }
1524 else if (STRNCMP(arg, "priority=", 9) == 0)
1525 {
1526 arg += 9;
1527 *prio = atoi((char *)arg);
1528 arg = skiptowhite(arg);
1529 }
1530 else if (STRNCMP(arg, "file=", 5) == 0)
1531 {
1532 arg += 5;
1533 filename = arg;
1534 *buf = buflist_findname_exp(arg);
1535 break;
1536 }
1537 else if (STRNCMP(arg, "buffer=", 7) == 0)
1538 {
1539 arg += 7;
1540 filename = arg;
1541 *buf = buflist_findnr((int)getdigits(&arg));
1542 if (*skipwhite(arg) != NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001543 emsg(_(e_trailing));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001544 break;
1545 }
1546 else
1547 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001548 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001549 return FAIL;
1550 }
1551 arg = skipwhite(arg);
1552 }
1553
1554 if (filename != NULL && *buf == NULL)
1555 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001556 semsg(_("E158: Invalid buffer name: %s"), filename);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001557 return FAIL;
1558 }
1559
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001560 // If the filename is not supplied for the sign place or the sign jump
1561 // command, then use the current buffer.
Bram Moolenaarb589f952019-01-07 22:10:00 +01001562 if (filename == NULL && ((cmd == SIGNCMD_PLACE && lnum_arg)
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001563 || cmd == SIGNCMD_JUMP))
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001564 *buf = curwin->w_buffer;
1565
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001566 return OK;
1567}
1568
1569/*
1570 * ":sign" command
1571 */
1572 void
1573ex_sign(exarg_T *eap)
1574{
1575 char_u *arg = eap->arg;
1576 char_u *p;
1577 int idx;
1578 sign_T *sp;
1579 buf_T *buf = NULL;
1580
1581 // Parse the subcommand.
1582 p = skiptowhite(arg);
1583 idx = sign_cmd_idx(arg, p);
1584 if (idx == SIGNCMD_LAST)
1585 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001586 semsg(_("E160: Unknown sign command: %s"), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001587 return;
1588 }
1589 arg = skipwhite(p);
1590
1591 if (idx <= SIGNCMD_LIST)
1592 {
1593 // Define, undefine or list signs.
1594 if (idx == SIGNCMD_LIST && *arg == NUL)
1595 {
1596 // ":sign list": list all defined signs
1597 for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next)
1598 sign_list_defined(sp);
1599 }
1600 else if (*arg == NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001601 emsg(_("E156: Missing sign name"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001602 else
1603 {
1604 char_u *name;
1605
1606 // Isolate the sign name. If it's a number skip leading zeroes,
1607 // so that "099" and "99" are the same sign. But keep "0".
1608 p = skiptowhite(arg);
1609 if (*p != NUL)
1610 *p++ = NUL;
1611 while (arg[0] == '0' && arg[1] != NUL)
1612 ++arg;
1613 name = vim_strsave(arg);
1614
1615 if (idx == SIGNCMD_DEFINE)
1616 sign_define_cmd(name, p);
1617 else if (idx == SIGNCMD_LIST)
1618 // ":sign list {name}"
1619 sign_list_by_name(name);
1620 else
1621 // ":sign undefine {name}"
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001622 sign_undefine_by_name(name, TRUE);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001623
1624 vim_free(name);
1625 return;
1626 }
1627 }
1628 else
1629 {
1630 int id = -1;
1631 linenr_T lnum = -1;
1632 char_u *sign_name = NULL;
1633 char_u *group = NULL;
1634 int prio = SIGN_DEF_PRIO;
1635
1636 // Parse command line arguments
1637 if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio,
1638 &buf, &lnum) == FAIL)
1639 return;
1640
1641 if (idx == SIGNCMD_PLACE)
1642 sign_place_cmd(buf, lnum, sign_name, id, group, prio);
1643 else if (idx == SIGNCMD_UNPLACE)
1644 sign_unplace_cmd(buf, lnum, sign_name, id, group);
1645 else if (idx == SIGNCMD_JUMP)
1646 sign_jump_cmd(buf, lnum, sign_name, id, group);
1647 }
1648}
1649
1650/*
1651 * Return information about a specified sign
1652 */
1653 static void
1654sign_getinfo(sign_T *sp, dict_T *retdict)
1655{
1656 char_u *p;
1657
1658 dict_add_string(retdict, "name", (char_u *)sp->sn_name);
1659 if (sp->sn_icon != NULL)
1660 dict_add_string(retdict, "icon", (char_u *)sp->sn_icon);
1661 if (sp->sn_text != NULL)
1662 dict_add_string(retdict, "text", (char_u *)sp->sn_text);
1663 if (sp->sn_line_hl > 0)
1664 {
1665 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1666 if (p == NULL)
1667 p = (char_u *)"NONE";
1668 dict_add_string(retdict, "linehl", (char_u *)p);
1669 }
1670 if (sp->sn_text_hl > 0)
1671 {
1672 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1673 if (p == NULL)
1674 p = (char_u *)"NONE";
1675 dict_add_string(retdict, "texthl", (char_u *)p);
1676 }
1677}
1678
1679/*
1680 * If 'name' is NULL, return a list of all the defined signs.
1681 * Otherwise, return information about the specified sign.
1682 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001683 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001684sign_getlist(char_u *name, list_T *retlist)
1685{
1686 sign_T *sp = first_sign;
1687 dict_T *dict;
1688
1689 if (name != NULL)
1690 {
1691 sp = sign_find(name, NULL);
1692 if (sp == NULL)
1693 return;
1694 }
1695
1696 for (; sp != NULL && !got_int; sp = sp->sn_next)
1697 {
1698 if ((dict = dict_alloc_id(aid_sign_getlist)) == NULL)
1699 return;
1700 if (list_append_dict(retlist, dict) == FAIL)
1701 return;
1702 sign_getinfo(sp, dict);
1703
1704 if (name != NULL) // handle only the specified sign
1705 break;
1706 }
1707}
1708
1709/*
1710 * Returns information about signs placed in a buffer as list of dicts.
1711 */
1712 void
1713get_buffer_signs(buf_T *buf, list_T *l)
1714{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001715 sign_entry_T *sign;
1716 dict_T *d;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001717
1718 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1719 {
1720 if ((d = sign_get_info(sign)) != NULL)
1721 list_append_dict(l, d);
1722 }
1723}
1724
1725/*
1726 * Return information about all the signs placed in a buffer
1727 */
1728 static void
1729sign_get_placed_in_buf(
1730 buf_T *buf,
1731 linenr_T lnum,
1732 int sign_id,
1733 char_u *sign_group,
1734 list_T *retlist)
1735{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001736 dict_T *d;
1737 list_T *l;
1738 sign_entry_T *sign;
1739 dict_T *sdict;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001740
1741 if ((d = dict_alloc_id(aid_sign_getplaced_dict)) == NULL)
1742 return;
1743 list_append_dict(retlist, d);
1744
1745 dict_add_number(d, "bufnr", (long)buf->b_fnum);
1746
1747 if ((l = list_alloc_id(aid_sign_getplaced_list)) == NULL)
1748 return;
1749 dict_add_list(d, "signs", l);
1750
1751 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1752 {
1753 if (!sign_in_group(sign, sign_group))
1754 continue;
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001755 if ((lnum == 0 && sign_id == 0)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001756 || (sign_id == 0 && lnum == sign->se_lnum)
1757 || (lnum == 0 && sign_id == sign->se_id)
1758 || (lnum == sign->se_lnum && sign_id == sign->se_id))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001759 {
1760 if ((sdict = sign_get_info(sign)) != NULL)
1761 list_append_dict(l, sdict);
1762 }
1763 }
1764}
1765
1766/*
1767 * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the
1768 * sign placed at the line number. If 'lnum' is zero, return all the signs
1769 * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers.
1770 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001771 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001772sign_get_placed(
1773 buf_T *buf,
1774 linenr_T lnum,
1775 int sign_id,
1776 char_u *sign_group,
1777 list_T *retlist)
1778{
1779 if (buf != NULL)
1780 sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist);
1781 else
1782 {
1783 FOR_ALL_BUFFERS(buf)
1784 {
1785 if (buf->b_signlist != NULL)
1786 sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist);
1787 }
1788 }
1789}
1790
1791# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1792/*
1793 * Allocate the icons. Called when the GUI has started. Allows defining
1794 * signs before it starts.
1795 */
1796 void
1797sign_gui_started(void)
1798{
1799 sign_T *sp;
1800
1801 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1802 if (sp->sn_icon != NULL)
1803 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
1804}
1805# endif
1806
1807/*
1808 * List one sign.
1809 */
1810 static void
1811sign_list_defined(sign_T *sp)
1812{
1813 char_u *p;
1814
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001815 smsg("sign %s", sp->sn_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001816 if (sp->sn_icon != NULL)
1817 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001818 msg_puts(" icon=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001819 msg_outtrans(sp->sn_icon);
1820# ifdef FEAT_SIGN_ICONS
1821 if (sp->sn_image == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001822 msg_puts(_(" (NOT FOUND)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001823# else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001824 msg_puts(_(" (not supported)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001825# endif
1826 }
1827 if (sp->sn_text != NULL)
1828 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001829 msg_puts(" text=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001830 msg_outtrans(sp->sn_text);
1831 }
1832 if (sp->sn_line_hl > 0)
1833 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001834 msg_puts(" linehl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001835 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1836 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001837 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001838 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001839 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001840 }
1841 if (sp->sn_text_hl > 0)
1842 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001843 msg_puts(" texthl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001844 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1845 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001846 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001847 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001848 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001849 }
1850}
1851
1852/*
1853 * Undefine a sign and free its memory.
1854 */
1855 static void
1856sign_undefine(sign_T *sp, sign_T *sp_prev)
1857{
1858 vim_free(sp->sn_name);
1859 vim_free(sp->sn_icon);
1860# ifdef FEAT_SIGN_ICONS
1861 if (sp->sn_image != NULL)
1862 {
1863 out_flush();
1864 gui_mch_destroy_sign(sp->sn_image);
1865 }
1866# endif
1867 vim_free(sp->sn_text);
1868 if (sp_prev == NULL)
1869 first_sign = sp->sn_next;
1870 else
1871 sp_prev->sn_next = sp->sn_next;
1872 vim_free(sp);
1873}
1874
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001875# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1876 void *
1877sign_get_image(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001878 int typenr) // the attribute which may have a sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001879{
1880 sign_T *sp;
1881
1882 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1883 if (sp->sn_typenr == typenr)
1884 return sp->sn_image;
1885 return NULL;
1886}
1887# endif
1888
1889/*
1890 * Undefine/free all signs.
1891 */
1892 void
1893free_signs(void)
1894{
1895 while (first_sign != NULL)
1896 sign_undefine(first_sign, NULL);
1897}
1898
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001899static enum
1900{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001901 EXP_SUBCMD, // expand :sign sub-commands
1902 EXP_DEFINE, // expand :sign define {name} args
1903 EXP_PLACE, // expand :sign place {id} args
Bram Moolenaar3678f652019-02-17 14:50:25 +01001904 EXP_LIST, // expand :sign place args
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001905 EXP_UNPLACE, // expand :sign unplace"
Bram Moolenaar3678f652019-02-17 14:50:25 +01001906 EXP_SIGN_NAMES, // expand with name of placed signs
1907 EXP_SIGN_GROUPS // expand with name of placed sign groups
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001908} expand_what;
1909
1910/*
Bram Moolenaar3678f652019-02-17 14:50:25 +01001911 * Return the n'th sign name (used for command line completion)
1912 */
1913 static char_u *
1914get_nth_sign_name(int idx)
1915{
1916 int current_idx;
1917 sign_T *sp;
1918
1919 // Complete with name of signs already defined
1920 current_idx = 0;
1921 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1922 if (current_idx++ == idx)
1923 return sp->sn_name;
1924 return NULL;
1925}
1926
1927/*
1928 * Return the n'th sign group name (used for command line completion)
1929 */
1930 static char_u *
1931get_nth_sign_group_name(int idx)
1932{
1933 int current_idx;
1934 int todo;
1935 hashitem_T *hi;
1936 signgroup_T *group;
1937
1938 // Complete with name of sign groups already defined
1939 current_idx = 0;
1940 todo = (int)sg_table.ht_used;
1941 for (hi = sg_table.ht_array; todo > 0; ++hi)
1942 {
1943 if (!HASHITEM_EMPTY(hi))
1944 {
1945 --todo;
1946 if (current_idx++ == idx)
1947 {
1948 group = HI2SG(hi);
1949 return group->sg_name;
1950 }
1951 }
1952 }
1953 return NULL;
1954}
1955
1956/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001957 * Function given to ExpandGeneric() to obtain the sign command
1958 * expansion.
1959 */
1960 char_u *
1961get_sign_name(expand_T *xp UNUSED, int idx)
1962{
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001963 switch (expand_what)
1964 {
1965 case EXP_SUBCMD:
1966 return (char_u *)cmds[idx];
1967 case EXP_DEFINE:
1968 {
1969 char *define_arg[] =
1970 {
1971 "icon=", "linehl=", "text=", "texthl=", NULL
1972 };
1973 return (char_u *)define_arg[idx];
1974 }
1975 case EXP_PLACE:
1976 {
1977 char *place_arg[] =
1978 {
1979 "line=", "name=", "group=", "priority=", "file=",
1980 "buffer=", NULL
1981 };
1982 return (char_u *)place_arg[idx];
1983 }
Bram Moolenaar3678f652019-02-17 14:50:25 +01001984 case EXP_LIST:
1985 {
1986 char *list_arg[] =
1987 {
1988 "group=", "file=", "buffer=", NULL
1989 };
1990 return (char_u *)list_arg[idx];
1991 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001992 case EXP_UNPLACE:
1993 {
1994 char *unplace_arg[] = { "group=", "file=", "buffer=", NULL };
1995 return (char_u *)unplace_arg[idx];
1996 }
1997 case EXP_SIGN_NAMES:
Bram Moolenaar3678f652019-02-17 14:50:25 +01001998 return get_nth_sign_name(idx);
1999 case EXP_SIGN_GROUPS:
2000 return get_nth_sign_group_name(idx);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002001 default:
2002 return NULL;
2003 }
2004}
2005
2006/*
2007 * Handle command line completion for :sign command.
2008 */
2009 void
2010set_context_in_sign_cmd(expand_T *xp, char_u *arg)
2011{
2012 char_u *p;
2013 char_u *end_subcmd;
2014 char_u *last;
2015 int cmd_idx;
2016 char_u *begin_subcmd_args;
2017
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002018 // Default: expand subcommands.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002019 xp->xp_context = EXPAND_SIGN;
2020 expand_what = EXP_SUBCMD;
2021 xp->xp_pattern = arg;
2022
2023 end_subcmd = skiptowhite(arg);
2024 if (*end_subcmd == NUL)
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002025 // expand subcmd name
2026 // :sign {subcmd}<CTRL-D>
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002027 return;
2028
2029 cmd_idx = sign_cmd_idx(arg, end_subcmd);
2030
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002031 // :sign {subcmd} {subcmd_args}
2032 // |
2033 // begin_subcmd_args
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002034 begin_subcmd_args = skipwhite(end_subcmd);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002035
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002036 // expand last argument of subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002037
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002038 // :sign define {name} {args}...
2039 // |
2040 // p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002041
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002042 // Loop until reaching last argument.
Bram Moolenaar3678f652019-02-17 14:50:25 +01002043 p = begin_subcmd_args;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002044 do
2045 {
2046 p = skipwhite(p);
2047 last = p;
2048 p = skiptowhite(p);
2049 } while (*p != NUL);
2050
2051 p = vim_strchr(last, '=');
2052
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002053 // :sign define {name} {args}... {last}=
2054 // | |
2055 // last p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002056 if (p == NULL)
2057 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002058 // Expand last argument name (before equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002059 xp->xp_pattern = last;
2060 switch (cmd_idx)
2061 {
2062 case SIGNCMD_DEFINE:
2063 expand_what = EXP_DEFINE;
2064 break;
2065 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002066 // List placed signs
2067 if (VIM_ISDIGIT(*begin_subcmd_args))
2068 // :sign place {id} {args}...
2069 expand_what = EXP_PLACE;
2070 else
2071 // :sign place {args}...
2072 expand_what = EXP_LIST;
2073 break;
2074 case SIGNCMD_LIST:
2075 case SIGNCMD_UNDEFINE:
2076 // :sign list <CTRL-D>
2077 // :sign undefine <CTRL-D>
2078 expand_what = EXP_SIGN_NAMES;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002079 break;
2080 case SIGNCMD_JUMP:
2081 case SIGNCMD_UNPLACE:
2082 expand_what = EXP_UNPLACE;
2083 break;
2084 default:
2085 xp->xp_context = EXPAND_NOTHING;
2086 }
2087 }
2088 else
2089 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002090 // Expand last argument value (after equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002091 xp->xp_pattern = p + 1;
2092 switch (cmd_idx)
2093 {
2094 case SIGNCMD_DEFINE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002095 if (STRNCMP(last, "texthl", 6) == 0
2096 || STRNCMP(last, "linehl", 6) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002097 xp->xp_context = EXPAND_HIGHLIGHT;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002098 else if (STRNCMP(last, "icon", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002099 xp->xp_context = EXPAND_FILES;
2100 else
2101 xp->xp_context = EXPAND_NOTHING;
2102 break;
2103 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002104 if (STRNCMP(last, "name", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002105 expand_what = EXP_SIGN_NAMES;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002106 else if (STRNCMP(last, "group", 5) == 0)
2107 expand_what = EXP_SIGN_GROUPS;
2108 else if (STRNCMP(last, "file", 4) == 0)
2109 xp->xp_context = EXPAND_BUFFERS;
2110 else
2111 xp->xp_context = EXPAND_NOTHING;
2112 break;
2113 case SIGNCMD_UNPLACE:
2114 case SIGNCMD_JUMP:
2115 if (STRNCMP(last, "group", 5) == 0)
2116 expand_what = EXP_SIGN_GROUPS;
2117 else if (STRNCMP(last, "file", 4) == 0)
2118 xp->xp_context = EXPAND_BUFFERS;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002119 else
2120 xp->xp_context = EXPAND_NOTHING;
2121 break;
2122 default:
2123 xp->xp_context = EXPAND_NOTHING;
2124 }
2125 }
2126}
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002127
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002128/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002129 * Define a sign using the attributes in 'dict'. Returns 0 on success and -1 on
2130 * failure.
2131 */
2132 static int
2133sign_define_from_dict(char_u *name_arg, dict_T *dict)
2134{
2135 char_u *name = NULL;
2136 char_u *icon = NULL;
2137 char_u *linehl = NULL;
2138 char_u *text = NULL;
2139 char_u *texthl = NULL;
2140 int retval = -1;
2141
2142 if (name_arg == NULL)
2143 {
2144 if (dict == NULL)
2145 return -1;
2146 name = dict_get_string(dict, (char_u *)"name", TRUE);
2147 }
2148 else
2149 name = vim_strsave(name_arg);
2150 if (name == NULL || name[0] == NUL)
2151 goto cleanup;
2152 if (dict != NULL)
2153 {
2154 icon = dict_get_string(dict, (char_u *)"icon", TRUE);
2155 linehl = dict_get_string(dict, (char_u *)"linehl", TRUE);
2156 text = dict_get_string(dict, (char_u *)"text", TRUE);
2157 texthl = dict_get_string(dict, (char_u *)"texthl", TRUE);
2158 }
2159
2160 if (sign_define_by_name(name, icon, linehl, text, texthl) == OK)
2161 retval = 0;
2162
2163cleanup:
2164 vim_free(name);
2165 vim_free(icon);
2166 vim_free(linehl);
2167 vim_free(text);
2168 vim_free(texthl);
2169
2170 return retval;
2171}
2172
2173/*
2174 * Define multiple signs using attributes from list 'l' and store the return
2175 * values in 'retlist'.
2176 */
2177 static void
2178sign_define_multiple(list_T *l, list_T *retlist)
2179{
2180 listitem_T *li;
2181 int retval;
2182
2183 for (li = l->lv_first; li != NULL; li = li->li_next)
2184 {
2185 retval = -1;
2186 if (li->li_tv.v_type == VAR_DICT)
2187 retval = sign_define_from_dict(NULL, li->li_tv.vval.v_dict);
2188 else
2189 emsg(_(e_dictreq));
2190 list_append_number(retlist, retval);
2191 }
2192}
2193
2194/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002195 * "sign_define()" function
2196 */
2197 void
2198f_sign_define(typval_T *argvars, typval_T *rettv)
2199{
2200 char_u *name;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002201
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002202 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2203 {
2204 // Define multiple signs
2205 if (rettv_list_alloc(rettv) != OK)
2206 return;
2207
2208 sign_define_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2209 return;
2210 }
2211
2212 // Define a single sign
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002213 rettv->vval.v_number = -1;
2214
2215 name = tv_get_string_chk(&argvars[0]);
2216 if (name == NULL)
2217 return;
2218
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002219 if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_DICT)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002220 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002221 emsg(_(e_dictreq));
2222 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002223 }
2224
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002225 rettv->vval.v_number = sign_define_from_dict(name,
2226 argvars[1].v_type == VAR_DICT ? argvars[1].vval.v_dict : NULL);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002227}
2228
2229/*
2230 * "sign_getdefined()" function
2231 */
2232 void
2233f_sign_getdefined(typval_T *argvars, typval_T *rettv)
2234{
2235 char_u *name = NULL;
2236
2237 if (rettv_list_alloc_id(rettv, aid_sign_getdefined) != OK)
2238 return;
2239
2240 if (argvars[0].v_type != VAR_UNKNOWN)
2241 name = tv_get_string(&argvars[0]);
2242
2243 sign_getlist(name, rettv->vval.v_list);
2244}
2245
2246/*
2247 * "sign_getplaced()" function
2248 */
2249 void
2250f_sign_getplaced(typval_T *argvars, typval_T *rettv)
2251{
2252 buf_T *buf = NULL;
2253 dict_T *dict;
2254 dictitem_T *di;
2255 linenr_T lnum = 0;
2256 int sign_id = 0;
2257 char_u *group = NULL;
2258 int notanum = FALSE;
2259
2260 if (rettv_list_alloc_id(rettv, aid_sign_getplaced) != OK)
2261 return;
2262
2263 if (argvars[0].v_type != VAR_UNKNOWN)
2264 {
2265 // get signs placed in the specified buffer
2266 buf = get_buf_arg(&argvars[0]);
2267 if (buf == NULL)
2268 return;
2269
2270 if (argvars[1].v_type != VAR_UNKNOWN)
2271 {
2272 if (argvars[1].v_type != VAR_DICT ||
2273 ((dict = argvars[1].vval.v_dict) == NULL))
2274 {
2275 emsg(_(e_dictreq));
2276 return;
2277 }
2278 if ((di = dict_find(dict, (char_u *)"lnum", -1)) != NULL)
2279 {
2280 // get signs placed at this line
2281 (void)tv_get_number_chk(&di->di_tv, &notanum);
2282 if (notanum)
2283 return;
2284 lnum = tv_get_lnum(&di->di_tv);
2285 }
2286 if ((di = dict_find(dict, (char_u *)"id", -1)) != NULL)
2287 {
2288 // get sign placed with this identifier
2289 sign_id = (int)tv_get_number_chk(&di->di_tv, &notanum);
2290 if (notanum)
2291 return;
2292 }
2293 if ((di = dict_find(dict, (char_u *)"group", -1)) != NULL)
2294 {
2295 group = tv_get_string_chk(&di->di_tv);
2296 if (group == NULL)
2297 return;
2298 if (*group == '\0') // empty string means global group
2299 group = NULL;
2300 }
2301 }
2302 }
2303
2304 sign_get_placed(buf, lnum, sign_id, group, rettv->vval.v_list);
2305}
2306
2307/*
2308 * "sign_jump()" function
2309 */
2310 void
2311f_sign_jump(typval_T *argvars, typval_T *rettv)
2312{
2313 int sign_id;
2314 char_u *sign_group = NULL;
2315 buf_T *buf;
2316 int notanum = FALSE;
2317
2318 rettv->vval.v_number = -1;
2319
2320 // Sign identifier
2321 sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
2322 if (notanum)
2323 return;
2324 if (sign_id <= 0)
2325 {
2326 emsg(_(e_invarg));
2327 return;
2328 }
2329
2330 // Sign group
2331 sign_group = tv_get_string_chk(&argvars[1]);
2332 if (sign_group == NULL)
2333 return;
2334 if (sign_group[0] == '\0')
2335 sign_group = NULL; // global sign group
2336 else
2337 {
2338 sign_group = vim_strsave(sign_group);
2339 if (sign_group == NULL)
2340 return;
2341 }
2342
2343 // Buffer to place the sign
2344 buf = get_buf_arg(&argvars[2]);
2345 if (buf == NULL)
2346 goto cleanup;
2347
2348 rettv->vval.v_number = sign_jump(sign_id, sign_group, buf);
2349
2350cleanup:
2351 vim_free(sign_group);
2352}
2353
2354/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002355 * Place a new sign using the values specified in dict 'dict'. Returns the sign
2356 * identifier if successfully placed, otherwise returns 0.
2357 */
2358 static int
2359sign_place_from_dict(
2360 typval_T *id_tv,
2361 typval_T *group_tv,
2362 typval_T *name_tv,
2363 typval_T *buf_tv,
2364 dict_T *dict)
2365{
2366 int sign_id = 0;
2367 char_u *group = NULL;
2368 char_u *sign_name = NULL;
2369 buf_T *buf = NULL;
2370 dictitem_T *di;
2371 linenr_T lnum = 0;
2372 int prio = SIGN_DEF_PRIO;
2373 int notanum = FALSE;
2374 int ret_sign_id = -1;
2375
2376 // sign identifier
2377 if (id_tv == NULL)
2378 {
2379 di = dict_find(dict, (char_u *)"id", -1);
2380 if (di != NULL)
2381 id_tv = &di->di_tv;
2382 }
2383 if (id_tv == NULL)
2384 sign_id = 0;
2385 else
2386 {
2387 sign_id = tv_get_number_chk(id_tv, &notanum);
2388 if (notanum)
2389 return -1;
2390 if (sign_id < 0)
2391 {
2392 emsg(_(e_invarg));
2393 return -1;
2394 }
2395 }
2396
2397 // sign group
2398 if (group_tv == NULL)
2399 {
2400 di = dict_find(dict, (char_u *)"group", -1);
2401 if (di != NULL)
2402 group_tv = &di->di_tv;
2403 }
2404 if (group_tv == NULL)
2405 group = NULL; // global group
2406 else
2407 {
2408 group = tv_get_string_chk(group_tv);
2409 if (group == NULL)
2410 goto cleanup;
2411 if (group[0] == '\0') // global sign group
2412 group = NULL;
2413 else
2414 {
2415 group = vim_strsave(group);
2416 if (group == NULL)
2417 return -1;
2418 }
2419 }
2420
2421 // sign name
2422 if (name_tv == NULL)
2423 {
2424 di = dict_find(dict, (char_u *)"name", -1);
2425 if (di != NULL)
2426 name_tv = &di->di_tv;
2427 }
2428 if (name_tv == NULL)
2429 goto cleanup;
2430 sign_name = tv_get_string_chk(name_tv);
2431 if (sign_name == NULL)
2432 goto cleanup;
2433
2434 // buffer to place the sign
2435 if (buf_tv == NULL)
2436 {
2437 di = dict_find(dict, (char_u *)"buffer", -1);
2438 if (di != NULL)
2439 buf_tv = &di->di_tv;
2440 }
2441 if (buf_tv == NULL)
2442 goto cleanup;
2443 buf = get_buf_arg(buf_tv);
2444 if (buf == NULL)
2445 goto cleanup;
2446
2447 // line number of the sign
2448 di = dict_find(dict, (char_u *)"lnum", -1);
2449 if (di != NULL)
2450 {
Bram Moolenaar42aff462019-08-21 13:20:29 +02002451 lnum = tv_get_lnum(&di->di_tv);
2452 if (lnum <= 0)
2453 {
2454 emsg(_(e_invarg));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002455 goto cleanup;
Bram Moolenaar42aff462019-08-21 13:20:29 +02002456 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002457 }
2458
2459 // sign priority
2460 di = dict_find(dict, (char_u *)"priority", -1);
2461 if (di != NULL)
2462 {
2463 prio = (int)tv_get_number_chk(&di->di_tv, &notanum);
2464 if (notanum)
2465 goto cleanup;
2466 }
2467
2468 if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK)
2469 ret_sign_id = sign_id;
2470
2471cleanup:
2472 vim_free(group);
2473
2474 return ret_sign_id;
2475}
2476
2477/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002478 * "sign_place()" function
2479 */
2480 void
2481f_sign_place(typval_T *argvars, typval_T *rettv)
2482{
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002483 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002484
2485 rettv->vval.v_number = -1;
2486
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002487 if (argvars[4].v_type != VAR_UNKNOWN
2488 && (argvars[4].v_type != VAR_DICT
2489 || ((dict = argvars[4].vval.v_dict) == NULL)))
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002490 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002491 emsg(_(e_dictreq));
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002492 return;
2493 }
2494
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002495 rettv->vval.v_number = sign_place_from_dict(&argvars[0], &argvars[1],
2496 &argvars[2], &argvars[3], dict);
2497}
2498
2499/*
2500 * "sign_placelist()" function. Place multiple signs.
2501 */
2502 void
2503f_sign_placelist(typval_T *argvars, typval_T *rettv)
2504{
2505 listitem_T *li;
2506 int sign_id;
2507
2508 if (rettv_list_alloc(rettv) != OK)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002509 return;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002510
2511 if (argvars[0].v_type != VAR_LIST)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002512 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002513 emsg(_(e_listreq));
2514 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002515 }
2516
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002517 // Process the List of sign attributes
2518 for (li = argvars[0].vval.v_list->lv_first; li != NULL; li = li->li_next)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002519 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002520 sign_id = -1;
2521 if (li->li_tv.v_type == VAR_DICT)
2522 sign_id = sign_place_from_dict(NULL, NULL, NULL, NULL,
2523 li->li_tv.vval.v_dict);
2524 else
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002525 emsg(_(e_dictreq));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002526 list_append_number(rettv->vval.v_list, sign_id);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002527 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002528}
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002529
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002530/*
2531 * Undefine multiple signs
2532 */
2533 static void
2534sign_undefine_multiple(list_T *l, list_T *retlist)
2535{
2536 char_u *name;
2537 listitem_T *li;
2538 int retval;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002539
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002540 for (li = l->lv_first; li != NULL; li = li->li_next)
2541 {
2542 retval = -1;
2543 name = tv_get_string_chk(&li->li_tv);
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002544 if (name != NULL && (sign_undefine_by_name(name, TRUE) == OK))
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002545 retval = 0;
2546 list_append_number(retlist, retval);
2547 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002548}
2549
2550/*
2551 * "sign_undefine()" function
2552 */
2553 void
2554f_sign_undefine(typval_T *argvars, typval_T *rettv)
2555{
2556 char_u *name;
2557
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002558 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2559 {
2560 // Undefine multiple signs
2561 if (rettv_list_alloc(rettv) != OK)
2562 return;
2563
2564 sign_undefine_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2565 return;
2566 }
2567
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002568 rettv->vval.v_number = -1;
2569
2570 if (argvars[0].v_type == VAR_UNKNOWN)
2571 {
2572 // Free all the signs
2573 free_signs();
2574 rettv->vval.v_number = 0;
2575 }
2576 else
2577 {
2578 // Free only the specified sign
2579 name = tv_get_string_chk(&argvars[0]);
2580 if (name == NULL)
2581 return;
2582
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002583 if (sign_undefine_by_name(name, TRUE) == OK)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002584 rettv->vval.v_number = 0;
2585 }
2586}
2587
2588/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002589 * Unplace the sign with attributes specified in 'dict'. Returns 0 on success
2590 * and -1 on failure.
2591 */
2592 static int
2593sign_unplace_from_dict(typval_T *group_tv, dict_T *dict)
2594{
2595 dictitem_T *di;
2596 int sign_id = 0;
2597 buf_T *buf = NULL;
2598 char_u *group = NULL;
2599 int retval = -1;
2600
2601 // sign group
2602 if (group_tv != NULL)
2603 group = tv_get_string(group_tv);
2604 else
2605 group = dict_get_string(dict, (char_u *)"group", FALSE);
2606 if (group != NULL)
2607 {
2608 if (group[0] == '\0') // global sign group
2609 group = NULL;
2610 else
2611 {
2612 group = vim_strsave(group);
2613 if (group == NULL)
2614 return -1;
2615 }
2616 }
2617
2618 if (dict != NULL)
2619 {
2620 if ((di = dict_find(dict, (char_u *)"buffer", -1)) != NULL)
2621 {
2622 buf = get_buf_arg(&di->di_tv);
2623 if (buf == NULL)
2624 goto cleanup;
2625 }
2626 if (dict_find(dict, (char_u *)"id", -1) != NULL)
2627 {
2628 sign_id = dict_get_number(dict, (char_u *)"id");
2629 if (sign_id <= 0)
2630 {
2631 emsg(_(e_invarg));
2632 goto cleanup;
2633 }
2634 }
2635 }
2636
2637 if (buf == NULL)
2638 {
2639 // Delete the sign in all the buffers
2640 retval = 0;
2641 FOR_ALL_BUFFERS(buf)
2642 if (sign_unplace(sign_id, group, buf, 0) != OK)
2643 retval = -1;
2644 }
2645 else if (sign_unplace(sign_id, group, buf, 0) == OK)
2646 retval = 0;
2647
2648cleanup:
2649 vim_free(group);
2650
2651 return retval;
2652}
2653
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002654 sign_entry_T *
2655get_first_valid_sign(win_T *wp)
2656{
2657 sign_entry_T *sign = wp->w_buffer->b_signlist;
2658
2659# ifdef FEAT_TEXT_PROP
Bram Moolenaar72570732019-11-30 14:21:53 +01002660 while (sign != NULL && !sign_group_for_window(sign, wp))
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002661 sign = sign->se_next;
2662# endif
2663 return sign;
2664}
2665
2666/*
2667 * Return TRUE when window "wp" has a column to draw signs in.
2668 */
2669 int
2670signcolumn_on(win_T *wp)
2671{
2672 // If 'signcolumn' is set to 'number', signs are displayed in the 'number'
2673 // column (if present). Otherwise signs are to be displayed in the sign
2674 // column.
2675 if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u')
2676 return get_first_valid_sign(wp) != NULL && !wp->w_p_nu && !wp->w_p_rnu;
2677
2678 if (*wp->w_p_scl == 'n')
2679 return FALSE;
2680 if (*wp->w_p_scl == 'y')
2681 return TRUE;
2682 return (get_first_valid_sign(wp) != NULL
2683# ifdef FEAT_NETBEANS_INTG
2684 || wp->w_buffer->b_has_sign_column
2685# endif
2686 );
2687}
2688
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002689/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002690 * "sign_unplace()" function
2691 */
2692 void
2693f_sign_unplace(typval_T *argvars, typval_T *rettv)
2694{
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002695 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002696
2697 rettv->vval.v_number = -1;
2698
2699 if (argvars[0].v_type != VAR_STRING)
2700 {
2701 emsg(_(e_invarg));
2702 return;
2703 }
2704
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002705 if (argvars[1].v_type != VAR_UNKNOWN)
2706 {
2707 if (argvars[1].v_type != VAR_DICT)
2708 {
2709 emsg(_(e_dictreq));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002710 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002711 }
2712 dict = argvars[1].vval.v_dict;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002713 }
2714
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002715 rettv->vval.v_number = sign_unplace_from_dict(&argvars[0], dict);
2716}
2717
2718/*
2719 * "sign_unplacelist()" function
2720 */
2721 void
2722f_sign_unplacelist(typval_T *argvars, typval_T *rettv)
2723{
2724 listitem_T *li;
2725 int retval;
2726
2727 if (rettv_list_alloc(rettv) != OK)
2728 return;
2729
2730 if (argvars[0].v_type != VAR_LIST)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002731 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002732 emsg(_(e_listreq));
2733 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002734 }
2735
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002736 for (li = argvars[0].vval.v_list->lv_first; li != NULL; li = li->li_next)
2737 {
2738 retval = -1;
2739 if (li->li_tv.v_type == VAR_DICT)
2740 retval = sign_unplace_from_dict(NULL, li->li_tv.vval.v_dict);
2741 else
2742 emsg(_(e_dictreq));
2743 list_append_number(rettv->vval.v_list, retval);
2744 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002745}
2746
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002747#endif /* FEAT_SIGNS */