blob: 42592a41c87e4154abf5641ed5aacd24935b77a5 [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
Bram Moolenaarbbea4702019-01-01 13:20:31 +010037};
38
39static sign_T *first_sign = NULL;
40static int next_sign_typenr = 1;
41
42static void sign_list_defined(sign_T *sp);
43static void sign_undefine(sign_T *sp, sign_T *sp_prev);
44
45static char *cmds[] = {
46 "define",
47# define SIGNCMD_DEFINE 0
48 "undefine",
49# define SIGNCMD_UNDEFINE 1
50 "list",
51# define SIGNCMD_LIST 2
52 "place",
53# define SIGNCMD_PLACE 3
54 "unplace",
55# define SIGNCMD_UNPLACE 4
56 "jump",
57# define SIGNCMD_JUMP 5
58 NULL
59# define SIGNCMD_LAST 6
60};
61
Bram Moolenaaraeea7212020-04-02 18:50:46 +020062#define FOR_ALL_SIGNS(sp) \
63 for ((sp) = first_sign; (sp) != NULL; (sp) = (sp)->sn_next)
64
Bram Moolenaarbbea4702019-01-01 13:20:31 +010065static hashtab_T sg_table; // sign group (signgroup_T) hashtable
66static int next_sign_id = 1; // next sign id in the global group
67
68/*
69 * Initialize data needed for managing signs
70 */
71 void
72init_signs(void)
73{
74 hash_init(&sg_table); // sign group hash table
75}
76
77/*
78 * A new sign in group 'groupname' is added. If the group is not present,
79 * create it. Otherwise reference the group.
80 */
81 static signgroup_T *
82sign_group_ref(char_u *groupname)
83{
84 hash_T hash;
85 hashitem_T *hi;
86 signgroup_T *group;
87
88 hash = hash_hash(groupname);
89 hi = hash_lookup(&sg_table, groupname, hash);
90 if (HASHITEM_EMPTY(hi))
91 {
92 // new group
Bram Moolenaar47ed5532019-08-08 20:49:14 +020093 group = alloc(offsetof(signgroup_T, sg_name) + STRLEN(groupname) + 1);
Bram Moolenaarbbea4702019-01-01 13:20:31 +010094 if (group == NULL)
95 return NULL;
96 STRCPY(group->sg_name, groupname);
Bram Moolenaar6656c2e2019-10-24 15:00:04 +020097 group->sg_refcount = 1;
98 group->sg_next_sign_id = 1;
Bram Moolenaarbbea4702019-01-01 13:20:31 +010099 hash_add_item(&sg_table, hi, group->sg_name, hash);
100 }
101 else
102 {
103 // existing group
104 group = HI2SG(hi);
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200105 group->sg_refcount++;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100106 }
107
108 return group;
109}
110
111/*
112 * A sign in group 'groupname' is removed. If all the signs in this group are
113 * removed, then remove the group.
114 */
115 static void
116sign_group_unref(char_u *groupname)
117{
118 hashitem_T *hi;
119 signgroup_T *group;
120
121 hi = hash_find(&sg_table, groupname);
122 if (!HASHITEM_EMPTY(hi))
123 {
124 group = HI2SG(hi);
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200125 group->sg_refcount--;
126 if (group->sg_refcount == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100127 {
128 // All the signs in this group are removed
129 hash_remove(&sg_table, hi);
130 vim_free(group);
131 }
132 }
133}
134
135/*
136 * Returns TRUE if 'sign' is in 'group'.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200137 * A sign can either be in the global group (sign->se_group == NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100138 * or in a named group. If 'group' is '*', then the sign is part of the group.
139 */
140 static int
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200141sign_in_group(sign_entry_T *sign, char_u *group)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100142{
143 return ((group != NULL && STRCMP(group, "*") == 0)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200144 || (group == NULL && sign->se_group == NULL)
145 || (group != NULL && sign->se_group != NULL
Bram Moolenaar72570732019-11-30 14:21:53 +0100146 && STRCMP(group, sign->se_group->sg_name) == 0));
147}
148
149/*
150 * Return TRUE if "sign" is to be displayed in window "wp".
151 * If the group name starts with "PopUp" it only shows in a popup window.
152 */
153 static int
154sign_group_for_window(sign_entry_T *sign, win_T *wp)
155{
156 int for_popup = sign->se_group != NULL
157 && STRNCMP("PopUp", sign->se_group->sg_name, 5) == 0;
158
159 return WIN_IS_POPUP(wp) ? for_popup : !for_popup;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100160}
161
162/*
163 * Get the next free sign identifier in the specified group
164 */
165 static int
166sign_group_get_next_signid(buf_T *buf, char_u *groupname)
167{
168 int id = 1;
169 signgroup_T *group = NULL;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200170 sign_entry_T *sign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100171 hashitem_T *hi;
172 int found = FALSE;
173
174 if (groupname != NULL)
175 {
176 hi = hash_find(&sg_table, groupname);
177 if (HASHITEM_EMPTY(hi))
178 return id;
179 group = HI2SG(hi);
180 }
181
Bram Moolenaarb5443cc2019-01-15 20:19:40 +0100182 // Search for the next usable sign identifier
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100183 while (!found)
184 {
185 if (group == NULL)
186 id = next_sign_id++; // global group
187 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200188 id = group->sg_next_sign_id++;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100189
190 // Check whether this sign is already placed in the buffer
191 found = TRUE;
192 FOR_ALL_SIGNS_IN_BUF(buf, sign)
193 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200194 if (id == sign->se_id && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100195 {
196 found = FALSE; // sign identifier is in use
197 break;
198 }
199 }
200 }
201
202 return id;
203}
204
205/*
206 * Insert a new sign into the signlist for buffer 'buf' between the 'prev' and
207 * 'next' signs.
208 */
209 static void
210insert_sign(
211 buf_T *buf, // buffer to store sign in
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200212 sign_entry_T *prev, // previous sign entry
213 sign_entry_T *next, // next sign entry
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100214 int id, // sign ID
215 char_u *group, // sign group; NULL for global group
216 int prio, // sign priority
217 linenr_T lnum, // line number which gets the mark
218 int typenr) // typenr of sign we are adding
219{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200220 sign_entry_T *newsign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100221
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200222 newsign = lalloc_id(sizeof(sign_entry_T), FALSE, aid_insert_sign);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100223 if (newsign != NULL)
224 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200225 newsign->se_id = id;
226 newsign->se_lnum = lnum;
227 newsign->se_typenr = typenr;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100228 if (group != NULL)
229 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200230 newsign->se_group = sign_group_ref(group);
231 if (newsign->se_group == NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100232 {
233 vim_free(newsign);
234 return;
235 }
236 }
237 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200238 newsign->se_group = NULL;
239 newsign->se_priority = prio;
240 newsign->se_next = next;
241 newsign->se_prev = prev;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100242 if (next != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200243 next->se_prev = newsign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100244
245 if (prev == NULL)
246 {
247 // When adding first sign need to redraw the windows to create the
248 // column for signs.
249 if (buf->b_signlist == NULL)
250 {
251 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200252 changed_line_abv_curs();
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100253 }
254
255 // first sign in signlist
256 buf->b_signlist = newsign;
257#ifdef FEAT_NETBEANS_INTG
258 if (netbeans_active())
259 buf->b_has_sign_column = TRUE;
260#endif
261 }
262 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200263 prev->se_next = newsign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100264 }
265}
266
267/*
268 * Insert a new sign sorted by line number and sign priority.
269 */
270 static void
271insert_sign_by_lnum_prio(
272 buf_T *buf, // buffer to store sign in
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200273 sign_entry_T *prev, // previous sign entry
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100274 int id, // sign ID
275 char_u *group, // sign group; NULL for global group
276 int prio, // sign priority
277 linenr_T lnum, // line number which gets the mark
278 int typenr) // typenr of sign we are adding
279{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200280 sign_entry_T *sign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100281
282 // keep signs sorted by lnum and by priority: insert new sign at
283 // the proper position in the list for this lnum.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200284 while (prev != NULL && prev->se_lnum == lnum && prev->se_priority <= prio)
285 prev = prev->se_prev;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100286 if (prev == NULL)
287 sign = buf->b_signlist;
288 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200289 sign = prev->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100290
291 insert_sign(buf, prev, sign, id, group, prio, lnum, typenr);
292}
293
294/*
Bram Moolenaar4e038572019-07-04 18:28:35 +0200295 * Lookup a sign by typenr. Returns NULL if sign is not found.
296 */
297 static sign_T *
298find_sign_by_typenr(int typenr)
299{
300 sign_T *sp;
301
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200302 FOR_ALL_SIGNS(sp)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200303 if (sp->sn_typenr == typenr)
304 return sp;
305 return NULL;
306}
307
308/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100309 * Get the name of a sign by its typenr.
310 */
311 static char_u *
312sign_typenr2name(int typenr)
313{
314 sign_T *sp;
315
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200316 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100317 if (sp->sn_typenr == typenr)
318 return sp->sn_name;
319 return (char_u *)_("[Deleted]");
320}
321
322/*
323 * Return information about a sign in a Dict
324 */
325 static dict_T *
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200326sign_get_info(sign_entry_T *sign)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100327{
328 dict_T *d;
329
330 if ((d = dict_alloc_id(aid_sign_getinfo)) == NULL)
331 return NULL;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200332 dict_add_number(d, "id", sign->se_id);
333 dict_add_string(d, "group", (sign->se_group == NULL) ?
334 (char_u *)"" : sign->se_group->sg_name);
335 dict_add_number(d, "lnum", sign->se_lnum);
336 dict_add_string(d, "name", sign_typenr2name(sign->se_typenr));
337 dict_add_number(d, "priority", sign->se_priority);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100338
339 return d;
340}
341
342/*
Bram Moolenaar64416122019-06-07 21:37:13 +0200343 * Sort the signs placed on the same line as "sign" by priority. Invoked after
344 * changing the priority of an already placed sign. Assumes the signs in the
345 * buffer are sorted by line number and priority.
346 */
347 static void
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200348sign_sort_by_prio_on_line(buf_T *buf, sign_entry_T *sign)
Bram Moolenaar64416122019-06-07 21:37:13 +0200349{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200350 sign_entry_T *p = NULL;
Bram Moolenaar64416122019-06-07 21:37:13 +0200351
352 // If there is only one sign in the buffer or only one sign on the line or
353 // the sign is already sorted by priority, then return.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200354 if ((sign->se_prev == NULL
355 || sign->se_prev->se_lnum != sign->se_lnum
356 || sign->se_prev->se_priority > sign->se_priority)
357 && (sign->se_next == NULL
358 || sign->se_next->se_lnum != sign->se_lnum
359 || sign->se_next->se_priority < sign->se_priority))
Bram Moolenaar64416122019-06-07 21:37:13 +0200360 return;
361
362 // One or more signs on the same line as 'sign'
363 // Find a sign after which 'sign' should be inserted
364
365 // First search backward for a sign with higher priority on the same line
366 p = sign;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200367 while (p->se_prev != NULL && p->se_prev->se_lnum == sign->se_lnum
368 && p->se_prev->se_priority <= sign->se_priority)
369 p = p->se_prev;
Bram Moolenaar64416122019-06-07 21:37:13 +0200370
371 if (p == sign)
372 {
373 // Sign not found. Search forward for a sign with priority just before
374 // 'sign'.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200375 p = sign->se_next;
376 while (p->se_next != NULL && p->se_next->se_lnum == sign->se_lnum
377 && p->se_next->se_priority > sign->se_priority)
378 p = p->se_next;
Bram Moolenaar64416122019-06-07 21:37:13 +0200379 }
380
381 // Remove 'sign' from the list
382 if (buf->b_signlist == sign)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200383 buf->b_signlist = sign->se_next;
384 if (sign->se_prev != NULL)
385 sign->se_prev->se_next = sign->se_next;
386 if (sign->se_next != NULL)
387 sign->se_next->se_prev = sign->se_prev;
388 sign->se_prev = NULL;
389 sign->se_next = NULL;
Bram Moolenaar64416122019-06-07 21:37:13 +0200390
391 // Re-insert 'sign' at the right place
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200392 if (p->se_priority <= sign->se_priority)
Bram Moolenaar64416122019-06-07 21:37:13 +0200393 {
394 // 'sign' has a higher priority and should be inserted before 'p'
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200395 sign->se_prev = p->se_prev;
396 sign->se_next = p;
397 p->se_prev = sign;
398 if (sign->se_prev != NULL)
399 sign->se_prev->se_next = sign;
Bram Moolenaar64416122019-06-07 21:37:13 +0200400 if (buf->b_signlist == p)
401 buf->b_signlist = sign;
402 }
403 else
404 {
405 // 'sign' has a lower priority and should be inserted after 'p'
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200406 sign->se_prev = p;
407 sign->se_next = p->se_next;
408 p->se_next = sign;
409 if (sign->se_next != NULL)
410 sign->se_next->se_prev = sign;
Bram Moolenaar64416122019-06-07 21:37:13 +0200411 }
412}
413
414/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100415 * Add the sign into the signlist. Find the right spot to do it though.
416 */
417 static void
418buf_addsign(
419 buf_T *buf, // buffer to store sign in
420 int id, // sign ID
421 char_u *groupname, // sign group
422 int prio, // sign priority
423 linenr_T lnum, // line number which gets the mark
424 int typenr) // typenr of sign we are adding
425{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200426 sign_entry_T *sign; // a sign in the signlist
427 sign_entry_T *prev; // the previous sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100428
429 prev = NULL;
430 FOR_ALL_SIGNS_IN_BUF(buf, sign)
431 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200432 if (lnum == sign->se_lnum && id == sign->se_id
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100433 && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100434 {
435 // Update an existing sign
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200436 sign->se_typenr = typenr;
437 sign->se_priority = prio;
Bram Moolenaar64416122019-06-07 21:37:13 +0200438 sign_sort_by_prio_on_line(buf, sign);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100439 return;
440 }
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200441 else if (lnum < sign->se_lnum)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100442 {
443 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio,
444 lnum, typenr);
445 return;
446 }
447 prev = sign;
448 }
449
450 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr);
451 return;
452}
453
454/*
455 * For an existing, placed sign "markId" change the type to "typenr".
456 * Returns the line number of the sign, or zero if the sign is not found.
457 */
458 static linenr_T
459buf_change_sign_type(
460 buf_T *buf, // buffer to store sign in
461 int markId, // sign ID
462 char_u *group, // sign group
Bram Moolenaar809ce4d2019-07-13 21:21:40 +0200463 int typenr, // typenr of sign we are adding
464 int prio) // sign priority
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100465{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200466 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100467
468 FOR_ALL_SIGNS_IN_BUF(buf, sign)
469 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200470 if (sign->se_id == markId && sign_in_group(sign, group))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100471 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200472 sign->se_typenr = typenr;
473 sign->se_priority = prio;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +0200474 sign_sort_by_prio_on_line(buf, sign);
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200475 return sign->se_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100476 }
477 }
478
479 return (linenr_T)0;
480}
481
482/*
Bram Moolenaar4e038572019-07-04 18:28:35 +0200483 * Return the attributes of the first sign placed on line 'lnum' in buffer
484 * 'buf'. Used when refreshing the screen. Returns TRUE if a sign is found on
485 * 'lnum', FALSE otherwise.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100486 */
487 int
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100488buf_get_signattrs(win_T *wp, linenr_T lnum, sign_attrs_T *sattr)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100489{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200490 sign_entry_T *sign;
491 sign_T *sp;
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100492 buf_T *buf = wp->w_buffer;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200493
Bram Moolenaara80faa82020-04-12 19:37:17 +0200494 CLEAR_POINTER(sattr);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100495
496 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200497 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200498 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200499 // Signs are sorted by line number in the buffer. No need to check
500 // for signs after the specified line number 'lnum'.
501 break;
502
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100503 if (sign->se_lnum == lnum
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100504# ifdef FEAT_PROP_POPUP
Bram Moolenaar72570732019-11-30 14:21:53 +0100505 && sign_group_for_window(sign, wp)
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100506# endif
507 )
Bram Moolenaar4e038572019-07-04 18:28:35 +0200508 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200509 sattr->sat_typenr = sign->se_typenr;
510 sp = find_sign_by_typenr(sign->se_typenr);
Bram Moolenaar4e038572019-07-04 18:28:35 +0200511 if (sp == NULL)
512 return FALSE;
513
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100514# ifdef FEAT_SIGN_ICONS
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200515 sattr->sat_icon = sp->sn_image;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100516# endif
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200517 sattr->sat_text = sp->sn_text;
518 if (sattr->sat_text != NULL && sp->sn_text_hl > 0)
519 sattr->sat_texthl = syn_id2attr(sp->sn_text_hl);
Bram Moolenaar4e038572019-07-04 18:28:35 +0200520 if (sp->sn_line_hl > 0)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200521 sattr->sat_linehl = syn_id2attr(sp->sn_line_hl);
Bram Moolenaare413ea02021-11-24 16:20:13 +0000522 if (sp->sn_cul_hl > 0)
523 sattr->sat_culhl = syn_id2attr(sp->sn_cul_hl);
James McCoya80aad72021-12-22 19:45:28 +0000524 if (sp->sn_num_hl > 0)
525 sattr->sat_numhl = syn_id2attr(sp->sn_num_hl);
Bram Moolenaar2f122842020-08-31 23:18:00 +0200526 sattr->sat_priority = sign->se_priority;
Bram Moolenaara2f6e422020-02-19 17:13:04 +0100527
528 // If there is another sign next with the same priority, may
529 // combine the text and the line highlighting.
530 if (sign->se_next != NULL
531 && sign->se_next->se_priority == sign->se_priority
532 && sign->se_next->se_lnum == sign->se_lnum)
533 {
534 sign_T *next_sp = find_sign_by_typenr(sign->se_next->se_typenr);
535
536 if (next_sp != NULL)
537 {
538 if (sattr->sat_icon == NULL && sattr->sat_text == NULL)
539 {
540# ifdef FEAT_SIGN_ICONS
541 sattr->sat_icon = next_sp->sn_image;
542# endif
543 sattr->sat_text = next_sp->sn_text;
544 }
545 if (sp->sn_text_hl <= 0 && next_sp->sn_text_hl > 0)
546 sattr->sat_texthl = syn_id2attr(next_sp->sn_text_hl);
547 if (sp->sn_line_hl <= 0 && next_sp->sn_line_hl > 0)
548 sattr->sat_linehl = syn_id2attr(next_sp->sn_line_hl);
Bram Moolenaare413ea02021-11-24 16:20:13 +0000549 if (sp->sn_cul_hl <= 0 && next_sp->sn_cul_hl > 0)
550 sattr->sat_culhl = syn_id2attr(next_sp->sn_cul_hl);
James McCoya80aad72021-12-22 19:45:28 +0000551 if (sp->sn_num_hl <= 0 && next_sp->sn_num_hl > 0)
552 sattr->sat_numhl = syn_id2attr(next_sp->sn_num_hl);
Bram Moolenaara2f6e422020-02-19 17:13:04 +0100553 }
554 }
Bram Moolenaar4e038572019-07-04 18:28:35 +0200555 return TRUE;
556 }
557 }
558 return FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100559}
560
561/*
562 * Delete sign 'id' in group 'group' from buffer 'buf'.
563 * If 'id' is zero, then delete all the signs in group 'group'. Otherwise
564 * delete only the specified sign.
565 * If 'group' is '*', then delete the sign in all the groups. If 'group' is
566 * NULL, then delete the sign in the global group. Otherwise delete the sign in
567 * the specified group.
568 * Returns the line number of the deleted sign. If multiple signs are deleted,
569 * then returns the line number of the last sign deleted.
570 */
571 linenr_T
572buf_delsign(
573 buf_T *buf, // buffer sign is stored in
574 linenr_T atlnum, // sign at this line, 0 - at any line
575 int id, // sign id
576 char_u *group) // sign group
577{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200578 sign_entry_T **lastp; // pointer to pointer to current sign
579 sign_entry_T *sign; // a sign in a b_signlist
580 sign_entry_T *next; // the next sign in a b_signlist
581 linenr_T lnum; // line number whose sign was deleted
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100582
583 lastp = &buf->b_signlist;
584 lnum = 0;
585 for (sign = buf->b_signlist; sign != NULL; sign = next)
586 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200587 next = sign->se_next;
588 if ((id == 0 || sign->se_id == id)
589 && (atlnum == 0 || sign->se_lnum == atlnum)
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100590 && sign_in_group(sign, group))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100591
592 {
593 *lastp = next;
594 if (next != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200595 next->se_prev = sign->se_prev;
596 lnum = sign->se_lnum;
597 if (sign->se_group != NULL)
598 sign_group_unref(sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100599 vim_free(sign);
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100600 redraw_buf_line_later(buf, lnum);
601
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100602 // Check whether only one sign needs to be deleted
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100603 // If deleting a sign with a specific identifier in a particular
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100604 // group or deleting any sign at a particular line number, delete
605 // only one sign.
606 if (group == NULL
607 || (*group != '*' && id != 0)
608 || (*group == '*' && atlnum != 0))
609 break;
610 }
611 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200612 lastp = &sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100613 }
614
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100615 // When deleting the last sign the cursor position may change, because the
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100616 // sign columns no longer shows. And the 'signcolumn' may be hidden.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100617 if (buf->b_signlist == NULL)
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100618 {
619 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200620 changed_line_abv_curs();
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100621 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100622
623 return lnum;
624}
625
626
627/*
628 * Find the line number of the sign with the requested id in group 'group'. If
629 * the sign does not exist, return 0 as the line number. This will still let
630 * the correct file get loaded.
631 */
632 int
633buf_findsign(
634 buf_T *buf, // buffer to store sign in
635 int id, // sign ID
636 char_u *group) // sign group
637{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200638 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100639
640 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200641 if (sign->se_id == id && sign_in_group(sign, group))
642 return sign->se_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100643
644 return 0;
645}
646
647/*
648 * Return the sign at line 'lnum' in buffer 'buf'. Returns NULL if a sign is
649 * not found at the line. If 'groupname' is NULL, searches in the global group.
650 */
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200651 static sign_entry_T *
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100652buf_getsign_at_line(
653 buf_T *buf, // buffer whose sign we are searching for
654 linenr_T lnum, // line number of sign
655 char_u *groupname) // sign group name
656{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200657 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100658
659 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200660 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200661 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200662 // Signs are sorted by line number in the buffer. No need to check
663 // for signs after the specified line number 'lnum'.
664 break;
665
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200666 if (sign->se_lnum == lnum && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100667 return sign;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200668 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100669
670 return NULL;
671}
672
673/*
674 * Return the identifier of the sign at line number 'lnum' in buffer 'buf'.
675 */
676 int
677buf_findsign_id(
678 buf_T *buf, // buffer whose sign we are searching for
679 linenr_T lnum, // line number of sign
680 char_u *groupname) // sign group name
681{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200682 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100683
684 sign = buf_getsign_at_line(buf, lnum, groupname);
685 if (sign != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200686 return sign->se_id;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100687
688 return 0;
689}
690
691# if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
692/*
693 * See if a given type of sign exists on a specific line.
694 */
695 int
696buf_findsigntype_id(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100697 buf_T *buf, // buffer whose sign we are searching for
698 linenr_T lnum, // line number of sign
699 int typenr) // sign type number
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100700{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200701 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100702
703 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200704 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200705 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200706 // Signs are sorted by line number in the buffer. No need to check
707 // for signs after the specified line number 'lnum'.
708 break;
709
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200710 if (sign->se_lnum == lnum && sign->se_typenr == typenr)
711 return sign->se_id;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200712 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100713
714 return 0;
715}
716
717
718# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
719/*
720 * Return the number of icons on the given line.
721 */
722 int
723buf_signcount(buf_T *buf, linenr_T lnum)
724{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200725 sign_entry_T *sign; // a sign in the signlist
726 int count = 0;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100727
728 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200729 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200730 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200731 // Signs are sorted by line number in the buffer. No need to check
732 // for signs after the specified line number 'lnum'.
733 break;
734
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200735 if (sign->se_lnum == lnum)
736 if (sign_get_image(sign->se_typenr) != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100737 count++;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200738 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100739
740 return count;
741}
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100742# endif // FEAT_SIGN_ICONS
743# endif // FEAT_NETBEANS_INTG
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100744
745/*
746 * Delete signs in group 'group' in buffer "buf". If 'group' is '*', then
747 * delete all the signs.
748 */
749 void
750buf_delete_signs(buf_T *buf, char_u *group)
751{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200752 sign_entry_T *sign;
753 sign_entry_T **lastp; // pointer to pointer to current sign
754 sign_entry_T *next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100755
756 // When deleting the last sign need to redraw the windows to remove the
757 // sign column. Not when curwin is NULL (this means we're exiting).
758 if (buf->b_signlist != NULL && curwin != NULL)
759 {
760 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200761 changed_line_abv_curs();
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100762 }
763
764 lastp = &buf->b_signlist;
765 for (sign = buf->b_signlist; sign != NULL; sign = next)
766 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200767 next = sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100768 if (sign_in_group(sign, group))
769 {
770 *lastp = next;
771 if (next != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200772 next->se_prev = sign->se_prev;
773 if (sign->se_group != NULL)
774 sign_group_unref(sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100775 vim_free(sign);
776 }
777 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200778 lastp = &sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100779 }
780}
781
782/*
783 * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers.
784 */
785 static void
786sign_list_placed(buf_T *rbuf, char_u *sign_group)
787{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200788 buf_T *buf;
789 sign_entry_T *sign;
790 char lbuf[MSG_BUF_LEN];
791 char group[MSG_BUF_LEN];
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100792
Bram Moolenaar32526b32019-01-19 17:43:09 +0100793 msg_puts_title(_("\n--- Signs ---"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100794 msg_putchar('\n');
795 if (rbuf == NULL)
796 buf = firstbuf;
797 else
798 buf = rbuf;
799 while (buf != NULL && !got_int)
800 {
801 if (buf->b_signlist != NULL)
802 {
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100803 vim_snprintf(lbuf, MSG_BUF_LEN, _("Signs for %s:"), buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100804 msg_puts_attr(lbuf, HL_ATTR(HLF_D));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100805 msg_putchar('\n');
806 }
807 FOR_ALL_SIGNS_IN_BUF(buf, sign)
808 {
809 if (got_int)
810 break;
811 if (!sign_in_group(sign, sign_group))
812 continue;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200813 if (sign->se_group != NULL)
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100814 vim_snprintf(group, MSG_BUF_LEN, _(" group=%s"),
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200815 sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100816 else
817 group[0] = '\0';
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100818 vim_snprintf(lbuf, MSG_BUF_LEN,
819 _(" line=%ld id=%d%s name=%s priority=%d"),
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200820 (long)sign->se_lnum, sign->se_id, group,
821 sign_typenr2name(sign->se_typenr), sign->se_priority);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100822 msg_puts(lbuf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100823 msg_putchar('\n');
824 }
825 if (rbuf != NULL)
826 break;
827 buf = buf->b_next;
828 }
829}
830
831/*
832 * Adjust a placed sign for inserted/deleted lines.
833 */
834 void
835sign_mark_adjust(
836 linenr_T line1,
837 linenr_T line2,
838 long amount,
839 long amount_after)
840{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200841 sign_entry_T *sign; // a sign in a b_signlist
842 linenr_T new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100843
844 FOR_ALL_SIGNS_IN_BUF(curbuf, sign)
845 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100846 // Ignore changes to lines after the sign
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200847 if (sign->se_lnum < line1)
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100848 continue;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200849 new_lnum = sign->se_lnum;
850 if (sign->se_lnum >= line1 && sign->se_lnum <= line2)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100851 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100852 if (amount != MAXLNUM)
853 new_lnum += amount;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100854 }
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200855 else if (sign->se_lnum > line2)
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100856 // Lines inserted or deleted before the sign
857 new_lnum += amount_after;
858
859 // If the new sign line number is past the last line in the buffer,
860 // then don't adjust the line number. Otherwise, it will always be past
861 // the last line and will not be visible.
862 if (new_lnum <= curbuf->b_ml.ml_line_count)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200863 sign->se_lnum = new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100864 }
865}
866
867/*
868 * Find index of a ":sign" subcmd from its name.
869 * "*end_cmd" must be writable.
870 */
871 static int
872sign_cmd_idx(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100873 char_u *begin_cmd, // begin of sign subcmd
874 char_u *end_cmd) // just after sign subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100875{
876 int idx;
877 char save = *end_cmd;
878
879 *end_cmd = NUL;
880 for (idx = 0; ; ++idx)
881 if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0)
882 break;
883 *end_cmd = save;
884 return idx;
885}
886
887/*
888 * Find a sign by name. Also returns pointer to the previous sign.
889 */
890 static sign_T *
891sign_find(char_u *name, sign_T **sp_prev)
892{
893 sign_T *sp;
894
895 if (sp_prev != NULL)
896 *sp_prev = NULL;
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200897 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100898 {
899 if (STRCMP(sp->sn_name, name) == 0)
900 break;
901 if (sp_prev != NULL)
902 *sp_prev = sp;
903 }
904
905 return sp;
906}
907
908/*
Bram Moolenaar03142362019-01-18 22:01:42 +0100909 * Allocate a new sign
910 */
911 static sign_T *
912alloc_new_sign(char_u *name)
913{
914 sign_T *sp;
915 sign_T *lp;
916 int start = next_sign_typenr;
917
918 // Allocate a new sign.
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200919 sp = alloc_clear_id(sizeof(sign_T), aid_sign_define_by_name);
Bram Moolenaar03142362019-01-18 22:01:42 +0100920 if (sp == NULL)
921 return NULL;
922
923 // Check that next_sign_typenr is not already being used.
924 // This only happens after wrapping around. Hopefully
925 // another one got deleted and we can use its number.
926 for (lp = first_sign; lp != NULL; )
927 {
928 if (lp->sn_typenr == next_sign_typenr)
929 {
930 ++next_sign_typenr;
931 if (next_sign_typenr == MAX_TYPENR)
932 next_sign_typenr = 1;
933 if (next_sign_typenr == start)
934 {
935 vim_free(sp);
Bram Moolenaard88be5b2022-01-04 19:57:55 +0000936 emsg(_(e_too_many_signs_defined));
Bram Moolenaar03142362019-01-18 22:01:42 +0100937 return NULL;
938 }
939 lp = first_sign; // start all over
940 continue;
941 }
942 lp = lp->sn_next;
943 }
944
945 sp->sn_typenr = next_sign_typenr;
946 if (++next_sign_typenr == MAX_TYPENR)
947 next_sign_typenr = 1; // wrap around
948
949 sp->sn_name = vim_strsave(name);
950 if (sp->sn_name == NULL) // out of memory
951 {
952 vim_free(sp);
953 return NULL;
954 }
955
956 return sp;
957}
958
959/*
960 * Initialize the icon information for a new sign
961 */
962 static void
963sign_define_init_icon(sign_T *sp, char_u *icon)
964{
965 vim_free(sp->sn_icon);
966 sp->sn_icon = vim_strsave(icon);
967 backslash_halve(sp->sn_icon);
968# ifdef FEAT_SIGN_ICONS
969 if (gui.in_use)
970 {
971 out_flush();
972 if (sp->sn_image != NULL)
973 gui_mch_destroy_sign(sp->sn_image);
974 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
975 }
976# endif
977}
978
979/*
980 * Initialize the text for a new sign
981 */
982 static int
983sign_define_init_text(sign_T *sp, char_u *text)
984{
985 char_u *s;
986 char_u *endp;
987 int cells;
988 int len;
989
990 endp = text + (int)STRLEN(text);
991
992 // Remove backslashes so that it is possible to use a space.
993 for (s = text; s + 1 < endp; ++s)
994 if (*s == '\\')
995 {
996 STRMOVE(s, s + 1);
997 --endp;
998 }
999
1000 // Count cells and check for non-printable chars
Bram Moolenaar03142362019-01-18 22:01:42 +01001001 if (has_mbyte)
1002 {
1003 cells = 0;
1004 for (s = text; s < endp; s += (*mb_ptr2len)(s))
1005 {
1006 if (!vim_isprintc((*mb_ptr2char)(s)))
1007 break;
1008 cells += (*mb_ptr2cells)(s);
1009 }
1010 }
1011 else
Bram Moolenaar03142362019-01-18 22:01:42 +01001012 {
1013 for (s = text; s < endp; ++s)
1014 if (!vim_isprintc(*s))
1015 break;
1016 cells = (int)(s - text);
1017 }
1018
1019 // Currently sign text must be one or two display cells
1020 if (s != endp || cells < 1 || cells > 2)
1021 {
Bram Moolenaarcbadefe2022-01-01 19:33:50 +00001022 semsg(_(e_invalid_sign_text_str), text);
Bram Moolenaar03142362019-01-18 22:01:42 +01001023 return FAIL;
1024 }
1025
1026 vim_free(sp->sn_text);
1027 // Allocate one byte more if we need to pad up
1028 // with a space.
1029 len = (int)(endp - text + ((cells == 1) ? 1 : 0));
1030 sp->sn_text = vim_strnsave(text, len);
1031
1032 // For single character sign text, pad with a space.
1033 if (sp->sn_text != NULL && cells == 1)
1034 STRCPY(sp->sn_text + len - 1, " ");
1035
1036 return OK;
1037}
1038
1039/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001040 * Define a new sign or update an existing sign
1041 */
1042 int
1043sign_define_by_name(
1044 char_u *name,
1045 char_u *icon,
1046 char_u *linehl,
1047 char_u *text,
Bram Moolenaare413ea02021-11-24 16:20:13 +00001048 char_u *texthl,
James McCoya80aad72021-12-22 19:45:28 +00001049 char_u *culhl,
1050 char_u *numhl)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001051{
1052 sign_T *sp_prev;
1053 sign_T *sp;
1054
1055 sp = sign_find(name, &sp_prev);
1056 if (sp == NULL)
1057 {
Bram Moolenaar03142362019-01-18 22:01:42 +01001058 sp = alloc_new_sign(name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001059 if (sp == NULL)
1060 return FAIL;
1061
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001062 // add the new sign to the list of signs
1063 if (sp_prev == NULL)
1064 first_sign = sp;
1065 else
1066 sp_prev->sn_next = sp;
1067 }
Bram Moolenaarbf0acff2020-01-09 21:01:59 +01001068 else
1069 {
1070 win_T *wp;
1071
1072 // Signs may already exist, a redraw is needed in windows with a
1073 // non-empty sign list.
1074 FOR_ALL_WINDOWS(wp)
1075 if (wp->w_buffer->b_signlist != NULL)
1076 redraw_buf_later(wp->w_buffer, NOT_VALID);
1077 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001078
1079 // set values for a defined sign.
1080 if (icon != NULL)
Bram Moolenaar03142362019-01-18 22:01:42 +01001081 sign_define_init_icon(sp, icon);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001082
Bram Moolenaar03142362019-01-18 22:01:42 +01001083 if (text != NULL && (sign_define_init_text(sp, text) == FAIL))
1084 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001085
1086 if (linehl != NULL)
Bram Moolenaar0bac5fc2021-12-05 17:45:49 +00001087 {
1088 if (*linehl == NUL)
1089 sp->sn_line_hl = 0;
1090 else
1091 sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl));
1092 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001093
1094 if (texthl != NULL)
Bram Moolenaar0bac5fc2021-12-05 17:45:49 +00001095 {
1096 if (*texthl == NUL)
1097 sp->sn_text_hl = 0;
1098 else
1099 sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl));
1100 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001101
Bram Moolenaare413ea02021-11-24 16:20:13 +00001102 if (culhl != NULL)
Bram Moolenaar0bac5fc2021-12-05 17:45:49 +00001103 {
1104 if (*culhl == NUL)
1105 sp->sn_cul_hl = 0;
1106 else
1107 sp->sn_cul_hl = syn_check_group(culhl, (int)STRLEN(culhl));
1108 }
Bram Moolenaare413ea02021-11-24 16:20:13 +00001109
James McCoya80aad72021-12-22 19:45:28 +00001110 if (numhl != NULL)
1111 {
1112 if (*numhl == NUL)
1113 sp->sn_num_hl = 0;
1114 else
1115 sp->sn_num_hl = syn_check_group(numhl, (int)STRLEN(numhl));
1116 }
1117
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001118 return OK;
1119}
1120
1121/*
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001122 * Return TRUE if sign "name" exists.
1123 */
1124 int
1125sign_exists_by_name(char_u *name)
1126{
1127 return sign_find(name, NULL) != NULL;
1128}
1129
1130/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001131 * Free the sign specified by 'name'.
1132 */
1133 int
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001134sign_undefine_by_name(char_u *name, int give_error)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001135{
1136 sign_T *sp_prev;
1137 sign_T *sp;
1138
1139 sp = sign_find(name, &sp_prev);
1140 if (sp == NULL)
1141 {
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001142 if (give_error)
Bram Moolenaareb822a22021-12-31 15:09:27 +00001143 semsg(_(e_unknown_sign_str), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001144 return FAIL;
1145 }
1146 sign_undefine(sp, sp_prev);
1147
1148 return OK;
1149}
1150
1151/*
1152 * List the signs matching 'name'
1153 */
1154 static void
1155sign_list_by_name(char_u *name)
1156{
1157 sign_T *sp;
1158
1159 sp = sign_find(name, NULL);
1160 if (sp != NULL)
1161 sign_list_defined(sp);
1162 else
Bram Moolenaareb822a22021-12-31 15:09:27 +00001163 semsg(_(e_unknown_sign_str), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001164}
1165
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001166 static void
1167may_force_numberwidth_recompute(buf_T *buf, int unplace)
1168{
1169 tabpage_T *tp;
1170 win_T *wp;
1171
1172 FOR_ALL_TAB_WINDOWS(tp, wp)
1173 if (wp->w_buffer == buf
1174 && (wp->w_p_nu || wp->w_p_rnu)
1175 && (unplace || wp->w_nrwidth_width < 2)
1176 && (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u'))
1177 wp->w_nrwidth_line_count = 0;
1178}
1179
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001180/*
Bram Moolenaar8144acb2019-01-14 23:08:18 +01001181 * Place a sign at the specified file location or update a sign.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001182 */
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001183 int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001184sign_place(
1185 int *sign_id,
1186 char_u *sign_group,
1187 char_u *sign_name,
1188 buf_T *buf,
1189 linenr_T lnum,
1190 int prio)
1191{
1192 sign_T *sp;
1193
1194 // Check for reserved character '*' in group name
1195 if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0'))
1196 return FAIL;
1197
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001198 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001199 if (STRCMP(sp->sn_name, sign_name) == 0)
1200 break;
1201 if (sp == NULL)
1202 {
Bram Moolenaareb822a22021-12-31 15:09:27 +00001203 semsg(_(e_unknown_sign_str), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001204 return FAIL;
1205 }
1206 if (*sign_id == 0)
1207 *sign_id = sign_group_get_next_signid(buf, sign_group);
1208
1209 if (lnum > 0)
1210 // ":sign place {id} line={lnum} name={name} file={fname}":
1211 // place a sign
1212 buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr);
1213 else
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02001214 // ":sign place {id} file={fname}": change sign type and/or priority
1215 lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr,
1216 prio);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001217 if (lnum > 0)
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001218 {
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001219 redraw_buf_line_later(buf, lnum);
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001220
1221 // When displaying signs in the 'number' column, if the width of the
1222 // number column is less than 2, then force recomputing the width.
1223 may_force_numberwidth_recompute(buf, FALSE);
1224 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001225 else
1226 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001227 semsg(_("E885: Not possible to change sign %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001228 return FAIL;
1229 }
1230
1231 return OK;
1232}
1233
1234/*
1235 * Unplace the specified sign
1236 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001237 static int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001238sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum)
1239{
1240 if (buf->b_signlist == NULL) // No signs in the buffer
1241 return OK;
1242
1243 if (sign_id == 0)
1244 {
1245 // Delete all the signs in the specified buffer
1246 redraw_buf_later(buf, NOT_VALID);
1247 buf_delete_signs(buf, sign_group);
1248 }
1249 else
1250 {
1251 linenr_T lnum;
1252
1253 // Delete only the specified signs
1254 lnum = buf_delsign(buf, atlnum, sign_id, sign_group);
1255 if (lnum == 0)
1256 return FAIL;
1257 }
1258
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001259 // When all the signs in a buffer are removed, force recomputing the
1260 // number column width (if enabled) in all the windows displaying the
1261 // buffer if 'signcolumn' is set to 'number' in that window.
1262 if (buf->b_signlist == NULL)
1263 may_force_numberwidth_recompute(buf, TRUE);
1264
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001265 return OK;
1266}
1267
1268/*
1269 * Unplace the sign at the current cursor line.
1270 */
1271 static void
1272sign_unplace_at_cursor(char_u *groupname)
1273{
1274 int id = -1;
1275
1276 id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname);
1277 if (id > 0)
1278 sign_unplace(id, groupname, curwin->w_buffer, curwin->w_cursor.lnum);
1279 else
Bram Moolenaareb822a22021-12-31 15:09:27 +00001280 emsg(_(e_missing_sign_number));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001281}
1282
1283/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001284 * Jump to a sign.
1285 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001286 static linenr_T
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001287sign_jump(int sign_id, char_u *sign_group, buf_T *buf)
1288{
1289 linenr_T lnum;
1290
1291 if ((lnum = buf_findsign(buf, sign_id, sign_group)) <= 0)
1292 {
Bram Moolenaareb822a22021-12-31 15:09:27 +00001293 semsg(_(e_invalid_sign_id_nr), sign_id);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001294 return -1;
1295 }
1296
1297 // goto a sign ...
1298 if (buf_jump_open_win(buf) != NULL)
1299 { // ... in a current window
1300 curwin->w_cursor.lnum = lnum;
1301 check_cursor_lnum();
1302 beginline(BL_WHITE);
1303 }
1304 else
1305 { // ... not currently in a window
1306 char_u *cmd;
1307
1308 if (buf->b_fname == NULL)
1309 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001310 emsg(_("E934: Cannot jump to a buffer that does not have a name"));
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001311 return -1;
1312 }
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02001313 cmd = alloc(STRLEN(buf->b_fname) + 25);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001314 if (cmd == NULL)
1315 return -1;
1316 sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname);
1317 do_cmdline_cmd(cmd);
1318 vim_free(cmd);
1319 }
1320# ifdef FEAT_FOLDING
1321 foldOpenCursor();
1322# endif
1323
1324 return lnum;
1325}
1326
1327/*
1328 * ":sign define {name} ..." command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001329 */
1330 static void
1331sign_define_cmd(char_u *sign_name, char_u *cmdline)
1332{
1333 char_u *arg;
1334 char_u *p = cmdline;
1335 char_u *icon = NULL;
1336 char_u *text = NULL;
1337 char_u *linehl = NULL;
1338 char_u *texthl = NULL;
Bram Moolenaare413ea02021-11-24 16:20:13 +00001339 char_u *culhl = NULL;
James McCoya80aad72021-12-22 19:45:28 +00001340 char_u *numhl = NULL;
Bram Moolenaar0bac5fc2021-12-05 17:45:49 +00001341 int failed = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001342
1343 // set values for a defined sign.
1344 for (;;)
1345 {
1346 arg = skipwhite(p);
1347 if (*arg == NUL)
1348 break;
1349 p = skiptowhite_esc(arg);
1350 if (STRNCMP(arg, "icon=", 5) == 0)
1351 {
1352 arg += 5;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001353 icon = vim_strnsave(arg, p - arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001354 }
1355 else if (STRNCMP(arg, "text=", 5) == 0)
1356 {
1357 arg += 5;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001358 text = vim_strnsave(arg, p - arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001359 }
1360 else if (STRNCMP(arg, "linehl=", 7) == 0)
1361 {
1362 arg += 7;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001363 linehl = vim_strnsave(arg, p - arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001364 }
1365 else if (STRNCMP(arg, "texthl=", 7) == 0)
1366 {
1367 arg += 7;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001368 texthl = vim_strnsave(arg, p - arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001369 }
Bram Moolenaare413ea02021-11-24 16:20:13 +00001370 else if (STRNCMP(arg, "culhl=", 6) == 0)
1371 {
1372 arg += 6;
1373 culhl = vim_strnsave(arg, p - arg);
1374 }
James McCoya80aad72021-12-22 19:45:28 +00001375 else if (STRNCMP(arg, "numhl=", 6) == 0)
1376 {
1377 arg += 6;
1378 numhl = vim_strnsave(arg, p - arg);
1379 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001380 else
1381 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001382 semsg(_(e_invalid_argument_str), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001383 failed = TRUE;
1384 break;
1385 }
1386 }
1387
1388 if (!failed)
James McCoya80aad72021-12-22 19:45:28 +00001389 sign_define_by_name(sign_name, icon, linehl, text, texthl, culhl, numhl);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001390
1391 vim_free(icon);
1392 vim_free(text);
1393 vim_free(linehl);
1394 vim_free(texthl);
Bram Moolenaare413ea02021-11-24 16:20:13 +00001395 vim_free(culhl);
James McCoya80aad72021-12-22 19:45:28 +00001396 vim_free(numhl);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001397}
1398
1399/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001400 * ":sign place" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001401 */
1402 static void
1403sign_place_cmd(
1404 buf_T *buf,
1405 linenr_T lnum,
1406 char_u *sign_name,
1407 int id,
1408 char_u *group,
1409 int prio)
1410{
1411 if (id <= 0)
1412 {
1413 // List signs placed in a file/buffer
1414 // :sign place file={fname}
1415 // :sign place group={group} file={fname}
1416 // :sign place group=* file={fname}
1417 // :sign place buffer={nr}
1418 // :sign place group={group} buffer={nr}
1419 // :sign place group=* buffer={nr}
1420 // :sign place
1421 // :sign place group={group}
1422 // :sign place group=*
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001423 if (lnum >= 0 || sign_name != NULL
1424 || (group != NULL && *group == '\0'))
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001425 emsg(_(e_invalid_argument));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001426 else
1427 sign_list_placed(buf, group);
1428 }
1429 else
1430 {
1431 // Place a new sign
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001432 if (sign_name == NULL || buf == NULL
1433 || (group != NULL && *group == '\0'))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001434 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001435 emsg(_(e_invalid_argument));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001436 return;
1437 }
1438
1439 sign_place(&id, group, sign_name, buf, lnum, prio);
1440 }
1441}
1442
1443/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001444 * ":sign unplace" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001445 */
1446 static void
1447sign_unplace_cmd(
1448 buf_T *buf,
1449 linenr_T lnum,
1450 char_u *sign_name,
1451 int id,
1452 char_u *group)
1453{
1454 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0'))
1455 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001456 emsg(_(e_invalid_argument));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001457 return;
1458 }
1459
1460 if (id == -2)
1461 {
1462 if (buf != NULL)
1463 // :sign unplace * file={fname}
1464 // :sign unplace * group={group} file={fname}
1465 // :sign unplace * group=* file={fname}
1466 // :sign unplace * buffer={nr}
1467 // :sign unplace * group={group} buffer={nr}
1468 // :sign unplace * group=* buffer={nr}
1469 sign_unplace(0, group, buf, 0);
1470 else
1471 // :sign unplace *
1472 // :sign unplace * group={group}
1473 // :sign unplace * group=*
1474 FOR_ALL_BUFFERS(buf)
1475 if (buf->b_signlist != NULL)
1476 buf_delete_signs(buf, group);
1477 }
1478 else
1479 {
1480 if (buf != NULL)
1481 // :sign unplace {id} file={fname}
1482 // :sign unplace {id} group={group} file={fname}
1483 // :sign unplace {id} group=* file={fname}
1484 // :sign unplace {id} buffer={nr}
1485 // :sign unplace {id} group={group} buffer={nr}
1486 // :sign unplace {id} group=* buffer={nr}
1487 sign_unplace(id, group, buf, 0);
1488 else
1489 {
1490 if (id == -1)
1491 {
1492 // :sign unplace group={group}
1493 // :sign unplace group=*
1494 sign_unplace_at_cursor(group);
1495 }
1496 else
1497 {
1498 // :sign unplace {id}
1499 // :sign unplace {id} group={group}
1500 // :sign unplace {id} group=*
1501 FOR_ALL_BUFFERS(buf)
1502 sign_unplace(id, group, buf, 0);
1503 }
1504 }
1505 }
1506}
1507
1508/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001509 * Jump to a placed sign commands:
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001510 * :sign jump {id} file={fname}
1511 * :sign jump {id} buffer={nr}
1512 * :sign jump {id} group={group} file={fname}
1513 * :sign jump {id} group={group} buffer={nr}
1514 */
1515 static void
1516sign_jump_cmd(
1517 buf_T *buf,
1518 linenr_T lnum,
1519 char_u *sign_name,
1520 int id,
1521 char_u *group)
1522{
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001523 if (sign_name == NULL && group == NULL && id == -1)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001524 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001525 emsg(_(e_argument_required));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001526 return;
1527 }
1528
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001529 if (buf == NULL || (group != NULL && *group == '\0')
1530 || lnum >= 0 || sign_name != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001531 {
1532 // File or buffer is not specified or an empty group is used
1533 // or a line number or a sign name is specified.
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001534 emsg(_(e_invalid_argument));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001535 return;
1536 }
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001537 (void)sign_jump(id, group, buf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001538}
1539
1540/*
1541 * Parse the command line arguments for the ":sign place", ":sign unplace" and
1542 * ":sign jump" commands.
1543 * The supported arguments are: line={lnum} name={name} group={group}
1544 * priority={prio} and file={fname} or buffer={nr}.
1545 */
1546 static int
1547parse_sign_cmd_args(
1548 int cmd,
1549 char_u *arg,
1550 char_u **sign_name,
1551 int *signid,
1552 char_u **group,
1553 int *prio,
1554 buf_T **buf,
1555 linenr_T *lnum)
1556{
1557 char_u *arg1;
1558 char_u *name;
1559 char_u *filename = NULL;
Bram Moolenaarb589f952019-01-07 22:10:00 +01001560 int lnum_arg = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001561
1562 // first arg could be placed sign id
1563 arg1 = arg;
1564 if (VIM_ISDIGIT(*arg))
1565 {
1566 *signid = getdigits(&arg);
1567 if (!VIM_ISWHITE(*arg) && *arg != NUL)
1568 {
1569 *signid = -1;
1570 arg = arg1;
1571 }
1572 else
1573 arg = skipwhite(arg);
1574 }
1575
1576 while (*arg != NUL)
1577 {
1578 if (STRNCMP(arg, "line=", 5) == 0)
1579 {
1580 arg += 5;
1581 *lnum = atoi((char *)arg);
1582 arg = skiptowhite(arg);
Bram Moolenaarb589f952019-01-07 22:10:00 +01001583 lnum_arg = TRUE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001584 }
1585 else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE)
1586 {
1587 if (*signid != -1)
1588 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001589 emsg(_(e_invalid_argument));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001590 return FAIL;
1591 }
1592 *signid = -2;
1593 arg = skiptowhite(arg + 1);
1594 }
1595 else if (STRNCMP(arg, "name=", 5) == 0)
1596 {
1597 arg += 5;
1598 name = arg;
1599 arg = skiptowhite(arg);
1600 if (*arg != NUL)
1601 *arg++ = NUL;
1602 while (name[0] == '0' && name[1] != NUL)
1603 ++name;
1604 *sign_name = name;
1605 }
1606 else if (STRNCMP(arg, "group=", 6) == 0)
1607 {
1608 arg += 6;
1609 *group = arg;
1610 arg = skiptowhite(arg);
1611 if (*arg != NUL)
1612 *arg++ = NUL;
1613 }
1614 else if (STRNCMP(arg, "priority=", 9) == 0)
1615 {
1616 arg += 9;
1617 *prio = atoi((char *)arg);
1618 arg = skiptowhite(arg);
1619 }
1620 else if (STRNCMP(arg, "file=", 5) == 0)
1621 {
1622 arg += 5;
1623 filename = arg;
1624 *buf = buflist_findname_exp(arg);
1625 break;
1626 }
1627 else if (STRNCMP(arg, "buffer=", 7) == 0)
1628 {
1629 arg += 7;
1630 filename = arg;
1631 *buf = buflist_findnr((int)getdigits(&arg));
1632 if (*skipwhite(arg) != NUL)
Bram Moolenaar74409f62022-01-01 15:58:22 +00001633 semsg(_(e_trailing_characters_str), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001634 break;
1635 }
1636 else
1637 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001638 emsg(_(e_invalid_argument));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001639 return FAIL;
1640 }
1641 arg = skipwhite(arg);
1642 }
1643
1644 if (filename != NULL && *buf == NULL)
1645 {
Bram Moolenaareb822a22021-12-31 15:09:27 +00001646 semsg(_(e_invalid_buffer_name_str), filename);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001647 return FAIL;
1648 }
1649
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001650 // If the filename is not supplied for the sign place or the sign jump
1651 // command, then use the current buffer.
Bram Moolenaarb589f952019-01-07 22:10:00 +01001652 if (filename == NULL && ((cmd == SIGNCMD_PLACE && lnum_arg)
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001653 || cmd == SIGNCMD_JUMP))
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001654 *buf = curwin->w_buffer;
1655
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001656 return OK;
1657}
1658
1659/*
1660 * ":sign" command
1661 */
1662 void
1663ex_sign(exarg_T *eap)
1664{
1665 char_u *arg = eap->arg;
1666 char_u *p;
1667 int idx;
1668 sign_T *sp;
1669 buf_T *buf = NULL;
1670
1671 // Parse the subcommand.
1672 p = skiptowhite(arg);
1673 idx = sign_cmd_idx(arg, p);
1674 if (idx == SIGNCMD_LAST)
1675 {
Bram Moolenaareb822a22021-12-31 15:09:27 +00001676 semsg(_(e_unknown_sign_command_str), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001677 return;
1678 }
1679 arg = skipwhite(p);
1680
1681 if (idx <= SIGNCMD_LIST)
1682 {
1683 // Define, undefine or list signs.
1684 if (idx == SIGNCMD_LIST && *arg == NUL)
1685 {
1686 // ":sign list": list all defined signs
1687 for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next)
1688 sign_list_defined(sp);
1689 }
1690 else if (*arg == NUL)
Bram Moolenaareb822a22021-12-31 15:09:27 +00001691 emsg(_(e_missing_sign_name));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001692 else
1693 {
1694 char_u *name;
1695
1696 // Isolate the sign name. If it's a number skip leading zeroes,
1697 // so that "099" and "99" are the same sign. But keep "0".
1698 p = skiptowhite(arg);
1699 if (*p != NUL)
1700 *p++ = NUL;
1701 while (arg[0] == '0' && arg[1] != NUL)
1702 ++arg;
1703 name = vim_strsave(arg);
1704
1705 if (idx == SIGNCMD_DEFINE)
1706 sign_define_cmd(name, p);
1707 else if (idx == SIGNCMD_LIST)
1708 // ":sign list {name}"
1709 sign_list_by_name(name);
1710 else
1711 // ":sign undefine {name}"
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001712 sign_undefine_by_name(name, TRUE);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001713
1714 vim_free(name);
1715 return;
1716 }
1717 }
1718 else
1719 {
1720 int id = -1;
1721 linenr_T lnum = -1;
1722 char_u *sign_name = NULL;
1723 char_u *group = NULL;
1724 int prio = SIGN_DEF_PRIO;
1725
1726 // Parse command line arguments
1727 if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio,
1728 &buf, &lnum) == FAIL)
1729 return;
1730
1731 if (idx == SIGNCMD_PLACE)
1732 sign_place_cmd(buf, lnum, sign_name, id, group, prio);
1733 else if (idx == SIGNCMD_UNPLACE)
1734 sign_unplace_cmd(buf, lnum, sign_name, id, group);
1735 else if (idx == SIGNCMD_JUMP)
1736 sign_jump_cmd(buf, lnum, sign_name, id, group);
1737 }
1738}
1739
1740/*
1741 * Return information about a specified sign
1742 */
1743 static void
1744sign_getinfo(sign_T *sp, dict_T *retdict)
1745{
1746 char_u *p;
1747
1748 dict_add_string(retdict, "name", (char_u *)sp->sn_name);
1749 if (sp->sn_icon != NULL)
1750 dict_add_string(retdict, "icon", (char_u *)sp->sn_icon);
1751 if (sp->sn_text != NULL)
1752 dict_add_string(retdict, "text", (char_u *)sp->sn_text);
1753 if (sp->sn_line_hl > 0)
1754 {
1755 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1756 if (p == NULL)
1757 p = (char_u *)"NONE";
1758 dict_add_string(retdict, "linehl", (char_u *)p);
1759 }
1760 if (sp->sn_text_hl > 0)
1761 {
1762 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1763 if (p == NULL)
1764 p = (char_u *)"NONE";
1765 dict_add_string(retdict, "texthl", (char_u *)p);
1766 }
Bram Moolenaare413ea02021-11-24 16:20:13 +00001767 if (sp->sn_cul_hl > 0)
1768 {
1769 p = get_highlight_name_ext(NULL, sp->sn_cul_hl - 1, FALSE);
1770 if (p == NULL)
1771 p = (char_u *)"NONE";
1772 dict_add_string(retdict, "culhl", (char_u *)p);
1773 }
James McCoya80aad72021-12-22 19:45:28 +00001774 if (sp->sn_num_hl > 0)
1775 {
1776 p = get_highlight_name_ext(NULL, sp->sn_num_hl - 1, FALSE);
1777 if (p == NULL)
1778 p = (char_u *)"NONE";
1779 dict_add_string(retdict, "numhl", (char_u *)p);
1780 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001781}
1782
1783/*
1784 * If 'name' is NULL, return a list of all the defined signs.
1785 * Otherwise, return information about the specified sign.
1786 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001787 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001788sign_getlist(char_u *name, list_T *retlist)
1789{
1790 sign_T *sp = first_sign;
1791 dict_T *dict;
1792
1793 if (name != NULL)
1794 {
1795 sp = sign_find(name, NULL);
1796 if (sp == NULL)
1797 return;
1798 }
1799
1800 for (; sp != NULL && !got_int; sp = sp->sn_next)
1801 {
1802 if ((dict = dict_alloc_id(aid_sign_getlist)) == NULL)
1803 return;
1804 if (list_append_dict(retlist, dict) == FAIL)
1805 return;
1806 sign_getinfo(sp, dict);
1807
1808 if (name != NULL) // handle only the specified sign
1809 break;
1810 }
1811}
1812
1813/*
1814 * Returns information about signs placed in a buffer as list of dicts.
1815 */
1816 void
1817get_buffer_signs(buf_T *buf, list_T *l)
1818{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001819 sign_entry_T *sign;
1820 dict_T *d;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001821
1822 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1823 {
1824 if ((d = sign_get_info(sign)) != NULL)
1825 list_append_dict(l, d);
1826 }
1827}
1828
1829/*
1830 * Return information about all the signs placed in a buffer
1831 */
1832 static void
1833sign_get_placed_in_buf(
1834 buf_T *buf,
1835 linenr_T lnum,
1836 int sign_id,
1837 char_u *sign_group,
1838 list_T *retlist)
1839{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001840 dict_T *d;
1841 list_T *l;
1842 sign_entry_T *sign;
1843 dict_T *sdict;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001844
1845 if ((d = dict_alloc_id(aid_sign_getplaced_dict)) == NULL)
1846 return;
1847 list_append_dict(retlist, d);
1848
1849 dict_add_number(d, "bufnr", (long)buf->b_fnum);
1850
1851 if ((l = list_alloc_id(aid_sign_getplaced_list)) == NULL)
1852 return;
1853 dict_add_list(d, "signs", l);
1854
1855 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1856 {
1857 if (!sign_in_group(sign, sign_group))
1858 continue;
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001859 if ((lnum == 0 && sign_id == 0)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001860 || (sign_id == 0 && lnum == sign->se_lnum)
1861 || (lnum == 0 && sign_id == sign->se_id)
1862 || (lnum == sign->se_lnum && sign_id == sign->se_id))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001863 {
1864 if ((sdict = sign_get_info(sign)) != NULL)
1865 list_append_dict(l, sdict);
1866 }
1867 }
1868}
1869
1870/*
1871 * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the
1872 * sign placed at the line number. If 'lnum' is zero, return all the signs
1873 * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers.
1874 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001875 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001876sign_get_placed(
1877 buf_T *buf,
1878 linenr_T lnum,
1879 int sign_id,
1880 char_u *sign_group,
1881 list_T *retlist)
1882{
1883 if (buf != NULL)
1884 sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist);
1885 else
1886 {
1887 FOR_ALL_BUFFERS(buf)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001888 if (buf->b_signlist != NULL)
1889 sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001890 }
1891}
1892
1893# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1894/*
1895 * Allocate the icons. Called when the GUI has started. Allows defining
1896 * signs before it starts.
1897 */
1898 void
1899sign_gui_started(void)
1900{
1901 sign_T *sp;
1902
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001903 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001904 if (sp->sn_icon != NULL)
1905 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
1906}
1907# endif
1908
1909/*
1910 * List one sign.
1911 */
1912 static void
1913sign_list_defined(sign_T *sp)
1914{
1915 char_u *p;
1916
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001917 smsg("sign %s", sp->sn_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001918 if (sp->sn_icon != NULL)
1919 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001920 msg_puts(" icon=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001921 msg_outtrans(sp->sn_icon);
1922# ifdef FEAT_SIGN_ICONS
1923 if (sp->sn_image == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001924 msg_puts(_(" (NOT FOUND)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001925# else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001926 msg_puts(_(" (not supported)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001927# endif
1928 }
1929 if (sp->sn_text != NULL)
1930 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001931 msg_puts(" text=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001932 msg_outtrans(sp->sn_text);
1933 }
1934 if (sp->sn_line_hl > 0)
1935 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001936 msg_puts(" linehl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001937 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1938 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001939 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001940 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001941 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001942 }
1943 if (sp->sn_text_hl > 0)
1944 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001945 msg_puts(" texthl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001946 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1947 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001948 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001949 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001950 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001951 }
Bram Moolenaare413ea02021-11-24 16:20:13 +00001952 if (sp->sn_cul_hl > 0)
1953 {
1954 msg_puts(" culhl=");
1955 p = get_highlight_name_ext(NULL, sp->sn_cul_hl - 1, FALSE);
1956 if (p == NULL)
1957 msg_puts("NONE");
1958 else
1959 msg_puts((char *)p);
1960 }
James McCoya80aad72021-12-22 19:45:28 +00001961 if (sp->sn_num_hl > 0)
1962 {
1963 msg_puts(" numhl=");
1964 p = get_highlight_name_ext(NULL, sp->sn_num_hl - 1, FALSE);
1965 if (p == NULL)
1966 msg_puts("NONE");
1967 else
1968 msg_puts((char *)p);
1969 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001970}
1971
1972/*
1973 * Undefine a sign and free its memory.
1974 */
1975 static void
1976sign_undefine(sign_T *sp, sign_T *sp_prev)
1977{
1978 vim_free(sp->sn_name);
1979 vim_free(sp->sn_icon);
1980# ifdef FEAT_SIGN_ICONS
1981 if (sp->sn_image != NULL)
1982 {
1983 out_flush();
1984 gui_mch_destroy_sign(sp->sn_image);
1985 }
1986# endif
1987 vim_free(sp->sn_text);
1988 if (sp_prev == NULL)
1989 first_sign = sp->sn_next;
1990 else
1991 sp_prev->sn_next = sp->sn_next;
1992 vim_free(sp);
1993}
1994
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001995# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1996 void *
1997sign_get_image(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001998 int typenr) // the attribute which may have a sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001999{
2000 sign_T *sp;
2001
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002002 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002003 if (sp->sn_typenr == typenr)
2004 return sp->sn_image;
2005 return NULL;
2006}
2007# endif
2008
2009/*
2010 * Undefine/free all signs.
2011 */
2012 void
2013free_signs(void)
2014{
2015 while (first_sign != NULL)
2016 sign_undefine(first_sign, NULL);
2017}
2018
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002019static enum
2020{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002021 EXP_SUBCMD, // expand :sign sub-commands
2022 EXP_DEFINE, // expand :sign define {name} args
2023 EXP_PLACE, // expand :sign place {id} args
Bram Moolenaar3678f652019-02-17 14:50:25 +01002024 EXP_LIST, // expand :sign place args
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002025 EXP_UNPLACE, // expand :sign unplace"
Bram Moolenaar3678f652019-02-17 14:50:25 +01002026 EXP_SIGN_NAMES, // expand with name of placed signs
2027 EXP_SIGN_GROUPS // expand with name of placed sign groups
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002028} expand_what;
2029
2030/*
Bram Moolenaar3678f652019-02-17 14:50:25 +01002031 * Return the n'th sign name (used for command line completion)
2032 */
2033 static char_u *
2034get_nth_sign_name(int idx)
2035{
2036 int current_idx;
2037 sign_T *sp;
2038
2039 // Complete with name of signs already defined
2040 current_idx = 0;
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002041 FOR_ALL_SIGNS(sp)
Bram Moolenaar3678f652019-02-17 14:50:25 +01002042 if (current_idx++ == idx)
2043 return sp->sn_name;
2044 return NULL;
2045}
2046
2047/*
2048 * Return the n'th sign group name (used for command line completion)
2049 */
2050 static char_u *
2051get_nth_sign_group_name(int idx)
2052{
2053 int current_idx;
2054 int todo;
2055 hashitem_T *hi;
2056 signgroup_T *group;
2057
2058 // Complete with name of sign groups already defined
2059 current_idx = 0;
2060 todo = (int)sg_table.ht_used;
2061 for (hi = sg_table.ht_array; todo > 0; ++hi)
2062 {
2063 if (!HASHITEM_EMPTY(hi))
2064 {
2065 --todo;
2066 if (current_idx++ == idx)
2067 {
2068 group = HI2SG(hi);
2069 return group->sg_name;
2070 }
2071 }
2072 }
2073 return NULL;
2074}
2075
2076/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002077 * Function given to ExpandGeneric() to obtain the sign command
2078 * expansion.
2079 */
2080 char_u *
2081get_sign_name(expand_T *xp UNUSED, int idx)
2082{
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002083 switch (expand_what)
2084 {
2085 case EXP_SUBCMD:
2086 return (char_u *)cmds[idx];
2087 case EXP_DEFINE:
2088 {
2089 char *define_arg[] =
2090 {
James McCoya80aad72021-12-22 19:45:28 +00002091 "culhl=", "icon=", "linehl=", "numhl=", "text=", "texthl=", NULL
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002092 };
2093 return (char_u *)define_arg[idx];
2094 }
2095 case EXP_PLACE:
2096 {
2097 char *place_arg[] =
2098 {
2099 "line=", "name=", "group=", "priority=", "file=",
2100 "buffer=", NULL
2101 };
2102 return (char_u *)place_arg[idx];
2103 }
Bram Moolenaar3678f652019-02-17 14:50:25 +01002104 case EXP_LIST:
2105 {
2106 char *list_arg[] =
2107 {
2108 "group=", "file=", "buffer=", NULL
2109 };
2110 return (char_u *)list_arg[idx];
2111 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002112 case EXP_UNPLACE:
2113 {
2114 char *unplace_arg[] = { "group=", "file=", "buffer=", NULL };
2115 return (char_u *)unplace_arg[idx];
2116 }
2117 case EXP_SIGN_NAMES:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002118 return get_nth_sign_name(idx);
2119 case EXP_SIGN_GROUPS:
2120 return get_nth_sign_group_name(idx);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002121 default:
2122 return NULL;
2123 }
2124}
2125
2126/*
2127 * Handle command line completion for :sign command.
2128 */
2129 void
2130set_context_in_sign_cmd(expand_T *xp, char_u *arg)
2131{
2132 char_u *p;
2133 char_u *end_subcmd;
2134 char_u *last;
2135 int cmd_idx;
2136 char_u *begin_subcmd_args;
2137
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002138 // Default: expand subcommands.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002139 xp->xp_context = EXPAND_SIGN;
2140 expand_what = EXP_SUBCMD;
2141 xp->xp_pattern = arg;
2142
2143 end_subcmd = skiptowhite(arg);
2144 if (*end_subcmd == NUL)
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002145 // expand subcmd name
2146 // :sign {subcmd}<CTRL-D>
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002147 return;
2148
2149 cmd_idx = sign_cmd_idx(arg, end_subcmd);
2150
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002151 // :sign {subcmd} {subcmd_args}
2152 // |
2153 // begin_subcmd_args
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002154 begin_subcmd_args = skipwhite(end_subcmd);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002155
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002156 // expand last argument of subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002157
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002158 // :sign define {name} {args}...
2159 // |
2160 // p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002161
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002162 // Loop until reaching last argument.
Bram Moolenaar3678f652019-02-17 14:50:25 +01002163 p = begin_subcmd_args;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002164 do
2165 {
2166 p = skipwhite(p);
2167 last = p;
2168 p = skiptowhite(p);
2169 } while (*p != NUL);
2170
2171 p = vim_strchr(last, '=');
2172
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002173 // :sign define {name} {args}... {last}=
2174 // | |
2175 // last p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002176 if (p == NULL)
2177 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002178 // Expand last argument name (before equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002179 xp->xp_pattern = last;
2180 switch (cmd_idx)
2181 {
2182 case SIGNCMD_DEFINE:
2183 expand_what = EXP_DEFINE;
2184 break;
2185 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002186 // List placed signs
2187 if (VIM_ISDIGIT(*begin_subcmd_args))
2188 // :sign place {id} {args}...
2189 expand_what = EXP_PLACE;
2190 else
2191 // :sign place {args}...
2192 expand_what = EXP_LIST;
2193 break;
2194 case SIGNCMD_LIST:
2195 case SIGNCMD_UNDEFINE:
2196 // :sign list <CTRL-D>
2197 // :sign undefine <CTRL-D>
2198 expand_what = EXP_SIGN_NAMES;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002199 break;
2200 case SIGNCMD_JUMP:
2201 case SIGNCMD_UNPLACE:
2202 expand_what = EXP_UNPLACE;
2203 break;
2204 default:
2205 xp->xp_context = EXPAND_NOTHING;
2206 }
2207 }
2208 else
2209 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002210 // Expand last argument value (after equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002211 xp->xp_pattern = p + 1;
2212 switch (cmd_idx)
2213 {
2214 case SIGNCMD_DEFINE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002215 if (STRNCMP(last, "texthl", 6) == 0
James McCoya80aad72021-12-22 19:45:28 +00002216 || STRNCMP(last, "linehl", 6) == 0
2217 || STRNCMP(last, "culhl", 5) == 0
2218 || STRNCMP(last, "numhl", 5) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002219 xp->xp_context = EXPAND_HIGHLIGHT;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002220 else if (STRNCMP(last, "icon", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002221 xp->xp_context = EXPAND_FILES;
2222 else
2223 xp->xp_context = EXPAND_NOTHING;
2224 break;
2225 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002226 if (STRNCMP(last, "name", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002227 expand_what = EXP_SIGN_NAMES;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002228 else if (STRNCMP(last, "group", 5) == 0)
2229 expand_what = EXP_SIGN_GROUPS;
2230 else if (STRNCMP(last, "file", 4) == 0)
2231 xp->xp_context = EXPAND_BUFFERS;
2232 else
2233 xp->xp_context = EXPAND_NOTHING;
2234 break;
2235 case SIGNCMD_UNPLACE:
2236 case SIGNCMD_JUMP:
2237 if (STRNCMP(last, "group", 5) == 0)
2238 expand_what = EXP_SIGN_GROUPS;
2239 else if (STRNCMP(last, "file", 4) == 0)
2240 xp->xp_context = EXPAND_BUFFERS;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002241 else
2242 xp->xp_context = EXPAND_NOTHING;
2243 break;
2244 default:
2245 xp->xp_context = EXPAND_NOTHING;
2246 }
2247 }
2248}
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002249
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002250/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002251 * Define a sign using the attributes in 'dict'. Returns 0 on success and -1 on
2252 * failure.
2253 */
2254 static int
2255sign_define_from_dict(char_u *name_arg, dict_T *dict)
2256{
2257 char_u *name = NULL;
2258 char_u *icon = NULL;
2259 char_u *linehl = NULL;
2260 char_u *text = NULL;
2261 char_u *texthl = NULL;
Bram Moolenaare413ea02021-11-24 16:20:13 +00002262 char_u *culhl = NULL;
James McCoya80aad72021-12-22 19:45:28 +00002263 char_u *numhl = NULL;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002264 int retval = -1;
2265
2266 if (name_arg == NULL)
2267 {
2268 if (dict == NULL)
2269 return -1;
2270 name = dict_get_string(dict, (char_u *)"name", TRUE);
2271 }
2272 else
2273 name = vim_strsave(name_arg);
2274 if (name == NULL || name[0] == NUL)
2275 goto cleanup;
2276 if (dict != NULL)
2277 {
2278 icon = dict_get_string(dict, (char_u *)"icon", TRUE);
2279 linehl = dict_get_string(dict, (char_u *)"linehl", TRUE);
2280 text = dict_get_string(dict, (char_u *)"text", TRUE);
2281 texthl = dict_get_string(dict, (char_u *)"texthl", TRUE);
Bram Moolenaare413ea02021-11-24 16:20:13 +00002282 culhl = dict_get_string(dict, (char_u *)"culhl", TRUE);
James McCoya80aad72021-12-22 19:45:28 +00002283 numhl = dict_get_string(dict, (char_u *)"numhl", TRUE);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002284 }
2285
James McCoya80aad72021-12-22 19:45:28 +00002286 if (sign_define_by_name(name, icon, linehl, text, texthl, culhl, numhl) == OK)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002287 retval = 0;
2288
2289cleanup:
2290 vim_free(name);
2291 vim_free(icon);
2292 vim_free(linehl);
2293 vim_free(text);
2294 vim_free(texthl);
Bram Moolenaare413ea02021-11-24 16:20:13 +00002295 vim_free(culhl);
James McCoya80aad72021-12-22 19:45:28 +00002296 vim_free(numhl);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002297
2298 return retval;
2299}
2300
2301/*
2302 * Define multiple signs using attributes from list 'l' and store the return
2303 * values in 'retlist'.
2304 */
2305 static void
2306sign_define_multiple(list_T *l, list_T *retlist)
2307{
2308 listitem_T *li;
2309 int retval;
2310
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002311 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002312 {
2313 retval = -1;
2314 if (li->li_tv.v_type == VAR_DICT)
2315 retval = sign_define_from_dict(NULL, li->li_tv.vval.v_dict);
2316 else
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002317 emsg(_(e_dictionary_required));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002318 list_append_number(retlist, retval);
2319 }
2320}
2321
2322/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002323 * "sign_define()" function
2324 */
2325 void
2326f_sign_define(typval_T *argvars, typval_T *rettv)
2327{
2328 char_u *name;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002329
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002330 if (in_vim9script()
2331 && (check_for_string_or_list_arg(argvars, 0) == FAIL
2332 || check_for_opt_dict_arg(argvars, 1) == FAIL))
2333 return;
2334
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002335 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2336 {
2337 // Define multiple signs
2338 if (rettv_list_alloc(rettv) != OK)
2339 return;
2340
2341 sign_define_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2342 return;
2343 }
2344
2345 // Define a single sign
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002346 rettv->vval.v_number = -1;
2347
2348 name = tv_get_string_chk(&argvars[0]);
2349 if (name == NULL)
2350 return;
2351
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002352 if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_DICT)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002353 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002354 emsg(_(e_dictionary_required));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002355 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002356 }
2357
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002358 rettv->vval.v_number = sign_define_from_dict(name,
2359 argvars[1].v_type == VAR_DICT ? argvars[1].vval.v_dict : NULL);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002360}
2361
2362/*
2363 * "sign_getdefined()" function
2364 */
2365 void
2366f_sign_getdefined(typval_T *argvars, typval_T *rettv)
2367{
2368 char_u *name = NULL;
2369
2370 if (rettv_list_alloc_id(rettv, aid_sign_getdefined) != OK)
2371 return;
2372
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002373 if (in_vim9script() && check_for_opt_string_arg(argvars, 0) == FAIL)
2374 return;
2375
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002376 if (argvars[0].v_type != VAR_UNKNOWN)
2377 name = tv_get_string(&argvars[0]);
2378
2379 sign_getlist(name, rettv->vval.v_list);
2380}
2381
2382/*
2383 * "sign_getplaced()" function
2384 */
2385 void
2386f_sign_getplaced(typval_T *argvars, typval_T *rettv)
2387{
2388 buf_T *buf = NULL;
2389 dict_T *dict;
2390 dictitem_T *di;
2391 linenr_T lnum = 0;
2392 int sign_id = 0;
2393 char_u *group = NULL;
2394 int notanum = FALSE;
2395
2396 if (rettv_list_alloc_id(rettv, aid_sign_getplaced) != OK)
2397 return;
2398
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002399 if (in_vim9script()
Yegappan Lakshmanancd917202021-07-21 19:09:09 +02002400 && (check_for_opt_buffer_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002401 || (argvars[0].v_type != VAR_UNKNOWN
2402 && check_for_opt_dict_arg(argvars, 1) == FAIL)))
2403 return;
2404
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002405 if (argvars[0].v_type != VAR_UNKNOWN)
2406 {
2407 // get signs placed in the specified buffer
2408 buf = get_buf_arg(&argvars[0]);
2409 if (buf == NULL)
2410 return;
2411
2412 if (argvars[1].v_type != VAR_UNKNOWN)
2413 {
2414 if (argvars[1].v_type != VAR_DICT ||
2415 ((dict = argvars[1].vval.v_dict) == NULL))
2416 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002417 emsg(_(e_dictionary_required));
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002418 return;
2419 }
2420 if ((di = dict_find(dict, (char_u *)"lnum", -1)) != NULL)
2421 {
2422 // get signs placed at this line
2423 (void)tv_get_number_chk(&di->di_tv, &notanum);
2424 if (notanum)
2425 return;
2426 lnum = tv_get_lnum(&di->di_tv);
2427 }
2428 if ((di = dict_find(dict, (char_u *)"id", -1)) != NULL)
2429 {
2430 // get sign placed with this identifier
2431 sign_id = (int)tv_get_number_chk(&di->di_tv, &notanum);
2432 if (notanum)
2433 return;
2434 }
2435 if ((di = dict_find(dict, (char_u *)"group", -1)) != NULL)
2436 {
2437 group = tv_get_string_chk(&di->di_tv);
2438 if (group == NULL)
2439 return;
2440 if (*group == '\0') // empty string means global group
2441 group = NULL;
2442 }
2443 }
2444 }
2445
2446 sign_get_placed(buf, lnum, sign_id, group, rettv->vval.v_list);
2447}
2448
2449/*
2450 * "sign_jump()" function
2451 */
2452 void
2453f_sign_jump(typval_T *argvars, typval_T *rettv)
2454{
2455 int sign_id;
2456 char_u *sign_group = NULL;
2457 buf_T *buf;
2458 int notanum = FALSE;
2459
2460 rettv->vval.v_number = -1;
2461
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002462 if (in_vim9script()
2463 && (check_for_number_arg(argvars, 0) == FAIL
2464 || check_for_string_arg(argvars, 1) == FAIL
2465 || check_for_buffer_arg(argvars, 2) == FAIL))
2466 return;
2467
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002468 // Sign identifier
2469 sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
2470 if (notanum)
2471 return;
2472 if (sign_id <= 0)
2473 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00002474 emsg(_(e_invalid_argument));
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002475 return;
2476 }
2477
2478 // Sign group
2479 sign_group = tv_get_string_chk(&argvars[1]);
2480 if (sign_group == NULL)
2481 return;
2482 if (sign_group[0] == '\0')
2483 sign_group = NULL; // global sign group
2484 else
2485 {
2486 sign_group = vim_strsave(sign_group);
2487 if (sign_group == NULL)
2488 return;
2489 }
2490
2491 // Buffer to place the sign
2492 buf = get_buf_arg(&argvars[2]);
2493 if (buf == NULL)
2494 goto cleanup;
2495
2496 rettv->vval.v_number = sign_jump(sign_id, sign_group, buf);
2497
2498cleanup:
2499 vim_free(sign_group);
2500}
2501
2502/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002503 * Place a new sign using the values specified in dict 'dict'. Returns the sign
2504 * identifier if successfully placed, otherwise returns 0.
2505 */
2506 static int
2507sign_place_from_dict(
2508 typval_T *id_tv,
2509 typval_T *group_tv,
2510 typval_T *name_tv,
2511 typval_T *buf_tv,
2512 dict_T *dict)
2513{
2514 int sign_id = 0;
2515 char_u *group = NULL;
2516 char_u *sign_name = NULL;
2517 buf_T *buf = NULL;
2518 dictitem_T *di;
2519 linenr_T lnum = 0;
2520 int prio = SIGN_DEF_PRIO;
2521 int notanum = FALSE;
2522 int ret_sign_id = -1;
2523
2524 // sign identifier
2525 if (id_tv == NULL)
2526 {
2527 di = dict_find(dict, (char_u *)"id", -1);
2528 if (di != NULL)
2529 id_tv = &di->di_tv;
2530 }
2531 if (id_tv == NULL)
2532 sign_id = 0;
2533 else
2534 {
2535 sign_id = tv_get_number_chk(id_tv, &notanum);
2536 if (notanum)
2537 return -1;
2538 if (sign_id < 0)
2539 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00002540 emsg(_(e_invalid_argument));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002541 return -1;
2542 }
2543 }
2544
2545 // sign group
2546 if (group_tv == NULL)
2547 {
2548 di = dict_find(dict, (char_u *)"group", -1);
2549 if (di != NULL)
2550 group_tv = &di->di_tv;
2551 }
2552 if (group_tv == NULL)
2553 group = NULL; // global group
2554 else
2555 {
2556 group = tv_get_string_chk(group_tv);
2557 if (group == NULL)
2558 goto cleanup;
2559 if (group[0] == '\0') // global sign group
2560 group = NULL;
2561 else
2562 {
2563 group = vim_strsave(group);
2564 if (group == NULL)
2565 return -1;
2566 }
2567 }
2568
2569 // sign name
2570 if (name_tv == NULL)
2571 {
2572 di = dict_find(dict, (char_u *)"name", -1);
2573 if (di != NULL)
2574 name_tv = &di->di_tv;
2575 }
2576 if (name_tv == NULL)
2577 goto cleanup;
2578 sign_name = tv_get_string_chk(name_tv);
2579 if (sign_name == NULL)
2580 goto cleanup;
2581
2582 // buffer to place the sign
2583 if (buf_tv == NULL)
2584 {
2585 di = dict_find(dict, (char_u *)"buffer", -1);
2586 if (di != NULL)
2587 buf_tv = &di->di_tv;
2588 }
2589 if (buf_tv == NULL)
2590 goto cleanup;
2591 buf = get_buf_arg(buf_tv);
2592 if (buf == NULL)
2593 goto cleanup;
2594
2595 // line number of the sign
2596 di = dict_find(dict, (char_u *)"lnum", -1);
2597 if (di != NULL)
2598 {
Bram Moolenaar42aff462019-08-21 13:20:29 +02002599 lnum = tv_get_lnum(&di->di_tv);
2600 if (lnum <= 0)
2601 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00002602 emsg(_(e_invalid_argument));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002603 goto cleanup;
Bram Moolenaar42aff462019-08-21 13:20:29 +02002604 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002605 }
2606
2607 // sign priority
2608 di = dict_find(dict, (char_u *)"priority", -1);
2609 if (di != NULL)
2610 {
2611 prio = (int)tv_get_number_chk(&di->di_tv, &notanum);
2612 if (notanum)
2613 goto cleanup;
2614 }
2615
2616 if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK)
2617 ret_sign_id = sign_id;
2618
2619cleanup:
2620 vim_free(group);
2621
2622 return ret_sign_id;
2623}
2624
2625/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002626 * "sign_place()" function
2627 */
2628 void
2629f_sign_place(typval_T *argvars, typval_T *rettv)
2630{
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002631 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002632
2633 rettv->vval.v_number = -1;
2634
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002635 if (in_vim9script()
2636 && (check_for_number_arg(argvars, 0) == FAIL
2637 || check_for_string_arg(argvars, 1) == FAIL
2638 || check_for_string_arg(argvars, 2) == FAIL
2639 || check_for_buffer_arg(argvars, 3) == FAIL
2640 || check_for_opt_dict_arg(argvars, 4) == FAIL))
2641 return;
2642
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002643 if (argvars[4].v_type != VAR_UNKNOWN
2644 && (argvars[4].v_type != VAR_DICT
2645 || ((dict = argvars[4].vval.v_dict) == NULL)))
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002646 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002647 emsg(_(e_dictionary_required));
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002648 return;
2649 }
2650
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002651 rettv->vval.v_number = sign_place_from_dict(&argvars[0], &argvars[1],
2652 &argvars[2], &argvars[3], dict);
2653}
2654
2655/*
2656 * "sign_placelist()" function. Place multiple signs.
2657 */
2658 void
2659f_sign_placelist(typval_T *argvars, typval_T *rettv)
2660{
2661 listitem_T *li;
2662 int sign_id;
2663
2664 if (rettv_list_alloc(rettv) != OK)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002665 return;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002666
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002667 if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
2668 return;
2669
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002670 if (argvars[0].v_type != VAR_LIST)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002671 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002672 emsg(_(e_list_required));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002673 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002674 }
2675
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002676 // Process the List of sign attributes
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002677 FOR_ALL_LIST_ITEMS(argvars[0].vval.v_list, li)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002678 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002679 sign_id = -1;
2680 if (li->li_tv.v_type == VAR_DICT)
2681 sign_id = sign_place_from_dict(NULL, NULL, NULL, NULL,
2682 li->li_tv.vval.v_dict);
2683 else
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002684 emsg(_(e_dictionary_required));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002685 list_append_number(rettv->vval.v_list, sign_id);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002686 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002687}
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002688
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002689/*
2690 * Undefine multiple signs
2691 */
2692 static void
2693sign_undefine_multiple(list_T *l, list_T *retlist)
2694{
2695 char_u *name;
2696 listitem_T *li;
2697 int retval;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002698
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002699 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002700 {
2701 retval = -1;
2702 name = tv_get_string_chk(&li->li_tv);
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002703 if (name != NULL && (sign_undefine_by_name(name, TRUE) == OK))
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002704 retval = 0;
2705 list_append_number(retlist, retval);
2706 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002707}
2708
2709/*
2710 * "sign_undefine()" function
2711 */
2712 void
2713f_sign_undefine(typval_T *argvars, typval_T *rettv)
2714{
2715 char_u *name;
2716
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002717 if (in_vim9script()
2718 && check_for_opt_string_or_list_arg(argvars, 0) == FAIL)
2719 return;
2720
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002721 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2722 {
2723 // Undefine multiple signs
2724 if (rettv_list_alloc(rettv) != OK)
2725 return;
2726
2727 sign_undefine_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2728 return;
2729 }
2730
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002731 rettv->vval.v_number = -1;
2732
2733 if (argvars[0].v_type == VAR_UNKNOWN)
2734 {
2735 // Free all the signs
2736 free_signs();
2737 rettv->vval.v_number = 0;
2738 }
2739 else
2740 {
2741 // Free only the specified sign
2742 name = tv_get_string_chk(&argvars[0]);
2743 if (name == NULL)
2744 return;
2745
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002746 if (sign_undefine_by_name(name, TRUE) == OK)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002747 rettv->vval.v_number = 0;
2748 }
2749}
2750
2751/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002752 * Unplace the sign with attributes specified in 'dict'. Returns 0 on success
2753 * and -1 on failure.
2754 */
2755 static int
2756sign_unplace_from_dict(typval_T *group_tv, dict_T *dict)
2757{
2758 dictitem_T *di;
2759 int sign_id = 0;
2760 buf_T *buf = NULL;
2761 char_u *group = NULL;
2762 int retval = -1;
2763
2764 // sign group
2765 if (group_tv != NULL)
2766 group = tv_get_string(group_tv);
2767 else
2768 group = dict_get_string(dict, (char_u *)"group", FALSE);
2769 if (group != NULL)
2770 {
2771 if (group[0] == '\0') // global sign group
2772 group = NULL;
2773 else
2774 {
2775 group = vim_strsave(group);
2776 if (group == NULL)
2777 return -1;
2778 }
2779 }
2780
2781 if (dict != NULL)
2782 {
2783 if ((di = dict_find(dict, (char_u *)"buffer", -1)) != NULL)
2784 {
2785 buf = get_buf_arg(&di->di_tv);
2786 if (buf == NULL)
2787 goto cleanup;
2788 }
2789 if (dict_find(dict, (char_u *)"id", -1) != NULL)
2790 {
2791 sign_id = dict_get_number(dict, (char_u *)"id");
2792 if (sign_id <= 0)
2793 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00002794 emsg(_(e_invalid_argument));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002795 goto cleanup;
2796 }
2797 }
2798 }
2799
2800 if (buf == NULL)
2801 {
2802 // Delete the sign in all the buffers
2803 retval = 0;
2804 FOR_ALL_BUFFERS(buf)
2805 if (sign_unplace(sign_id, group, buf, 0) != OK)
2806 retval = -1;
2807 }
2808 else if (sign_unplace(sign_id, group, buf, 0) == OK)
2809 retval = 0;
2810
2811cleanup:
2812 vim_free(group);
2813
2814 return retval;
2815}
2816
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002817 sign_entry_T *
2818get_first_valid_sign(win_T *wp)
2819{
2820 sign_entry_T *sign = wp->w_buffer->b_signlist;
2821
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002822# ifdef FEAT_PROP_POPUP
Bram Moolenaar72570732019-11-30 14:21:53 +01002823 while (sign != NULL && !sign_group_for_window(sign, wp))
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002824 sign = sign->se_next;
2825# endif
2826 return sign;
2827}
2828
2829/*
2830 * Return TRUE when window "wp" has a column to draw signs in.
2831 */
2832 int
2833signcolumn_on(win_T *wp)
2834{
2835 // If 'signcolumn' is set to 'number', signs are displayed in the 'number'
2836 // column (if present). Otherwise signs are to be displayed in the sign
2837 // column.
2838 if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u')
2839 return get_first_valid_sign(wp) != NULL && !wp->w_p_nu && !wp->w_p_rnu;
2840
2841 if (*wp->w_p_scl == 'n')
2842 return FALSE;
2843 if (*wp->w_p_scl == 'y')
2844 return TRUE;
2845 return (get_first_valid_sign(wp) != NULL
2846# ifdef FEAT_NETBEANS_INTG
2847 || wp->w_buffer->b_has_sign_column
2848# endif
2849 );
2850}
2851
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002852/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002853 * "sign_unplace()" function
2854 */
2855 void
2856f_sign_unplace(typval_T *argvars, typval_T *rettv)
2857{
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002858 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002859
2860 rettv->vval.v_number = -1;
2861
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002862 if (in_vim9script()
2863 && (check_for_string_arg(argvars, 0) == FAIL
2864 || check_for_opt_dict_arg(argvars, 1) == FAIL))
2865 return;
2866
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002867 if (argvars[0].v_type != VAR_STRING)
2868 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00002869 emsg(_(e_invalid_argument));
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002870 return;
2871 }
2872
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002873 if (argvars[1].v_type != VAR_UNKNOWN)
2874 {
2875 if (argvars[1].v_type != VAR_DICT)
2876 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002877 emsg(_(e_dictionary_required));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002878 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002879 }
2880 dict = argvars[1].vval.v_dict;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002881 }
2882
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002883 rettv->vval.v_number = sign_unplace_from_dict(&argvars[0], dict);
2884}
2885
2886/*
2887 * "sign_unplacelist()" function
2888 */
2889 void
2890f_sign_unplacelist(typval_T *argvars, typval_T *rettv)
2891{
2892 listitem_T *li;
2893 int retval;
2894
2895 if (rettv_list_alloc(rettv) != OK)
2896 return;
2897
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002898 if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
2899 return;
2900
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002901 if (argvars[0].v_type != VAR_LIST)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002902 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002903 emsg(_(e_list_required));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002904 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002905 }
2906
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002907 FOR_ALL_LIST_ITEMS(argvars[0].vval.v_list, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002908 {
2909 retval = -1;
2910 if (li->li_tv.v_type == VAR_DICT)
2911 retval = sign_unplace_from_dict(NULL, li->li_tv.vval.v_dict);
2912 else
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00002913 emsg(_(e_dictionary_required));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002914 list_append_number(rettv->vval.v_list, retval);
2915 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002916}
2917
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002918#endif // FEAT_SIGNS