blob: 1964c83087ba56b2e8fcaedb42f7ce74a8fa500e [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
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100499# ifdef FEAT_PROP_POPUP
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 Moolenaara2f6e422020-02-19 17:13:04 +0100517
518 // If there is another sign next with the same priority, may
519 // combine the text and the line highlighting.
520 if (sign->se_next != NULL
521 && sign->se_next->se_priority == sign->se_priority
522 && sign->se_next->se_lnum == sign->se_lnum)
523 {
524 sign_T *next_sp = find_sign_by_typenr(sign->se_next->se_typenr);
525
526 if (next_sp != NULL)
527 {
528 if (sattr->sat_icon == NULL && sattr->sat_text == NULL)
529 {
530# ifdef FEAT_SIGN_ICONS
531 sattr->sat_icon = next_sp->sn_image;
532# endif
533 sattr->sat_text = next_sp->sn_text;
534 }
535 if (sp->sn_text_hl <= 0 && next_sp->sn_text_hl > 0)
536 sattr->sat_texthl = syn_id2attr(next_sp->sn_text_hl);
537 if (sp->sn_line_hl <= 0 && next_sp->sn_line_hl > 0)
538 sattr->sat_linehl = syn_id2attr(next_sp->sn_line_hl);
539 }
540 }
Bram Moolenaar4e038572019-07-04 18:28:35 +0200541 return TRUE;
542 }
543 }
544 return FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100545}
546
547/*
548 * Delete sign 'id' in group 'group' from buffer 'buf'.
549 * If 'id' is zero, then delete all the signs in group 'group'. Otherwise
550 * delete only the specified sign.
551 * If 'group' is '*', then delete the sign in all the groups. If 'group' is
552 * NULL, then delete the sign in the global group. Otherwise delete the sign in
553 * the specified group.
554 * Returns the line number of the deleted sign. If multiple signs are deleted,
555 * then returns the line number of the last sign deleted.
556 */
557 linenr_T
558buf_delsign(
559 buf_T *buf, // buffer sign is stored in
560 linenr_T atlnum, // sign at this line, 0 - at any line
561 int id, // sign id
562 char_u *group) // sign group
563{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200564 sign_entry_T **lastp; // pointer to pointer to current sign
565 sign_entry_T *sign; // a sign in a b_signlist
566 sign_entry_T *next; // the next sign in a b_signlist
567 linenr_T lnum; // line number whose sign was deleted
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100568
569 lastp = &buf->b_signlist;
570 lnum = 0;
571 for (sign = buf->b_signlist; sign != NULL; sign = next)
572 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200573 next = sign->se_next;
574 if ((id == 0 || sign->se_id == id)
575 && (atlnum == 0 || sign->se_lnum == atlnum)
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100576 && sign_in_group(sign, group))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100577
578 {
579 *lastp = next;
580 if (next != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200581 next->se_prev = sign->se_prev;
582 lnum = sign->se_lnum;
583 if (sign->se_group != NULL)
584 sign_group_unref(sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100585 vim_free(sign);
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100586 redraw_buf_line_later(buf, lnum);
587
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100588 // Check whether only one sign needs to be deleted
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100589 // If deleting a sign with a specific identifier in a particular
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100590 // group or deleting any sign at a particular line number, delete
591 // only one sign.
592 if (group == NULL
593 || (*group != '*' && id != 0)
594 || (*group == '*' && atlnum != 0))
595 break;
596 }
597 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200598 lastp = &sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100599 }
600
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100601 // When deleting the last sign the cursor position may change, because the
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100602 // sign columns no longer shows. And the 'signcolumn' may be hidden.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100603 if (buf->b_signlist == NULL)
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100604 {
605 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200606 changed_line_abv_curs();
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100607 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100608
609 return lnum;
610}
611
612
613/*
614 * Find the line number of the sign with the requested id in group 'group'. If
615 * the sign does not exist, return 0 as the line number. This will still let
616 * the correct file get loaded.
617 */
618 int
619buf_findsign(
620 buf_T *buf, // buffer to store sign in
621 int id, // sign ID
622 char_u *group) // sign group
623{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200624 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100625
626 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200627 if (sign->se_id == id && sign_in_group(sign, group))
628 return sign->se_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100629
630 return 0;
631}
632
633/*
634 * Return the sign at line 'lnum' in buffer 'buf'. Returns NULL if a sign is
635 * not found at the line. If 'groupname' is NULL, searches in the global group.
636 */
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200637 static sign_entry_T *
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100638buf_getsign_at_line(
639 buf_T *buf, // buffer whose sign we are searching for
640 linenr_T lnum, // line number of sign
641 char_u *groupname) // sign group name
642{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200643 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100644
645 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200646 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200647 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200648 // Signs are sorted by line number in the buffer. No need to check
649 // for signs after the specified line number 'lnum'.
650 break;
651
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200652 if (sign->se_lnum == lnum && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100653 return sign;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200654 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100655
656 return NULL;
657}
658
659/*
660 * Return the identifier of the sign at line number 'lnum' in buffer 'buf'.
661 */
662 int
663buf_findsign_id(
664 buf_T *buf, // buffer whose sign we are searching for
665 linenr_T lnum, // line number of sign
666 char_u *groupname) // sign group name
667{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200668 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100669
670 sign = buf_getsign_at_line(buf, lnum, groupname);
671 if (sign != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200672 return sign->se_id;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100673
674 return 0;
675}
676
677# if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
678/*
679 * See if a given type of sign exists on a specific line.
680 */
681 int
682buf_findsigntype_id(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100683 buf_T *buf, // buffer whose sign we are searching for
684 linenr_T lnum, // line number of sign
685 int typenr) // sign type number
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100686{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200687 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100688
689 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200690 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200691 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200692 // Signs are sorted by line number in the buffer. No need to check
693 // for signs after the specified line number 'lnum'.
694 break;
695
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200696 if (sign->se_lnum == lnum && sign->se_typenr == typenr)
697 return sign->se_id;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200698 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100699
700 return 0;
701}
702
703
704# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
705/*
706 * Return the number of icons on the given line.
707 */
708 int
709buf_signcount(buf_T *buf, linenr_T lnum)
710{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200711 sign_entry_T *sign; // a sign in the signlist
712 int count = 0;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100713
714 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200715 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200716 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200717 // Signs are sorted by line number in the buffer. No need to check
718 // for signs after the specified line number 'lnum'.
719 break;
720
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200721 if (sign->se_lnum == lnum)
722 if (sign_get_image(sign->se_typenr) != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100723 count++;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200724 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100725
726 return count;
727}
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100728# endif // FEAT_SIGN_ICONS
729# endif // FEAT_NETBEANS_INTG
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100730
731/*
732 * Delete signs in group 'group' in buffer "buf". If 'group' is '*', then
733 * delete all the signs.
734 */
735 void
736buf_delete_signs(buf_T *buf, char_u *group)
737{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200738 sign_entry_T *sign;
739 sign_entry_T **lastp; // pointer to pointer to current sign
740 sign_entry_T *next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100741
742 // When deleting the last sign need to redraw the windows to remove the
743 // sign column. Not when curwin is NULL (this means we're exiting).
744 if (buf->b_signlist != NULL && curwin != NULL)
745 {
746 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200747 changed_line_abv_curs();
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100748 }
749
750 lastp = &buf->b_signlist;
751 for (sign = buf->b_signlist; sign != NULL; sign = next)
752 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200753 next = sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100754 if (sign_in_group(sign, group))
755 {
756 *lastp = next;
757 if (next != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200758 next->se_prev = sign->se_prev;
759 if (sign->se_group != NULL)
760 sign_group_unref(sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100761 vim_free(sign);
762 }
763 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200764 lastp = &sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100765 }
766}
767
768/*
769 * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers.
770 */
771 static void
772sign_list_placed(buf_T *rbuf, char_u *sign_group)
773{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200774 buf_T *buf;
775 sign_entry_T *sign;
776 char lbuf[MSG_BUF_LEN];
777 char group[MSG_BUF_LEN];
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100778
Bram Moolenaar32526b32019-01-19 17:43:09 +0100779 msg_puts_title(_("\n--- Signs ---"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100780 msg_putchar('\n');
781 if (rbuf == NULL)
782 buf = firstbuf;
783 else
784 buf = rbuf;
785 while (buf != NULL && !got_int)
786 {
787 if (buf->b_signlist != NULL)
788 {
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100789 vim_snprintf(lbuf, MSG_BUF_LEN, _("Signs for %s:"), buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100790 msg_puts_attr(lbuf, HL_ATTR(HLF_D));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100791 msg_putchar('\n');
792 }
793 FOR_ALL_SIGNS_IN_BUF(buf, sign)
794 {
795 if (got_int)
796 break;
797 if (!sign_in_group(sign, sign_group))
798 continue;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200799 if (sign->se_group != NULL)
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100800 vim_snprintf(group, MSG_BUF_LEN, _(" group=%s"),
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200801 sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100802 else
803 group[0] = '\0';
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100804 vim_snprintf(lbuf, MSG_BUF_LEN,
805 _(" line=%ld id=%d%s name=%s priority=%d"),
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200806 (long)sign->se_lnum, sign->se_id, group,
807 sign_typenr2name(sign->se_typenr), sign->se_priority);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100808 msg_puts(lbuf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100809 msg_putchar('\n');
810 }
811 if (rbuf != NULL)
812 break;
813 buf = buf->b_next;
814 }
815}
816
817/*
818 * Adjust a placed sign for inserted/deleted lines.
819 */
820 void
821sign_mark_adjust(
822 linenr_T line1,
823 linenr_T line2,
824 long amount,
825 long amount_after)
826{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200827 sign_entry_T *sign; // a sign in a b_signlist
828 linenr_T new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100829
830 FOR_ALL_SIGNS_IN_BUF(curbuf, sign)
831 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100832 // Ignore changes to lines after the sign
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200833 if (sign->se_lnum < line1)
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100834 continue;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200835 new_lnum = sign->se_lnum;
836 if (sign->se_lnum >= line1 && sign->se_lnum <= line2)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100837 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100838 if (amount != MAXLNUM)
839 new_lnum += amount;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100840 }
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200841 else if (sign->se_lnum > line2)
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100842 // Lines inserted or deleted before the sign
843 new_lnum += amount_after;
844
845 // If the new sign line number is past the last line in the buffer,
846 // then don't adjust the line number. Otherwise, it will always be past
847 // the last line and will not be visible.
848 if (new_lnum <= curbuf->b_ml.ml_line_count)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200849 sign->se_lnum = new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100850 }
851}
852
853/*
854 * Find index of a ":sign" subcmd from its name.
855 * "*end_cmd" must be writable.
856 */
857 static int
858sign_cmd_idx(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100859 char_u *begin_cmd, // begin of sign subcmd
860 char_u *end_cmd) // just after sign subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100861{
862 int idx;
863 char save = *end_cmd;
864
865 *end_cmd = NUL;
866 for (idx = 0; ; ++idx)
867 if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0)
868 break;
869 *end_cmd = save;
870 return idx;
871}
872
873/*
874 * Find a sign by name. Also returns pointer to the previous sign.
875 */
876 static sign_T *
877sign_find(char_u *name, sign_T **sp_prev)
878{
879 sign_T *sp;
880
881 if (sp_prev != NULL)
882 *sp_prev = NULL;
883 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
884 {
885 if (STRCMP(sp->sn_name, name) == 0)
886 break;
887 if (sp_prev != NULL)
888 *sp_prev = sp;
889 }
890
891 return sp;
892}
893
894/*
Bram Moolenaar03142362019-01-18 22:01:42 +0100895 * Allocate a new sign
896 */
897 static sign_T *
898alloc_new_sign(char_u *name)
899{
900 sign_T *sp;
901 sign_T *lp;
902 int start = next_sign_typenr;
903
904 // Allocate a new sign.
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200905 sp = alloc_clear_id(sizeof(sign_T), aid_sign_define_by_name);
Bram Moolenaar03142362019-01-18 22:01:42 +0100906 if (sp == NULL)
907 return NULL;
908
909 // Check that next_sign_typenr is not already being used.
910 // This only happens after wrapping around. Hopefully
911 // another one got deleted and we can use its number.
912 for (lp = first_sign; lp != NULL; )
913 {
914 if (lp->sn_typenr == next_sign_typenr)
915 {
916 ++next_sign_typenr;
917 if (next_sign_typenr == MAX_TYPENR)
918 next_sign_typenr = 1;
919 if (next_sign_typenr == start)
920 {
921 vim_free(sp);
922 emsg(_("E612: Too many signs defined"));
923 return NULL;
924 }
925 lp = first_sign; // start all over
926 continue;
927 }
928 lp = lp->sn_next;
929 }
930
931 sp->sn_typenr = next_sign_typenr;
932 if (++next_sign_typenr == MAX_TYPENR)
933 next_sign_typenr = 1; // wrap around
934
935 sp->sn_name = vim_strsave(name);
936 if (sp->sn_name == NULL) // out of memory
937 {
938 vim_free(sp);
939 return NULL;
940 }
941
942 return sp;
943}
944
945/*
946 * Initialize the icon information for a new sign
947 */
948 static void
949sign_define_init_icon(sign_T *sp, char_u *icon)
950{
951 vim_free(sp->sn_icon);
952 sp->sn_icon = vim_strsave(icon);
953 backslash_halve(sp->sn_icon);
954# ifdef FEAT_SIGN_ICONS
955 if (gui.in_use)
956 {
957 out_flush();
958 if (sp->sn_image != NULL)
959 gui_mch_destroy_sign(sp->sn_image);
960 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
961 }
962# endif
963}
964
965/*
966 * Initialize the text for a new sign
967 */
968 static int
969sign_define_init_text(sign_T *sp, char_u *text)
970{
971 char_u *s;
972 char_u *endp;
973 int cells;
974 int len;
975
976 endp = text + (int)STRLEN(text);
977
978 // Remove backslashes so that it is possible to use a space.
979 for (s = text; s + 1 < endp; ++s)
980 if (*s == '\\')
981 {
982 STRMOVE(s, s + 1);
983 --endp;
984 }
985
986 // Count cells and check for non-printable chars
Bram Moolenaar03142362019-01-18 22:01:42 +0100987 if (has_mbyte)
988 {
989 cells = 0;
990 for (s = text; s < endp; s += (*mb_ptr2len)(s))
991 {
992 if (!vim_isprintc((*mb_ptr2char)(s)))
993 break;
994 cells += (*mb_ptr2cells)(s);
995 }
996 }
997 else
Bram Moolenaar03142362019-01-18 22:01:42 +0100998 {
999 for (s = text; s < endp; ++s)
1000 if (!vim_isprintc(*s))
1001 break;
1002 cells = (int)(s - text);
1003 }
1004
1005 // Currently sign text must be one or two display cells
1006 if (s != endp || cells < 1 || cells > 2)
1007 {
1008 semsg(_("E239: Invalid sign text: %s"), text);
1009 return FAIL;
1010 }
1011
1012 vim_free(sp->sn_text);
1013 // Allocate one byte more if we need to pad up
1014 // with a space.
1015 len = (int)(endp - text + ((cells == 1) ? 1 : 0));
1016 sp->sn_text = vim_strnsave(text, len);
1017
1018 // For single character sign text, pad with a space.
1019 if (sp->sn_text != NULL && cells == 1)
1020 STRCPY(sp->sn_text + len - 1, " ");
1021
1022 return OK;
1023}
1024
1025/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001026 * Define a new sign or update an existing sign
1027 */
1028 int
1029sign_define_by_name(
1030 char_u *name,
1031 char_u *icon,
1032 char_u *linehl,
1033 char_u *text,
1034 char_u *texthl)
1035{
1036 sign_T *sp_prev;
1037 sign_T *sp;
1038
1039 sp = sign_find(name, &sp_prev);
1040 if (sp == NULL)
1041 {
Bram Moolenaar03142362019-01-18 22:01:42 +01001042 sp = alloc_new_sign(name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001043 if (sp == NULL)
1044 return FAIL;
1045
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001046 // add the new sign to the list of signs
1047 if (sp_prev == NULL)
1048 first_sign = sp;
1049 else
1050 sp_prev->sn_next = sp;
1051 }
Bram Moolenaarbf0acff2020-01-09 21:01:59 +01001052 else
1053 {
1054 win_T *wp;
1055
1056 // Signs may already exist, a redraw is needed in windows with a
1057 // non-empty sign list.
1058 FOR_ALL_WINDOWS(wp)
1059 if (wp->w_buffer->b_signlist != NULL)
1060 redraw_buf_later(wp->w_buffer, NOT_VALID);
1061 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001062
1063 // set values for a defined sign.
1064 if (icon != NULL)
Bram Moolenaar03142362019-01-18 22:01:42 +01001065 sign_define_init_icon(sp, icon);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001066
Bram Moolenaar03142362019-01-18 22:01:42 +01001067 if (text != NULL && (sign_define_init_text(sp, text) == FAIL))
1068 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001069
1070 if (linehl != NULL)
1071 sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl));
1072
1073 if (texthl != NULL)
1074 sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl));
1075
1076 return OK;
1077}
1078
1079/*
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001080 * Return TRUE if sign "name" exists.
1081 */
1082 int
1083sign_exists_by_name(char_u *name)
1084{
1085 return sign_find(name, NULL) != NULL;
1086}
1087
1088/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001089 * Free the sign specified by 'name'.
1090 */
1091 int
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001092sign_undefine_by_name(char_u *name, int give_error)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001093{
1094 sign_T *sp_prev;
1095 sign_T *sp;
1096
1097 sp = sign_find(name, &sp_prev);
1098 if (sp == NULL)
1099 {
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001100 if (give_error)
1101 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001102 return FAIL;
1103 }
1104 sign_undefine(sp, sp_prev);
1105
1106 return OK;
1107}
1108
1109/*
1110 * List the signs matching 'name'
1111 */
1112 static void
1113sign_list_by_name(char_u *name)
1114{
1115 sign_T *sp;
1116
1117 sp = sign_find(name, NULL);
1118 if (sp != NULL)
1119 sign_list_defined(sp);
1120 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001121 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001122}
1123
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001124 static void
1125may_force_numberwidth_recompute(buf_T *buf, int unplace)
1126{
1127 tabpage_T *tp;
1128 win_T *wp;
1129
1130 FOR_ALL_TAB_WINDOWS(tp, wp)
1131 if (wp->w_buffer == buf
1132 && (wp->w_p_nu || wp->w_p_rnu)
1133 && (unplace || wp->w_nrwidth_width < 2)
1134 && (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u'))
1135 wp->w_nrwidth_line_count = 0;
1136}
1137
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001138/*
Bram Moolenaar8144acb2019-01-14 23:08:18 +01001139 * Place a sign at the specified file location or update a sign.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001140 */
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001141 int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001142sign_place(
1143 int *sign_id,
1144 char_u *sign_group,
1145 char_u *sign_name,
1146 buf_T *buf,
1147 linenr_T lnum,
1148 int prio)
1149{
1150 sign_T *sp;
1151
1152 // Check for reserved character '*' in group name
1153 if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0'))
1154 return FAIL;
1155
1156 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1157 if (STRCMP(sp->sn_name, sign_name) == 0)
1158 break;
1159 if (sp == NULL)
1160 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001161 semsg(_("E155: Unknown sign: %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001162 return FAIL;
1163 }
1164 if (*sign_id == 0)
1165 *sign_id = sign_group_get_next_signid(buf, sign_group);
1166
1167 if (lnum > 0)
1168 // ":sign place {id} line={lnum} name={name} file={fname}":
1169 // place a sign
1170 buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr);
1171 else
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02001172 // ":sign place {id} file={fname}": change sign type and/or priority
1173 lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr,
1174 prio);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001175 if (lnum > 0)
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001176 {
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001177 redraw_buf_line_later(buf, lnum);
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001178
1179 // When displaying signs in the 'number' column, if the width of the
1180 // number column is less than 2, then force recomputing the width.
1181 may_force_numberwidth_recompute(buf, FALSE);
1182 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001183 else
1184 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001185 semsg(_("E885: Not possible to change sign %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001186 return FAIL;
1187 }
1188
1189 return OK;
1190}
1191
1192/*
1193 * Unplace the specified sign
1194 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001195 static int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001196sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum)
1197{
1198 if (buf->b_signlist == NULL) // No signs in the buffer
1199 return OK;
1200
1201 if (sign_id == 0)
1202 {
1203 // Delete all the signs in the specified buffer
1204 redraw_buf_later(buf, NOT_VALID);
1205 buf_delete_signs(buf, sign_group);
1206 }
1207 else
1208 {
1209 linenr_T lnum;
1210
1211 // Delete only the specified signs
1212 lnum = buf_delsign(buf, atlnum, sign_id, sign_group);
1213 if (lnum == 0)
1214 return FAIL;
1215 }
1216
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001217 // When all the signs in a buffer are removed, force recomputing the
1218 // number column width (if enabled) in all the windows displaying the
1219 // buffer if 'signcolumn' is set to 'number' in that window.
1220 if (buf->b_signlist == NULL)
1221 may_force_numberwidth_recompute(buf, TRUE);
1222
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001223 return OK;
1224}
1225
1226/*
1227 * Unplace the sign at the current cursor line.
1228 */
1229 static void
1230sign_unplace_at_cursor(char_u *groupname)
1231{
1232 int id = -1;
1233
1234 id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname);
1235 if (id > 0)
1236 sign_unplace(id, groupname, curwin->w_buffer, curwin->w_cursor.lnum);
1237 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001238 emsg(_("E159: Missing sign number"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001239}
1240
1241/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001242 * Jump to a sign.
1243 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001244 static linenr_T
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001245sign_jump(int sign_id, char_u *sign_group, buf_T *buf)
1246{
1247 linenr_T lnum;
1248
1249 if ((lnum = buf_findsign(buf, sign_id, sign_group)) <= 0)
1250 {
Bram Moolenaarb5443cc2019-01-15 20:19:40 +01001251 semsg(_("E157: Invalid sign ID: %d"), sign_id);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001252 return -1;
1253 }
1254
1255 // goto a sign ...
1256 if (buf_jump_open_win(buf) != NULL)
1257 { // ... in a current window
1258 curwin->w_cursor.lnum = lnum;
1259 check_cursor_lnum();
1260 beginline(BL_WHITE);
1261 }
1262 else
1263 { // ... not currently in a window
1264 char_u *cmd;
1265
1266 if (buf->b_fname == NULL)
1267 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001268 emsg(_("E934: Cannot jump to a buffer that does not have a name"));
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001269 return -1;
1270 }
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02001271 cmd = alloc(STRLEN(buf->b_fname) + 25);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001272 if (cmd == NULL)
1273 return -1;
1274 sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname);
1275 do_cmdline_cmd(cmd);
1276 vim_free(cmd);
1277 }
1278# ifdef FEAT_FOLDING
1279 foldOpenCursor();
1280# endif
1281
1282 return lnum;
1283}
1284
1285/*
1286 * ":sign define {name} ..." command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001287 */
1288 static void
1289sign_define_cmd(char_u *sign_name, char_u *cmdline)
1290{
1291 char_u *arg;
1292 char_u *p = cmdline;
1293 char_u *icon = NULL;
1294 char_u *text = NULL;
1295 char_u *linehl = NULL;
1296 char_u *texthl = NULL;
1297 int failed = FALSE;
1298
1299 // set values for a defined sign.
1300 for (;;)
1301 {
1302 arg = skipwhite(p);
1303 if (*arg == NUL)
1304 break;
1305 p = skiptowhite_esc(arg);
1306 if (STRNCMP(arg, "icon=", 5) == 0)
1307 {
1308 arg += 5;
1309 icon = vim_strnsave(arg, (int)(p - arg));
1310 }
1311 else if (STRNCMP(arg, "text=", 5) == 0)
1312 {
1313 arg += 5;
1314 text = vim_strnsave(arg, (int)(p - arg));
1315 }
1316 else if (STRNCMP(arg, "linehl=", 7) == 0)
1317 {
1318 arg += 7;
1319 linehl = vim_strnsave(arg, (int)(p - arg));
1320 }
1321 else if (STRNCMP(arg, "texthl=", 7) == 0)
1322 {
1323 arg += 7;
1324 texthl = vim_strnsave(arg, (int)(p - arg));
1325 }
1326 else
1327 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001328 semsg(_(e_invarg2), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001329 failed = TRUE;
1330 break;
1331 }
1332 }
1333
1334 if (!failed)
1335 sign_define_by_name(sign_name, icon, linehl, text, texthl);
1336
1337 vim_free(icon);
1338 vim_free(text);
1339 vim_free(linehl);
1340 vim_free(texthl);
1341}
1342
1343/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001344 * ":sign place" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001345 */
1346 static void
1347sign_place_cmd(
1348 buf_T *buf,
1349 linenr_T lnum,
1350 char_u *sign_name,
1351 int id,
1352 char_u *group,
1353 int prio)
1354{
1355 if (id <= 0)
1356 {
1357 // List signs placed in a file/buffer
1358 // :sign place file={fname}
1359 // :sign place group={group} file={fname}
1360 // :sign place group=* file={fname}
1361 // :sign place buffer={nr}
1362 // :sign place group={group} buffer={nr}
1363 // :sign place group=* buffer={nr}
1364 // :sign place
1365 // :sign place group={group}
1366 // :sign place group=*
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001367 if (lnum >= 0 || sign_name != NULL
1368 || (group != NULL && *group == '\0'))
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001369 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001370 else
1371 sign_list_placed(buf, group);
1372 }
1373 else
1374 {
1375 // Place a new sign
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001376 if (sign_name == NULL || buf == NULL
1377 || (group != NULL && *group == '\0'))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001378 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001379 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001380 return;
1381 }
1382
1383 sign_place(&id, group, sign_name, buf, lnum, prio);
1384 }
1385}
1386
1387/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001388 * ":sign unplace" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001389 */
1390 static void
1391sign_unplace_cmd(
1392 buf_T *buf,
1393 linenr_T lnum,
1394 char_u *sign_name,
1395 int id,
1396 char_u *group)
1397{
1398 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0'))
1399 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001400 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001401 return;
1402 }
1403
1404 if (id == -2)
1405 {
1406 if (buf != NULL)
1407 // :sign unplace * file={fname}
1408 // :sign unplace * group={group} file={fname}
1409 // :sign unplace * group=* file={fname}
1410 // :sign unplace * buffer={nr}
1411 // :sign unplace * group={group} buffer={nr}
1412 // :sign unplace * group=* buffer={nr}
1413 sign_unplace(0, group, buf, 0);
1414 else
1415 // :sign unplace *
1416 // :sign unplace * group={group}
1417 // :sign unplace * group=*
1418 FOR_ALL_BUFFERS(buf)
1419 if (buf->b_signlist != NULL)
1420 buf_delete_signs(buf, group);
1421 }
1422 else
1423 {
1424 if (buf != NULL)
1425 // :sign unplace {id} file={fname}
1426 // :sign unplace {id} group={group} file={fname}
1427 // :sign unplace {id} group=* file={fname}
1428 // :sign unplace {id} buffer={nr}
1429 // :sign unplace {id} group={group} buffer={nr}
1430 // :sign unplace {id} group=* buffer={nr}
1431 sign_unplace(id, group, buf, 0);
1432 else
1433 {
1434 if (id == -1)
1435 {
1436 // :sign unplace group={group}
1437 // :sign unplace group=*
1438 sign_unplace_at_cursor(group);
1439 }
1440 else
1441 {
1442 // :sign unplace {id}
1443 // :sign unplace {id} group={group}
1444 // :sign unplace {id} group=*
1445 FOR_ALL_BUFFERS(buf)
1446 sign_unplace(id, group, buf, 0);
1447 }
1448 }
1449 }
1450}
1451
1452/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001453 * Jump to a placed sign commands:
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001454 * :sign jump {id} file={fname}
1455 * :sign jump {id} buffer={nr}
1456 * :sign jump {id} group={group} file={fname}
1457 * :sign jump {id} group={group} buffer={nr}
1458 */
1459 static void
1460sign_jump_cmd(
1461 buf_T *buf,
1462 linenr_T lnum,
1463 char_u *sign_name,
1464 int id,
1465 char_u *group)
1466{
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001467 if (sign_name == NULL && group == NULL && id == -1)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001468 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001469 emsg(_(e_argreq));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001470 return;
1471 }
1472
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001473 if (buf == NULL || (group != NULL && *group == '\0')
1474 || lnum >= 0 || sign_name != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001475 {
1476 // File or buffer is not specified or an empty group is used
1477 // or a line number or a sign name is specified.
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001478 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001479 return;
1480 }
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001481 (void)sign_jump(id, group, buf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001482}
1483
1484/*
1485 * Parse the command line arguments for the ":sign place", ":sign unplace" and
1486 * ":sign jump" commands.
1487 * The supported arguments are: line={lnum} name={name} group={group}
1488 * priority={prio} and file={fname} or buffer={nr}.
1489 */
1490 static int
1491parse_sign_cmd_args(
1492 int cmd,
1493 char_u *arg,
1494 char_u **sign_name,
1495 int *signid,
1496 char_u **group,
1497 int *prio,
1498 buf_T **buf,
1499 linenr_T *lnum)
1500{
1501 char_u *arg1;
1502 char_u *name;
1503 char_u *filename = NULL;
Bram Moolenaarb589f952019-01-07 22:10:00 +01001504 int lnum_arg = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001505
1506 // first arg could be placed sign id
1507 arg1 = arg;
1508 if (VIM_ISDIGIT(*arg))
1509 {
1510 *signid = getdigits(&arg);
1511 if (!VIM_ISWHITE(*arg) && *arg != NUL)
1512 {
1513 *signid = -1;
1514 arg = arg1;
1515 }
1516 else
1517 arg = skipwhite(arg);
1518 }
1519
1520 while (*arg != NUL)
1521 {
1522 if (STRNCMP(arg, "line=", 5) == 0)
1523 {
1524 arg += 5;
1525 *lnum = atoi((char *)arg);
1526 arg = skiptowhite(arg);
Bram Moolenaarb589f952019-01-07 22:10:00 +01001527 lnum_arg = TRUE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001528 }
1529 else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE)
1530 {
1531 if (*signid != -1)
1532 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001533 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001534 return FAIL;
1535 }
1536 *signid = -2;
1537 arg = skiptowhite(arg + 1);
1538 }
1539 else if (STRNCMP(arg, "name=", 5) == 0)
1540 {
1541 arg += 5;
1542 name = arg;
1543 arg = skiptowhite(arg);
1544 if (*arg != NUL)
1545 *arg++ = NUL;
1546 while (name[0] == '0' && name[1] != NUL)
1547 ++name;
1548 *sign_name = name;
1549 }
1550 else if (STRNCMP(arg, "group=", 6) == 0)
1551 {
1552 arg += 6;
1553 *group = arg;
1554 arg = skiptowhite(arg);
1555 if (*arg != NUL)
1556 *arg++ = NUL;
1557 }
1558 else if (STRNCMP(arg, "priority=", 9) == 0)
1559 {
1560 arg += 9;
1561 *prio = atoi((char *)arg);
1562 arg = skiptowhite(arg);
1563 }
1564 else if (STRNCMP(arg, "file=", 5) == 0)
1565 {
1566 arg += 5;
1567 filename = arg;
1568 *buf = buflist_findname_exp(arg);
1569 break;
1570 }
1571 else if (STRNCMP(arg, "buffer=", 7) == 0)
1572 {
1573 arg += 7;
1574 filename = arg;
1575 *buf = buflist_findnr((int)getdigits(&arg));
1576 if (*skipwhite(arg) != NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001577 emsg(_(e_trailing));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001578 break;
1579 }
1580 else
1581 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001582 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001583 return FAIL;
1584 }
1585 arg = skipwhite(arg);
1586 }
1587
1588 if (filename != NULL && *buf == NULL)
1589 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001590 semsg(_("E158: Invalid buffer name: %s"), filename);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001591 return FAIL;
1592 }
1593
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001594 // If the filename is not supplied for the sign place or the sign jump
1595 // command, then use the current buffer.
Bram Moolenaarb589f952019-01-07 22:10:00 +01001596 if (filename == NULL && ((cmd == SIGNCMD_PLACE && lnum_arg)
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001597 || cmd == SIGNCMD_JUMP))
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001598 *buf = curwin->w_buffer;
1599
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001600 return OK;
1601}
1602
1603/*
1604 * ":sign" command
1605 */
1606 void
1607ex_sign(exarg_T *eap)
1608{
1609 char_u *arg = eap->arg;
1610 char_u *p;
1611 int idx;
1612 sign_T *sp;
1613 buf_T *buf = NULL;
1614
1615 // Parse the subcommand.
1616 p = skiptowhite(arg);
1617 idx = sign_cmd_idx(arg, p);
1618 if (idx == SIGNCMD_LAST)
1619 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001620 semsg(_("E160: Unknown sign command: %s"), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001621 return;
1622 }
1623 arg = skipwhite(p);
1624
1625 if (idx <= SIGNCMD_LIST)
1626 {
1627 // Define, undefine or list signs.
1628 if (idx == SIGNCMD_LIST && *arg == NUL)
1629 {
1630 // ":sign list": list all defined signs
1631 for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next)
1632 sign_list_defined(sp);
1633 }
1634 else if (*arg == NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001635 emsg(_("E156: Missing sign name"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001636 else
1637 {
1638 char_u *name;
1639
1640 // Isolate the sign name. If it's a number skip leading zeroes,
1641 // so that "099" and "99" are the same sign. But keep "0".
1642 p = skiptowhite(arg);
1643 if (*p != NUL)
1644 *p++ = NUL;
1645 while (arg[0] == '0' && arg[1] != NUL)
1646 ++arg;
1647 name = vim_strsave(arg);
1648
1649 if (idx == SIGNCMD_DEFINE)
1650 sign_define_cmd(name, p);
1651 else if (idx == SIGNCMD_LIST)
1652 // ":sign list {name}"
1653 sign_list_by_name(name);
1654 else
1655 // ":sign undefine {name}"
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001656 sign_undefine_by_name(name, TRUE);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001657
1658 vim_free(name);
1659 return;
1660 }
1661 }
1662 else
1663 {
1664 int id = -1;
1665 linenr_T lnum = -1;
1666 char_u *sign_name = NULL;
1667 char_u *group = NULL;
1668 int prio = SIGN_DEF_PRIO;
1669
1670 // Parse command line arguments
1671 if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio,
1672 &buf, &lnum) == FAIL)
1673 return;
1674
1675 if (idx == SIGNCMD_PLACE)
1676 sign_place_cmd(buf, lnum, sign_name, id, group, prio);
1677 else if (idx == SIGNCMD_UNPLACE)
1678 sign_unplace_cmd(buf, lnum, sign_name, id, group);
1679 else if (idx == SIGNCMD_JUMP)
1680 sign_jump_cmd(buf, lnum, sign_name, id, group);
1681 }
1682}
1683
1684/*
1685 * Return information about a specified sign
1686 */
1687 static void
1688sign_getinfo(sign_T *sp, dict_T *retdict)
1689{
1690 char_u *p;
1691
1692 dict_add_string(retdict, "name", (char_u *)sp->sn_name);
1693 if (sp->sn_icon != NULL)
1694 dict_add_string(retdict, "icon", (char_u *)sp->sn_icon);
1695 if (sp->sn_text != NULL)
1696 dict_add_string(retdict, "text", (char_u *)sp->sn_text);
1697 if (sp->sn_line_hl > 0)
1698 {
1699 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1700 if (p == NULL)
1701 p = (char_u *)"NONE";
1702 dict_add_string(retdict, "linehl", (char_u *)p);
1703 }
1704 if (sp->sn_text_hl > 0)
1705 {
1706 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1707 if (p == NULL)
1708 p = (char_u *)"NONE";
1709 dict_add_string(retdict, "texthl", (char_u *)p);
1710 }
1711}
1712
1713/*
1714 * If 'name' is NULL, return a list of all the defined signs.
1715 * Otherwise, return information about the specified sign.
1716 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001717 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001718sign_getlist(char_u *name, list_T *retlist)
1719{
1720 sign_T *sp = first_sign;
1721 dict_T *dict;
1722
1723 if (name != NULL)
1724 {
1725 sp = sign_find(name, NULL);
1726 if (sp == NULL)
1727 return;
1728 }
1729
1730 for (; sp != NULL && !got_int; sp = sp->sn_next)
1731 {
1732 if ((dict = dict_alloc_id(aid_sign_getlist)) == NULL)
1733 return;
1734 if (list_append_dict(retlist, dict) == FAIL)
1735 return;
1736 sign_getinfo(sp, dict);
1737
1738 if (name != NULL) // handle only the specified sign
1739 break;
1740 }
1741}
1742
1743/*
1744 * Returns information about signs placed in a buffer as list of dicts.
1745 */
1746 void
1747get_buffer_signs(buf_T *buf, list_T *l)
1748{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001749 sign_entry_T *sign;
1750 dict_T *d;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001751
1752 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1753 {
1754 if ((d = sign_get_info(sign)) != NULL)
1755 list_append_dict(l, d);
1756 }
1757}
1758
1759/*
1760 * Return information about all the signs placed in a buffer
1761 */
1762 static void
1763sign_get_placed_in_buf(
1764 buf_T *buf,
1765 linenr_T lnum,
1766 int sign_id,
1767 char_u *sign_group,
1768 list_T *retlist)
1769{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001770 dict_T *d;
1771 list_T *l;
1772 sign_entry_T *sign;
1773 dict_T *sdict;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001774
1775 if ((d = dict_alloc_id(aid_sign_getplaced_dict)) == NULL)
1776 return;
1777 list_append_dict(retlist, d);
1778
1779 dict_add_number(d, "bufnr", (long)buf->b_fnum);
1780
1781 if ((l = list_alloc_id(aid_sign_getplaced_list)) == NULL)
1782 return;
1783 dict_add_list(d, "signs", l);
1784
1785 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1786 {
1787 if (!sign_in_group(sign, sign_group))
1788 continue;
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001789 if ((lnum == 0 && sign_id == 0)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001790 || (sign_id == 0 && lnum == sign->se_lnum)
1791 || (lnum == 0 && sign_id == sign->se_id)
1792 || (lnum == sign->se_lnum && sign_id == sign->se_id))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001793 {
1794 if ((sdict = sign_get_info(sign)) != NULL)
1795 list_append_dict(l, sdict);
1796 }
1797 }
1798}
1799
1800/*
1801 * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the
1802 * sign placed at the line number. If 'lnum' is zero, return all the signs
1803 * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers.
1804 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001805 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001806sign_get_placed(
1807 buf_T *buf,
1808 linenr_T lnum,
1809 int sign_id,
1810 char_u *sign_group,
1811 list_T *retlist)
1812{
1813 if (buf != NULL)
1814 sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist);
1815 else
1816 {
1817 FOR_ALL_BUFFERS(buf)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001818 if (buf->b_signlist != NULL)
1819 sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001820 }
1821}
1822
1823# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1824/*
1825 * Allocate the icons. Called when the GUI has started. Allows defining
1826 * signs before it starts.
1827 */
1828 void
1829sign_gui_started(void)
1830{
1831 sign_T *sp;
1832
1833 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1834 if (sp->sn_icon != NULL)
1835 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
1836}
1837# endif
1838
1839/*
1840 * List one sign.
1841 */
1842 static void
1843sign_list_defined(sign_T *sp)
1844{
1845 char_u *p;
1846
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001847 smsg("sign %s", sp->sn_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001848 if (sp->sn_icon != NULL)
1849 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001850 msg_puts(" icon=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001851 msg_outtrans(sp->sn_icon);
1852# ifdef FEAT_SIGN_ICONS
1853 if (sp->sn_image == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001854 msg_puts(_(" (NOT FOUND)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001855# else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001856 msg_puts(_(" (not supported)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001857# endif
1858 }
1859 if (sp->sn_text != NULL)
1860 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001861 msg_puts(" text=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001862 msg_outtrans(sp->sn_text);
1863 }
1864 if (sp->sn_line_hl > 0)
1865 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001866 msg_puts(" linehl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001867 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1868 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001869 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001870 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001871 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001872 }
1873 if (sp->sn_text_hl > 0)
1874 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001875 msg_puts(" texthl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001876 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1877 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001878 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001879 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001880 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001881 }
1882}
1883
1884/*
1885 * Undefine a sign and free its memory.
1886 */
1887 static void
1888sign_undefine(sign_T *sp, sign_T *sp_prev)
1889{
1890 vim_free(sp->sn_name);
1891 vim_free(sp->sn_icon);
1892# ifdef FEAT_SIGN_ICONS
1893 if (sp->sn_image != NULL)
1894 {
1895 out_flush();
1896 gui_mch_destroy_sign(sp->sn_image);
1897 }
1898# endif
1899 vim_free(sp->sn_text);
1900 if (sp_prev == NULL)
1901 first_sign = sp->sn_next;
1902 else
1903 sp_prev->sn_next = sp->sn_next;
1904 vim_free(sp);
1905}
1906
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001907# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1908 void *
1909sign_get_image(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001910 int typenr) // the attribute which may have a sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001911{
1912 sign_T *sp;
1913
1914 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1915 if (sp->sn_typenr == typenr)
1916 return sp->sn_image;
1917 return NULL;
1918}
1919# endif
1920
1921/*
1922 * Undefine/free all signs.
1923 */
1924 void
1925free_signs(void)
1926{
1927 while (first_sign != NULL)
1928 sign_undefine(first_sign, NULL);
1929}
1930
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001931static enum
1932{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001933 EXP_SUBCMD, // expand :sign sub-commands
1934 EXP_DEFINE, // expand :sign define {name} args
1935 EXP_PLACE, // expand :sign place {id} args
Bram Moolenaar3678f652019-02-17 14:50:25 +01001936 EXP_LIST, // expand :sign place args
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001937 EXP_UNPLACE, // expand :sign unplace"
Bram Moolenaar3678f652019-02-17 14:50:25 +01001938 EXP_SIGN_NAMES, // expand with name of placed signs
1939 EXP_SIGN_GROUPS // expand with name of placed sign groups
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001940} expand_what;
1941
1942/*
Bram Moolenaar3678f652019-02-17 14:50:25 +01001943 * Return the n'th sign name (used for command line completion)
1944 */
1945 static char_u *
1946get_nth_sign_name(int idx)
1947{
1948 int current_idx;
1949 sign_T *sp;
1950
1951 // Complete with name of signs already defined
1952 current_idx = 0;
1953 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
1954 if (current_idx++ == idx)
1955 return sp->sn_name;
1956 return NULL;
1957}
1958
1959/*
1960 * Return the n'th sign group name (used for command line completion)
1961 */
1962 static char_u *
1963get_nth_sign_group_name(int idx)
1964{
1965 int current_idx;
1966 int todo;
1967 hashitem_T *hi;
1968 signgroup_T *group;
1969
1970 // Complete with name of sign groups already defined
1971 current_idx = 0;
1972 todo = (int)sg_table.ht_used;
1973 for (hi = sg_table.ht_array; todo > 0; ++hi)
1974 {
1975 if (!HASHITEM_EMPTY(hi))
1976 {
1977 --todo;
1978 if (current_idx++ == idx)
1979 {
1980 group = HI2SG(hi);
1981 return group->sg_name;
1982 }
1983 }
1984 }
1985 return NULL;
1986}
1987
1988/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001989 * Function given to ExpandGeneric() to obtain the sign command
1990 * expansion.
1991 */
1992 char_u *
1993get_sign_name(expand_T *xp UNUSED, int idx)
1994{
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001995 switch (expand_what)
1996 {
1997 case EXP_SUBCMD:
1998 return (char_u *)cmds[idx];
1999 case EXP_DEFINE:
2000 {
2001 char *define_arg[] =
2002 {
2003 "icon=", "linehl=", "text=", "texthl=", NULL
2004 };
2005 return (char_u *)define_arg[idx];
2006 }
2007 case EXP_PLACE:
2008 {
2009 char *place_arg[] =
2010 {
2011 "line=", "name=", "group=", "priority=", "file=",
2012 "buffer=", NULL
2013 };
2014 return (char_u *)place_arg[idx];
2015 }
Bram Moolenaar3678f652019-02-17 14:50:25 +01002016 case EXP_LIST:
2017 {
2018 char *list_arg[] =
2019 {
2020 "group=", "file=", "buffer=", NULL
2021 };
2022 return (char_u *)list_arg[idx];
2023 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002024 case EXP_UNPLACE:
2025 {
2026 char *unplace_arg[] = { "group=", "file=", "buffer=", NULL };
2027 return (char_u *)unplace_arg[idx];
2028 }
2029 case EXP_SIGN_NAMES:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002030 return get_nth_sign_name(idx);
2031 case EXP_SIGN_GROUPS:
2032 return get_nth_sign_group_name(idx);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002033 default:
2034 return NULL;
2035 }
2036}
2037
2038/*
2039 * Handle command line completion for :sign command.
2040 */
2041 void
2042set_context_in_sign_cmd(expand_T *xp, char_u *arg)
2043{
2044 char_u *p;
2045 char_u *end_subcmd;
2046 char_u *last;
2047 int cmd_idx;
2048 char_u *begin_subcmd_args;
2049
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002050 // Default: expand subcommands.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002051 xp->xp_context = EXPAND_SIGN;
2052 expand_what = EXP_SUBCMD;
2053 xp->xp_pattern = arg;
2054
2055 end_subcmd = skiptowhite(arg);
2056 if (*end_subcmd == NUL)
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002057 // expand subcmd name
2058 // :sign {subcmd}<CTRL-D>
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002059 return;
2060
2061 cmd_idx = sign_cmd_idx(arg, end_subcmd);
2062
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002063 // :sign {subcmd} {subcmd_args}
2064 // |
2065 // begin_subcmd_args
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002066 begin_subcmd_args = skipwhite(end_subcmd);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002067
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002068 // expand last argument of subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002069
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002070 // :sign define {name} {args}...
2071 // |
2072 // p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002073
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002074 // Loop until reaching last argument.
Bram Moolenaar3678f652019-02-17 14:50:25 +01002075 p = begin_subcmd_args;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002076 do
2077 {
2078 p = skipwhite(p);
2079 last = p;
2080 p = skiptowhite(p);
2081 } while (*p != NUL);
2082
2083 p = vim_strchr(last, '=');
2084
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002085 // :sign define {name} {args}... {last}=
2086 // | |
2087 // last p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002088 if (p == NULL)
2089 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002090 // Expand last argument name (before equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002091 xp->xp_pattern = last;
2092 switch (cmd_idx)
2093 {
2094 case SIGNCMD_DEFINE:
2095 expand_what = EXP_DEFINE;
2096 break;
2097 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002098 // List placed signs
2099 if (VIM_ISDIGIT(*begin_subcmd_args))
2100 // :sign place {id} {args}...
2101 expand_what = EXP_PLACE;
2102 else
2103 // :sign place {args}...
2104 expand_what = EXP_LIST;
2105 break;
2106 case SIGNCMD_LIST:
2107 case SIGNCMD_UNDEFINE:
2108 // :sign list <CTRL-D>
2109 // :sign undefine <CTRL-D>
2110 expand_what = EXP_SIGN_NAMES;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002111 break;
2112 case SIGNCMD_JUMP:
2113 case SIGNCMD_UNPLACE:
2114 expand_what = EXP_UNPLACE;
2115 break;
2116 default:
2117 xp->xp_context = EXPAND_NOTHING;
2118 }
2119 }
2120 else
2121 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002122 // Expand last argument value (after equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002123 xp->xp_pattern = p + 1;
2124 switch (cmd_idx)
2125 {
2126 case SIGNCMD_DEFINE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002127 if (STRNCMP(last, "texthl", 6) == 0
2128 || STRNCMP(last, "linehl", 6) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002129 xp->xp_context = EXPAND_HIGHLIGHT;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002130 else if (STRNCMP(last, "icon", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002131 xp->xp_context = EXPAND_FILES;
2132 else
2133 xp->xp_context = EXPAND_NOTHING;
2134 break;
2135 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002136 if (STRNCMP(last, "name", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002137 expand_what = EXP_SIGN_NAMES;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002138 else if (STRNCMP(last, "group", 5) == 0)
2139 expand_what = EXP_SIGN_GROUPS;
2140 else if (STRNCMP(last, "file", 4) == 0)
2141 xp->xp_context = EXPAND_BUFFERS;
2142 else
2143 xp->xp_context = EXPAND_NOTHING;
2144 break;
2145 case SIGNCMD_UNPLACE:
2146 case SIGNCMD_JUMP:
2147 if (STRNCMP(last, "group", 5) == 0)
2148 expand_what = EXP_SIGN_GROUPS;
2149 else if (STRNCMP(last, "file", 4) == 0)
2150 xp->xp_context = EXPAND_BUFFERS;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002151 else
2152 xp->xp_context = EXPAND_NOTHING;
2153 break;
2154 default:
2155 xp->xp_context = EXPAND_NOTHING;
2156 }
2157 }
2158}
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002159
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002160/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002161 * Define a sign using the attributes in 'dict'. Returns 0 on success and -1 on
2162 * failure.
2163 */
2164 static int
2165sign_define_from_dict(char_u *name_arg, dict_T *dict)
2166{
2167 char_u *name = NULL;
2168 char_u *icon = NULL;
2169 char_u *linehl = NULL;
2170 char_u *text = NULL;
2171 char_u *texthl = NULL;
2172 int retval = -1;
2173
2174 if (name_arg == NULL)
2175 {
2176 if (dict == NULL)
2177 return -1;
2178 name = dict_get_string(dict, (char_u *)"name", TRUE);
2179 }
2180 else
2181 name = vim_strsave(name_arg);
2182 if (name == NULL || name[0] == NUL)
2183 goto cleanup;
2184 if (dict != NULL)
2185 {
2186 icon = dict_get_string(dict, (char_u *)"icon", TRUE);
2187 linehl = dict_get_string(dict, (char_u *)"linehl", TRUE);
2188 text = dict_get_string(dict, (char_u *)"text", TRUE);
2189 texthl = dict_get_string(dict, (char_u *)"texthl", TRUE);
2190 }
2191
2192 if (sign_define_by_name(name, icon, linehl, text, texthl) == OK)
2193 retval = 0;
2194
2195cleanup:
2196 vim_free(name);
2197 vim_free(icon);
2198 vim_free(linehl);
2199 vim_free(text);
2200 vim_free(texthl);
2201
2202 return retval;
2203}
2204
2205/*
2206 * Define multiple signs using attributes from list 'l' and store the return
2207 * values in 'retlist'.
2208 */
2209 static void
2210sign_define_multiple(list_T *l, list_T *retlist)
2211{
2212 listitem_T *li;
2213 int retval;
2214
2215 for (li = l->lv_first; li != NULL; li = li->li_next)
2216 {
2217 retval = -1;
2218 if (li->li_tv.v_type == VAR_DICT)
2219 retval = sign_define_from_dict(NULL, li->li_tv.vval.v_dict);
2220 else
2221 emsg(_(e_dictreq));
2222 list_append_number(retlist, retval);
2223 }
2224}
2225
2226/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002227 * "sign_define()" function
2228 */
2229 void
2230f_sign_define(typval_T *argvars, typval_T *rettv)
2231{
2232 char_u *name;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002233
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002234 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2235 {
2236 // Define multiple signs
2237 if (rettv_list_alloc(rettv) != OK)
2238 return;
2239
2240 sign_define_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2241 return;
2242 }
2243
2244 // Define a single sign
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002245 rettv->vval.v_number = -1;
2246
2247 name = tv_get_string_chk(&argvars[0]);
2248 if (name == NULL)
2249 return;
2250
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002251 if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_DICT)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002252 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002253 emsg(_(e_dictreq));
2254 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002255 }
2256
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002257 rettv->vval.v_number = sign_define_from_dict(name,
2258 argvars[1].v_type == VAR_DICT ? argvars[1].vval.v_dict : NULL);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002259}
2260
2261/*
2262 * "sign_getdefined()" function
2263 */
2264 void
2265f_sign_getdefined(typval_T *argvars, typval_T *rettv)
2266{
2267 char_u *name = NULL;
2268
2269 if (rettv_list_alloc_id(rettv, aid_sign_getdefined) != OK)
2270 return;
2271
2272 if (argvars[0].v_type != VAR_UNKNOWN)
2273 name = tv_get_string(&argvars[0]);
2274
2275 sign_getlist(name, rettv->vval.v_list);
2276}
2277
2278/*
2279 * "sign_getplaced()" function
2280 */
2281 void
2282f_sign_getplaced(typval_T *argvars, typval_T *rettv)
2283{
2284 buf_T *buf = NULL;
2285 dict_T *dict;
2286 dictitem_T *di;
2287 linenr_T lnum = 0;
2288 int sign_id = 0;
2289 char_u *group = NULL;
2290 int notanum = FALSE;
2291
2292 if (rettv_list_alloc_id(rettv, aid_sign_getplaced) != OK)
2293 return;
2294
2295 if (argvars[0].v_type != VAR_UNKNOWN)
2296 {
2297 // get signs placed in the specified buffer
2298 buf = get_buf_arg(&argvars[0]);
2299 if (buf == NULL)
2300 return;
2301
2302 if (argvars[1].v_type != VAR_UNKNOWN)
2303 {
2304 if (argvars[1].v_type != VAR_DICT ||
2305 ((dict = argvars[1].vval.v_dict) == NULL))
2306 {
2307 emsg(_(e_dictreq));
2308 return;
2309 }
2310 if ((di = dict_find(dict, (char_u *)"lnum", -1)) != NULL)
2311 {
2312 // get signs placed at this line
2313 (void)tv_get_number_chk(&di->di_tv, &notanum);
2314 if (notanum)
2315 return;
2316 lnum = tv_get_lnum(&di->di_tv);
2317 }
2318 if ((di = dict_find(dict, (char_u *)"id", -1)) != NULL)
2319 {
2320 // get sign placed with this identifier
2321 sign_id = (int)tv_get_number_chk(&di->di_tv, &notanum);
2322 if (notanum)
2323 return;
2324 }
2325 if ((di = dict_find(dict, (char_u *)"group", -1)) != NULL)
2326 {
2327 group = tv_get_string_chk(&di->di_tv);
2328 if (group == NULL)
2329 return;
2330 if (*group == '\0') // empty string means global group
2331 group = NULL;
2332 }
2333 }
2334 }
2335
2336 sign_get_placed(buf, lnum, sign_id, group, rettv->vval.v_list);
2337}
2338
2339/*
2340 * "sign_jump()" function
2341 */
2342 void
2343f_sign_jump(typval_T *argvars, typval_T *rettv)
2344{
2345 int sign_id;
2346 char_u *sign_group = NULL;
2347 buf_T *buf;
2348 int notanum = FALSE;
2349
2350 rettv->vval.v_number = -1;
2351
2352 // Sign identifier
2353 sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
2354 if (notanum)
2355 return;
2356 if (sign_id <= 0)
2357 {
2358 emsg(_(e_invarg));
2359 return;
2360 }
2361
2362 // Sign group
2363 sign_group = tv_get_string_chk(&argvars[1]);
2364 if (sign_group == NULL)
2365 return;
2366 if (sign_group[0] == '\0')
2367 sign_group = NULL; // global sign group
2368 else
2369 {
2370 sign_group = vim_strsave(sign_group);
2371 if (sign_group == NULL)
2372 return;
2373 }
2374
2375 // Buffer to place the sign
2376 buf = get_buf_arg(&argvars[2]);
2377 if (buf == NULL)
2378 goto cleanup;
2379
2380 rettv->vval.v_number = sign_jump(sign_id, sign_group, buf);
2381
2382cleanup:
2383 vim_free(sign_group);
2384}
2385
2386/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002387 * Place a new sign using the values specified in dict 'dict'. Returns the sign
2388 * identifier if successfully placed, otherwise returns 0.
2389 */
2390 static int
2391sign_place_from_dict(
2392 typval_T *id_tv,
2393 typval_T *group_tv,
2394 typval_T *name_tv,
2395 typval_T *buf_tv,
2396 dict_T *dict)
2397{
2398 int sign_id = 0;
2399 char_u *group = NULL;
2400 char_u *sign_name = NULL;
2401 buf_T *buf = NULL;
2402 dictitem_T *di;
2403 linenr_T lnum = 0;
2404 int prio = SIGN_DEF_PRIO;
2405 int notanum = FALSE;
2406 int ret_sign_id = -1;
2407
2408 // sign identifier
2409 if (id_tv == NULL)
2410 {
2411 di = dict_find(dict, (char_u *)"id", -1);
2412 if (di != NULL)
2413 id_tv = &di->di_tv;
2414 }
2415 if (id_tv == NULL)
2416 sign_id = 0;
2417 else
2418 {
2419 sign_id = tv_get_number_chk(id_tv, &notanum);
2420 if (notanum)
2421 return -1;
2422 if (sign_id < 0)
2423 {
2424 emsg(_(e_invarg));
2425 return -1;
2426 }
2427 }
2428
2429 // sign group
2430 if (group_tv == NULL)
2431 {
2432 di = dict_find(dict, (char_u *)"group", -1);
2433 if (di != NULL)
2434 group_tv = &di->di_tv;
2435 }
2436 if (group_tv == NULL)
2437 group = NULL; // global group
2438 else
2439 {
2440 group = tv_get_string_chk(group_tv);
2441 if (group == NULL)
2442 goto cleanup;
2443 if (group[0] == '\0') // global sign group
2444 group = NULL;
2445 else
2446 {
2447 group = vim_strsave(group);
2448 if (group == NULL)
2449 return -1;
2450 }
2451 }
2452
2453 // sign name
2454 if (name_tv == NULL)
2455 {
2456 di = dict_find(dict, (char_u *)"name", -1);
2457 if (di != NULL)
2458 name_tv = &di->di_tv;
2459 }
2460 if (name_tv == NULL)
2461 goto cleanup;
2462 sign_name = tv_get_string_chk(name_tv);
2463 if (sign_name == NULL)
2464 goto cleanup;
2465
2466 // buffer to place the sign
2467 if (buf_tv == NULL)
2468 {
2469 di = dict_find(dict, (char_u *)"buffer", -1);
2470 if (di != NULL)
2471 buf_tv = &di->di_tv;
2472 }
2473 if (buf_tv == NULL)
2474 goto cleanup;
2475 buf = get_buf_arg(buf_tv);
2476 if (buf == NULL)
2477 goto cleanup;
2478
2479 // line number of the sign
2480 di = dict_find(dict, (char_u *)"lnum", -1);
2481 if (di != NULL)
2482 {
Bram Moolenaar42aff462019-08-21 13:20:29 +02002483 lnum = tv_get_lnum(&di->di_tv);
2484 if (lnum <= 0)
2485 {
2486 emsg(_(e_invarg));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002487 goto cleanup;
Bram Moolenaar42aff462019-08-21 13:20:29 +02002488 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002489 }
2490
2491 // sign priority
2492 di = dict_find(dict, (char_u *)"priority", -1);
2493 if (di != NULL)
2494 {
2495 prio = (int)tv_get_number_chk(&di->di_tv, &notanum);
2496 if (notanum)
2497 goto cleanup;
2498 }
2499
2500 if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK)
2501 ret_sign_id = sign_id;
2502
2503cleanup:
2504 vim_free(group);
2505
2506 return ret_sign_id;
2507}
2508
2509/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002510 * "sign_place()" function
2511 */
2512 void
2513f_sign_place(typval_T *argvars, typval_T *rettv)
2514{
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002515 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002516
2517 rettv->vval.v_number = -1;
2518
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002519 if (argvars[4].v_type != VAR_UNKNOWN
2520 && (argvars[4].v_type != VAR_DICT
2521 || ((dict = argvars[4].vval.v_dict) == NULL)))
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002522 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002523 emsg(_(e_dictreq));
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002524 return;
2525 }
2526
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002527 rettv->vval.v_number = sign_place_from_dict(&argvars[0], &argvars[1],
2528 &argvars[2], &argvars[3], dict);
2529}
2530
2531/*
2532 * "sign_placelist()" function. Place multiple signs.
2533 */
2534 void
2535f_sign_placelist(typval_T *argvars, typval_T *rettv)
2536{
2537 listitem_T *li;
2538 int sign_id;
2539
2540 if (rettv_list_alloc(rettv) != OK)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002541 return;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002542
2543 if (argvars[0].v_type != VAR_LIST)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002544 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002545 emsg(_(e_listreq));
2546 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002547 }
2548
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002549 // Process the List of sign attributes
2550 for (li = argvars[0].vval.v_list->lv_first; li != NULL; li = li->li_next)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002551 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002552 sign_id = -1;
2553 if (li->li_tv.v_type == VAR_DICT)
2554 sign_id = sign_place_from_dict(NULL, NULL, NULL, NULL,
2555 li->li_tv.vval.v_dict);
2556 else
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002557 emsg(_(e_dictreq));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002558 list_append_number(rettv->vval.v_list, sign_id);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002559 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002560}
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002561
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002562/*
2563 * Undefine multiple signs
2564 */
2565 static void
2566sign_undefine_multiple(list_T *l, list_T *retlist)
2567{
2568 char_u *name;
2569 listitem_T *li;
2570 int retval;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002571
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002572 for (li = l->lv_first; li != NULL; li = li->li_next)
2573 {
2574 retval = -1;
2575 name = tv_get_string_chk(&li->li_tv);
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002576 if (name != NULL && (sign_undefine_by_name(name, TRUE) == OK))
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002577 retval = 0;
2578 list_append_number(retlist, retval);
2579 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002580}
2581
2582/*
2583 * "sign_undefine()" function
2584 */
2585 void
2586f_sign_undefine(typval_T *argvars, typval_T *rettv)
2587{
2588 char_u *name;
2589
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002590 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2591 {
2592 // Undefine multiple signs
2593 if (rettv_list_alloc(rettv) != OK)
2594 return;
2595
2596 sign_undefine_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2597 return;
2598 }
2599
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002600 rettv->vval.v_number = -1;
2601
2602 if (argvars[0].v_type == VAR_UNKNOWN)
2603 {
2604 // Free all the signs
2605 free_signs();
2606 rettv->vval.v_number = 0;
2607 }
2608 else
2609 {
2610 // Free only the specified sign
2611 name = tv_get_string_chk(&argvars[0]);
2612 if (name == NULL)
2613 return;
2614
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002615 if (sign_undefine_by_name(name, TRUE) == OK)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002616 rettv->vval.v_number = 0;
2617 }
2618}
2619
2620/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002621 * Unplace the sign with attributes specified in 'dict'. Returns 0 on success
2622 * and -1 on failure.
2623 */
2624 static int
2625sign_unplace_from_dict(typval_T *group_tv, dict_T *dict)
2626{
2627 dictitem_T *di;
2628 int sign_id = 0;
2629 buf_T *buf = NULL;
2630 char_u *group = NULL;
2631 int retval = -1;
2632
2633 // sign group
2634 if (group_tv != NULL)
2635 group = tv_get_string(group_tv);
2636 else
2637 group = dict_get_string(dict, (char_u *)"group", FALSE);
2638 if (group != NULL)
2639 {
2640 if (group[0] == '\0') // global sign group
2641 group = NULL;
2642 else
2643 {
2644 group = vim_strsave(group);
2645 if (group == NULL)
2646 return -1;
2647 }
2648 }
2649
2650 if (dict != NULL)
2651 {
2652 if ((di = dict_find(dict, (char_u *)"buffer", -1)) != NULL)
2653 {
2654 buf = get_buf_arg(&di->di_tv);
2655 if (buf == NULL)
2656 goto cleanup;
2657 }
2658 if (dict_find(dict, (char_u *)"id", -1) != NULL)
2659 {
2660 sign_id = dict_get_number(dict, (char_u *)"id");
2661 if (sign_id <= 0)
2662 {
2663 emsg(_(e_invarg));
2664 goto cleanup;
2665 }
2666 }
2667 }
2668
2669 if (buf == NULL)
2670 {
2671 // Delete the sign in all the buffers
2672 retval = 0;
2673 FOR_ALL_BUFFERS(buf)
2674 if (sign_unplace(sign_id, group, buf, 0) != OK)
2675 retval = -1;
2676 }
2677 else if (sign_unplace(sign_id, group, buf, 0) == OK)
2678 retval = 0;
2679
2680cleanup:
2681 vim_free(group);
2682
2683 return retval;
2684}
2685
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002686 sign_entry_T *
2687get_first_valid_sign(win_T *wp)
2688{
2689 sign_entry_T *sign = wp->w_buffer->b_signlist;
2690
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002691# ifdef FEAT_PROP_POPUP
Bram Moolenaar72570732019-11-30 14:21:53 +01002692 while (sign != NULL && !sign_group_for_window(sign, wp))
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002693 sign = sign->se_next;
2694# endif
2695 return sign;
2696}
2697
2698/*
2699 * Return TRUE when window "wp" has a column to draw signs in.
2700 */
2701 int
2702signcolumn_on(win_T *wp)
2703{
2704 // If 'signcolumn' is set to 'number', signs are displayed in the 'number'
2705 // column (if present). Otherwise signs are to be displayed in the sign
2706 // column.
2707 if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u')
2708 return get_first_valid_sign(wp) != NULL && !wp->w_p_nu && !wp->w_p_rnu;
2709
2710 if (*wp->w_p_scl == 'n')
2711 return FALSE;
2712 if (*wp->w_p_scl == 'y')
2713 return TRUE;
2714 return (get_first_valid_sign(wp) != NULL
2715# ifdef FEAT_NETBEANS_INTG
2716 || wp->w_buffer->b_has_sign_column
2717# endif
2718 );
2719}
2720
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002721/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002722 * "sign_unplace()" function
2723 */
2724 void
2725f_sign_unplace(typval_T *argvars, typval_T *rettv)
2726{
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002727 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002728
2729 rettv->vval.v_number = -1;
2730
2731 if (argvars[0].v_type != VAR_STRING)
2732 {
2733 emsg(_(e_invarg));
2734 return;
2735 }
2736
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002737 if (argvars[1].v_type != VAR_UNKNOWN)
2738 {
2739 if (argvars[1].v_type != VAR_DICT)
2740 {
2741 emsg(_(e_dictreq));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002742 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002743 }
2744 dict = argvars[1].vval.v_dict;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002745 }
2746
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002747 rettv->vval.v_number = sign_unplace_from_dict(&argvars[0], dict);
2748}
2749
2750/*
2751 * "sign_unplacelist()" function
2752 */
2753 void
2754f_sign_unplacelist(typval_T *argvars, typval_T *rettv)
2755{
2756 listitem_T *li;
2757 int retval;
2758
2759 if (rettv_list_alloc(rettv) != OK)
2760 return;
2761
2762 if (argvars[0].v_type != VAR_LIST)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002763 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002764 emsg(_(e_listreq));
2765 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002766 }
2767
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002768 for (li = argvars[0].vval.v_list->lv_first; li != NULL; li = li->li_next)
2769 {
2770 retval = -1;
2771 if (li->li_tv.v_type == VAR_DICT)
2772 retval = sign_unplace_from_dict(NULL, li->li_tv.vval.v_dict);
2773 else
2774 emsg(_(e_dictreq));
2775 list_append_number(rettv->vval.v_list, retval);
2776 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002777}
2778
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002779#endif // FEAT_SIGNS