blob: 66a29db1d711ac61aa84789f8ab8d90512e4a266 [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 Moolenaare413ea02021-11-24 16:20:13 +000035 int sn_cul_hl; // highlight ID for text on current line when 'cursorline' is set
Bram Moolenaarbbea4702019-01-01 13:20:31 +010036};
37
38static sign_T *first_sign = NULL;
39static int next_sign_typenr = 1;
40
41static void sign_list_defined(sign_T *sp);
42static void sign_undefine(sign_T *sp, sign_T *sp_prev);
43
44static char *cmds[] = {
45 "define",
46# define SIGNCMD_DEFINE 0
47 "undefine",
48# define SIGNCMD_UNDEFINE 1
49 "list",
50# define SIGNCMD_LIST 2
51 "place",
52# define SIGNCMD_PLACE 3
53 "unplace",
54# define SIGNCMD_UNPLACE 4
55 "jump",
56# define SIGNCMD_JUMP 5
57 NULL
58# define SIGNCMD_LAST 6
59};
60
Bram Moolenaaraeea7212020-04-02 18:50:46 +020061#define FOR_ALL_SIGNS(sp) \
62 for ((sp) = first_sign; (sp) != NULL; (sp) = (sp)->sn_next)
63
Bram Moolenaarbbea4702019-01-01 13:20:31 +010064static hashtab_T sg_table; // sign group (signgroup_T) hashtable
65static int next_sign_id = 1; // next sign id in the global group
66
67/*
68 * Initialize data needed for managing signs
69 */
70 void
71init_signs(void)
72{
73 hash_init(&sg_table); // sign group hash table
74}
75
76/*
77 * A new sign in group 'groupname' is added. If the group is not present,
78 * create it. Otherwise reference the group.
79 */
80 static signgroup_T *
81sign_group_ref(char_u *groupname)
82{
83 hash_T hash;
84 hashitem_T *hi;
85 signgroup_T *group;
86
87 hash = hash_hash(groupname);
88 hi = hash_lookup(&sg_table, groupname, hash);
89 if (HASHITEM_EMPTY(hi))
90 {
91 // new group
Bram Moolenaar47ed5532019-08-08 20:49:14 +020092 group = alloc(offsetof(signgroup_T, sg_name) + STRLEN(groupname) + 1);
Bram Moolenaarbbea4702019-01-01 13:20:31 +010093 if (group == NULL)
94 return NULL;
95 STRCPY(group->sg_name, groupname);
Bram Moolenaar6656c2e2019-10-24 15:00:04 +020096 group->sg_refcount = 1;
97 group->sg_next_sign_id = 1;
Bram Moolenaarbbea4702019-01-01 13:20:31 +010098 hash_add_item(&sg_table, hi, group->sg_name, hash);
99 }
100 else
101 {
102 // existing group
103 group = HI2SG(hi);
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200104 group->sg_refcount++;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100105 }
106
107 return group;
108}
109
110/*
111 * A sign in group 'groupname' is removed. If all the signs in this group are
112 * removed, then remove the group.
113 */
114 static void
115sign_group_unref(char_u *groupname)
116{
117 hashitem_T *hi;
118 signgroup_T *group;
119
120 hi = hash_find(&sg_table, groupname);
121 if (!HASHITEM_EMPTY(hi))
122 {
123 group = HI2SG(hi);
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200124 group->sg_refcount--;
125 if (group->sg_refcount == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100126 {
127 // All the signs in this group are removed
128 hash_remove(&sg_table, hi);
129 vim_free(group);
130 }
131 }
132}
133
134/*
135 * Returns TRUE if 'sign' is in 'group'.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200136 * A sign can either be in the global group (sign->se_group == NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100137 * or in a named group. If 'group' is '*', then the sign is part of the group.
138 */
139 static int
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200140sign_in_group(sign_entry_T *sign, char_u *group)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100141{
142 return ((group != NULL && STRCMP(group, "*") == 0)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200143 || (group == NULL && sign->se_group == NULL)
144 || (group != NULL && sign->se_group != NULL
Bram Moolenaar72570732019-11-30 14:21:53 +0100145 && STRCMP(group, sign->se_group->sg_name) == 0));
146}
147
148/*
149 * Return TRUE if "sign" is to be displayed in window "wp".
150 * If the group name starts with "PopUp" it only shows in a popup window.
151 */
152 static int
153sign_group_for_window(sign_entry_T *sign, win_T *wp)
154{
155 int for_popup = sign->se_group != NULL
156 && STRNCMP("PopUp", sign->se_group->sg_name, 5) == 0;
157
158 return WIN_IS_POPUP(wp) ? for_popup : !for_popup;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100159}
160
161/*
162 * Get the next free sign identifier in the specified group
163 */
164 static int
165sign_group_get_next_signid(buf_T *buf, char_u *groupname)
166{
167 int id = 1;
168 signgroup_T *group = NULL;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200169 sign_entry_T *sign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100170 hashitem_T *hi;
171 int found = FALSE;
172
173 if (groupname != NULL)
174 {
175 hi = hash_find(&sg_table, groupname);
176 if (HASHITEM_EMPTY(hi))
177 return id;
178 group = HI2SG(hi);
179 }
180
Bram Moolenaarb5443cc2019-01-15 20:19:40 +0100181 // Search for the next usable sign identifier
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100182 while (!found)
183 {
184 if (group == NULL)
185 id = next_sign_id++; // global group
186 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200187 id = group->sg_next_sign_id++;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100188
189 // Check whether this sign is already placed in the buffer
190 found = TRUE;
191 FOR_ALL_SIGNS_IN_BUF(buf, sign)
192 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200193 if (id == sign->se_id && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100194 {
195 found = FALSE; // sign identifier is in use
196 break;
197 }
198 }
199 }
200
201 return id;
202}
203
204/*
205 * Insert a new sign into the signlist for buffer 'buf' between the 'prev' and
206 * 'next' signs.
207 */
208 static void
209insert_sign(
210 buf_T *buf, // buffer to store sign in
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200211 sign_entry_T *prev, // previous sign entry
212 sign_entry_T *next, // next sign entry
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100213 int id, // sign ID
214 char_u *group, // sign group; NULL for global group
215 int prio, // sign priority
216 linenr_T lnum, // line number which gets the mark
217 int typenr) // typenr of sign we are adding
218{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200219 sign_entry_T *newsign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100220
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200221 newsign = lalloc_id(sizeof(sign_entry_T), FALSE, aid_insert_sign);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100222 if (newsign != NULL)
223 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200224 newsign->se_id = id;
225 newsign->se_lnum = lnum;
226 newsign->se_typenr = typenr;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100227 if (group != NULL)
228 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200229 newsign->se_group = sign_group_ref(group);
230 if (newsign->se_group == NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100231 {
232 vim_free(newsign);
233 return;
234 }
235 }
236 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200237 newsign->se_group = NULL;
238 newsign->se_priority = prio;
239 newsign->se_next = next;
240 newsign->se_prev = prev;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100241 if (next != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200242 next->se_prev = newsign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100243
244 if (prev == NULL)
245 {
246 // When adding first sign need to redraw the windows to create the
247 // column for signs.
248 if (buf->b_signlist == NULL)
249 {
250 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200251 changed_line_abv_curs();
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100252 }
253
254 // first sign in signlist
255 buf->b_signlist = newsign;
256#ifdef FEAT_NETBEANS_INTG
257 if (netbeans_active())
258 buf->b_has_sign_column = TRUE;
259#endif
260 }
261 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200262 prev->se_next = newsign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100263 }
264}
265
266/*
267 * Insert a new sign sorted by line number and sign priority.
268 */
269 static void
270insert_sign_by_lnum_prio(
271 buf_T *buf, // buffer to store sign in
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200272 sign_entry_T *prev, // previous sign entry
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100273 int id, // sign ID
274 char_u *group, // sign group; NULL for global group
275 int prio, // sign priority
276 linenr_T lnum, // line number which gets the mark
277 int typenr) // typenr of sign we are adding
278{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200279 sign_entry_T *sign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100280
281 // keep signs sorted by lnum and by priority: insert new sign at
282 // the proper position in the list for this lnum.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200283 while (prev != NULL && prev->se_lnum == lnum && prev->se_priority <= prio)
284 prev = prev->se_prev;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100285 if (prev == NULL)
286 sign = buf->b_signlist;
287 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200288 sign = prev->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100289
290 insert_sign(buf, prev, sign, id, group, prio, lnum, typenr);
291}
292
293/*
Bram Moolenaar4e038572019-07-04 18:28:35 +0200294 * Lookup a sign by typenr. Returns NULL if sign is not found.
295 */
296 static sign_T *
297find_sign_by_typenr(int typenr)
298{
299 sign_T *sp;
300
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200301 FOR_ALL_SIGNS(sp)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200302 if (sp->sn_typenr == typenr)
303 return sp;
304 return NULL;
305}
306
307/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100308 * Get the name of a sign by its typenr.
309 */
310 static char_u *
311sign_typenr2name(int typenr)
312{
313 sign_T *sp;
314
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200315 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100316 if (sp->sn_typenr == typenr)
317 return sp->sn_name;
318 return (char_u *)_("[Deleted]");
319}
320
321/*
322 * Return information about a sign in a Dict
323 */
324 static dict_T *
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200325sign_get_info(sign_entry_T *sign)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100326{
327 dict_T *d;
328
329 if ((d = dict_alloc_id(aid_sign_getinfo)) == NULL)
330 return NULL;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200331 dict_add_number(d, "id", sign->se_id);
332 dict_add_string(d, "group", (sign->se_group == NULL) ?
333 (char_u *)"" : sign->se_group->sg_name);
334 dict_add_number(d, "lnum", sign->se_lnum);
335 dict_add_string(d, "name", sign_typenr2name(sign->se_typenr));
336 dict_add_number(d, "priority", sign->se_priority);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100337
338 return d;
339}
340
341/*
Bram Moolenaar64416122019-06-07 21:37:13 +0200342 * Sort the signs placed on the same line as "sign" by priority. Invoked after
343 * changing the priority of an already placed sign. Assumes the signs in the
344 * buffer are sorted by line number and priority.
345 */
346 static void
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200347sign_sort_by_prio_on_line(buf_T *buf, sign_entry_T *sign)
Bram Moolenaar64416122019-06-07 21:37:13 +0200348{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200349 sign_entry_T *p = NULL;
Bram Moolenaar64416122019-06-07 21:37:13 +0200350
351 // If there is only one sign in the buffer or only one sign on the line or
352 // the sign is already sorted by priority, then return.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200353 if ((sign->se_prev == NULL
354 || sign->se_prev->se_lnum != sign->se_lnum
355 || sign->se_prev->se_priority > sign->se_priority)
356 && (sign->se_next == NULL
357 || sign->se_next->se_lnum != sign->se_lnum
358 || sign->se_next->se_priority < sign->se_priority))
Bram Moolenaar64416122019-06-07 21:37:13 +0200359 return;
360
361 // One or more signs on the same line as 'sign'
362 // Find a sign after which 'sign' should be inserted
363
364 // First search backward for a sign with higher priority on the same line
365 p = sign;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200366 while (p->se_prev != NULL && p->se_prev->se_lnum == sign->se_lnum
367 && p->se_prev->se_priority <= sign->se_priority)
368 p = p->se_prev;
Bram Moolenaar64416122019-06-07 21:37:13 +0200369
370 if (p == sign)
371 {
372 // Sign not found. Search forward for a sign with priority just before
373 // 'sign'.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200374 p = sign->se_next;
375 while (p->se_next != NULL && p->se_next->se_lnum == sign->se_lnum
376 && p->se_next->se_priority > sign->se_priority)
377 p = p->se_next;
Bram Moolenaar64416122019-06-07 21:37:13 +0200378 }
379
380 // Remove 'sign' from the list
381 if (buf->b_signlist == sign)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200382 buf->b_signlist = sign->se_next;
383 if (sign->se_prev != NULL)
384 sign->se_prev->se_next = sign->se_next;
385 if (sign->se_next != NULL)
386 sign->se_next->se_prev = sign->se_prev;
387 sign->se_prev = NULL;
388 sign->se_next = NULL;
Bram Moolenaar64416122019-06-07 21:37:13 +0200389
390 // Re-insert 'sign' at the right place
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200391 if (p->se_priority <= sign->se_priority)
Bram Moolenaar64416122019-06-07 21:37:13 +0200392 {
393 // 'sign' has a higher priority and should be inserted before 'p'
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200394 sign->se_prev = p->se_prev;
395 sign->se_next = p;
396 p->se_prev = sign;
397 if (sign->se_prev != NULL)
398 sign->se_prev->se_next = sign;
Bram Moolenaar64416122019-06-07 21:37:13 +0200399 if (buf->b_signlist == p)
400 buf->b_signlist = sign;
401 }
402 else
403 {
404 // 'sign' has a lower priority and should be inserted after 'p'
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200405 sign->se_prev = p;
406 sign->se_next = p->se_next;
407 p->se_next = sign;
408 if (sign->se_next != NULL)
409 sign->se_next->se_prev = sign;
Bram Moolenaar64416122019-06-07 21:37:13 +0200410 }
411}
412
413/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100414 * Add the sign into the signlist. Find the right spot to do it though.
415 */
416 static void
417buf_addsign(
418 buf_T *buf, // buffer to store sign in
419 int id, // sign ID
420 char_u *groupname, // sign group
421 int prio, // sign priority
422 linenr_T lnum, // line number which gets the mark
423 int typenr) // typenr of sign we are adding
424{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200425 sign_entry_T *sign; // a sign in the signlist
426 sign_entry_T *prev; // the previous sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100427
428 prev = NULL;
429 FOR_ALL_SIGNS_IN_BUF(buf, sign)
430 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200431 if (lnum == sign->se_lnum && id == sign->se_id
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100432 && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100433 {
434 // Update an existing sign
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200435 sign->se_typenr = typenr;
436 sign->se_priority = prio;
Bram Moolenaar64416122019-06-07 21:37:13 +0200437 sign_sort_by_prio_on_line(buf, sign);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100438 return;
439 }
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200440 else if (lnum < sign->se_lnum)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100441 {
442 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio,
443 lnum, typenr);
444 return;
445 }
446 prev = sign;
447 }
448
449 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr);
450 return;
451}
452
453/*
454 * For an existing, placed sign "markId" change the type to "typenr".
455 * Returns the line number of the sign, or zero if the sign is not found.
456 */
457 static linenr_T
458buf_change_sign_type(
459 buf_T *buf, // buffer to store sign in
460 int markId, // sign ID
461 char_u *group, // sign group
Bram Moolenaar809ce4d2019-07-13 21:21:40 +0200462 int typenr, // typenr of sign we are adding
463 int prio) // sign priority
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100464{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200465 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100466
467 FOR_ALL_SIGNS_IN_BUF(buf, sign)
468 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200469 if (sign->se_id == markId && sign_in_group(sign, group))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100470 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200471 sign->se_typenr = typenr;
472 sign->se_priority = prio;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +0200473 sign_sort_by_prio_on_line(buf, sign);
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200474 return sign->se_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100475 }
476 }
477
478 return (linenr_T)0;
479}
480
481/*
Bram Moolenaar4e038572019-07-04 18:28:35 +0200482 * Return the attributes of the first sign placed on line 'lnum' in buffer
483 * 'buf'. Used when refreshing the screen. Returns TRUE if a sign is found on
484 * 'lnum', FALSE otherwise.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100485 */
486 int
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100487buf_get_signattrs(win_T *wp, linenr_T lnum, sign_attrs_T *sattr)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100488{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200489 sign_entry_T *sign;
490 sign_T *sp;
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100491 buf_T *buf = wp->w_buffer;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200492
Bram Moolenaara80faa82020-04-12 19:37:17 +0200493 CLEAR_POINTER(sattr);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100494
495 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200496 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200497 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200498 // Signs are sorted by line number in the buffer. No need to check
499 // for signs after the specified line number 'lnum'.
500 break;
501
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100502 if (sign->se_lnum == lnum
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100503# ifdef FEAT_PROP_POPUP
Bram Moolenaar72570732019-11-30 14:21:53 +0100504 && sign_group_for_window(sign, wp)
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100505# endif
506 )
Bram Moolenaar4e038572019-07-04 18:28:35 +0200507 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200508 sattr->sat_typenr = sign->se_typenr;
509 sp = find_sign_by_typenr(sign->se_typenr);
Bram Moolenaar4e038572019-07-04 18:28:35 +0200510 if (sp == NULL)
511 return FALSE;
512
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100513# ifdef FEAT_SIGN_ICONS
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200514 sattr->sat_icon = sp->sn_image;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100515# endif
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200516 sattr->sat_text = sp->sn_text;
517 if (sattr->sat_text != NULL && sp->sn_text_hl > 0)
518 sattr->sat_texthl = syn_id2attr(sp->sn_text_hl);
Bram Moolenaar4e038572019-07-04 18:28:35 +0200519 if (sp->sn_line_hl > 0)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200520 sattr->sat_linehl = syn_id2attr(sp->sn_line_hl);
Bram Moolenaare413ea02021-11-24 16:20:13 +0000521 if (sp->sn_cul_hl > 0)
522 sattr->sat_culhl = syn_id2attr(sp->sn_cul_hl);
Bram Moolenaar2f122842020-08-31 23:18:00 +0200523 sattr->sat_priority = sign->se_priority;
Bram Moolenaara2f6e422020-02-19 17:13:04 +0100524
525 // If there is another sign next with the same priority, may
526 // combine the text and the line highlighting.
527 if (sign->se_next != NULL
528 && sign->se_next->se_priority == sign->se_priority
529 && sign->se_next->se_lnum == sign->se_lnum)
530 {
531 sign_T *next_sp = find_sign_by_typenr(sign->se_next->se_typenr);
532
533 if (next_sp != NULL)
534 {
535 if (sattr->sat_icon == NULL && sattr->sat_text == NULL)
536 {
537# ifdef FEAT_SIGN_ICONS
538 sattr->sat_icon = next_sp->sn_image;
539# endif
540 sattr->sat_text = next_sp->sn_text;
541 }
542 if (sp->sn_text_hl <= 0 && next_sp->sn_text_hl > 0)
543 sattr->sat_texthl = syn_id2attr(next_sp->sn_text_hl);
544 if (sp->sn_line_hl <= 0 && next_sp->sn_line_hl > 0)
545 sattr->sat_linehl = syn_id2attr(next_sp->sn_line_hl);
Bram Moolenaare413ea02021-11-24 16:20:13 +0000546 if (sp->sn_cul_hl <= 0 && next_sp->sn_cul_hl > 0)
547 sattr->sat_culhl = syn_id2attr(next_sp->sn_cul_hl);
Bram Moolenaara2f6e422020-02-19 17:13:04 +0100548 }
549 }
Bram Moolenaar4e038572019-07-04 18:28:35 +0200550 return TRUE;
551 }
552 }
553 return FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100554}
555
556/*
557 * Delete sign 'id' in group 'group' from buffer 'buf'.
558 * If 'id' is zero, then delete all the signs in group 'group'. Otherwise
559 * delete only the specified sign.
560 * If 'group' is '*', then delete the sign in all the groups. If 'group' is
561 * NULL, then delete the sign in the global group. Otherwise delete the sign in
562 * the specified group.
563 * Returns the line number of the deleted sign. If multiple signs are deleted,
564 * then returns the line number of the last sign deleted.
565 */
566 linenr_T
567buf_delsign(
568 buf_T *buf, // buffer sign is stored in
569 linenr_T atlnum, // sign at this line, 0 - at any line
570 int id, // sign id
571 char_u *group) // sign group
572{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200573 sign_entry_T **lastp; // pointer to pointer to current sign
574 sign_entry_T *sign; // a sign in a b_signlist
575 sign_entry_T *next; // the next sign in a b_signlist
576 linenr_T lnum; // line number whose sign was deleted
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100577
578 lastp = &buf->b_signlist;
579 lnum = 0;
580 for (sign = buf->b_signlist; sign != NULL; sign = next)
581 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200582 next = sign->se_next;
583 if ((id == 0 || sign->se_id == id)
584 && (atlnum == 0 || sign->se_lnum == atlnum)
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100585 && sign_in_group(sign, group))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100586
587 {
588 *lastp = next;
589 if (next != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200590 next->se_prev = sign->se_prev;
591 lnum = sign->se_lnum;
592 if (sign->se_group != NULL)
593 sign_group_unref(sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100594 vim_free(sign);
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100595 redraw_buf_line_later(buf, lnum);
596
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100597 // Check whether only one sign needs to be deleted
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100598 // If deleting a sign with a specific identifier in a particular
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100599 // group or deleting any sign at a particular line number, delete
600 // only one sign.
601 if (group == NULL
602 || (*group != '*' && id != 0)
603 || (*group == '*' && atlnum != 0))
604 break;
605 }
606 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200607 lastp = &sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100608 }
609
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100610 // When deleting the last sign the cursor position may change, because the
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100611 // sign columns no longer shows. And the 'signcolumn' may be hidden.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100612 if (buf->b_signlist == NULL)
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100613 {
614 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200615 changed_line_abv_curs();
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100616 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100617
618 return lnum;
619}
620
621
622/*
623 * Find the line number of the sign with the requested id in group 'group'. If
624 * the sign does not exist, return 0 as the line number. This will still let
625 * the correct file get loaded.
626 */
627 int
628buf_findsign(
629 buf_T *buf, // buffer to store sign in
630 int id, // sign ID
631 char_u *group) // sign group
632{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200633 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100634
635 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200636 if (sign->se_id == id && sign_in_group(sign, group))
637 return sign->se_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100638
639 return 0;
640}
641
642/*
643 * Return the sign at line 'lnum' in buffer 'buf'. Returns NULL if a sign is
644 * not found at the line. If 'groupname' is NULL, searches in the global group.
645 */
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200646 static sign_entry_T *
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100647buf_getsign_at_line(
648 buf_T *buf, // buffer whose sign we are searching for
649 linenr_T lnum, // line number of sign
650 char_u *groupname) // sign group name
651{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200652 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100653
654 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200655 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200656 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200657 // Signs are sorted by line number in the buffer. No need to check
658 // for signs after the specified line number 'lnum'.
659 break;
660
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200661 if (sign->se_lnum == lnum && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100662 return sign;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200663 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100664
665 return NULL;
666}
667
668/*
669 * Return the identifier of the sign at line number 'lnum' in buffer 'buf'.
670 */
671 int
672buf_findsign_id(
673 buf_T *buf, // buffer whose sign we are searching for
674 linenr_T lnum, // line number of sign
675 char_u *groupname) // sign group name
676{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200677 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100678
679 sign = buf_getsign_at_line(buf, lnum, groupname);
680 if (sign != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200681 return sign->se_id;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100682
683 return 0;
684}
685
686# if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
687/*
688 * See if a given type of sign exists on a specific line.
689 */
690 int
691buf_findsigntype_id(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100692 buf_T *buf, // buffer whose sign we are searching for
693 linenr_T lnum, // line number of sign
694 int typenr) // sign type number
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100695{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200696 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100697
698 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200699 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200700 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200701 // Signs are sorted by line number in the buffer. No need to check
702 // for signs after the specified line number 'lnum'.
703 break;
704
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200705 if (sign->se_lnum == lnum && sign->se_typenr == typenr)
706 return sign->se_id;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200707 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100708
709 return 0;
710}
711
712
713# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
714/*
715 * Return the number of icons on the given line.
716 */
717 int
718buf_signcount(buf_T *buf, linenr_T lnum)
719{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200720 sign_entry_T *sign; // a sign in the signlist
721 int count = 0;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100722
723 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200724 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200725 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200726 // Signs are sorted by line number in the buffer. No need to check
727 // for signs after the specified line number 'lnum'.
728 break;
729
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200730 if (sign->se_lnum == lnum)
731 if (sign_get_image(sign->se_typenr) != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100732 count++;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200733 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100734
735 return count;
736}
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100737# endif // FEAT_SIGN_ICONS
738# endif // FEAT_NETBEANS_INTG
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100739
740/*
741 * Delete signs in group 'group' in buffer "buf". If 'group' is '*', then
742 * delete all the signs.
743 */
744 void
745buf_delete_signs(buf_T *buf, char_u *group)
746{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200747 sign_entry_T *sign;
748 sign_entry_T **lastp; // pointer to pointer to current sign
749 sign_entry_T *next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100750
751 // When deleting the last sign need to redraw the windows to remove the
752 // sign column. Not when curwin is NULL (this means we're exiting).
753 if (buf->b_signlist != NULL && curwin != NULL)
754 {
755 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200756 changed_line_abv_curs();
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100757 }
758
759 lastp = &buf->b_signlist;
760 for (sign = buf->b_signlist; sign != NULL; sign = next)
761 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200762 next = sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100763 if (sign_in_group(sign, group))
764 {
765 *lastp = next;
766 if (next != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200767 next->se_prev = sign->se_prev;
768 if (sign->se_group != NULL)
769 sign_group_unref(sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100770 vim_free(sign);
771 }
772 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200773 lastp = &sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100774 }
775}
776
777/*
778 * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers.
779 */
780 static void
781sign_list_placed(buf_T *rbuf, char_u *sign_group)
782{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200783 buf_T *buf;
784 sign_entry_T *sign;
785 char lbuf[MSG_BUF_LEN];
786 char group[MSG_BUF_LEN];
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100787
Bram Moolenaar32526b32019-01-19 17:43:09 +0100788 msg_puts_title(_("\n--- Signs ---"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100789 msg_putchar('\n');
790 if (rbuf == NULL)
791 buf = firstbuf;
792 else
793 buf = rbuf;
794 while (buf != NULL && !got_int)
795 {
796 if (buf->b_signlist != NULL)
797 {
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100798 vim_snprintf(lbuf, MSG_BUF_LEN, _("Signs for %s:"), buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100799 msg_puts_attr(lbuf, HL_ATTR(HLF_D));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100800 msg_putchar('\n');
801 }
802 FOR_ALL_SIGNS_IN_BUF(buf, sign)
803 {
804 if (got_int)
805 break;
806 if (!sign_in_group(sign, sign_group))
807 continue;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200808 if (sign->se_group != NULL)
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100809 vim_snprintf(group, MSG_BUF_LEN, _(" group=%s"),
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200810 sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100811 else
812 group[0] = '\0';
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100813 vim_snprintf(lbuf, MSG_BUF_LEN,
814 _(" line=%ld id=%d%s name=%s priority=%d"),
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200815 (long)sign->se_lnum, sign->se_id, group,
816 sign_typenr2name(sign->se_typenr), sign->se_priority);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100817 msg_puts(lbuf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100818 msg_putchar('\n');
819 }
820 if (rbuf != NULL)
821 break;
822 buf = buf->b_next;
823 }
824}
825
826/*
827 * Adjust a placed sign for inserted/deleted lines.
828 */
829 void
830sign_mark_adjust(
831 linenr_T line1,
832 linenr_T line2,
833 long amount,
834 long amount_after)
835{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200836 sign_entry_T *sign; // a sign in a b_signlist
837 linenr_T new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100838
839 FOR_ALL_SIGNS_IN_BUF(curbuf, sign)
840 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100841 // Ignore changes to lines after the sign
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200842 if (sign->se_lnum < line1)
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100843 continue;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200844 new_lnum = sign->se_lnum;
845 if (sign->se_lnum >= line1 && sign->se_lnum <= line2)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100846 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100847 if (amount != MAXLNUM)
848 new_lnum += amount;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100849 }
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200850 else if (sign->se_lnum > line2)
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100851 // Lines inserted or deleted before the sign
852 new_lnum += amount_after;
853
854 // If the new sign line number is past the last line in the buffer,
855 // then don't adjust the line number. Otherwise, it will always be past
856 // the last line and will not be visible.
857 if (new_lnum <= curbuf->b_ml.ml_line_count)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200858 sign->se_lnum = new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100859 }
860}
861
862/*
863 * Find index of a ":sign" subcmd from its name.
864 * "*end_cmd" must be writable.
865 */
866 static int
867sign_cmd_idx(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100868 char_u *begin_cmd, // begin of sign subcmd
869 char_u *end_cmd) // just after sign subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100870{
871 int idx;
872 char save = *end_cmd;
873
874 *end_cmd = NUL;
875 for (idx = 0; ; ++idx)
876 if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0)
877 break;
878 *end_cmd = save;
879 return idx;
880}
881
882/*
883 * Find a sign by name. Also returns pointer to the previous sign.
884 */
885 static sign_T *
886sign_find(char_u *name, sign_T **sp_prev)
887{
888 sign_T *sp;
889
890 if (sp_prev != NULL)
891 *sp_prev = NULL;
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200892 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100893 {
894 if (STRCMP(sp->sn_name, name) == 0)
895 break;
896 if (sp_prev != NULL)
897 *sp_prev = sp;
898 }
899
900 return sp;
901}
902
903/*
Bram Moolenaar03142362019-01-18 22:01:42 +0100904 * Allocate a new sign
905 */
906 static sign_T *
907alloc_new_sign(char_u *name)
908{
909 sign_T *sp;
910 sign_T *lp;
911 int start = next_sign_typenr;
912
913 // Allocate a new sign.
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200914 sp = alloc_clear_id(sizeof(sign_T), aid_sign_define_by_name);
Bram Moolenaar03142362019-01-18 22:01:42 +0100915 if (sp == NULL)
916 return NULL;
917
918 // Check that next_sign_typenr is not already being used.
919 // This only happens after wrapping around. Hopefully
920 // another one got deleted and we can use its number.
921 for (lp = first_sign; lp != NULL; )
922 {
923 if (lp->sn_typenr == next_sign_typenr)
924 {
925 ++next_sign_typenr;
926 if (next_sign_typenr == MAX_TYPENR)
927 next_sign_typenr = 1;
928 if (next_sign_typenr == start)
929 {
930 vim_free(sp);
931 emsg(_("E612: Too many signs defined"));
932 return NULL;
933 }
934 lp = first_sign; // start all over
935 continue;
936 }
937 lp = lp->sn_next;
938 }
939
940 sp->sn_typenr = next_sign_typenr;
941 if (++next_sign_typenr == MAX_TYPENR)
942 next_sign_typenr = 1; // wrap around
943
944 sp->sn_name = vim_strsave(name);
945 if (sp->sn_name == NULL) // out of memory
946 {
947 vim_free(sp);
948 return NULL;
949 }
950
951 return sp;
952}
953
954/*
955 * Initialize the icon information for a new sign
956 */
957 static void
958sign_define_init_icon(sign_T *sp, char_u *icon)
959{
960 vim_free(sp->sn_icon);
961 sp->sn_icon = vim_strsave(icon);
962 backslash_halve(sp->sn_icon);
963# ifdef FEAT_SIGN_ICONS
964 if (gui.in_use)
965 {
966 out_flush();
967 if (sp->sn_image != NULL)
968 gui_mch_destroy_sign(sp->sn_image);
969 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
970 }
971# endif
972}
973
974/*
975 * Initialize the text for a new sign
976 */
977 static int
978sign_define_init_text(sign_T *sp, char_u *text)
979{
980 char_u *s;
981 char_u *endp;
982 int cells;
983 int len;
984
985 endp = text + (int)STRLEN(text);
986
987 // Remove backslashes so that it is possible to use a space.
988 for (s = text; s + 1 < endp; ++s)
989 if (*s == '\\')
990 {
991 STRMOVE(s, s + 1);
992 --endp;
993 }
994
995 // Count cells and check for non-printable chars
Bram Moolenaar03142362019-01-18 22:01:42 +0100996 if (has_mbyte)
997 {
998 cells = 0;
999 for (s = text; s < endp; s += (*mb_ptr2len)(s))
1000 {
1001 if (!vim_isprintc((*mb_ptr2char)(s)))
1002 break;
1003 cells += (*mb_ptr2cells)(s);
1004 }
1005 }
1006 else
Bram Moolenaar03142362019-01-18 22:01:42 +01001007 {
1008 for (s = text; s < endp; ++s)
1009 if (!vim_isprintc(*s))
1010 break;
1011 cells = (int)(s - text);
1012 }
1013
1014 // Currently sign text must be one or two display cells
1015 if (s != endp || cells < 1 || cells > 2)
1016 {
1017 semsg(_("E239: Invalid sign text: %s"), text);
1018 return FAIL;
1019 }
1020
1021 vim_free(sp->sn_text);
1022 // Allocate one byte more if we need to pad up
1023 // with a space.
1024 len = (int)(endp - text + ((cells == 1) ? 1 : 0));
1025 sp->sn_text = vim_strnsave(text, len);
1026
1027 // For single character sign text, pad with a space.
1028 if (sp->sn_text != NULL && cells == 1)
1029 STRCPY(sp->sn_text + len - 1, " ");
1030
1031 return OK;
1032}
1033
1034/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001035 * Define a new sign or update an existing sign
1036 */
1037 int
1038sign_define_by_name(
1039 char_u *name,
1040 char_u *icon,
1041 char_u *linehl,
1042 char_u *text,
Bram Moolenaare413ea02021-11-24 16:20:13 +00001043 char_u *texthl,
1044 char_u *culhl)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001045{
1046 sign_T *sp_prev;
1047 sign_T *sp;
1048
1049 sp = sign_find(name, &sp_prev);
1050 if (sp == NULL)
1051 {
Bram Moolenaar03142362019-01-18 22:01:42 +01001052 sp = alloc_new_sign(name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001053 if (sp == NULL)
1054 return FAIL;
1055
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001056 // add the new sign to the list of signs
1057 if (sp_prev == NULL)
1058 first_sign = sp;
1059 else
1060 sp_prev->sn_next = sp;
1061 }
Bram Moolenaarbf0acff2020-01-09 21:01:59 +01001062 else
1063 {
1064 win_T *wp;
1065
1066 // Signs may already exist, a redraw is needed in windows with a
1067 // non-empty sign list.
1068 FOR_ALL_WINDOWS(wp)
1069 if (wp->w_buffer->b_signlist != NULL)
1070 redraw_buf_later(wp->w_buffer, NOT_VALID);
1071 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001072
1073 // set values for a defined sign.
1074 if (icon != NULL)
Bram Moolenaar03142362019-01-18 22:01:42 +01001075 sign_define_init_icon(sp, icon);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001076
Bram Moolenaar03142362019-01-18 22:01:42 +01001077 if (text != NULL && (sign_define_init_text(sp, text) == FAIL))
1078 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001079
1080 if (linehl != NULL)
1081 sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl));
1082
1083 if (texthl != NULL)
1084 sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl));
1085
Bram Moolenaare413ea02021-11-24 16:20:13 +00001086 if (culhl != NULL)
1087 sp->sn_cul_hl = syn_check_group(culhl, (int)STRLEN(culhl));
1088
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001089 return OK;
1090}
1091
1092/*
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001093 * Return TRUE if sign "name" exists.
1094 */
1095 int
1096sign_exists_by_name(char_u *name)
1097{
1098 return sign_find(name, NULL) != NULL;
1099}
1100
1101/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001102 * Free the sign specified by 'name'.
1103 */
1104 int
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001105sign_undefine_by_name(char_u *name, int give_error)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001106{
1107 sign_T *sp_prev;
1108 sign_T *sp;
1109
1110 sp = sign_find(name, &sp_prev);
1111 if (sp == NULL)
1112 {
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001113 if (give_error)
1114 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001115 return FAIL;
1116 }
1117 sign_undefine(sp, sp_prev);
1118
1119 return OK;
1120}
1121
1122/*
1123 * List the signs matching 'name'
1124 */
1125 static void
1126sign_list_by_name(char_u *name)
1127{
1128 sign_T *sp;
1129
1130 sp = sign_find(name, NULL);
1131 if (sp != NULL)
1132 sign_list_defined(sp);
1133 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001134 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001135}
1136
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001137 static void
1138may_force_numberwidth_recompute(buf_T *buf, int unplace)
1139{
1140 tabpage_T *tp;
1141 win_T *wp;
1142
1143 FOR_ALL_TAB_WINDOWS(tp, wp)
1144 if (wp->w_buffer == buf
1145 && (wp->w_p_nu || wp->w_p_rnu)
1146 && (unplace || wp->w_nrwidth_width < 2)
1147 && (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u'))
1148 wp->w_nrwidth_line_count = 0;
1149}
1150
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001151/*
Bram Moolenaar8144acb2019-01-14 23:08:18 +01001152 * Place a sign at the specified file location or update a sign.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001153 */
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001154 int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001155sign_place(
1156 int *sign_id,
1157 char_u *sign_group,
1158 char_u *sign_name,
1159 buf_T *buf,
1160 linenr_T lnum,
1161 int prio)
1162{
1163 sign_T *sp;
1164
1165 // Check for reserved character '*' in group name
1166 if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0'))
1167 return FAIL;
1168
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001169 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001170 if (STRCMP(sp->sn_name, sign_name) == 0)
1171 break;
1172 if (sp == NULL)
1173 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001174 semsg(_("E155: Unknown sign: %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001175 return FAIL;
1176 }
1177 if (*sign_id == 0)
1178 *sign_id = sign_group_get_next_signid(buf, sign_group);
1179
1180 if (lnum > 0)
1181 // ":sign place {id} line={lnum} name={name} file={fname}":
1182 // place a sign
1183 buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr);
1184 else
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02001185 // ":sign place {id} file={fname}": change sign type and/or priority
1186 lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr,
1187 prio);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001188 if (lnum > 0)
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001189 {
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001190 redraw_buf_line_later(buf, lnum);
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001191
1192 // When displaying signs in the 'number' column, if the width of the
1193 // number column is less than 2, then force recomputing the width.
1194 may_force_numberwidth_recompute(buf, FALSE);
1195 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001196 else
1197 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001198 semsg(_("E885: Not possible to change sign %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001199 return FAIL;
1200 }
1201
1202 return OK;
1203}
1204
1205/*
1206 * Unplace the specified sign
1207 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001208 static int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001209sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum)
1210{
1211 if (buf->b_signlist == NULL) // No signs in the buffer
1212 return OK;
1213
1214 if (sign_id == 0)
1215 {
1216 // Delete all the signs in the specified buffer
1217 redraw_buf_later(buf, NOT_VALID);
1218 buf_delete_signs(buf, sign_group);
1219 }
1220 else
1221 {
1222 linenr_T lnum;
1223
1224 // Delete only the specified signs
1225 lnum = buf_delsign(buf, atlnum, sign_id, sign_group);
1226 if (lnum == 0)
1227 return FAIL;
1228 }
1229
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001230 // When all the signs in a buffer are removed, force recomputing the
1231 // number column width (if enabled) in all the windows displaying the
1232 // buffer if 'signcolumn' is set to 'number' in that window.
1233 if (buf->b_signlist == NULL)
1234 may_force_numberwidth_recompute(buf, TRUE);
1235
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001236 return OK;
1237}
1238
1239/*
1240 * Unplace the sign at the current cursor line.
1241 */
1242 static void
1243sign_unplace_at_cursor(char_u *groupname)
1244{
1245 int id = -1;
1246
1247 id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname);
1248 if (id > 0)
1249 sign_unplace(id, groupname, curwin->w_buffer, curwin->w_cursor.lnum);
1250 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001251 emsg(_("E159: Missing sign number"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001252}
1253
1254/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001255 * Jump to a sign.
1256 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001257 static linenr_T
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001258sign_jump(int sign_id, char_u *sign_group, buf_T *buf)
1259{
1260 linenr_T lnum;
1261
1262 if ((lnum = buf_findsign(buf, sign_id, sign_group)) <= 0)
1263 {
Bram Moolenaarb5443cc2019-01-15 20:19:40 +01001264 semsg(_("E157: Invalid sign ID: %d"), sign_id);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001265 return -1;
1266 }
1267
1268 // goto a sign ...
1269 if (buf_jump_open_win(buf) != NULL)
1270 { // ... in a current window
1271 curwin->w_cursor.lnum = lnum;
1272 check_cursor_lnum();
1273 beginline(BL_WHITE);
1274 }
1275 else
1276 { // ... not currently in a window
1277 char_u *cmd;
1278
1279 if (buf->b_fname == NULL)
1280 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001281 emsg(_("E934: Cannot jump to a buffer that does not have a name"));
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001282 return -1;
1283 }
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02001284 cmd = alloc(STRLEN(buf->b_fname) + 25);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001285 if (cmd == NULL)
1286 return -1;
1287 sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname);
1288 do_cmdline_cmd(cmd);
1289 vim_free(cmd);
1290 }
1291# ifdef FEAT_FOLDING
1292 foldOpenCursor();
1293# endif
1294
1295 return lnum;
1296}
1297
Bram Moolenaar5e18ccc2021-12-05 13:02:50 +00001298 static int
1299check_empty_group(size_t len, char *name)
1300{
1301 if (len == 0)
1302 {
1303 semsg(_(e_group_name_missing_for_str), name);
1304 return FAIL;
1305 }
1306 return OK;
1307}
1308
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001309/*
1310 * ":sign define {name} ..." command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001311 */
1312 static void
1313sign_define_cmd(char_u *sign_name, char_u *cmdline)
1314{
1315 char_u *arg;
1316 char_u *p = cmdline;
1317 char_u *icon = NULL;
1318 char_u *text = NULL;
1319 char_u *linehl = NULL;
1320 char_u *texthl = NULL;
Bram Moolenaare413ea02021-11-24 16:20:13 +00001321 char_u *culhl = NULL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001322 int failed = FALSE;
1323
1324 // set values for a defined sign.
1325 for (;;)
1326 {
1327 arg = skipwhite(p);
1328 if (*arg == NUL)
1329 break;
1330 p = skiptowhite_esc(arg);
1331 if (STRNCMP(arg, "icon=", 5) == 0)
1332 {
1333 arg += 5;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001334 icon = vim_strnsave(arg, p - arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001335 }
1336 else if (STRNCMP(arg, "text=", 5) == 0)
1337 {
1338 arg += 5;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001339 text = vim_strnsave(arg, p - arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001340 }
1341 else if (STRNCMP(arg, "linehl=", 7) == 0)
1342 {
1343 arg += 7;
Bram Moolenaar5e18ccc2021-12-05 13:02:50 +00001344 if (check_empty_group(p - arg, "linehl") == FAIL)
1345 {
1346 failed = TRUE;
1347 break;
1348 }
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001349 linehl = vim_strnsave(arg, p - arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001350 }
1351 else if (STRNCMP(arg, "texthl=", 7) == 0)
1352 {
1353 arg += 7;
Bram Moolenaar5e18ccc2021-12-05 13:02:50 +00001354 if (check_empty_group(p - arg, "texthl") == FAIL)
1355 {
1356 failed = TRUE;
1357 break;
1358 }
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001359 texthl = vim_strnsave(arg, p - arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001360 }
Bram Moolenaare413ea02021-11-24 16:20:13 +00001361 else if (STRNCMP(arg, "culhl=", 6) == 0)
1362 {
1363 arg += 6;
Bram Moolenaar5e18ccc2021-12-05 13:02:50 +00001364 if (check_empty_group(p - arg, "culhl") == FAIL)
1365 {
1366 failed = TRUE;
1367 break;
1368 }
Bram Moolenaare413ea02021-11-24 16:20:13 +00001369 culhl = vim_strnsave(arg, p - arg);
1370 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001371 else
1372 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001373 semsg(_(e_invarg2), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001374 failed = TRUE;
1375 break;
1376 }
1377 }
1378
1379 if (!failed)
Bram Moolenaare413ea02021-11-24 16:20:13 +00001380 sign_define_by_name(sign_name, icon, linehl, text, texthl, culhl);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001381
1382 vim_free(icon);
1383 vim_free(text);
1384 vim_free(linehl);
1385 vim_free(texthl);
Bram Moolenaare413ea02021-11-24 16:20:13 +00001386 vim_free(culhl);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001387}
1388
1389/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001390 * ":sign place" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001391 */
1392 static void
1393sign_place_cmd(
1394 buf_T *buf,
1395 linenr_T lnum,
1396 char_u *sign_name,
1397 int id,
1398 char_u *group,
1399 int prio)
1400{
1401 if (id <= 0)
1402 {
1403 // List signs placed in a file/buffer
1404 // :sign place file={fname}
1405 // :sign place group={group} file={fname}
1406 // :sign place group=* file={fname}
1407 // :sign place buffer={nr}
1408 // :sign place group={group} buffer={nr}
1409 // :sign place group=* buffer={nr}
1410 // :sign place
1411 // :sign place group={group}
1412 // :sign place group=*
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001413 if (lnum >= 0 || sign_name != NULL
1414 || (group != NULL && *group == '\0'))
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001415 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001416 else
1417 sign_list_placed(buf, group);
1418 }
1419 else
1420 {
1421 // Place a new sign
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001422 if (sign_name == NULL || buf == NULL
1423 || (group != NULL && *group == '\0'))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001424 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001425 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001426 return;
1427 }
1428
1429 sign_place(&id, group, sign_name, buf, lnum, prio);
1430 }
1431}
1432
1433/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001434 * ":sign unplace" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001435 */
1436 static void
1437sign_unplace_cmd(
1438 buf_T *buf,
1439 linenr_T lnum,
1440 char_u *sign_name,
1441 int id,
1442 char_u *group)
1443{
1444 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0'))
1445 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001446 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001447 return;
1448 }
1449
1450 if (id == -2)
1451 {
1452 if (buf != NULL)
1453 // :sign unplace * file={fname}
1454 // :sign unplace * group={group} file={fname}
1455 // :sign unplace * group=* file={fname}
1456 // :sign unplace * buffer={nr}
1457 // :sign unplace * group={group} buffer={nr}
1458 // :sign unplace * group=* buffer={nr}
1459 sign_unplace(0, group, buf, 0);
1460 else
1461 // :sign unplace *
1462 // :sign unplace * group={group}
1463 // :sign unplace * group=*
1464 FOR_ALL_BUFFERS(buf)
1465 if (buf->b_signlist != NULL)
1466 buf_delete_signs(buf, group);
1467 }
1468 else
1469 {
1470 if (buf != NULL)
1471 // :sign unplace {id} file={fname}
1472 // :sign unplace {id} group={group} file={fname}
1473 // :sign unplace {id} group=* file={fname}
1474 // :sign unplace {id} buffer={nr}
1475 // :sign unplace {id} group={group} buffer={nr}
1476 // :sign unplace {id} group=* buffer={nr}
1477 sign_unplace(id, group, buf, 0);
1478 else
1479 {
1480 if (id == -1)
1481 {
1482 // :sign unplace group={group}
1483 // :sign unplace group=*
1484 sign_unplace_at_cursor(group);
1485 }
1486 else
1487 {
1488 // :sign unplace {id}
1489 // :sign unplace {id} group={group}
1490 // :sign unplace {id} group=*
1491 FOR_ALL_BUFFERS(buf)
1492 sign_unplace(id, group, buf, 0);
1493 }
1494 }
1495 }
1496}
1497
1498/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001499 * Jump to a placed sign commands:
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001500 * :sign jump {id} file={fname}
1501 * :sign jump {id} buffer={nr}
1502 * :sign jump {id} group={group} file={fname}
1503 * :sign jump {id} group={group} buffer={nr}
1504 */
1505 static void
1506sign_jump_cmd(
1507 buf_T *buf,
1508 linenr_T lnum,
1509 char_u *sign_name,
1510 int id,
1511 char_u *group)
1512{
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001513 if (sign_name == NULL && group == NULL && id == -1)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001514 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001515 emsg(_(e_argreq));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001516 return;
1517 }
1518
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001519 if (buf == NULL || (group != NULL && *group == '\0')
1520 || lnum >= 0 || sign_name != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001521 {
1522 // File or buffer is not specified or an empty group is used
1523 // or a line number or a sign name is specified.
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001524 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001525 return;
1526 }
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001527 (void)sign_jump(id, group, buf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001528}
1529
1530/*
1531 * Parse the command line arguments for the ":sign place", ":sign unplace" and
1532 * ":sign jump" commands.
1533 * The supported arguments are: line={lnum} name={name} group={group}
1534 * priority={prio} and file={fname} or buffer={nr}.
1535 */
1536 static int
1537parse_sign_cmd_args(
1538 int cmd,
1539 char_u *arg,
1540 char_u **sign_name,
1541 int *signid,
1542 char_u **group,
1543 int *prio,
1544 buf_T **buf,
1545 linenr_T *lnum)
1546{
1547 char_u *arg1;
1548 char_u *name;
1549 char_u *filename = NULL;
Bram Moolenaarb589f952019-01-07 22:10:00 +01001550 int lnum_arg = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001551
1552 // first arg could be placed sign id
1553 arg1 = arg;
1554 if (VIM_ISDIGIT(*arg))
1555 {
1556 *signid = getdigits(&arg);
1557 if (!VIM_ISWHITE(*arg) && *arg != NUL)
1558 {
1559 *signid = -1;
1560 arg = arg1;
1561 }
1562 else
1563 arg = skipwhite(arg);
1564 }
1565
1566 while (*arg != NUL)
1567 {
1568 if (STRNCMP(arg, "line=", 5) == 0)
1569 {
1570 arg += 5;
1571 *lnum = atoi((char *)arg);
1572 arg = skiptowhite(arg);
Bram Moolenaarb589f952019-01-07 22:10:00 +01001573 lnum_arg = TRUE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001574 }
1575 else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE)
1576 {
1577 if (*signid != -1)
1578 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001579 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001580 return FAIL;
1581 }
1582 *signid = -2;
1583 arg = skiptowhite(arg + 1);
1584 }
1585 else if (STRNCMP(arg, "name=", 5) == 0)
1586 {
1587 arg += 5;
1588 name = arg;
1589 arg = skiptowhite(arg);
1590 if (*arg != NUL)
1591 *arg++ = NUL;
1592 while (name[0] == '0' && name[1] != NUL)
1593 ++name;
1594 *sign_name = name;
1595 }
1596 else if (STRNCMP(arg, "group=", 6) == 0)
1597 {
1598 arg += 6;
1599 *group = arg;
1600 arg = skiptowhite(arg);
1601 if (*arg != NUL)
1602 *arg++ = NUL;
1603 }
1604 else if (STRNCMP(arg, "priority=", 9) == 0)
1605 {
1606 arg += 9;
1607 *prio = atoi((char *)arg);
1608 arg = skiptowhite(arg);
1609 }
1610 else if (STRNCMP(arg, "file=", 5) == 0)
1611 {
1612 arg += 5;
1613 filename = arg;
1614 *buf = buflist_findname_exp(arg);
1615 break;
1616 }
1617 else if (STRNCMP(arg, "buffer=", 7) == 0)
1618 {
1619 arg += 7;
1620 filename = arg;
1621 *buf = buflist_findnr((int)getdigits(&arg));
1622 if (*skipwhite(arg) != NUL)
Bram Moolenaar2d06bfd2020-07-23 17:16:18 +02001623 semsg(_(e_trailing_arg), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001624 break;
1625 }
1626 else
1627 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001628 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001629 return FAIL;
1630 }
1631 arg = skipwhite(arg);
1632 }
1633
1634 if (filename != NULL && *buf == NULL)
1635 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001636 semsg(_("E158: Invalid buffer name: %s"), filename);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001637 return FAIL;
1638 }
1639
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001640 // If the filename is not supplied for the sign place or the sign jump
1641 // command, then use the current buffer.
Bram Moolenaarb589f952019-01-07 22:10:00 +01001642 if (filename == NULL && ((cmd == SIGNCMD_PLACE && lnum_arg)
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001643 || cmd == SIGNCMD_JUMP))
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001644 *buf = curwin->w_buffer;
1645
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001646 return OK;
1647}
1648
1649/*
1650 * ":sign" command
1651 */
1652 void
1653ex_sign(exarg_T *eap)
1654{
1655 char_u *arg = eap->arg;
1656 char_u *p;
1657 int idx;
1658 sign_T *sp;
1659 buf_T *buf = NULL;
1660
1661 // Parse the subcommand.
1662 p = skiptowhite(arg);
1663 idx = sign_cmd_idx(arg, p);
1664 if (idx == SIGNCMD_LAST)
1665 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001666 semsg(_("E160: Unknown sign command: %s"), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001667 return;
1668 }
1669 arg = skipwhite(p);
1670
1671 if (idx <= SIGNCMD_LIST)
1672 {
1673 // Define, undefine or list signs.
1674 if (idx == SIGNCMD_LIST && *arg == NUL)
1675 {
1676 // ":sign list": list all defined signs
1677 for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next)
1678 sign_list_defined(sp);
1679 }
1680 else if (*arg == NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001681 emsg(_("E156: Missing sign name"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001682 else
1683 {
1684 char_u *name;
1685
1686 // Isolate the sign name. If it's a number skip leading zeroes,
1687 // so that "099" and "99" are the same sign. But keep "0".
1688 p = skiptowhite(arg);
1689 if (*p != NUL)
1690 *p++ = NUL;
1691 while (arg[0] == '0' && arg[1] != NUL)
1692 ++arg;
1693 name = vim_strsave(arg);
1694
1695 if (idx == SIGNCMD_DEFINE)
1696 sign_define_cmd(name, p);
1697 else if (idx == SIGNCMD_LIST)
1698 // ":sign list {name}"
1699 sign_list_by_name(name);
1700 else
1701 // ":sign undefine {name}"
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001702 sign_undefine_by_name(name, TRUE);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001703
1704 vim_free(name);
1705 return;
1706 }
1707 }
1708 else
1709 {
1710 int id = -1;
1711 linenr_T lnum = -1;
1712 char_u *sign_name = NULL;
1713 char_u *group = NULL;
1714 int prio = SIGN_DEF_PRIO;
1715
1716 // Parse command line arguments
1717 if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio,
1718 &buf, &lnum) == FAIL)
1719 return;
1720
1721 if (idx == SIGNCMD_PLACE)
1722 sign_place_cmd(buf, lnum, sign_name, id, group, prio);
1723 else if (idx == SIGNCMD_UNPLACE)
1724 sign_unplace_cmd(buf, lnum, sign_name, id, group);
1725 else if (idx == SIGNCMD_JUMP)
1726 sign_jump_cmd(buf, lnum, sign_name, id, group);
1727 }
1728}
1729
1730/*
1731 * Return information about a specified sign
1732 */
1733 static void
1734sign_getinfo(sign_T *sp, dict_T *retdict)
1735{
1736 char_u *p;
1737
1738 dict_add_string(retdict, "name", (char_u *)sp->sn_name);
1739 if (sp->sn_icon != NULL)
1740 dict_add_string(retdict, "icon", (char_u *)sp->sn_icon);
1741 if (sp->sn_text != NULL)
1742 dict_add_string(retdict, "text", (char_u *)sp->sn_text);
1743 if (sp->sn_line_hl > 0)
1744 {
1745 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1746 if (p == NULL)
1747 p = (char_u *)"NONE";
1748 dict_add_string(retdict, "linehl", (char_u *)p);
1749 }
1750 if (sp->sn_text_hl > 0)
1751 {
1752 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1753 if (p == NULL)
1754 p = (char_u *)"NONE";
1755 dict_add_string(retdict, "texthl", (char_u *)p);
1756 }
Bram Moolenaare413ea02021-11-24 16:20:13 +00001757 if (sp->sn_cul_hl > 0)
1758 {
1759 p = get_highlight_name_ext(NULL, sp->sn_cul_hl - 1, FALSE);
1760 if (p == NULL)
1761 p = (char_u *)"NONE";
1762 dict_add_string(retdict, "culhl", (char_u *)p);
1763 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001764}
1765
1766/*
1767 * If 'name' is NULL, return a list of all the defined signs.
1768 * Otherwise, return information about the specified sign.
1769 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001770 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001771sign_getlist(char_u *name, list_T *retlist)
1772{
1773 sign_T *sp = first_sign;
1774 dict_T *dict;
1775
1776 if (name != NULL)
1777 {
1778 sp = sign_find(name, NULL);
1779 if (sp == NULL)
1780 return;
1781 }
1782
1783 for (; sp != NULL && !got_int; sp = sp->sn_next)
1784 {
1785 if ((dict = dict_alloc_id(aid_sign_getlist)) == NULL)
1786 return;
1787 if (list_append_dict(retlist, dict) == FAIL)
1788 return;
1789 sign_getinfo(sp, dict);
1790
1791 if (name != NULL) // handle only the specified sign
1792 break;
1793 }
1794}
1795
1796/*
1797 * Returns information about signs placed in a buffer as list of dicts.
1798 */
1799 void
1800get_buffer_signs(buf_T *buf, list_T *l)
1801{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001802 sign_entry_T *sign;
1803 dict_T *d;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001804
1805 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1806 {
1807 if ((d = sign_get_info(sign)) != NULL)
1808 list_append_dict(l, d);
1809 }
1810}
1811
1812/*
1813 * Return information about all the signs placed in a buffer
1814 */
1815 static void
1816sign_get_placed_in_buf(
1817 buf_T *buf,
1818 linenr_T lnum,
1819 int sign_id,
1820 char_u *sign_group,
1821 list_T *retlist)
1822{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001823 dict_T *d;
1824 list_T *l;
1825 sign_entry_T *sign;
1826 dict_T *sdict;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001827
1828 if ((d = dict_alloc_id(aid_sign_getplaced_dict)) == NULL)
1829 return;
1830 list_append_dict(retlist, d);
1831
1832 dict_add_number(d, "bufnr", (long)buf->b_fnum);
1833
1834 if ((l = list_alloc_id(aid_sign_getplaced_list)) == NULL)
1835 return;
1836 dict_add_list(d, "signs", l);
1837
1838 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1839 {
1840 if (!sign_in_group(sign, sign_group))
1841 continue;
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001842 if ((lnum == 0 && sign_id == 0)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001843 || (sign_id == 0 && lnum == sign->se_lnum)
1844 || (lnum == 0 && sign_id == sign->se_id)
1845 || (lnum == sign->se_lnum && sign_id == sign->se_id))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001846 {
1847 if ((sdict = sign_get_info(sign)) != NULL)
1848 list_append_dict(l, sdict);
1849 }
1850 }
1851}
1852
1853/*
1854 * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the
1855 * sign placed at the line number. If 'lnum' is zero, return all the signs
1856 * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers.
1857 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001858 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001859sign_get_placed(
1860 buf_T *buf,
1861 linenr_T lnum,
1862 int sign_id,
1863 char_u *sign_group,
1864 list_T *retlist)
1865{
1866 if (buf != NULL)
1867 sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist);
1868 else
1869 {
1870 FOR_ALL_BUFFERS(buf)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001871 if (buf->b_signlist != NULL)
1872 sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001873 }
1874}
1875
1876# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1877/*
1878 * Allocate the icons. Called when the GUI has started. Allows defining
1879 * signs before it starts.
1880 */
1881 void
1882sign_gui_started(void)
1883{
1884 sign_T *sp;
1885
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001886 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001887 if (sp->sn_icon != NULL)
1888 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
1889}
1890# endif
1891
1892/*
1893 * List one sign.
1894 */
1895 static void
1896sign_list_defined(sign_T *sp)
1897{
1898 char_u *p;
1899
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001900 smsg("sign %s", sp->sn_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001901 if (sp->sn_icon != NULL)
1902 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001903 msg_puts(" icon=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001904 msg_outtrans(sp->sn_icon);
1905# ifdef FEAT_SIGN_ICONS
1906 if (sp->sn_image == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001907 msg_puts(_(" (NOT FOUND)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001908# else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001909 msg_puts(_(" (not supported)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001910# endif
1911 }
1912 if (sp->sn_text != NULL)
1913 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001914 msg_puts(" text=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001915 msg_outtrans(sp->sn_text);
1916 }
1917 if (sp->sn_line_hl > 0)
1918 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001919 msg_puts(" linehl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001920 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1921 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001922 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001923 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001924 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001925 }
1926 if (sp->sn_text_hl > 0)
1927 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001928 msg_puts(" texthl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001929 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1930 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001931 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001932 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001933 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001934 }
Bram Moolenaare413ea02021-11-24 16:20:13 +00001935 if (sp->sn_cul_hl > 0)
1936 {
1937 msg_puts(" culhl=");
1938 p = get_highlight_name_ext(NULL, sp->sn_cul_hl - 1, FALSE);
1939 if (p == NULL)
1940 msg_puts("NONE");
1941 else
1942 msg_puts((char *)p);
1943 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001944}
1945
1946/*
1947 * Undefine a sign and free its memory.
1948 */
1949 static void
1950sign_undefine(sign_T *sp, sign_T *sp_prev)
1951{
1952 vim_free(sp->sn_name);
1953 vim_free(sp->sn_icon);
1954# ifdef FEAT_SIGN_ICONS
1955 if (sp->sn_image != NULL)
1956 {
1957 out_flush();
1958 gui_mch_destroy_sign(sp->sn_image);
1959 }
1960# endif
1961 vim_free(sp->sn_text);
1962 if (sp_prev == NULL)
1963 first_sign = sp->sn_next;
1964 else
1965 sp_prev->sn_next = sp->sn_next;
1966 vim_free(sp);
1967}
1968
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001969# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1970 void *
1971sign_get_image(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001972 int typenr) // the attribute which may have a sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001973{
1974 sign_T *sp;
1975
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001976 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001977 if (sp->sn_typenr == typenr)
1978 return sp->sn_image;
1979 return NULL;
1980}
1981# endif
1982
1983/*
1984 * Undefine/free all signs.
1985 */
1986 void
1987free_signs(void)
1988{
1989 while (first_sign != NULL)
1990 sign_undefine(first_sign, NULL);
1991}
1992
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001993static enum
1994{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001995 EXP_SUBCMD, // expand :sign sub-commands
1996 EXP_DEFINE, // expand :sign define {name} args
1997 EXP_PLACE, // expand :sign place {id} args
Bram Moolenaar3678f652019-02-17 14:50:25 +01001998 EXP_LIST, // expand :sign place args
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001999 EXP_UNPLACE, // expand :sign unplace"
Bram Moolenaar3678f652019-02-17 14:50:25 +01002000 EXP_SIGN_NAMES, // expand with name of placed signs
2001 EXP_SIGN_GROUPS // expand with name of placed sign groups
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002002} expand_what;
2003
2004/*
Bram Moolenaar3678f652019-02-17 14:50:25 +01002005 * Return the n'th sign name (used for command line completion)
2006 */
2007 static char_u *
2008get_nth_sign_name(int idx)
2009{
2010 int current_idx;
2011 sign_T *sp;
2012
2013 // Complete with name of signs already defined
2014 current_idx = 0;
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002015 FOR_ALL_SIGNS(sp)
Bram Moolenaar3678f652019-02-17 14:50:25 +01002016 if (current_idx++ == idx)
2017 return sp->sn_name;
2018 return NULL;
2019}
2020
2021/*
2022 * Return the n'th sign group name (used for command line completion)
2023 */
2024 static char_u *
2025get_nth_sign_group_name(int idx)
2026{
2027 int current_idx;
2028 int todo;
2029 hashitem_T *hi;
2030 signgroup_T *group;
2031
2032 // Complete with name of sign groups already defined
2033 current_idx = 0;
2034 todo = (int)sg_table.ht_used;
2035 for (hi = sg_table.ht_array; todo > 0; ++hi)
2036 {
2037 if (!HASHITEM_EMPTY(hi))
2038 {
2039 --todo;
2040 if (current_idx++ == idx)
2041 {
2042 group = HI2SG(hi);
2043 return group->sg_name;
2044 }
2045 }
2046 }
2047 return NULL;
2048}
2049
2050/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002051 * Function given to ExpandGeneric() to obtain the sign command
2052 * expansion.
2053 */
2054 char_u *
2055get_sign_name(expand_T *xp UNUSED, int idx)
2056{
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002057 switch (expand_what)
2058 {
2059 case EXP_SUBCMD:
2060 return (char_u *)cmds[idx];
2061 case EXP_DEFINE:
2062 {
2063 char *define_arg[] =
2064 {
2065 "icon=", "linehl=", "text=", "texthl=", NULL
2066 };
2067 return (char_u *)define_arg[idx];
2068 }
2069 case EXP_PLACE:
2070 {
2071 char *place_arg[] =
2072 {
2073 "line=", "name=", "group=", "priority=", "file=",
2074 "buffer=", NULL
2075 };
2076 return (char_u *)place_arg[idx];
2077 }
Bram Moolenaar3678f652019-02-17 14:50:25 +01002078 case EXP_LIST:
2079 {
2080 char *list_arg[] =
2081 {
2082 "group=", "file=", "buffer=", NULL
2083 };
2084 return (char_u *)list_arg[idx];
2085 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002086 case EXP_UNPLACE:
2087 {
2088 char *unplace_arg[] = { "group=", "file=", "buffer=", NULL };
2089 return (char_u *)unplace_arg[idx];
2090 }
2091 case EXP_SIGN_NAMES:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002092 return get_nth_sign_name(idx);
2093 case EXP_SIGN_GROUPS:
2094 return get_nth_sign_group_name(idx);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002095 default:
2096 return NULL;
2097 }
2098}
2099
2100/*
2101 * Handle command line completion for :sign command.
2102 */
2103 void
2104set_context_in_sign_cmd(expand_T *xp, char_u *arg)
2105{
2106 char_u *p;
2107 char_u *end_subcmd;
2108 char_u *last;
2109 int cmd_idx;
2110 char_u *begin_subcmd_args;
2111
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002112 // Default: expand subcommands.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002113 xp->xp_context = EXPAND_SIGN;
2114 expand_what = EXP_SUBCMD;
2115 xp->xp_pattern = arg;
2116
2117 end_subcmd = skiptowhite(arg);
2118 if (*end_subcmd == NUL)
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002119 // expand subcmd name
2120 // :sign {subcmd}<CTRL-D>
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002121 return;
2122
2123 cmd_idx = sign_cmd_idx(arg, end_subcmd);
2124
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002125 // :sign {subcmd} {subcmd_args}
2126 // |
2127 // begin_subcmd_args
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002128 begin_subcmd_args = skipwhite(end_subcmd);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002129
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002130 // expand last argument of subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002131
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002132 // :sign define {name} {args}...
2133 // |
2134 // p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002135
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002136 // Loop until reaching last argument.
Bram Moolenaar3678f652019-02-17 14:50:25 +01002137 p = begin_subcmd_args;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002138 do
2139 {
2140 p = skipwhite(p);
2141 last = p;
2142 p = skiptowhite(p);
2143 } while (*p != NUL);
2144
2145 p = vim_strchr(last, '=');
2146
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002147 // :sign define {name} {args}... {last}=
2148 // | |
2149 // last p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002150 if (p == NULL)
2151 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002152 // Expand last argument name (before equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002153 xp->xp_pattern = last;
2154 switch (cmd_idx)
2155 {
2156 case SIGNCMD_DEFINE:
2157 expand_what = EXP_DEFINE;
2158 break;
2159 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002160 // List placed signs
2161 if (VIM_ISDIGIT(*begin_subcmd_args))
2162 // :sign place {id} {args}...
2163 expand_what = EXP_PLACE;
2164 else
2165 // :sign place {args}...
2166 expand_what = EXP_LIST;
2167 break;
2168 case SIGNCMD_LIST:
2169 case SIGNCMD_UNDEFINE:
2170 // :sign list <CTRL-D>
2171 // :sign undefine <CTRL-D>
2172 expand_what = EXP_SIGN_NAMES;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002173 break;
2174 case SIGNCMD_JUMP:
2175 case SIGNCMD_UNPLACE:
2176 expand_what = EXP_UNPLACE;
2177 break;
2178 default:
2179 xp->xp_context = EXPAND_NOTHING;
2180 }
2181 }
2182 else
2183 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002184 // Expand last argument value (after equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002185 xp->xp_pattern = p + 1;
2186 switch (cmd_idx)
2187 {
2188 case SIGNCMD_DEFINE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002189 if (STRNCMP(last, "texthl", 6) == 0
2190 || STRNCMP(last, "linehl", 6) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002191 xp->xp_context = EXPAND_HIGHLIGHT;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002192 else if (STRNCMP(last, "icon", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002193 xp->xp_context = EXPAND_FILES;
2194 else
2195 xp->xp_context = EXPAND_NOTHING;
2196 break;
2197 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002198 if (STRNCMP(last, "name", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002199 expand_what = EXP_SIGN_NAMES;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002200 else if (STRNCMP(last, "group", 5) == 0)
2201 expand_what = EXP_SIGN_GROUPS;
2202 else if (STRNCMP(last, "file", 4) == 0)
2203 xp->xp_context = EXPAND_BUFFERS;
2204 else
2205 xp->xp_context = EXPAND_NOTHING;
2206 break;
2207 case SIGNCMD_UNPLACE:
2208 case SIGNCMD_JUMP:
2209 if (STRNCMP(last, "group", 5) == 0)
2210 expand_what = EXP_SIGN_GROUPS;
2211 else if (STRNCMP(last, "file", 4) == 0)
2212 xp->xp_context = EXPAND_BUFFERS;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002213 else
2214 xp->xp_context = EXPAND_NOTHING;
2215 break;
2216 default:
2217 xp->xp_context = EXPAND_NOTHING;
2218 }
2219 }
2220}
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002221
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002222/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002223 * Define a sign using the attributes in 'dict'. Returns 0 on success and -1 on
2224 * failure.
2225 */
2226 static int
2227sign_define_from_dict(char_u *name_arg, dict_T *dict)
2228{
2229 char_u *name = NULL;
2230 char_u *icon = NULL;
2231 char_u *linehl = NULL;
2232 char_u *text = NULL;
2233 char_u *texthl = NULL;
Bram Moolenaare413ea02021-11-24 16:20:13 +00002234 char_u *culhl = NULL;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002235 int retval = -1;
2236
2237 if (name_arg == NULL)
2238 {
2239 if (dict == NULL)
2240 return -1;
2241 name = dict_get_string(dict, (char_u *)"name", TRUE);
2242 }
2243 else
2244 name = vim_strsave(name_arg);
2245 if (name == NULL || name[0] == NUL)
2246 goto cleanup;
2247 if (dict != NULL)
2248 {
2249 icon = dict_get_string(dict, (char_u *)"icon", TRUE);
2250 linehl = dict_get_string(dict, (char_u *)"linehl", TRUE);
2251 text = dict_get_string(dict, (char_u *)"text", TRUE);
2252 texthl = dict_get_string(dict, (char_u *)"texthl", TRUE);
Bram Moolenaare413ea02021-11-24 16:20:13 +00002253 culhl = dict_get_string(dict, (char_u *)"culhl", TRUE);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002254 }
2255
Bram Moolenaare413ea02021-11-24 16:20:13 +00002256 if (sign_define_by_name(name, icon, linehl, text, texthl, culhl) == OK)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002257 retval = 0;
2258
2259cleanup:
2260 vim_free(name);
2261 vim_free(icon);
2262 vim_free(linehl);
2263 vim_free(text);
2264 vim_free(texthl);
Bram Moolenaare413ea02021-11-24 16:20:13 +00002265 vim_free(culhl);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002266
2267 return retval;
2268}
2269
2270/*
2271 * Define multiple signs using attributes from list 'l' and store the return
2272 * values in 'retlist'.
2273 */
2274 static void
2275sign_define_multiple(list_T *l, list_T *retlist)
2276{
2277 listitem_T *li;
2278 int retval;
2279
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002280 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002281 {
2282 retval = -1;
2283 if (li->li_tv.v_type == VAR_DICT)
2284 retval = sign_define_from_dict(NULL, li->li_tv.vval.v_dict);
2285 else
2286 emsg(_(e_dictreq));
2287 list_append_number(retlist, retval);
2288 }
2289}
2290
2291/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002292 * "sign_define()" function
2293 */
2294 void
2295f_sign_define(typval_T *argvars, typval_T *rettv)
2296{
2297 char_u *name;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002298
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002299 if (in_vim9script()
2300 && (check_for_string_or_list_arg(argvars, 0) == FAIL
2301 || check_for_opt_dict_arg(argvars, 1) == FAIL))
2302 return;
2303
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002304 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2305 {
2306 // Define multiple signs
2307 if (rettv_list_alloc(rettv) != OK)
2308 return;
2309
2310 sign_define_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2311 return;
2312 }
2313
2314 // Define a single sign
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002315 rettv->vval.v_number = -1;
2316
2317 name = tv_get_string_chk(&argvars[0]);
2318 if (name == NULL)
2319 return;
2320
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002321 if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_DICT)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002322 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002323 emsg(_(e_dictreq));
2324 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002325 }
2326
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002327 rettv->vval.v_number = sign_define_from_dict(name,
2328 argvars[1].v_type == VAR_DICT ? argvars[1].vval.v_dict : NULL);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002329}
2330
2331/*
2332 * "sign_getdefined()" function
2333 */
2334 void
2335f_sign_getdefined(typval_T *argvars, typval_T *rettv)
2336{
2337 char_u *name = NULL;
2338
2339 if (rettv_list_alloc_id(rettv, aid_sign_getdefined) != OK)
2340 return;
2341
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002342 if (in_vim9script() && check_for_opt_string_arg(argvars, 0) == FAIL)
2343 return;
2344
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002345 if (argvars[0].v_type != VAR_UNKNOWN)
2346 name = tv_get_string(&argvars[0]);
2347
2348 sign_getlist(name, rettv->vval.v_list);
2349}
2350
2351/*
2352 * "sign_getplaced()" function
2353 */
2354 void
2355f_sign_getplaced(typval_T *argvars, typval_T *rettv)
2356{
2357 buf_T *buf = NULL;
2358 dict_T *dict;
2359 dictitem_T *di;
2360 linenr_T lnum = 0;
2361 int sign_id = 0;
2362 char_u *group = NULL;
2363 int notanum = FALSE;
2364
2365 if (rettv_list_alloc_id(rettv, aid_sign_getplaced) != OK)
2366 return;
2367
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002368 if (in_vim9script()
Yegappan Lakshmanancd917202021-07-21 19:09:09 +02002369 && (check_for_opt_buffer_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002370 || (argvars[0].v_type != VAR_UNKNOWN
2371 && check_for_opt_dict_arg(argvars, 1) == FAIL)))
2372 return;
2373
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002374 if (argvars[0].v_type != VAR_UNKNOWN)
2375 {
2376 // get signs placed in the specified buffer
2377 buf = get_buf_arg(&argvars[0]);
2378 if (buf == NULL)
2379 return;
2380
2381 if (argvars[1].v_type != VAR_UNKNOWN)
2382 {
2383 if (argvars[1].v_type != VAR_DICT ||
2384 ((dict = argvars[1].vval.v_dict) == NULL))
2385 {
2386 emsg(_(e_dictreq));
2387 return;
2388 }
2389 if ((di = dict_find(dict, (char_u *)"lnum", -1)) != NULL)
2390 {
2391 // get signs placed at this line
2392 (void)tv_get_number_chk(&di->di_tv, &notanum);
2393 if (notanum)
2394 return;
2395 lnum = tv_get_lnum(&di->di_tv);
2396 }
2397 if ((di = dict_find(dict, (char_u *)"id", -1)) != NULL)
2398 {
2399 // get sign placed with this identifier
2400 sign_id = (int)tv_get_number_chk(&di->di_tv, &notanum);
2401 if (notanum)
2402 return;
2403 }
2404 if ((di = dict_find(dict, (char_u *)"group", -1)) != NULL)
2405 {
2406 group = tv_get_string_chk(&di->di_tv);
2407 if (group == NULL)
2408 return;
2409 if (*group == '\0') // empty string means global group
2410 group = NULL;
2411 }
2412 }
2413 }
2414
2415 sign_get_placed(buf, lnum, sign_id, group, rettv->vval.v_list);
2416}
2417
2418/*
2419 * "sign_jump()" function
2420 */
2421 void
2422f_sign_jump(typval_T *argvars, typval_T *rettv)
2423{
2424 int sign_id;
2425 char_u *sign_group = NULL;
2426 buf_T *buf;
2427 int notanum = FALSE;
2428
2429 rettv->vval.v_number = -1;
2430
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002431 if (in_vim9script()
2432 && (check_for_number_arg(argvars, 0) == FAIL
2433 || check_for_string_arg(argvars, 1) == FAIL
2434 || check_for_buffer_arg(argvars, 2) == FAIL))
2435 return;
2436
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002437 // Sign identifier
2438 sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
2439 if (notanum)
2440 return;
2441 if (sign_id <= 0)
2442 {
2443 emsg(_(e_invarg));
2444 return;
2445 }
2446
2447 // Sign group
2448 sign_group = tv_get_string_chk(&argvars[1]);
2449 if (sign_group == NULL)
2450 return;
2451 if (sign_group[0] == '\0')
2452 sign_group = NULL; // global sign group
2453 else
2454 {
2455 sign_group = vim_strsave(sign_group);
2456 if (sign_group == NULL)
2457 return;
2458 }
2459
2460 // Buffer to place the sign
2461 buf = get_buf_arg(&argvars[2]);
2462 if (buf == NULL)
2463 goto cleanup;
2464
2465 rettv->vval.v_number = sign_jump(sign_id, sign_group, buf);
2466
2467cleanup:
2468 vim_free(sign_group);
2469}
2470
2471/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002472 * Place a new sign using the values specified in dict 'dict'. Returns the sign
2473 * identifier if successfully placed, otherwise returns 0.
2474 */
2475 static int
2476sign_place_from_dict(
2477 typval_T *id_tv,
2478 typval_T *group_tv,
2479 typval_T *name_tv,
2480 typval_T *buf_tv,
2481 dict_T *dict)
2482{
2483 int sign_id = 0;
2484 char_u *group = NULL;
2485 char_u *sign_name = NULL;
2486 buf_T *buf = NULL;
2487 dictitem_T *di;
2488 linenr_T lnum = 0;
2489 int prio = SIGN_DEF_PRIO;
2490 int notanum = FALSE;
2491 int ret_sign_id = -1;
2492
2493 // sign identifier
2494 if (id_tv == NULL)
2495 {
2496 di = dict_find(dict, (char_u *)"id", -1);
2497 if (di != NULL)
2498 id_tv = &di->di_tv;
2499 }
2500 if (id_tv == NULL)
2501 sign_id = 0;
2502 else
2503 {
2504 sign_id = tv_get_number_chk(id_tv, &notanum);
2505 if (notanum)
2506 return -1;
2507 if (sign_id < 0)
2508 {
2509 emsg(_(e_invarg));
2510 return -1;
2511 }
2512 }
2513
2514 // sign group
2515 if (group_tv == NULL)
2516 {
2517 di = dict_find(dict, (char_u *)"group", -1);
2518 if (di != NULL)
2519 group_tv = &di->di_tv;
2520 }
2521 if (group_tv == NULL)
2522 group = NULL; // global group
2523 else
2524 {
2525 group = tv_get_string_chk(group_tv);
2526 if (group == NULL)
2527 goto cleanup;
2528 if (group[0] == '\0') // global sign group
2529 group = NULL;
2530 else
2531 {
2532 group = vim_strsave(group);
2533 if (group == NULL)
2534 return -1;
2535 }
2536 }
2537
2538 // sign name
2539 if (name_tv == NULL)
2540 {
2541 di = dict_find(dict, (char_u *)"name", -1);
2542 if (di != NULL)
2543 name_tv = &di->di_tv;
2544 }
2545 if (name_tv == NULL)
2546 goto cleanup;
2547 sign_name = tv_get_string_chk(name_tv);
2548 if (sign_name == NULL)
2549 goto cleanup;
2550
2551 // buffer to place the sign
2552 if (buf_tv == NULL)
2553 {
2554 di = dict_find(dict, (char_u *)"buffer", -1);
2555 if (di != NULL)
2556 buf_tv = &di->di_tv;
2557 }
2558 if (buf_tv == NULL)
2559 goto cleanup;
2560 buf = get_buf_arg(buf_tv);
2561 if (buf == NULL)
2562 goto cleanup;
2563
2564 // line number of the sign
2565 di = dict_find(dict, (char_u *)"lnum", -1);
2566 if (di != NULL)
2567 {
Bram Moolenaar42aff462019-08-21 13:20:29 +02002568 lnum = tv_get_lnum(&di->di_tv);
2569 if (lnum <= 0)
2570 {
2571 emsg(_(e_invarg));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002572 goto cleanup;
Bram Moolenaar42aff462019-08-21 13:20:29 +02002573 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002574 }
2575
2576 // sign priority
2577 di = dict_find(dict, (char_u *)"priority", -1);
2578 if (di != NULL)
2579 {
2580 prio = (int)tv_get_number_chk(&di->di_tv, &notanum);
2581 if (notanum)
2582 goto cleanup;
2583 }
2584
2585 if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK)
2586 ret_sign_id = sign_id;
2587
2588cleanup:
2589 vim_free(group);
2590
2591 return ret_sign_id;
2592}
2593
2594/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002595 * "sign_place()" function
2596 */
2597 void
2598f_sign_place(typval_T *argvars, typval_T *rettv)
2599{
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002600 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002601
2602 rettv->vval.v_number = -1;
2603
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002604 if (in_vim9script()
2605 && (check_for_number_arg(argvars, 0) == FAIL
2606 || check_for_string_arg(argvars, 1) == FAIL
2607 || check_for_string_arg(argvars, 2) == FAIL
2608 || check_for_buffer_arg(argvars, 3) == FAIL
2609 || check_for_opt_dict_arg(argvars, 4) == FAIL))
2610 return;
2611
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002612 if (argvars[4].v_type != VAR_UNKNOWN
2613 && (argvars[4].v_type != VAR_DICT
2614 || ((dict = argvars[4].vval.v_dict) == NULL)))
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002615 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002616 emsg(_(e_dictreq));
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002617 return;
2618 }
2619
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002620 rettv->vval.v_number = sign_place_from_dict(&argvars[0], &argvars[1],
2621 &argvars[2], &argvars[3], dict);
2622}
2623
2624/*
2625 * "sign_placelist()" function. Place multiple signs.
2626 */
2627 void
2628f_sign_placelist(typval_T *argvars, typval_T *rettv)
2629{
2630 listitem_T *li;
2631 int sign_id;
2632
2633 if (rettv_list_alloc(rettv) != OK)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002634 return;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002635
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002636 if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
2637 return;
2638
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002639 if (argvars[0].v_type != VAR_LIST)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002640 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002641 emsg(_(e_listreq));
2642 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002643 }
2644
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002645 // Process the List of sign attributes
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002646 FOR_ALL_LIST_ITEMS(argvars[0].vval.v_list, li)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002647 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002648 sign_id = -1;
2649 if (li->li_tv.v_type == VAR_DICT)
2650 sign_id = sign_place_from_dict(NULL, NULL, NULL, NULL,
2651 li->li_tv.vval.v_dict);
2652 else
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002653 emsg(_(e_dictreq));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002654 list_append_number(rettv->vval.v_list, sign_id);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002655 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002656}
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002657
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002658/*
2659 * Undefine multiple signs
2660 */
2661 static void
2662sign_undefine_multiple(list_T *l, list_T *retlist)
2663{
2664 char_u *name;
2665 listitem_T *li;
2666 int retval;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002667
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002668 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002669 {
2670 retval = -1;
2671 name = tv_get_string_chk(&li->li_tv);
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002672 if (name != NULL && (sign_undefine_by_name(name, TRUE) == OK))
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002673 retval = 0;
2674 list_append_number(retlist, retval);
2675 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002676}
2677
2678/*
2679 * "sign_undefine()" function
2680 */
2681 void
2682f_sign_undefine(typval_T *argvars, typval_T *rettv)
2683{
2684 char_u *name;
2685
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002686 if (in_vim9script()
2687 && check_for_opt_string_or_list_arg(argvars, 0) == FAIL)
2688 return;
2689
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002690 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2691 {
2692 // Undefine multiple signs
2693 if (rettv_list_alloc(rettv) != OK)
2694 return;
2695
2696 sign_undefine_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2697 return;
2698 }
2699
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002700 rettv->vval.v_number = -1;
2701
2702 if (argvars[0].v_type == VAR_UNKNOWN)
2703 {
2704 // Free all the signs
2705 free_signs();
2706 rettv->vval.v_number = 0;
2707 }
2708 else
2709 {
2710 // Free only the specified sign
2711 name = tv_get_string_chk(&argvars[0]);
2712 if (name == NULL)
2713 return;
2714
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002715 if (sign_undefine_by_name(name, TRUE) == OK)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002716 rettv->vval.v_number = 0;
2717 }
2718}
2719
2720/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002721 * Unplace the sign with attributes specified in 'dict'. Returns 0 on success
2722 * and -1 on failure.
2723 */
2724 static int
2725sign_unplace_from_dict(typval_T *group_tv, dict_T *dict)
2726{
2727 dictitem_T *di;
2728 int sign_id = 0;
2729 buf_T *buf = NULL;
2730 char_u *group = NULL;
2731 int retval = -1;
2732
2733 // sign group
2734 if (group_tv != NULL)
2735 group = tv_get_string(group_tv);
2736 else
2737 group = dict_get_string(dict, (char_u *)"group", FALSE);
2738 if (group != NULL)
2739 {
2740 if (group[0] == '\0') // global sign group
2741 group = NULL;
2742 else
2743 {
2744 group = vim_strsave(group);
2745 if (group == NULL)
2746 return -1;
2747 }
2748 }
2749
2750 if (dict != NULL)
2751 {
2752 if ((di = dict_find(dict, (char_u *)"buffer", -1)) != NULL)
2753 {
2754 buf = get_buf_arg(&di->di_tv);
2755 if (buf == NULL)
2756 goto cleanup;
2757 }
2758 if (dict_find(dict, (char_u *)"id", -1) != NULL)
2759 {
2760 sign_id = dict_get_number(dict, (char_u *)"id");
2761 if (sign_id <= 0)
2762 {
2763 emsg(_(e_invarg));
2764 goto cleanup;
2765 }
2766 }
2767 }
2768
2769 if (buf == NULL)
2770 {
2771 // Delete the sign in all the buffers
2772 retval = 0;
2773 FOR_ALL_BUFFERS(buf)
2774 if (sign_unplace(sign_id, group, buf, 0) != OK)
2775 retval = -1;
2776 }
2777 else if (sign_unplace(sign_id, group, buf, 0) == OK)
2778 retval = 0;
2779
2780cleanup:
2781 vim_free(group);
2782
2783 return retval;
2784}
2785
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002786 sign_entry_T *
2787get_first_valid_sign(win_T *wp)
2788{
2789 sign_entry_T *sign = wp->w_buffer->b_signlist;
2790
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002791# ifdef FEAT_PROP_POPUP
Bram Moolenaar72570732019-11-30 14:21:53 +01002792 while (sign != NULL && !sign_group_for_window(sign, wp))
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002793 sign = sign->se_next;
2794# endif
2795 return sign;
2796}
2797
2798/*
2799 * Return TRUE when window "wp" has a column to draw signs in.
2800 */
2801 int
2802signcolumn_on(win_T *wp)
2803{
2804 // If 'signcolumn' is set to 'number', signs are displayed in the 'number'
2805 // column (if present). Otherwise signs are to be displayed in the sign
2806 // column.
2807 if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u')
2808 return get_first_valid_sign(wp) != NULL && !wp->w_p_nu && !wp->w_p_rnu;
2809
2810 if (*wp->w_p_scl == 'n')
2811 return FALSE;
2812 if (*wp->w_p_scl == 'y')
2813 return TRUE;
2814 return (get_first_valid_sign(wp) != NULL
2815# ifdef FEAT_NETBEANS_INTG
2816 || wp->w_buffer->b_has_sign_column
2817# endif
2818 );
2819}
2820
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002821/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002822 * "sign_unplace()" function
2823 */
2824 void
2825f_sign_unplace(typval_T *argvars, typval_T *rettv)
2826{
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002827 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002828
2829 rettv->vval.v_number = -1;
2830
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002831 if (in_vim9script()
2832 && (check_for_string_arg(argvars, 0) == FAIL
2833 || check_for_opt_dict_arg(argvars, 1) == FAIL))
2834 return;
2835
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002836 if (argvars[0].v_type != VAR_STRING)
2837 {
2838 emsg(_(e_invarg));
2839 return;
2840 }
2841
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002842 if (argvars[1].v_type != VAR_UNKNOWN)
2843 {
2844 if (argvars[1].v_type != VAR_DICT)
2845 {
2846 emsg(_(e_dictreq));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002847 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002848 }
2849 dict = argvars[1].vval.v_dict;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002850 }
2851
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002852 rettv->vval.v_number = sign_unplace_from_dict(&argvars[0], dict);
2853}
2854
2855/*
2856 * "sign_unplacelist()" function
2857 */
2858 void
2859f_sign_unplacelist(typval_T *argvars, typval_T *rettv)
2860{
2861 listitem_T *li;
2862 int retval;
2863
2864 if (rettv_list_alloc(rettv) != OK)
2865 return;
2866
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002867 if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
2868 return;
2869
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002870 if (argvars[0].v_type != VAR_LIST)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002871 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002872 emsg(_(e_listreq));
2873 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002874 }
2875
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002876 FOR_ALL_LIST_ITEMS(argvars[0].vval.v_list, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002877 {
2878 retval = -1;
2879 if (li->li_tv.v_type == VAR_DICT)
2880 retval = sign_unplace_from_dict(NULL, li->li_tv.vval.v_dict);
2881 else
2882 emsg(_(e_dictreq));
2883 list_append_number(rettv->vval.v_list, retval);
2884 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002885}
2886
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002887#endif // FEAT_SIGNS