blob: e2259095064ba56e16f056385ef445650b37660c [file] [log] [blame]
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001/* vi:set ts=8 sts=4 sw=4 et:
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002 *
Luca Saccarola3cf094e2024-11-19 22:55:13 +01003 * VIM - Vi IMproved by Bram Moolenaar
Bram Moolenaarbbea4702019-01-01 13:20:31 +01004 *
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{
Luca Saccarola3cf094e2024-11-19 22:55:13 +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
Luca Saccarola3cf094e2024-11-19 22:55:13 +010030 void *sn_image; // icon image
Bram Moolenaarbbea4702019-01-01 13:20:31 +010031# endif
Luca Saccarola3cf094e2024-11-19 22:55:13 +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
35 int sn_cul_hl; // highlight ID for text on current line when 'cursorline' is set
36 int sn_num_hl; // highlight ID for line number
37 int sn_priority; // default priority of this sign, -1 means SIGN_DEF_PRIO
Bram Moolenaarbbea4702019-01-01 13:20:31 +010038};
39
Luca Saccarola3cf094e2024-11-19 22:55:13 +010040static sign_T *first_sign = NULL;
41static int next_sign_typenr = 1;
Bram Moolenaarbbea4702019-01-01 13:20:31 +010042
43static void sign_list_defined(sign_T *sp);
44static void sign_undefine(sign_T *sp, sign_T *sp_prev);
45
Luca Saccarola3cf094e2024-11-19 22:55:13 +010046static char *cmds[] = { "define",
47# define SIGNCMD_DEFINE 0
48 "undefine",
Bram Moolenaarbbea4702019-01-01 13:20:31 +010049# define SIGNCMD_UNDEFINE 1
Luca Saccarola3cf094e2024-11-19 22:55:13 +010050 "list",
51# define SIGNCMD_LIST 2
52 "place",
53# define SIGNCMD_PLACE 3
54 "unplace",
Bram Moolenaarbbea4702019-01-01 13:20:31 +010055# define SIGNCMD_UNPLACE 4
Luca Saccarola3cf094e2024-11-19 22:55:13 +010056 "jump",
57# define SIGNCMD_JUMP 5
58 NULL
59# define SIGNCMD_LAST 6
Bram Moolenaarbbea4702019-01-01 13:20:31 +010060};
61
Luca Saccarola3cf094e2024-11-19 22:55:13 +010062# define FOR_ALL_SIGNS(sp) \
63 for ((sp) = first_sign; (sp) != NULL; (sp) = (sp)->sn_next)
Bram Moolenaaraeea7212020-04-02 18:50:46 +020064
Luca Saccarola3cf094e2024-11-19 22:55:13 +010065static hashtab_T sg_table; // sign group (signgroup_T) hashtable
66static int next_sign_id = 1; // next sign id in the global group
Bram Moolenaarbbea4702019-01-01 13:20:31 +010067
68/*
69 * Initialize data needed for managing signs
70 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +010071void
Bram Moolenaarbbea4702019-01-01 13:20:31 +010072init_signs(void)
73{
Luca Saccarola3cf094e2024-11-19 22:55:13 +010074 hash_init(&sg_table); // sign group hash table
Bram Moolenaarbbea4702019-01-01 13:20:31 +010075}
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 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +010081static signgroup_T *
Bram Moolenaarbbea4702019-01-01 13:20:31 +010082sign_group_ref(char_u *groupname)
83{
Luca Saccarola3cf094e2024-11-19 22:55:13 +010084 hash_T hash;
85 hashitem_T *hi;
86 signgroup_T *group;
Bram Moolenaarbbea4702019-01-01 13:20:31 +010087
88 hash = hash_hash(groupname);
89 hi = hash_lookup(&sg_table, groupname, hash);
90 if (HASHITEM_EMPTY(hi))
91 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +010092 // new group
93 group = alloc(offsetof(signgroup_T, sg_name) + STRLEN(groupname) + 1);
94 if (group == NULL)
95 return NULL;
96 STRCPY(group->sg_name, groupname);
97 group->sg_refcount = 1;
98 group->sg_next_sign_id = 1;
99 hash_add_item(&sg_table, hi, group->sg_name, hash);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100100 }
101 else
102 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100103 // existing group
104 group = HI2SG(hi);
105 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 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100115static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100116sign_group_unref(char_u *groupname)
117{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100118 hashitem_T *hi;
119 signgroup_T *group;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100120
121 hi = hash_find(&sg_table, groupname);
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000122 if (HASHITEM_EMPTY(hi))
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100123 return;
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000124
125 group = HI2SG(hi);
126 group->sg_refcount--;
127 if (group->sg_refcount == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100128 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100129 // All the signs in this group are removed
130 hash_remove(&sg_table, hi, "sign remove");
131 vim_free(group);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100132 }
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 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100140static 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{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100143 return ((group != NULL && STRCMP(group, "*") == 0) ||
144 (group == NULL && sign->se_group == NULL) ||
145 (group != NULL && sign->se_group != NULL &&
146 STRCMP(group, sign->se_group->sg_name) == 0));
Bram Moolenaar72570732019-11-30 14:21:53 +0100147}
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 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100153static int
Bram Moolenaar72570732019-11-30 14:21:53 +0100154sign_group_for_window(sign_entry_T *sign, win_T *wp)
155{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100156 int for_popup = sign->se_group != NULL &&
157 STRNCMP("PopUp", sign->se_group->sg_name, 5) == 0;
Bram Moolenaar72570732019-11-30 14:21:53 +0100158
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 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100165static int
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100166sign_group_get_next_signid(buf_T *buf, char_u *groupname)
167{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100168 int id = 1;
169 signgroup_T *group = NULL;
170 sign_entry_T *sign;
171 hashitem_T *hi;
172 int found = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100173
174 if (groupname != NULL)
175 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100176 hi = hash_find(&sg_table, groupname);
177 if (HASHITEM_EMPTY(hi))
178 return id;
179 group = HI2SG(hi);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100180 }
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 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100185 if (group == NULL)
186 id = next_sign_id++; // global group
187 else
188 id = group->sg_next_sign_id++;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100189
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100190 // Check whether this sign is already placed in the buffer
191 found = TRUE;
192 FOR_ALL_SIGNS_IN_BUF(buf, sign)
193 {
194 if (id == sign->se_id && sign_in_group(sign, groupname))
195 {
196 found = FALSE; // sign identifier is in use
197 break;
198 }
199 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100200 }
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 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100209static void
210insert_sign(buf_T *buf, // buffer to store sign in
211 sign_entry_T *prev, // previous sign entry
212 sign_entry_T *next, // next sign entry
213 int id, // sign ID
214 char_u *group, // sign group; NULL for global group
215 int prio, // sign priority
216 linenr_T lnum, // line number which gets the mark
217 int typenr) // typenr of sign we are adding
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100218{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200219 sign_entry_T *newsign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100220
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200221 newsign = lalloc_id(sizeof(sign_entry_T), FALSE, aid_insert_sign);
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000222 if (newsign == NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100223 return;
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000224
225 newsign->se_id = id;
226 newsign->se_lnum = lnum;
227 newsign->se_typenr = typenr;
228 if (group != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100229 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100230 newsign->se_group = sign_group_ref(group);
231 if (newsign->se_group == NULL)
232 {
233 vim_free(newsign);
234 return;
235 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100236 }
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000237 else
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100238 newsign->se_group = NULL;
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000239 newsign->se_priority = prio;
240 newsign->se_next = next;
241 newsign->se_prev = prev;
242 if (next != NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100243 next->se_prev = newsign;
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000244
245 if (prev == NULL)
246 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100247 // 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, UPD_NOT_VALID);
252 changed_line_abv_curs();
253 }
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000254
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100255 // 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
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000261 }
262 else
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100263 prev->se_next = newsign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100264}
265
266/*
267 * Insert a new sign sorted by line number and sign priority.
268 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100269static void
270insert_sign_by_lnum_prio(buf_T *buf, // buffer to store sign in
271 sign_entry_T *prev, // previous sign entry
272 int id, // sign ID
273 char_u *group, // sign group; NULL for global group
274 int prio, // sign priority
275 linenr_T lnum, // line number which gets the mark
276 int typenr) // typenr of sign we are adding
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100277{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100278 sign_entry_T *sign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100279
280 // keep signs sorted by lnum and by priority: insert new sign at
281 // the proper position in the list for this lnum.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200282 while (prev != NULL && prev->se_lnum == lnum && prev->se_priority <= prio)
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100283 prev = prev->se_prev;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100284 if (prev == NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100285 sign = buf->b_signlist;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100286 else
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100287 sign = prev->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100288
289 insert_sign(buf, prev, sign, id, group, prio, lnum, typenr);
290}
291
292/*
Bram Moolenaar4e038572019-07-04 18:28:35 +0200293 * Lookup a sign by typenr. Returns NULL if sign is not found.
294 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100295static sign_T *
Bram Moolenaar4e038572019-07-04 18:28:35 +0200296find_sign_by_typenr(int typenr)
297{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100298 sign_T *sp;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200299
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200300 FOR_ALL_SIGNS(sp)
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100301 if (sp->sn_typenr == typenr)
302 return sp;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200303 return NULL;
304}
305
306/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100307 * Get the name of a sign by its typenr.
308 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100309static char_u *
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100310sign_typenr2name(int typenr)
311{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100312 sign_T *sp;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100313
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200314 FOR_ALL_SIGNS(sp)
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100315 if (sp->sn_typenr == typenr)
316 return sp->sn_name;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100317 return (char_u *)_("[Deleted]");
318}
319
320/*
321 * Return information about a sign in a Dict
322 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100323static dict_T *
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200324sign_get_info(sign_entry_T *sign)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100325{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100326 dict_T *d;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100327
328 if ((d = dict_alloc_id(aid_sign_getinfo)) == NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100329 return NULL;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200330 dict_add_number(d, "id", sign->se_id);
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100331 dict_add_string(d, "group",
332 (sign->se_group == NULL) ? (char_u *)""
333 : sign->se_group->sg_name);
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200334 dict_add_number(d, "lnum", sign->se_lnum);
335 dict_add_string(d, "name", sign_typenr2name(sign->se_typenr));
336 dict_add_number(d, "priority", sign->se_priority);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100337
338 return d;
339}
340
341/*
Bram Moolenaar64416122019-06-07 21:37:13 +0200342 * Sort the signs placed on the same line as "sign" by priority. Invoked after
343 * changing the priority of an already placed sign. Assumes the signs in the
344 * buffer are sorted by line number and priority.
345 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100346static void
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200347sign_sort_by_prio_on_line(buf_T *buf, sign_entry_T *sign)
Bram Moolenaar64416122019-06-07 21:37:13 +0200348{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200349 sign_entry_T *p = NULL;
Bram Moolenaar64416122019-06-07 21:37:13 +0200350
351 // If there is only one sign in the buffer or only one sign on the line or
352 // the sign is already sorted by priority, then return.
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100353 if ((sign->se_prev == NULL || sign->se_prev->se_lnum != sign->se_lnum ||
354 sign->se_prev->se_priority > sign->se_priority) &&
355 (sign->se_next == NULL || sign->se_next->se_lnum != sign->se_lnum ||
356 sign->se_next->se_priority < sign->se_priority))
357 return;
Bram Moolenaar64416122019-06-07 21:37:13 +0200358
359 // One or more signs on the same line as 'sign'
360 // Find a sign after which 'sign' should be inserted
361
362 // First search backward for a sign with higher priority on the same line
363 p = sign;
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100364 while (p->se_prev != NULL && p->se_prev->se_lnum == sign->se_lnum &&
365 p->se_prev->se_priority <= sign->se_priority)
366 p = p->se_prev;
Bram Moolenaar64416122019-06-07 21:37:13 +0200367
368 if (p == sign)
369 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100370 // Sign not found. Search forward for a sign with priority just before
371 // 'sign'.
372 p = sign->se_next;
373 while (p->se_next != NULL && p->se_next->se_lnum == sign->se_lnum &&
374 p->se_next->se_priority > sign->se_priority)
375 p = p->se_next;
Bram Moolenaar64416122019-06-07 21:37:13 +0200376 }
377
378 // Remove 'sign' from the list
379 if (buf->b_signlist == sign)
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100380 buf->b_signlist = sign->se_next;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200381 if (sign->se_prev != NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100382 sign->se_prev->se_next = sign->se_next;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200383 if (sign->se_next != NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100384 sign->se_next->se_prev = sign->se_prev;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200385 sign->se_prev = NULL;
386 sign->se_next = NULL;
Bram Moolenaar64416122019-06-07 21:37:13 +0200387
388 // Re-insert 'sign' at the right place
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200389 if (p->se_priority <= sign->se_priority)
Bram Moolenaar64416122019-06-07 21:37:13 +0200390 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100391 // 'sign' has a higher priority and should be inserted before 'p'
392 sign->se_prev = p->se_prev;
393 sign->se_next = p;
394 p->se_prev = sign;
395 if (sign->se_prev != NULL)
396 sign->se_prev->se_next = sign;
397 if (buf->b_signlist == p)
398 buf->b_signlist = sign;
Bram Moolenaar64416122019-06-07 21:37:13 +0200399 }
400 else
401 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100402 // 'sign' has a lower priority and should be inserted after 'p'
403 sign->se_prev = p;
404 sign->se_next = p->se_next;
405 p->se_next = sign;
406 if (sign->se_next != NULL)
407 sign->se_next->se_prev = sign;
Bram Moolenaar64416122019-06-07 21:37:13 +0200408 }
409}
410
411/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100412 * Add the sign into the signlist. Find the right spot to do it though.
413 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100414static void
415buf_addsign(buf_T *buf, // buffer to store sign in
416 int id, // sign ID
417 char_u *groupname, // sign group
418 int prio, // sign priority
419 linenr_T lnum, // line number which gets the mark
420 int typenr) // typenr of sign we are adding
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100421{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100422 sign_entry_T *sign; // a sign in the signlist
423 sign_entry_T *prev; // the previous sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100424
425 prev = NULL;
426 FOR_ALL_SIGNS_IN_BUF(buf, sign)
427 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100428 if (lnum == sign->se_lnum && id == sign->se_id &&
429 sign_in_group(sign, groupname))
430 {
431 // Update an existing sign
432 sign->se_typenr = typenr;
433 sign->se_priority = prio;
434 sign_sort_by_prio_on_line(buf, sign);
435 return;
436 }
437 else if (lnum < sign->se_lnum)
438 {
439 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum,
440 typenr);
441 return;
442 }
443 prev = sign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100444 }
445
446 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr);
447 return;
448}
449
450/*
451 * For an existing, placed sign "markId" change the type to "typenr".
452 * Returns the line number of the sign, or zero if the sign is not found.
453 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100454static linenr_T
455buf_change_sign_type(buf_T *buf, // buffer to store sign in
456 int markId, // sign ID
457 char_u *group, // sign group
458 int typenr, // typenr of sign we are adding
459 int prio) // sign priority
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100460{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100461 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100462
463 FOR_ALL_SIGNS_IN_BUF(buf, sign)
464 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100465 if (sign->se_id == markId && sign_in_group(sign, group))
466 {
467 sign->se_typenr = typenr;
468 sign->se_priority = prio;
469 sign_sort_by_prio_on_line(buf, sign);
470 return sign->se_lnum;
471 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100472 }
473
474 return (linenr_T)0;
475}
476
477/*
Bram Moolenaar4e038572019-07-04 18:28:35 +0200478 * Return the attributes of the first sign placed on line 'lnum' in buffer
479 * 'buf'. Used when refreshing the screen. Returns TRUE if a sign is found on
480 * 'lnum', FALSE otherwise.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100481 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100482int
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100483buf_get_signattrs(win_T *wp, linenr_T lnum, sign_attrs_T *sattr)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100484{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100485 sign_entry_T *sign;
486 sign_T *sp;
487 buf_T *buf = wp->w_buffer;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200488
Bram Moolenaara80faa82020-04-12 19:37:17 +0200489 CLEAR_POINTER(sattr);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100490
491 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200492 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100493 if (sign->se_lnum > lnum)
494 // Signs are sorted by line number in the buffer. No need to check
495 // for signs after the specified line number 'lnum'.
496 break;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200497
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100498 if (sign->se_lnum == lnum
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100499# ifdef FEAT_PROP_POPUP
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100500 && sign_group_for_window(sign, wp)
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100501# endif
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100502 )
503 {
504 sattr->sat_typenr = sign->se_typenr;
505 sp = find_sign_by_typenr(sign->se_typenr);
506 if (sp == NULL)
507 return FALSE;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200508
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100509# ifdef FEAT_SIGN_ICONS
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100510 sattr->sat_icon = sp->sn_image;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100511# endif
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100512 sattr->sat_text = sp->sn_text;
513 if (sattr->sat_text != NULL && sp->sn_text_hl > 0)
514 sattr->sat_texthl = syn_id2attr(sp->sn_text_hl);
515 if (sp->sn_line_hl > 0)
516 sattr->sat_linehl = syn_id2attr(sp->sn_line_hl);
517 if (sp->sn_cul_hl > 0)
518 sattr->sat_culhl = syn_id2attr(sp->sn_cul_hl);
519 if (sp->sn_num_hl > 0)
520 sattr->sat_numhl = syn_id2attr(sp->sn_num_hl);
521 sattr->sat_priority = sign->se_priority;
Bram Moolenaara2f6e422020-02-19 17:13:04 +0100522
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100523 // If there is another sign next with the same priority, may
524 // combine the text and the line highlighting.
525 if (sign->se_next != NULL &&
526 sign->se_next->se_priority == sign->se_priority &&
527 sign->se_next->se_lnum == sign->se_lnum)
528 {
529 sign_T *next_sp = find_sign_by_typenr(sign->se_next->se_typenr);
Bram Moolenaara2f6e422020-02-19 17:13:04 +0100530
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100531 if (next_sp != NULL)
532 {
533 if (sattr->sat_icon == NULL && sattr->sat_text == NULL)
534 {
Bram Moolenaara2f6e422020-02-19 17:13:04 +0100535# ifdef FEAT_SIGN_ICONS
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100536 sattr->sat_icon = next_sp->sn_image;
Bram Moolenaara2f6e422020-02-19 17:13:04 +0100537# endif
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100538 sattr->sat_text = next_sp->sn_text;
539 }
540 if (sp->sn_text_hl <= 0 && next_sp->sn_text_hl > 0)
541 sattr->sat_texthl = syn_id2attr(next_sp->sn_text_hl);
542 if (sp->sn_line_hl <= 0 && next_sp->sn_line_hl > 0)
543 sattr->sat_linehl = syn_id2attr(next_sp->sn_line_hl);
544 if (sp->sn_cul_hl <= 0 && next_sp->sn_cul_hl > 0)
545 sattr->sat_culhl = syn_id2attr(next_sp->sn_cul_hl);
546 if (sp->sn_num_hl <= 0 && next_sp->sn_num_hl > 0)
547 sattr->sat_numhl = syn_id2attr(next_sp->sn_num_hl);
548 }
549 }
550 return TRUE;
551 }
Bram Moolenaar4e038572019-07-04 18:28:35 +0200552 }
553 return FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100554}
555
556/*
557 * Delete sign 'id' in group 'group' from buffer 'buf'.
558 * If 'id' is zero, then delete all the signs in group 'group'. Otherwise
559 * delete only the specified sign.
560 * If 'group' is '*', then delete the sign in all the groups. If 'group' is
561 * NULL, then delete the sign in the global group. Otherwise delete the sign in
562 * the specified group.
563 * Returns the line number of the deleted sign. If multiple signs are deleted,
564 * then returns the line number of the last sign deleted.
565 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100566linenr_T
567buf_delsign(buf_T *buf, // buffer sign is stored in
568 linenr_T atlnum, // sign at this line, 0 - at any line
569 int id, // sign id
570 char_u *group) // sign group
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100571{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100572 sign_entry_T **lastp; // pointer to pointer to current sign
573 sign_entry_T *sign; // a sign in a b_signlist
574 sign_entry_T *next; // the next sign in a b_signlist
575 linenr_T lnum; // line number whose sign was deleted
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100576
577 lastp = &buf->b_signlist;
578 lnum = 0;
579 for (sign = buf->b_signlist; sign != NULL; sign = next)
580 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100581 next = sign->se_next;
582 if ((id == 0 || sign->se_id == id) &&
583 (atlnum == 0 || sign->se_lnum == atlnum) &&
584 sign_in_group(sign, group))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100585
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100586 {
587 *lastp = next;
588 if (next != NULL)
589 next->se_prev = sign->se_prev;
590 lnum = sign->se_lnum;
591 if (sign->se_group != NULL)
592 sign_group_unref(sign->se_group->sg_name);
593 vim_free(sign);
594 redraw_buf_line_later(buf, lnum);
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100595
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100596 // Check whether only one sign needs to be deleted
597 // If deleting a sign with a specific identifier in a particular
598 // group or deleting any sign at a particular line number, delete
599 // only one sign.
600 if (group == NULL || (*group != '*' && id != 0) ||
601 (*group == '*' && atlnum != 0))
602 break;
603 }
604 else
605 lastp = &sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100606 }
607
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100608 // When deleting the last sign the cursor position may change, because the
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100609 // sign columns no longer shows. And the 'signcolumn' may be hidden.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100610 if (buf->b_signlist == NULL)
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100611 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100612 redraw_buf_later(buf, UPD_NOT_VALID);
613 changed_line_abv_curs();
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100614 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100615
616 return lnum;
617}
618
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100619/*
620 * Find the line number of the sign with the requested id in group 'group'. If
621 * the sign does not exist, return 0 as the line number. This will still let
622 * the correct file get loaded.
623 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100624int
625buf_findsign(buf_T *buf, // buffer to store sign in
626 int id, // sign ID
627 char_u *group) // sign group
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100628{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100629 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100630
631 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100632 if (sign->se_id == id && sign_in_group(sign, group))
633 return sign->se_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100634
635 return 0;
636}
637
638/*
639 * Return the sign at line 'lnum' in buffer 'buf'. Returns NULL if a sign is
640 * not found at the line. If 'groupname' is NULL, searches in the global group.
641 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100642static sign_entry_T *
643buf_getsign_at_line(buf_T *buf, // buffer whose sign we are searching for
644 linenr_T lnum, // line number of sign
645 char_u *groupname) // sign group name
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100646{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100647 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100648
649 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200650 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100651 if (sign->se_lnum > lnum)
652 // Signs are sorted by line number in the buffer. No need to check
653 // for signs after the specified line number 'lnum'.
654 break;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200655
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100656 if (sign->se_lnum == lnum && sign_in_group(sign, groupname))
657 return sign;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200658 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100659
660 return NULL;
661}
662
663/*
664 * Return the identifier of the sign at line number 'lnum' in buffer 'buf'.
665 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100666int
667buf_findsign_id(buf_T *buf, // buffer whose sign we are searching for
668 linenr_T lnum, // line number of sign
669 char_u *groupname) // sign group name
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100670{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100671 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100672
673 sign = buf_getsign_at_line(buf, lnum, groupname);
674 if (sign != NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100675 return sign->se_id;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100676
677 return 0;
678}
679
680# if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
681/*
682 * See if a given type of sign exists on a specific line.
683 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100684int
685buf_findsigntype_id(buf_T *buf, // buffer whose sign we are searching for
686 linenr_T lnum, // line number of sign
687 int typenr) // sign type number
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100688{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100689 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100690
691 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200692 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100693 if (sign->se_lnum > lnum)
694 // Signs are sorted by line number in the buffer. No need to check
695 // for signs after the specified line number 'lnum'.
696 break;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200697
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100698 if (sign->se_lnum == lnum && sign->se_typenr == typenr)
699 return sign->se_id;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200700 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100701
702 return 0;
703}
704
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100705# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
706/*
707 * Return the number of icons on the given line.
708 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100709int
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100710buf_signcount(buf_T *buf, linenr_T lnum)
711{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100712 sign_entry_T *sign; // a sign in the signlist
713 int count = 0;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100714
715 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200716 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100717 if (sign->se_lnum > lnum)
718 // Signs are sorted by line number in the buffer. No need to check
719 // for signs after the specified line number 'lnum'.
720 break;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200721
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100722 if (sign->se_lnum == lnum)
723 if (sign_get_image(sign->se_typenr) != NULL)
724 count++;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200725 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100726
727 return count;
728}
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100729# endif // FEAT_SIGN_ICONS
730# endif // FEAT_NETBEANS_INTG
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100731
732/*
733 * Delete signs in group 'group' in buffer "buf". If 'group' is '*', then
734 * delete all the signs.
735 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100736void
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100737buf_delete_signs(buf_T *buf, char_u *group)
738{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100739 sign_entry_T *sign;
740 sign_entry_T **lastp; // pointer to pointer to current sign
741 sign_entry_T *next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100742
743 // When deleting the last sign need to redraw the windows to remove the
744 // sign column. Not when curwin is NULL (this means we're exiting).
745 if (buf->b_signlist != NULL && curwin != NULL)
746 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100747 redraw_buf_later(buf, UPD_NOT_VALID);
748 changed_line_abv_curs();
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100749 }
750
751 lastp = &buf->b_signlist;
752 for (sign = buf->b_signlist; sign != NULL; sign = next)
753 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100754 next = sign->se_next;
755 if (sign_in_group(sign, group))
756 {
757 *lastp = next;
758 if (next != NULL)
759 next->se_prev = sign->se_prev;
760 if (sign->se_group != NULL)
761 sign_group_unref(sign->se_group->sg_name);
762 vim_free(sign);
763 }
764 else
765 lastp = &sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100766 }
767}
768
769/*
770 * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers.
771 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100772static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100773sign_list_placed(buf_T *rbuf, char_u *sign_group)
774{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100775 buf_T *buf;
776 sign_entry_T *sign;
777 char lbuf[MSG_BUF_LEN];
778 char group[MSG_BUF_LEN];
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100779
Bram Moolenaar32526b32019-01-19 17:43:09 +0100780 msg_puts_title(_("\n--- Signs ---"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100781 msg_putchar('\n');
782 if (rbuf == NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100783 buf = firstbuf;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100784 else
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100785 buf = rbuf;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100786 while (buf != NULL && !got_int)
787 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100788 if (buf->b_signlist != NULL)
789 {
790 vim_snprintf(lbuf, MSG_BUF_LEN, _("Signs for %s:"), buf->b_fname);
791 msg_puts_attr(lbuf, HL_ATTR(HLF_D));
792 msg_putchar('\n');
793 }
794 FOR_ALL_SIGNS_IN_BUF(buf, sign)
795 {
796 if (got_int)
797 break;
798 if (!sign_in_group(sign, sign_group))
799 continue;
800 if (sign->se_group != NULL)
801 vim_snprintf(group, MSG_BUF_LEN, _(" group=%s"),
802 sign->se_group->sg_name);
803 else
804 group[0] = '\0';
805 vim_snprintf(lbuf, MSG_BUF_LEN,
806 _(" line=%ld id=%d%s name=%s priority=%d"),
807 (long)sign->se_lnum, sign->se_id, group,
808 sign_typenr2name(sign->se_typenr), sign->se_priority);
809 msg_puts(lbuf);
810 msg_putchar('\n');
811 }
812 if (rbuf != NULL)
813 break;
814 buf = buf->b_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100815 }
816}
817
818/*
819 * Adjust a placed sign for inserted/deleted lines.
820 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100821void
822sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100823{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100824 sign_entry_T *sign; // a sign in a b_signlist
825 linenr_T new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100826
827 FOR_ALL_SIGNS_IN_BUF(curbuf, sign)
828 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100829 // Ignore changes to lines after the sign
830 if (sign->se_lnum < line1)
831 continue;
832 new_lnum = sign->se_lnum;
833 if (sign->se_lnum <= line2)
834 {
835 if (amount != MAXLNUM)
836 new_lnum += amount;
837 }
838 else if (sign->se_lnum > line2)
839 // Lines inserted or deleted before the sign
840 new_lnum += amount_after;
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100841
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100842 // If the new sign line number is past the last line in the buffer,
843 // then don't adjust the line number. Otherwise, it will always be past
844 // the last line and will not be visible.
845 if (new_lnum <= curbuf->b_ml.ml_line_count)
846 sign->se_lnum = new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100847 }
848}
849
850/*
851 * Find index of a ":sign" subcmd from its name.
852 * "*end_cmd" must be writable.
853 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100854static int
855sign_cmd_idx(char_u *begin_cmd, // begin of sign subcmd
856 char_u *end_cmd) // just after sign subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100857{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100858 int idx;
859 char save = *end_cmd;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100860
861 *end_cmd = NUL;
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100862 for (idx = 0;; ++idx)
863 if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0)
864 break;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100865 *end_cmd = save;
866 return idx;
867}
868
869/*
870 * Find a sign by name. Also returns pointer to the previous sign.
871 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100872static sign_T *
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100873sign_find(char_u *name, sign_T **sp_prev)
874{
875 sign_T *sp;
876
877 if (sp_prev != NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100878 *sp_prev = NULL;
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200879 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100880 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100881 if (STRCMP(sp->sn_name, name) == 0)
882 break;
883 if (sp_prev != NULL)
884 *sp_prev = sp;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100885 }
886
887 return sp;
888}
889
890/*
Bram Moolenaar03142362019-01-18 22:01:42 +0100891 * Allocate a new sign
892 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100893static sign_T *
Bram Moolenaar03142362019-01-18 22:01:42 +0100894alloc_new_sign(char_u *name)
895{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100896 sign_T *sp;
897 sign_T *lp;
898 int start = next_sign_typenr;
Bram Moolenaar03142362019-01-18 22:01:42 +0100899
900 // Allocate a new sign.
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200901 sp = alloc_clear_id(sizeof(sign_T), aid_sign_define_by_name);
Bram Moolenaar03142362019-01-18 22:01:42 +0100902 if (sp == NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100903 return NULL;
Bram Moolenaar03142362019-01-18 22:01:42 +0100904
905 // Check that next_sign_typenr is not already being used.
906 // This only happens after wrapping around. Hopefully
907 // another one got deleted and we can use its number.
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100908 for (lp = first_sign; lp != NULL;)
Bram Moolenaar03142362019-01-18 22:01:42 +0100909 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100910 if (lp->sn_typenr == next_sign_typenr)
911 {
912 ++next_sign_typenr;
913 if (next_sign_typenr == MAX_TYPENR)
914 next_sign_typenr = 1;
915 if (next_sign_typenr == start)
916 {
917 vim_free(sp);
918 emsg(_(e_too_many_signs_defined));
919 return NULL;
920 }
921 lp = first_sign; // start all over
922 continue;
923 }
924 lp = lp->sn_next;
Bram Moolenaar03142362019-01-18 22:01:42 +0100925 }
926
927 sp->sn_typenr = next_sign_typenr;
928 if (++next_sign_typenr == MAX_TYPENR)
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100929 next_sign_typenr = 1; // wrap around
Bram Moolenaar03142362019-01-18 22:01:42 +0100930
931 sp->sn_name = vim_strsave(name);
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100932 if (sp->sn_name == NULL) // out of memory
Bram Moolenaar03142362019-01-18 22:01:42 +0100933 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100934 vim_free(sp);
935 return NULL;
Bram Moolenaar03142362019-01-18 22:01:42 +0100936 }
937
938 return sp;
939}
940
941/*
942 * Initialize the icon information for a new sign
943 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100944static void
Bram Moolenaar03142362019-01-18 22:01:42 +0100945sign_define_init_icon(sign_T *sp, char_u *icon)
946{
947 vim_free(sp->sn_icon);
948 sp->sn_icon = vim_strsave(icon);
949 backslash_halve(sp->sn_icon);
950# ifdef FEAT_SIGN_ICONS
951 if (gui.in_use)
952 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100953 out_flush();
954 if (sp->sn_image != NULL)
955 gui_mch_destroy_sign(sp->sn_image);
956 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
Bram Moolenaar03142362019-01-18 22:01:42 +0100957 }
958# endif
959}
960
961/*
962 * Initialize the text for a new sign
963 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100964static int
Bram Moolenaar03142362019-01-18 22:01:42 +0100965sign_define_init_text(sign_T *sp, char_u *text)
966{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100967 char_u *s;
968 char_u *endp;
969 int cells;
970 int len;
Bram Moolenaar03142362019-01-18 22:01:42 +0100971
972 endp = text + (int)STRLEN(text);
973
974 // Remove backslashes so that it is possible to use a space.
975 for (s = text; s + 1 < endp; ++s)
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100976 if (*s == '\\')
977 {
978 STRMOVE(s, s + 1);
979 --endp;
980 }
Bram Moolenaar03142362019-01-18 22:01:42 +0100981
982 // Count cells and check for non-printable chars
Bram Moolenaar03142362019-01-18 22:01:42 +0100983 if (has_mbyte)
984 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100985 cells = 0;
986 for (s = text; s < endp; s += (*mb_ptr2len)(s))
987 {
988 if (!vim_isprintc((*mb_ptr2char)(s)))
989 break;
990 cells += (*mb_ptr2cells)(s);
991 }
Bram Moolenaar03142362019-01-18 22:01:42 +0100992 }
993 else
Bram Moolenaar03142362019-01-18 22:01:42 +0100994 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100995 for (s = text; s < endp; ++s)
996 if (!vim_isprintc(*s))
997 break;
998 cells = (int)(s - text);
Bram Moolenaar03142362019-01-18 22:01:42 +0100999 }
1000
1001 // Currently sign text must be one or two display cells
1002 if (s != endp || cells < 1 || cells > 2)
1003 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001004 semsg(_(e_invalid_sign_text_str), text);
1005 return FAIL;
Bram Moolenaar03142362019-01-18 22:01:42 +01001006 }
1007
1008 vim_free(sp->sn_text);
1009 // Allocate one byte more if we need to pad up
1010 // with a space.
1011 len = (int)(endp - text + ((cells == 1) ? 1 : 0));
1012 sp->sn_text = vim_strnsave(text, len);
1013
1014 // For single character sign text, pad with a space.
1015 if (sp->sn_text != NULL && cells == 1)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001016 STRCPY(sp->sn_text + len - 1, " ");
Bram Moolenaar03142362019-01-18 22:01:42 +01001017
1018 return OK;
1019}
1020
1021/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001022 * Define a new sign or update an existing sign
1023 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001024int
1025sign_define_by_name(char_u *name,
1026 char_u *icon,
1027 char_u *linehl,
1028 char_u *text,
1029 char_u *texthl,
1030 char_u *culhl,
1031 char_u *numhl,
1032 int prio)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001033{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001034 sign_T *sp_prev;
1035 sign_T *sp;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001036
1037 sp = sign_find(name, &sp_prev);
1038 if (sp == NULL)
1039 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001040 sp = alloc_new_sign(name);
1041 if (sp == NULL)
1042 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001043
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001044 // add the new sign to the list of signs
1045 if (sp_prev == NULL)
1046 first_sign = sp;
1047 else
1048 sp_prev->sn_next = sp;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001049 }
Bram Moolenaarbf0acff2020-01-09 21:01:59 +01001050 else
1051 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001052 win_T *wp;
Bram Moolenaarbf0acff2020-01-09 21:01:59 +01001053
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001054 // Signs may already exist, a redraw is needed in windows with a
1055 // non-empty sign list.
1056 FOR_ALL_WINDOWS(wp)
1057 if (wp->w_buffer->b_signlist != NULL)
1058 redraw_buf_later(wp->w_buffer, UPD_NOT_VALID);
Bram Moolenaarbf0acff2020-01-09 21:01:59 +01001059 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001060
1061 // set values for a defined sign.
1062 if (icon != NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001063 sign_define_init_icon(sp, icon);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001064
Bram Moolenaar03142362019-01-18 22:01:42 +01001065 if (text != NULL && (sign_define_init_text(sp, text) == FAIL))
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001066 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001067
LemonBoyb975ddf2024-07-06 18:04:09 +02001068 sp->sn_priority = prio;
1069
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001070 if (linehl != NULL)
Bram Moolenaar0bac5fc2021-12-05 17:45:49 +00001071 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001072 if (*linehl == NUL)
1073 sp->sn_line_hl = 0;
1074 else
1075 sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl));
Bram Moolenaar0bac5fc2021-12-05 17:45:49 +00001076 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001077
1078 if (texthl != NULL)
Bram Moolenaar0bac5fc2021-12-05 17:45:49 +00001079 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001080 if (*texthl == NUL)
1081 sp->sn_text_hl = 0;
1082 else
1083 sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl));
Bram Moolenaar0bac5fc2021-12-05 17:45:49 +00001084 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001085
Bram Moolenaare413ea02021-11-24 16:20:13 +00001086 if (culhl != NULL)
Bram Moolenaar0bac5fc2021-12-05 17:45:49 +00001087 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001088 if (*culhl == NUL)
1089 sp->sn_cul_hl = 0;
1090 else
1091 sp->sn_cul_hl = syn_check_group(culhl, (int)STRLEN(culhl));
Bram Moolenaar0bac5fc2021-12-05 17:45:49 +00001092 }
Bram Moolenaare413ea02021-11-24 16:20:13 +00001093
James McCoya80aad72021-12-22 19:45:28 +00001094 if (numhl != NULL)
1095 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001096 if (*numhl == NUL)
1097 sp->sn_num_hl = 0;
1098 else
1099 sp->sn_num_hl = syn_check_group(numhl, (int)STRLEN(numhl));
James McCoya80aad72021-12-22 19:45:28 +00001100 }
1101
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001102 return OK;
1103}
1104
1105/*
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001106 * Return TRUE if sign "name" exists.
1107 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001108int
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001109sign_exists_by_name(char_u *name)
1110{
1111 return sign_find(name, NULL) != NULL;
1112}
1113
1114/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001115 * Free the sign specified by 'name'.
1116 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001117int
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001118sign_undefine_by_name(char_u *name, int give_error)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001119{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001120 sign_T *sp_prev;
1121 sign_T *sp;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001122
1123 sp = sign_find(name, &sp_prev);
1124 if (sp == NULL)
1125 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001126 if (give_error)
1127 semsg(_(e_unknown_sign_str), name);
1128 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001129 }
1130 sign_undefine(sp, sp_prev);
1131
1132 return OK;
1133}
1134
1135/*
1136 * List the signs matching 'name'
1137 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001138static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001139sign_list_by_name(char_u *name)
1140{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001141 sign_T *sp;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001142
1143 sp = sign_find(name, NULL);
1144 if (sp != NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001145 sign_list_defined(sp);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001146 else
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001147 semsg(_(e_unknown_sign_str), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001148}
1149
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001150static void
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001151may_force_numberwidth_recompute(buf_T *buf, int unplace)
1152{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001153 tabpage_T *tp;
1154 win_T *wp;
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001155
1156 FOR_ALL_TAB_WINDOWS(tp, wp)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001157 if (wp->w_buffer == buf && (wp->w_p_nu || wp->w_p_rnu) &&
1158 (unplace || wp->w_nrwidth_width < 2) &&
1159 (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u'))
1160 wp->w_nrwidth_line_count = 0;
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001161}
1162
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001163/*
Bram Moolenaar8144acb2019-01-14 23:08:18 +01001164 * Place a sign at the specified file location or update a sign.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001165 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001166int
1167sign_place(int *sign_id,
1168 char_u *sign_group,
1169 char_u *sign_name,
1170 buf_T *buf,
1171 linenr_T lnum,
1172 int prio)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001173{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001174 sign_T *sp;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001175
1176 // Check for reserved character '*' in group name
1177 if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0'))
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001178 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001179
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001180 FOR_ALL_SIGNS(sp)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001181 if (STRCMP(sp->sn_name, sign_name) == 0)
1182 break;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001183 if (sp == NULL)
1184 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001185 semsg(_(e_unknown_sign_str), sign_name);
1186 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001187 }
1188 if (*sign_id == 0)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001189 *sign_id = sign_group_get_next_signid(buf, sign_group);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001190
LemonBoyb975ddf2024-07-06 18:04:09 +02001191 // Use the default priority value for this sign.
1192 if (prio == -1)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001193 prio = (sp->sn_priority != -1) ? sp->sn_priority : SIGN_DEF_PRIO;
LemonBoyb975ddf2024-07-06 18:04:09 +02001194
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001195 if (lnum > 0)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001196 // ":sign place {id} line={lnum} name={name} file={fname}":
1197 // place a sign
1198 buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001199 else
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001200 // ":sign place {id} file={fname}": change sign type and/or priority
1201 lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr,
1202 prio);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001203 if (lnum > 0)
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001204 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001205 redraw_buf_line_later(buf, lnum);
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001206
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001207 // When displaying signs in the 'number' column, if the width of the
1208 // number column is less than 2, then force recomputing the width.
1209 may_force_numberwidth_recompute(buf, FALSE);
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001210 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001211 else
1212 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001213 semsg(_(e_not_possible_to_change_sign_str), sign_name);
1214 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001215 }
1216
1217 return OK;
1218}
1219
1220/*
1221 * Unplace the specified sign
1222 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001223static int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001224sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum)
1225{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001226 if (buf->b_signlist == NULL) // No signs in the buffer
1227 return OK;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001228
1229 if (sign_id == 0)
1230 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001231 // Delete all the signs in the specified buffer
1232 redraw_buf_later(buf, UPD_NOT_VALID);
1233 buf_delete_signs(buf, sign_group);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001234 }
1235 else
1236 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001237 linenr_T lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001238
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001239 // Delete only the specified signs
1240 lnum = buf_delsign(buf, atlnum, sign_id, sign_group);
1241 if (lnum == 0)
1242 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001243 }
1244
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001245 // When all the signs in a buffer are removed, force recomputing the
1246 // number column width (if enabled) in all the windows displaying the
1247 // buffer if 'signcolumn' is set to 'number' in that window.
1248 if (buf->b_signlist == NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001249 may_force_numberwidth_recompute(buf, TRUE);
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001250
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001251 return OK;
1252}
1253
1254/*
1255 * Unplace the sign at the current cursor line.
1256 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001257static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001258sign_unplace_at_cursor(char_u *groupname)
1259{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001260 int id = -1;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001261
1262 id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname);
1263 if (id > 0)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001264 sign_unplace(id, groupname, curwin->w_buffer, curwin->w_cursor.lnum);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001265 else
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001266 emsg(_(e_missing_sign_number));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001267}
1268
1269/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001270 * Jump to a sign.
1271 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001272static linenr_T
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001273sign_jump(int sign_id, char_u *sign_group, buf_T *buf)
1274{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001275 linenr_T lnum;
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001276
1277 if ((lnum = buf_findsign(buf, sign_id, sign_group)) <= 0)
1278 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001279 semsg(_(e_invalid_sign_id_nr), sign_id);
1280 return -1;
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001281 }
1282
1283 // goto a sign ...
1284 if (buf_jump_open_win(buf) != NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001285 { // ... in a current window
1286 curwin->w_cursor.lnum = lnum;
1287 check_cursor_lnum();
1288 beginline(BL_WHITE);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001289 }
1290 else
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001291 { // ... not currently in a window
1292 char_u *cmd;
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001293
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001294 if (buf->b_fname == NULL)
1295 {
1296 emsg(_(e_cannot_jump_to_buffer_that_does_not_have_name));
1297 return -1;
1298 }
1299 cmd = alloc(STRLEN(buf->b_fname) + 25);
1300 if (cmd == NULL)
1301 return -1;
1302 sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname);
1303 do_cmdline_cmd(cmd);
1304 vim_free(cmd);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001305 }
1306# ifdef FEAT_FOLDING
1307 foldOpenCursor();
1308# endif
1309
1310 return lnum;
1311}
1312
1313/*
1314 * ":sign define {name} ..." command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001315 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001316static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001317sign_define_cmd(char_u *sign_name, char_u *cmdline)
1318{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001319 char_u *arg;
1320 char_u *p = cmdline;
1321 char_u *icon = NULL;
1322 char_u *text = NULL;
1323 char_u *linehl = NULL;
1324 char_u *texthl = NULL;
1325 char_u *culhl = NULL;
1326 char_u *numhl = NULL;
1327 int prio = -1;
1328 int failed = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001329
1330 // set values for a defined sign.
1331 for (;;)
1332 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001333 arg = skipwhite(p);
1334 if (*arg == NUL)
1335 break;
1336 p = skiptowhite_esc(arg);
1337 if (STRNCMP(arg, "icon=", 5) == 0)
1338 {
1339 arg += 5;
1340 icon = vim_strnsave(arg, p - arg);
1341 }
1342 else if (STRNCMP(arg, "text=", 5) == 0)
1343 {
1344 arg += 5;
1345 text = vim_strnsave(arg, p - arg);
1346 }
1347 else if (STRNCMP(arg, "linehl=", 7) == 0)
1348 {
1349 arg += 7;
1350 linehl = vim_strnsave(arg, p - arg);
1351 }
1352 else if (STRNCMP(arg, "texthl=", 7) == 0)
1353 {
1354 arg += 7;
1355 texthl = vim_strnsave(arg, p - arg);
1356 }
1357 else if (STRNCMP(arg, "culhl=", 6) == 0)
1358 {
1359 arg += 6;
1360 culhl = vim_strnsave(arg, p - arg);
1361 }
1362 else if (STRNCMP(arg, "numhl=", 6) == 0)
1363 {
1364 arg += 6;
1365 numhl = vim_strnsave(arg, p - arg);
1366 }
1367 else if (STRNCMP(arg, "priority=", 9) == 0)
1368 {
1369 arg += 9;
1370 prio = atoi((char *)arg);
1371 }
1372 else
1373 {
1374 semsg(_(e_invalid_argument_str), arg);
1375 failed = TRUE;
1376 break;
1377 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001378 }
1379
1380 if (!failed)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001381 sign_define_by_name(sign_name, icon, linehl, text, texthl, culhl, numhl,
1382 prio);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001383
1384 vim_free(icon);
1385 vim_free(text);
1386 vim_free(linehl);
1387 vim_free(texthl);
Bram Moolenaare413ea02021-11-24 16:20:13 +00001388 vim_free(culhl);
James McCoya80aad72021-12-22 19:45:28 +00001389 vim_free(numhl);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001390}
1391
1392/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001393 * ":sign place" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001394 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001395static void
1396sign_place_cmd(buf_T *buf,
1397 linenr_T lnum,
1398 char_u *sign_name,
1399 int id,
1400 char_u *group,
1401 int prio)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001402{
1403 if (id <= 0)
1404 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001405 // List signs placed in a file/buffer
1406 // :sign place file={fname}
1407 // :sign place group={group} file={fname}
1408 // :sign place group=* file={fname}
1409 // :sign place buffer={nr}
1410 // :sign place group={group} buffer={nr}
1411 // :sign place group=* buffer={nr}
1412 // :sign place
1413 // :sign place group={group}
1414 // :sign place group=*
1415 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0'))
1416 emsg(_(e_invalid_argument));
1417 else
1418 sign_list_placed(buf, group);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001419 }
1420 else
1421 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001422 // Place a new sign
1423 if (sign_name == NULL || buf == NULL ||
1424 (group != NULL && *group == '\0'))
1425 {
1426 emsg(_(e_invalid_argument));
1427 return;
1428 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001429
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001430 sign_place(&id, group, sign_name, buf, lnum, prio);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001431 }
1432}
1433
1434/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001435 * ":sign unplace" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001436 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001437static void
1438sign_unplace_cmd(buf_T *buf,
1439 linenr_T lnum,
1440 char_u *sign_name,
1441 int id,
1442 char_u *group)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001443{
1444 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0'))
1445 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001446 emsg(_(e_invalid_argument));
1447 return;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001448 }
1449
1450 if (id == -2)
1451 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001452 if (buf != NULL)
1453 // :sign unplace * file={fname}
1454 // :sign unplace * group={group} file={fname}
1455 // :sign unplace * group=* file={fname}
1456 // :sign unplace * buffer={nr}
1457 // :sign unplace * group={group} buffer={nr}
1458 // :sign unplace * group=* buffer={nr}
1459 sign_unplace(0, group, buf, 0);
1460 else
1461 // :sign unplace *
1462 // :sign unplace * group={group}
1463 // :sign unplace * group=*
1464 FOR_ALL_BUFFERS(buf)
1465 if (buf->b_signlist != NULL)
1466 buf_delete_signs(buf, group);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001467 }
1468 else
1469 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001470 if (buf != NULL)
1471 // :sign unplace {id} file={fname}
1472 // :sign unplace {id} group={group} file={fname}
1473 // :sign unplace {id} group=* file={fname}
1474 // :sign unplace {id} buffer={nr}
1475 // :sign unplace {id} group={group} buffer={nr}
1476 // :sign unplace {id} group=* buffer={nr}
1477 sign_unplace(id, group, buf, 0);
1478 else
1479 {
1480 if (id == -1)
1481 {
1482 // :sign unplace group={group}
1483 // :sign unplace group=*
1484 sign_unplace_at_cursor(group);
1485 }
1486 else
1487 {
1488 // :sign unplace {id}
1489 // :sign unplace {id} group={group}
1490 // :sign unplace {id} group=*
1491 FOR_ALL_BUFFERS(buf)
1492 sign_unplace(id, group, buf, 0);
1493 }
1494 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001495 }
1496}
1497
1498/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001499 * Jump to a placed sign commands:
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001500 * :sign jump {id} file={fname}
1501 * :sign jump {id} buffer={nr}
1502 * :sign jump {id} group={group} file={fname}
1503 * :sign jump {id} group={group} buffer={nr}
1504 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001505static void
1506sign_jump_cmd(buf_T *buf,
1507 linenr_T lnum,
1508 char_u *sign_name,
1509 int id,
1510 char_u *group)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001511{
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001512 if (sign_name == NULL && group == NULL && id == -1)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001513 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001514 emsg(_(e_argument_required));
1515 return;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001516 }
1517
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001518 if (buf == NULL || (group != NULL && *group == '\0') || lnum >= 0 ||
1519 sign_name != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001520 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001521 // File or buffer is not specified or an empty group is used
1522 // or a line number or a sign name is specified.
1523 emsg(_(e_invalid_argument));
1524 return;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001525 }
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001526 (void)sign_jump(id, group, buf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001527}
1528
1529/*
1530 * Parse the command line arguments for the ":sign place", ":sign unplace" and
1531 * ":sign jump" commands.
1532 * The supported arguments are: line={lnum} name={name} group={group}
1533 * priority={prio} and file={fname} or buffer={nr}.
1534 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001535static int
1536parse_sign_cmd_args(int cmd,
1537 char_u *arg,
1538 char_u **sign_name,
1539 int *signid,
1540 char_u **group,
1541 int *prio,
1542 buf_T **buf,
1543 linenr_T *lnum)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001544{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001545 char_u *arg1;
1546 char_u *name;
1547 char_u *filename = NULL;
1548 int lnum_arg = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001549
1550 // first arg could be placed sign id
1551 arg1 = arg;
1552 if (VIM_ISDIGIT(*arg))
1553 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001554 *signid = getdigits(&arg);
1555 if (!VIM_ISWHITE(*arg) && *arg != NUL)
1556 {
1557 *signid = -1;
1558 arg = arg1;
1559 }
1560 else
1561 arg = skipwhite(arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001562 }
1563
1564 while (*arg != NUL)
1565 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001566 if (STRNCMP(arg, "line=", 5) == 0)
1567 {
1568 arg += 5;
1569 *lnum = atoi((char *)arg);
1570 arg = skiptowhite(arg);
1571 lnum_arg = TRUE;
1572 }
1573 else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE)
1574 {
1575 if (*signid != -1)
1576 {
1577 emsg(_(e_invalid_argument));
1578 return FAIL;
1579 }
1580 *signid = -2;
1581 arg = skiptowhite(arg + 1);
1582 }
1583 else if (STRNCMP(arg, "name=", 5) == 0)
1584 {
1585 arg += 5;
1586 name = arg;
1587 arg = skiptowhite(arg);
1588 if (*arg != NUL)
1589 *arg++ = NUL;
1590 while (name[0] == '0' && name[1] != NUL)
1591 ++name;
1592 *sign_name = name;
1593 }
1594 else if (STRNCMP(arg, "group=", 6) == 0)
1595 {
1596 arg += 6;
1597 *group = arg;
1598 arg = skiptowhite(arg);
1599 if (*arg != NUL)
1600 *arg++ = NUL;
1601 }
1602 else if (STRNCMP(arg, "priority=", 9) == 0)
1603 {
1604 arg += 9;
1605 *prio = atoi((char *)arg);
1606 arg = skiptowhite(arg);
1607 }
1608 else if (STRNCMP(arg, "file=", 5) == 0)
1609 {
1610 arg += 5;
1611 filename = arg;
1612 *buf = buflist_findname_exp(arg);
1613 break;
1614 }
1615 else if (STRNCMP(arg, "buffer=", 7) == 0)
1616 {
1617 arg += 7;
1618 filename = arg;
1619 *buf = buflist_findnr((int)getdigits(&arg));
1620 if (*skipwhite(arg) != NUL)
1621 semsg(_(e_trailing_characters_str), arg);
1622 break;
1623 }
1624 else
1625 {
1626 emsg(_(e_invalid_argument));
1627 return FAIL;
1628 }
1629 arg = skipwhite(arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001630 }
1631
1632 if (filename != NULL && *buf == NULL)
1633 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001634 semsg(_(e_invalid_buffer_name_str), filename);
1635 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001636 }
1637
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001638 // If the filename is not supplied for the sign place or the sign jump
1639 // command, then use the current buffer.
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001640 if (filename == NULL &&
1641 ((cmd == SIGNCMD_PLACE && lnum_arg) || cmd == SIGNCMD_JUMP))
1642 *buf = curwin->w_buffer;
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001643
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001644 return OK;
1645}
1646
1647/*
1648 * ":sign" command
1649 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001650void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001651ex_sign(exarg_T *eap)
1652{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001653 char_u *arg = eap->arg;
1654 char_u *p;
1655 int idx;
1656 sign_T *sp;
1657 buf_T *buf = NULL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001658
1659 // Parse the subcommand.
1660 p = skiptowhite(arg);
1661 idx = sign_cmd_idx(arg, p);
1662 if (idx == SIGNCMD_LAST)
1663 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001664 semsg(_(e_unknown_sign_command_str), arg);
1665 return;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001666 }
1667 arg = skipwhite(p);
1668
1669 if (idx <= SIGNCMD_LIST)
1670 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001671 // Define, undefine or list signs.
1672 if (idx == SIGNCMD_LIST && *arg == NUL)
1673 {
1674 // ":sign list": list all defined signs
1675 for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next)
1676 sign_list_defined(sp);
1677 }
1678 else if (*arg == NUL)
1679 emsg(_(e_missing_sign_name));
1680 else
1681 {
1682 char_u *name;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001683
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001684 // Isolate the sign name. If it's a number skip leading zeroes,
1685 // so that "099" and "99" are the same sign. But keep "0".
1686 p = skiptowhite(arg);
1687 if (*p != NUL)
1688 *p++ = NUL;
1689 while (arg[0] == '0' && arg[1] != NUL)
1690 ++arg;
1691 name = vim_strsave(arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001692
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001693 if (idx == SIGNCMD_DEFINE)
1694 sign_define_cmd(name, p);
1695 else if (idx == SIGNCMD_LIST)
1696 // ":sign list {name}"
1697 sign_list_by_name(name);
1698 else
1699 // ":sign undefine {name}"
1700 sign_undefine_by_name(name, TRUE);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001701
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001702 vim_free(name);
1703 return;
1704 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001705 }
1706 else
1707 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001708 int id = -1;
1709 linenr_T lnum = -1;
1710 char_u *sign_name = NULL;
1711 char_u *group = NULL;
1712 int prio = -1;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001713
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001714 // Parse command line arguments
1715 if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio, &buf,
1716 &lnum) == FAIL)
1717 return;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001718
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001719 if (idx == SIGNCMD_PLACE)
1720 sign_place_cmd(buf, lnum, sign_name, id, group, prio);
1721 else if (idx == SIGNCMD_UNPLACE)
1722 sign_unplace_cmd(buf, lnum, sign_name, id, group);
1723 else if (idx == SIGNCMD_JUMP)
1724 sign_jump_cmd(buf, lnum, sign_name, id, group);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001725 }
1726}
1727
1728/*
1729 * Return information about a specified sign
1730 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001731static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001732sign_getinfo(sign_T *sp, dict_T *retdict)
1733{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001734 char_u *p;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001735
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00001736 dict_add_string(retdict, "name", sp->sn_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001737 if (sp->sn_icon != NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001738 dict_add_string(retdict, "icon", sp->sn_icon);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001739 if (sp->sn_text != NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001740 dict_add_string(retdict, "text", sp->sn_text);
LemonBoyb975ddf2024-07-06 18:04:09 +02001741 if (sp->sn_priority > 0)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001742 dict_add_number(retdict, "priority", sp->sn_priority);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001743 if (sp->sn_line_hl > 0)
1744 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001745 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1746 if (p == NULL)
1747 p = (char_u *)"NONE";
1748 dict_add_string(retdict, "linehl", p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001749 }
1750 if (sp->sn_text_hl > 0)
1751 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001752 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1753 if (p == NULL)
1754 p = (char_u *)"NONE";
1755 dict_add_string(retdict, "texthl", p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001756 }
Bram Moolenaare413ea02021-11-24 16:20:13 +00001757 if (sp->sn_cul_hl > 0)
1758 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001759 p = get_highlight_name_ext(NULL, sp->sn_cul_hl - 1, FALSE);
1760 if (p == NULL)
1761 p = (char_u *)"NONE";
1762 dict_add_string(retdict, "culhl", p);
Bram Moolenaare413ea02021-11-24 16:20:13 +00001763 }
James McCoya80aad72021-12-22 19:45:28 +00001764 if (sp->sn_num_hl > 0)
1765 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001766 p = get_highlight_name_ext(NULL, sp->sn_num_hl - 1, FALSE);
1767 if (p == NULL)
1768 p = (char_u *)"NONE";
1769 dict_add_string(retdict, "numhl", p);
James McCoya80aad72021-12-22 19:45:28 +00001770 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001771}
1772
1773/*
1774 * If 'name' is NULL, return a list of all the defined signs.
1775 * Otherwise, return information about the specified sign.
1776 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001777static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001778sign_getlist(char_u *name, list_T *retlist)
1779{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001780 sign_T *sp = first_sign;
1781 dict_T *dict;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001782
1783 if (name != NULL)
1784 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001785 sp = sign_find(name, NULL);
1786 if (sp == NULL)
1787 return;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001788 }
1789
1790 for (; sp != NULL && !got_int; sp = sp->sn_next)
1791 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001792 if ((dict = dict_alloc_id(aid_sign_getlist)) == NULL)
1793 return;
1794 if (list_append_dict(retlist, dict) == FAIL)
1795 return;
1796 sign_getinfo(sp, dict);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001797
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001798 if (name != NULL) // handle only the specified sign
1799 break;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001800 }
1801}
1802
1803/*
1804 * Returns information about signs placed in a buffer as list of dicts.
1805 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001806void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001807get_buffer_signs(buf_T *buf, list_T *l)
1808{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001809 sign_entry_T *sign;
1810 dict_T *d;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001811
1812 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1813 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001814 if ((d = sign_get_info(sign)) != NULL)
1815 list_append_dict(l, d);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001816 }
1817}
1818
1819/*
1820 * Return information about all the signs placed in a buffer
1821 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001822static void
1823sign_get_placed_in_buf(buf_T *buf,
1824 linenr_T lnum,
1825 int sign_id,
1826 char_u *sign_group,
1827 list_T *retlist)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001828{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001829 dict_T *d;
1830 list_T *l;
1831 sign_entry_T *sign;
1832 dict_T *sdict;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001833
1834 if ((d = dict_alloc_id(aid_sign_getplaced_dict)) == NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001835 return;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001836 list_append_dict(retlist, d);
1837
1838 dict_add_number(d, "bufnr", (long)buf->b_fnum);
1839
1840 if ((l = list_alloc_id(aid_sign_getplaced_list)) == NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001841 return;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001842 dict_add_list(d, "signs", l);
1843
1844 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1845 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001846 if (!sign_in_group(sign, sign_group))
1847 continue;
1848 if ((lnum == 0 && sign_id == 0) ||
1849 (sign_id == 0 && lnum == sign->se_lnum) ||
1850 (lnum == 0 && sign_id == sign->se_id) ||
1851 (lnum == sign->se_lnum && sign_id == sign->se_id))
1852 {
1853 if ((sdict = sign_get_info(sign)) != NULL)
1854 list_append_dict(l, sdict);
1855 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001856 }
1857}
1858
1859/*
1860 * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the
1861 * sign placed at the line number. If 'lnum' is zero, return all the signs
1862 * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers.
1863 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001864static void
1865sign_get_placed(buf_T *buf,
1866 linenr_T lnum,
1867 int sign_id,
1868 char_u *sign_group,
1869 list_T *retlist)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001870{
1871 if (buf != NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001872 sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001873 else
1874 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001875 FOR_ALL_BUFFERS(buf)
1876 if (buf->b_signlist != NULL)
1877 sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001878 }
1879}
1880
1881# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1882/*
1883 * Allocate the icons. Called when the GUI has started. Allows defining
1884 * signs before it starts.
1885 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001886void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001887sign_gui_started(void)
1888{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001889 sign_T *sp;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001890
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001891 FOR_ALL_SIGNS(sp)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001892 if (sp->sn_icon != NULL)
1893 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001894}
1895# endif
1896
1897/*
1898 * List one sign.
1899 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001900static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001901sign_list_defined(sign_T *sp)
1902{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001903 char_u *p;
1904 char lbuf[MSG_BUF_LEN];
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001905
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001906 smsg("sign %s", sp->sn_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001907 if (sp->sn_icon != NULL)
1908 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001909 msg_puts(" icon=");
1910 msg_outtrans(sp->sn_icon);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001911# ifdef FEAT_SIGN_ICONS
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001912 if (sp->sn_image == NULL)
1913 msg_puts(_(" (NOT FOUND)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001914# else
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001915 msg_puts(_(" (not supported)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001916# endif
1917 }
1918 if (sp->sn_text != NULL)
1919 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001920 msg_puts(" text=");
1921 msg_outtrans(sp->sn_text);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001922 }
LemonBoyb975ddf2024-07-06 18:04:09 +02001923 if (sp->sn_priority > 0)
1924 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001925 vim_snprintf(lbuf, MSG_BUF_LEN, " priority=%d", sp->sn_priority);
1926 msg_puts(lbuf);
LemonBoyb975ddf2024-07-06 18:04:09 +02001927 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001928 if (sp->sn_line_hl > 0)
1929 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001930 msg_puts(" linehl=");
1931 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1932 if (p == NULL)
1933 msg_puts("NONE");
1934 else
1935 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001936 }
1937 if (sp->sn_text_hl > 0)
1938 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001939 msg_puts(" texthl=");
1940 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1941 if (p == NULL)
1942 msg_puts("NONE");
1943 else
1944 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001945 }
Bram Moolenaare413ea02021-11-24 16:20:13 +00001946 if (sp->sn_cul_hl > 0)
1947 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001948 msg_puts(" culhl=");
1949 p = get_highlight_name_ext(NULL, sp->sn_cul_hl - 1, FALSE);
1950 if (p == NULL)
1951 msg_puts("NONE");
1952 else
1953 msg_puts((char *)p);
Bram Moolenaare413ea02021-11-24 16:20:13 +00001954 }
James McCoya80aad72021-12-22 19:45:28 +00001955 if (sp->sn_num_hl > 0)
1956 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001957 msg_puts(" numhl=");
1958 p = get_highlight_name_ext(NULL, sp->sn_num_hl - 1, FALSE);
1959 if (p == NULL)
1960 msg_puts("NONE");
1961 else
1962 msg_puts((char *)p);
James McCoya80aad72021-12-22 19:45:28 +00001963 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001964}
1965
1966/*
1967 * Undefine a sign and free its memory.
1968 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001969static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001970sign_undefine(sign_T *sp, sign_T *sp_prev)
1971{
1972 vim_free(sp->sn_name);
1973 vim_free(sp->sn_icon);
1974# ifdef FEAT_SIGN_ICONS
1975 if (sp->sn_image != NULL)
1976 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001977 out_flush();
1978 gui_mch_destroy_sign(sp->sn_image);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001979 }
1980# endif
1981 vim_free(sp->sn_text);
1982 if (sp_prev == NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001983 first_sign = sp->sn_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001984 else
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001985 sp_prev->sn_next = sp->sn_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001986 vim_free(sp);
1987}
1988
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001989# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001990void *
1991sign_get_image(int typenr) // the attribute which may have a sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001992{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001993 sign_T *sp;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001994
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001995 FOR_ALL_SIGNS(sp)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001996 if (sp->sn_typenr == typenr)
1997 return sp->sn_image;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001998 return NULL;
1999}
2000# endif
2001
2002/*
2003 * Undefine/free all signs.
2004 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002005void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002006free_signs(void)
2007{
2008 while (first_sign != NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002009 sign_undefine(first_sign, NULL);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002010}
2011
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002012static enum
2013{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002014 EXP_SUBCMD, // expand :sign sub-commands
2015 EXP_DEFINE, // expand :sign define {name} args
2016 EXP_PLACE, // expand :sign place {id} args
2017 EXP_LIST, // expand :sign place args
2018 EXP_UNPLACE, // expand :sign unplace"
2019 EXP_SIGN_NAMES, // expand with name of placed signs
2020 EXP_SIGN_GROUPS // expand with name of placed sign groups
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002021} expand_what;
2022
2023/*
Bram Moolenaar3678f652019-02-17 14:50:25 +01002024 * Return the n'th sign name (used for command line completion)
2025 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002026static char_u *
Bram Moolenaar3678f652019-02-17 14:50:25 +01002027get_nth_sign_name(int idx)
2028{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002029 int current_idx;
2030 sign_T *sp;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002031
2032 // Complete with name of signs already defined
2033 current_idx = 0;
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002034 FOR_ALL_SIGNS(sp)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002035 if (current_idx++ == idx)
2036 return sp->sn_name;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002037 return NULL;
2038}
2039
2040/*
2041 * Return the n'th sign group name (used for command line completion)
2042 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002043static char_u *
Bram Moolenaar3678f652019-02-17 14:50:25 +01002044get_nth_sign_group_name(int idx)
2045{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002046 int current_idx;
2047 int todo;
2048 hashitem_T *hi;
2049 signgroup_T *group;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002050
2051 // Complete with name of sign groups already defined
2052 current_idx = 0;
2053 todo = (int)sg_table.ht_used;
Yegappan Lakshmanan14113fd2023-03-07 17:13:51 +00002054 FOR_ALL_HASHTAB_ITEMS(&sg_table, hi, todo)
Bram Moolenaar3678f652019-02-17 14:50:25 +01002055 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002056 if (!HASHITEM_EMPTY(hi))
2057 {
2058 --todo;
2059 if (current_idx++ == idx)
2060 {
2061 group = HI2SG(hi);
2062 return group->sg_name;
2063 }
2064 }
Bram Moolenaar3678f652019-02-17 14:50:25 +01002065 }
2066 return NULL;
2067}
2068
2069/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002070 * Function given to ExpandGeneric() to obtain the sign command
2071 * expansion.
2072 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002073char_u *
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002074get_sign_name(expand_T *xp UNUSED, int idx)
2075{
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002076 switch (expand_what)
2077 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002078 case EXP_SUBCMD:
2079 return (char_u *)cmds[idx];
2080 case EXP_DEFINE:
2081 {
2082 char *define_arg[] = { "culhl=", "icon=", "linehl=", "numhl=",
2083 "text=", "texthl=", "priority=", NULL };
2084 return (char_u *)define_arg[idx];
2085 }
2086 case EXP_PLACE:
2087 {
2088 char *place_arg[] = { "line=", "name=", "group=", "priority=",
2089 "file=", "buffer=", NULL };
2090 return (char_u *)place_arg[idx];
2091 }
2092 case EXP_LIST:
2093 {
2094 char *list_arg[] = { "group=", "file=", "buffer=", NULL };
2095 return (char_u *)list_arg[idx];
2096 }
2097 case EXP_UNPLACE:
2098 {
2099 char *unplace_arg[] = { "group=", "file=", "buffer=", NULL };
2100 return (char_u *)unplace_arg[idx];
2101 }
2102 case EXP_SIGN_NAMES:
2103 return get_nth_sign_name(idx);
2104 case EXP_SIGN_GROUPS:
2105 return get_nth_sign_group_name(idx);
2106 default:
2107 return NULL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002108 }
2109}
2110
2111/*
2112 * Handle command line completion for :sign command.
2113 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002114void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002115set_context_in_sign_cmd(expand_T *xp, char_u *arg)
2116{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002117 char_u *p;
2118 char_u *end_subcmd;
2119 char_u *last;
2120 int cmd_idx;
2121 char_u *begin_subcmd_args;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002122
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002123 // Default: expand subcommands.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002124 xp->xp_context = EXPAND_SIGN;
2125 expand_what = EXP_SUBCMD;
2126 xp->xp_pattern = arg;
2127
2128 end_subcmd = skiptowhite(arg);
2129 if (*end_subcmd == NUL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002130 // expand subcmd name
2131 // :sign {subcmd}<CTRL-D>
2132 return;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002133
2134 cmd_idx = sign_cmd_idx(arg, end_subcmd);
2135
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002136 // :sign {subcmd} {subcmd_args}
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002137 // |
2138 // begin_subcmd_args
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002139 begin_subcmd_args = skipwhite(end_subcmd);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002140
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002141 // expand last argument of subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002142
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002143 // :sign define {name} {args}...
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002144 // |
2145 // p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002146
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002147 // Loop until reaching last argument.
Bram Moolenaar3678f652019-02-17 14:50:25 +01002148 p = begin_subcmd_args;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002149 do
2150 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002151 p = skipwhite(p);
2152 last = p;
2153 p = skiptowhite(p);
2154 }
2155 while (*p != NUL);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002156
2157 p = vim_strchr(last, '=');
2158
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002159 // :sign define {name} {args}... {last}=
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002160 // | |
2161 // last p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002162 if (p == NULL)
2163 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002164 // Expand last argument name (before equal sign).
2165 xp->xp_pattern = last;
2166 switch (cmd_idx)
2167 {
2168 case SIGNCMD_DEFINE:
2169 expand_what = EXP_DEFINE;
2170 break;
2171 case SIGNCMD_PLACE:
2172 // List placed signs
2173 if (VIM_ISDIGIT(*begin_subcmd_args))
2174 // :sign place {id} {args}...
2175 expand_what = EXP_PLACE;
2176 else
2177 // :sign place {args}...
2178 expand_what = EXP_LIST;
2179 break;
2180 case SIGNCMD_LIST:
2181 case SIGNCMD_UNDEFINE:
2182 // :sign list <CTRL-D>
2183 // :sign undefine <CTRL-D>
2184 expand_what = EXP_SIGN_NAMES;
2185 break;
2186 case SIGNCMD_JUMP:
2187 case SIGNCMD_UNPLACE:
2188 expand_what = EXP_UNPLACE;
2189 break;
2190 default:
2191 xp->xp_context = EXPAND_NOTHING;
2192 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002193 }
2194 else
2195 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002196 // Expand last argument value (after equal sign).
2197 xp->xp_pattern = p + 1;
2198 switch (cmd_idx)
2199 {
2200 case SIGNCMD_DEFINE:
2201 if (STRNCMP(last, "texthl", 6) == 0 ||
2202 STRNCMP(last, "linehl", 6) == 0 ||
2203 STRNCMP(last, "culhl", 5) == 0 ||
2204 STRNCMP(last, "numhl", 5) == 0)
2205 xp->xp_context = EXPAND_HIGHLIGHT;
2206 else if (STRNCMP(last, "icon", 4) == 0)
2207 xp->xp_context = EXPAND_FILES;
2208 else
2209 xp->xp_context = EXPAND_NOTHING;
2210 break;
2211 case SIGNCMD_PLACE:
2212 if (STRNCMP(last, "name", 4) == 0)
2213 expand_what = EXP_SIGN_NAMES;
2214 else if (STRNCMP(last, "group", 5) == 0)
2215 expand_what = EXP_SIGN_GROUPS;
2216 else if (STRNCMP(last, "file", 4) == 0)
2217 xp->xp_context = EXPAND_BUFFERS;
2218 else
2219 xp->xp_context = EXPAND_NOTHING;
2220 break;
2221 case SIGNCMD_UNPLACE:
2222 case SIGNCMD_JUMP:
2223 if (STRNCMP(last, "group", 5) == 0)
2224 expand_what = EXP_SIGN_GROUPS;
2225 else if (STRNCMP(last, "file", 4) == 0)
2226 xp->xp_context = EXPAND_BUFFERS;
2227 else
2228 xp->xp_context = EXPAND_NOTHING;
2229 break;
2230 default:
2231 xp->xp_context = EXPAND_NOTHING;
2232 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002233 }
2234}
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002235
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002236/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002237 * Define a sign using the attributes in 'dict'. Returns 0 on success and -1 on
2238 * failure.
2239 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002240static int
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002241sign_define_from_dict(char_u *name_arg, dict_T *dict)
2242{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002243 char_u *name = NULL;
2244 char_u *icon = NULL;
2245 char_u *linehl = NULL;
2246 char_u *text = NULL;
2247 char_u *texthl = NULL;
2248 char_u *culhl = NULL;
2249 char_u *numhl = NULL;
2250 int prio = -1;
2251 int retval = -1;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002252
2253 if (name_arg == NULL)
2254 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002255 if (dict == NULL)
2256 return -1;
2257 name = dict_get_string(dict, "name", TRUE);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002258 }
2259 else
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002260 name = vim_strsave(name_arg);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002261 if (name == NULL || name[0] == NUL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002262 goto cleanup;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002263 if (dict != NULL)
2264 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002265 icon = dict_get_string(dict, "icon", TRUE);
2266 linehl = dict_get_string(dict, "linehl", TRUE);
2267 text = dict_get_string(dict, "text", TRUE);
2268 texthl = dict_get_string(dict, "texthl", TRUE);
2269 culhl = dict_get_string(dict, "culhl", TRUE);
2270 numhl = dict_get_string(dict, "numhl", TRUE);
2271 prio = dict_get_number_def(dict, "priority", -1);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002272 }
2273
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002274 if (sign_define_by_name(name, icon, linehl, text, texthl, culhl, numhl,
2275 prio) == OK)
2276 retval = 0;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002277
2278cleanup:
2279 vim_free(name);
2280 vim_free(icon);
2281 vim_free(linehl);
2282 vim_free(text);
2283 vim_free(texthl);
Bram Moolenaare413ea02021-11-24 16:20:13 +00002284 vim_free(culhl);
James McCoya80aad72021-12-22 19:45:28 +00002285 vim_free(numhl);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002286
2287 return retval;
2288}
2289
2290/*
2291 * Define multiple signs using attributes from list 'l' and store the return
2292 * values in 'retlist'.
2293 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002294static void
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002295sign_define_multiple(list_T *l, list_T *retlist)
2296{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002297 listitem_T *li;
2298 int retval;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002299
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002300 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002301 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002302 retval = -1;
2303 if (li->li_tv.v_type == VAR_DICT)
2304 retval = sign_define_from_dict(NULL, li->li_tv.vval.v_dict);
2305 else
2306 emsg(_(e_dictionary_required));
2307 list_append_number(retlist, retval);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002308 }
2309}
2310
2311/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002312 * "sign_define()" function
2313 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002314void
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002315f_sign_define(typval_T *argvars, typval_T *rettv)
2316{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002317 char_u *name;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002318
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002319 if (in_vim9script() && (check_for_string_or_list_arg(argvars, 0) == FAIL ||
2320 check_for_opt_dict_arg(argvars, 1) == FAIL))
2321 return;
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002322
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002323 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2324 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002325 // Define multiple signs
2326 if (rettv_list_alloc(rettv) == FAIL)
2327 return;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002328
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002329 sign_define_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2330 return;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002331 }
2332
2333 // Define a single sign
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002334 rettv->vval.v_number = -1;
2335
2336 name = tv_get_string_chk(&argvars[0]);
2337 if (name == NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002338 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002339
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +01002340 if (check_for_opt_dict_arg(argvars, 1) == FAIL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002341 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002342
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002343 rettv->vval.v_number = sign_define_from_dict(
2344 name, argvars[1].v_type == VAR_DICT ? argvars[1].vval.v_dict : NULL);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002345}
2346
2347/*
2348 * "sign_getdefined()" function
2349 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002350void
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002351f_sign_getdefined(typval_T *argvars, typval_T *rettv)
2352{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002353 char_u *name = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002354
Bram Moolenaar93a10962022-06-16 11:42:09 +01002355 if (rettv_list_alloc_id(rettv, aid_sign_getdefined) == FAIL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002356 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002357
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002358 if (in_vim9script() && check_for_opt_string_arg(argvars, 0) == FAIL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002359 return;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002360
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002361 if (argvars[0].v_type != VAR_UNKNOWN)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002362 name = tv_get_string(&argvars[0]);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002363
2364 sign_getlist(name, rettv->vval.v_list);
2365}
2366
2367/*
2368 * "sign_getplaced()" function
2369 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002370void
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002371f_sign_getplaced(typval_T *argvars, typval_T *rettv)
2372{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002373 buf_T *buf = NULL;
2374 dict_T *dict;
2375 dictitem_T *di;
2376 linenr_T lnum = 0;
2377 int sign_id = 0;
2378 char_u *group = NULL;
2379 int notanum = FALSE;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002380
Bram Moolenaar93a10962022-06-16 11:42:09 +01002381 if (rettv_list_alloc_id(rettv, aid_sign_getplaced) == FAIL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002382 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002383
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002384 if (in_vim9script() && (check_for_opt_buffer_arg(argvars, 0) == FAIL ||
2385 (argvars[0].v_type != VAR_UNKNOWN &&
2386 check_for_opt_dict_arg(argvars, 1) == FAIL)))
2387 return;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002388
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002389 if (argvars[0].v_type != VAR_UNKNOWN)
2390 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002391 // get signs placed in the specified buffer
2392 buf = get_buf_arg(&argvars[0]);
2393 if (buf == NULL)
2394 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002395
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002396 if (argvars[1].v_type != VAR_UNKNOWN)
2397 {
2398 if (check_for_nonnull_dict_arg(argvars, 1) == FAIL)
2399 return;
2400 dict = argvars[1].vval.v_dict;
2401 if ((di = dict_find(dict, (char_u *)"lnum", -1)) != NULL)
2402 {
2403 // get signs placed at this line
2404 (void)tv_get_number_chk(&di->di_tv, &notanum);
2405 if (notanum)
2406 return;
2407 lnum = tv_get_lnum(&di->di_tv);
2408 }
2409 if ((di = dict_find(dict, (char_u *)"id", -1)) != NULL)
2410 {
2411 // get sign placed with this identifier
2412 sign_id = (int)tv_get_number_chk(&di->di_tv, &notanum);
2413 if (notanum)
2414 return;
2415 }
2416 if ((di = dict_find(dict, (char_u *)"group", -1)) != NULL)
2417 {
2418 group = tv_get_string_chk(&di->di_tv);
2419 if (group == NULL)
2420 return;
2421 if (*group == '\0') // empty string means global group
2422 group = NULL;
2423 }
2424 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002425 }
2426
2427 sign_get_placed(buf, lnum, sign_id, group, rettv->vval.v_list);
2428}
2429
2430/*
2431 * "sign_jump()" function
2432 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002433void
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002434f_sign_jump(typval_T *argvars, typval_T *rettv)
2435{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002436 int sign_id;
2437 char_u *sign_group = NULL;
2438 buf_T *buf;
2439 int notanum = FALSE;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002440
2441 rettv->vval.v_number = -1;
2442
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002443 if (in_vim9script() && (check_for_number_arg(argvars, 0) == FAIL ||
2444 check_for_string_arg(argvars, 1) == FAIL ||
2445 check_for_buffer_arg(argvars, 2) == FAIL))
2446 return;
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002447
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002448 // Sign identifier
2449 sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
2450 if (notanum)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002451 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002452 if (sign_id <= 0)
2453 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002454 emsg(_(e_invalid_argument));
2455 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002456 }
2457
2458 // Sign group
2459 sign_group = tv_get_string_chk(&argvars[1]);
2460 if (sign_group == NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002461 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002462 if (sign_group[0] == '\0')
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002463 sign_group = NULL; // global sign group
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002464 else
2465 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002466 sign_group = vim_strsave(sign_group);
2467 if (sign_group == NULL)
2468 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002469 }
2470
2471 // Buffer to place the sign
2472 buf = get_buf_arg(&argvars[2]);
2473 if (buf == NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002474 goto cleanup;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002475
2476 rettv->vval.v_number = sign_jump(sign_id, sign_group, buf);
2477
2478cleanup:
2479 vim_free(sign_group);
2480}
2481
2482/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002483 * Place a new sign using the values specified in dict 'dict'. Returns the sign
2484 * identifier if successfully placed, otherwise returns 0.
2485 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002486static int
2487sign_place_from_dict(typval_T *id_tv,
2488 typval_T *group_tv,
2489 typval_T *name_tv,
2490 typval_T *buf_tv,
2491 dict_T *dict)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002492{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002493 int sign_id = 0;
2494 char_u *group = NULL;
2495 char_u *sign_name = NULL;
2496 buf_T *buf = NULL;
2497 dictitem_T *di;
2498 linenr_T lnum = 0;
2499 int prio = -1;
2500 int notanum = FALSE;
2501 int ret_sign_id = -1;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002502
2503 // sign identifier
2504 if (id_tv == NULL)
2505 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002506 di = dict_find(dict, (char_u *)"id", -1);
2507 if (di != NULL)
2508 id_tv = &di->di_tv;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002509 }
2510 if (id_tv == NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002511 sign_id = 0;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002512 else
2513 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002514 sign_id = tv_get_number_chk(id_tv, &notanum);
2515 if (notanum)
2516 return -1;
2517 if (sign_id < 0)
2518 {
2519 emsg(_(e_invalid_argument));
2520 return -1;
2521 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002522 }
2523
2524 // sign group
2525 if (group_tv == NULL)
2526 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002527 di = dict_find(dict, (char_u *)"group", -1);
2528 if (di != NULL)
2529 group_tv = &di->di_tv;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002530 }
2531 if (group_tv == NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002532 group = NULL; // global group
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002533 else
2534 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002535 group = tv_get_string_chk(group_tv);
2536 if (group == NULL)
2537 goto cleanup;
2538 if (group[0] == '\0') // global sign group
2539 group = NULL;
2540 else
2541 {
2542 group = vim_strsave(group);
2543 if (group == NULL)
2544 return -1;
2545 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002546 }
2547
2548 // sign name
2549 if (name_tv == NULL)
2550 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002551 di = dict_find(dict, (char_u *)"name", -1);
2552 if (di != NULL)
2553 name_tv = &di->di_tv;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002554 }
2555 if (name_tv == NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002556 goto cleanup;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002557 sign_name = tv_get_string_chk(name_tv);
2558 if (sign_name == NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002559 goto cleanup;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002560
2561 // buffer to place the sign
2562 if (buf_tv == NULL)
2563 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002564 di = dict_find(dict, (char_u *)"buffer", -1);
2565 if (di != NULL)
2566 buf_tv = &di->di_tv;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002567 }
2568 if (buf_tv == NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002569 goto cleanup;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002570 buf = get_buf_arg(buf_tv);
2571 if (buf == NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002572 goto cleanup;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002573
2574 // line number of the sign
2575 di = dict_find(dict, (char_u *)"lnum", -1);
2576 if (di != NULL)
2577 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002578 lnum = tv_get_lnum(&di->di_tv);
2579 if (lnum <= 0)
2580 {
2581 emsg(_(e_invalid_argument));
2582 goto cleanup;
2583 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002584 }
2585
2586 // sign priority
2587 di = dict_find(dict, (char_u *)"priority", -1);
2588 if (di != NULL)
2589 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002590 prio = (int)tv_get_number_chk(&di->di_tv, &notanum);
2591 if (notanum)
2592 goto cleanup;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002593 }
2594
2595 if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002596 ret_sign_id = sign_id;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002597
2598cleanup:
2599 vim_free(group);
2600
2601 return ret_sign_id;
2602}
2603
2604/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002605 * "sign_place()" function
2606 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002607void
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002608f_sign_place(typval_T *argvars, typval_T *rettv)
2609{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002610 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002611
2612 rettv->vval.v_number = -1;
2613
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002614 if (in_vim9script() && (check_for_number_arg(argvars, 0) == FAIL ||
2615 check_for_string_arg(argvars, 1) == FAIL ||
2616 check_for_string_arg(argvars, 2) == FAIL ||
2617 check_for_buffer_arg(argvars, 3) == FAIL ||
2618 check_for_opt_dict_arg(argvars, 4) == FAIL))
2619 return;
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002620
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +01002621 if (argvars[4].v_type != VAR_UNKNOWN)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002622 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002623 if (check_for_nonnull_dict_arg(argvars, 4) == FAIL)
2624 return;
2625 dict = argvars[4].vval.v_dict;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002626 }
2627
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002628 rettv->vval.v_number = sign_place_from_dict(&argvars[0], &argvars[1],
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002629 &argvars[2], &argvars[3], dict);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002630}
2631
2632/*
2633 * "sign_placelist()" function. Place multiple signs.
2634 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002635void
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002636f_sign_placelist(typval_T *argvars, typval_T *rettv)
2637{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002638 listitem_T *li;
2639 int sign_id;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002640
Bram Moolenaar93a10962022-06-16 11:42:09 +01002641 if (rettv_list_alloc(rettv) == FAIL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002642 return;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002643
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002644 if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002645 return;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002646
Bram Moolenaard83392a2022-09-01 12:22:46 +01002647 if (check_for_list_arg(argvars, 0) == FAIL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002648 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002649
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002650 // Process the List of sign attributes
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002651 FOR_ALL_LIST_ITEMS(argvars[0].vval.v_list, li)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002652 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002653 sign_id = -1;
2654 if (li->li_tv.v_type == VAR_DICT)
2655 sign_id = sign_place_from_dict(NULL, NULL, NULL, NULL,
2656 li->li_tv.vval.v_dict);
2657 else
2658 emsg(_(e_dictionary_required));
2659 list_append_number(rettv->vval.v_list, sign_id);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002660 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002661}
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002662
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002663/*
2664 * Undefine multiple signs
2665 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002666static void
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002667sign_undefine_multiple(list_T *l, list_T *retlist)
2668{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002669 char_u *name;
2670 listitem_T *li;
2671 int retval;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002672
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002673 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002674 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002675 retval = -1;
2676 name = tv_get_string_chk(&li->li_tv);
2677 if (name != NULL && (sign_undefine_by_name(name, TRUE) == OK))
2678 retval = 0;
2679 list_append_number(retlist, retval);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002680 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002681}
2682
2683/*
2684 * "sign_undefine()" function
2685 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002686void
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002687f_sign_undefine(typval_T *argvars, typval_T *rettv)
2688{
2689 char_u *name;
2690
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002691 if (in_vim9script() && check_for_opt_string_or_list_arg(argvars, 0) == FAIL)
2692 return;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002693
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002694 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2695 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002696 // Undefine multiple signs
2697 if (rettv_list_alloc(rettv) == FAIL)
2698 return;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002699
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002700 sign_undefine_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2701 return;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002702 }
2703
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002704 rettv->vval.v_number = -1;
2705
2706 if (argvars[0].v_type == VAR_UNKNOWN)
2707 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002708 // Free all the signs
2709 free_signs();
2710 rettv->vval.v_number = 0;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002711 }
2712 else
2713 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002714 // Free only the specified sign
2715 name = tv_get_string_chk(&argvars[0]);
2716 if (name == NULL)
2717 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002718
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002719 if (sign_undefine_by_name(name, TRUE) == OK)
2720 rettv->vval.v_number = 0;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002721 }
2722}
2723
2724/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002725 * Unplace the sign with attributes specified in 'dict'. Returns 0 on success
2726 * and -1 on failure.
2727 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002728static int
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002729sign_unplace_from_dict(typval_T *group_tv, dict_T *dict)
2730{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002731 dictitem_T *di;
2732 int sign_id = 0;
2733 buf_T *buf = NULL;
2734 char_u *group = NULL;
2735 int retval = -1;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002736
2737 // sign group
2738 if (group_tv != NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002739 group = tv_get_string(group_tv);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002740 else
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002741 group = dict_get_string(dict, "group", FALSE);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002742 if (group != NULL)
2743 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002744 if (group[0] == '\0') // global sign group
2745 group = NULL;
2746 else
2747 {
2748 group = vim_strsave(group);
2749 if (group == NULL)
2750 return -1;
2751 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002752 }
2753
2754 if (dict != NULL)
2755 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002756 if ((di = dict_find(dict, (char_u *)"buffer", -1)) != NULL)
2757 {
2758 buf = get_buf_arg(&di->di_tv);
2759 if (buf == NULL)
2760 goto cleanup;
2761 }
2762 if (dict_has_key(dict, "id"))
2763 {
2764 sign_id = dict_get_number(dict, "id");
2765 if (sign_id <= 0)
2766 {
2767 emsg(_(e_invalid_argument));
2768 goto cleanup;
2769 }
2770 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002771 }
2772
2773 if (buf == NULL)
2774 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002775 // Delete the sign in all the buffers
2776 retval = 0;
2777 FOR_ALL_BUFFERS(buf)
2778 if (sign_unplace(sign_id, group, buf, 0) != OK)
2779 retval = -1;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002780 }
2781 else if (sign_unplace(sign_id, group, buf, 0) == OK)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002782 retval = 0;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002783
2784cleanup:
2785 vim_free(group);
2786
2787 return retval;
2788}
2789
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002790sign_entry_T *
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002791get_first_valid_sign(win_T *wp)
2792{
2793 sign_entry_T *sign = wp->w_buffer->b_signlist;
2794
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002795# ifdef FEAT_PROP_POPUP
Bram Moolenaar72570732019-11-30 14:21:53 +01002796 while (sign != NULL && !sign_group_for_window(sign, wp))
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002797 sign = sign->se_next;
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002798# endif
2799 return sign;
2800}
2801
2802/*
2803 * Return TRUE when window "wp" has a column to draw signs in.
2804 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002805int
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002806signcolumn_on(win_T *wp)
2807{
2808 // If 'signcolumn' is set to 'number', signs are displayed in the 'number'
2809 // column (if present). Otherwise signs are to be displayed in the sign
2810 // column.
2811 if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u')
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002812 return get_first_valid_sign(wp) != NULL && !wp->w_p_nu && !wp->w_p_rnu;
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002813
2814 if (*wp->w_p_scl == 'n')
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002815 return FALSE;
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002816 if (*wp->w_p_scl == 'y')
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002817 return TRUE;
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002818 return (get_first_valid_sign(wp) != NULL
2819# ifdef FEAT_NETBEANS_INTG
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002820 || wp->w_buffer->b_has_sign_column
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002821# endif
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002822 );
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002823}
2824
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002825/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002826 * "sign_unplace()" function
2827 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002828void
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002829f_sign_unplace(typval_T *argvars, typval_T *rettv)
2830{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002831 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002832
2833 rettv->vval.v_number = -1;
2834
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002835 if ((check_for_string_arg(argvars, 0) == FAIL ||
2836 check_for_opt_dict_arg(argvars, 1) == FAIL))
2837 return;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002838
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002839 if (argvars[1].v_type != VAR_UNKNOWN)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002840 dict = argvars[1].vval.v_dict;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002841
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002842 rettv->vval.v_number = sign_unplace_from_dict(&argvars[0], dict);
2843}
2844
2845/*
2846 * "sign_unplacelist()" function
2847 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002848void
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002849f_sign_unplacelist(typval_T *argvars, typval_T *rettv)
2850{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002851 listitem_T *li;
2852 int retval;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002853
Bram Moolenaar93a10962022-06-16 11:42:09 +01002854 if (rettv_list_alloc(rettv) == FAIL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002855 return;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002856
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002857 if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002858 return;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002859
Bram Moolenaard83392a2022-09-01 12:22:46 +01002860 if (check_for_list_arg(argvars, 0) == FAIL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002861 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002862
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002863 FOR_ALL_LIST_ITEMS(argvars[0].vval.v_list, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002864 {
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002865 retval = -1;
2866 if (li->li_tv.v_type == VAR_DICT)
2867 retval = sign_unplace_from_dict(NULL, li->li_tv.vval.v_dict);
2868 else
2869 emsg(_(e_dictionary_required));
2870 list_append_number(rettv->vval.v_list, retval);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002871 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002872}
2873
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002874#endif // FEAT_SIGNS