blob: 1ae714d9a17b69105d4553fd195687748a008ab0 [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);
1393 arg = skiptowhite(arg);
1394 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001395 else
1396 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001397 semsg(_(e_invalid_argument_str), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001398 failed = TRUE;
1399 break;
1400 }
1401 }
1402
1403 if (!failed)
LemonBoyb975ddf2024-07-06 18:04:09 +02001404 sign_define_by_name(sign_name, icon, linehl, text, texthl, culhl, numhl, prio);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001405
1406 vim_free(icon);
1407 vim_free(text);
1408 vim_free(linehl);
1409 vim_free(texthl);
Bram Moolenaare413ea02021-11-24 16:20:13 +00001410 vim_free(culhl);
James McCoya80aad72021-12-22 19:45:28 +00001411 vim_free(numhl);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001412}
1413
1414/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001415 * ":sign place" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001416 */
1417 static void
1418sign_place_cmd(
1419 buf_T *buf,
1420 linenr_T lnum,
1421 char_u *sign_name,
1422 int id,
1423 char_u *group,
1424 int prio)
1425{
1426 if (id <= 0)
1427 {
1428 // List signs placed in a file/buffer
1429 // :sign place file={fname}
1430 // :sign place group={group} file={fname}
1431 // :sign place group=* file={fname}
1432 // :sign place buffer={nr}
1433 // :sign place group={group} buffer={nr}
1434 // :sign place group=* buffer={nr}
1435 // :sign place
1436 // :sign place group={group}
1437 // :sign place group=*
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001438 if (lnum >= 0 || sign_name != NULL
1439 || (group != NULL && *group == '\0'))
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001440 emsg(_(e_invalid_argument));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001441 else
1442 sign_list_placed(buf, group);
1443 }
1444 else
1445 {
1446 // Place a new sign
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001447 if (sign_name == NULL || buf == NULL
1448 || (group != NULL && *group == '\0'))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001449 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001450 emsg(_(e_invalid_argument));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001451 return;
1452 }
1453
1454 sign_place(&id, group, sign_name, buf, lnum, prio);
1455 }
1456}
1457
1458/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001459 * ":sign unplace" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001460 */
1461 static void
1462sign_unplace_cmd(
1463 buf_T *buf,
1464 linenr_T lnum,
1465 char_u *sign_name,
1466 int id,
1467 char_u *group)
1468{
1469 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0'))
1470 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001471 emsg(_(e_invalid_argument));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001472 return;
1473 }
1474
1475 if (id == -2)
1476 {
1477 if (buf != NULL)
1478 // :sign unplace * file={fname}
1479 // :sign unplace * group={group} file={fname}
1480 // :sign unplace * group=* file={fname}
1481 // :sign unplace * buffer={nr}
1482 // :sign unplace * group={group} buffer={nr}
1483 // :sign unplace * group=* buffer={nr}
1484 sign_unplace(0, group, buf, 0);
1485 else
1486 // :sign unplace *
1487 // :sign unplace * group={group}
1488 // :sign unplace * group=*
1489 FOR_ALL_BUFFERS(buf)
1490 if (buf->b_signlist != NULL)
1491 buf_delete_signs(buf, group);
1492 }
1493 else
1494 {
1495 if (buf != NULL)
1496 // :sign unplace {id} file={fname}
1497 // :sign unplace {id} group={group} file={fname}
1498 // :sign unplace {id} group=* file={fname}
1499 // :sign unplace {id} buffer={nr}
1500 // :sign unplace {id} group={group} buffer={nr}
1501 // :sign unplace {id} group=* buffer={nr}
1502 sign_unplace(id, group, buf, 0);
1503 else
1504 {
1505 if (id == -1)
1506 {
1507 // :sign unplace group={group}
1508 // :sign unplace group=*
1509 sign_unplace_at_cursor(group);
1510 }
1511 else
1512 {
1513 // :sign unplace {id}
1514 // :sign unplace {id} group={group}
1515 // :sign unplace {id} group=*
1516 FOR_ALL_BUFFERS(buf)
1517 sign_unplace(id, group, buf, 0);
1518 }
1519 }
1520 }
1521}
1522
1523/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001524 * Jump to a placed sign commands:
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001525 * :sign jump {id} file={fname}
1526 * :sign jump {id} buffer={nr}
1527 * :sign jump {id} group={group} file={fname}
1528 * :sign jump {id} group={group} buffer={nr}
1529 */
1530 static void
1531sign_jump_cmd(
1532 buf_T *buf,
1533 linenr_T lnum,
1534 char_u *sign_name,
1535 int id,
1536 char_u *group)
1537{
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001538 if (sign_name == NULL && group == NULL && id == -1)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001539 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001540 emsg(_(e_argument_required));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001541 return;
1542 }
1543
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001544 if (buf == NULL || (group != NULL && *group == '\0')
1545 || lnum >= 0 || sign_name != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001546 {
1547 // File or buffer is not specified or an empty group is used
1548 // or a line number or a sign name is specified.
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001549 emsg(_(e_invalid_argument));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001550 return;
1551 }
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001552 (void)sign_jump(id, group, buf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001553}
1554
1555/*
1556 * Parse the command line arguments for the ":sign place", ":sign unplace" and
1557 * ":sign jump" commands.
1558 * The supported arguments are: line={lnum} name={name} group={group}
1559 * priority={prio} and file={fname} or buffer={nr}.
1560 */
1561 static int
1562parse_sign_cmd_args(
1563 int cmd,
1564 char_u *arg,
1565 char_u **sign_name,
1566 int *signid,
1567 char_u **group,
1568 int *prio,
1569 buf_T **buf,
1570 linenr_T *lnum)
1571{
1572 char_u *arg1;
1573 char_u *name;
1574 char_u *filename = NULL;
Bram Moolenaarb589f952019-01-07 22:10:00 +01001575 int lnum_arg = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001576
1577 // first arg could be placed sign id
1578 arg1 = arg;
1579 if (VIM_ISDIGIT(*arg))
1580 {
1581 *signid = getdigits(&arg);
1582 if (!VIM_ISWHITE(*arg) && *arg != NUL)
1583 {
1584 *signid = -1;
1585 arg = arg1;
1586 }
1587 else
1588 arg = skipwhite(arg);
1589 }
1590
1591 while (*arg != NUL)
1592 {
1593 if (STRNCMP(arg, "line=", 5) == 0)
1594 {
1595 arg += 5;
1596 *lnum = atoi((char *)arg);
1597 arg = skiptowhite(arg);
Bram Moolenaarb589f952019-01-07 22:10:00 +01001598 lnum_arg = TRUE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001599 }
1600 else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE)
1601 {
1602 if (*signid != -1)
1603 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001604 emsg(_(e_invalid_argument));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001605 return FAIL;
1606 }
1607 *signid = -2;
1608 arg = skiptowhite(arg + 1);
1609 }
1610 else if (STRNCMP(arg, "name=", 5) == 0)
1611 {
1612 arg += 5;
1613 name = arg;
1614 arg = skiptowhite(arg);
1615 if (*arg != NUL)
1616 *arg++ = NUL;
1617 while (name[0] == '0' && name[1] != NUL)
1618 ++name;
1619 *sign_name = name;
1620 }
1621 else if (STRNCMP(arg, "group=", 6) == 0)
1622 {
1623 arg += 6;
1624 *group = arg;
1625 arg = skiptowhite(arg);
1626 if (*arg != NUL)
1627 *arg++ = NUL;
1628 }
1629 else if (STRNCMP(arg, "priority=", 9) == 0)
1630 {
1631 arg += 9;
1632 *prio = atoi((char *)arg);
1633 arg = skiptowhite(arg);
1634 }
1635 else if (STRNCMP(arg, "file=", 5) == 0)
1636 {
1637 arg += 5;
1638 filename = arg;
1639 *buf = buflist_findname_exp(arg);
1640 break;
1641 }
1642 else if (STRNCMP(arg, "buffer=", 7) == 0)
1643 {
1644 arg += 7;
1645 filename = arg;
1646 *buf = buflist_findnr((int)getdigits(&arg));
1647 if (*skipwhite(arg) != NUL)
Bram Moolenaar74409f62022-01-01 15:58:22 +00001648 semsg(_(e_trailing_characters_str), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001649 break;
1650 }
1651 else
1652 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001653 emsg(_(e_invalid_argument));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001654 return FAIL;
1655 }
1656 arg = skipwhite(arg);
1657 }
1658
1659 if (filename != NULL && *buf == NULL)
1660 {
Bram Moolenaareb822a22021-12-31 15:09:27 +00001661 semsg(_(e_invalid_buffer_name_str), filename);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001662 return FAIL;
1663 }
1664
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001665 // If the filename is not supplied for the sign place or the sign jump
1666 // command, then use the current buffer.
Bram Moolenaarb589f952019-01-07 22:10:00 +01001667 if (filename == NULL && ((cmd == SIGNCMD_PLACE && lnum_arg)
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001668 || cmd == SIGNCMD_JUMP))
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001669 *buf = curwin->w_buffer;
1670
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001671 return OK;
1672}
1673
1674/*
1675 * ":sign" command
1676 */
1677 void
1678ex_sign(exarg_T *eap)
1679{
1680 char_u *arg = eap->arg;
1681 char_u *p;
1682 int idx;
1683 sign_T *sp;
1684 buf_T *buf = NULL;
1685
1686 // Parse the subcommand.
1687 p = skiptowhite(arg);
1688 idx = sign_cmd_idx(arg, p);
1689 if (idx == SIGNCMD_LAST)
1690 {
Bram Moolenaareb822a22021-12-31 15:09:27 +00001691 semsg(_(e_unknown_sign_command_str), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001692 return;
1693 }
1694 arg = skipwhite(p);
1695
1696 if (idx <= SIGNCMD_LIST)
1697 {
1698 // Define, undefine or list signs.
1699 if (idx == SIGNCMD_LIST && *arg == NUL)
1700 {
1701 // ":sign list": list all defined signs
1702 for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next)
1703 sign_list_defined(sp);
1704 }
1705 else if (*arg == NUL)
Bram Moolenaareb822a22021-12-31 15:09:27 +00001706 emsg(_(e_missing_sign_name));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001707 else
1708 {
1709 char_u *name;
1710
1711 // Isolate the sign name. If it's a number skip leading zeroes,
1712 // so that "099" and "99" are the same sign. But keep "0".
1713 p = skiptowhite(arg);
1714 if (*p != NUL)
1715 *p++ = NUL;
1716 while (arg[0] == '0' && arg[1] != NUL)
1717 ++arg;
1718 name = vim_strsave(arg);
1719
1720 if (idx == SIGNCMD_DEFINE)
1721 sign_define_cmd(name, p);
1722 else if (idx == SIGNCMD_LIST)
1723 // ":sign list {name}"
1724 sign_list_by_name(name);
1725 else
1726 // ":sign undefine {name}"
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001727 sign_undefine_by_name(name, TRUE);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001728
1729 vim_free(name);
1730 return;
1731 }
1732 }
1733 else
1734 {
1735 int id = -1;
1736 linenr_T lnum = -1;
1737 char_u *sign_name = NULL;
1738 char_u *group = NULL;
LemonBoyb975ddf2024-07-06 18:04:09 +02001739 int prio = -1;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001740
1741 // Parse command line arguments
1742 if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio,
1743 &buf, &lnum) == FAIL)
1744 return;
1745
1746 if (idx == SIGNCMD_PLACE)
1747 sign_place_cmd(buf, lnum, sign_name, id, group, prio);
1748 else if (idx == SIGNCMD_UNPLACE)
1749 sign_unplace_cmd(buf, lnum, sign_name, id, group);
1750 else if (idx == SIGNCMD_JUMP)
1751 sign_jump_cmd(buf, lnum, sign_name, id, group);
1752 }
1753}
1754
1755/*
1756 * Return information about a specified sign
1757 */
1758 static void
1759sign_getinfo(sign_T *sp, dict_T *retdict)
1760{
1761 char_u *p;
1762
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00001763 dict_add_string(retdict, "name", sp->sn_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001764 if (sp->sn_icon != NULL)
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00001765 dict_add_string(retdict, "icon", sp->sn_icon);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001766 if (sp->sn_text != NULL)
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00001767 dict_add_string(retdict, "text", sp->sn_text);
LemonBoyb975ddf2024-07-06 18:04:09 +02001768 if (sp->sn_priority > 0)
1769 dict_add_number(retdict, "priority", sp->sn_priority);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001770 if (sp->sn_line_hl > 0)
1771 {
1772 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1773 if (p == NULL)
1774 p = (char_u *)"NONE";
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00001775 dict_add_string(retdict, "linehl", p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001776 }
1777 if (sp->sn_text_hl > 0)
1778 {
1779 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1780 if (p == NULL)
1781 p = (char_u *)"NONE";
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00001782 dict_add_string(retdict, "texthl", p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001783 }
Bram Moolenaare413ea02021-11-24 16:20:13 +00001784 if (sp->sn_cul_hl > 0)
1785 {
1786 p = get_highlight_name_ext(NULL, sp->sn_cul_hl - 1, FALSE);
1787 if (p == NULL)
1788 p = (char_u *)"NONE";
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00001789 dict_add_string(retdict, "culhl", p);
Bram Moolenaare413ea02021-11-24 16:20:13 +00001790 }
James McCoya80aad72021-12-22 19:45:28 +00001791 if (sp->sn_num_hl > 0)
1792 {
1793 p = get_highlight_name_ext(NULL, sp->sn_num_hl - 1, FALSE);
1794 if (p == NULL)
1795 p = (char_u *)"NONE";
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00001796 dict_add_string(retdict, "numhl", p);
James McCoya80aad72021-12-22 19:45:28 +00001797 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001798}
1799
1800/*
1801 * If 'name' is NULL, return a list of all the defined signs.
1802 * Otherwise, return information about the specified sign.
1803 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001804 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001805sign_getlist(char_u *name, list_T *retlist)
1806{
1807 sign_T *sp = first_sign;
1808 dict_T *dict;
1809
1810 if (name != NULL)
1811 {
1812 sp = sign_find(name, NULL);
1813 if (sp == NULL)
1814 return;
1815 }
1816
1817 for (; sp != NULL && !got_int; sp = sp->sn_next)
1818 {
1819 if ((dict = dict_alloc_id(aid_sign_getlist)) == NULL)
1820 return;
1821 if (list_append_dict(retlist, dict) == FAIL)
1822 return;
1823 sign_getinfo(sp, dict);
1824
1825 if (name != NULL) // handle only the specified sign
1826 break;
1827 }
1828}
1829
1830/*
1831 * Returns information about signs placed in a buffer as list of dicts.
1832 */
1833 void
1834get_buffer_signs(buf_T *buf, list_T *l)
1835{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001836 sign_entry_T *sign;
1837 dict_T *d;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001838
1839 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1840 {
1841 if ((d = sign_get_info(sign)) != NULL)
1842 list_append_dict(l, d);
1843 }
1844}
1845
1846/*
1847 * Return information about all the signs placed in a buffer
1848 */
1849 static void
1850sign_get_placed_in_buf(
1851 buf_T *buf,
1852 linenr_T lnum,
1853 int sign_id,
1854 char_u *sign_group,
1855 list_T *retlist)
1856{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001857 dict_T *d;
1858 list_T *l;
1859 sign_entry_T *sign;
1860 dict_T *sdict;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001861
1862 if ((d = dict_alloc_id(aid_sign_getplaced_dict)) == NULL)
1863 return;
1864 list_append_dict(retlist, d);
1865
1866 dict_add_number(d, "bufnr", (long)buf->b_fnum);
1867
1868 if ((l = list_alloc_id(aid_sign_getplaced_list)) == NULL)
1869 return;
1870 dict_add_list(d, "signs", l);
1871
1872 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1873 {
1874 if (!sign_in_group(sign, sign_group))
1875 continue;
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001876 if ((lnum == 0 && sign_id == 0)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001877 || (sign_id == 0 && lnum == sign->se_lnum)
1878 || (lnum == 0 && sign_id == sign->se_id)
1879 || (lnum == sign->se_lnum && sign_id == sign->se_id))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001880 {
1881 if ((sdict = sign_get_info(sign)) != NULL)
1882 list_append_dict(l, sdict);
1883 }
1884 }
1885}
1886
1887/*
1888 * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the
1889 * sign placed at the line number. If 'lnum' is zero, return all the signs
1890 * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers.
1891 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001892 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001893sign_get_placed(
1894 buf_T *buf,
1895 linenr_T lnum,
1896 int sign_id,
1897 char_u *sign_group,
1898 list_T *retlist)
1899{
1900 if (buf != NULL)
1901 sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist);
1902 else
1903 {
1904 FOR_ALL_BUFFERS(buf)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001905 if (buf->b_signlist != NULL)
1906 sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001907 }
1908}
1909
1910# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1911/*
1912 * Allocate the icons. Called when the GUI has started. Allows defining
1913 * signs before it starts.
1914 */
1915 void
1916sign_gui_started(void)
1917{
1918 sign_T *sp;
1919
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001920 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001921 if (sp->sn_icon != NULL)
1922 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
1923}
1924# endif
1925
1926/*
1927 * List one sign.
1928 */
1929 static void
1930sign_list_defined(sign_T *sp)
1931{
1932 char_u *p;
LemonBoyb975ddf2024-07-06 18:04:09 +02001933 char lbuf[MSG_BUF_LEN];
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001934
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001935 smsg("sign %s", sp->sn_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001936 if (sp->sn_icon != NULL)
1937 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001938 msg_puts(" icon=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001939 msg_outtrans(sp->sn_icon);
1940# ifdef FEAT_SIGN_ICONS
1941 if (sp->sn_image == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001942 msg_puts(_(" (NOT FOUND)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001943# else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001944 msg_puts(_(" (not supported)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001945# endif
1946 }
1947 if (sp->sn_text != NULL)
1948 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001949 msg_puts(" text=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001950 msg_outtrans(sp->sn_text);
1951 }
LemonBoyb975ddf2024-07-06 18:04:09 +02001952 if (sp->sn_priority > 0)
1953 {
1954 vim_snprintf(lbuf, MSG_BUF_LEN, " priority=%d", sp->sn_priority);
1955 msg_puts(lbuf);
1956 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001957 if (sp->sn_line_hl > 0)
1958 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001959 msg_puts(" linehl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001960 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1961 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001962 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001963 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001964 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001965 }
1966 if (sp->sn_text_hl > 0)
1967 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001968 msg_puts(" texthl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001969 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1970 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001971 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001972 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001973 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001974 }
Bram Moolenaare413ea02021-11-24 16:20:13 +00001975 if (sp->sn_cul_hl > 0)
1976 {
1977 msg_puts(" culhl=");
1978 p = get_highlight_name_ext(NULL, sp->sn_cul_hl - 1, FALSE);
1979 if (p == NULL)
1980 msg_puts("NONE");
1981 else
1982 msg_puts((char *)p);
1983 }
James McCoya80aad72021-12-22 19:45:28 +00001984 if (sp->sn_num_hl > 0)
1985 {
1986 msg_puts(" numhl=");
1987 p = get_highlight_name_ext(NULL, sp->sn_num_hl - 1, FALSE);
1988 if (p == NULL)
1989 msg_puts("NONE");
1990 else
1991 msg_puts((char *)p);
1992 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001993}
1994
1995/*
1996 * Undefine a sign and free its memory.
1997 */
1998 static void
1999sign_undefine(sign_T *sp, sign_T *sp_prev)
2000{
2001 vim_free(sp->sn_name);
2002 vim_free(sp->sn_icon);
2003# ifdef FEAT_SIGN_ICONS
2004 if (sp->sn_image != NULL)
2005 {
2006 out_flush();
2007 gui_mch_destroy_sign(sp->sn_image);
2008 }
2009# endif
2010 vim_free(sp->sn_text);
2011 if (sp_prev == NULL)
2012 first_sign = sp->sn_next;
2013 else
2014 sp_prev->sn_next = sp->sn_next;
2015 vim_free(sp);
2016}
2017
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002018# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
2019 void *
2020sign_get_image(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002021 int typenr) // the attribute which may have a sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002022{
2023 sign_T *sp;
2024
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002025 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002026 if (sp->sn_typenr == typenr)
2027 return sp->sn_image;
2028 return NULL;
2029}
2030# endif
2031
2032/*
2033 * Undefine/free all signs.
2034 */
2035 void
2036free_signs(void)
2037{
2038 while (first_sign != NULL)
2039 sign_undefine(first_sign, NULL);
2040}
2041
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002042static enum
2043{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002044 EXP_SUBCMD, // expand :sign sub-commands
2045 EXP_DEFINE, // expand :sign define {name} args
2046 EXP_PLACE, // expand :sign place {id} args
Bram Moolenaar3678f652019-02-17 14:50:25 +01002047 EXP_LIST, // expand :sign place args
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002048 EXP_UNPLACE, // expand :sign unplace"
Bram Moolenaar3678f652019-02-17 14:50:25 +01002049 EXP_SIGN_NAMES, // expand with name of placed signs
2050 EXP_SIGN_GROUPS // expand with name of placed sign groups
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002051} expand_what;
2052
2053/*
Bram Moolenaar3678f652019-02-17 14:50:25 +01002054 * Return the n'th sign name (used for command line completion)
2055 */
2056 static char_u *
2057get_nth_sign_name(int idx)
2058{
2059 int current_idx;
2060 sign_T *sp;
2061
2062 // Complete with name of signs already defined
2063 current_idx = 0;
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002064 FOR_ALL_SIGNS(sp)
Bram Moolenaar3678f652019-02-17 14:50:25 +01002065 if (current_idx++ == idx)
2066 return sp->sn_name;
2067 return NULL;
2068}
2069
2070/*
2071 * Return the n'th sign group name (used for command line completion)
2072 */
2073 static char_u *
2074get_nth_sign_group_name(int idx)
2075{
2076 int current_idx;
2077 int todo;
2078 hashitem_T *hi;
2079 signgroup_T *group;
2080
2081 // Complete with name of sign groups already defined
2082 current_idx = 0;
2083 todo = (int)sg_table.ht_used;
Yegappan Lakshmanan14113fd2023-03-07 17:13:51 +00002084 FOR_ALL_HASHTAB_ITEMS(&sg_table, hi, todo)
Bram Moolenaar3678f652019-02-17 14:50:25 +01002085 {
2086 if (!HASHITEM_EMPTY(hi))
2087 {
2088 --todo;
2089 if (current_idx++ == idx)
2090 {
2091 group = HI2SG(hi);
2092 return group->sg_name;
2093 }
2094 }
2095 }
2096 return NULL;
2097}
2098
2099/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002100 * Function given to ExpandGeneric() to obtain the sign command
2101 * expansion.
2102 */
2103 char_u *
2104get_sign_name(expand_T *xp UNUSED, int idx)
2105{
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002106 switch (expand_what)
2107 {
2108 case EXP_SUBCMD:
2109 return (char_u *)cmds[idx];
2110 case EXP_DEFINE:
2111 {
2112 char *define_arg[] =
2113 {
LemonBoyb975ddf2024-07-06 18:04:09 +02002114 "culhl=", "icon=", "linehl=", "numhl=", "text=", "texthl=", "priority=",
2115 NULL
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002116 };
2117 return (char_u *)define_arg[idx];
2118 }
2119 case EXP_PLACE:
2120 {
2121 char *place_arg[] =
2122 {
2123 "line=", "name=", "group=", "priority=", "file=",
2124 "buffer=", NULL
2125 };
2126 return (char_u *)place_arg[idx];
2127 }
Bram Moolenaar3678f652019-02-17 14:50:25 +01002128 case EXP_LIST:
2129 {
2130 char *list_arg[] =
2131 {
2132 "group=", "file=", "buffer=", NULL
2133 };
2134 return (char_u *)list_arg[idx];
2135 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002136 case EXP_UNPLACE:
2137 {
2138 char *unplace_arg[] = { "group=", "file=", "buffer=", NULL };
2139 return (char_u *)unplace_arg[idx];
2140 }
2141 case EXP_SIGN_NAMES:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002142 return get_nth_sign_name(idx);
2143 case EXP_SIGN_GROUPS:
2144 return get_nth_sign_group_name(idx);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002145 default:
2146 return NULL;
2147 }
2148}
2149
2150/*
2151 * Handle command line completion for :sign command.
2152 */
2153 void
2154set_context_in_sign_cmd(expand_T *xp, char_u *arg)
2155{
2156 char_u *p;
2157 char_u *end_subcmd;
2158 char_u *last;
2159 int cmd_idx;
2160 char_u *begin_subcmd_args;
2161
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002162 // Default: expand subcommands.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002163 xp->xp_context = EXPAND_SIGN;
2164 expand_what = EXP_SUBCMD;
2165 xp->xp_pattern = arg;
2166
2167 end_subcmd = skiptowhite(arg);
2168 if (*end_subcmd == NUL)
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002169 // expand subcmd name
2170 // :sign {subcmd}<CTRL-D>
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002171 return;
2172
2173 cmd_idx = sign_cmd_idx(arg, end_subcmd);
2174
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002175 // :sign {subcmd} {subcmd_args}
2176 // |
2177 // begin_subcmd_args
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002178 begin_subcmd_args = skipwhite(end_subcmd);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002179
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002180 // expand last argument of subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002181
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002182 // :sign define {name} {args}...
2183 // |
2184 // p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002185
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002186 // Loop until reaching last argument.
Bram Moolenaar3678f652019-02-17 14:50:25 +01002187 p = begin_subcmd_args;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002188 do
2189 {
2190 p = skipwhite(p);
2191 last = p;
2192 p = skiptowhite(p);
2193 } while (*p != NUL);
2194
2195 p = vim_strchr(last, '=');
2196
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002197 // :sign define {name} {args}... {last}=
2198 // | |
2199 // last p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002200 if (p == NULL)
2201 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002202 // Expand last argument name (before equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002203 xp->xp_pattern = last;
2204 switch (cmd_idx)
2205 {
2206 case SIGNCMD_DEFINE:
2207 expand_what = EXP_DEFINE;
2208 break;
2209 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002210 // List placed signs
2211 if (VIM_ISDIGIT(*begin_subcmd_args))
2212 // :sign place {id} {args}...
2213 expand_what = EXP_PLACE;
2214 else
2215 // :sign place {args}...
2216 expand_what = EXP_LIST;
2217 break;
2218 case SIGNCMD_LIST:
2219 case SIGNCMD_UNDEFINE:
2220 // :sign list <CTRL-D>
2221 // :sign undefine <CTRL-D>
2222 expand_what = EXP_SIGN_NAMES;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002223 break;
2224 case SIGNCMD_JUMP:
2225 case SIGNCMD_UNPLACE:
2226 expand_what = EXP_UNPLACE;
2227 break;
2228 default:
2229 xp->xp_context = EXPAND_NOTHING;
2230 }
2231 }
2232 else
2233 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002234 // Expand last argument value (after equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002235 xp->xp_pattern = p + 1;
2236 switch (cmd_idx)
2237 {
2238 case SIGNCMD_DEFINE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002239 if (STRNCMP(last, "texthl", 6) == 0
James McCoya80aad72021-12-22 19:45:28 +00002240 || STRNCMP(last, "linehl", 6) == 0
2241 || STRNCMP(last, "culhl", 5) == 0
2242 || STRNCMP(last, "numhl", 5) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002243 xp->xp_context = EXPAND_HIGHLIGHT;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002244 else if (STRNCMP(last, "icon", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002245 xp->xp_context = EXPAND_FILES;
2246 else
2247 xp->xp_context = EXPAND_NOTHING;
2248 break;
2249 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002250 if (STRNCMP(last, "name", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002251 expand_what = EXP_SIGN_NAMES;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002252 else if (STRNCMP(last, "group", 5) == 0)
2253 expand_what = EXP_SIGN_GROUPS;
2254 else if (STRNCMP(last, "file", 4) == 0)
2255 xp->xp_context = EXPAND_BUFFERS;
2256 else
2257 xp->xp_context = EXPAND_NOTHING;
2258 break;
2259 case SIGNCMD_UNPLACE:
2260 case SIGNCMD_JUMP:
2261 if (STRNCMP(last, "group", 5) == 0)
2262 expand_what = EXP_SIGN_GROUPS;
2263 else if (STRNCMP(last, "file", 4) == 0)
2264 xp->xp_context = EXPAND_BUFFERS;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002265 else
2266 xp->xp_context = EXPAND_NOTHING;
2267 break;
2268 default:
2269 xp->xp_context = EXPAND_NOTHING;
2270 }
2271 }
2272}
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002273
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002274/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002275 * Define a sign using the attributes in 'dict'. Returns 0 on success and -1 on
2276 * failure.
2277 */
2278 static int
2279sign_define_from_dict(char_u *name_arg, dict_T *dict)
2280{
2281 char_u *name = NULL;
2282 char_u *icon = NULL;
2283 char_u *linehl = NULL;
2284 char_u *text = NULL;
2285 char_u *texthl = NULL;
Bram Moolenaare413ea02021-11-24 16:20:13 +00002286 char_u *culhl = NULL;
James McCoya80aad72021-12-22 19:45:28 +00002287 char_u *numhl = NULL;
LemonBoyb975ddf2024-07-06 18:04:09 +02002288 int prio = -1;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002289 int retval = -1;
2290
2291 if (name_arg == NULL)
2292 {
2293 if (dict == NULL)
2294 return -1;
Bram Moolenaard61efa52022-07-23 09:52:04 +01002295 name = dict_get_string(dict, "name", TRUE);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002296 }
2297 else
2298 name = vim_strsave(name_arg);
2299 if (name == NULL || name[0] == NUL)
2300 goto cleanup;
2301 if (dict != NULL)
2302 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01002303 icon = dict_get_string(dict, "icon", TRUE);
2304 linehl = dict_get_string(dict, "linehl", TRUE);
2305 text = dict_get_string(dict, "text", TRUE);
2306 texthl = dict_get_string(dict, "texthl", TRUE);
2307 culhl = dict_get_string(dict, "culhl", TRUE);
2308 numhl = dict_get_string(dict, "numhl", TRUE);
LemonBoyb975ddf2024-07-06 18:04:09 +02002309 prio = dict_get_number_def(dict, "priority", -1);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002310 }
2311
LemonBoyb975ddf2024-07-06 18:04:09 +02002312 if (sign_define_by_name(name, icon, linehl, text, texthl, culhl, numhl, prio) == OK)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002313 retval = 0;
2314
2315cleanup:
2316 vim_free(name);
2317 vim_free(icon);
2318 vim_free(linehl);
2319 vim_free(text);
2320 vim_free(texthl);
Bram Moolenaare413ea02021-11-24 16:20:13 +00002321 vim_free(culhl);
James McCoya80aad72021-12-22 19:45:28 +00002322 vim_free(numhl);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002323
2324 return retval;
2325}
2326
2327/*
2328 * Define multiple signs using attributes from list 'l' and store the return
2329 * values in 'retlist'.
2330 */
2331 static void
2332sign_define_multiple(list_T *l, list_T *retlist)
2333{
2334 listitem_T *li;
2335 int retval;
2336
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002337 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002338 {
2339 retval = -1;
2340 if (li->li_tv.v_type == VAR_DICT)
2341 retval = sign_define_from_dict(NULL, li->li_tv.vval.v_dict);
2342 else
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002343 emsg(_(e_dictionary_required));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002344 list_append_number(retlist, retval);
2345 }
2346}
2347
2348/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002349 * "sign_define()" function
2350 */
2351 void
2352f_sign_define(typval_T *argvars, typval_T *rettv)
2353{
2354 char_u *name;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002355
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002356 if (in_vim9script()
2357 && (check_for_string_or_list_arg(argvars, 0) == FAIL
2358 || check_for_opt_dict_arg(argvars, 1) == FAIL))
2359 return;
2360
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002361 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2362 {
2363 // Define multiple signs
Bram Moolenaar93a10962022-06-16 11:42:09 +01002364 if (rettv_list_alloc(rettv) == FAIL)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002365 return;
2366
2367 sign_define_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2368 return;
2369 }
2370
2371 // Define a single sign
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002372 rettv->vval.v_number = -1;
2373
2374 name = tv_get_string_chk(&argvars[0]);
2375 if (name == NULL)
2376 return;
2377
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +01002378 if (check_for_opt_dict_arg(argvars, 1) == FAIL)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002379 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002380
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002381 rettv->vval.v_number = sign_define_from_dict(name,
2382 argvars[1].v_type == VAR_DICT ? argvars[1].vval.v_dict : NULL);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002383}
2384
2385/*
2386 * "sign_getdefined()" function
2387 */
2388 void
2389f_sign_getdefined(typval_T *argvars, typval_T *rettv)
2390{
2391 char_u *name = NULL;
2392
Bram Moolenaar93a10962022-06-16 11:42:09 +01002393 if (rettv_list_alloc_id(rettv, aid_sign_getdefined) == FAIL)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002394 return;
2395
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002396 if (in_vim9script() && check_for_opt_string_arg(argvars, 0) == FAIL)
2397 return;
2398
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002399 if (argvars[0].v_type != VAR_UNKNOWN)
2400 name = tv_get_string(&argvars[0]);
2401
2402 sign_getlist(name, rettv->vval.v_list);
2403}
2404
2405/*
2406 * "sign_getplaced()" function
2407 */
2408 void
2409f_sign_getplaced(typval_T *argvars, typval_T *rettv)
2410{
2411 buf_T *buf = NULL;
2412 dict_T *dict;
2413 dictitem_T *di;
2414 linenr_T lnum = 0;
2415 int sign_id = 0;
2416 char_u *group = NULL;
2417 int notanum = FALSE;
2418
Bram Moolenaar93a10962022-06-16 11:42:09 +01002419 if (rettv_list_alloc_id(rettv, aid_sign_getplaced) == FAIL)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002420 return;
2421
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002422 if (in_vim9script()
Yegappan Lakshmanancd917202021-07-21 19:09:09 +02002423 && (check_for_opt_buffer_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002424 || (argvars[0].v_type != VAR_UNKNOWN
2425 && check_for_opt_dict_arg(argvars, 1) == FAIL)))
2426 return;
2427
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002428 if (argvars[0].v_type != VAR_UNKNOWN)
2429 {
2430 // get signs placed in the specified buffer
2431 buf = get_buf_arg(&argvars[0]);
2432 if (buf == NULL)
2433 return;
2434
2435 if (argvars[1].v_type != VAR_UNKNOWN)
2436 {
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +01002437 if (check_for_nonnull_dict_arg(argvars, 1) == FAIL)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002438 return;
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +01002439 dict = argvars[1].vval.v_dict;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002440 if ((di = dict_find(dict, (char_u *)"lnum", -1)) != NULL)
2441 {
2442 // get signs placed at this line
2443 (void)tv_get_number_chk(&di->di_tv, &notanum);
2444 if (notanum)
2445 return;
2446 lnum = tv_get_lnum(&di->di_tv);
2447 }
2448 if ((di = dict_find(dict, (char_u *)"id", -1)) != NULL)
2449 {
2450 // get sign placed with this identifier
2451 sign_id = (int)tv_get_number_chk(&di->di_tv, &notanum);
2452 if (notanum)
2453 return;
2454 }
2455 if ((di = dict_find(dict, (char_u *)"group", -1)) != NULL)
2456 {
2457 group = tv_get_string_chk(&di->di_tv);
2458 if (group == NULL)
2459 return;
2460 if (*group == '\0') // empty string means global group
2461 group = NULL;
2462 }
2463 }
2464 }
2465
2466 sign_get_placed(buf, lnum, sign_id, group, rettv->vval.v_list);
2467}
2468
2469/*
2470 * "sign_jump()" function
2471 */
2472 void
2473f_sign_jump(typval_T *argvars, typval_T *rettv)
2474{
2475 int sign_id;
2476 char_u *sign_group = NULL;
2477 buf_T *buf;
2478 int notanum = FALSE;
2479
2480 rettv->vval.v_number = -1;
2481
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002482 if (in_vim9script()
2483 && (check_for_number_arg(argvars, 0) == FAIL
2484 || check_for_string_arg(argvars, 1) == FAIL
2485 || check_for_buffer_arg(argvars, 2) == FAIL))
2486 return;
2487
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002488 // Sign identifier
2489 sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
2490 if (notanum)
2491 return;
2492 if (sign_id <= 0)
2493 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00002494 emsg(_(e_invalid_argument));
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002495 return;
2496 }
2497
2498 // Sign group
2499 sign_group = tv_get_string_chk(&argvars[1]);
2500 if (sign_group == NULL)
2501 return;
2502 if (sign_group[0] == '\0')
2503 sign_group = NULL; // global sign group
2504 else
2505 {
2506 sign_group = vim_strsave(sign_group);
2507 if (sign_group == NULL)
2508 return;
2509 }
2510
2511 // Buffer to place the sign
2512 buf = get_buf_arg(&argvars[2]);
2513 if (buf == NULL)
2514 goto cleanup;
2515
2516 rettv->vval.v_number = sign_jump(sign_id, sign_group, buf);
2517
2518cleanup:
2519 vim_free(sign_group);
2520}
2521
2522/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002523 * Place a new sign using the values specified in dict 'dict'. Returns the sign
2524 * identifier if successfully placed, otherwise returns 0.
2525 */
2526 static int
2527sign_place_from_dict(
2528 typval_T *id_tv,
2529 typval_T *group_tv,
2530 typval_T *name_tv,
2531 typval_T *buf_tv,
2532 dict_T *dict)
2533{
2534 int sign_id = 0;
2535 char_u *group = NULL;
2536 char_u *sign_name = NULL;
2537 buf_T *buf = NULL;
2538 dictitem_T *di;
2539 linenr_T lnum = 0;
LemonBoyb975ddf2024-07-06 18:04:09 +02002540 int prio = -1;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002541 int notanum = FALSE;
2542 int ret_sign_id = -1;
2543
2544 // sign identifier
2545 if (id_tv == NULL)
2546 {
2547 di = dict_find(dict, (char_u *)"id", -1);
2548 if (di != NULL)
2549 id_tv = &di->di_tv;
2550 }
2551 if (id_tv == NULL)
2552 sign_id = 0;
2553 else
2554 {
2555 sign_id = tv_get_number_chk(id_tv, &notanum);
2556 if (notanum)
2557 return -1;
2558 if (sign_id < 0)
2559 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00002560 emsg(_(e_invalid_argument));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002561 return -1;
2562 }
2563 }
2564
2565 // sign group
2566 if (group_tv == NULL)
2567 {
2568 di = dict_find(dict, (char_u *)"group", -1);
2569 if (di != NULL)
2570 group_tv = &di->di_tv;
2571 }
2572 if (group_tv == NULL)
2573 group = NULL; // global group
2574 else
2575 {
2576 group = tv_get_string_chk(group_tv);
2577 if (group == NULL)
2578 goto cleanup;
2579 if (group[0] == '\0') // global sign group
2580 group = NULL;
2581 else
2582 {
2583 group = vim_strsave(group);
2584 if (group == NULL)
2585 return -1;
2586 }
2587 }
2588
2589 // sign name
2590 if (name_tv == NULL)
2591 {
2592 di = dict_find(dict, (char_u *)"name", -1);
2593 if (di != NULL)
2594 name_tv = &di->di_tv;
2595 }
2596 if (name_tv == NULL)
2597 goto cleanup;
2598 sign_name = tv_get_string_chk(name_tv);
2599 if (sign_name == NULL)
2600 goto cleanup;
2601
2602 // buffer to place the sign
2603 if (buf_tv == NULL)
2604 {
2605 di = dict_find(dict, (char_u *)"buffer", -1);
2606 if (di != NULL)
2607 buf_tv = &di->di_tv;
2608 }
2609 if (buf_tv == NULL)
2610 goto cleanup;
2611 buf = get_buf_arg(buf_tv);
2612 if (buf == NULL)
2613 goto cleanup;
2614
2615 // line number of the sign
2616 di = dict_find(dict, (char_u *)"lnum", -1);
2617 if (di != NULL)
2618 {
Bram Moolenaar42aff462019-08-21 13:20:29 +02002619 lnum = tv_get_lnum(&di->di_tv);
2620 if (lnum <= 0)
2621 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00002622 emsg(_(e_invalid_argument));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002623 goto cleanup;
Bram Moolenaar42aff462019-08-21 13:20:29 +02002624 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002625 }
2626
2627 // sign priority
2628 di = dict_find(dict, (char_u *)"priority", -1);
2629 if (di != NULL)
2630 {
2631 prio = (int)tv_get_number_chk(&di->di_tv, &notanum);
2632 if (notanum)
2633 goto cleanup;
2634 }
2635
2636 if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK)
2637 ret_sign_id = sign_id;
2638
2639cleanup:
2640 vim_free(group);
2641
2642 return ret_sign_id;
2643}
2644
2645/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002646 * "sign_place()" function
2647 */
2648 void
2649f_sign_place(typval_T *argvars, typval_T *rettv)
2650{
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002651 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002652
2653 rettv->vval.v_number = -1;
2654
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002655 if (in_vim9script()
2656 && (check_for_number_arg(argvars, 0) == FAIL
2657 || check_for_string_arg(argvars, 1) == FAIL
2658 || check_for_string_arg(argvars, 2) == FAIL
2659 || check_for_buffer_arg(argvars, 3) == FAIL
2660 || check_for_opt_dict_arg(argvars, 4) == FAIL))
2661 return;
2662
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +01002663 if (argvars[4].v_type != VAR_UNKNOWN)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002664 {
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +01002665 if (check_for_nonnull_dict_arg(argvars, 4) == FAIL)
2666 return;
2667 dict = argvars[4].vval.v_dict;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002668 }
2669
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002670 rettv->vval.v_number = sign_place_from_dict(&argvars[0], &argvars[1],
2671 &argvars[2], &argvars[3], dict);
2672}
2673
2674/*
2675 * "sign_placelist()" function. Place multiple signs.
2676 */
2677 void
2678f_sign_placelist(typval_T *argvars, typval_T *rettv)
2679{
2680 listitem_T *li;
2681 int sign_id;
2682
Bram Moolenaar93a10962022-06-16 11:42:09 +01002683 if (rettv_list_alloc(rettv) == FAIL)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002684 return;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002685
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002686 if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
2687 return;
2688
Bram Moolenaard83392a2022-09-01 12:22:46 +01002689 if (check_for_list_arg(argvars, 0) == FAIL)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002690 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002691
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002692 // Process the List of sign attributes
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002693 FOR_ALL_LIST_ITEMS(argvars[0].vval.v_list, li)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002694 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002695 sign_id = -1;
2696 if (li->li_tv.v_type == VAR_DICT)
2697 sign_id = sign_place_from_dict(NULL, NULL, NULL, NULL,
2698 li->li_tv.vval.v_dict);
2699 else
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002700 emsg(_(e_dictionary_required));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002701 list_append_number(rettv->vval.v_list, sign_id);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002702 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002703}
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002704
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002705/*
2706 * Undefine multiple signs
2707 */
2708 static void
2709sign_undefine_multiple(list_T *l, list_T *retlist)
2710{
2711 char_u *name;
2712 listitem_T *li;
2713 int retval;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002714
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002715 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002716 {
2717 retval = -1;
2718 name = tv_get_string_chk(&li->li_tv);
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002719 if (name != NULL && (sign_undefine_by_name(name, TRUE) == OK))
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002720 retval = 0;
2721 list_append_number(retlist, retval);
2722 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002723}
2724
2725/*
2726 * "sign_undefine()" function
2727 */
2728 void
2729f_sign_undefine(typval_T *argvars, typval_T *rettv)
2730{
2731 char_u *name;
2732
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002733 if (in_vim9script()
2734 && check_for_opt_string_or_list_arg(argvars, 0) == FAIL)
2735 return;
2736
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002737 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2738 {
2739 // Undefine multiple signs
Bram Moolenaar93a10962022-06-16 11:42:09 +01002740 if (rettv_list_alloc(rettv) == FAIL)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002741 return;
2742
2743 sign_undefine_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2744 return;
2745 }
2746
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002747 rettv->vval.v_number = -1;
2748
2749 if (argvars[0].v_type == VAR_UNKNOWN)
2750 {
2751 // Free all the signs
2752 free_signs();
2753 rettv->vval.v_number = 0;
2754 }
2755 else
2756 {
2757 // Free only the specified sign
2758 name = tv_get_string_chk(&argvars[0]);
2759 if (name == NULL)
2760 return;
2761
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002762 if (sign_undefine_by_name(name, TRUE) == OK)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002763 rettv->vval.v_number = 0;
2764 }
2765}
2766
2767/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002768 * Unplace the sign with attributes specified in 'dict'. Returns 0 on success
2769 * and -1 on failure.
2770 */
2771 static int
2772sign_unplace_from_dict(typval_T *group_tv, dict_T *dict)
2773{
2774 dictitem_T *di;
2775 int sign_id = 0;
2776 buf_T *buf = NULL;
2777 char_u *group = NULL;
2778 int retval = -1;
2779
2780 // sign group
2781 if (group_tv != NULL)
2782 group = tv_get_string(group_tv);
2783 else
Bram Moolenaard61efa52022-07-23 09:52:04 +01002784 group = dict_get_string(dict, "group", FALSE);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002785 if (group != NULL)
2786 {
2787 if (group[0] == '\0') // global sign group
2788 group = NULL;
2789 else
2790 {
2791 group = vim_strsave(group);
2792 if (group == NULL)
2793 return -1;
2794 }
2795 }
2796
2797 if (dict != NULL)
2798 {
2799 if ((di = dict_find(dict, (char_u *)"buffer", -1)) != NULL)
2800 {
2801 buf = get_buf_arg(&di->di_tv);
2802 if (buf == NULL)
2803 goto cleanup;
2804 }
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002805 if (dict_has_key(dict, "id"))
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002806 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01002807 sign_id = dict_get_number(dict, "id");
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002808 if (sign_id <= 0)
2809 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00002810 emsg(_(e_invalid_argument));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002811 goto cleanup;
2812 }
2813 }
2814 }
2815
2816 if (buf == NULL)
2817 {
2818 // Delete the sign in all the buffers
2819 retval = 0;
2820 FOR_ALL_BUFFERS(buf)
2821 if (sign_unplace(sign_id, group, buf, 0) != OK)
2822 retval = -1;
2823 }
2824 else if (sign_unplace(sign_id, group, buf, 0) == OK)
2825 retval = 0;
2826
2827cleanup:
2828 vim_free(group);
2829
2830 return retval;
2831}
2832
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002833 sign_entry_T *
2834get_first_valid_sign(win_T *wp)
2835{
2836 sign_entry_T *sign = wp->w_buffer->b_signlist;
2837
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002838# ifdef FEAT_PROP_POPUP
Bram Moolenaar72570732019-11-30 14:21:53 +01002839 while (sign != NULL && !sign_group_for_window(sign, wp))
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002840 sign = sign->se_next;
2841# endif
2842 return sign;
2843}
2844
2845/*
2846 * Return TRUE when window "wp" has a column to draw signs in.
2847 */
2848 int
2849signcolumn_on(win_T *wp)
2850{
2851 // If 'signcolumn' is set to 'number', signs are displayed in the 'number'
2852 // column (if present). Otherwise signs are to be displayed in the sign
2853 // column.
2854 if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u')
2855 return get_first_valid_sign(wp) != NULL && !wp->w_p_nu && !wp->w_p_rnu;
2856
2857 if (*wp->w_p_scl == 'n')
2858 return FALSE;
2859 if (*wp->w_p_scl == 'y')
2860 return TRUE;
2861 return (get_first_valid_sign(wp) != NULL
2862# ifdef FEAT_NETBEANS_INTG
2863 || wp->w_buffer->b_has_sign_column
2864# endif
2865 );
2866}
2867
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002868/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002869 * "sign_unplace()" function
2870 */
2871 void
2872f_sign_unplace(typval_T *argvars, typval_T *rettv)
2873{
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002874 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002875
2876 rettv->vval.v_number = -1;
2877
Yegappan Lakshmanan8deb2b32022-09-02 15:15:27 +01002878 if ((check_for_string_arg(argvars, 0) == FAIL
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002879 || check_for_opt_dict_arg(argvars, 1) == FAIL))
2880 return;
2881
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002882 if (argvars[1].v_type != VAR_UNKNOWN)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002883 dict = argvars[1].vval.v_dict;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002884
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002885 rettv->vval.v_number = sign_unplace_from_dict(&argvars[0], dict);
2886}
2887
2888/*
2889 * "sign_unplacelist()" function
2890 */
2891 void
2892f_sign_unplacelist(typval_T *argvars, typval_T *rettv)
2893{
2894 listitem_T *li;
2895 int retval;
2896
Bram Moolenaar93a10962022-06-16 11:42:09 +01002897 if (rettv_list_alloc(rettv) == FAIL)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002898 return;
2899
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002900 if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
2901 return;
2902
Bram Moolenaard83392a2022-09-01 12:22:46 +01002903 if (check_for_list_arg(argvars, 0) == FAIL)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002904 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002905
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002906 FOR_ALL_LIST_ITEMS(argvars[0].vval.v_list, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002907 {
2908 retval = -1;
2909 if (li->li_tv.v_type == VAR_DICT)
2910 retval = sign_unplace_from_dict(NULL, li->li_tv.vval.v_dict);
2911 else
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002912 emsg(_(e_dictionary_required));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002913 list_append_number(rettv->vval.v_list, retval);
2914 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002915}
2916
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002917#endif // FEAT_SIGNS