blob: e358ee97527122bfc2df32ad6bfd43edee595400 [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)
Bram Moolenaar0bac5fc2021-12-05 17:45:49 +00001081 {
1082 if (*linehl == NUL)
1083 sp->sn_line_hl = 0;
1084 else
1085 sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl));
1086 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001087
1088 if (texthl != NULL)
Bram Moolenaar0bac5fc2021-12-05 17:45:49 +00001089 {
1090 if (*texthl == NUL)
1091 sp->sn_text_hl = 0;
1092 else
1093 sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl));
1094 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001095
Bram Moolenaare413ea02021-11-24 16:20:13 +00001096 if (culhl != NULL)
Bram Moolenaar0bac5fc2021-12-05 17:45:49 +00001097 {
1098 if (*culhl == NUL)
1099 sp->sn_cul_hl = 0;
1100 else
1101 sp->sn_cul_hl = syn_check_group(culhl, (int)STRLEN(culhl));
1102 }
Bram Moolenaare413ea02021-11-24 16:20:13 +00001103
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001104 return OK;
1105}
1106
1107/*
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001108 * Return TRUE if sign "name" exists.
1109 */
1110 int
1111sign_exists_by_name(char_u *name)
1112{
1113 return sign_find(name, NULL) != NULL;
1114}
1115
1116/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001117 * Free the sign specified by 'name'.
1118 */
1119 int
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001120sign_undefine_by_name(char_u *name, int give_error)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001121{
1122 sign_T *sp_prev;
1123 sign_T *sp;
1124
1125 sp = sign_find(name, &sp_prev);
1126 if (sp == NULL)
1127 {
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001128 if (give_error)
1129 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001130 return FAIL;
1131 }
1132 sign_undefine(sp, sp_prev);
1133
1134 return OK;
1135}
1136
1137/*
1138 * List the signs matching 'name'
1139 */
1140 static void
1141sign_list_by_name(char_u *name)
1142{
1143 sign_T *sp;
1144
1145 sp = sign_find(name, NULL);
1146 if (sp != NULL)
1147 sign_list_defined(sp);
1148 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001149 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001150}
1151
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001152 static void
1153may_force_numberwidth_recompute(buf_T *buf, int unplace)
1154{
1155 tabpage_T *tp;
1156 win_T *wp;
1157
1158 FOR_ALL_TAB_WINDOWS(tp, wp)
1159 if (wp->w_buffer == buf
1160 && (wp->w_p_nu || wp->w_p_rnu)
1161 && (unplace || wp->w_nrwidth_width < 2)
1162 && (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u'))
1163 wp->w_nrwidth_line_count = 0;
1164}
1165
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001166/*
Bram Moolenaar8144acb2019-01-14 23:08:18 +01001167 * Place a sign at the specified file location or update a sign.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001168 */
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001169 int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001170sign_place(
1171 int *sign_id,
1172 char_u *sign_group,
1173 char_u *sign_name,
1174 buf_T *buf,
1175 linenr_T lnum,
1176 int prio)
1177{
1178 sign_T *sp;
1179
1180 // Check for reserved character '*' in group name
1181 if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0'))
1182 return FAIL;
1183
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001184 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001185 if (STRCMP(sp->sn_name, sign_name) == 0)
1186 break;
1187 if (sp == NULL)
1188 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001189 semsg(_("E155: Unknown sign: %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001190 return FAIL;
1191 }
1192 if (*sign_id == 0)
1193 *sign_id = sign_group_get_next_signid(buf, sign_group);
1194
1195 if (lnum > 0)
1196 // ":sign place {id} line={lnum} name={name} file={fname}":
1197 // place a sign
1198 buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr);
1199 else
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02001200 // ":sign place {id} file={fname}": change sign type and/or priority
1201 lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr,
1202 prio);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001203 if (lnum > 0)
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001204 {
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001205 redraw_buf_line_later(buf, lnum);
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001206
1207 // When displaying signs in the 'number' column, if the width of the
1208 // number column is less than 2, then force recomputing the width.
1209 may_force_numberwidth_recompute(buf, FALSE);
1210 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001211 else
1212 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001213 semsg(_("E885: Not possible to change sign %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001214 return FAIL;
1215 }
1216
1217 return OK;
1218}
1219
1220/*
1221 * Unplace the specified sign
1222 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001223 static int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001224sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum)
1225{
1226 if (buf->b_signlist == NULL) // No signs in the buffer
1227 return OK;
1228
1229 if (sign_id == 0)
1230 {
1231 // Delete all the signs in the specified buffer
1232 redraw_buf_later(buf, NOT_VALID);
1233 buf_delete_signs(buf, sign_group);
1234 }
1235 else
1236 {
1237 linenr_T lnum;
1238
1239 // Delete only the specified signs
1240 lnum = buf_delsign(buf, atlnum, sign_id, sign_group);
1241 if (lnum == 0)
1242 return FAIL;
1243 }
1244
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001245 // When all the signs in a buffer are removed, force recomputing the
1246 // number column width (if enabled) in all the windows displaying the
1247 // buffer if 'signcolumn' is set to 'number' in that window.
1248 if (buf->b_signlist == NULL)
1249 may_force_numberwidth_recompute(buf, TRUE);
1250
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001251 return OK;
1252}
1253
1254/*
1255 * Unplace the sign at the current cursor line.
1256 */
1257 static void
1258sign_unplace_at_cursor(char_u *groupname)
1259{
1260 int id = -1;
1261
1262 id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname);
1263 if (id > 0)
1264 sign_unplace(id, groupname, curwin->w_buffer, curwin->w_cursor.lnum);
1265 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001266 emsg(_("E159: Missing sign number"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001267}
1268
1269/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001270 * Jump to a sign.
1271 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001272 static linenr_T
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001273sign_jump(int sign_id, char_u *sign_group, buf_T *buf)
1274{
1275 linenr_T lnum;
1276
1277 if ((lnum = buf_findsign(buf, sign_id, sign_group)) <= 0)
1278 {
Bram Moolenaarb5443cc2019-01-15 20:19:40 +01001279 semsg(_("E157: Invalid sign ID: %d"), sign_id);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001280 return -1;
1281 }
1282
1283 // goto a sign ...
1284 if (buf_jump_open_win(buf) != NULL)
1285 { // ... in a current window
1286 curwin->w_cursor.lnum = lnum;
1287 check_cursor_lnum();
1288 beginline(BL_WHITE);
1289 }
1290 else
1291 { // ... not currently in a window
1292 char_u *cmd;
1293
1294 if (buf->b_fname == NULL)
1295 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001296 emsg(_("E934: Cannot jump to a buffer that does not have a name"));
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001297 return -1;
1298 }
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02001299 cmd = alloc(STRLEN(buf->b_fname) + 25);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001300 if (cmd == NULL)
1301 return -1;
1302 sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname);
1303 do_cmdline_cmd(cmd);
1304 vim_free(cmd);
1305 }
1306# ifdef FEAT_FOLDING
1307 foldOpenCursor();
1308# endif
1309
1310 return lnum;
1311}
1312
1313/*
1314 * ":sign define {name} ..." command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001315 */
1316 static void
1317sign_define_cmd(char_u *sign_name, char_u *cmdline)
1318{
1319 char_u *arg;
1320 char_u *p = cmdline;
1321 char_u *icon = NULL;
1322 char_u *text = NULL;
1323 char_u *linehl = NULL;
1324 char_u *texthl = NULL;
Bram Moolenaare413ea02021-11-24 16:20:13 +00001325 char_u *culhl = NULL;
Bram Moolenaar0bac5fc2021-12-05 17:45:49 +00001326 int failed = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001327
1328 // set values for a defined sign.
1329 for (;;)
1330 {
1331 arg = skipwhite(p);
1332 if (*arg == NUL)
1333 break;
1334 p = skiptowhite_esc(arg);
1335 if (STRNCMP(arg, "icon=", 5) == 0)
1336 {
1337 arg += 5;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001338 icon = vim_strnsave(arg, p - arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001339 }
1340 else if (STRNCMP(arg, "text=", 5) == 0)
1341 {
1342 arg += 5;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001343 text = vim_strnsave(arg, p - arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001344 }
1345 else if (STRNCMP(arg, "linehl=", 7) == 0)
1346 {
1347 arg += 7;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001348 linehl = vim_strnsave(arg, p - arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001349 }
1350 else if (STRNCMP(arg, "texthl=", 7) == 0)
1351 {
1352 arg += 7;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001353 texthl = vim_strnsave(arg, p - arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001354 }
Bram Moolenaare413ea02021-11-24 16:20:13 +00001355 else if (STRNCMP(arg, "culhl=", 6) == 0)
1356 {
1357 arg += 6;
1358 culhl = vim_strnsave(arg, p - arg);
1359 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001360 else
1361 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001362 semsg(_(e_invarg2), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001363 failed = TRUE;
1364 break;
1365 }
1366 }
1367
1368 if (!failed)
Bram Moolenaare413ea02021-11-24 16:20:13 +00001369 sign_define_by_name(sign_name, icon, linehl, text, texthl, culhl);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001370
1371 vim_free(icon);
1372 vim_free(text);
1373 vim_free(linehl);
1374 vim_free(texthl);
Bram Moolenaare413ea02021-11-24 16:20:13 +00001375 vim_free(culhl);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001376}
1377
1378/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001379 * ":sign place" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001380 */
1381 static void
1382sign_place_cmd(
1383 buf_T *buf,
1384 linenr_T lnum,
1385 char_u *sign_name,
1386 int id,
1387 char_u *group,
1388 int prio)
1389{
1390 if (id <= 0)
1391 {
1392 // List signs placed in a file/buffer
1393 // :sign place file={fname}
1394 // :sign place group={group} file={fname}
1395 // :sign place group=* file={fname}
1396 // :sign place buffer={nr}
1397 // :sign place group={group} buffer={nr}
1398 // :sign place group=* buffer={nr}
1399 // :sign place
1400 // :sign place group={group}
1401 // :sign place group=*
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001402 if (lnum >= 0 || sign_name != NULL
1403 || (group != NULL && *group == '\0'))
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001404 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001405 else
1406 sign_list_placed(buf, group);
1407 }
1408 else
1409 {
1410 // Place a new sign
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001411 if (sign_name == NULL || buf == NULL
1412 || (group != NULL && *group == '\0'))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001413 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001414 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001415 return;
1416 }
1417
1418 sign_place(&id, group, sign_name, buf, lnum, prio);
1419 }
1420}
1421
1422/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001423 * ":sign unplace" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001424 */
1425 static void
1426sign_unplace_cmd(
1427 buf_T *buf,
1428 linenr_T lnum,
1429 char_u *sign_name,
1430 int id,
1431 char_u *group)
1432{
1433 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0'))
1434 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001435 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001436 return;
1437 }
1438
1439 if (id == -2)
1440 {
1441 if (buf != NULL)
1442 // :sign unplace * file={fname}
1443 // :sign unplace * group={group} file={fname}
1444 // :sign unplace * group=* file={fname}
1445 // :sign unplace * buffer={nr}
1446 // :sign unplace * group={group} buffer={nr}
1447 // :sign unplace * group=* buffer={nr}
1448 sign_unplace(0, group, buf, 0);
1449 else
1450 // :sign unplace *
1451 // :sign unplace * group={group}
1452 // :sign unplace * group=*
1453 FOR_ALL_BUFFERS(buf)
1454 if (buf->b_signlist != NULL)
1455 buf_delete_signs(buf, group);
1456 }
1457 else
1458 {
1459 if (buf != NULL)
1460 // :sign unplace {id} file={fname}
1461 // :sign unplace {id} group={group} file={fname}
1462 // :sign unplace {id} group=* file={fname}
1463 // :sign unplace {id} buffer={nr}
1464 // :sign unplace {id} group={group} buffer={nr}
1465 // :sign unplace {id} group=* buffer={nr}
1466 sign_unplace(id, group, buf, 0);
1467 else
1468 {
1469 if (id == -1)
1470 {
1471 // :sign unplace group={group}
1472 // :sign unplace group=*
1473 sign_unplace_at_cursor(group);
1474 }
1475 else
1476 {
1477 // :sign unplace {id}
1478 // :sign unplace {id} group={group}
1479 // :sign unplace {id} group=*
1480 FOR_ALL_BUFFERS(buf)
1481 sign_unplace(id, group, buf, 0);
1482 }
1483 }
1484 }
1485}
1486
1487/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001488 * Jump to a placed sign commands:
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001489 * :sign jump {id} file={fname}
1490 * :sign jump {id} buffer={nr}
1491 * :sign jump {id} group={group} file={fname}
1492 * :sign jump {id} group={group} buffer={nr}
1493 */
1494 static void
1495sign_jump_cmd(
1496 buf_T *buf,
1497 linenr_T lnum,
1498 char_u *sign_name,
1499 int id,
1500 char_u *group)
1501{
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001502 if (sign_name == NULL && group == NULL && id == -1)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001503 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001504 emsg(_(e_argreq));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001505 return;
1506 }
1507
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001508 if (buf == NULL || (group != NULL && *group == '\0')
1509 || lnum >= 0 || sign_name != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001510 {
1511 // File or buffer is not specified or an empty group is used
1512 // or a line number or a sign name is specified.
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001513 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001514 return;
1515 }
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001516 (void)sign_jump(id, group, buf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001517}
1518
1519/*
1520 * Parse the command line arguments for the ":sign place", ":sign unplace" and
1521 * ":sign jump" commands.
1522 * The supported arguments are: line={lnum} name={name} group={group}
1523 * priority={prio} and file={fname} or buffer={nr}.
1524 */
1525 static int
1526parse_sign_cmd_args(
1527 int cmd,
1528 char_u *arg,
1529 char_u **sign_name,
1530 int *signid,
1531 char_u **group,
1532 int *prio,
1533 buf_T **buf,
1534 linenr_T *lnum)
1535{
1536 char_u *arg1;
1537 char_u *name;
1538 char_u *filename = NULL;
Bram Moolenaarb589f952019-01-07 22:10:00 +01001539 int lnum_arg = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001540
1541 // first arg could be placed sign id
1542 arg1 = arg;
1543 if (VIM_ISDIGIT(*arg))
1544 {
1545 *signid = getdigits(&arg);
1546 if (!VIM_ISWHITE(*arg) && *arg != NUL)
1547 {
1548 *signid = -1;
1549 arg = arg1;
1550 }
1551 else
1552 arg = skipwhite(arg);
1553 }
1554
1555 while (*arg != NUL)
1556 {
1557 if (STRNCMP(arg, "line=", 5) == 0)
1558 {
1559 arg += 5;
1560 *lnum = atoi((char *)arg);
1561 arg = skiptowhite(arg);
Bram Moolenaarb589f952019-01-07 22:10:00 +01001562 lnum_arg = TRUE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001563 }
1564 else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE)
1565 {
1566 if (*signid != -1)
1567 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001568 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001569 return FAIL;
1570 }
1571 *signid = -2;
1572 arg = skiptowhite(arg + 1);
1573 }
1574 else if (STRNCMP(arg, "name=", 5) == 0)
1575 {
1576 arg += 5;
1577 name = arg;
1578 arg = skiptowhite(arg);
1579 if (*arg != NUL)
1580 *arg++ = NUL;
1581 while (name[0] == '0' && name[1] != NUL)
1582 ++name;
1583 *sign_name = name;
1584 }
1585 else if (STRNCMP(arg, "group=", 6) == 0)
1586 {
1587 arg += 6;
1588 *group = arg;
1589 arg = skiptowhite(arg);
1590 if (*arg != NUL)
1591 *arg++ = NUL;
1592 }
1593 else if (STRNCMP(arg, "priority=", 9) == 0)
1594 {
1595 arg += 9;
1596 *prio = atoi((char *)arg);
1597 arg = skiptowhite(arg);
1598 }
1599 else if (STRNCMP(arg, "file=", 5) == 0)
1600 {
1601 arg += 5;
1602 filename = arg;
1603 *buf = buflist_findname_exp(arg);
1604 break;
1605 }
1606 else if (STRNCMP(arg, "buffer=", 7) == 0)
1607 {
1608 arg += 7;
1609 filename = arg;
1610 *buf = buflist_findnr((int)getdigits(&arg));
1611 if (*skipwhite(arg) != NUL)
Bram Moolenaar2d06bfd2020-07-23 17:16:18 +02001612 semsg(_(e_trailing_arg), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001613 break;
1614 }
1615 else
1616 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001617 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001618 return FAIL;
1619 }
1620 arg = skipwhite(arg);
1621 }
1622
1623 if (filename != NULL && *buf == NULL)
1624 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001625 semsg(_("E158: Invalid buffer name: %s"), filename);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001626 return FAIL;
1627 }
1628
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001629 // If the filename is not supplied for the sign place or the sign jump
1630 // command, then use the current buffer.
Bram Moolenaarb589f952019-01-07 22:10:00 +01001631 if (filename == NULL && ((cmd == SIGNCMD_PLACE && lnum_arg)
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001632 || cmd == SIGNCMD_JUMP))
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001633 *buf = curwin->w_buffer;
1634
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001635 return OK;
1636}
1637
1638/*
1639 * ":sign" command
1640 */
1641 void
1642ex_sign(exarg_T *eap)
1643{
1644 char_u *arg = eap->arg;
1645 char_u *p;
1646 int idx;
1647 sign_T *sp;
1648 buf_T *buf = NULL;
1649
1650 // Parse the subcommand.
1651 p = skiptowhite(arg);
1652 idx = sign_cmd_idx(arg, p);
1653 if (idx == SIGNCMD_LAST)
1654 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001655 semsg(_("E160: Unknown sign command: %s"), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001656 return;
1657 }
1658 arg = skipwhite(p);
1659
1660 if (idx <= SIGNCMD_LIST)
1661 {
1662 // Define, undefine or list signs.
1663 if (idx == SIGNCMD_LIST && *arg == NUL)
1664 {
1665 // ":sign list": list all defined signs
1666 for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next)
1667 sign_list_defined(sp);
1668 }
1669 else if (*arg == NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001670 emsg(_("E156: Missing sign name"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001671 else
1672 {
1673 char_u *name;
1674
1675 // Isolate the sign name. If it's a number skip leading zeroes,
1676 // so that "099" and "99" are the same sign. But keep "0".
1677 p = skiptowhite(arg);
1678 if (*p != NUL)
1679 *p++ = NUL;
1680 while (arg[0] == '0' && arg[1] != NUL)
1681 ++arg;
1682 name = vim_strsave(arg);
1683
1684 if (idx == SIGNCMD_DEFINE)
1685 sign_define_cmd(name, p);
1686 else if (idx == SIGNCMD_LIST)
1687 // ":sign list {name}"
1688 sign_list_by_name(name);
1689 else
1690 // ":sign undefine {name}"
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001691 sign_undefine_by_name(name, TRUE);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001692
1693 vim_free(name);
1694 return;
1695 }
1696 }
1697 else
1698 {
1699 int id = -1;
1700 linenr_T lnum = -1;
1701 char_u *sign_name = NULL;
1702 char_u *group = NULL;
1703 int prio = SIGN_DEF_PRIO;
1704
1705 // Parse command line arguments
1706 if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio,
1707 &buf, &lnum) == FAIL)
1708 return;
1709
1710 if (idx == SIGNCMD_PLACE)
1711 sign_place_cmd(buf, lnum, sign_name, id, group, prio);
1712 else if (idx == SIGNCMD_UNPLACE)
1713 sign_unplace_cmd(buf, lnum, sign_name, id, group);
1714 else if (idx == SIGNCMD_JUMP)
1715 sign_jump_cmd(buf, lnum, sign_name, id, group);
1716 }
1717}
1718
1719/*
1720 * Return information about a specified sign
1721 */
1722 static void
1723sign_getinfo(sign_T *sp, dict_T *retdict)
1724{
1725 char_u *p;
1726
1727 dict_add_string(retdict, "name", (char_u *)sp->sn_name);
1728 if (sp->sn_icon != NULL)
1729 dict_add_string(retdict, "icon", (char_u *)sp->sn_icon);
1730 if (sp->sn_text != NULL)
1731 dict_add_string(retdict, "text", (char_u *)sp->sn_text);
1732 if (sp->sn_line_hl > 0)
1733 {
1734 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1735 if (p == NULL)
1736 p = (char_u *)"NONE";
1737 dict_add_string(retdict, "linehl", (char_u *)p);
1738 }
1739 if (sp->sn_text_hl > 0)
1740 {
1741 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1742 if (p == NULL)
1743 p = (char_u *)"NONE";
1744 dict_add_string(retdict, "texthl", (char_u *)p);
1745 }
Bram Moolenaare413ea02021-11-24 16:20:13 +00001746 if (sp->sn_cul_hl > 0)
1747 {
1748 p = get_highlight_name_ext(NULL, sp->sn_cul_hl - 1, FALSE);
1749 if (p == NULL)
1750 p = (char_u *)"NONE";
1751 dict_add_string(retdict, "culhl", (char_u *)p);
1752 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001753}
1754
1755/*
1756 * If 'name' is NULL, return a list of all the defined signs.
1757 * Otherwise, return information about the specified sign.
1758 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001759 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001760sign_getlist(char_u *name, list_T *retlist)
1761{
1762 sign_T *sp = first_sign;
1763 dict_T *dict;
1764
1765 if (name != NULL)
1766 {
1767 sp = sign_find(name, NULL);
1768 if (sp == NULL)
1769 return;
1770 }
1771
1772 for (; sp != NULL && !got_int; sp = sp->sn_next)
1773 {
1774 if ((dict = dict_alloc_id(aid_sign_getlist)) == NULL)
1775 return;
1776 if (list_append_dict(retlist, dict) == FAIL)
1777 return;
1778 sign_getinfo(sp, dict);
1779
1780 if (name != NULL) // handle only the specified sign
1781 break;
1782 }
1783}
1784
1785/*
1786 * Returns information about signs placed in a buffer as list of dicts.
1787 */
1788 void
1789get_buffer_signs(buf_T *buf, list_T *l)
1790{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001791 sign_entry_T *sign;
1792 dict_T *d;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001793
1794 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1795 {
1796 if ((d = sign_get_info(sign)) != NULL)
1797 list_append_dict(l, d);
1798 }
1799}
1800
1801/*
1802 * Return information about all the signs placed in a buffer
1803 */
1804 static void
1805sign_get_placed_in_buf(
1806 buf_T *buf,
1807 linenr_T lnum,
1808 int sign_id,
1809 char_u *sign_group,
1810 list_T *retlist)
1811{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001812 dict_T *d;
1813 list_T *l;
1814 sign_entry_T *sign;
1815 dict_T *sdict;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001816
1817 if ((d = dict_alloc_id(aid_sign_getplaced_dict)) == NULL)
1818 return;
1819 list_append_dict(retlist, d);
1820
1821 dict_add_number(d, "bufnr", (long)buf->b_fnum);
1822
1823 if ((l = list_alloc_id(aid_sign_getplaced_list)) == NULL)
1824 return;
1825 dict_add_list(d, "signs", l);
1826
1827 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1828 {
1829 if (!sign_in_group(sign, sign_group))
1830 continue;
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001831 if ((lnum == 0 && sign_id == 0)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001832 || (sign_id == 0 && lnum == sign->se_lnum)
1833 || (lnum == 0 && sign_id == sign->se_id)
1834 || (lnum == sign->se_lnum && sign_id == sign->se_id))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001835 {
1836 if ((sdict = sign_get_info(sign)) != NULL)
1837 list_append_dict(l, sdict);
1838 }
1839 }
1840}
1841
1842/*
1843 * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the
1844 * sign placed at the line number. If 'lnum' is zero, return all the signs
1845 * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers.
1846 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001847 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001848sign_get_placed(
1849 buf_T *buf,
1850 linenr_T lnum,
1851 int sign_id,
1852 char_u *sign_group,
1853 list_T *retlist)
1854{
1855 if (buf != NULL)
1856 sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist);
1857 else
1858 {
1859 FOR_ALL_BUFFERS(buf)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001860 if (buf->b_signlist != NULL)
1861 sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001862 }
1863}
1864
1865# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1866/*
1867 * Allocate the icons. Called when the GUI has started. Allows defining
1868 * signs before it starts.
1869 */
1870 void
1871sign_gui_started(void)
1872{
1873 sign_T *sp;
1874
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001875 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001876 if (sp->sn_icon != NULL)
1877 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
1878}
1879# endif
1880
1881/*
1882 * List one sign.
1883 */
1884 static void
1885sign_list_defined(sign_T *sp)
1886{
1887 char_u *p;
1888
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001889 smsg("sign %s", sp->sn_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001890 if (sp->sn_icon != NULL)
1891 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001892 msg_puts(" icon=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001893 msg_outtrans(sp->sn_icon);
1894# ifdef FEAT_SIGN_ICONS
1895 if (sp->sn_image == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001896 msg_puts(_(" (NOT FOUND)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001897# else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001898 msg_puts(_(" (not supported)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001899# endif
1900 }
1901 if (sp->sn_text != NULL)
1902 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001903 msg_puts(" text=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001904 msg_outtrans(sp->sn_text);
1905 }
1906 if (sp->sn_line_hl > 0)
1907 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001908 msg_puts(" linehl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001909 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1910 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001911 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001912 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001913 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001914 }
1915 if (sp->sn_text_hl > 0)
1916 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001917 msg_puts(" texthl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001918 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1919 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001920 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001921 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001922 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001923 }
Bram Moolenaare413ea02021-11-24 16:20:13 +00001924 if (sp->sn_cul_hl > 0)
1925 {
1926 msg_puts(" culhl=");
1927 p = get_highlight_name_ext(NULL, sp->sn_cul_hl - 1, FALSE);
1928 if (p == NULL)
1929 msg_puts("NONE");
1930 else
1931 msg_puts((char *)p);
1932 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001933}
1934
1935/*
1936 * Undefine a sign and free its memory.
1937 */
1938 static void
1939sign_undefine(sign_T *sp, sign_T *sp_prev)
1940{
1941 vim_free(sp->sn_name);
1942 vim_free(sp->sn_icon);
1943# ifdef FEAT_SIGN_ICONS
1944 if (sp->sn_image != NULL)
1945 {
1946 out_flush();
1947 gui_mch_destroy_sign(sp->sn_image);
1948 }
1949# endif
1950 vim_free(sp->sn_text);
1951 if (sp_prev == NULL)
1952 first_sign = sp->sn_next;
1953 else
1954 sp_prev->sn_next = sp->sn_next;
1955 vim_free(sp);
1956}
1957
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001958# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1959 void *
1960sign_get_image(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001961 int typenr) // the attribute which may have a sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001962{
1963 sign_T *sp;
1964
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001965 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001966 if (sp->sn_typenr == typenr)
1967 return sp->sn_image;
1968 return NULL;
1969}
1970# endif
1971
1972/*
1973 * Undefine/free all signs.
1974 */
1975 void
1976free_signs(void)
1977{
1978 while (first_sign != NULL)
1979 sign_undefine(first_sign, NULL);
1980}
1981
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001982static enum
1983{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001984 EXP_SUBCMD, // expand :sign sub-commands
1985 EXP_DEFINE, // expand :sign define {name} args
1986 EXP_PLACE, // expand :sign place {id} args
Bram Moolenaar3678f652019-02-17 14:50:25 +01001987 EXP_LIST, // expand :sign place args
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001988 EXP_UNPLACE, // expand :sign unplace"
Bram Moolenaar3678f652019-02-17 14:50:25 +01001989 EXP_SIGN_NAMES, // expand with name of placed signs
1990 EXP_SIGN_GROUPS // expand with name of placed sign groups
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001991} expand_what;
1992
1993/*
Bram Moolenaar3678f652019-02-17 14:50:25 +01001994 * Return the n'th sign name (used for command line completion)
1995 */
1996 static char_u *
1997get_nth_sign_name(int idx)
1998{
1999 int current_idx;
2000 sign_T *sp;
2001
2002 // Complete with name of signs already defined
2003 current_idx = 0;
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002004 FOR_ALL_SIGNS(sp)
Bram Moolenaar3678f652019-02-17 14:50:25 +01002005 if (current_idx++ == idx)
2006 return sp->sn_name;
2007 return NULL;
2008}
2009
2010/*
2011 * Return the n'th sign group name (used for command line completion)
2012 */
2013 static char_u *
2014get_nth_sign_group_name(int idx)
2015{
2016 int current_idx;
2017 int todo;
2018 hashitem_T *hi;
2019 signgroup_T *group;
2020
2021 // Complete with name of sign groups already defined
2022 current_idx = 0;
2023 todo = (int)sg_table.ht_used;
2024 for (hi = sg_table.ht_array; todo > 0; ++hi)
2025 {
2026 if (!HASHITEM_EMPTY(hi))
2027 {
2028 --todo;
2029 if (current_idx++ == idx)
2030 {
2031 group = HI2SG(hi);
2032 return group->sg_name;
2033 }
2034 }
2035 }
2036 return NULL;
2037}
2038
2039/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002040 * Function given to ExpandGeneric() to obtain the sign command
2041 * expansion.
2042 */
2043 char_u *
2044get_sign_name(expand_T *xp UNUSED, int idx)
2045{
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002046 switch (expand_what)
2047 {
2048 case EXP_SUBCMD:
2049 return (char_u *)cmds[idx];
2050 case EXP_DEFINE:
2051 {
2052 char *define_arg[] =
2053 {
2054 "icon=", "linehl=", "text=", "texthl=", NULL
2055 };
2056 return (char_u *)define_arg[idx];
2057 }
2058 case EXP_PLACE:
2059 {
2060 char *place_arg[] =
2061 {
2062 "line=", "name=", "group=", "priority=", "file=",
2063 "buffer=", NULL
2064 };
2065 return (char_u *)place_arg[idx];
2066 }
Bram Moolenaar3678f652019-02-17 14:50:25 +01002067 case EXP_LIST:
2068 {
2069 char *list_arg[] =
2070 {
2071 "group=", "file=", "buffer=", NULL
2072 };
2073 return (char_u *)list_arg[idx];
2074 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002075 case EXP_UNPLACE:
2076 {
2077 char *unplace_arg[] = { "group=", "file=", "buffer=", NULL };
2078 return (char_u *)unplace_arg[idx];
2079 }
2080 case EXP_SIGN_NAMES:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002081 return get_nth_sign_name(idx);
2082 case EXP_SIGN_GROUPS:
2083 return get_nth_sign_group_name(idx);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002084 default:
2085 return NULL;
2086 }
2087}
2088
2089/*
2090 * Handle command line completion for :sign command.
2091 */
2092 void
2093set_context_in_sign_cmd(expand_T *xp, char_u *arg)
2094{
2095 char_u *p;
2096 char_u *end_subcmd;
2097 char_u *last;
2098 int cmd_idx;
2099 char_u *begin_subcmd_args;
2100
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002101 // Default: expand subcommands.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002102 xp->xp_context = EXPAND_SIGN;
2103 expand_what = EXP_SUBCMD;
2104 xp->xp_pattern = arg;
2105
2106 end_subcmd = skiptowhite(arg);
2107 if (*end_subcmd == NUL)
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002108 // expand subcmd name
2109 // :sign {subcmd}<CTRL-D>
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002110 return;
2111
2112 cmd_idx = sign_cmd_idx(arg, end_subcmd);
2113
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002114 // :sign {subcmd} {subcmd_args}
2115 // |
2116 // begin_subcmd_args
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002117 begin_subcmd_args = skipwhite(end_subcmd);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002118
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002119 // expand last argument of subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002120
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002121 // :sign define {name} {args}...
2122 // |
2123 // p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002124
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002125 // Loop until reaching last argument.
Bram Moolenaar3678f652019-02-17 14:50:25 +01002126 p = begin_subcmd_args;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002127 do
2128 {
2129 p = skipwhite(p);
2130 last = p;
2131 p = skiptowhite(p);
2132 } while (*p != NUL);
2133
2134 p = vim_strchr(last, '=');
2135
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002136 // :sign define {name} {args}... {last}=
2137 // | |
2138 // last p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002139 if (p == NULL)
2140 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002141 // Expand last argument name (before equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002142 xp->xp_pattern = last;
2143 switch (cmd_idx)
2144 {
2145 case SIGNCMD_DEFINE:
2146 expand_what = EXP_DEFINE;
2147 break;
2148 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002149 // List placed signs
2150 if (VIM_ISDIGIT(*begin_subcmd_args))
2151 // :sign place {id} {args}...
2152 expand_what = EXP_PLACE;
2153 else
2154 // :sign place {args}...
2155 expand_what = EXP_LIST;
2156 break;
2157 case SIGNCMD_LIST:
2158 case SIGNCMD_UNDEFINE:
2159 // :sign list <CTRL-D>
2160 // :sign undefine <CTRL-D>
2161 expand_what = EXP_SIGN_NAMES;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002162 break;
2163 case SIGNCMD_JUMP:
2164 case SIGNCMD_UNPLACE:
2165 expand_what = EXP_UNPLACE;
2166 break;
2167 default:
2168 xp->xp_context = EXPAND_NOTHING;
2169 }
2170 }
2171 else
2172 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002173 // Expand last argument value (after equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002174 xp->xp_pattern = p + 1;
2175 switch (cmd_idx)
2176 {
2177 case SIGNCMD_DEFINE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002178 if (STRNCMP(last, "texthl", 6) == 0
2179 || STRNCMP(last, "linehl", 6) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002180 xp->xp_context = EXPAND_HIGHLIGHT;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002181 else if (STRNCMP(last, "icon", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002182 xp->xp_context = EXPAND_FILES;
2183 else
2184 xp->xp_context = EXPAND_NOTHING;
2185 break;
2186 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002187 if (STRNCMP(last, "name", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002188 expand_what = EXP_SIGN_NAMES;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002189 else if (STRNCMP(last, "group", 5) == 0)
2190 expand_what = EXP_SIGN_GROUPS;
2191 else if (STRNCMP(last, "file", 4) == 0)
2192 xp->xp_context = EXPAND_BUFFERS;
2193 else
2194 xp->xp_context = EXPAND_NOTHING;
2195 break;
2196 case SIGNCMD_UNPLACE:
2197 case SIGNCMD_JUMP:
2198 if (STRNCMP(last, "group", 5) == 0)
2199 expand_what = EXP_SIGN_GROUPS;
2200 else if (STRNCMP(last, "file", 4) == 0)
2201 xp->xp_context = EXPAND_BUFFERS;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002202 else
2203 xp->xp_context = EXPAND_NOTHING;
2204 break;
2205 default:
2206 xp->xp_context = EXPAND_NOTHING;
2207 }
2208 }
2209}
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002210
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002211/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002212 * Define a sign using the attributes in 'dict'. Returns 0 on success and -1 on
2213 * failure.
2214 */
2215 static int
2216sign_define_from_dict(char_u *name_arg, dict_T *dict)
2217{
2218 char_u *name = NULL;
2219 char_u *icon = NULL;
2220 char_u *linehl = NULL;
2221 char_u *text = NULL;
2222 char_u *texthl = NULL;
Bram Moolenaare413ea02021-11-24 16:20:13 +00002223 char_u *culhl = NULL;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002224 int retval = -1;
2225
2226 if (name_arg == NULL)
2227 {
2228 if (dict == NULL)
2229 return -1;
2230 name = dict_get_string(dict, (char_u *)"name", TRUE);
2231 }
2232 else
2233 name = vim_strsave(name_arg);
2234 if (name == NULL || name[0] == NUL)
2235 goto cleanup;
2236 if (dict != NULL)
2237 {
2238 icon = dict_get_string(dict, (char_u *)"icon", TRUE);
2239 linehl = dict_get_string(dict, (char_u *)"linehl", TRUE);
2240 text = dict_get_string(dict, (char_u *)"text", TRUE);
2241 texthl = dict_get_string(dict, (char_u *)"texthl", TRUE);
Bram Moolenaare413ea02021-11-24 16:20:13 +00002242 culhl = dict_get_string(dict, (char_u *)"culhl", TRUE);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002243 }
2244
Bram Moolenaare413ea02021-11-24 16:20:13 +00002245 if (sign_define_by_name(name, icon, linehl, text, texthl, culhl) == OK)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002246 retval = 0;
2247
2248cleanup:
2249 vim_free(name);
2250 vim_free(icon);
2251 vim_free(linehl);
2252 vim_free(text);
2253 vim_free(texthl);
Bram Moolenaare413ea02021-11-24 16:20:13 +00002254 vim_free(culhl);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002255
2256 return retval;
2257}
2258
2259/*
2260 * Define multiple signs using attributes from list 'l' and store the return
2261 * values in 'retlist'.
2262 */
2263 static void
2264sign_define_multiple(list_T *l, list_T *retlist)
2265{
2266 listitem_T *li;
2267 int retval;
2268
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002269 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002270 {
2271 retval = -1;
2272 if (li->li_tv.v_type == VAR_DICT)
2273 retval = sign_define_from_dict(NULL, li->li_tv.vval.v_dict);
2274 else
2275 emsg(_(e_dictreq));
2276 list_append_number(retlist, retval);
2277 }
2278}
2279
2280/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002281 * "sign_define()" function
2282 */
2283 void
2284f_sign_define(typval_T *argvars, typval_T *rettv)
2285{
2286 char_u *name;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002287
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002288 if (in_vim9script()
2289 && (check_for_string_or_list_arg(argvars, 0) == FAIL
2290 || check_for_opt_dict_arg(argvars, 1) == FAIL))
2291 return;
2292
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002293 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2294 {
2295 // Define multiple signs
2296 if (rettv_list_alloc(rettv) != OK)
2297 return;
2298
2299 sign_define_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2300 return;
2301 }
2302
2303 // Define a single sign
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002304 rettv->vval.v_number = -1;
2305
2306 name = tv_get_string_chk(&argvars[0]);
2307 if (name == NULL)
2308 return;
2309
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002310 if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_DICT)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002311 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002312 emsg(_(e_dictreq));
2313 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002314 }
2315
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002316 rettv->vval.v_number = sign_define_from_dict(name,
2317 argvars[1].v_type == VAR_DICT ? argvars[1].vval.v_dict : NULL);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002318}
2319
2320/*
2321 * "sign_getdefined()" function
2322 */
2323 void
2324f_sign_getdefined(typval_T *argvars, typval_T *rettv)
2325{
2326 char_u *name = NULL;
2327
2328 if (rettv_list_alloc_id(rettv, aid_sign_getdefined) != OK)
2329 return;
2330
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002331 if (in_vim9script() && check_for_opt_string_arg(argvars, 0) == FAIL)
2332 return;
2333
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002334 if (argvars[0].v_type != VAR_UNKNOWN)
2335 name = tv_get_string(&argvars[0]);
2336
2337 sign_getlist(name, rettv->vval.v_list);
2338}
2339
2340/*
2341 * "sign_getplaced()" function
2342 */
2343 void
2344f_sign_getplaced(typval_T *argvars, typval_T *rettv)
2345{
2346 buf_T *buf = NULL;
2347 dict_T *dict;
2348 dictitem_T *di;
2349 linenr_T lnum = 0;
2350 int sign_id = 0;
2351 char_u *group = NULL;
2352 int notanum = FALSE;
2353
2354 if (rettv_list_alloc_id(rettv, aid_sign_getplaced) != OK)
2355 return;
2356
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002357 if (in_vim9script()
Yegappan Lakshmanancd917202021-07-21 19:09:09 +02002358 && (check_for_opt_buffer_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002359 || (argvars[0].v_type != VAR_UNKNOWN
2360 && check_for_opt_dict_arg(argvars, 1) == FAIL)))
2361 return;
2362
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002363 if (argvars[0].v_type != VAR_UNKNOWN)
2364 {
2365 // get signs placed in the specified buffer
2366 buf = get_buf_arg(&argvars[0]);
2367 if (buf == NULL)
2368 return;
2369
2370 if (argvars[1].v_type != VAR_UNKNOWN)
2371 {
2372 if (argvars[1].v_type != VAR_DICT ||
2373 ((dict = argvars[1].vval.v_dict) == NULL))
2374 {
2375 emsg(_(e_dictreq));
2376 return;
2377 }
2378 if ((di = dict_find(dict, (char_u *)"lnum", -1)) != NULL)
2379 {
2380 // get signs placed at this line
2381 (void)tv_get_number_chk(&di->di_tv, &notanum);
2382 if (notanum)
2383 return;
2384 lnum = tv_get_lnum(&di->di_tv);
2385 }
2386 if ((di = dict_find(dict, (char_u *)"id", -1)) != NULL)
2387 {
2388 // get sign placed with this identifier
2389 sign_id = (int)tv_get_number_chk(&di->di_tv, &notanum);
2390 if (notanum)
2391 return;
2392 }
2393 if ((di = dict_find(dict, (char_u *)"group", -1)) != NULL)
2394 {
2395 group = tv_get_string_chk(&di->di_tv);
2396 if (group == NULL)
2397 return;
2398 if (*group == '\0') // empty string means global group
2399 group = NULL;
2400 }
2401 }
2402 }
2403
2404 sign_get_placed(buf, lnum, sign_id, group, rettv->vval.v_list);
2405}
2406
2407/*
2408 * "sign_jump()" function
2409 */
2410 void
2411f_sign_jump(typval_T *argvars, typval_T *rettv)
2412{
2413 int sign_id;
2414 char_u *sign_group = NULL;
2415 buf_T *buf;
2416 int notanum = FALSE;
2417
2418 rettv->vval.v_number = -1;
2419
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002420 if (in_vim9script()
2421 && (check_for_number_arg(argvars, 0) == FAIL
2422 || check_for_string_arg(argvars, 1) == FAIL
2423 || check_for_buffer_arg(argvars, 2) == FAIL))
2424 return;
2425
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002426 // Sign identifier
2427 sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
2428 if (notanum)
2429 return;
2430 if (sign_id <= 0)
2431 {
2432 emsg(_(e_invarg));
2433 return;
2434 }
2435
2436 // Sign group
2437 sign_group = tv_get_string_chk(&argvars[1]);
2438 if (sign_group == NULL)
2439 return;
2440 if (sign_group[0] == '\0')
2441 sign_group = NULL; // global sign group
2442 else
2443 {
2444 sign_group = vim_strsave(sign_group);
2445 if (sign_group == NULL)
2446 return;
2447 }
2448
2449 // Buffer to place the sign
2450 buf = get_buf_arg(&argvars[2]);
2451 if (buf == NULL)
2452 goto cleanup;
2453
2454 rettv->vval.v_number = sign_jump(sign_id, sign_group, buf);
2455
2456cleanup:
2457 vim_free(sign_group);
2458}
2459
2460/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002461 * Place a new sign using the values specified in dict 'dict'. Returns the sign
2462 * identifier if successfully placed, otherwise returns 0.
2463 */
2464 static int
2465sign_place_from_dict(
2466 typval_T *id_tv,
2467 typval_T *group_tv,
2468 typval_T *name_tv,
2469 typval_T *buf_tv,
2470 dict_T *dict)
2471{
2472 int sign_id = 0;
2473 char_u *group = NULL;
2474 char_u *sign_name = NULL;
2475 buf_T *buf = NULL;
2476 dictitem_T *di;
2477 linenr_T lnum = 0;
2478 int prio = SIGN_DEF_PRIO;
2479 int notanum = FALSE;
2480 int ret_sign_id = -1;
2481
2482 // sign identifier
2483 if (id_tv == NULL)
2484 {
2485 di = dict_find(dict, (char_u *)"id", -1);
2486 if (di != NULL)
2487 id_tv = &di->di_tv;
2488 }
2489 if (id_tv == NULL)
2490 sign_id = 0;
2491 else
2492 {
2493 sign_id = tv_get_number_chk(id_tv, &notanum);
2494 if (notanum)
2495 return -1;
2496 if (sign_id < 0)
2497 {
2498 emsg(_(e_invarg));
2499 return -1;
2500 }
2501 }
2502
2503 // sign group
2504 if (group_tv == NULL)
2505 {
2506 di = dict_find(dict, (char_u *)"group", -1);
2507 if (di != NULL)
2508 group_tv = &di->di_tv;
2509 }
2510 if (group_tv == NULL)
2511 group = NULL; // global group
2512 else
2513 {
2514 group = tv_get_string_chk(group_tv);
2515 if (group == NULL)
2516 goto cleanup;
2517 if (group[0] == '\0') // global sign group
2518 group = NULL;
2519 else
2520 {
2521 group = vim_strsave(group);
2522 if (group == NULL)
2523 return -1;
2524 }
2525 }
2526
2527 // sign name
2528 if (name_tv == NULL)
2529 {
2530 di = dict_find(dict, (char_u *)"name", -1);
2531 if (di != NULL)
2532 name_tv = &di->di_tv;
2533 }
2534 if (name_tv == NULL)
2535 goto cleanup;
2536 sign_name = tv_get_string_chk(name_tv);
2537 if (sign_name == NULL)
2538 goto cleanup;
2539
2540 // buffer to place the sign
2541 if (buf_tv == NULL)
2542 {
2543 di = dict_find(dict, (char_u *)"buffer", -1);
2544 if (di != NULL)
2545 buf_tv = &di->di_tv;
2546 }
2547 if (buf_tv == NULL)
2548 goto cleanup;
2549 buf = get_buf_arg(buf_tv);
2550 if (buf == NULL)
2551 goto cleanup;
2552
2553 // line number of the sign
2554 di = dict_find(dict, (char_u *)"lnum", -1);
2555 if (di != NULL)
2556 {
Bram Moolenaar42aff462019-08-21 13:20:29 +02002557 lnum = tv_get_lnum(&di->di_tv);
2558 if (lnum <= 0)
2559 {
2560 emsg(_(e_invarg));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002561 goto cleanup;
Bram Moolenaar42aff462019-08-21 13:20:29 +02002562 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002563 }
2564
2565 // sign priority
2566 di = dict_find(dict, (char_u *)"priority", -1);
2567 if (di != NULL)
2568 {
2569 prio = (int)tv_get_number_chk(&di->di_tv, &notanum);
2570 if (notanum)
2571 goto cleanup;
2572 }
2573
2574 if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK)
2575 ret_sign_id = sign_id;
2576
2577cleanup:
2578 vim_free(group);
2579
2580 return ret_sign_id;
2581}
2582
2583/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002584 * "sign_place()" function
2585 */
2586 void
2587f_sign_place(typval_T *argvars, typval_T *rettv)
2588{
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002589 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002590
2591 rettv->vval.v_number = -1;
2592
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002593 if (in_vim9script()
2594 && (check_for_number_arg(argvars, 0) == FAIL
2595 || check_for_string_arg(argvars, 1) == FAIL
2596 || check_for_string_arg(argvars, 2) == FAIL
2597 || check_for_buffer_arg(argvars, 3) == FAIL
2598 || check_for_opt_dict_arg(argvars, 4) == FAIL))
2599 return;
2600
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002601 if (argvars[4].v_type != VAR_UNKNOWN
2602 && (argvars[4].v_type != VAR_DICT
2603 || ((dict = argvars[4].vval.v_dict) == NULL)))
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002604 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002605 emsg(_(e_dictreq));
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002606 return;
2607 }
2608
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002609 rettv->vval.v_number = sign_place_from_dict(&argvars[0], &argvars[1],
2610 &argvars[2], &argvars[3], dict);
2611}
2612
2613/*
2614 * "sign_placelist()" function. Place multiple signs.
2615 */
2616 void
2617f_sign_placelist(typval_T *argvars, typval_T *rettv)
2618{
2619 listitem_T *li;
2620 int sign_id;
2621
2622 if (rettv_list_alloc(rettv) != OK)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002623 return;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002624
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002625 if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
2626 return;
2627
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002628 if (argvars[0].v_type != VAR_LIST)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002629 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002630 emsg(_(e_listreq));
2631 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002632 }
2633
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002634 // Process the List of sign attributes
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002635 FOR_ALL_LIST_ITEMS(argvars[0].vval.v_list, li)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002636 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002637 sign_id = -1;
2638 if (li->li_tv.v_type == VAR_DICT)
2639 sign_id = sign_place_from_dict(NULL, NULL, NULL, NULL,
2640 li->li_tv.vval.v_dict);
2641 else
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002642 emsg(_(e_dictreq));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002643 list_append_number(rettv->vval.v_list, sign_id);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002644 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002645}
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002646
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002647/*
2648 * Undefine multiple signs
2649 */
2650 static void
2651sign_undefine_multiple(list_T *l, list_T *retlist)
2652{
2653 char_u *name;
2654 listitem_T *li;
2655 int retval;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002656
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002657 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002658 {
2659 retval = -1;
2660 name = tv_get_string_chk(&li->li_tv);
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002661 if (name != NULL && (sign_undefine_by_name(name, TRUE) == OK))
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002662 retval = 0;
2663 list_append_number(retlist, retval);
2664 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002665}
2666
2667/*
2668 * "sign_undefine()" function
2669 */
2670 void
2671f_sign_undefine(typval_T *argvars, typval_T *rettv)
2672{
2673 char_u *name;
2674
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002675 if (in_vim9script()
2676 && check_for_opt_string_or_list_arg(argvars, 0) == FAIL)
2677 return;
2678
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002679 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2680 {
2681 // Undefine multiple signs
2682 if (rettv_list_alloc(rettv) != OK)
2683 return;
2684
2685 sign_undefine_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2686 return;
2687 }
2688
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002689 rettv->vval.v_number = -1;
2690
2691 if (argvars[0].v_type == VAR_UNKNOWN)
2692 {
2693 // Free all the signs
2694 free_signs();
2695 rettv->vval.v_number = 0;
2696 }
2697 else
2698 {
2699 // Free only the specified sign
2700 name = tv_get_string_chk(&argvars[0]);
2701 if (name == NULL)
2702 return;
2703
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002704 if (sign_undefine_by_name(name, TRUE) == OK)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002705 rettv->vval.v_number = 0;
2706 }
2707}
2708
2709/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002710 * Unplace the sign with attributes specified in 'dict'. Returns 0 on success
2711 * and -1 on failure.
2712 */
2713 static int
2714sign_unplace_from_dict(typval_T *group_tv, dict_T *dict)
2715{
2716 dictitem_T *di;
2717 int sign_id = 0;
2718 buf_T *buf = NULL;
2719 char_u *group = NULL;
2720 int retval = -1;
2721
2722 // sign group
2723 if (group_tv != NULL)
2724 group = tv_get_string(group_tv);
2725 else
2726 group = dict_get_string(dict, (char_u *)"group", FALSE);
2727 if (group != NULL)
2728 {
2729 if (group[0] == '\0') // global sign group
2730 group = NULL;
2731 else
2732 {
2733 group = vim_strsave(group);
2734 if (group == NULL)
2735 return -1;
2736 }
2737 }
2738
2739 if (dict != NULL)
2740 {
2741 if ((di = dict_find(dict, (char_u *)"buffer", -1)) != NULL)
2742 {
2743 buf = get_buf_arg(&di->di_tv);
2744 if (buf == NULL)
2745 goto cleanup;
2746 }
2747 if (dict_find(dict, (char_u *)"id", -1) != NULL)
2748 {
2749 sign_id = dict_get_number(dict, (char_u *)"id");
2750 if (sign_id <= 0)
2751 {
2752 emsg(_(e_invarg));
2753 goto cleanup;
2754 }
2755 }
2756 }
2757
2758 if (buf == NULL)
2759 {
2760 // Delete the sign in all the buffers
2761 retval = 0;
2762 FOR_ALL_BUFFERS(buf)
2763 if (sign_unplace(sign_id, group, buf, 0) != OK)
2764 retval = -1;
2765 }
2766 else if (sign_unplace(sign_id, group, buf, 0) == OK)
2767 retval = 0;
2768
2769cleanup:
2770 vim_free(group);
2771
2772 return retval;
2773}
2774
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002775 sign_entry_T *
2776get_first_valid_sign(win_T *wp)
2777{
2778 sign_entry_T *sign = wp->w_buffer->b_signlist;
2779
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002780# ifdef FEAT_PROP_POPUP
Bram Moolenaar72570732019-11-30 14:21:53 +01002781 while (sign != NULL && !sign_group_for_window(sign, wp))
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002782 sign = sign->se_next;
2783# endif
2784 return sign;
2785}
2786
2787/*
2788 * Return TRUE when window "wp" has a column to draw signs in.
2789 */
2790 int
2791signcolumn_on(win_T *wp)
2792{
2793 // If 'signcolumn' is set to 'number', signs are displayed in the 'number'
2794 // column (if present). Otherwise signs are to be displayed in the sign
2795 // column.
2796 if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u')
2797 return get_first_valid_sign(wp) != NULL && !wp->w_p_nu && !wp->w_p_rnu;
2798
2799 if (*wp->w_p_scl == 'n')
2800 return FALSE;
2801 if (*wp->w_p_scl == 'y')
2802 return TRUE;
2803 return (get_first_valid_sign(wp) != NULL
2804# ifdef FEAT_NETBEANS_INTG
2805 || wp->w_buffer->b_has_sign_column
2806# endif
2807 );
2808}
2809
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002810/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002811 * "sign_unplace()" function
2812 */
2813 void
2814f_sign_unplace(typval_T *argvars, typval_T *rettv)
2815{
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002816 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002817
2818 rettv->vval.v_number = -1;
2819
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002820 if (in_vim9script()
2821 && (check_for_string_arg(argvars, 0) == FAIL
2822 || check_for_opt_dict_arg(argvars, 1) == FAIL))
2823 return;
2824
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002825 if (argvars[0].v_type != VAR_STRING)
2826 {
2827 emsg(_(e_invarg));
2828 return;
2829 }
2830
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002831 if (argvars[1].v_type != VAR_UNKNOWN)
2832 {
2833 if (argvars[1].v_type != VAR_DICT)
2834 {
2835 emsg(_(e_dictreq));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002836 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002837 }
2838 dict = argvars[1].vval.v_dict;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002839 }
2840
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002841 rettv->vval.v_number = sign_unplace_from_dict(&argvars[0], dict);
2842}
2843
2844/*
2845 * "sign_unplacelist()" function
2846 */
2847 void
2848f_sign_unplacelist(typval_T *argvars, typval_T *rettv)
2849{
2850 listitem_T *li;
2851 int retval;
2852
2853 if (rettv_list_alloc(rettv) != OK)
2854 return;
2855
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002856 if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
2857 return;
2858
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002859 if (argvars[0].v_type != VAR_LIST)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002860 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002861 emsg(_(e_listreq));
2862 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002863 }
2864
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002865 FOR_ALL_LIST_ITEMS(argvars[0].vval.v_list, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002866 {
2867 retval = -1;
2868 if (li->li_tv.v_type == VAR_DICT)
2869 retval = sign_unplace_from_dict(NULL, li->li_tv.vval.v_dict);
2870 else
2871 emsg(_(e_dictreq));
2872 list_append_number(rettv->vval.v_list, retval);
2873 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002874}
2875
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002876#endif // FEAT_SIGNS