blob: a4c89679b44feabbd42134e113f6cad891064cd2 [file] [log] [blame]
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * sign.c: functions for managing signs
12 */
13
14#include "vim.h"
15
16#if defined(FEAT_SIGNS) || defined(PROTO)
17
18/*
19 * Struct to hold the sign properties.
20 */
21typedef struct sign sign_T;
22
23struct sign
24{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +010025 sign_T *sn_next; // next sign in list
26 int sn_typenr; // type number of sign
27 char_u *sn_name; // name of sign
28 char_u *sn_icon; // name of pixmap
Bram Moolenaarbbea4702019-01-01 13:20:31 +010029# ifdef FEAT_SIGN_ICONS
Bram Moolenaar6b7b7192019-01-11 13:42:41 +010030 void *sn_image; // icon image
Bram Moolenaarbbea4702019-01-01 13:20:31 +010031# endif
Bram Moolenaar6b7b7192019-01-11 13:42:41 +010032 char_u *sn_text; // text used instead of pixmap
33 int sn_line_hl; // highlight ID for line
34 int sn_text_hl; // highlight ID for text
Bram Moolenaare413ea02021-11-24 16:20:13 +000035 int sn_cul_hl; // highlight ID for text on current line when 'cursorline' is set
Bram Moolenaarbbea4702019-01-01 13:20:31 +010036};
37
38static sign_T *first_sign = NULL;
39static int next_sign_typenr = 1;
40
41static void sign_list_defined(sign_T *sp);
42static void sign_undefine(sign_T *sp, sign_T *sp_prev);
43
44static char *cmds[] = {
45 "define",
46# define SIGNCMD_DEFINE 0
47 "undefine",
48# define SIGNCMD_UNDEFINE 1
49 "list",
50# define SIGNCMD_LIST 2
51 "place",
52# define SIGNCMD_PLACE 3
53 "unplace",
54# define SIGNCMD_UNPLACE 4
55 "jump",
56# define SIGNCMD_JUMP 5
57 NULL
58# define SIGNCMD_LAST 6
59};
60
Bram Moolenaaraeea7212020-04-02 18:50:46 +020061#define FOR_ALL_SIGNS(sp) \
62 for ((sp) = first_sign; (sp) != NULL; (sp) = (sp)->sn_next)
63
Bram Moolenaarbbea4702019-01-01 13:20:31 +010064static hashtab_T sg_table; // sign group (signgroup_T) hashtable
65static int next_sign_id = 1; // next sign id in the global group
66
67/*
68 * Initialize data needed for managing signs
69 */
70 void
71init_signs(void)
72{
73 hash_init(&sg_table); // sign group hash table
74}
75
76/*
77 * A new sign in group 'groupname' is added. If the group is not present,
78 * create it. Otherwise reference the group.
79 */
80 static signgroup_T *
81sign_group_ref(char_u *groupname)
82{
83 hash_T hash;
84 hashitem_T *hi;
85 signgroup_T *group;
86
87 hash = hash_hash(groupname);
88 hi = hash_lookup(&sg_table, groupname, hash);
89 if (HASHITEM_EMPTY(hi))
90 {
91 // new group
Bram Moolenaar47ed5532019-08-08 20:49:14 +020092 group = alloc(offsetof(signgroup_T, sg_name) + STRLEN(groupname) + 1);
Bram Moolenaarbbea4702019-01-01 13:20:31 +010093 if (group == NULL)
94 return NULL;
95 STRCPY(group->sg_name, groupname);
Bram Moolenaar6656c2e2019-10-24 15:00:04 +020096 group->sg_refcount = 1;
97 group->sg_next_sign_id = 1;
Bram Moolenaarbbea4702019-01-01 13:20:31 +010098 hash_add_item(&sg_table, hi, group->sg_name, hash);
99 }
100 else
101 {
102 // existing group
103 group = HI2SG(hi);
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200104 group->sg_refcount++;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100105 }
106
107 return group;
108}
109
110/*
111 * A sign in group 'groupname' is removed. If all the signs in this group are
112 * removed, then remove the group.
113 */
114 static void
115sign_group_unref(char_u *groupname)
116{
117 hashitem_T *hi;
118 signgroup_T *group;
119
120 hi = hash_find(&sg_table, groupname);
121 if (!HASHITEM_EMPTY(hi))
122 {
123 group = HI2SG(hi);
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200124 group->sg_refcount--;
125 if (group->sg_refcount == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100126 {
127 // All the signs in this group are removed
128 hash_remove(&sg_table, hi);
129 vim_free(group);
130 }
131 }
132}
133
134/*
135 * Returns TRUE if 'sign' is in 'group'.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200136 * A sign can either be in the global group (sign->se_group == NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100137 * or in a named group. If 'group' is '*', then the sign is part of the group.
138 */
139 static int
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200140sign_in_group(sign_entry_T *sign, char_u *group)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100141{
142 return ((group != NULL && STRCMP(group, "*") == 0)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200143 || (group == NULL && sign->se_group == NULL)
144 || (group != NULL && sign->se_group != NULL
Bram Moolenaar72570732019-11-30 14:21:53 +0100145 && STRCMP(group, sign->se_group->sg_name) == 0));
146}
147
148/*
149 * Return TRUE if "sign" is to be displayed in window "wp".
150 * If the group name starts with "PopUp" it only shows in a popup window.
151 */
152 static int
153sign_group_for_window(sign_entry_T *sign, win_T *wp)
154{
155 int for_popup = sign->se_group != NULL
156 && STRNCMP("PopUp", sign->se_group->sg_name, 5) == 0;
157
158 return WIN_IS_POPUP(wp) ? for_popup : !for_popup;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100159}
160
161/*
162 * Get the next free sign identifier in the specified group
163 */
164 static int
165sign_group_get_next_signid(buf_T *buf, char_u *groupname)
166{
167 int id = 1;
168 signgroup_T *group = NULL;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200169 sign_entry_T *sign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100170 hashitem_T *hi;
171 int found = FALSE;
172
173 if (groupname != NULL)
174 {
175 hi = hash_find(&sg_table, groupname);
176 if (HASHITEM_EMPTY(hi))
177 return id;
178 group = HI2SG(hi);
179 }
180
Bram Moolenaarb5443cc2019-01-15 20:19:40 +0100181 // Search for the next usable sign identifier
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100182 while (!found)
183 {
184 if (group == NULL)
185 id = next_sign_id++; // global group
186 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200187 id = group->sg_next_sign_id++;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100188
189 // Check whether this sign is already placed in the buffer
190 found = TRUE;
191 FOR_ALL_SIGNS_IN_BUF(buf, sign)
192 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200193 if (id == sign->se_id && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100194 {
195 found = FALSE; // sign identifier is in use
196 break;
197 }
198 }
199 }
200
201 return id;
202}
203
204/*
205 * Insert a new sign into the signlist for buffer 'buf' between the 'prev' and
206 * 'next' signs.
207 */
208 static void
209insert_sign(
210 buf_T *buf, // buffer to store sign in
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200211 sign_entry_T *prev, // previous sign entry
212 sign_entry_T *next, // next sign entry
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100213 int id, // sign ID
214 char_u *group, // sign group; NULL for global group
215 int prio, // sign priority
216 linenr_T lnum, // line number which gets the mark
217 int typenr) // typenr of sign we are adding
218{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200219 sign_entry_T *newsign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100220
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200221 newsign = lalloc_id(sizeof(sign_entry_T), FALSE, aid_insert_sign);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100222 if (newsign != NULL)
223 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200224 newsign->se_id = id;
225 newsign->se_lnum = lnum;
226 newsign->se_typenr = typenr;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100227 if (group != NULL)
228 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200229 newsign->se_group = sign_group_ref(group);
230 if (newsign->se_group == NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100231 {
232 vim_free(newsign);
233 return;
234 }
235 }
236 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200237 newsign->se_group = NULL;
238 newsign->se_priority = prio;
239 newsign->se_next = next;
240 newsign->se_prev = prev;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100241 if (next != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200242 next->se_prev = newsign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100243
244 if (prev == NULL)
245 {
246 // When adding first sign need to redraw the windows to create the
247 // column for signs.
248 if (buf->b_signlist == NULL)
249 {
250 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200251 changed_line_abv_curs();
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100252 }
253
254 // first sign in signlist
255 buf->b_signlist = newsign;
256#ifdef FEAT_NETBEANS_INTG
257 if (netbeans_active())
258 buf->b_has_sign_column = TRUE;
259#endif
260 }
261 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200262 prev->se_next = newsign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100263 }
264}
265
266/*
267 * Insert a new sign sorted by line number and sign priority.
268 */
269 static void
270insert_sign_by_lnum_prio(
271 buf_T *buf, // buffer to store sign in
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200272 sign_entry_T *prev, // previous sign entry
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100273 int id, // sign ID
274 char_u *group, // sign group; NULL for global group
275 int prio, // sign priority
276 linenr_T lnum, // line number which gets the mark
277 int typenr) // typenr of sign we are adding
278{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200279 sign_entry_T *sign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100280
281 // keep signs sorted by lnum and by priority: insert new sign at
282 // the proper position in the list for this lnum.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200283 while (prev != NULL && prev->se_lnum == lnum && prev->se_priority <= prio)
284 prev = prev->se_prev;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100285 if (prev == NULL)
286 sign = buf->b_signlist;
287 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200288 sign = prev->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100289
290 insert_sign(buf, prev, sign, id, group, prio, lnum, typenr);
291}
292
293/*
Bram Moolenaar4e038572019-07-04 18:28:35 +0200294 * Lookup a sign by typenr. Returns NULL if sign is not found.
295 */
296 static sign_T *
297find_sign_by_typenr(int typenr)
298{
299 sign_T *sp;
300
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200301 FOR_ALL_SIGNS(sp)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200302 if (sp->sn_typenr == typenr)
303 return sp;
304 return NULL;
305}
306
307/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100308 * Get the name of a sign by its typenr.
309 */
310 static char_u *
311sign_typenr2name(int typenr)
312{
313 sign_T *sp;
314
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200315 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100316 if (sp->sn_typenr == typenr)
317 return sp->sn_name;
318 return (char_u *)_("[Deleted]");
319}
320
321/*
322 * Return information about a sign in a Dict
323 */
324 static dict_T *
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200325sign_get_info(sign_entry_T *sign)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100326{
327 dict_T *d;
328
329 if ((d = dict_alloc_id(aid_sign_getinfo)) == NULL)
330 return NULL;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200331 dict_add_number(d, "id", sign->se_id);
332 dict_add_string(d, "group", (sign->se_group == NULL) ?
333 (char_u *)"" : sign->se_group->sg_name);
334 dict_add_number(d, "lnum", sign->se_lnum);
335 dict_add_string(d, "name", sign_typenr2name(sign->se_typenr));
336 dict_add_number(d, "priority", sign->se_priority);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100337
338 return d;
339}
340
341/*
Bram Moolenaar64416122019-06-07 21:37:13 +0200342 * Sort the signs placed on the same line as "sign" by priority. Invoked after
343 * changing the priority of an already placed sign. Assumes the signs in the
344 * buffer are sorted by line number and priority.
345 */
346 static void
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200347sign_sort_by_prio_on_line(buf_T *buf, sign_entry_T *sign)
Bram Moolenaar64416122019-06-07 21:37:13 +0200348{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200349 sign_entry_T *p = NULL;
Bram Moolenaar64416122019-06-07 21:37:13 +0200350
351 // If there is only one sign in the buffer or only one sign on the line or
352 // the sign is already sorted by priority, then return.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200353 if ((sign->se_prev == NULL
354 || sign->se_prev->se_lnum != sign->se_lnum
355 || sign->se_prev->se_priority > sign->se_priority)
356 && (sign->se_next == NULL
357 || sign->se_next->se_lnum != sign->se_lnum
358 || sign->se_next->se_priority < sign->se_priority))
Bram Moolenaar64416122019-06-07 21:37:13 +0200359 return;
360
361 // One or more signs on the same line as 'sign'
362 // Find a sign after which 'sign' should be inserted
363
364 // First search backward for a sign with higher priority on the same line
365 p = sign;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200366 while (p->se_prev != NULL && p->se_prev->se_lnum == sign->se_lnum
367 && p->se_prev->se_priority <= sign->se_priority)
368 p = p->se_prev;
Bram Moolenaar64416122019-06-07 21:37:13 +0200369
370 if (p == sign)
371 {
372 // Sign not found. Search forward for a sign with priority just before
373 // 'sign'.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200374 p = sign->se_next;
375 while (p->se_next != NULL && p->se_next->se_lnum == sign->se_lnum
376 && p->se_next->se_priority > sign->se_priority)
377 p = p->se_next;
Bram Moolenaar64416122019-06-07 21:37:13 +0200378 }
379
380 // Remove 'sign' from the list
381 if (buf->b_signlist == sign)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200382 buf->b_signlist = sign->se_next;
383 if (sign->se_prev != NULL)
384 sign->se_prev->se_next = sign->se_next;
385 if (sign->se_next != NULL)
386 sign->se_next->se_prev = sign->se_prev;
387 sign->se_prev = NULL;
388 sign->se_next = NULL;
Bram Moolenaar64416122019-06-07 21:37:13 +0200389
390 // Re-insert 'sign' at the right place
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200391 if (p->se_priority <= sign->se_priority)
Bram Moolenaar64416122019-06-07 21:37:13 +0200392 {
393 // 'sign' has a higher priority and should be inserted before 'p'
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200394 sign->se_prev = p->se_prev;
395 sign->se_next = p;
396 p->se_prev = sign;
397 if (sign->se_prev != NULL)
398 sign->se_prev->se_next = sign;
Bram Moolenaar64416122019-06-07 21:37:13 +0200399 if (buf->b_signlist == p)
400 buf->b_signlist = sign;
401 }
402 else
403 {
404 // 'sign' has a lower priority and should be inserted after 'p'
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200405 sign->se_prev = p;
406 sign->se_next = p->se_next;
407 p->se_next = sign;
408 if (sign->se_next != NULL)
409 sign->se_next->se_prev = sign;
Bram Moolenaar64416122019-06-07 21:37:13 +0200410 }
411}
412
413/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100414 * Add the sign into the signlist. Find the right spot to do it though.
415 */
416 static void
417buf_addsign(
418 buf_T *buf, // buffer to store sign in
419 int id, // sign ID
420 char_u *groupname, // sign group
421 int prio, // sign priority
422 linenr_T lnum, // line number which gets the mark
423 int typenr) // typenr of sign we are adding
424{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200425 sign_entry_T *sign; // a sign in the signlist
426 sign_entry_T *prev; // the previous sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100427
428 prev = NULL;
429 FOR_ALL_SIGNS_IN_BUF(buf, sign)
430 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200431 if (lnum == sign->se_lnum && id == sign->se_id
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100432 && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100433 {
434 // Update an existing sign
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200435 sign->se_typenr = typenr;
436 sign->se_priority = prio;
Bram Moolenaar64416122019-06-07 21:37:13 +0200437 sign_sort_by_prio_on_line(buf, sign);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100438 return;
439 }
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200440 else if (lnum < sign->se_lnum)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100441 {
442 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio,
443 lnum, typenr);
444 return;
445 }
446 prev = sign;
447 }
448
449 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr);
450 return;
451}
452
453/*
454 * For an existing, placed sign "markId" change the type to "typenr".
455 * Returns the line number of the sign, or zero if the sign is not found.
456 */
457 static linenr_T
458buf_change_sign_type(
459 buf_T *buf, // buffer to store sign in
460 int markId, // sign ID
461 char_u *group, // sign group
Bram Moolenaar809ce4d2019-07-13 21:21:40 +0200462 int typenr, // typenr of sign we are adding
463 int prio) // sign priority
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100464{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200465 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100466
467 FOR_ALL_SIGNS_IN_BUF(buf, sign)
468 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200469 if (sign->se_id == markId && sign_in_group(sign, group))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100470 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200471 sign->se_typenr = typenr;
472 sign->se_priority = prio;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +0200473 sign_sort_by_prio_on_line(buf, sign);
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200474 return sign->se_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100475 }
476 }
477
478 return (linenr_T)0;
479}
480
481/*
Bram Moolenaar4e038572019-07-04 18:28:35 +0200482 * Return the attributes of the first sign placed on line 'lnum' in buffer
483 * 'buf'. Used when refreshing the screen. Returns TRUE if a sign is found on
484 * 'lnum', FALSE otherwise.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100485 */
486 int
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100487buf_get_signattrs(win_T *wp, linenr_T lnum, sign_attrs_T *sattr)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100488{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200489 sign_entry_T *sign;
490 sign_T *sp;
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100491 buf_T *buf = wp->w_buffer;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200492
Bram Moolenaara80faa82020-04-12 19:37:17 +0200493 CLEAR_POINTER(sattr);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100494
495 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200496 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200497 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200498 // Signs are sorted by line number in the buffer. No need to check
499 // for signs after the specified line number 'lnum'.
500 break;
501
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100502 if (sign->se_lnum == lnum
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100503# ifdef FEAT_PROP_POPUP
Bram Moolenaar72570732019-11-30 14:21:53 +0100504 && sign_group_for_window(sign, wp)
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100505# endif
506 )
Bram Moolenaar4e038572019-07-04 18:28:35 +0200507 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200508 sattr->sat_typenr = sign->se_typenr;
509 sp = find_sign_by_typenr(sign->se_typenr);
Bram Moolenaar4e038572019-07-04 18:28:35 +0200510 if (sp == NULL)
511 return FALSE;
512
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100513# ifdef FEAT_SIGN_ICONS
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200514 sattr->sat_icon = sp->sn_image;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100515# endif
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200516 sattr->sat_text = sp->sn_text;
517 if (sattr->sat_text != NULL && sp->sn_text_hl > 0)
518 sattr->sat_texthl = syn_id2attr(sp->sn_text_hl);
Bram Moolenaar4e038572019-07-04 18:28:35 +0200519 if (sp->sn_line_hl > 0)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200520 sattr->sat_linehl = syn_id2attr(sp->sn_line_hl);
Bram Moolenaare413ea02021-11-24 16:20:13 +0000521 if (sp->sn_cul_hl > 0)
522 sattr->sat_culhl = syn_id2attr(sp->sn_cul_hl);
Bram Moolenaar2f122842020-08-31 23:18:00 +0200523 sattr->sat_priority = sign->se_priority;
Bram Moolenaara2f6e422020-02-19 17:13:04 +0100524
525 // If there is another sign next with the same priority, may
526 // combine the text and the line highlighting.
527 if (sign->se_next != NULL
528 && sign->se_next->se_priority == sign->se_priority
529 && sign->se_next->se_lnum == sign->se_lnum)
530 {
531 sign_T *next_sp = find_sign_by_typenr(sign->se_next->se_typenr);
532
533 if (next_sp != NULL)
534 {
535 if (sattr->sat_icon == NULL && sattr->sat_text == NULL)
536 {
537# ifdef FEAT_SIGN_ICONS
538 sattr->sat_icon = next_sp->sn_image;
539# endif
540 sattr->sat_text = next_sp->sn_text;
541 }
542 if (sp->sn_text_hl <= 0 && next_sp->sn_text_hl > 0)
543 sattr->sat_texthl = syn_id2attr(next_sp->sn_text_hl);
544 if (sp->sn_line_hl <= 0 && next_sp->sn_line_hl > 0)
545 sattr->sat_linehl = syn_id2attr(next_sp->sn_line_hl);
Bram Moolenaare413ea02021-11-24 16:20:13 +0000546 if (sp->sn_cul_hl <= 0 && next_sp->sn_cul_hl > 0)
547 sattr->sat_culhl = syn_id2attr(next_sp->sn_cul_hl);
Bram Moolenaara2f6e422020-02-19 17:13:04 +0100548 }
549 }
Bram Moolenaar4e038572019-07-04 18:28:35 +0200550 return TRUE;
551 }
552 }
553 return FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100554}
555
556/*
557 * Delete sign 'id' in group 'group' from buffer 'buf'.
558 * If 'id' is zero, then delete all the signs in group 'group'. Otherwise
559 * delete only the specified sign.
560 * If 'group' is '*', then delete the sign in all the groups. If 'group' is
561 * NULL, then delete the sign in the global group. Otherwise delete the sign in
562 * the specified group.
563 * Returns the line number of the deleted sign. If multiple signs are deleted,
564 * then returns the line number of the last sign deleted.
565 */
566 linenr_T
567buf_delsign(
568 buf_T *buf, // buffer sign is stored in
569 linenr_T atlnum, // sign at this line, 0 - at any line
570 int id, // sign id
571 char_u *group) // sign group
572{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200573 sign_entry_T **lastp; // pointer to pointer to current sign
574 sign_entry_T *sign; // a sign in a b_signlist
575 sign_entry_T *next; // the next sign in a b_signlist
576 linenr_T lnum; // line number whose sign was deleted
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100577
578 lastp = &buf->b_signlist;
579 lnum = 0;
580 for (sign = buf->b_signlist; sign != NULL; sign = next)
581 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200582 next = sign->se_next;
583 if ((id == 0 || sign->se_id == id)
584 && (atlnum == 0 || sign->se_lnum == atlnum)
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100585 && sign_in_group(sign, group))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100586
587 {
588 *lastp = next;
589 if (next != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200590 next->se_prev = sign->se_prev;
591 lnum = sign->se_lnum;
592 if (sign->se_group != NULL)
593 sign_group_unref(sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100594 vim_free(sign);
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100595 redraw_buf_line_later(buf, lnum);
596
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100597 // Check whether only one sign needs to be deleted
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100598 // If deleting a sign with a specific identifier in a particular
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100599 // group or deleting any sign at a particular line number, delete
600 // only one sign.
601 if (group == NULL
602 || (*group != '*' && id != 0)
603 || (*group == '*' && atlnum != 0))
604 break;
605 }
606 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200607 lastp = &sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100608 }
609
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100610 // When deleting the last sign the cursor position may change, because the
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100611 // sign columns no longer shows. And the 'signcolumn' may be hidden.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100612 if (buf->b_signlist == NULL)
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100613 {
614 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200615 changed_line_abv_curs();
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100616 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100617
618 return lnum;
619}
620
621
622/*
623 * Find the line number of the sign with the requested id in group 'group'. If
624 * the sign does not exist, return 0 as the line number. This will still let
625 * the correct file get loaded.
626 */
627 int
628buf_findsign(
629 buf_T *buf, // buffer to store sign in
630 int id, // sign ID
631 char_u *group) // sign group
632{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200633 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100634
635 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200636 if (sign->se_id == id && sign_in_group(sign, group))
637 return sign->se_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100638
639 return 0;
640}
641
642/*
643 * Return the sign at line 'lnum' in buffer 'buf'. Returns NULL if a sign is
644 * not found at the line. If 'groupname' is NULL, searches in the global group.
645 */
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200646 static sign_entry_T *
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100647buf_getsign_at_line(
648 buf_T *buf, // buffer whose sign we are searching for
649 linenr_T lnum, // line number of sign
650 char_u *groupname) // sign group name
651{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200652 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100653
654 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200655 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200656 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200657 // Signs are sorted by line number in the buffer. No need to check
658 // for signs after the specified line number 'lnum'.
659 break;
660
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200661 if (sign->se_lnum == lnum && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100662 return sign;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200663 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100664
665 return NULL;
666}
667
668/*
669 * Return the identifier of the sign at line number 'lnum' in buffer 'buf'.
670 */
671 int
672buf_findsign_id(
673 buf_T *buf, // buffer whose sign we are searching for
674 linenr_T lnum, // line number of sign
675 char_u *groupname) // sign group name
676{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200677 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100678
679 sign = buf_getsign_at_line(buf, lnum, groupname);
680 if (sign != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200681 return sign->se_id;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100682
683 return 0;
684}
685
686# if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
687/*
688 * See if a given type of sign exists on a specific line.
689 */
690 int
691buf_findsigntype_id(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100692 buf_T *buf, // buffer whose sign we are searching for
693 linenr_T lnum, // line number of sign
694 int typenr) // sign type number
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100695{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200696 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100697
698 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200699 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200700 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200701 // Signs are sorted by line number in the buffer. No need to check
702 // for signs after the specified line number 'lnum'.
703 break;
704
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200705 if (sign->se_lnum == lnum && sign->se_typenr == typenr)
706 return sign->se_id;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200707 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100708
709 return 0;
710}
711
712
713# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
714/*
715 * Return the number of icons on the given line.
716 */
717 int
718buf_signcount(buf_T *buf, linenr_T lnum)
719{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200720 sign_entry_T *sign; // a sign in the signlist
721 int count = 0;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100722
723 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200724 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200725 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200726 // Signs are sorted by line number in the buffer. No need to check
727 // for signs after the specified line number 'lnum'.
728 break;
729
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200730 if (sign->se_lnum == lnum)
731 if (sign_get_image(sign->se_typenr) != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100732 count++;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200733 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100734
735 return count;
736}
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100737# endif // FEAT_SIGN_ICONS
738# endif // FEAT_NETBEANS_INTG
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100739
740/*
741 * Delete signs in group 'group' in buffer "buf". If 'group' is '*', then
742 * delete all the signs.
743 */
744 void
745buf_delete_signs(buf_T *buf, char_u *group)
746{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200747 sign_entry_T *sign;
748 sign_entry_T **lastp; // pointer to pointer to current sign
749 sign_entry_T *next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100750
751 // When deleting the last sign need to redraw the windows to remove the
752 // sign column. Not when curwin is NULL (this means we're exiting).
753 if (buf->b_signlist != NULL && curwin != NULL)
754 {
755 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200756 changed_line_abv_curs();
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100757 }
758
759 lastp = &buf->b_signlist;
760 for (sign = buf->b_signlist; sign != NULL; sign = next)
761 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200762 next = sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100763 if (sign_in_group(sign, group))
764 {
765 *lastp = next;
766 if (next != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200767 next->se_prev = sign->se_prev;
768 if (sign->se_group != NULL)
769 sign_group_unref(sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100770 vim_free(sign);
771 }
772 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200773 lastp = &sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100774 }
775}
776
777/*
778 * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers.
779 */
780 static void
781sign_list_placed(buf_T *rbuf, char_u *sign_group)
782{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200783 buf_T *buf;
784 sign_entry_T *sign;
785 char lbuf[MSG_BUF_LEN];
786 char group[MSG_BUF_LEN];
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100787
Bram Moolenaar32526b32019-01-19 17:43:09 +0100788 msg_puts_title(_("\n--- Signs ---"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100789 msg_putchar('\n');
790 if (rbuf == NULL)
791 buf = firstbuf;
792 else
793 buf = rbuf;
794 while (buf != NULL && !got_int)
795 {
796 if (buf->b_signlist != NULL)
797 {
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100798 vim_snprintf(lbuf, MSG_BUF_LEN, _("Signs for %s:"), buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100799 msg_puts_attr(lbuf, HL_ATTR(HLF_D));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100800 msg_putchar('\n');
801 }
802 FOR_ALL_SIGNS_IN_BUF(buf, sign)
803 {
804 if (got_int)
805 break;
806 if (!sign_in_group(sign, sign_group))
807 continue;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200808 if (sign->se_group != NULL)
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100809 vim_snprintf(group, MSG_BUF_LEN, _(" group=%s"),
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200810 sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100811 else
812 group[0] = '\0';
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100813 vim_snprintf(lbuf, MSG_BUF_LEN,
814 _(" line=%ld id=%d%s name=%s priority=%d"),
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200815 (long)sign->se_lnum, sign->se_id, group,
816 sign_typenr2name(sign->se_typenr), sign->se_priority);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100817 msg_puts(lbuf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100818 msg_putchar('\n');
819 }
820 if (rbuf != NULL)
821 break;
822 buf = buf->b_next;
823 }
824}
825
826/*
827 * Adjust a placed sign for inserted/deleted lines.
828 */
829 void
830sign_mark_adjust(
831 linenr_T line1,
832 linenr_T line2,
833 long amount,
834 long amount_after)
835{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200836 sign_entry_T *sign; // a sign in a b_signlist
837 linenr_T new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100838
839 FOR_ALL_SIGNS_IN_BUF(curbuf, sign)
840 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100841 // Ignore changes to lines after the sign
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200842 if (sign->se_lnum < line1)
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100843 continue;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200844 new_lnum = sign->se_lnum;
845 if (sign->se_lnum >= line1 && sign->se_lnum <= line2)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100846 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100847 if (amount != MAXLNUM)
848 new_lnum += amount;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100849 }
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200850 else if (sign->se_lnum > line2)
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100851 // Lines inserted or deleted before the sign
852 new_lnum += amount_after;
853
854 // If the new sign line number is past the last line in the buffer,
855 // then don't adjust the line number. Otherwise, it will always be past
856 // the last line and will not be visible.
857 if (new_lnum <= curbuf->b_ml.ml_line_count)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200858 sign->se_lnum = new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100859 }
860}
861
862/*
863 * Find index of a ":sign" subcmd from its name.
864 * "*end_cmd" must be writable.
865 */
866 static int
867sign_cmd_idx(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100868 char_u *begin_cmd, // begin of sign subcmd
869 char_u *end_cmd) // just after sign subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100870{
871 int idx;
872 char save = *end_cmd;
873
874 *end_cmd = NUL;
875 for (idx = 0; ; ++idx)
876 if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0)
877 break;
878 *end_cmd = save;
879 return idx;
880}
881
882/*
883 * Find a sign by name. Also returns pointer to the previous sign.
884 */
885 static sign_T *
886sign_find(char_u *name, sign_T **sp_prev)
887{
888 sign_T *sp;
889
890 if (sp_prev != NULL)
891 *sp_prev = NULL;
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200892 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100893 {
894 if (STRCMP(sp->sn_name, name) == 0)
895 break;
896 if (sp_prev != NULL)
897 *sp_prev = sp;
898 }
899
900 return sp;
901}
902
903/*
Bram Moolenaar03142362019-01-18 22:01:42 +0100904 * Allocate a new sign
905 */
906 static sign_T *
907alloc_new_sign(char_u *name)
908{
909 sign_T *sp;
910 sign_T *lp;
911 int start = next_sign_typenr;
912
913 // Allocate a new sign.
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200914 sp = alloc_clear_id(sizeof(sign_T), aid_sign_define_by_name);
Bram Moolenaar03142362019-01-18 22:01:42 +0100915 if (sp == NULL)
916 return NULL;
917
918 // Check that next_sign_typenr is not already being used.
919 // This only happens after wrapping around. Hopefully
920 // another one got deleted and we can use its number.
921 for (lp = first_sign; lp != NULL; )
922 {
923 if (lp->sn_typenr == next_sign_typenr)
924 {
925 ++next_sign_typenr;
926 if (next_sign_typenr == MAX_TYPENR)
927 next_sign_typenr = 1;
928 if (next_sign_typenr == start)
929 {
930 vim_free(sp);
931 emsg(_("E612: Too many signs defined"));
932 return NULL;
933 }
934 lp = first_sign; // start all over
935 continue;
936 }
937 lp = lp->sn_next;
938 }
939
940 sp->sn_typenr = next_sign_typenr;
941 if (++next_sign_typenr == MAX_TYPENR)
942 next_sign_typenr = 1; // wrap around
943
944 sp->sn_name = vim_strsave(name);
945 if (sp->sn_name == NULL) // out of memory
946 {
947 vim_free(sp);
948 return NULL;
949 }
950
951 return sp;
952}
953
954/*
955 * Initialize the icon information for a new sign
956 */
957 static void
958sign_define_init_icon(sign_T *sp, char_u *icon)
959{
960 vim_free(sp->sn_icon);
961 sp->sn_icon = vim_strsave(icon);
962 backslash_halve(sp->sn_icon);
963# ifdef FEAT_SIGN_ICONS
964 if (gui.in_use)
965 {
966 out_flush();
967 if (sp->sn_image != NULL)
968 gui_mch_destroy_sign(sp->sn_image);
969 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
970 }
971# endif
972}
973
974/*
975 * Initialize the text for a new sign
976 */
977 static int
978sign_define_init_text(sign_T *sp, char_u *text)
979{
980 char_u *s;
981 char_u *endp;
982 int cells;
983 int len;
984
985 endp = text + (int)STRLEN(text);
986
987 // Remove backslashes so that it is possible to use a space.
988 for (s = text; s + 1 < endp; ++s)
989 if (*s == '\\')
990 {
991 STRMOVE(s, s + 1);
992 --endp;
993 }
994
995 // Count cells and check for non-printable chars
Bram Moolenaar03142362019-01-18 22:01:42 +0100996 if (has_mbyte)
997 {
998 cells = 0;
999 for (s = text; s < endp; s += (*mb_ptr2len)(s))
1000 {
1001 if (!vim_isprintc((*mb_ptr2char)(s)))
1002 break;
1003 cells += (*mb_ptr2cells)(s);
1004 }
1005 }
1006 else
Bram Moolenaar03142362019-01-18 22:01:42 +01001007 {
1008 for (s = text; s < endp; ++s)
1009 if (!vim_isprintc(*s))
1010 break;
1011 cells = (int)(s - text);
1012 }
1013
1014 // Currently sign text must be one or two display cells
1015 if (s != endp || cells < 1 || cells > 2)
1016 {
1017 semsg(_("E239: Invalid sign text: %s"), text);
1018 return FAIL;
1019 }
1020
1021 vim_free(sp->sn_text);
1022 // Allocate one byte more if we need to pad up
1023 // with a space.
1024 len = (int)(endp - text + ((cells == 1) ? 1 : 0));
1025 sp->sn_text = vim_strnsave(text, len);
1026
1027 // For single character sign text, pad with a space.
1028 if (sp->sn_text != NULL && cells == 1)
1029 STRCPY(sp->sn_text + len - 1, " ");
1030
1031 return OK;
1032}
1033
1034/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001035 * Define a new sign or update an existing sign
1036 */
1037 int
1038sign_define_by_name(
1039 char_u *name,
1040 char_u *icon,
1041 char_u *linehl,
1042 char_u *text,
Bram Moolenaare413ea02021-11-24 16:20:13 +00001043 char_u *texthl,
1044 char_u *culhl)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001045{
1046 sign_T *sp_prev;
1047 sign_T *sp;
1048
1049 sp = sign_find(name, &sp_prev);
1050 if (sp == NULL)
1051 {
Bram Moolenaar03142362019-01-18 22:01:42 +01001052 sp = alloc_new_sign(name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001053 if (sp == NULL)
1054 return FAIL;
1055
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001056 // add the new sign to the list of signs
1057 if (sp_prev == NULL)
1058 first_sign = sp;
1059 else
1060 sp_prev->sn_next = sp;
1061 }
Bram Moolenaarbf0acff2020-01-09 21:01:59 +01001062 else
1063 {
1064 win_T *wp;
1065
1066 // Signs may already exist, a redraw is needed in windows with a
1067 // non-empty sign list.
1068 FOR_ALL_WINDOWS(wp)
1069 if (wp->w_buffer->b_signlist != NULL)
1070 redraw_buf_later(wp->w_buffer, NOT_VALID);
1071 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001072
1073 // set values for a defined sign.
1074 if (icon != NULL)
Bram Moolenaar03142362019-01-18 22:01:42 +01001075 sign_define_init_icon(sp, icon);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001076
Bram Moolenaar03142362019-01-18 22:01:42 +01001077 if (text != NULL && (sign_define_init_text(sp, text) == FAIL))
1078 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001079
1080 if (linehl != NULL)
1081 sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl));
1082
1083 if (texthl != NULL)
1084 sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl));
1085
Bram Moolenaare413ea02021-11-24 16:20:13 +00001086 if (culhl != NULL)
1087 sp->sn_cul_hl = syn_check_group(culhl, (int)STRLEN(culhl));
1088
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001089 return OK;
1090}
1091
1092/*
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001093 * Return TRUE if sign "name" exists.
1094 */
1095 int
1096sign_exists_by_name(char_u *name)
1097{
1098 return sign_find(name, NULL) != NULL;
1099}
1100
1101/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001102 * Free the sign specified by 'name'.
1103 */
1104 int
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001105sign_undefine_by_name(char_u *name, int give_error)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001106{
1107 sign_T *sp_prev;
1108 sign_T *sp;
1109
1110 sp = sign_find(name, &sp_prev);
1111 if (sp == NULL)
1112 {
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001113 if (give_error)
1114 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001115 return FAIL;
1116 }
1117 sign_undefine(sp, sp_prev);
1118
1119 return OK;
1120}
1121
1122/*
1123 * List the signs matching 'name'
1124 */
1125 static void
1126sign_list_by_name(char_u *name)
1127{
1128 sign_T *sp;
1129
1130 sp = sign_find(name, NULL);
1131 if (sp != NULL)
1132 sign_list_defined(sp);
1133 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001134 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001135}
1136
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001137 static void
1138may_force_numberwidth_recompute(buf_T *buf, int unplace)
1139{
1140 tabpage_T *tp;
1141 win_T *wp;
1142
1143 FOR_ALL_TAB_WINDOWS(tp, wp)
1144 if (wp->w_buffer == buf
1145 && (wp->w_p_nu || wp->w_p_rnu)
1146 && (unplace || wp->w_nrwidth_width < 2)
1147 && (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u'))
1148 wp->w_nrwidth_line_count = 0;
1149}
1150
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001151/*
Bram Moolenaar8144acb2019-01-14 23:08:18 +01001152 * Place a sign at the specified file location or update a sign.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001153 */
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001154 int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001155sign_place(
1156 int *sign_id,
1157 char_u *sign_group,
1158 char_u *sign_name,
1159 buf_T *buf,
1160 linenr_T lnum,
1161 int prio)
1162{
1163 sign_T *sp;
1164
1165 // Check for reserved character '*' in group name
1166 if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0'))
1167 return FAIL;
1168
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001169 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001170 if (STRCMP(sp->sn_name, sign_name) == 0)
1171 break;
1172 if (sp == NULL)
1173 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001174 semsg(_("E155: Unknown sign: %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001175 return FAIL;
1176 }
1177 if (*sign_id == 0)
1178 *sign_id = sign_group_get_next_signid(buf, sign_group);
1179
1180 if (lnum > 0)
1181 // ":sign place {id} line={lnum} name={name} file={fname}":
1182 // place a sign
1183 buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr);
1184 else
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02001185 // ":sign place {id} file={fname}": change sign type and/or priority
1186 lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr,
1187 prio);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001188 if (lnum > 0)
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001189 {
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001190 redraw_buf_line_later(buf, lnum);
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001191
1192 // When displaying signs in the 'number' column, if the width of the
1193 // number column is less than 2, then force recomputing the width.
1194 may_force_numberwidth_recompute(buf, FALSE);
1195 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001196 else
1197 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001198 semsg(_("E885: Not possible to change sign %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001199 return FAIL;
1200 }
1201
1202 return OK;
1203}
1204
1205/*
1206 * Unplace the specified sign
1207 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001208 static int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001209sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum)
1210{
1211 if (buf->b_signlist == NULL) // No signs in the buffer
1212 return OK;
1213
1214 if (sign_id == 0)
1215 {
1216 // Delete all the signs in the specified buffer
1217 redraw_buf_later(buf, NOT_VALID);
1218 buf_delete_signs(buf, sign_group);
1219 }
1220 else
1221 {
1222 linenr_T lnum;
1223
1224 // Delete only the specified signs
1225 lnum = buf_delsign(buf, atlnum, sign_id, sign_group);
1226 if (lnum == 0)
1227 return FAIL;
1228 }
1229
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001230 // When all the signs in a buffer are removed, force recomputing the
1231 // number column width (if enabled) in all the windows displaying the
1232 // buffer if 'signcolumn' is set to 'number' in that window.
1233 if (buf->b_signlist == NULL)
1234 may_force_numberwidth_recompute(buf, TRUE);
1235
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001236 return OK;
1237}
1238
1239/*
1240 * Unplace the sign at the current cursor line.
1241 */
1242 static void
1243sign_unplace_at_cursor(char_u *groupname)
1244{
1245 int id = -1;
1246
1247 id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname);
1248 if (id > 0)
1249 sign_unplace(id, groupname, curwin->w_buffer, curwin->w_cursor.lnum);
1250 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001251 emsg(_("E159: Missing sign number"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001252}
1253
1254/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001255 * Jump to a sign.
1256 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001257 static linenr_T
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001258sign_jump(int sign_id, char_u *sign_group, buf_T *buf)
1259{
1260 linenr_T lnum;
1261
1262 if ((lnum = buf_findsign(buf, sign_id, sign_group)) <= 0)
1263 {
Bram Moolenaarb5443cc2019-01-15 20:19:40 +01001264 semsg(_("E157: Invalid sign ID: %d"), sign_id);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001265 return -1;
1266 }
1267
1268 // goto a sign ...
1269 if (buf_jump_open_win(buf) != NULL)
1270 { // ... in a current window
1271 curwin->w_cursor.lnum = lnum;
1272 check_cursor_lnum();
1273 beginline(BL_WHITE);
1274 }
1275 else
1276 { // ... not currently in a window
1277 char_u *cmd;
1278
1279 if (buf->b_fname == NULL)
1280 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001281 emsg(_("E934: Cannot jump to a buffer that does not have a name"));
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001282 return -1;
1283 }
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02001284 cmd = alloc(STRLEN(buf->b_fname) + 25);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001285 if (cmd == NULL)
1286 return -1;
1287 sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname);
1288 do_cmdline_cmd(cmd);
1289 vim_free(cmd);
1290 }
1291# ifdef FEAT_FOLDING
1292 foldOpenCursor();
1293# endif
1294
1295 return lnum;
1296}
1297
1298/*
1299 * ":sign define {name} ..." command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001300 */
1301 static void
1302sign_define_cmd(char_u *sign_name, char_u *cmdline)
1303{
1304 char_u *arg;
1305 char_u *p = cmdline;
1306 char_u *icon = NULL;
1307 char_u *text = NULL;
1308 char_u *linehl = NULL;
1309 char_u *texthl = NULL;
Bram Moolenaare413ea02021-11-24 16:20:13 +00001310 char_u *culhl = NULL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001311 int failed = FALSE;
1312
1313 // set values for a defined sign.
1314 for (;;)
1315 {
1316 arg = skipwhite(p);
1317 if (*arg == NUL)
1318 break;
1319 p = skiptowhite_esc(arg);
1320 if (STRNCMP(arg, "icon=", 5) == 0)
1321 {
1322 arg += 5;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001323 icon = vim_strnsave(arg, p - arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001324 }
1325 else if (STRNCMP(arg, "text=", 5) == 0)
1326 {
1327 arg += 5;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001328 text = vim_strnsave(arg, p - arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001329 }
1330 else if (STRNCMP(arg, "linehl=", 7) == 0)
1331 {
1332 arg += 7;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001333 linehl = vim_strnsave(arg, p - arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001334 }
1335 else if (STRNCMP(arg, "texthl=", 7) == 0)
1336 {
1337 arg += 7;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001338 texthl = vim_strnsave(arg, p - arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001339 }
Bram Moolenaare413ea02021-11-24 16:20:13 +00001340 else if (STRNCMP(arg, "culhl=", 6) == 0)
1341 {
1342 arg += 6;
1343 culhl = vim_strnsave(arg, p - arg);
1344 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001345 else
1346 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001347 semsg(_(e_invarg2), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001348 failed = TRUE;
1349 break;
1350 }
1351 }
1352
1353 if (!failed)
Bram Moolenaare413ea02021-11-24 16:20:13 +00001354 sign_define_by_name(sign_name, icon, linehl, text, texthl, culhl);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001355
1356 vim_free(icon);
1357 vim_free(text);
1358 vim_free(linehl);
1359 vim_free(texthl);
Bram Moolenaare413ea02021-11-24 16:20:13 +00001360 vim_free(culhl);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001361}
1362
1363/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001364 * ":sign place" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001365 */
1366 static void
1367sign_place_cmd(
1368 buf_T *buf,
1369 linenr_T lnum,
1370 char_u *sign_name,
1371 int id,
1372 char_u *group,
1373 int prio)
1374{
1375 if (id <= 0)
1376 {
1377 // List signs placed in a file/buffer
1378 // :sign place file={fname}
1379 // :sign place group={group} file={fname}
1380 // :sign place group=* file={fname}
1381 // :sign place buffer={nr}
1382 // :sign place group={group} buffer={nr}
1383 // :sign place group=* buffer={nr}
1384 // :sign place
1385 // :sign place group={group}
1386 // :sign place group=*
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001387 if (lnum >= 0 || sign_name != NULL
1388 || (group != NULL && *group == '\0'))
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001389 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001390 else
1391 sign_list_placed(buf, group);
1392 }
1393 else
1394 {
1395 // Place a new sign
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001396 if (sign_name == NULL || buf == NULL
1397 || (group != NULL && *group == '\0'))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001398 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001399 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001400 return;
1401 }
1402
1403 sign_place(&id, group, sign_name, buf, lnum, prio);
1404 }
1405}
1406
1407/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001408 * ":sign unplace" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001409 */
1410 static void
1411sign_unplace_cmd(
1412 buf_T *buf,
1413 linenr_T lnum,
1414 char_u *sign_name,
1415 int id,
1416 char_u *group)
1417{
1418 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0'))
1419 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001420 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001421 return;
1422 }
1423
1424 if (id == -2)
1425 {
1426 if (buf != NULL)
1427 // :sign unplace * file={fname}
1428 // :sign unplace * group={group} file={fname}
1429 // :sign unplace * group=* file={fname}
1430 // :sign unplace * buffer={nr}
1431 // :sign unplace * group={group} buffer={nr}
1432 // :sign unplace * group=* buffer={nr}
1433 sign_unplace(0, group, buf, 0);
1434 else
1435 // :sign unplace *
1436 // :sign unplace * group={group}
1437 // :sign unplace * group=*
1438 FOR_ALL_BUFFERS(buf)
1439 if (buf->b_signlist != NULL)
1440 buf_delete_signs(buf, group);
1441 }
1442 else
1443 {
1444 if (buf != NULL)
1445 // :sign unplace {id} file={fname}
1446 // :sign unplace {id} group={group} file={fname}
1447 // :sign unplace {id} group=* file={fname}
1448 // :sign unplace {id} buffer={nr}
1449 // :sign unplace {id} group={group} buffer={nr}
1450 // :sign unplace {id} group=* buffer={nr}
1451 sign_unplace(id, group, buf, 0);
1452 else
1453 {
1454 if (id == -1)
1455 {
1456 // :sign unplace group={group}
1457 // :sign unplace group=*
1458 sign_unplace_at_cursor(group);
1459 }
1460 else
1461 {
1462 // :sign unplace {id}
1463 // :sign unplace {id} group={group}
1464 // :sign unplace {id} group=*
1465 FOR_ALL_BUFFERS(buf)
1466 sign_unplace(id, group, buf, 0);
1467 }
1468 }
1469 }
1470}
1471
1472/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001473 * Jump to a placed sign commands:
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001474 * :sign jump {id} file={fname}
1475 * :sign jump {id} buffer={nr}
1476 * :sign jump {id} group={group} file={fname}
1477 * :sign jump {id} group={group} buffer={nr}
1478 */
1479 static void
1480sign_jump_cmd(
1481 buf_T *buf,
1482 linenr_T lnum,
1483 char_u *sign_name,
1484 int id,
1485 char_u *group)
1486{
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001487 if (sign_name == NULL && group == NULL && id == -1)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001488 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001489 emsg(_(e_argreq));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001490 return;
1491 }
1492
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001493 if (buf == NULL || (group != NULL && *group == '\0')
1494 || lnum >= 0 || sign_name != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001495 {
1496 // File or buffer is not specified or an empty group is used
1497 // or a line number or a sign name is specified.
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001498 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001499 return;
1500 }
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001501 (void)sign_jump(id, group, buf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001502}
1503
1504/*
1505 * Parse the command line arguments for the ":sign place", ":sign unplace" and
1506 * ":sign jump" commands.
1507 * The supported arguments are: line={lnum} name={name} group={group}
1508 * priority={prio} and file={fname} or buffer={nr}.
1509 */
1510 static int
1511parse_sign_cmd_args(
1512 int cmd,
1513 char_u *arg,
1514 char_u **sign_name,
1515 int *signid,
1516 char_u **group,
1517 int *prio,
1518 buf_T **buf,
1519 linenr_T *lnum)
1520{
1521 char_u *arg1;
1522 char_u *name;
1523 char_u *filename = NULL;
Bram Moolenaarb589f952019-01-07 22:10:00 +01001524 int lnum_arg = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001525
1526 // first arg could be placed sign id
1527 arg1 = arg;
1528 if (VIM_ISDIGIT(*arg))
1529 {
1530 *signid = getdigits(&arg);
1531 if (!VIM_ISWHITE(*arg) && *arg != NUL)
1532 {
1533 *signid = -1;
1534 arg = arg1;
1535 }
1536 else
1537 arg = skipwhite(arg);
1538 }
1539
1540 while (*arg != NUL)
1541 {
1542 if (STRNCMP(arg, "line=", 5) == 0)
1543 {
1544 arg += 5;
1545 *lnum = atoi((char *)arg);
1546 arg = skiptowhite(arg);
Bram Moolenaarb589f952019-01-07 22:10:00 +01001547 lnum_arg = TRUE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001548 }
1549 else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE)
1550 {
1551 if (*signid != -1)
1552 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001553 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001554 return FAIL;
1555 }
1556 *signid = -2;
1557 arg = skiptowhite(arg + 1);
1558 }
1559 else if (STRNCMP(arg, "name=", 5) == 0)
1560 {
1561 arg += 5;
1562 name = arg;
1563 arg = skiptowhite(arg);
1564 if (*arg != NUL)
1565 *arg++ = NUL;
1566 while (name[0] == '0' && name[1] != NUL)
1567 ++name;
1568 *sign_name = name;
1569 }
1570 else if (STRNCMP(arg, "group=", 6) == 0)
1571 {
1572 arg += 6;
1573 *group = arg;
1574 arg = skiptowhite(arg);
1575 if (*arg != NUL)
1576 *arg++ = NUL;
1577 }
1578 else if (STRNCMP(arg, "priority=", 9) == 0)
1579 {
1580 arg += 9;
1581 *prio = atoi((char *)arg);
1582 arg = skiptowhite(arg);
1583 }
1584 else if (STRNCMP(arg, "file=", 5) == 0)
1585 {
1586 arg += 5;
1587 filename = arg;
1588 *buf = buflist_findname_exp(arg);
1589 break;
1590 }
1591 else if (STRNCMP(arg, "buffer=", 7) == 0)
1592 {
1593 arg += 7;
1594 filename = arg;
1595 *buf = buflist_findnr((int)getdigits(&arg));
1596 if (*skipwhite(arg) != NUL)
Bram Moolenaar2d06bfd2020-07-23 17:16:18 +02001597 semsg(_(e_trailing_arg), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001598 break;
1599 }
1600 else
1601 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001602 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001603 return FAIL;
1604 }
1605 arg = skipwhite(arg);
1606 }
1607
1608 if (filename != NULL && *buf == NULL)
1609 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001610 semsg(_("E158: Invalid buffer name: %s"), filename);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001611 return FAIL;
1612 }
1613
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001614 // If the filename is not supplied for the sign place or the sign jump
1615 // command, then use the current buffer.
Bram Moolenaarb589f952019-01-07 22:10:00 +01001616 if (filename == NULL && ((cmd == SIGNCMD_PLACE && lnum_arg)
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001617 || cmd == SIGNCMD_JUMP))
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001618 *buf = curwin->w_buffer;
1619
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001620 return OK;
1621}
1622
1623/*
1624 * ":sign" command
1625 */
1626 void
1627ex_sign(exarg_T *eap)
1628{
1629 char_u *arg = eap->arg;
1630 char_u *p;
1631 int idx;
1632 sign_T *sp;
1633 buf_T *buf = NULL;
1634
1635 // Parse the subcommand.
1636 p = skiptowhite(arg);
1637 idx = sign_cmd_idx(arg, p);
1638 if (idx == SIGNCMD_LAST)
1639 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001640 semsg(_("E160: Unknown sign command: %s"), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001641 return;
1642 }
1643 arg = skipwhite(p);
1644
1645 if (idx <= SIGNCMD_LIST)
1646 {
1647 // Define, undefine or list signs.
1648 if (idx == SIGNCMD_LIST && *arg == NUL)
1649 {
1650 // ":sign list": list all defined signs
1651 for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next)
1652 sign_list_defined(sp);
1653 }
1654 else if (*arg == NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001655 emsg(_("E156: Missing sign name"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001656 else
1657 {
1658 char_u *name;
1659
1660 // Isolate the sign name. If it's a number skip leading zeroes,
1661 // so that "099" and "99" are the same sign. But keep "0".
1662 p = skiptowhite(arg);
1663 if (*p != NUL)
1664 *p++ = NUL;
1665 while (arg[0] == '0' && arg[1] != NUL)
1666 ++arg;
1667 name = vim_strsave(arg);
1668
1669 if (idx == SIGNCMD_DEFINE)
1670 sign_define_cmd(name, p);
1671 else if (idx == SIGNCMD_LIST)
1672 // ":sign list {name}"
1673 sign_list_by_name(name);
1674 else
1675 // ":sign undefine {name}"
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001676 sign_undefine_by_name(name, TRUE);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001677
1678 vim_free(name);
1679 return;
1680 }
1681 }
1682 else
1683 {
1684 int id = -1;
1685 linenr_T lnum = -1;
1686 char_u *sign_name = NULL;
1687 char_u *group = NULL;
1688 int prio = SIGN_DEF_PRIO;
1689
1690 // Parse command line arguments
1691 if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio,
1692 &buf, &lnum) == FAIL)
1693 return;
1694
1695 if (idx == SIGNCMD_PLACE)
1696 sign_place_cmd(buf, lnum, sign_name, id, group, prio);
1697 else if (idx == SIGNCMD_UNPLACE)
1698 sign_unplace_cmd(buf, lnum, sign_name, id, group);
1699 else if (idx == SIGNCMD_JUMP)
1700 sign_jump_cmd(buf, lnum, sign_name, id, group);
1701 }
1702}
1703
1704/*
1705 * Return information about a specified sign
1706 */
1707 static void
1708sign_getinfo(sign_T *sp, dict_T *retdict)
1709{
1710 char_u *p;
1711
1712 dict_add_string(retdict, "name", (char_u *)sp->sn_name);
1713 if (sp->sn_icon != NULL)
1714 dict_add_string(retdict, "icon", (char_u *)sp->sn_icon);
1715 if (sp->sn_text != NULL)
1716 dict_add_string(retdict, "text", (char_u *)sp->sn_text);
1717 if (sp->sn_line_hl > 0)
1718 {
1719 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1720 if (p == NULL)
1721 p = (char_u *)"NONE";
1722 dict_add_string(retdict, "linehl", (char_u *)p);
1723 }
1724 if (sp->sn_text_hl > 0)
1725 {
1726 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1727 if (p == NULL)
1728 p = (char_u *)"NONE";
1729 dict_add_string(retdict, "texthl", (char_u *)p);
1730 }
Bram Moolenaare413ea02021-11-24 16:20:13 +00001731 if (sp->sn_cul_hl > 0)
1732 {
1733 p = get_highlight_name_ext(NULL, sp->sn_cul_hl - 1, FALSE);
1734 if (p == NULL)
1735 p = (char_u *)"NONE";
1736 dict_add_string(retdict, "culhl", (char_u *)p);
1737 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001738}
1739
1740/*
1741 * If 'name' is NULL, return a list of all the defined signs.
1742 * Otherwise, return information about the specified sign.
1743 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001744 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001745sign_getlist(char_u *name, list_T *retlist)
1746{
1747 sign_T *sp = first_sign;
1748 dict_T *dict;
1749
1750 if (name != NULL)
1751 {
1752 sp = sign_find(name, NULL);
1753 if (sp == NULL)
1754 return;
1755 }
1756
1757 for (; sp != NULL && !got_int; sp = sp->sn_next)
1758 {
1759 if ((dict = dict_alloc_id(aid_sign_getlist)) == NULL)
1760 return;
1761 if (list_append_dict(retlist, dict) == FAIL)
1762 return;
1763 sign_getinfo(sp, dict);
1764
1765 if (name != NULL) // handle only the specified sign
1766 break;
1767 }
1768}
1769
1770/*
1771 * Returns information about signs placed in a buffer as list of dicts.
1772 */
1773 void
1774get_buffer_signs(buf_T *buf, list_T *l)
1775{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001776 sign_entry_T *sign;
1777 dict_T *d;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001778
1779 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1780 {
1781 if ((d = sign_get_info(sign)) != NULL)
1782 list_append_dict(l, d);
1783 }
1784}
1785
1786/*
1787 * Return information about all the signs placed in a buffer
1788 */
1789 static void
1790sign_get_placed_in_buf(
1791 buf_T *buf,
1792 linenr_T lnum,
1793 int sign_id,
1794 char_u *sign_group,
1795 list_T *retlist)
1796{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001797 dict_T *d;
1798 list_T *l;
1799 sign_entry_T *sign;
1800 dict_T *sdict;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001801
1802 if ((d = dict_alloc_id(aid_sign_getplaced_dict)) == NULL)
1803 return;
1804 list_append_dict(retlist, d);
1805
1806 dict_add_number(d, "bufnr", (long)buf->b_fnum);
1807
1808 if ((l = list_alloc_id(aid_sign_getplaced_list)) == NULL)
1809 return;
1810 dict_add_list(d, "signs", l);
1811
1812 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1813 {
1814 if (!sign_in_group(sign, sign_group))
1815 continue;
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001816 if ((lnum == 0 && sign_id == 0)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001817 || (sign_id == 0 && lnum == sign->se_lnum)
1818 || (lnum == 0 && sign_id == sign->se_id)
1819 || (lnum == sign->se_lnum && sign_id == sign->se_id))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001820 {
1821 if ((sdict = sign_get_info(sign)) != NULL)
1822 list_append_dict(l, sdict);
1823 }
1824 }
1825}
1826
1827/*
1828 * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the
1829 * sign placed at the line number. If 'lnum' is zero, return all the signs
1830 * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers.
1831 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001832 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001833sign_get_placed(
1834 buf_T *buf,
1835 linenr_T lnum,
1836 int sign_id,
1837 char_u *sign_group,
1838 list_T *retlist)
1839{
1840 if (buf != NULL)
1841 sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist);
1842 else
1843 {
1844 FOR_ALL_BUFFERS(buf)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001845 if (buf->b_signlist != NULL)
1846 sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001847 }
1848}
1849
1850# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1851/*
1852 * Allocate the icons. Called when the GUI has started. Allows defining
1853 * signs before it starts.
1854 */
1855 void
1856sign_gui_started(void)
1857{
1858 sign_T *sp;
1859
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001860 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001861 if (sp->sn_icon != NULL)
1862 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
1863}
1864# endif
1865
1866/*
1867 * List one sign.
1868 */
1869 static void
1870sign_list_defined(sign_T *sp)
1871{
1872 char_u *p;
1873
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001874 smsg("sign %s", sp->sn_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001875 if (sp->sn_icon != NULL)
1876 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001877 msg_puts(" icon=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001878 msg_outtrans(sp->sn_icon);
1879# ifdef FEAT_SIGN_ICONS
1880 if (sp->sn_image == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001881 msg_puts(_(" (NOT FOUND)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001882# else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001883 msg_puts(_(" (not supported)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001884# endif
1885 }
1886 if (sp->sn_text != NULL)
1887 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001888 msg_puts(" text=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001889 msg_outtrans(sp->sn_text);
1890 }
1891 if (sp->sn_line_hl > 0)
1892 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001893 msg_puts(" linehl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001894 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1895 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001896 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001897 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001898 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001899 }
1900 if (sp->sn_text_hl > 0)
1901 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001902 msg_puts(" texthl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001903 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1904 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001905 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001906 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001907 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001908 }
Bram Moolenaare413ea02021-11-24 16:20:13 +00001909 if (sp->sn_cul_hl > 0)
1910 {
1911 msg_puts(" culhl=");
1912 p = get_highlight_name_ext(NULL, sp->sn_cul_hl - 1, FALSE);
1913 if (p == NULL)
1914 msg_puts("NONE");
1915 else
1916 msg_puts((char *)p);
1917 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001918}
1919
1920/*
1921 * Undefine a sign and free its memory.
1922 */
1923 static void
1924sign_undefine(sign_T *sp, sign_T *sp_prev)
1925{
1926 vim_free(sp->sn_name);
1927 vim_free(sp->sn_icon);
1928# ifdef FEAT_SIGN_ICONS
1929 if (sp->sn_image != NULL)
1930 {
1931 out_flush();
1932 gui_mch_destroy_sign(sp->sn_image);
1933 }
1934# endif
1935 vim_free(sp->sn_text);
1936 if (sp_prev == NULL)
1937 first_sign = sp->sn_next;
1938 else
1939 sp_prev->sn_next = sp->sn_next;
1940 vim_free(sp);
1941}
1942
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001943# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1944 void *
1945sign_get_image(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001946 int typenr) // the attribute which may have a sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001947{
1948 sign_T *sp;
1949
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001950 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001951 if (sp->sn_typenr == typenr)
1952 return sp->sn_image;
1953 return NULL;
1954}
1955# endif
1956
1957/*
1958 * Undefine/free all signs.
1959 */
1960 void
1961free_signs(void)
1962{
1963 while (first_sign != NULL)
1964 sign_undefine(first_sign, NULL);
1965}
1966
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001967static enum
1968{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001969 EXP_SUBCMD, // expand :sign sub-commands
1970 EXP_DEFINE, // expand :sign define {name} args
1971 EXP_PLACE, // expand :sign place {id} args
Bram Moolenaar3678f652019-02-17 14:50:25 +01001972 EXP_LIST, // expand :sign place args
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001973 EXP_UNPLACE, // expand :sign unplace"
Bram Moolenaar3678f652019-02-17 14:50:25 +01001974 EXP_SIGN_NAMES, // expand with name of placed signs
1975 EXP_SIGN_GROUPS // expand with name of placed sign groups
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001976} expand_what;
1977
1978/*
Bram Moolenaar3678f652019-02-17 14:50:25 +01001979 * Return the n'th sign name (used for command line completion)
1980 */
1981 static char_u *
1982get_nth_sign_name(int idx)
1983{
1984 int current_idx;
1985 sign_T *sp;
1986
1987 // Complete with name of signs already defined
1988 current_idx = 0;
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001989 FOR_ALL_SIGNS(sp)
Bram Moolenaar3678f652019-02-17 14:50:25 +01001990 if (current_idx++ == idx)
1991 return sp->sn_name;
1992 return NULL;
1993}
1994
1995/*
1996 * Return the n'th sign group name (used for command line completion)
1997 */
1998 static char_u *
1999get_nth_sign_group_name(int idx)
2000{
2001 int current_idx;
2002 int todo;
2003 hashitem_T *hi;
2004 signgroup_T *group;
2005
2006 // Complete with name of sign groups already defined
2007 current_idx = 0;
2008 todo = (int)sg_table.ht_used;
2009 for (hi = sg_table.ht_array; todo > 0; ++hi)
2010 {
2011 if (!HASHITEM_EMPTY(hi))
2012 {
2013 --todo;
2014 if (current_idx++ == idx)
2015 {
2016 group = HI2SG(hi);
2017 return group->sg_name;
2018 }
2019 }
2020 }
2021 return NULL;
2022}
2023
2024/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002025 * Function given to ExpandGeneric() to obtain the sign command
2026 * expansion.
2027 */
2028 char_u *
2029get_sign_name(expand_T *xp UNUSED, int idx)
2030{
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002031 switch (expand_what)
2032 {
2033 case EXP_SUBCMD:
2034 return (char_u *)cmds[idx];
2035 case EXP_DEFINE:
2036 {
2037 char *define_arg[] =
2038 {
2039 "icon=", "linehl=", "text=", "texthl=", NULL
2040 };
2041 return (char_u *)define_arg[idx];
2042 }
2043 case EXP_PLACE:
2044 {
2045 char *place_arg[] =
2046 {
2047 "line=", "name=", "group=", "priority=", "file=",
2048 "buffer=", NULL
2049 };
2050 return (char_u *)place_arg[idx];
2051 }
Bram Moolenaar3678f652019-02-17 14:50:25 +01002052 case EXP_LIST:
2053 {
2054 char *list_arg[] =
2055 {
2056 "group=", "file=", "buffer=", NULL
2057 };
2058 return (char_u *)list_arg[idx];
2059 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002060 case EXP_UNPLACE:
2061 {
2062 char *unplace_arg[] = { "group=", "file=", "buffer=", NULL };
2063 return (char_u *)unplace_arg[idx];
2064 }
2065 case EXP_SIGN_NAMES:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002066 return get_nth_sign_name(idx);
2067 case EXP_SIGN_GROUPS:
2068 return get_nth_sign_group_name(idx);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002069 default:
2070 return NULL;
2071 }
2072}
2073
2074/*
2075 * Handle command line completion for :sign command.
2076 */
2077 void
2078set_context_in_sign_cmd(expand_T *xp, char_u *arg)
2079{
2080 char_u *p;
2081 char_u *end_subcmd;
2082 char_u *last;
2083 int cmd_idx;
2084 char_u *begin_subcmd_args;
2085
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002086 // Default: expand subcommands.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002087 xp->xp_context = EXPAND_SIGN;
2088 expand_what = EXP_SUBCMD;
2089 xp->xp_pattern = arg;
2090
2091 end_subcmd = skiptowhite(arg);
2092 if (*end_subcmd == NUL)
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002093 // expand subcmd name
2094 // :sign {subcmd}<CTRL-D>
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002095 return;
2096
2097 cmd_idx = sign_cmd_idx(arg, end_subcmd);
2098
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002099 // :sign {subcmd} {subcmd_args}
2100 // |
2101 // begin_subcmd_args
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002102 begin_subcmd_args = skipwhite(end_subcmd);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002103
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002104 // expand last argument of subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002105
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002106 // :sign define {name} {args}...
2107 // |
2108 // p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002109
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002110 // Loop until reaching last argument.
Bram Moolenaar3678f652019-02-17 14:50:25 +01002111 p = begin_subcmd_args;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002112 do
2113 {
2114 p = skipwhite(p);
2115 last = p;
2116 p = skiptowhite(p);
2117 } while (*p != NUL);
2118
2119 p = vim_strchr(last, '=');
2120
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002121 // :sign define {name} {args}... {last}=
2122 // | |
2123 // last p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002124 if (p == NULL)
2125 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002126 // Expand last argument name (before equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002127 xp->xp_pattern = last;
2128 switch (cmd_idx)
2129 {
2130 case SIGNCMD_DEFINE:
2131 expand_what = EXP_DEFINE;
2132 break;
2133 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002134 // List placed signs
2135 if (VIM_ISDIGIT(*begin_subcmd_args))
2136 // :sign place {id} {args}...
2137 expand_what = EXP_PLACE;
2138 else
2139 // :sign place {args}...
2140 expand_what = EXP_LIST;
2141 break;
2142 case SIGNCMD_LIST:
2143 case SIGNCMD_UNDEFINE:
2144 // :sign list <CTRL-D>
2145 // :sign undefine <CTRL-D>
2146 expand_what = EXP_SIGN_NAMES;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002147 break;
2148 case SIGNCMD_JUMP:
2149 case SIGNCMD_UNPLACE:
2150 expand_what = EXP_UNPLACE;
2151 break;
2152 default:
2153 xp->xp_context = EXPAND_NOTHING;
2154 }
2155 }
2156 else
2157 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002158 // Expand last argument value (after equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002159 xp->xp_pattern = p + 1;
2160 switch (cmd_idx)
2161 {
2162 case SIGNCMD_DEFINE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002163 if (STRNCMP(last, "texthl", 6) == 0
2164 || STRNCMP(last, "linehl", 6) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002165 xp->xp_context = EXPAND_HIGHLIGHT;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002166 else if (STRNCMP(last, "icon", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002167 xp->xp_context = EXPAND_FILES;
2168 else
2169 xp->xp_context = EXPAND_NOTHING;
2170 break;
2171 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002172 if (STRNCMP(last, "name", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002173 expand_what = EXP_SIGN_NAMES;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002174 else if (STRNCMP(last, "group", 5) == 0)
2175 expand_what = EXP_SIGN_GROUPS;
2176 else if (STRNCMP(last, "file", 4) == 0)
2177 xp->xp_context = EXPAND_BUFFERS;
2178 else
2179 xp->xp_context = EXPAND_NOTHING;
2180 break;
2181 case SIGNCMD_UNPLACE:
2182 case SIGNCMD_JUMP:
2183 if (STRNCMP(last, "group", 5) == 0)
2184 expand_what = EXP_SIGN_GROUPS;
2185 else if (STRNCMP(last, "file", 4) == 0)
2186 xp->xp_context = EXPAND_BUFFERS;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002187 else
2188 xp->xp_context = EXPAND_NOTHING;
2189 break;
2190 default:
2191 xp->xp_context = EXPAND_NOTHING;
2192 }
2193 }
2194}
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002195
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002196/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002197 * Define a sign using the attributes in 'dict'. Returns 0 on success and -1 on
2198 * failure.
2199 */
2200 static int
2201sign_define_from_dict(char_u *name_arg, dict_T *dict)
2202{
2203 char_u *name = NULL;
2204 char_u *icon = NULL;
2205 char_u *linehl = NULL;
2206 char_u *text = NULL;
2207 char_u *texthl = NULL;
Bram Moolenaare413ea02021-11-24 16:20:13 +00002208 char_u *culhl = NULL;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002209 int retval = -1;
2210
2211 if (name_arg == NULL)
2212 {
2213 if (dict == NULL)
2214 return -1;
2215 name = dict_get_string(dict, (char_u *)"name", TRUE);
2216 }
2217 else
2218 name = vim_strsave(name_arg);
2219 if (name == NULL || name[0] == NUL)
2220 goto cleanup;
2221 if (dict != NULL)
2222 {
2223 icon = dict_get_string(dict, (char_u *)"icon", TRUE);
2224 linehl = dict_get_string(dict, (char_u *)"linehl", TRUE);
2225 text = dict_get_string(dict, (char_u *)"text", TRUE);
2226 texthl = dict_get_string(dict, (char_u *)"texthl", TRUE);
Bram Moolenaare413ea02021-11-24 16:20:13 +00002227 culhl = dict_get_string(dict, (char_u *)"culhl", TRUE);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002228 }
2229
Bram Moolenaare413ea02021-11-24 16:20:13 +00002230 if (sign_define_by_name(name, icon, linehl, text, texthl, culhl) == OK)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002231 retval = 0;
2232
2233cleanup:
2234 vim_free(name);
2235 vim_free(icon);
2236 vim_free(linehl);
2237 vim_free(text);
2238 vim_free(texthl);
Bram Moolenaare413ea02021-11-24 16:20:13 +00002239 vim_free(culhl);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002240
2241 return retval;
2242}
2243
2244/*
2245 * Define multiple signs using attributes from list 'l' and store the return
2246 * values in 'retlist'.
2247 */
2248 static void
2249sign_define_multiple(list_T *l, list_T *retlist)
2250{
2251 listitem_T *li;
2252 int retval;
2253
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002254 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002255 {
2256 retval = -1;
2257 if (li->li_tv.v_type == VAR_DICT)
2258 retval = sign_define_from_dict(NULL, li->li_tv.vval.v_dict);
2259 else
2260 emsg(_(e_dictreq));
2261 list_append_number(retlist, retval);
2262 }
2263}
2264
2265/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002266 * "sign_define()" function
2267 */
2268 void
2269f_sign_define(typval_T *argvars, typval_T *rettv)
2270{
2271 char_u *name;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002272
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002273 if (in_vim9script()
2274 && (check_for_string_or_list_arg(argvars, 0) == FAIL
2275 || check_for_opt_dict_arg(argvars, 1) == FAIL))
2276 return;
2277
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002278 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2279 {
2280 // Define multiple signs
2281 if (rettv_list_alloc(rettv) != OK)
2282 return;
2283
2284 sign_define_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2285 return;
2286 }
2287
2288 // Define a single sign
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002289 rettv->vval.v_number = -1;
2290
2291 name = tv_get_string_chk(&argvars[0]);
2292 if (name == NULL)
2293 return;
2294
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002295 if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_DICT)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002296 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002297 emsg(_(e_dictreq));
2298 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002299 }
2300
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002301 rettv->vval.v_number = sign_define_from_dict(name,
2302 argvars[1].v_type == VAR_DICT ? argvars[1].vval.v_dict : NULL);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002303}
2304
2305/*
2306 * "sign_getdefined()" function
2307 */
2308 void
2309f_sign_getdefined(typval_T *argvars, typval_T *rettv)
2310{
2311 char_u *name = NULL;
2312
2313 if (rettv_list_alloc_id(rettv, aid_sign_getdefined) != OK)
2314 return;
2315
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002316 if (in_vim9script() && check_for_opt_string_arg(argvars, 0) == FAIL)
2317 return;
2318
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002319 if (argvars[0].v_type != VAR_UNKNOWN)
2320 name = tv_get_string(&argvars[0]);
2321
2322 sign_getlist(name, rettv->vval.v_list);
2323}
2324
2325/*
2326 * "sign_getplaced()" function
2327 */
2328 void
2329f_sign_getplaced(typval_T *argvars, typval_T *rettv)
2330{
2331 buf_T *buf = NULL;
2332 dict_T *dict;
2333 dictitem_T *di;
2334 linenr_T lnum = 0;
2335 int sign_id = 0;
2336 char_u *group = NULL;
2337 int notanum = FALSE;
2338
2339 if (rettv_list_alloc_id(rettv, aid_sign_getplaced) != OK)
2340 return;
2341
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002342 if (in_vim9script()
Yegappan Lakshmanancd917202021-07-21 19:09:09 +02002343 && (check_for_opt_buffer_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002344 || (argvars[0].v_type != VAR_UNKNOWN
2345 && check_for_opt_dict_arg(argvars, 1) == FAIL)))
2346 return;
2347
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002348 if (argvars[0].v_type != VAR_UNKNOWN)
2349 {
2350 // get signs placed in the specified buffer
2351 buf = get_buf_arg(&argvars[0]);
2352 if (buf == NULL)
2353 return;
2354
2355 if (argvars[1].v_type != VAR_UNKNOWN)
2356 {
2357 if (argvars[1].v_type != VAR_DICT ||
2358 ((dict = argvars[1].vval.v_dict) == NULL))
2359 {
2360 emsg(_(e_dictreq));
2361 return;
2362 }
2363 if ((di = dict_find(dict, (char_u *)"lnum", -1)) != NULL)
2364 {
2365 // get signs placed at this line
2366 (void)tv_get_number_chk(&di->di_tv, &notanum);
2367 if (notanum)
2368 return;
2369 lnum = tv_get_lnum(&di->di_tv);
2370 }
2371 if ((di = dict_find(dict, (char_u *)"id", -1)) != NULL)
2372 {
2373 // get sign placed with this identifier
2374 sign_id = (int)tv_get_number_chk(&di->di_tv, &notanum);
2375 if (notanum)
2376 return;
2377 }
2378 if ((di = dict_find(dict, (char_u *)"group", -1)) != NULL)
2379 {
2380 group = tv_get_string_chk(&di->di_tv);
2381 if (group == NULL)
2382 return;
2383 if (*group == '\0') // empty string means global group
2384 group = NULL;
2385 }
2386 }
2387 }
2388
2389 sign_get_placed(buf, lnum, sign_id, group, rettv->vval.v_list);
2390}
2391
2392/*
2393 * "sign_jump()" function
2394 */
2395 void
2396f_sign_jump(typval_T *argvars, typval_T *rettv)
2397{
2398 int sign_id;
2399 char_u *sign_group = NULL;
2400 buf_T *buf;
2401 int notanum = FALSE;
2402
2403 rettv->vval.v_number = -1;
2404
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002405 if (in_vim9script()
2406 && (check_for_number_arg(argvars, 0) == FAIL
2407 || check_for_string_arg(argvars, 1) == FAIL
2408 || check_for_buffer_arg(argvars, 2) == FAIL))
2409 return;
2410
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002411 // Sign identifier
2412 sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
2413 if (notanum)
2414 return;
2415 if (sign_id <= 0)
2416 {
2417 emsg(_(e_invarg));
2418 return;
2419 }
2420
2421 // Sign group
2422 sign_group = tv_get_string_chk(&argvars[1]);
2423 if (sign_group == NULL)
2424 return;
2425 if (sign_group[0] == '\0')
2426 sign_group = NULL; // global sign group
2427 else
2428 {
2429 sign_group = vim_strsave(sign_group);
2430 if (sign_group == NULL)
2431 return;
2432 }
2433
2434 // Buffer to place the sign
2435 buf = get_buf_arg(&argvars[2]);
2436 if (buf == NULL)
2437 goto cleanup;
2438
2439 rettv->vval.v_number = sign_jump(sign_id, sign_group, buf);
2440
2441cleanup:
2442 vim_free(sign_group);
2443}
2444
2445/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002446 * Place a new sign using the values specified in dict 'dict'. Returns the sign
2447 * identifier if successfully placed, otherwise returns 0.
2448 */
2449 static int
2450sign_place_from_dict(
2451 typval_T *id_tv,
2452 typval_T *group_tv,
2453 typval_T *name_tv,
2454 typval_T *buf_tv,
2455 dict_T *dict)
2456{
2457 int sign_id = 0;
2458 char_u *group = NULL;
2459 char_u *sign_name = NULL;
2460 buf_T *buf = NULL;
2461 dictitem_T *di;
2462 linenr_T lnum = 0;
2463 int prio = SIGN_DEF_PRIO;
2464 int notanum = FALSE;
2465 int ret_sign_id = -1;
2466
2467 // sign identifier
2468 if (id_tv == NULL)
2469 {
2470 di = dict_find(dict, (char_u *)"id", -1);
2471 if (di != NULL)
2472 id_tv = &di->di_tv;
2473 }
2474 if (id_tv == NULL)
2475 sign_id = 0;
2476 else
2477 {
2478 sign_id = tv_get_number_chk(id_tv, &notanum);
2479 if (notanum)
2480 return -1;
2481 if (sign_id < 0)
2482 {
2483 emsg(_(e_invarg));
2484 return -1;
2485 }
2486 }
2487
2488 // sign group
2489 if (group_tv == NULL)
2490 {
2491 di = dict_find(dict, (char_u *)"group", -1);
2492 if (di != NULL)
2493 group_tv = &di->di_tv;
2494 }
2495 if (group_tv == NULL)
2496 group = NULL; // global group
2497 else
2498 {
2499 group = tv_get_string_chk(group_tv);
2500 if (group == NULL)
2501 goto cleanup;
2502 if (group[0] == '\0') // global sign group
2503 group = NULL;
2504 else
2505 {
2506 group = vim_strsave(group);
2507 if (group == NULL)
2508 return -1;
2509 }
2510 }
2511
2512 // sign name
2513 if (name_tv == NULL)
2514 {
2515 di = dict_find(dict, (char_u *)"name", -1);
2516 if (di != NULL)
2517 name_tv = &di->di_tv;
2518 }
2519 if (name_tv == NULL)
2520 goto cleanup;
2521 sign_name = tv_get_string_chk(name_tv);
2522 if (sign_name == NULL)
2523 goto cleanup;
2524
2525 // buffer to place the sign
2526 if (buf_tv == NULL)
2527 {
2528 di = dict_find(dict, (char_u *)"buffer", -1);
2529 if (di != NULL)
2530 buf_tv = &di->di_tv;
2531 }
2532 if (buf_tv == NULL)
2533 goto cleanup;
2534 buf = get_buf_arg(buf_tv);
2535 if (buf == NULL)
2536 goto cleanup;
2537
2538 // line number of the sign
2539 di = dict_find(dict, (char_u *)"lnum", -1);
2540 if (di != NULL)
2541 {
Bram Moolenaar42aff462019-08-21 13:20:29 +02002542 lnum = tv_get_lnum(&di->di_tv);
2543 if (lnum <= 0)
2544 {
2545 emsg(_(e_invarg));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002546 goto cleanup;
Bram Moolenaar42aff462019-08-21 13:20:29 +02002547 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002548 }
2549
2550 // sign priority
2551 di = dict_find(dict, (char_u *)"priority", -1);
2552 if (di != NULL)
2553 {
2554 prio = (int)tv_get_number_chk(&di->di_tv, &notanum);
2555 if (notanum)
2556 goto cleanup;
2557 }
2558
2559 if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK)
2560 ret_sign_id = sign_id;
2561
2562cleanup:
2563 vim_free(group);
2564
2565 return ret_sign_id;
2566}
2567
2568/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002569 * "sign_place()" function
2570 */
2571 void
2572f_sign_place(typval_T *argvars, typval_T *rettv)
2573{
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002574 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002575
2576 rettv->vval.v_number = -1;
2577
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002578 if (in_vim9script()
2579 && (check_for_number_arg(argvars, 0) == FAIL
2580 || check_for_string_arg(argvars, 1) == FAIL
2581 || check_for_string_arg(argvars, 2) == FAIL
2582 || check_for_buffer_arg(argvars, 3) == FAIL
2583 || check_for_opt_dict_arg(argvars, 4) == FAIL))
2584 return;
2585
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002586 if (argvars[4].v_type != VAR_UNKNOWN
2587 && (argvars[4].v_type != VAR_DICT
2588 || ((dict = argvars[4].vval.v_dict) == NULL)))
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002589 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002590 emsg(_(e_dictreq));
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002591 return;
2592 }
2593
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002594 rettv->vval.v_number = sign_place_from_dict(&argvars[0], &argvars[1],
2595 &argvars[2], &argvars[3], dict);
2596}
2597
2598/*
2599 * "sign_placelist()" function. Place multiple signs.
2600 */
2601 void
2602f_sign_placelist(typval_T *argvars, typval_T *rettv)
2603{
2604 listitem_T *li;
2605 int sign_id;
2606
2607 if (rettv_list_alloc(rettv) != OK)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002608 return;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002609
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002610 if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
2611 return;
2612
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002613 if (argvars[0].v_type != VAR_LIST)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002614 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002615 emsg(_(e_listreq));
2616 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002617 }
2618
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002619 // Process the List of sign attributes
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002620 FOR_ALL_LIST_ITEMS(argvars[0].vval.v_list, li)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002621 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002622 sign_id = -1;
2623 if (li->li_tv.v_type == VAR_DICT)
2624 sign_id = sign_place_from_dict(NULL, NULL, NULL, NULL,
2625 li->li_tv.vval.v_dict);
2626 else
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002627 emsg(_(e_dictreq));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002628 list_append_number(rettv->vval.v_list, sign_id);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002629 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002630}
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002631
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002632/*
2633 * Undefine multiple signs
2634 */
2635 static void
2636sign_undefine_multiple(list_T *l, list_T *retlist)
2637{
2638 char_u *name;
2639 listitem_T *li;
2640 int retval;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002641
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002642 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002643 {
2644 retval = -1;
2645 name = tv_get_string_chk(&li->li_tv);
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002646 if (name != NULL && (sign_undefine_by_name(name, TRUE) == OK))
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002647 retval = 0;
2648 list_append_number(retlist, retval);
2649 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002650}
2651
2652/*
2653 * "sign_undefine()" function
2654 */
2655 void
2656f_sign_undefine(typval_T *argvars, typval_T *rettv)
2657{
2658 char_u *name;
2659
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002660 if (in_vim9script()
2661 && check_for_opt_string_or_list_arg(argvars, 0) == FAIL)
2662 return;
2663
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002664 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2665 {
2666 // Undefine multiple signs
2667 if (rettv_list_alloc(rettv) != OK)
2668 return;
2669
2670 sign_undefine_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2671 return;
2672 }
2673
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002674 rettv->vval.v_number = -1;
2675
2676 if (argvars[0].v_type == VAR_UNKNOWN)
2677 {
2678 // Free all the signs
2679 free_signs();
2680 rettv->vval.v_number = 0;
2681 }
2682 else
2683 {
2684 // Free only the specified sign
2685 name = tv_get_string_chk(&argvars[0]);
2686 if (name == NULL)
2687 return;
2688
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002689 if (sign_undefine_by_name(name, TRUE) == OK)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002690 rettv->vval.v_number = 0;
2691 }
2692}
2693
2694/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002695 * Unplace the sign with attributes specified in 'dict'. Returns 0 on success
2696 * and -1 on failure.
2697 */
2698 static int
2699sign_unplace_from_dict(typval_T *group_tv, dict_T *dict)
2700{
2701 dictitem_T *di;
2702 int sign_id = 0;
2703 buf_T *buf = NULL;
2704 char_u *group = NULL;
2705 int retval = -1;
2706
2707 // sign group
2708 if (group_tv != NULL)
2709 group = tv_get_string(group_tv);
2710 else
2711 group = dict_get_string(dict, (char_u *)"group", FALSE);
2712 if (group != NULL)
2713 {
2714 if (group[0] == '\0') // global sign group
2715 group = NULL;
2716 else
2717 {
2718 group = vim_strsave(group);
2719 if (group == NULL)
2720 return -1;
2721 }
2722 }
2723
2724 if (dict != NULL)
2725 {
2726 if ((di = dict_find(dict, (char_u *)"buffer", -1)) != NULL)
2727 {
2728 buf = get_buf_arg(&di->di_tv);
2729 if (buf == NULL)
2730 goto cleanup;
2731 }
2732 if (dict_find(dict, (char_u *)"id", -1) != NULL)
2733 {
2734 sign_id = dict_get_number(dict, (char_u *)"id");
2735 if (sign_id <= 0)
2736 {
2737 emsg(_(e_invarg));
2738 goto cleanup;
2739 }
2740 }
2741 }
2742
2743 if (buf == NULL)
2744 {
2745 // Delete the sign in all the buffers
2746 retval = 0;
2747 FOR_ALL_BUFFERS(buf)
2748 if (sign_unplace(sign_id, group, buf, 0) != OK)
2749 retval = -1;
2750 }
2751 else if (sign_unplace(sign_id, group, buf, 0) == OK)
2752 retval = 0;
2753
2754cleanup:
2755 vim_free(group);
2756
2757 return retval;
2758}
2759
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002760 sign_entry_T *
2761get_first_valid_sign(win_T *wp)
2762{
2763 sign_entry_T *sign = wp->w_buffer->b_signlist;
2764
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002765# ifdef FEAT_PROP_POPUP
Bram Moolenaar72570732019-11-30 14:21:53 +01002766 while (sign != NULL && !sign_group_for_window(sign, wp))
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002767 sign = sign->se_next;
2768# endif
2769 return sign;
2770}
2771
2772/*
2773 * Return TRUE when window "wp" has a column to draw signs in.
2774 */
2775 int
2776signcolumn_on(win_T *wp)
2777{
2778 // If 'signcolumn' is set to 'number', signs are displayed in the 'number'
2779 // column (if present). Otherwise signs are to be displayed in the sign
2780 // column.
2781 if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u')
2782 return get_first_valid_sign(wp) != NULL && !wp->w_p_nu && !wp->w_p_rnu;
2783
2784 if (*wp->w_p_scl == 'n')
2785 return FALSE;
2786 if (*wp->w_p_scl == 'y')
2787 return TRUE;
2788 return (get_first_valid_sign(wp) != NULL
2789# ifdef FEAT_NETBEANS_INTG
2790 || wp->w_buffer->b_has_sign_column
2791# endif
2792 );
2793}
2794
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002795/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002796 * "sign_unplace()" function
2797 */
2798 void
2799f_sign_unplace(typval_T *argvars, typval_T *rettv)
2800{
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002801 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002802
2803 rettv->vval.v_number = -1;
2804
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002805 if (in_vim9script()
2806 && (check_for_string_arg(argvars, 0) == FAIL
2807 || check_for_opt_dict_arg(argvars, 1) == FAIL))
2808 return;
2809
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002810 if (argvars[0].v_type != VAR_STRING)
2811 {
2812 emsg(_(e_invarg));
2813 return;
2814 }
2815
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002816 if (argvars[1].v_type != VAR_UNKNOWN)
2817 {
2818 if (argvars[1].v_type != VAR_DICT)
2819 {
2820 emsg(_(e_dictreq));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002821 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002822 }
2823 dict = argvars[1].vval.v_dict;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002824 }
2825
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002826 rettv->vval.v_number = sign_unplace_from_dict(&argvars[0], dict);
2827}
2828
2829/*
2830 * "sign_unplacelist()" function
2831 */
2832 void
2833f_sign_unplacelist(typval_T *argvars, typval_T *rettv)
2834{
2835 listitem_T *li;
2836 int retval;
2837
2838 if (rettv_list_alloc(rettv) != OK)
2839 return;
2840
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002841 if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
2842 return;
2843
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002844 if (argvars[0].v_type != VAR_LIST)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002845 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002846 emsg(_(e_listreq));
2847 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002848 }
2849
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002850 FOR_ALL_LIST_ITEMS(argvars[0].vval.v_list, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002851 {
2852 retval = -1;
2853 if (li->li_tv.v_type == VAR_DICT)
2854 retval = sign_unplace_from_dict(NULL, li->li_tv.vval.v_dict);
2855 else
2856 emsg(_(e_dictreq));
2857 list_append_number(rettv->vval.v_list, retval);
2858 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002859}
2860
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002861#endif // FEAT_SIGNS