blob: b1f496c38889cd75106d593ece0d2459e31b3dbc [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
James McCoya80aad72021-12-22 19:45:28 +000036 int sn_num_hl; // highlight ID for line number
LemonBoyb975ddf2024-07-06 18:04:09 +020037 int sn_priority; // default priority of this sign, -1 means SIGN_DEF_PRIO
Bram Moolenaarbbea4702019-01-01 13:20:31 +010038};
39
40static sign_T *first_sign = NULL;
41static int next_sign_typenr = 1;
42
43static void sign_list_defined(sign_T *sp);
44static void sign_undefine(sign_T *sp, sign_T *sp_prev);
45
46static char *cmds[] = {
47 "define",
48# define SIGNCMD_DEFINE 0
49 "undefine",
50# define SIGNCMD_UNDEFINE 1
51 "list",
52# define SIGNCMD_LIST 2
53 "place",
54# define SIGNCMD_PLACE 3
55 "unplace",
56# define SIGNCMD_UNPLACE 4
57 "jump",
58# define SIGNCMD_JUMP 5
59 NULL
60# define SIGNCMD_LAST 6
61};
62
Bram Moolenaaraeea7212020-04-02 18:50:46 +020063#define FOR_ALL_SIGNS(sp) \
64 for ((sp) = first_sign; (sp) != NULL; (sp) = (sp)->sn_next)
65
Bram Moolenaarbbea4702019-01-01 13:20:31 +010066static hashtab_T sg_table; // sign group (signgroup_T) hashtable
67static int next_sign_id = 1; // next sign id in the global group
68
69/*
70 * Initialize data needed for managing signs
71 */
72 void
73init_signs(void)
74{
75 hash_init(&sg_table); // sign group hash table
76}
77
78/*
79 * A new sign in group 'groupname' is added. If the group is not present,
80 * create it. Otherwise reference the group.
81 */
82 static signgroup_T *
83sign_group_ref(char_u *groupname)
84{
85 hash_T hash;
86 hashitem_T *hi;
87 signgroup_T *group;
88
89 hash = hash_hash(groupname);
90 hi = hash_lookup(&sg_table, groupname, hash);
91 if (HASHITEM_EMPTY(hi))
92 {
93 // new group
Bram Moolenaar47ed5532019-08-08 20:49:14 +020094 group = alloc(offsetof(signgroup_T, sg_name) + STRLEN(groupname) + 1);
Bram Moolenaarbbea4702019-01-01 13:20:31 +010095 if (group == NULL)
96 return NULL;
97 STRCPY(group->sg_name, groupname);
Bram Moolenaar6656c2e2019-10-24 15:00:04 +020098 group->sg_refcount = 1;
99 group->sg_next_sign_id = 1;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100100 hash_add_item(&sg_table, hi, group->sg_name, hash);
101 }
102 else
103 {
104 // existing group
105 group = HI2SG(hi);
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200106 group->sg_refcount++;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100107 }
108
109 return group;
110}
111
112/*
113 * A sign in group 'groupname' is removed. If all the signs in this group are
114 * removed, then remove the group.
115 */
116 static void
117sign_group_unref(char_u *groupname)
118{
119 hashitem_T *hi;
120 signgroup_T *group;
121
122 hi = hash_find(&sg_table, groupname);
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000123 if (HASHITEM_EMPTY(hi))
124 return;
125
126 group = HI2SG(hi);
127 group->sg_refcount--;
128 if (group->sg_refcount == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100129 {
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000130 // All the signs in this group are removed
131 hash_remove(&sg_table, hi, "sign remove");
132 vim_free(group);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100133 }
134}
135
136/*
137 * Returns TRUE if 'sign' is in 'group'.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200138 * A sign can either be in the global group (sign->se_group == NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100139 * or in a named group. If 'group' is '*', then the sign is part of the group.
140 */
141 static int
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200142sign_in_group(sign_entry_T *sign, char_u *group)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100143{
144 return ((group != NULL && STRCMP(group, "*") == 0)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200145 || (group == NULL && sign->se_group == NULL)
146 || (group != NULL && sign->se_group != NULL
Bram Moolenaar72570732019-11-30 14:21:53 +0100147 && STRCMP(group, sign->se_group->sg_name) == 0));
148}
149
150/*
151 * Return TRUE if "sign" is to be displayed in window "wp".
152 * If the group name starts with "PopUp" it only shows in a popup window.
153 */
154 static int
155sign_group_for_window(sign_entry_T *sign, win_T *wp)
156{
157 int for_popup = sign->se_group != NULL
158 && STRNCMP("PopUp", sign->se_group->sg_name, 5) == 0;
159
160 return WIN_IS_POPUP(wp) ? for_popup : !for_popup;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100161}
162
163/*
164 * Get the next free sign identifier in the specified group
165 */
166 static int
167sign_group_get_next_signid(buf_T *buf, char_u *groupname)
168{
169 int id = 1;
170 signgroup_T *group = NULL;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200171 sign_entry_T *sign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100172 hashitem_T *hi;
173 int found = FALSE;
174
175 if (groupname != NULL)
176 {
177 hi = hash_find(&sg_table, groupname);
178 if (HASHITEM_EMPTY(hi))
179 return id;
180 group = HI2SG(hi);
181 }
182
Bram Moolenaarb5443cc2019-01-15 20:19:40 +0100183 // Search for the next usable sign identifier
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100184 while (!found)
185 {
186 if (group == NULL)
187 id = next_sign_id++; // global group
188 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200189 id = group->sg_next_sign_id++;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100190
191 // Check whether this sign is already placed in the buffer
192 found = TRUE;
193 FOR_ALL_SIGNS_IN_BUF(buf, sign)
194 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200195 if (id == sign->se_id && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100196 {
197 found = FALSE; // sign identifier is in use
198 break;
199 }
200 }
201 }
202
203 return id;
204}
205
206/*
207 * Insert a new sign into the signlist for buffer 'buf' between the 'prev' and
208 * 'next' signs.
209 */
210 static void
211insert_sign(
212 buf_T *buf, // buffer to store sign in
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200213 sign_entry_T *prev, // previous sign entry
214 sign_entry_T *next, // next sign entry
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100215 int id, // sign ID
216 char_u *group, // sign group; NULL for global group
217 int prio, // sign priority
218 linenr_T lnum, // line number which gets the mark
219 int typenr) // typenr of sign we are adding
220{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200221 sign_entry_T *newsign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100222
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200223 newsign = lalloc_id(sizeof(sign_entry_T), FALSE, aid_insert_sign);
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000224 if (newsign == NULL)
225 return;
226
227 newsign->se_id = id;
228 newsign->se_lnum = lnum;
229 newsign->se_typenr = typenr;
230 if (group != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100231 {
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000232 newsign->se_group = sign_group_ref(group);
233 if (newsign->se_group == NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100234 {
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000235 vim_free(newsign);
236 return;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100237 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100238 }
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000239 else
240 newsign->se_group = NULL;
241 newsign->se_priority = prio;
242 newsign->se_next = next;
243 newsign->se_prev = prev;
244 if (next != NULL)
245 next->se_prev = newsign;
246
247 if (prev == NULL)
248 {
249 // When adding first sign need to redraw the windows to create the
250 // column for signs.
251 if (buf->b_signlist == NULL)
252 {
253 redraw_buf_later(buf, UPD_NOT_VALID);
254 changed_line_abv_curs();
255 }
256
257 // first sign in signlist
258 buf->b_signlist = newsign;
259#ifdef FEAT_NETBEANS_INTG
260 if (netbeans_active())
261 buf->b_has_sign_column = TRUE;
262#endif
263 }
264 else
265 prev->se_next = newsign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100266}
267
268/*
269 * Insert a new sign sorted by line number and sign priority.
270 */
271 static void
272insert_sign_by_lnum_prio(
273 buf_T *buf, // buffer to store sign in
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200274 sign_entry_T *prev, // previous sign entry
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100275 int id, // sign ID
276 char_u *group, // sign group; NULL for global group
277 int prio, // sign priority
278 linenr_T lnum, // line number which gets the mark
279 int typenr) // typenr of sign we are adding
280{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200281 sign_entry_T *sign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100282
283 // keep signs sorted by lnum and by priority: insert new sign at
284 // the proper position in the list for this lnum.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200285 while (prev != NULL && prev->se_lnum == lnum && prev->se_priority <= prio)
286 prev = prev->se_prev;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100287 if (prev == NULL)
288 sign = buf->b_signlist;
289 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200290 sign = prev->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100291
292 insert_sign(buf, prev, sign, id, group, prio, lnum, typenr);
293}
294
295/*
Bram Moolenaar4e038572019-07-04 18:28:35 +0200296 * Lookup a sign by typenr. Returns NULL if sign is not found.
297 */
298 static sign_T *
299find_sign_by_typenr(int typenr)
300{
301 sign_T *sp;
302
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200303 FOR_ALL_SIGNS(sp)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200304 if (sp->sn_typenr == typenr)
305 return sp;
306 return NULL;
307}
308
309/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100310 * Get the name of a sign by its typenr.
311 */
312 static char_u *
313sign_typenr2name(int typenr)
314{
315 sign_T *sp;
316
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200317 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100318 if (sp->sn_typenr == typenr)
319 return sp->sn_name;
320 return (char_u *)_("[Deleted]");
321}
322
323/*
324 * Return information about a sign in a Dict
325 */
326 static dict_T *
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200327sign_get_info(sign_entry_T *sign)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100328{
329 dict_T *d;
330
331 if ((d = dict_alloc_id(aid_sign_getinfo)) == NULL)
332 return NULL;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200333 dict_add_number(d, "id", sign->se_id);
334 dict_add_string(d, "group", (sign->se_group == NULL) ?
335 (char_u *)"" : sign->se_group->sg_name);
336 dict_add_number(d, "lnum", sign->se_lnum);
337 dict_add_string(d, "name", sign_typenr2name(sign->se_typenr));
338 dict_add_number(d, "priority", sign->se_priority);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100339
340 return d;
341}
342
343/*
Bram Moolenaar64416122019-06-07 21:37:13 +0200344 * Sort the signs placed on the same line as "sign" by priority. Invoked after
345 * changing the priority of an already placed sign. Assumes the signs in the
346 * buffer are sorted by line number and priority.
347 */
348 static void
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200349sign_sort_by_prio_on_line(buf_T *buf, sign_entry_T *sign)
Bram Moolenaar64416122019-06-07 21:37:13 +0200350{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200351 sign_entry_T *p = NULL;
Bram Moolenaar64416122019-06-07 21:37:13 +0200352
353 // If there is only one sign in the buffer or only one sign on the line or
354 // the sign is already sorted by priority, then return.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200355 if ((sign->se_prev == NULL
356 || sign->se_prev->se_lnum != sign->se_lnum
357 || sign->se_prev->se_priority > sign->se_priority)
358 && (sign->se_next == NULL
359 || sign->se_next->se_lnum != sign->se_lnum
360 || sign->se_next->se_priority < sign->se_priority))
Bram Moolenaar64416122019-06-07 21:37:13 +0200361 return;
362
363 // One or more signs on the same line as 'sign'
364 // Find a sign after which 'sign' should be inserted
365
366 // First search backward for a sign with higher priority on the same line
367 p = sign;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200368 while (p->se_prev != NULL && p->se_prev->se_lnum == sign->se_lnum
369 && p->se_prev->se_priority <= sign->se_priority)
370 p = p->se_prev;
Bram Moolenaar64416122019-06-07 21:37:13 +0200371
372 if (p == sign)
373 {
374 // Sign not found. Search forward for a sign with priority just before
375 // 'sign'.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200376 p = sign->se_next;
377 while (p->se_next != NULL && p->se_next->se_lnum == sign->se_lnum
378 && p->se_next->se_priority > sign->se_priority)
379 p = p->se_next;
Bram Moolenaar64416122019-06-07 21:37:13 +0200380 }
381
382 // Remove 'sign' from the list
383 if (buf->b_signlist == sign)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200384 buf->b_signlist = sign->se_next;
385 if (sign->se_prev != NULL)
386 sign->se_prev->se_next = sign->se_next;
387 if (sign->se_next != NULL)
388 sign->se_next->se_prev = sign->se_prev;
389 sign->se_prev = NULL;
390 sign->se_next = NULL;
Bram Moolenaar64416122019-06-07 21:37:13 +0200391
392 // Re-insert 'sign' at the right place
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200393 if (p->se_priority <= sign->se_priority)
Bram Moolenaar64416122019-06-07 21:37:13 +0200394 {
395 // 'sign' has a higher priority and should be inserted before 'p'
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200396 sign->se_prev = p->se_prev;
397 sign->se_next = p;
398 p->se_prev = sign;
399 if (sign->se_prev != NULL)
400 sign->se_prev->se_next = sign;
Bram Moolenaar64416122019-06-07 21:37:13 +0200401 if (buf->b_signlist == p)
402 buf->b_signlist = sign;
403 }
404 else
405 {
406 // 'sign' has a lower priority and should be inserted after 'p'
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200407 sign->se_prev = p;
408 sign->se_next = p->se_next;
409 p->se_next = sign;
410 if (sign->se_next != NULL)
411 sign->se_next->se_prev = sign;
Bram Moolenaar64416122019-06-07 21:37:13 +0200412 }
413}
414
415/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100416 * Add the sign into the signlist. Find the right spot to do it though.
417 */
418 static void
419buf_addsign(
420 buf_T *buf, // buffer to store sign in
421 int id, // sign ID
422 char_u *groupname, // sign group
423 int prio, // sign priority
424 linenr_T lnum, // line number which gets the mark
425 int typenr) // typenr of sign we are adding
426{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200427 sign_entry_T *sign; // a sign in the signlist
428 sign_entry_T *prev; // the previous sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100429
430 prev = NULL;
431 FOR_ALL_SIGNS_IN_BUF(buf, sign)
432 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200433 if (lnum == sign->se_lnum && id == sign->se_id
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100434 && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100435 {
436 // Update an existing sign
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200437 sign->se_typenr = typenr;
438 sign->se_priority = prio;
Bram Moolenaar64416122019-06-07 21:37:13 +0200439 sign_sort_by_prio_on_line(buf, sign);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100440 return;
441 }
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200442 else if (lnum < sign->se_lnum)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100443 {
444 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio,
445 lnum, typenr);
446 return;
447 }
448 prev = sign;
449 }
450
451 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr);
452 return;
453}
454
455/*
456 * For an existing, placed sign "markId" change the type to "typenr".
457 * Returns the line number of the sign, or zero if the sign is not found.
458 */
459 static linenr_T
460buf_change_sign_type(
461 buf_T *buf, // buffer to store sign in
462 int markId, // sign ID
463 char_u *group, // sign group
Bram Moolenaar809ce4d2019-07-13 21:21:40 +0200464 int typenr, // typenr of sign we are adding
465 int prio) // sign priority
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100466{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200467 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100468
469 FOR_ALL_SIGNS_IN_BUF(buf, sign)
470 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200471 if (sign->se_id == markId && sign_in_group(sign, group))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100472 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200473 sign->se_typenr = typenr;
474 sign->se_priority = prio;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +0200475 sign_sort_by_prio_on_line(buf, sign);
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200476 return sign->se_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100477 }
478 }
479
480 return (linenr_T)0;
481}
482
483/*
Bram Moolenaar4e038572019-07-04 18:28:35 +0200484 * Return the attributes of the first sign placed on line 'lnum' in buffer
485 * 'buf'. Used when refreshing the screen. Returns TRUE if a sign is found on
486 * 'lnum', FALSE otherwise.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100487 */
488 int
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100489buf_get_signattrs(win_T *wp, linenr_T lnum, sign_attrs_T *sattr)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100490{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200491 sign_entry_T *sign;
492 sign_T *sp;
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100493 buf_T *buf = wp->w_buffer;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200494
Bram Moolenaara80faa82020-04-12 19:37:17 +0200495 CLEAR_POINTER(sattr);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100496
497 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200498 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200499 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200500 // Signs are sorted by line number in the buffer. No need to check
501 // for signs after the specified line number 'lnum'.
502 break;
503
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100504 if (sign->se_lnum == lnum
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100505# ifdef FEAT_PROP_POPUP
Bram Moolenaar72570732019-11-30 14:21:53 +0100506 && sign_group_for_window(sign, wp)
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100507# endif
508 )
Bram Moolenaar4e038572019-07-04 18:28:35 +0200509 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200510 sattr->sat_typenr = sign->se_typenr;
511 sp = find_sign_by_typenr(sign->se_typenr);
Bram Moolenaar4e038572019-07-04 18:28:35 +0200512 if (sp == NULL)
513 return FALSE;
514
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100515# ifdef FEAT_SIGN_ICONS
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200516 sattr->sat_icon = sp->sn_image;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100517# endif
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200518 sattr->sat_text = sp->sn_text;
519 if (sattr->sat_text != NULL && sp->sn_text_hl > 0)
520 sattr->sat_texthl = syn_id2attr(sp->sn_text_hl);
Bram Moolenaar4e038572019-07-04 18:28:35 +0200521 if (sp->sn_line_hl > 0)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200522 sattr->sat_linehl = syn_id2attr(sp->sn_line_hl);
Bram Moolenaare413ea02021-11-24 16:20:13 +0000523 if (sp->sn_cul_hl > 0)
524 sattr->sat_culhl = syn_id2attr(sp->sn_cul_hl);
James McCoya80aad72021-12-22 19:45:28 +0000525 if (sp->sn_num_hl > 0)
526 sattr->sat_numhl = syn_id2attr(sp->sn_num_hl);
Bram Moolenaar2f122842020-08-31 23:18:00 +0200527 sattr->sat_priority = sign->se_priority;
Bram Moolenaara2f6e422020-02-19 17:13:04 +0100528
529 // If there is another sign next with the same priority, may
530 // combine the text and the line highlighting.
531 if (sign->se_next != NULL
532 && sign->se_next->se_priority == sign->se_priority
533 && sign->se_next->se_lnum == sign->se_lnum)
534 {
535 sign_T *next_sp = find_sign_by_typenr(sign->se_next->se_typenr);
536
537 if (next_sp != NULL)
538 {
539 if (sattr->sat_icon == NULL && sattr->sat_text == NULL)
540 {
541# ifdef FEAT_SIGN_ICONS
542 sattr->sat_icon = next_sp->sn_image;
543# endif
544 sattr->sat_text = next_sp->sn_text;
545 }
546 if (sp->sn_text_hl <= 0 && next_sp->sn_text_hl > 0)
547 sattr->sat_texthl = syn_id2attr(next_sp->sn_text_hl);
548 if (sp->sn_line_hl <= 0 && next_sp->sn_line_hl > 0)
549 sattr->sat_linehl = syn_id2attr(next_sp->sn_line_hl);
Bram Moolenaare413ea02021-11-24 16:20:13 +0000550 if (sp->sn_cul_hl <= 0 && next_sp->sn_cul_hl > 0)
551 sattr->sat_culhl = syn_id2attr(next_sp->sn_cul_hl);
James McCoya80aad72021-12-22 19:45:28 +0000552 if (sp->sn_num_hl <= 0 && next_sp->sn_num_hl > 0)
553 sattr->sat_numhl = syn_id2attr(next_sp->sn_num_hl);
Bram Moolenaara2f6e422020-02-19 17:13:04 +0100554 }
555 }
Bram Moolenaar4e038572019-07-04 18:28:35 +0200556 return TRUE;
557 }
558 }
559 return FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100560}
561
562/*
563 * Delete sign 'id' in group 'group' from buffer 'buf'.
564 * If 'id' is zero, then delete all the signs in group 'group'. Otherwise
565 * delete only the specified sign.
566 * If 'group' is '*', then delete the sign in all the groups. If 'group' is
567 * NULL, then delete the sign in the global group. Otherwise delete the sign in
568 * the specified group.
569 * Returns the line number of the deleted sign. If multiple signs are deleted,
570 * then returns the line number of the last sign deleted.
571 */
572 linenr_T
573buf_delsign(
574 buf_T *buf, // buffer sign is stored in
575 linenr_T atlnum, // sign at this line, 0 - at any line
576 int id, // sign id
577 char_u *group) // sign group
578{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200579 sign_entry_T **lastp; // pointer to pointer to current sign
580 sign_entry_T *sign; // a sign in a b_signlist
581 sign_entry_T *next; // the next sign in a b_signlist
582 linenr_T lnum; // line number whose sign was deleted
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100583
584 lastp = &buf->b_signlist;
585 lnum = 0;
586 for (sign = buf->b_signlist; sign != NULL; sign = next)
587 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200588 next = sign->se_next;
589 if ((id == 0 || sign->se_id == id)
590 && (atlnum == 0 || sign->se_lnum == atlnum)
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100591 && sign_in_group(sign, group))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100592
593 {
594 *lastp = next;
595 if (next != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200596 next->se_prev = sign->se_prev;
597 lnum = sign->se_lnum;
598 if (sign->se_group != NULL)
599 sign_group_unref(sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100600 vim_free(sign);
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100601 redraw_buf_line_later(buf, lnum);
602
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100603 // Check whether only one sign needs to be deleted
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100604 // If deleting a sign with a specific identifier in a particular
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100605 // group or deleting any sign at a particular line number, delete
606 // only one sign.
607 if (group == NULL
608 || (*group != '*' && id != 0)
609 || (*group == '*' && atlnum != 0))
610 break;
611 }
612 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200613 lastp = &sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100614 }
615
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100616 // When deleting the last sign the cursor position may change, because the
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100617 // sign columns no longer shows. And the 'signcolumn' may be hidden.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100618 if (buf->b_signlist == NULL)
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100619 {
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100620 redraw_buf_later(buf, UPD_NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200621 changed_line_abv_curs();
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100622 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100623
624 return lnum;
625}
626
627
628/*
629 * Find the line number of the sign with the requested id in group 'group'. If
630 * the sign does not exist, return 0 as the line number. This will still let
631 * the correct file get loaded.
632 */
633 int
634buf_findsign(
635 buf_T *buf, // buffer to store sign in
636 int id, // sign ID
637 char_u *group) // sign group
638{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200639 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100640
641 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200642 if (sign->se_id == id && sign_in_group(sign, group))
643 return sign->se_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100644
645 return 0;
646}
647
648/*
649 * Return the sign at line 'lnum' in buffer 'buf'. Returns NULL if a sign is
650 * not found at the line. If 'groupname' is NULL, searches in the global group.
651 */
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200652 static sign_entry_T *
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100653buf_getsign_at_line(
654 buf_T *buf, // buffer whose sign we are searching for
655 linenr_T lnum, // line number of sign
656 char_u *groupname) // sign group name
657{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200658 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100659
660 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200661 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200662 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200663 // Signs are sorted by line number in the buffer. No need to check
664 // for signs after the specified line number 'lnum'.
665 break;
666
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200667 if (sign->se_lnum == lnum && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100668 return sign;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200669 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100670
671 return NULL;
672}
673
674/*
675 * Return the identifier of the sign at line number 'lnum' in buffer 'buf'.
676 */
677 int
678buf_findsign_id(
679 buf_T *buf, // buffer whose sign we are searching for
680 linenr_T lnum, // line number of sign
681 char_u *groupname) // sign group name
682{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200683 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100684
685 sign = buf_getsign_at_line(buf, lnum, groupname);
686 if (sign != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200687 return sign->se_id;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100688
689 return 0;
690}
691
692# if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
693/*
694 * See if a given type of sign exists on a specific line.
695 */
696 int
697buf_findsigntype_id(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100698 buf_T *buf, // buffer whose sign we are searching for
699 linenr_T lnum, // line number of sign
700 int typenr) // sign type number
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100701{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200702 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100703
704 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200705 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200706 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200707 // Signs are sorted by line number in the buffer. No need to check
708 // for signs after the specified line number 'lnum'.
709 break;
710
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200711 if (sign->se_lnum == lnum && sign->se_typenr == typenr)
712 return sign->se_id;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200713 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100714
715 return 0;
716}
717
718
719# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
720/*
721 * Return the number of icons on the given line.
722 */
723 int
724buf_signcount(buf_T *buf, linenr_T lnum)
725{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200726 sign_entry_T *sign; // a sign in the signlist
727 int count = 0;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100728
729 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200730 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200731 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200732 // Signs are sorted by line number in the buffer. No need to check
733 // for signs after the specified line number 'lnum'.
734 break;
735
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200736 if (sign->se_lnum == lnum)
737 if (sign_get_image(sign->se_typenr) != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100738 count++;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200739 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100740
741 return count;
742}
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100743# endif // FEAT_SIGN_ICONS
744# endif // FEAT_NETBEANS_INTG
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100745
746/*
747 * Delete signs in group 'group' in buffer "buf". If 'group' is '*', then
748 * delete all the signs.
749 */
750 void
751buf_delete_signs(buf_T *buf, char_u *group)
752{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200753 sign_entry_T *sign;
754 sign_entry_T **lastp; // pointer to pointer to current sign
755 sign_entry_T *next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100756
757 // When deleting the last sign need to redraw the windows to remove the
758 // sign column. Not when curwin is NULL (this means we're exiting).
759 if (buf->b_signlist != NULL && curwin != NULL)
760 {
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100761 redraw_buf_later(buf, UPD_NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200762 changed_line_abv_curs();
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100763 }
764
765 lastp = &buf->b_signlist;
766 for (sign = buf->b_signlist; sign != NULL; sign = next)
767 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200768 next = sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100769 if (sign_in_group(sign, group))
770 {
771 *lastp = next;
772 if (next != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200773 next->se_prev = sign->se_prev;
774 if (sign->se_group != NULL)
775 sign_group_unref(sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100776 vim_free(sign);
777 }
778 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200779 lastp = &sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100780 }
781}
782
783/*
784 * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers.
785 */
786 static void
787sign_list_placed(buf_T *rbuf, char_u *sign_group)
788{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200789 buf_T *buf;
790 sign_entry_T *sign;
791 char lbuf[MSG_BUF_LEN];
792 char group[MSG_BUF_LEN];
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100793
Bram Moolenaar32526b32019-01-19 17:43:09 +0100794 msg_puts_title(_("\n--- Signs ---"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100795 msg_putchar('\n');
796 if (rbuf == NULL)
797 buf = firstbuf;
798 else
799 buf = rbuf;
800 while (buf != NULL && !got_int)
801 {
802 if (buf->b_signlist != NULL)
803 {
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100804 vim_snprintf(lbuf, MSG_BUF_LEN, _("Signs for %s:"), buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100805 msg_puts_attr(lbuf, HL_ATTR(HLF_D));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100806 msg_putchar('\n');
807 }
808 FOR_ALL_SIGNS_IN_BUF(buf, sign)
809 {
810 if (got_int)
811 break;
812 if (!sign_in_group(sign, sign_group))
813 continue;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200814 if (sign->se_group != NULL)
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100815 vim_snprintf(group, MSG_BUF_LEN, _(" group=%s"),
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200816 sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100817 else
818 group[0] = '\0';
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100819 vim_snprintf(lbuf, MSG_BUF_LEN,
820 _(" line=%ld id=%d%s name=%s priority=%d"),
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200821 (long)sign->se_lnum, sign->se_id, group,
822 sign_typenr2name(sign->se_typenr), sign->se_priority);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100823 msg_puts(lbuf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100824 msg_putchar('\n');
825 }
826 if (rbuf != NULL)
827 break;
828 buf = buf->b_next;
829 }
830}
831
832/*
833 * Adjust a placed sign for inserted/deleted lines.
834 */
835 void
836sign_mark_adjust(
837 linenr_T line1,
838 linenr_T line2,
839 long amount,
840 long amount_after)
841{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200842 sign_entry_T *sign; // a sign in a b_signlist
843 linenr_T new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100844
845 FOR_ALL_SIGNS_IN_BUF(curbuf, sign)
846 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100847 // Ignore changes to lines after the sign
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200848 if (sign->se_lnum < line1)
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100849 continue;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200850 new_lnum = sign->se_lnum;
Bram Moolenaarfe154992022-03-22 20:42:12 +0000851 if (sign->se_lnum <= line2)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100852 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100853 if (amount != MAXLNUM)
854 new_lnum += amount;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100855 }
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200856 else if (sign->se_lnum > line2)
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100857 // Lines inserted or deleted before the sign
858 new_lnum += amount_after;
859
860 // If the new sign line number is past the last line in the buffer,
861 // then don't adjust the line number. Otherwise, it will always be past
862 // the last line and will not be visible.
863 if (new_lnum <= curbuf->b_ml.ml_line_count)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200864 sign->se_lnum = new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100865 }
866}
867
868/*
869 * Find index of a ":sign" subcmd from its name.
870 * "*end_cmd" must be writable.
871 */
872 static int
873sign_cmd_idx(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100874 char_u *begin_cmd, // begin of sign subcmd
875 char_u *end_cmd) // just after sign subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100876{
877 int idx;
878 char save = *end_cmd;
879
880 *end_cmd = NUL;
881 for (idx = 0; ; ++idx)
882 if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0)
883 break;
884 *end_cmd = save;
885 return idx;
886}
887
888/*
889 * Find a sign by name. Also returns pointer to the previous sign.
890 */
891 static sign_T *
892sign_find(char_u *name, sign_T **sp_prev)
893{
894 sign_T *sp;
895
896 if (sp_prev != NULL)
897 *sp_prev = NULL;
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200898 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100899 {
900 if (STRCMP(sp->sn_name, name) == 0)
901 break;
902 if (sp_prev != NULL)
903 *sp_prev = sp;
904 }
905
906 return sp;
907}
908
909/*
Bram Moolenaar03142362019-01-18 22:01:42 +0100910 * Allocate a new sign
911 */
912 static sign_T *
913alloc_new_sign(char_u *name)
914{
915 sign_T *sp;
916 sign_T *lp;
917 int start = next_sign_typenr;
918
919 // Allocate a new sign.
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200920 sp = alloc_clear_id(sizeof(sign_T), aid_sign_define_by_name);
Bram Moolenaar03142362019-01-18 22:01:42 +0100921 if (sp == NULL)
922 return NULL;
923
924 // Check that next_sign_typenr is not already being used.
925 // This only happens after wrapping around. Hopefully
926 // another one got deleted and we can use its number.
927 for (lp = first_sign; lp != NULL; )
928 {
929 if (lp->sn_typenr == next_sign_typenr)
930 {
931 ++next_sign_typenr;
932 if (next_sign_typenr == MAX_TYPENR)
933 next_sign_typenr = 1;
934 if (next_sign_typenr == start)
935 {
936 vim_free(sp);
Bram Moolenaard88be5b2022-01-04 19:57:55 +0000937 emsg(_(e_too_many_signs_defined));
Bram Moolenaar03142362019-01-18 22:01:42 +0100938 return NULL;
939 }
940 lp = first_sign; // start all over
941 continue;
942 }
943 lp = lp->sn_next;
944 }
945
946 sp->sn_typenr = next_sign_typenr;
947 if (++next_sign_typenr == MAX_TYPENR)
948 next_sign_typenr = 1; // wrap around
949
950 sp->sn_name = vim_strsave(name);
951 if (sp->sn_name == NULL) // out of memory
952 {
953 vim_free(sp);
954 return NULL;
955 }
956
957 return sp;
958}
959
960/*
961 * Initialize the icon information for a new sign
962 */
963 static void
964sign_define_init_icon(sign_T *sp, char_u *icon)
965{
966 vim_free(sp->sn_icon);
967 sp->sn_icon = vim_strsave(icon);
968 backslash_halve(sp->sn_icon);
969# ifdef FEAT_SIGN_ICONS
970 if (gui.in_use)
971 {
972 out_flush();
973 if (sp->sn_image != NULL)
974 gui_mch_destroy_sign(sp->sn_image);
975 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
976 }
977# endif
978}
979
980/*
981 * Initialize the text for a new sign
982 */
983 static int
984sign_define_init_text(sign_T *sp, char_u *text)
985{
986 char_u *s;
987 char_u *endp;
988 int cells;
989 int len;
990
991 endp = text + (int)STRLEN(text);
992
993 // Remove backslashes so that it is possible to use a space.
994 for (s = text; s + 1 < endp; ++s)
995 if (*s == '\\')
996 {
997 STRMOVE(s, s + 1);
998 --endp;
999 }
1000
1001 // Count cells and check for non-printable chars
Bram Moolenaar03142362019-01-18 22:01:42 +01001002 if (has_mbyte)
1003 {
1004 cells = 0;
1005 for (s = text; s < endp; s += (*mb_ptr2len)(s))
1006 {
1007 if (!vim_isprintc((*mb_ptr2char)(s)))
1008 break;
1009 cells += (*mb_ptr2cells)(s);
1010 }
1011 }
1012 else
Bram Moolenaar03142362019-01-18 22:01:42 +01001013 {
1014 for (s = text; s < endp; ++s)
1015 if (!vim_isprintc(*s))
1016 break;
1017 cells = (int)(s - text);
1018 }
1019
1020 // Currently sign text must be one or two display cells
1021 if (s != endp || cells < 1 || cells > 2)
1022 {
Bram Moolenaarcbadefe2022-01-01 19:33:50 +00001023 semsg(_(e_invalid_sign_text_str), text);
Bram Moolenaar03142362019-01-18 22:01:42 +01001024 return FAIL;
1025 }
1026
1027 vim_free(sp->sn_text);
1028 // Allocate one byte more if we need to pad up
1029 // with a space.
1030 len = (int)(endp - text + ((cells == 1) ? 1 : 0));
1031 sp->sn_text = vim_strnsave(text, len);
1032
1033 // For single character sign text, pad with a space.
1034 if (sp->sn_text != NULL && cells == 1)
1035 STRCPY(sp->sn_text + len - 1, " ");
1036
1037 return OK;
1038}
1039
1040/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001041 * Define a new sign or update an existing sign
1042 */
1043 int
1044sign_define_by_name(
1045 char_u *name,
1046 char_u *icon,
1047 char_u *linehl,
1048 char_u *text,
Bram Moolenaare413ea02021-11-24 16:20:13 +00001049 char_u *texthl,
James McCoya80aad72021-12-22 19:45:28 +00001050 char_u *culhl,
LemonBoyb975ddf2024-07-06 18:04:09 +02001051 char_u *numhl,
1052 int prio)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001053{
1054 sign_T *sp_prev;
1055 sign_T *sp;
1056
1057 sp = sign_find(name, &sp_prev);
1058 if (sp == NULL)
1059 {
Bram Moolenaar03142362019-01-18 22:01:42 +01001060 sp = alloc_new_sign(name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001061 if (sp == NULL)
1062 return FAIL;
1063
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001064 // add the new sign to the list of signs
1065 if (sp_prev == NULL)
1066 first_sign = sp;
1067 else
1068 sp_prev->sn_next = sp;
1069 }
Bram Moolenaarbf0acff2020-01-09 21:01:59 +01001070 else
1071 {
1072 win_T *wp;
1073
1074 // Signs may already exist, a redraw is needed in windows with a
1075 // non-empty sign list.
1076 FOR_ALL_WINDOWS(wp)
1077 if (wp->w_buffer->b_signlist != NULL)
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001078 redraw_buf_later(wp->w_buffer, UPD_NOT_VALID);
Bram Moolenaarbf0acff2020-01-09 21:01:59 +01001079 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001080
1081 // set values for a defined sign.
1082 if (icon != NULL)
Bram Moolenaar03142362019-01-18 22:01:42 +01001083 sign_define_init_icon(sp, icon);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001084
Bram Moolenaar03142362019-01-18 22:01:42 +01001085 if (text != NULL && (sign_define_init_text(sp, text) == FAIL))
1086 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001087
LemonBoyb975ddf2024-07-06 18:04:09 +02001088 sp->sn_priority = prio;
1089
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001090 if (linehl != NULL)
Bram Moolenaar0bac5fc2021-12-05 17:45:49 +00001091 {
1092 if (*linehl == NUL)
1093 sp->sn_line_hl = 0;
1094 else
1095 sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl));
1096 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001097
1098 if (texthl != NULL)
Bram Moolenaar0bac5fc2021-12-05 17:45:49 +00001099 {
1100 if (*texthl == NUL)
1101 sp->sn_text_hl = 0;
1102 else
1103 sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl));
1104 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001105
Bram Moolenaare413ea02021-11-24 16:20:13 +00001106 if (culhl != NULL)
Bram Moolenaar0bac5fc2021-12-05 17:45:49 +00001107 {
1108 if (*culhl == NUL)
1109 sp->sn_cul_hl = 0;
1110 else
1111 sp->sn_cul_hl = syn_check_group(culhl, (int)STRLEN(culhl));
1112 }
Bram Moolenaare413ea02021-11-24 16:20:13 +00001113
James McCoya80aad72021-12-22 19:45:28 +00001114 if (numhl != NULL)
1115 {
1116 if (*numhl == NUL)
1117 sp->sn_num_hl = 0;
1118 else
1119 sp->sn_num_hl = syn_check_group(numhl, (int)STRLEN(numhl));
1120 }
1121
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001122 return OK;
1123}
1124
1125/*
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001126 * Return TRUE if sign "name" exists.
1127 */
1128 int
1129sign_exists_by_name(char_u *name)
1130{
1131 return sign_find(name, NULL) != NULL;
1132}
1133
1134/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001135 * Free the sign specified by 'name'.
1136 */
1137 int
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001138sign_undefine_by_name(char_u *name, int give_error)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001139{
1140 sign_T *sp_prev;
1141 sign_T *sp;
1142
1143 sp = sign_find(name, &sp_prev);
1144 if (sp == NULL)
1145 {
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001146 if (give_error)
Bram Moolenaareb822a22021-12-31 15:09:27 +00001147 semsg(_(e_unknown_sign_str), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001148 return FAIL;
1149 }
1150 sign_undefine(sp, sp_prev);
1151
1152 return OK;
1153}
1154
1155/*
1156 * List the signs matching 'name'
1157 */
1158 static void
1159sign_list_by_name(char_u *name)
1160{
1161 sign_T *sp;
1162
1163 sp = sign_find(name, NULL);
1164 if (sp != NULL)
1165 sign_list_defined(sp);
1166 else
Bram Moolenaareb822a22021-12-31 15:09:27 +00001167 semsg(_(e_unknown_sign_str), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001168}
1169
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001170 static void
1171may_force_numberwidth_recompute(buf_T *buf, int unplace)
1172{
1173 tabpage_T *tp;
1174 win_T *wp;
1175
1176 FOR_ALL_TAB_WINDOWS(tp, wp)
1177 if (wp->w_buffer == buf
1178 && (wp->w_p_nu || wp->w_p_rnu)
1179 && (unplace || wp->w_nrwidth_width < 2)
1180 && (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u'))
1181 wp->w_nrwidth_line_count = 0;
1182}
1183
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001184/*
Bram Moolenaar8144acb2019-01-14 23:08:18 +01001185 * Place a sign at the specified file location or update a sign.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001186 */
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001187 int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001188sign_place(
1189 int *sign_id,
1190 char_u *sign_group,
1191 char_u *sign_name,
1192 buf_T *buf,
1193 linenr_T lnum,
1194 int prio)
1195{
1196 sign_T *sp;
1197
1198 // Check for reserved character '*' in group name
1199 if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0'))
1200 return FAIL;
1201
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001202 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001203 if (STRCMP(sp->sn_name, sign_name) == 0)
1204 break;
1205 if (sp == NULL)
1206 {
Bram Moolenaareb822a22021-12-31 15:09:27 +00001207 semsg(_(e_unknown_sign_str), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001208 return FAIL;
1209 }
1210 if (*sign_id == 0)
1211 *sign_id = sign_group_get_next_signid(buf, sign_group);
1212
LemonBoyb975ddf2024-07-06 18:04:09 +02001213 // Use the default priority value for this sign.
1214 if (prio == -1)
1215 prio = (sp->sn_priority != -1) ? sp->sn_priority : SIGN_DEF_PRIO;
1216
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001217 if (lnum > 0)
1218 // ":sign place {id} line={lnum} name={name} file={fname}":
1219 // place a sign
1220 buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr);
1221 else
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02001222 // ":sign place {id} file={fname}": change sign type and/or priority
1223 lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr,
1224 prio);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001225 if (lnum > 0)
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001226 {
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001227 redraw_buf_line_later(buf, lnum);
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001228
1229 // When displaying signs in the 'number' column, if the width of the
1230 // number column is less than 2, then force recomputing the width.
1231 may_force_numberwidth_recompute(buf, FALSE);
1232 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001233 else
1234 {
Bram Moolenaard82a47d2022-01-05 20:24:39 +00001235 semsg(_(e_not_possible_to_change_sign_str), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001236 return FAIL;
1237 }
1238
1239 return OK;
1240}
1241
1242/*
1243 * Unplace the specified sign
1244 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001245 static int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001246sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum)
1247{
1248 if (buf->b_signlist == NULL) // No signs in the buffer
1249 return OK;
1250
1251 if (sign_id == 0)
1252 {
1253 // Delete all the signs in the specified buffer
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001254 redraw_buf_later(buf, UPD_NOT_VALID);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001255 buf_delete_signs(buf, sign_group);
1256 }
1257 else
1258 {
1259 linenr_T lnum;
1260
1261 // Delete only the specified signs
1262 lnum = buf_delsign(buf, atlnum, sign_id, sign_group);
1263 if (lnum == 0)
1264 return FAIL;
1265 }
1266
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001267 // When all the signs in a buffer are removed, force recomputing the
1268 // number column width (if enabled) in all the windows displaying the
1269 // buffer if 'signcolumn' is set to 'number' in that window.
1270 if (buf->b_signlist == NULL)
1271 may_force_numberwidth_recompute(buf, TRUE);
1272
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001273 return OK;
1274}
1275
1276/*
1277 * Unplace the sign at the current cursor line.
1278 */
1279 static void
1280sign_unplace_at_cursor(char_u *groupname)
1281{
1282 int id = -1;
1283
1284 id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname);
1285 if (id > 0)
1286 sign_unplace(id, groupname, curwin->w_buffer, curwin->w_cursor.lnum);
1287 else
Bram Moolenaareb822a22021-12-31 15:09:27 +00001288 emsg(_(e_missing_sign_number));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001289}
1290
1291/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001292 * Jump to a sign.
1293 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001294 static linenr_T
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001295sign_jump(int sign_id, char_u *sign_group, buf_T *buf)
1296{
1297 linenr_T lnum;
1298
1299 if ((lnum = buf_findsign(buf, sign_id, sign_group)) <= 0)
1300 {
Bram Moolenaareb822a22021-12-31 15:09:27 +00001301 semsg(_(e_invalid_sign_id_nr), sign_id);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001302 return -1;
1303 }
1304
1305 // goto a sign ...
1306 if (buf_jump_open_win(buf) != NULL)
1307 { // ... in a current window
1308 curwin->w_cursor.lnum = lnum;
1309 check_cursor_lnum();
1310 beginline(BL_WHITE);
1311 }
1312 else
1313 { // ... not currently in a window
1314 char_u *cmd;
1315
1316 if (buf->b_fname == NULL)
1317 {
Bram Moolenaard82a47d2022-01-05 20:24:39 +00001318 emsg(_(e_cannot_jump_to_buffer_that_does_not_have_name));
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001319 return -1;
1320 }
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02001321 cmd = alloc(STRLEN(buf->b_fname) + 25);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001322 if (cmd == NULL)
1323 return -1;
1324 sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname);
1325 do_cmdline_cmd(cmd);
1326 vim_free(cmd);
1327 }
1328# ifdef FEAT_FOLDING
1329 foldOpenCursor();
1330# endif
1331
1332 return lnum;
1333}
1334
1335/*
1336 * ":sign define {name} ..." command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001337 */
1338 static void
1339sign_define_cmd(char_u *sign_name, char_u *cmdline)
1340{
1341 char_u *arg;
1342 char_u *p = cmdline;
1343 char_u *icon = NULL;
1344 char_u *text = NULL;
1345 char_u *linehl = NULL;
1346 char_u *texthl = NULL;
Bram Moolenaare413ea02021-11-24 16:20:13 +00001347 char_u *culhl = NULL;
James McCoya80aad72021-12-22 19:45:28 +00001348 char_u *numhl = NULL;
LemonBoyb975ddf2024-07-06 18:04:09 +02001349 int prio = -1;
Bram Moolenaar0bac5fc2021-12-05 17:45:49 +00001350 int failed = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001351
1352 // set values for a defined sign.
1353 for (;;)
1354 {
1355 arg = skipwhite(p);
1356 if (*arg == NUL)
1357 break;
1358 p = skiptowhite_esc(arg);
1359 if (STRNCMP(arg, "icon=", 5) == 0)
1360 {
1361 arg += 5;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001362 icon = vim_strnsave(arg, p - arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001363 }
1364 else if (STRNCMP(arg, "text=", 5) == 0)
1365 {
1366 arg += 5;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001367 text = vim_strnsave(arg, p - arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001368 }
1369 else if (STRNCMP(arg, "linehl=", 7) == 0)
1370 {
1371 arg += 7;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001372 linehl = vim_strnsave(arg, p - arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001373 }
1374 else if (STRNCMP(arg, "texthl=", 7) == 0)
1375 {
1376 arg += 7;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001377 texthl = vim_strnsave(arg, p - arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001378 }
Bram Moolenaare413ea02021-11-24 16:20:13 +00001379 else if (STRNCMP(arg, "culhl=", 6) == 0)
1380 {
1381 arg += 6;
1382 culhl = vim_strnsave(arg, p - arg);
1383 }
James McCoya80aad72021-12-22 19:45:28 +00001384 else if (STRNCMP(arg, "numhl=", 6) == 0)
1385 {
1386 arg += 6;
1387 numhl = vim_strnsave(arg, p - arg);
1388 }
LemonBoyb975ddf2024-07-06 18:04:09 +02001389 else if (STRNCMP(arg, "priority=", 9) == 0)
1390 {
1391 arg += 9;
1392 prio = atoi((char *)arg);
LemonBoyb975ddf2024-07-06 18:04:09 +02001393 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001394 else
1395 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001396 semsg(_(e_invalid_argument_str), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001397 failed = TRUE;
1398 break;
1399 }
1400 }
1401
1402 if (!failed)
LemonBoyb975ddf2024-07-06 18:04:09 +02001403 sign_define_by_name(sign_name, icon, linehl, text, texthl, culhl, numhl, prio);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001404
1405 vim_free(icon);
1406 vim_free(text);
1407 vim_free(linehl);
1408 vim_free(texthl);
Bram Moolenaare413ea02021-11-24 16:20:13 +00001409 vim_free(culhl);
James McCoya80aad72021-12-22 19:45:28 +00001410 vim_free(numhl);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001411}
1412
1413/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001414 * ":sign place" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001415 */
1416 static void
1417sign_place_cmd(
1418 buf_T *buf,
1419 linenr_T lnum,
1420 char_u *sign_name,
1421 int id,
1422 char_u *group,
1423 int prio)
1424{
1425 if (id <= 0)
1426 {
1427 // List signs placed in a file/buffer
1428 // :sign place file={fname}
1429 // :sign place group={group} file={fname}
1430 // :sign place group=* file={fname}
1431 // :sign place buffer={nr}
1432 // :sign place group={group} buffer={nr}
1433 // :sign place group=* buffer={nr}
1434 // :sign place
1435 // :sign place group={group}
1436 // :sign place group=*
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001437 if (lnum >= 0 || sign_name != NULL
1438 || (group != NULL && *group == '\0'))
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001439 emsg(_(e_invalid_argument));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001440 else
1441 sign_list_placed(buf, group);
1442 }
1443 else
1444 {
1445 // Place a new sign
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001446 if (sign_name == NULL || buf == NULL
1447 || (group != NULL && *group == '\0'))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001448 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001449 emsg(_(e_invalid_argument));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001450 return;
1451 }
1452
1453 sign_place(&id, group, sign_name, buf, lnum, prio);
1454 }
1455}
1456
1457/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001458 * ":sign unplace" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001459 */
1460 static void
1461sign_unplace_cmd(
1462 buf_T *buf,
1463 linenr_T lnum,
1464 char_u *sign_name,
1465 int id,
1466 char_u *group)
1467{
1468 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0'))
1469 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001470 emsg(_(e_invalid_argument));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001471 return;
1472 }
1473
1474 if (id == -2)
1475 {
1476 if (buf != NULL)
1477 // :sign unplace * file={fname}
1478 // :sign unplace * group={group} file={fname}
1479 // :sign unplace * group=* file={fname}
1480 // :sign unplace * buffer={nr}
1481 // :sign unplace * group={group} buffer={nr}
1482 // :sign unplace * group=* buffer={nr}
1483 sign_unplace(0, group, buf, 0);
1484 else
1485 // :sign unplace *
1486 // :sign unplace * group={group}
1487 // :sign unplace * group=*
1488 FOR_ALL_BUFFERS(buf)
1489 if (buf->b_signlist != NULL)
1490 buf_delete_signs(buf, group);
1491 }
1492 else
1493 {
1494 if (buf != NULL)
1495 // :sign unplace {id} file={fname}
1496 // :sign unplace {id} group={group} file={fname}
1497 // :sign unplace {id} group=* file={fname}
1498 // :sign unplace {id} buffer={nr}
1499 // :sign unplace {id} group={group} buffer={nr}
1500 // :sign unplace {id} group=* buffer={nr}
1501 sign_unplace(id, group, buf, 0);
1502 else
1503 {
1504 if (id == -1)
1505 {
1506 // :sign unplace group={group}
1507 // :sign unplace group=*
1508 sign_unplace_at_cursor(group);
1509 }
1510 else
1511 {
1512 // :sign unplace {id}
1513 // :sign unplace {id} group={group}
1514 // :sign unplace {id} group=*
1515 FOR_ALL_BUFFERS(buf)
1516 sign_unplace(id, group, buf, 0);
1517 }
1518 }
1519 }
1520}
1521
1522/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001523 * Jump to a placed sign commands:
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001524 * :sign jump {id} file={fname}
1525 * :sign jump {id} buffer={nr}
1526 * :sign jump {id} group={group} file={fname}
1527 * :sign jump {id} group={group} buffer={nr}
1528 */
1529 static void
1530sign_jump_cmd(
1531 buf_T *buf,
1532 linenr_T lnum,
1533 char_u *sign_name,
1534 int id,
1535 char_u *group)
1536{
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001537 if (sign_name == NULL && group == NULL && id == -1)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001538 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001539 emsg(_(e_argument_required));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001540 return;
1541 }
1542
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001543 if (buf == NULL || (group != NULL && *group == '\0')
1544 || lnum >= 0 || sign_name != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001545 {
1546 // File or buffer is not specified or an empty group is used
1547 // or a line number or a sign name is specified.
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001548 emsg(_(e_invalid_argument));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001549 return;
1550 }
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001551 (void)sign_jump(id, group, buf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001552}
1553
1554/*
1555 * Parse the command line arguments for the ":sign place", ":sign unplace" and
1556 * ":sign jump" commands.
1557 * The supported arguments are: line={lnum} name={name} group={group}
1558 * priority={prio} and file={fname} or buffer={nr}.
1559 */
1560 static int
1561parse_sign_cmd_args(
1562 int cmd,
1563 char_u *arg,
1564 char_u **sign_name,
1565 int *signid,
1566 char_u **group,
1567 int *prio,
1568 buf_T **buf,
1569 linenr_T *lnum)
1570{
1571 char_u *arg1;
1572 char_u *name;
1573 char_u *filename = NULL;
Bram Moolenaarb589f952019-01-07 22:10:00 +01001574 int lnum_arg = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001575
1576 // first arg could be placed sign id
1577 arg1 = arg;
1578 if (VIM_ISDIGIT(*arg))
1579 {
1580 *signid = getdigits(&arg);
1581 if (!VIM_ISWHITE(*arg) && *arg != NUL)
1582 {
1583 *signid = -1;
1584 arg = arg1;
1585 }
1586 else
1587 arg = skipwhite(arg);
1588 }
1589
1590 while (*arg != NUL)
1591 {
1592 if (STRNCMP(arg, "line=", 5) == 0)
1593 {
1594 arg += 5;
1595 *lnum = atoi((char *)arg);
1596 arg = skiptowhite(arg);
Bram Moolenaarb589f952019-01-07 22:10:00 +01001597 lnum_arg = TRUE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001598 }
1599 else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE)
1600 {
1601 if (*signid != -1)
1602 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001603 emsg(_(e_invalid_argument));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001604 return FAIL;
1605 }
1606 *signid = -2;
1607 arg = skiptowhite(arg + 1);
1608 }
1609 else if (STRNCMP(arg, "name=", 5) == 0)
1610 {
1611 arg += 5;
1612 name = arg;
1613 arg = skiptowhite(arg);
1614 if (*arg != NUL)
1615 *arg++ = NUL;
1616 while (name[0] == '0' && name[1] != NUL)
1617 ++name;
1618 *sign_name = name;
1619 }
1620 else if (STRNCMP(arg, "group=", 6) == 0)
1621 {
1622 arg += 6;
1623 *group = arg;
1624 arg = skiptowhite(arg);
1625 if (*arg != NUL)
1626 *arg++ = NUL;
1627 }
1628 else if (STRNCMP(arg, "priority=", 9) == 0)
1629 {
1630 arg += 9;
1631 *prio = atoi((char *)arg);
1632 arg = skiptowhite(arg);
1633 }
1634 else if (STRNCMP(arg, "file=", 5) == 0)
1635 {
1636 arg += 5;
1637 filename = arg;
1638 *buf = buflist_findname_exp(arg);
1639 break;
1640 }
1641 else if (STRNCMP(arg, "buffer=", 7) == 0)
1642 {
1643 arg += 7;
1644 filename = arg;
1645 *buf = buflist_findnr((int)getdigits(&arg));
1646 if (*skipwhite(arg) != NUL)
Bram Moolenaar74409f62022-01-01 15:58:22 +00001647 semsg(_(e_trailing_characters_str), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001648 break;
1649 }
1650 else
1651 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001652 emsg(_(e_invalid_argument));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001653 return FAIL;
1654 }
1655 arg = skipwhite(arg);
1656 }
1657
1658 if (filename != NULL && *buf == NULL)
1659 {
Bram Moolenaareb822a22021-12-31 15:09:27 +00001660 semsg(_(e_invalid_buffer_name_str), filename);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001661 return FAIL;
1662 }
1663
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001664 // If the filename is not supplied for the sign place or the sign jump
1665 // command, then use the current buffer.
Bram Moolenaarb589f952019-01-07 22:10:00 +01001666 if (filename == NULL && ((cmd == SIGNCMD_PLACE && lnum_arg)
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001667 || cmd == SIGNCMD_JUMP))
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001668 *buf = curwin->w_buffer;
1669
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001670 return OK;
1671}
1672
1673/*
1674 * ":sign" command
1675 */
1676 void
1677ex_sign(exarg_T *eap)
1678{
1679 char_u *arg = eap->arg;
1680 char_u *p;
1681 int idx;
1682 sign_T *sp;
1683 buf_T *buf = NULL;
1684
1685 // Parse the subcommand.
1686 p = skiptowhite(arg);
1687 idx = sign_cmd_idx(arg, p);
1688 if (idx == SIGNCMD_LAST)
1689 {
Bram Moolenaareb822a22021-12-31 15:09:27 +00001690 semsg(_(e_unknown_sign_command_str), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001691 return;
1692 }
1693 arg = skipwhite(p);
1694
1695 if (idx <= SIGNCMD_LIST)
1696 {
1697 // Define, undefine or list signs.
1698 if (idx == SIGNCMD_LIST && *arg == NUL)
1699 {
1700 // ":sign list": list all defined signs
1701 for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next)
1702 sign_list_defined(sp);
1703 }
1704 else if (*arg == NUL)
Bram Moolenaareb822a22021-12-31 15:09:27 +00001705 emsg(_(e_missing_sign_name));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001706 else
1707 {
1708 char_u *name;
1709
1710 // Isolate the sign name. If it's a number skip leading zeroes,
1711 // so that "099" and "99" are the same sign. But keep "0".
1712 p = skiptowhite(arg);
1713 if (*p != NUL)
1714 *p++ = NUL;
1715 while (arg[0] == '0' && arg[1] != NUL)
1716 ++arg;
1717 name = vim_strsave(arg);
1718
1719 if (idx == SIGNCMD_DEFINE)
1720 sign_define_cmd(name, p);
1721 else if (idx == SIGNCMD_LIST)
1722 // ":sign list {name}"
1723 sign_list_by_name(name);
1724 else
1725 // ":sign undefine {name}"
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001726 sign_undefine_by_name(name, TRUE);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001727
1728 vim_free(name);
1729 return;
1730 }
1731 }
1732 else
1733 {
1734 int id = -1;
1735 linenr_T lnum = -1;
1736 char_u *sign_name = NULL;
1737 char_u *group = NULL;
LemonBoyb975ddf2024-07-06 18:04:09 +02001738 int prio = -1;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001739
1740 // Parse command line arguments
1741 if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio,
1742 &buf, &lnum) == FAIL)
1743 return;
1744
1745 if (idx == SIGNCMD_PLACE)
1746 sign_place_cmd(buf, lnum, sign_name, id, group, prio);
1747 else if (idx == SIGNCMD_UNPLACE)
1748 sign_unplace_cmd(buf, lnum, sign_name, id, group);
1749 else if (idx == SIGNCMD_JUMP)
1750 sign_jump_cmd(buf, lnum, sign_name, id, group);
1751 }
1752}
1753
1754/*
1755 * Return information about a specified sign
1756 */
1757 static void
1758sign_getinfo(sign_T *sp, dict_T *retdict)
1759{
1760 char_u *p;
1761
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00001762 dict_add_string(retdict, "name", sp->sn_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001763 if (sp->sn_icon != NULL)
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00001764 dict_add_string(retdict, "icon", sp->sn_icon);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001765 if (sp->sn_text != NULL)
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00001766 dict_add_string(retdict, "text", sp->sn_text);
LemonBoyb975ddf2024-07-06 18:04:09 +02001767 if (sp->sn_priority > 0)
1768 dict_add_number(retdict, "priority", sp->sn_priority);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001769 if (sp->sn_line_hl > 0)
1770 {
1771 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1772 if (p == NULL)
1773 p = (char_u *)"NONE";
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00001774 dict_add_string(retdict, "linehl", p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001775 }
1776 if (sp->sn_text_hl > 0)
1777 {
1778 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1779 if (p == NULL)
1780 p = (char_u *)"NONE";
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00001781 dict_add_string(retdict, "texthl", p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001782 }
Bram Moolenaare413ea02021-11-24 16:20:13 +00001783 if (sp->sn_cul_hl > 0)
1784 {
1785 p = get_highlight_name_ext(NULL, sp->sn_cul_hl - 1, FALSE);
1786 if (p == NULL)
1787 p = (char_u *)"NONE";
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00001788 dict_add_string(retdict, "culhl", p);
Bram Moolenaare413ea02021-11-24 16:20:13 +00001789 }
James McCoya80aad72021-12-22 19:45:28 +00001790 if (sp->sn_num_hl > 0)
1791 {
1792 p = get_highlight_name_ext(NULL, sp->sn_num_hl - 1, FALSE);
1793 if (p == NULL)
1794 p = (char_u *)"NONE";
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00001795 dict_add_string(retdict, "numhl", p);
James McCoya80aad72021-12-22 19:45:28 +00001796 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001797}
1798
1799/*
1800 * If 'name' is NULL, return a list of all the defined signs.
1801 * Otherwise, return information about the specified sign.
1802 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001803 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001804sign_getlist(char_u *name, list_T *retlist)
1805{
1806 sign_T *sp = first_sign;
1807 dict_T *dict;
1808
1809 if (name != NULL)
1810 {
1811 sp = sign_find(name, NULL);
1812 if (sp == NULL)
1813 return;
1814 }
1815
1816 for (; sp != NULL && !got_int; sp = sp->sn_next)
1817 {
1818 if ((dict = dict_alloc_id(aid_sign_getlist)) == NULL)
1819 return;
1820 if (list_append_dict(retlist, dict) == FAIL)
1821 return;
1822 sign_getinfo(sp, dict);
1823
1824 if (name != NULL) // handle only the specified sign
1825 break;
1826 }
1827}
1828
1829/*
1830 * Returns information about signs placed in a buffer as list of dicts.
1831 */
1832 void
1833get_buffer_signs(buf_T *buf, list_T *l)
1834{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001835 sign_entry_T *sign;
1836 dict_T *d;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001837
1838 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1839 {
1840 if ((d = sign_get_info(sign)) != NULL)
1841 list_append_dict(l, d);
1842 }
1843}
1844
1845/*
1846 * Return information about all the signs placed in a buffer
1847 */
1848 static void
1849sign_get_placed_in_buf(
1850 buf_T *buf,
1851 linenr_T lnum,
1852 int sign_id,
1853 char_u *sign_group,
1854 list_T *retlist)
1855{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001856 dict_T *d;
1857 list_T *l;
1858 sign_entry_T *sign;
1859 dict_T *sdict;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001860
1861 if ((d = dict_alloc_id(aid_sign_getplaced_dict)) == NULL)
1862 return;
1863 list_append_dict(retlist, d);
1864
1865 dict_add_number(d, "bufnr", (long)buf->b_fnum);
1866
1867 if ((l = list_alloc_id(aid_sign_getplaced_list)) == NULL)
1868 return;
1869 dict_add_list(d, "signs", l);
1870
1871 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1872 {
1873 if (!sign_in_group(sign, sign_group))
1874 continue;
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001875 if ((lnum == 0 && sign_id == 0)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001876 || (sign_id == 0 && lnum == sign->se_lnum)
1877 || (lnum == 0 && sign_id == sign->se_id)
1878 || (lnum == sign->se_lnum && sign_id == sign->se_id))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001879 {
1880 if ((sdict = sign_get_info(sign)) != NULL)
1881 list_append_dict(l, sdict);
1882 }
1883 }
1884}
1885
1886/*
1887 * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the
1888 * sign placed at the line number. If 'lnum' is zero, return all the signs
1889 * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers.
1890 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001891 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001892sign_get_placed(
1893 buf_T *buf,
1894 linenr_T lnum,
1895 int sign_id,
1896 char_u *sign_group,
1897 list_T *retlist)
1898{
1899 if (buf != NULL)
1900 sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist);
1901 else
1902 {
1903 FOR_ALL_BUFFERS(buf)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001904 if (buf->b_signlist != NULL)
1905 sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001906 }
1907}
1908
1909# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1910/*
1911 * Allocate the icons. Called when the GUI has started. Allows defining
1912 * signs before it starts.
1913 */
1914 void
1915sign_gui_started(void)
1916{
1917 sign_T *sp;
1918
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001919 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001920 if (sp->sn_icon != NULL)
1921 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
1922}
1923# endif
1924
1925/*
1926 * List one sign.
1927 */
1928 static void
1929sign_list_defined(sign_T *sp)
1930{
1931 char_u *p;
LemonBoyb975ddf2024-07-06 18:04:09 +02001932 char lbuf[MSG_BUF_LEN];
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001933
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001934 smsg("sign %s", sp->sn_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001935 if (sp->sn_icon != NULL)
1936 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001937 msg_puts(" icon=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001938 msg_outtrans(sp->sn_icon);
1939# ifdef FEAT_SIGN_ICONS
1940 if (sp->sn_image == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001941 msg_puts(_(" (NOT FOUND)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001942# else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001943 msg_puts(_(" (not supported)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001944# endif
1945 }
1946 if (sp->sn_text != NULL)
1947 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001948 msg_puts(" text=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001949 msg_outtrans(sp->sn_text);
1950 }
LemonBoyb975ddf2024-07-06 18:04:09 +02001951 if (sp->sn_priority > 0)
1952 {
1953 vim_snprintf(lbuf, MSG_BUF_LEN, " priority=%d", sp->sn_priority);
1954 msg_puts(lbuf);
1955 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001956 if (sp->sn_line_hl > 0)
1957 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001958 msg_puts(" linehl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001959 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1960 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001961 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001962 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001963 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001964 }
1965 if (sp->sn_text_hl > 0)
1966 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001967 msg_puts(" texthl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001968 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1969 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001970 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001971 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001972 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001973 }
Bram Moolenaare413ea02021-11-24 16:20:13 +00001974 if (sp->sn_cul_hl > 0)
1975 {
1976 msg_puts(" culhl=");
1977 p = get_highlight_name_ext(NULL, sp->sn_cul_hl - 1, FALSE);
1978 if (p == NULL)
1979 msg_puts("NONE");
1980 else
1981 msg_puts((char *)p);
1982 }
James McCoya80aad72021-12-22 19:45:28 +00001983 if (sp->sn_num_hl > 0)
1984 {
1985 msg_puts(" numhl=");
1986 p = get_highlight_name_ext(NULL, sp->sn_num_hl - 1, FALSE);
1987 if (p == NULL)
1988 msg_puts("NONE");
1989 else
1990 msg_puts((char *)p);
1991 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001992}
1993
1994/*
1995 * Undefine a sign and free its memory.
1996 */
1997 static void
1998sign_undefine(sign_T *sp, sign_T *sp_prev)
1999{
2000 vim_free(sp->sn_name);
2001 vim_free(sp->sn_icon);
2002# ifdef FEAT_SIGN_ICONS
2003 if (sp->sn_image != NULL)
2004 {
2005 out_flush();
2006 gui_mch_destroy_sign(sp->sn_image);
2007 }
2008# endif
2009 vim_free(sp->sn_text);
2010 if (sp_prev == NULL)
2011 first_sign = sp->sn_next;
2012 else
2013 sp_prev->sn_next = sp->sn_next;
2014 vim_free(sp);
2015}
2016
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002017# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
2018 void *
2019sign_get_image(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002020 int typenr) // the attribute which may have a sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002021{
2022 sign_T *sp;
2023
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002024 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002025 if (sp->sn_typenr == typenr)
2026 return sp->sn_image;
2027 return NULL;
2028}
2029# endif
2030
2031/*
2032 * Undefine/free all signs.
2033 */
2034 void
2035free_signs(void)
2036{
2037 while (first_sign != NULL)
2038 sign_undefine(first_sign, NULL);
2039}
2040
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002041static enum
2042{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002043 EXP_SUBCMD, // expand :sign sub-commands
2044 EXP_DEFINE, // expand :sign define {name} args
2045 EXP_PLACE, // expand :sign place {id} args
Bram Moolenaar3678f652019-02-17 14:50:25 +01002046 EXP_LIST, // expand :sign place args
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002047 EXP_UNPLACE, // expand :sign unplace"
Bram Moolenaar3678f652019-02-17 14:50:25 +01002048 EXP_SIGN_NAMES, // expand with name of placed signs
2049 EXP_SIGN_GROUPS // expand with name of placed sign groups
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002050} expand_what;
2051
2052/*
Bram Moolenaar3678f652019-02-17 14:50:25 +01002053 * Return the n'th sign name (used for command line completion)
2054 */
2055 static char_u *
2056get_nth_sign_name(int idx)
2057{
2058 int current_idx;
2059 sign_T *sp;
2060
2061 // Complete with name of signs already defined
2062 current_idx = 0;
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002063 FOR_ALL_SIGNS(sp)
Bram Moolenaar3678f652019-02-17 14:50:25 +01002064 if (current_idx++ == idx)
2065 return sp->sn_name;
2066 return NULL;
2067}
2068
2069/*
2070 * Return the n'th sign group name (used for command line completion)
2071 */
2072 static char_u *
2073get_nth_sign_group_name(int idx)
2074{
2075 int current_idx;
2076 int todo;
2077 hashitem_T *hi;
2078 signgroup_T *group;
2079
2080 // Complete with name of sign groups already defined
2081 current_idx = 0;
2082 todo = (int)sg_table.ht_used;
Yegappan Lakshmanan14113fd2023-03-07 17:13:51 +00002083 FOR_ALL_HASHTAB_ITEMS(&sg_table, hi, todo)
Bram Moolenaar3678f652019-02-17 14:50:25 +01002084 {
2085 if (!HASHITEM_EMPTY(hi))
2086 {
2087 --todo;
2088 if (current_idx++ == idx)
2089 {
2090 group = HI2SG(hi);
2091 return group->sg_name;
2092 }
2093 }
2094 }
2095 return NULL;
2096}
2097
2098/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002099 * Function given to ExpandGeneric() to obtain the sign command
2100 * expansion.
2101 */
2102 char_u *
2103get_sign_name(expand_T *xp UNUSED, int idx)
2104{
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002105 switch (expand_what)
2106 {
2107 case EXP_SUBCMD:
2108 return (char_u *)cmds[idx];
2109 case EXP_DEFINE:
2110 {
2111 char *define_arg[] =
2112 {
LemonBoyb975ddf2024-07-06 18:04:09 +02002113 "culhl=", "icon=", "linehl=", "numhl=", "text=", "texthl=", "priority=",
2114 NULL
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002115 };
2116 return (char_u *)define_arg[idx];
2117 }
2118 case EXP_PLACE:
2119 {
2120 char *place_arg[] =
2121 {
2122 "line=", "name=", "group=", "priority=", "file=",
2123 "buffer=", NULL
2124 };
2125 return (char_u *)place_arg[idx];
2126 }
Bram Moolenaar3678f652019-02-17 14:50:25 +01002127 case EXP_LIST:
2128 {
2129 char *list_arg[] =
2130 {
2131 "group=", "file=", "buffer=", NULL
2132 };
2133 return (char_u *)list_arg[idx];
2134 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002135 case EXP_UNPLACE:
2136 {
2137 char *unplace_arg[] = { "group=", "file=", "buffer=", NULL };
2138 return (char_u *)unplace_arg[idx];
2139 }
2140 case EXP_SIGN_NAMES:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002141 return get_nth_sign_name(idx);
2142 case EXP_SIGN_GROUPS:
2143 return get_nth_sign_group_name(idx);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002144 default:
2145 return NULL;
2146 }
2147}
2148
2149/*
2150 * Handle command line completion for :sign command.
2151 */
2152 void
2153set_context_in_sign_cmd(expand_T *xp, char_u *arg)
2154{
2155 char_u *p;
2156 char_u *end_subcmd;
2157 char_u *last;
2158 int cmd_idx;
2159 char_u *begin_subcmd_args;
2160
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002161 // Default: expand subcommands.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002162 xp->xp_context = EXPAND_SIGN;
2163 expand_what = EXP_SUBCMD;
2164 xp->xp_pattern = arg;
2165
2166 end_subcmd = skiptowhite(arg);
2167 if (*end_subcmd == NUL)
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002168 // expand subcmd name
2169 // :sign {subcmd}<CTRL-D>
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002170 return;
2171
2172 cmd_idx = sign_cmd_idx(arg, end_subcmd);
2173
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002174 // :sign {subcmd} {subcmd_args}
2175 // |
2176 // begin_subcmd_args
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002177 begin_subcmd_args = skipwhite(end_subcmd);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002178
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002179 // expand last argument of subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002180
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002181 // :sign define {name} {args}...
2182 // |
2183 // p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002184
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002185 // Loop until reaching last argument.
Bram Moolenaar3678f652019-02-17 14:50:25 +01002186 p = begin_subcmd_args;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002187 do
2188 {
2189 p = skipwhite(p);
2190 last = p;
2191 p = skiptowhite(p);
2192 } while (*p != NUL);
2193
2194 p = vim_strchr(last, '=');
2195
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002196 // :sign define {name} {args}... {last}=
2197 // | |
2198 // last p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002199 if (p == NULL)
2200 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002201 // Expand last argument name (before equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002202 xp->xp_pattern = last;
2203 switch (cmd_idx)
2204 {
2205 case SIGNCMD_DEFINE:
2206 expand_what = EXP_DEFINE;
2207 break;
2208 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002209 // List placed signs
2210 if (VIM_ISDIGIT(*begin_subcmd_args))
2211 // :sign place {id} {args}...
2212 expand_what = EXP_PLACE;
2213 else
2214 // :sign place {args}...
2215 expand_what = EXP_LIST;
2216 break;
2217 case SIGNCMD_LIST:
2218 case SIGNCMD_UNDEFINE:
2219 // :sign list <CTRL-D>
2220 // :sign undefine <CTRL-D>
2221 expand_what = EXP_SIGN_NAMES;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002222 break;
2223 case SIGNCMD_JUMP:
2224 case SIGNCMD_UNPLACE:
2225 expand_what = EXP_UNPLACE;
2226 break;
2227 default:
2228 xp->xp_context = EXPAND_NOTHING;
2229 }
2230 }
2231 else
2232 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002233 // Expand last argument value (after equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002234 xp->xp_pattern = p + 1;
2235 switch (cmd_idx)
2236 {
2237 case SIGNCMD_DEFINE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002238 if (STRNCMP(last, "texthl", 6) == 0
James McCoya80aad72021-12-22 19:45:28 +00002239 || STRNCMP(last, "linehl", 6) == 0
2240 || STRNCMP(last, "culhl", 5) == 0
2241 || STRNCMP(last, "numhl", 5) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002242 xp->xp_context = EXPAND_HIGHLIGHT;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002243 else if (STRNCMP(last, "icon", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002244 xp->xp_context = EXPAND_FILES;
2245 else
2246 xp->xp_context = EXPAND_NOTHING;
2247 break;
2248 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002249 if (STRNCMP(last, "name", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002250 expand_what = EXP_SIGN_NAMES;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002251 else if (STRNCMP(last, "group", 5) == 0)
2252 expand_what = EXP_SIGN_GROUPS;
2253 else if (STRNCMP(last, "file", 4) == 0)
2254 xp->xp_context = EXPAND_BUFFERS;
2255 else
2256 xp->xp_context = EXPAND_NOTHING;
2257 break;
2258 case SIGNCMD_UNPLACE:
2259 case SIGNCMD_JUMP:
2260 if (STRNCMP(last, "group", 5) == 0)
2261 expand_what = EXP_SIGN_GROUPS;
2262 else if (STRNCMP(last, "file", 4) == 0)
2263 xp->xp_context = EXPAND_BUFFERS;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002264 else
2265 xp->xp_context = EXPAND_NOTHING;
2266 break;
2267 default:
2268 xp->xp_context = EXPAND_NOTHING;
2269 }
2270 }
2271}
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002272
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002273/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002274 * Define a sign using the attributes in 'dict'. Returns 0 on success and -1 on
2275 * failure.
2276 */
2277 static int
2278sign_define_from_dict(char_u *name_arg, dict_T *dict)
2279{
2280 char_u *name = NULL;
2281 char_u *icon = NULL;
2282 char_u *linehl = NULL;
2283 char_u *text = NULL;
2284 char_u *texthl = NULL;
Bram Moolenaare413ea02021-11-24 16:20:13 +00002285 char_u *culhl = NULL;
James McCoya80aad72021-12-22 19:45:28 +00002286 char_u *numhl = NULL;
LemonBoyb975ddf2024-07-06 18:04:09 +02002287 int prio = -1;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002288 int retval = -1;
2289
2290 if (name_arg == NULL)
2291 {
2292 if (dict == NULL)
2293 return -1;
Bram Moolenaard61efa52022-07-23 09:52:04 +01002294 name = dict_get_string(dict, "name", TRUE);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002295 }
2296 else
2297 name = vim_strsave(name_arg);
2298 if (name == NULL || name[0] == NUL)
2299 goto cleanup;
2300 if (dict != NULL)
2301 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01002302 icon = dict_get_string(dict, "icon", TRUE);
2303 linehl = dict_get_string(dict, "linehl", TRUE);
2304 text = dict_get_string(dict, "text", TRUE);
2305 texthl = dict_get_string(dict, "texthl", TRUE);
2306 culhl = dict_get_string(dict, "culhl", TRUE);
2307 numhl = dict_get_string(dict, "numhl", TRUE);
LemonBoyb975ddf2024-07-06 18:04:09 +02002308 prio = dict_get_number_def(dict, "priority", -1);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002309 }
2310
LemonBoyb975ddf2024-07-06 18:04:09 +02002311 if (sign_define_by_name(name, icon, linehl, text, texthl, culhl, numhl, prio) == OK)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002312 retval = 0;
2313
2314cleanup:
2315 vim_free(name);
2316 vim_free(icon);
2317 vim_free(linehl);
2318 vim_free(text);
2319 vim_free(texthl);
Bram Moolenaare413ea02021-11-24 16:20:13 +00002320 vim_free(culhl);
James McCoya80aad72021-12-22 19:45:28 +00002321 vim_free(numhl);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002322
2323 return retval;
2324}
2325
2326/*
2327 * Define multiple signs using attributes from list 'l' and store the return
2328 * values in 'retlist'.
2329 */
2330 static void
2331sign_define_multiple(list_T *l, list_T *retlist)
2332{
2333 listitem_T *li;
2334 int retval;
2335
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002336 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002337 {
2338 retval = -1;
2339 if (li->li_tv.v_type == VAR_DICT)
2340 retval = sign_define_from_dict(NULL, li->li_tv.vval.v_dict);
2341 else
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002342 emsg(_(e_dictionary_required));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002343 list_append_number(retlist, retval);
2344 }
2345}
2346
2347/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002348 * "sign_define()" function
2349 */
2350 void
2351f_sign_define(typval_T *argvars, typval_T *rettv)
2352{
2353 char_u *name;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002354
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002355 if (in_vim9script()
2356 && (check_for_string_or_list_arg(argvars, 0) == FAIL
2357 || check_for_opt_dict_arg(argvars, 1) == FAIL))
2358 return;
2359
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002360 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2361 {
2362 // Define multiple signs
Bram Moolenaar93a10962022-06-16 11:42:09 +01002363 if (rettv_list_alloc(rettv) == FAIL)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002364 return;
2365
2366 sign_define_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2367 return;
2368 }
2369
2370 // Define a single sign
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002371 rettv->vval.v_number = -1;
2372
2373 name = tv_get_string_chk(&argvars[0]);
2374 if (name == NULL)
2375 return;
2376
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +01002377 if (check_for_opt_dict_arg(argvars, 1) == FAIL)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002378 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002379
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002380 rettv->vval.v_number = sign_define_from_dict(name,
2381 argvars[1].v_type == VAR_DICT ? argvars[1].vval.v_dict : NULL);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002382}
2383
2384/*
2385 * "sign_getdefined()" function
2386 */
2387 void
2388f_sign_getdefined(typval_T *argvars, typval_T *rettv)
2389{
2390 char_u *name = NULL;
2391
Bram Moolenaar93a10962022-06-16 11:42:09 +01002392 if (rettv_list_alloc_id(rettv, aid_sign_getdefined) == FAIL)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002393 return;
2394
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002395 if (in_vim9script() && check_for_opt_string_arg(argvars, 0) == FAIL)
2396 return;
2397
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002398 if (argvars[0].v_type != VAR_UNKNOWN)
2399 name = tv_get_string(&argvars[0]);
2400
2401 sign_getlist(name, rettv->vval.v_list);
2402}
2403
2404/*
2405 * "sign_getplaced()" function
2406 */
2407 void
2408f_sign_getplaced(typval_T *argvars, typval_T *rettv)
2409{
2410 buf_T *buf = NULL;
2411 dict_T *dict;
2412 dictitem_T *di;
2413 linenr_T lnum = 0;
2414 int sign_id = 0;
2415 char_u *group = NULL;
2416 int notanum = FALSE;
2417
Bram Moolenaar93a10962022-06-16 11:42:09 +01002418 if (rettv_list_alloc_id(rettv, aid_sign_getplaced) == FAIL)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002419 return;
2420
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002421 if (in_vim9script()
Yegappan Lakshmanancd917202021-07-21 19:09:09 +02002422 && (check_for_opt_buffer_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002423 || (argvars[0].v_type != VAR_UNKNOWN
2424 && check_for_opt_dict_arg(argvars, 1) == FAIL)))
2425 return;
2426
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002427 if (argvars[0].v_type != VAR_UNKNOWN)
2428 {
2429 // get signs placed in the specified buffer
2430 buf = get_buf_arg(&argvars[0]);
2431 if (buf == NULL)
2432 return;
2433
2434 if (argvars[1].v_type != VAR_UNKNOWN)
2435 {
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +01002436 if (check_for_nonnull_dict_arg(argvars, 1) == FAIL)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002437 return;
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +01002438 dict = argvars[1].vval.v_dict;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002439 if ((di = dict_find(dict, (char_u *)"lnum", -1)) != NULL)
2440 {
2441 // get signs placed at this line
2442 (void)tv_get_number_chk(&di->di_tv, &notanum);
2443 if (notanum)
2444 return;
2445 lnum = tv_get_lnum(&di->di_tv);
2446 }
2447 if ((di = dict_find(dict, (char_u *)"id", -1)) != NULL)
2448 {
2449 // get sign placed with this identifier
2450 sign_id = (int)tv_get_number_chk(&di->di_tv, &notanum);
2451 if (notanum)
2452 return;
2453 }
2454 if ((di = dict_find(dict, (char_u *)"group", -1)) != NULL)
2455 {
2456 group = tv_get_string_chk(&di->di_tv);
2457 if (group == NULL)
2458 return;
2459 if (*group == '\0') // empty string means global group
2460 group = NULL;
2461 }
2462 }
2463 }
2464
2465 sign_get_placed(buf, lnum, sign_id, group, rettv->vval.v_list);
2466}
2467
2468/*
2469 * "sign_jump()" function
2470 */
2471 void
2472f_sign_jump(typval_T *argvars, typval_T *rettv)
2473{
2474 int sign_id;
2475 char_u *sign_group = NULL;
2476 buf_T *buf;
2477 int notanum = FALSE;
2478
2479 rettv->vval.v_number = -1;
2480
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002481 if (in_vim9script()
2482 && (check_for_number_arg(argvars, 0) == FAIL
2483 || check_for_string_arg(argvars, 1) == FAIL
2484 || check_for_buffer_arg(argvars, 2) == FAIL))
2485 return;
2486
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002487 // Sign identifier
2488 sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
2489 if (notanum)
2490 return;
2491 if (sign_id <= 0)
2492 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00002493 emsg(_(e_invalid_argument));
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002494 return;
2495 }
2496
2497 // Sign group
2498 sign_group = tv_get_string_chk(&argvars[1]);
2499 if (sign_group == NULL)
2500 return;
2501 if (sign_group[0] == '\0')
2502 sign_group = NULL; // global sign group
2503 else
2504 {
2505 sign_group = vim_strsave(sign_group);
2506 if (sign_group == NULL)
2507 return;
2508 }
2509
2510 // Buffer to place the sign
2511 buf = get_buf_arg(&argvars[2]);
2512 if (buf == NULL)
2513 goto cleanup;
2514
2515 rettv->vval.v_number = sign_jump(sign_id, sign_group, buf);
2516
2517cleanup:
2518 vim_free(sign_group);
2519}
2520
2521/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002522 * Place a new sign using the values specified in dict 'dict'. Returns the sign
2523 * identifier if successfully placed, otherwise returns 0.
2524 */
2525 static int
2526sign_place_from_dict(
2527 typval_T *id_tv,
2528 typval_T *group_tv,
2529 typval_T *name_tv,
2530 typval_T *buf_tv,
2531 dict_T *dict)
2532{
2533 int sign_id = 0;
2534 char_u *group = NULL;
2535 char_u *sign_name = NULL;
2536 buf_T *buf = NULL;
2537 dictitem_T *di;
2538 linenr_T lnum = 0;
LemonBoyb975ddf2024-07-06 18:04:09 +02002539 int prio = -1;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002540 int notanum = FALSE;
2541 int ret_sign_id = -1;
2542
2543 // sign identifier
2544 if (id_tv == NULL)
2545 {
2546 di = dict_find(dict, (char_u *)"id", -1);
2547 if (di != NULL)
2548 id_tv = &di->di_tv;
2549 }
2550 if (id_tv == NULL)
2551 sign_id = 0;
2552 else
2553 {
2554 sign_id = tv_get_number_chk(id_tv, &notanum);
2555 if (notanum)
2556 return -1;
2557 if (sign_id < 0)
2558 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00002559 emsg(_(e_invalid_argument));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002560 return -1;
2561 }
2562 }
2563
2564 // sign group
2565 if (group_tv == NULL)
2566 {
2567 di = dict_find(dict, (char_u *)"group", -1);
2568 if (di != NULL)
2569 group_tv = &di->di_tv;
2570 }
2571 if (group_tv == NULL)
2572 group = NULL; // global group
2573 else
2574 {
2575 group = tv_get_string_chk(group_tv);
2576 if (group == NULL)
2577 goto cleanup;
2578 if (group[0] == '\0') // global sign group
2579 group = NULL;
2580 else
2581 {
2582 group = vim_strsave(group);
2583 if (group == NULL)
2584 return -1;
2585 }
2586 }
2587
2588 // sign name
2589 if (name_tv == NULL)
2590 {
2591 di = dict_find(dict, (char_u *)"name", -1);
2592 if (di != NULL)
2593 name_tv = &di->di_tv;
2594 }
2595 if (name_tv == NULL)
2596 goto cleanup;
2597 sign_name = tv_get_string_chk(name_tv);
2598 if (sign_name == NULL)
2599 goto cleanup;
2600
2601 // buffer to place the sign
2602 if (buf_tv == NULL)
2603 {
2604 di = dict_find(dict, (char_u *)"buffer", -1);
2605 if (di != NULL)
2606 buf_tv = &di->di_tv;
2607 }
2608 if (buf_tv == NULL)
2609 goto cleanup;
2610 buf = get_buf_arg(buf_tv);
2611 if (buf == NULL)
2612 goto cleanup;
2613
2614 // line number of the sign
2615 di = dict_find(dict, (char_u *)"lnum", -1);
2616 if (di != NULL)
2617 {
Bram Moolenaar42aff462019-08-21 13:20:29 +02002618 lnum = tv_get_lnum(&di->di_tv);
2619 if (lnum <= 0)
2620 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00002621 emsg(_(e_invalid_argument));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002622 goto cleanup;
Bram Moolenaar42aff462019-08-21 13:20:29 +02002623 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002624 }
2625
2626 // sign priority
2627 di = dict_find(dict, (char_u *)"priority", -1);
2628 if (di != NULL)
2629 {
2630 prio = (int)tv_get_number_chk(&di->di_tv, &notanum);
2631 if (notanum)
2632 goto cleanup;
2633 }
2634
2635 if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK)
2636 ret_sign_id = sign_id;
2637
2638cleanup:
2639 vim_free(group);
2640
2641 return ret_sign_id;
2642}
2643
2644/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002645 * "sign_place()" function
2646 */
2647 void
2648f_sign_place(typval_T *argvars, typval_T *rettv)
2649{
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002650 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002651
2652 rettv->vval.v_number = -1;
2653
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002654 if (in_vim9script()
2655 && (check_for_number_arg(argvars, 0) == FAIL
2656 || check_for_string_arg(argvars, 1) == FAIL
2657 || check_for_string_arg(argvars, 2) == FAIL
2658 || check_for_buffer_arg(argvars, 3) == FAIL
2659 || check_for_opt_dict_arg(argvars, 4) == FAIL))
2660 return;
2661
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +01002662 if (argvars[4].v_type != VAR_UNKNOWN)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002663 {
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +01002664 if (check_for_nonnull_dict_arg(argvars, 4) == FAIL)
2665 return;
2666 dict = argvars[4].vval.v_dict;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002667 }
2668
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002669 rettv->vval.v_number = sign_place_from_dict(&argvars[0], &argvars[1],
2670 &argvars[2], &argvars[3], dict);
2671}
2672
2673/*
2674 * "sign_placelist()" function. Place multiple signs.
2675 */
2676 void
2677f_sign_placelist(typval_T *argvars, typval_T *rettv)
2678{
2679 listitem_T *li;
2680 int sign_id;
2681
Bram Moolenaar93a10962022-06-16 11:42:09 +01002682 if (rettv_list_alloc(rettv) == FAIL)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002683 return;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002684
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002685 if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
2686 return;
2687
Bram Moolenaard83392a2022-09-01 12:22:46 +01002688 if (check_for_list_arg(argvars, 0) == FAIL)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002689 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002690
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002691 // Process the List of sign attributes
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002692 FOR_ALL_LIST_ITEMS(argvars[0].vval.v_list, li)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002693 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002694 sign_id = -1;
2695 if (li->li_tv.v_type == VAR_DICT)
2696 sign_id = sign_place_from_dict(NULL, NULL, NULL, NULL,
2697 li->li_tv.vval.v_dict);
2698 else
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002699 emsg(_(e_dictionary_required));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002700 list_append_number(rettv->vval.v_list, sign_id);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002701 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002702}
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002703
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002704/*
2705 * Undefine multiple signs
2706 */
2707 static void
2708sign_undefine_multiple(list_T *l, list_T *retlist)
2709{
2710 char_u *name;
2711 listitem_T *li;
2712 int retval;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002713
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002714 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002715 {
2716 retval = -1;
2717 name = tv_get_string_chk(&li->li_tv);
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002718 if (name != NULL && (sign_undefine_by_name(name, TRUE) == OK))
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002719 retval = 0;
2720 list_append_number(retlist, retval);
2721 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002722}
2723
2724/*
2725 * "sign_undefine()" function
2726 */
2727 void
2728f_sign_undefine(typval_T *argvars, typval_T *rettv)
2729{
2730 char_u *name;
2731
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002732 if (in_vim9script()
2733 && check_for_opt_string_or_list_arg(argvars, 0) == FAIL)
2734 return;
2735
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002736 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2737 {
2738 // Undefine multiple signs
Bram Moolenaar93a10962022-06-16 11:42:09 +01002739 if (rettv_list_alloc(rettv) == FAIL)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002740 return;
2741
2742 sign_undefine_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2743 return;
2744 }
2745
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002746 rettv->vval.v_number = -1;
2747
2748 if (argvars[0].v_type == VAR_UNKNOWN)
2749 {
2750 // Free all the signs
2751 free_signs();
2752 rettv->vval.v_number = 0;
2753 }
2754 else
2755 {
2756 // Free only the specified sign
2757 name = tv_get_string_chk(&argvars[0]);
2758 if (name == NULL)
2759 return;
2760
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002761 if (sign_undefine_by_name(name, TRUE) == OK)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002762 rettv->vval.v_number = 0;
2763 }
2764}
2765
2766/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002767 * Unplace the sign with attributes specified in 'dict'. Returns 0 on success
2768 * and -1 on failure.
2769 */
2770 static int
2771sign_unplace_from_dict(typval_T *group_tv, dict_T *dict)
2772{
2773 dictitem_T *di;
2774 int sign_id = 0;
2775 buf_T *buf = NULL;
2776 char_u *group = NULL;
2777 int retval = -1;
2778
2779 // sign group
2780 if (group_tv != NULL)
2781 group = tv_get_string(group_tv);
2782 else
Bram Moolenaard61efa52022-07-23 09:52:04 +01002783 group = dict_get_string(dict, "group", FALSE);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002784 if (group != NULL)
2785 {
2786 if (group[0] == '\0') // global sign group
2787 group = NULL;
2788 else
2789 {
2790 group = vim_strsave(group);
2791 if (group == NULL)
2792 return -1;
2793 }
2794 }
2795
2796 if (dict != NULL)
2797 {
2798 if ((di = dict_find(dict, (char_u *)"buffer", -1)) != NULL)
2799 {
2800 buf = get_buf_arg(&di->di_tv);
2801 if (buf == NULL)
2802 goto cleanup;
2803 }
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002804 if (dict_has_key(dict, "id"))
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002805 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01002806 sign_id = dict_get_number(dict, "id");
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002807 if (sign_id <= 0)
2808 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00002809 emsg(_(e_invalid_argument));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002810 goto cleanup;
2811 }
2812 }
2813 }
2814
2815 if (buf == NULL)
2816 {
2817 // Delete the sign in all the buffers
2818 retval = 0;
2819 FOR_ALL_BUFFERS(buf)
2820 if (sign_unplace(sign_id, group, buf, 0) != OK)
2821 retval = -1;
2822 }
2823 else if (sign_unplace(sign_id, group, buf, 0) == OK)
2824 retval = 0;
2825
2826cleanup:
2827 vim_free(group);
2828
2829 return retval;
2830}
2831
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002832 sign_entry_T *
2833get_first_valid_sign(win_T *wp)
2834{
2835 sign_entry_T *sign = wp->w_buffer->b_signlist;
2836
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002837# ifdef FEAT_PROP_POPUP
Bram Moolenaar72570732019-11-30 14:21:53 +01002838 while (sign != NULL && !sign_group_for_window(sign, wp))
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002839 sign = sign->se_next;
2840# endif
2841 return sign;
2842}
2843
2844/*
2845 * Return TRUE when window "wp" has a column to draw signs in.
2846 */
2847 int
2848signcolumn_on(win_T *wp)
2849{
2850 // If 'signcolumn' is set to 'number', signs are displayed in the 'number'
2851 // column (if present). Otherwise signs are to be displayed in the sign
2852 // column.
2853 if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u')
2854 return get_first_valid_sign(wp) != NULL && !wp->w_p_nu && !wp->w_p_rnu;
2855
2856 if (*wp->w_p_scl == 'n')
2857 return FALSE;
2858 if (*wp->w_p_scl == 'y')
2859 return TRUE;
2860 return (get_first_valid_sign(wp) != NULL
2861# ifdef FEAT_NETBEANS_INTG
2862 || wp->w_buffer->b_has_sign_column
2863# endif
2864 );
2865}
2866
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002867/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002868 * "sign_unplace()" function
2869 */
2870 void
2871f_sign_unplace(typval_T *argvars, typval_T *rettv)
2872{
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002873 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002874
2875 rettv->vval.v_number = -1;
2876
Yegappan Lakshmanan8deb2b32022-09-02 15:15:27 +01002877 if ((check_for_string_arg(argvars, 0) == FAIL
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002878 || check_for_opt_dict_arg(argvars, 1) == FAIL))
2879 return;
2880
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002881 if (argvars[1].v_type != VAR_UNKNOWN)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002882 dict = argvars[1].vval.v_dict;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002883
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002884 rettv->vval.v_number = sign_unplace_from_dict(&argvars[0], dict);
2885}
2886
2887/*
2888 * "sign_unplacelist()" function
2889 */
2890 void
2891f_sign_unplacelist(typval_T *argvars, typval_T *rettv)
2892{
2893 listitem_T *li;
2894 int retval;
2895
Bram Moolenaar93a10962022-06-16 11:42:09 +01002896 if (rettv_list_alloc(rettv) == FAIL)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002897 return;
2898
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002899 if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
2900 return;
2901
Bram Moolenaard83392a2022-09-01 12:22:46 +01002902 if (check_for_list_arg(argvars, 0) == FAIL)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002903 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002904
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002905 FOR_ALL_LIST_ITEMS(argvars[0].vval.v_list, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002906 {
2907 retval = -1;
2908 if (li->li_tv.v_type == VAR_DICT)
2909 retval = sign_unplace_from_dict(NULL, li->li_tv.vval.v_dict);
2910 else
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002911 emsg(_(e_dictionary_required));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002912 list_append_number(rettv->vval.v_list, retval);
2913 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002914}
2915
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002916#endif // FEAT_SIGNS