blob: e997a770ab03c89ae23d96f3ceccba2fe1c7eb2e [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
Hirohito Higashi38972d82025-05-06 18:13:29 +020048 "undefine",
Bram Moolenaarbbea4702019-01-01 13:20:31 +010049# define SIGNCMD_UNDEFINE 1
Hirohito Higashi38972d82025-05-06 18:13:29 +020050 "list",
Luca Saccarola3cf094e2024-11-19 22:55:13 +010051# define SIGNCMD_LIST 2
Hirohito Higashi38972d82025-05-06 18:13:29 +020052 "place",
Luca Saccarola3cf094e2024-11-19 22:55:13 +010053# define SIGNCMD_PLACE 3
Hirohito Higashi38972d82025-05-06 18:13:29 +020054 "unplace",
Bram Moolenaarbbea4702019-01-01 13:20:31 +010055# define SIGNCMD_UNPLACE 4
Hirohito Higashi38972d82025-05-06 18:13:29 +020056 "jump",
Luca Saccarola3cf094e2024-11-19 22:55:13 +010057# define SIGNCMD_JUMP 5
Hirohito Higashi38972d82025-05-06 18:13:29 +020058 NULL
Luca Saccarola3cf094e2024-11-19 22:55:13 +010059# 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 Saccarola78ca80f2024-11-24 14:25:06 +010084 hash_T hash = hash_hash(groupname);
85 hashitem_T *hi = hash_lookup(&sg_table, groupname, hash);
86 signgroup_T *group = NULL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +010087
Bram Moolenaarbbea4702019-01-01 13:20:31 +010088 if (HASHITEM_EMPTY(hi))
89 {
Hirohito Higashi38972d82025-05-06 18:13:29 +020090 // new group
91 group = alloc(offsetof(signgroup_T, sg_name) + STRLEN(groupname) + 1);
92 if (group == NULL)
93 return NULL;
Luca Saccarola78ca80f2024-11-24 14:25:06 +010094
Hirohito Higashi38972d82025-05-06 18:13:29 +020095 STRCPY(group->sg_name, groupname);
96 group->sg_refcount = 1;
97 group->sg_next_sign_id = 1;
98 hash_add_item(&sg_table, hi, group->sg_name, hash);
Bram Moolenaarbbea4702019-01-01 13:20:31 +010099 }
100 else
101 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200102 // existing group
103 group = HI2SG(hi);
104 group->sg_refcount++;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100105 }
106
107 return group;
108}
109
110/*
111 * A sign in group 'groupname' is removed. If all the signs in this group are
112 * removed, then remove the group.
113 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100114static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100115sign_group_unref(char_u *groupname)
116{
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100117 hashitem_T *hi = hash_find(&sg_table, groupname);
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000118 if (HASHITEM_EMPTY(hi))
Hirohito Higashi38972d82025-05-06 18:13:29 +0200119 return;
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000120
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100121 signgroup_T *group = HI2SG(hi);
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000122 group->sg_refcount--;
123 if (group->sg_refcount == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100124 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200125 // All the signs in this group are removed
126 hash_remove(&sg_table, hi, "sign remove");
127 vim_free(group);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100128 }
129}
130
131/*
132 * Returns TRUE if 'sign' is in 'group'.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200133 * A sign can either be in the global group (sign->se_group == NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100134 * or in a named group. If 'group' is '*', then the sign is part of the group.
135 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100136static int
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200137sign_in_group(sign_entry_T *sign, char_u *group)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100138{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100139 return ((group != NULL && STRCMP(group, "*") == 0) ||
Hirohito Higashi38972d82025-05-06 18:13:29 +0200140 (group == NULL && sign->se_group == NULL) ||
141 (group != NULL && sign->se_group != NULL &&
142 STRCMP(group, sign->se_group->sg_name) == 0));
Bram Moolenaar72570732019-11-30 14:21:53 +0100143}
144
145/*
146 * Return TRUE if "sign" is to be displayed in window "wp".
147 * If the group name starts with "PopUp" it only shows in a popup window.
148 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100149static int
Bram Moolenaar72570732019-11-30 14:21:53 +0100150sign_group_for_window(sign_entry_T *sign, win_T *wp)
151{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100152 int for_popup = sign->se_group != NULL &&
Hirohito Higashi38972d82025-05-06 18:13:29 +0200153 STRNCMP("PopUp", sign->se_group->sg_name, 5) == 0;
Bram Moolenaar72570732019-11-30 14:21:53 +0100154
155 return WIN_IS_POPUP(wp) ? for_popup : !for_popup;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100156}
157
158/*
159 * Get the next free sign identifier in the specified group
160 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100161static int
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100162sign_group_get_next_signid(buf_T *buf, char_u *groupname)
163{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100164 int id = 1;
165 signgroup_T *group = NULL;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100166 sign_entry_T *sign = NULL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100167
168 if (groupname != NULL)
169 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200170 hashitem_T *hi = hash_find(&sg_table, groupname);
171 if (HASHITEM_EMPTY(hi))
172 return id;
173 group = HI2SG(hi);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100174 }
175
Bram Moolenaarb5443cc2019-01-15 20:19:40 +0100176 // Search for the next usable sign identifier
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100177 int found = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100178 while (!found)
179 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200180 if (group == NULL)
181 id = next_sign_id++; // global group
182 else
183 id = group->sg_next_sign_id++;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100184
Hirohito Higashi38972d82025-05-06 18:13:29 +0200185 // Check whether this sign is already placed in the buffer
186 found = TRUE;
187 FOR_ALL_SIGNS_IN_BUF(buf, sign)
188 {
189 if (id == sign->se_id && sign_in_group(sign, groupname))
190 {
191 found = FALSE; // sign identifier is in use
192 break;
193 }
194 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100195 }
196
197 return id;
198}
199
200/*
201 * Insert a new sign into the signlist for buffer 'buf' between the 'prev' and
202 * 'next' signs.
203 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100204static void
205insert_sign(buf_T *buf, // buffer to store sign in
Hirohito Higashi38972d82025-05-06 18:13:29 +0200206 sign_entry_T *prev, // previous sign entry
207 sign_entry_T *next, // next sign entry
208 int id, // sign ID
209 char_u *group, // sign group; NULL for global group
210 int prio, // sign priority
211 linenr_T lnum, // line number which gets the mark
212 int typenr) // typenr of sign we are adding
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100213{
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100214 sign_entry_T *newsign =
Hirohito Higashi38972d82025-05-06 18:13:29 +0200215 lalloc_id(sizeof(sign_entry_T), FALSE, aid_insert_sign);
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000216 if (newsign == NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200217 return;
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000218
219 newsign->se_id = id;
220 newsign->se_lnum = lnum;
221 newsign->se_typenr = typenr;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100222
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000223 if (group != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100224 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200225 newsign->se_group = sign_group_ref(group);
226 if (newsign->se_group == NULL)
227 {
228 vim_free(newsign);
229 return;
230 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100231 }
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000232 else
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100233 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200234 newsign->se_group = NULL;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100235 }
236
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000237 newsign->se_priority = prio;
238 newsign->se_next = next;
239 newsign->se_prev = prev;
240 if (next != NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200241 next->se_prev = newsign;
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000242
243 if (prev == NULL)
244 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200245 // When adding first sign need to redraw the windows to create the
246 // column for signs.
247 if (buf->b_signlist == NULL)
248 {
249 redraw_buf_later(buf, UPD_NOT_VALID);
250 changed_line_abv_curs();
251 }
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000252
Hirohito Higashi38972d82025-05-06 18:13:29 +0200253 // first sign in signlist
254 buf->b_signlist = newsign;
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100255# ifdef FEAT_NETBEANS_INTG
Hirohito Higashi38972d82025-05-06 18:13:29 +0200256 if (netbeans_active())
257 buf->b_has_sign_column = TRUE;
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100258# endif
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000259 }
260 else
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100261 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200262 prev->se_next = newsign;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100263 }
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
Hirohito Higashi38972d82025-05-06 18:13:29 +0200271 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{
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100278 // keep signs sorted by lnum and by priority: insert new sign at
279 // the proper position in the list for this lnum.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200280 while (prev != NULL && prev->se_lnum == lnum && prev->se_priority <= prio)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200281 prev = prev->se_prev;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100282
283 sign_entry_T *sign = (prev == NULL) ? buf->b_signlist : prev->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100284
285 insert_sign(buf, prev, sign, id, group, prio, lnum, typenr);
286}
287
288/*
Bram Moolenaar4e038572019-07-04 18:28:35 +0200289 * Lookup a sign by typenr. Returns NULL if sign is not found.
290 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100291static sign_T *
Bram Moolenaar4e038572019-07-04 18:28:35 +0200292find_sign_by_typenr(int typenr)
293{
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100294 sign_T *sp = NULL;
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200295 FOR_ALL_SIGNS(sp)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200296 if (sp->sn_typenr == typenr)
297 return sp;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200298 return NULL;
299}
300
301/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100302 * Get the name of a sign by its typenr.
303 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100304static char_u *
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100305sign_typenr2name(int typenr)
306{
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100307 sign_T *sp = NULL;
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200308 FOR_ALL_SIGNS(sp)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200309 if (sp->sn_typenr == typenr)
310 return sp->sn_name;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100311 return (char_u *)_("[Deleted]");
312}
313
314/*
315 * Return information about a sign in a Dict
316 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100317static dict_T *
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200318sign_get_info(sign_entry_T *sign)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100319{
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100320 dict_T *d = dict_alloc_id(aid_sign_getinfo);
321 if (d == NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200322 return NULL;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100323
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200324 dict_add_number(d, "id", sign->se_id);
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100325 dict_add_string(d, "group",
Hirohito Higashi38972d82025-05-06 18:13:29 +0200326 (sign->se_group == NULL) ? (char_u *)""
327 : sign->se_group->sg_name);
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200328 dict_add_number(d, "lnum", sign->se_lnum);
329 dict_add_string(d, "name", sign_typenr2name(sign->se_typenr));
330 dict_add_number(d, "priority", sign->se_priority);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100331
332 return d;
333}
334
335/*
Bram Moolenaar64416122019-06-07 21:37:13 +0200336 * Sort the signs placed on the same line as "sign" by priority. Invoked after
337 * changing the priority of an already placed sign. Assumes the signs in the
338 * buffer are sorted by line number and priority.
339 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100340static void
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200341sign_sort_by_prio_on_line(buf_T *buf, sign_entry_T *sign)
Bram Moolenaar64416122019-06-07 21:37:13 +0200342{
Bram Moolenaar64416122019-06-07 21:37:13 +0200343 // If there is only one sign in the buffer or only one sign on the line or
344 // the sign is already sorted by priority, then return.
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100345 if ((sign->se_prev == NULL || sign->se_prev->se_lnum != sign->se_lnum ||
Hirohito Higashi38972d82025-05-06 18:13:29 +0200346 sign->se_prev->se_priority > sign->se_priority) &&
347 (sign->se_next == NULL || sign->se_next->se_lnum != sign->se_lnum ||
348 sign->se_next->se_priority < sign->se_priority))
349 return;
Bram Moolenaar64416122019-06-07 21:37:13 +0200350
351 // One or more signs on the same line as 'sign'
352 // Find a sign after which 'sign' should be inserted
353
354 // First search backward for a sign with higher priority on the same line
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100355 sign_entry_T *p = sign;
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100356 while (p->se_prev != NULL && p->se_prev->se_lnum == sign->se_lnum &&
Hirohito Higashi38972d82025-05-06 18:13:29 +0200357 p->se_prev->se_priority <= sign->se_priority)
358 p = p->se_prev;
Bram Moolenaar64416122019-06-07 21:37:13 +0200359
360 if (p == sign)
361 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200362 // Sign not found. Search forward for a sign with priority just before
363 // 'sign'.
364 p = sign->se_next;
365 while (p->se_next != NULL && p->se_next->se_lnum == sign->se_lnum &&
366 p->se_next->se_priority > sign->se_priority)
367 p = p->se_next;
Bram Moolenaar64416122019-06-07 21:37:13 +0200368 }
369
370 // Remove 'sign' from the list
371 if (buf->b_signlist == sign)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200372 buf->b_signlist = sign->se_next;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100373
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200374 if (sign->se_prev != NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200375 sign->se_prev->se_next = sign->se_next;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100376
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200377 if (sign->se_next != NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200378 sign->se_next->se_prev = sign->se_prev;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100379
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200380 sign->se_prev = NULL;
381 sign->se_next = NULL;
Bram Moolenaar64416122019-06-07 21:37:13 +0200382
383 // Re-insert 'sign' at the right place
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200384 if (p->se_priority <= sign->se_priority)
Bram Moolenaar64416122019-06-07 21:37:13 +0200385 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200386 // 'sign' has a higher priority and should be inserted before 'p'
387 sign->se_prev = p->se_prev;
388 sign->se_next = p;
389 p->se_prev = sign;
390 if (sign->se_prev != NULL)
391 sign->se_prev->se_next = sign;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100392
Hirohito Higashi38972d82025-05-06 18:13:29 +0200393 if (buf->b_signlist == p)
394 buf->b_signlist = sign;
Bram Moolenaar64416122019-06-07 21:37:13 +0200395 }
396 else
397 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200398 // 'sign' has a lower priority and should be inserted after 'p'
399 sign->se_prev = p;
400 sign->se_next = p->se_next;
401 p->se_next = sign;
402 if (sign->se_next != NULL)
403 sign->se_next->se_prev = sign;
Bram Moolenaar64416122019-06-07 21:37:13 +0200404 }
405}
406
407/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100408 * Add the sign into the signlist. Find the right spot to do it though.
409 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100410static void
411buf_addsign(buf_T *buf, // buffer to store sign in
Hirohito Higashi38972d82025-05-06 18:13:29 +0200412 int id, // sign ID
413 char_u *groupname, // sign group
414 int prio, // sign priority
415 linenr_T lnum, // line number which gets the mark
416 int typenr) // typenr of sign we are adding
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100417{
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100418 sign_entry_T *sign = NULL; // a sign in the signlist
419 sign_entry_T *prev = NULL; // the previous sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100420 FOR_ALL_SIGNS_IN_BUF(buf, sign)
421 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200422 if (lnum == sign->se_lnum && id == sign->se_id &&
423 sign_in_group(sign, groupname))
424 {
425 // Update an existing sign
426 sign->se_typenr = typenr;
427 sign->se_priority = prio;
428 sign_sort_by_prio_on_line(buf, sign);
429 return;
430 }
431 else if (lnum < sign->se_lnum)
432 {
433 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum,
434 typenr);
435 return;
436 }
437 prev = sign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100438 }
439
440 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100441}
442
443/*
444 * For an existing, placed sign "markId" change the type to "typenr".
445 * Returns the line number of the sign, or zero if the sign is not found.
446 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100447static linenr_T
448buf_change_sign_type(buf_T *buf, // buffer to store sign in
Hirohito Higashi38972d82025-05-06 18:13:29 +0200449 int markId, // sign ID
450 char_u *group, // sign group
451 int typenr, // typenr of sign we are adding
452 int prio) // sign priority
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100453{
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100454 sign_entry_T *sign = NULL; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100455 FOR_ALL_SIGNS_IN_BUF(buf, sign)
456 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200457 if (sign->se_id == markId && sign_in_group(sign, group))
458 {
459 sign->se_typenr = typenr;
460 sign->se_priority = prio;
461 sign_sort_by_prio_on_line(buf, sign);
462 return sign->se_lnum;
463 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100464 }
465
466 return (linenr_T)0;
467}
468
469/*
Bram Moolenaar4e038572019-07-04 18:28:35 +0200470 * Return the attributes of the first sign placed on line 'lnum' in buffer
471 * 'buf'. Used when refreshing the screen. Returns TRUE if a sign is found on
472 * 'lnum', FALSE otherwise.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100473 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100474int
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100475buf_get_signattrs(win_T *wp, linenr_T lnum, sign_attrs_T *sattr)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100476{
Bram Moolenaara80faa82020-04-12 19:37:17 +0200477 CLEAR_POINTER(sattr);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100478
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100479 buf_T *buf = wp->w_buffer;
480 sign_entry_T *sign = NULL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100481 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200482 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200483 // Signs are sorted by line number in the buffer. No need to check
484 // for signs after the specified line number 'lnum'.
485 if (sign->se_lnum > lnum)
486 break;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200487
Hirohito Higashi38972d82025-05-06 18:13:29 +0200488 if (sign->se_lnum == lnum
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100489# ifdef FEAT_PROP_POPUP
Hirohito Higashi38972d82025-05-06 18:13:29 +0200490 && sign_group_for_window(sign, wp)
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100491# endif
Hirohito Higashi38972d82025-05-06 18:13:29 +0200492 )
493 {
494 sattr->sat_typenr = sign->se_typenr;
495 sign_T *sp = find_sign_by_typenr(sign->se_typenr);
496 if (sp == NULL)
497 return FALSE;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200498
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100499# ifdef FEAT_SIGN_ICONS
Hirohito Higashi38972d82025-05-06 18:13:29 +0200500 sattr->sat_icon = sp->sn_image;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100501# endif
Hirohito Higashi38972d82025-05-06 18:13:29 +0200502 sattr->sat_text = sp->sn_text;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100503
Hirohito Higashi38972d82025-05-06 18:13:29 +0200504 if (sattr->sat_text != NULL && sp->sn_text_hl > 0)
505 sattr->sat_texthl = syn_id2attr(sp->sn_text_hl);
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100506
Hirohito Higashi38972d82025-05-06 18:13:29 +0200507 if (sp->sn_line_hl > 0)
508 sattr->sat_linehl = syn_id2attr(sp->sn_line_hl);
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100509
Hirohito Higashi38972d82025-05-06 18:13:29 +0200510 if (sp->sn_cul_hl > 0)
511 sattr->sat_culhl = syn_id2attr(sp->sn_cul_hl);
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100512
Hirohito Higashi38972d82025-05-06 18:13:29 +0200513 if (sp->sn_num_hl > 0)
514 sattr->sat_numhl = syn_id2attr(sp->sn_num_hl);
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100515
Hirohito Higashi38972d82025-05-06 18:13:29 +0200516 sattr->sat_priority = sign->se_priority;
Bram Moolenaara2f6e422020-02-19 17:13:04 +0100517
Hirohito Higashi38972d82025-05-06 18:13:29 +0200518 // If there is another sign next with the same priority, may
519 // combine the text and the line highlighting.
520 if (sign->se_next != NULL &&
521 sign->se_next->se_priority == sign->se_priority &&
522 sign->se_next->se_lnum == sign->se_lnum)
523 {
524 sign_T *next_sp = find_sign_by_typenr(sign->se_next->se_typenr);
525 if (next_sp == NULL)
526 return FALSE;
Bram Moolenaara2f6e422020-02-19 17:13:04 +0100527
Hirohito Higashi38972d82025-05-06 18:13:29 +0200528 if (sattr->sat_icon == NULL && sattr->sat_text == NULL)
529 {
Bram Moolenaara2f6e422020-02-19 17:13:04 +0100530# ifdef FEAT_SIGN_ICONS
Hirohito Higashi38972d82025-05-06 18:13:29 +0200531 sattr->sat_icon = next_sp->sn_image;
Bram Moolenaara2f6e422020-02-19 17:13:04 +0100532# endif
Hirohito Higashi38972d82025-05-06 18:13:29 +0200533 sattr->sat_text = next_sp->sn_text;
534 }
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100535
Hirohito Higashi38972d82025-05-06 18:13:29 +0200536 if (sp->sn_text_hl <= 0 && next_sp->sn_text_hl > 0)
537 sattr->sat_texthl = syn_id2attr(next_sp->sn_text_hl);
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100538
Hirohito Higashi38972d82025-05-06 18:13:29 +0200539 if (sp->sn_line_hl <= 0 && next_sp->sn_line_hl > 0)
540 sattr->sat_linehl = syn_id2attr(next_sp->sn_line_hl);
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100541
Hirohito Higashi38972d82025-05-06 18:13:29 +0200542 if (sp->sn_cul_hl <= 0 && next_sp->sn_cul_hl > 0)
543 sattr->sat_culhl = syn_id2attr(next_sp->sn_cul_hl);
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100544
Hirohito Higashi38972d82025-05-06 18:13:29 +0200545 if (sp->sn_num_hl <= 0 && next_sp->sn_num_hl > 0)
546 sattr->sat_numhl = syn_id2attr(next_sp->sn_num_hl);
547 }
548 return TRUE;
549 }
Bram Moolenaar4e038572019-07-04 18:28:35 +0200550 }
551 return FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100552}
553
554/*
555 * Delete sign 'id' in group 'group' from buffer 'buf'.
556 * If 'id' is zero, then delete all the signs in group 'group'. Otherwise
557 * delete only the specified sign.
558 * If 'group' is '*', then delete the sign in all the groups. If 'group' is
559 * NULL, then delete the sign in the global group. Otherwise delete the sign in
560 * the specified group.
561 * Returns the line number of the deleted sign. If multiple signs are deleted,
562 * then returns the line number of the last sign deleted.
563 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100564linenr_T
565buf_delsign(buf_T *buf, // buffer sign is stored in
Hirohito Higashi38972d82025-05-06 18:13:29 +0200566 linenr_T atlnum, // sign at this line, 0 - at any line
567 int id, // sign id
568 char_u *group) // sign group
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100569{
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100570 // pointer to pointer to current sign
571 sign_entry_T **lastp = &buf->b_signlist;
572 sign_entry_T *next = NULL; // the next sign in a b_signlist
573 linenr_T lnum = 0; // line number whose sign was deleted
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100574
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100575 for (sign_entry_T *sign = buf->b_signlist; sign != NULL; sign = next)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100576 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200577 next = sign->se_next;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100578
Hirohito Higashi38972d82025-05-06 18:13:29 +0200579 if ((id == 0 || sign->se_id == id) &&
580 (atlnum == 0 || sign->se_lnum == atlnum) &&
581 sign_in_group(sign, group))
582 {
583 *lastp = next;
584 if (next != NULL)
585 next->se_prev = sign->se_prev;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100586
Hirohito Higashi38972d82025-05-06 18:13:29 +0200587 lnum = sign->se_lnum;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100588
Hirohito Higashi38972d82025-05-06 18:13:29 +0200589 if (sign->se_group != NULL)
590 sign_group_unref(sign->se_group->sg_name);
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100591
Hirohito Higashi38972d82025-05-06 18:13:29 +0200592 vim_free(sign);
593 redraw_buf_line_later(buf, lnum);
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100594
Hirohito Higashi38972d82025-05-06 18:13:29 +0200595 // Check whether only one sign needs to be deleted
596 // If deleting a sign with a specific identifier in a particular
597 // group or deleting any sign at a particular line number, delete
598 // only one sign.
599 if (group == NULL || (*group != '*' && id != 0) ||
600 (*group == '*' && atlnum != 0))
601 break;
602 }
603 else
604 {
605 lastp = &sign->se_next;
606 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100607 }
608
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100609 // When deleting the last sign the cursor position may change, because the
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100610 // sign columns no longer shows. And the 'signcolumn' may be hidden.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100611 if (buf->b_signlist == NULL)
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100612 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200613 redraw_buf_later(buf, UPD_NOT_VALID);
614 changed_line_abv_curs();
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100615 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100616
617 return lnum;
618}
619
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100620/*
621 * Find the line number of the sign with the requested id in group 'group'. If
622 * the sign does not exist, return 0 as the line number. This will still let
623 * the correct file get loaded.
624 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100625int
626buf_findsign(buf_T *buf, // buffer to store sign in
Hirohito Higashi38972d82025-05-06 18:13:29 +0200627 int id, // sign ID
628 char_u *group) // sign group
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100629{
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100630 sign_entry_T *sign = NULL; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100631 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200632 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
Hirohito Higashi38972d82025-05-06 18:13:29 +0200644 linenr_T lnum, // line number of sign
645 char_u *groupname) // sign group name
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100646{
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100647 sign_entry_T *sign = NULL; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100648 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200649 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200650 // Signs are sorted by line number in the buffer. No need to check
651 // for signs after the specified line number 'lnum'.
652 if (sign->se_lnum > lnum)
653 break;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200654
Hirohito Higashi38972d82025-05-06 18:13:29 +0200655 if (sign->se_lnum == lnum && sign_in_group(sign, groupname))
656 return sign;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200657 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100658
659 return NULL;
660}
661
662/*
663 * Return the identifier of the sign at line number 'lnum' in buffer 'buf'.
664 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100665int
666buf_findsign_id(buf_T *buf, // buffer whose sign we are searching for
Hirohito Higashi38972d82025-05-06 18:13:29 +0200667 linenr_T lnum, // line number of sign
668 char_u *groupname) // sign group name
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100669{
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100670 // a sign in the signlist
671 sign_entry_T *sign = buf_getsign_at_line(buf, lnum, groupname);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100672 if (sign != NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200673 return sign->se_id;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100674
675 return 0;
676}
677
678# if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
679/*
680 * See if a given type of sign exists on a specific line.
681 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100682int
683buf_findsigntype_id(buf_T *buf, // buffer whose sign we are searching for
Hirohito Higashi38972d82025-05-06 18:13:29 +0200684 linenr_T lnum, // line number of sign
685 int typenr) // sign type number
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100686{
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100687 sign_entry_T *sign = NULL; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100688 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200689 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200690 // Signs are sorted by line number in the buffer. No need to check
691 // for signs after the specified line number 'lnum'.
692 if (sign->se_lnum > lnum)
693 break;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200694
Hirohito Higashi38972d82025-05-06 18:13:29 +0200695 if (sign->se_lnum == lnum && sign->se_typenr == typenr)
696 return sign->se_id;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200697 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100698
699 return 0;
700}
701
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100702# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
703/*
704 * Return the number of icons on the given line.
705 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100706int
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100707buf_signcount(buf_T *buf, linenr_T lnum)
708{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100709 int count = 0;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100710 sign_entry_T *sign = NULL; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100711 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200712 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200713 // Signs are sorted by line number in the buffer. No need to check
714 // for signs after the specified line number 'lnum'.
715 if (sign->se_lnum > lnum)
716 break;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200717
Hirohito Higashi38972d82025-05-06 18:13:29 +0200718 if (sign->se_lnum == lnum && sign_get_image(sign->se_typenr) != NULL)
719 count++;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200720 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100721
722 return count;
723}
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100724# endif // FEAT_SIGN_ICONS
725# endif // FEAT_NETBEANS_INTG
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100726
727/*
728 * Delete signs in group 'group' in buffer "buf". If 'group' is '*', then
729 * delete all the signs.
730 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100731void
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100732buf_delete_signs(buf_T *buf, char_u *group)
733{
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100734 // When deleting the last sign need to redraw the windows to remove the
735 // sign column. Not when curwin is NULL (this means we're exiting).
736 if (buf->b_signlist != NULL && curwin != NULL)
737 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200738 redraw_buf_later(buf, UPD_NOT_VALID);
739 changed_line_abv_curs();
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100740 }
741
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100742 // pointer to pointer to current sign
743 sign_entry_T **lastp = &buf->b_signlist;
744 sign_entry_T *next = NULL;
745
746 for (sign_entry_T *sign = buf->b_signlist; sign != NULL; sign = next)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100747 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200748 next = sign->se_next;
749 if (sign_in_group(sign, group))
750 {
751 *lastp = next;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100752
Hirohito Higashi38972d82025-05-06 18:13:29 +0200753 if (next != NULL)
754 next->se_prev = sign->se_prev;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100755
Hirohito Higashi38972d82025-05-06 18:13:29 +0200756 if (sign->se_group != NULL)
757 sign_group_unref(sign->se_group->sg_name);
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100758
Hirohito Higashi38972d82025-05-06 18:13:29 +0200759 vim_free(sign);
760 }
761 else
762 {
763 lastp = &sign->se_next;
764 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100765 }
766}
767
768/*
769 * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers.
770 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100771static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100772sign_list_placed(buf_T *rbuf, char_u *sign_group)
773{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100774 char lbuf[MSG_BUF_LEN];
775 char group[MSG_BUF_LEN];
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100776
Bram Moolenaar32526b32019-01-19 17:43:09 +0100777 msg_puts_title(_("\n--- Signs ---"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100778 msg_putchar('\n');
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100779
780 buf_T *buf = (rbuf == NULL) ? firstbuf : rbuf;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100781 while (buf != NULL && !got_int)
782 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200783 if (buf->b_signlist != NULL)
784 {
785 vim_snprintf(lbuf, MSG_BUF_LEN, _("Signs for %s:"), buf->b_fname);
786 msg_puts_attr(lbuf, HL_ATTR(HLF_D));
787 msg_putchar('\n');
788 }
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100789
Hirohito Higashi38972d82025-05-06 18:13:29 +0200790 sign_entry_T *sign = NULL;
791 FOR_ALL_SIGNS_IN_BUF(buf, sign)
792 {
793 if (got_int)
794 break;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100795
Hirohito Higashi38972d82025-05-06 18:13:29 +0200796 if (!sign_in_group(sign, sign_group))
797 continue;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100798
Hirohito Higashi38972d82025-05-06 18:13:29 +0200799 if (sign->se_group != NULL)
800 vim_snprintf(group, MSG_BUF_LEN, _(" group=%s"),
801 sign->se_group->sg_name);
802 else
803 group[0] = '\0';
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100804
Hirohito Higashi38972d82025-05-06 18:13:29 +0200805 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);
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100809
Hirohito Higashi38972d82025-05-06 18:13:29 +0200810 msg_puts(lbuf);
811 msg_putchar('\n');
812 }
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100813
Hirohito Higashi38972d82025-05-06 18:13:29 +0200814 if (rbuf != NULL)
815 break;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100816
Hirohito Higashi38972d82025-05-06 18:13:29 +0200817 buf = buf->b_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100818 }
819}
820
821/*
822 * Adjust a placed sign for inserted/deleted lines.
823 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100824void
825sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100826{
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100827 sign_entry_T *sign = NULL; // a sign in a b_signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100828 FOR_ALL_SIGNS_IN_BUF(curbuf, sign)
829 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200830 // Ignore changes to lines after the sign
831 if (sign->se_lnum < line1)
832 continue;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100833
Hirohito Higashi38972d82025-05-06 18:13:29 +0200834 linenr_T new_lnum = sign->se_lnum;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100835
Hirohito Higashi38972d82025-05-06 18:13:29 +0200836 if (sign->se_lnum <= line2)
837 {
838 if (amount != MAXLNUM)
839 new_lnum += amount;
840 }
841 else if (sign->se_lnum > line2)
842 {
843 // Lines inserted or deleted before the sign
844 new_lnum += amount_after;
845 }
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100846
Hirohito Higashi38972d82025-05-06 18:13:29 +0200847 // If the new sign line number is past the last line in the buffer,
848 // then don't adjust the line number. Otherwise, it will always be past
849 // the last line and will not be visible.
850 if (new_lnum <= curbuf->b_ml.ml_line_count)
851 sign->se_lnum = new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100852 }
853}
854
855/*
856 * Find index of a ":sign" subcmd from its name.
857 * "*end_cmd" must be writable.
858 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100859static int
860sign_cmd_idx(char_u *begin_cmd, // begin of sign subcmd
Hirohito Higashi38972d82025-05-06 18:13:29 +0200861 char_u *end_cmd) // just after sign subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100862{
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100863 int idx = 0;
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100864 char save = *end_cmd;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100865 *end_cmd = NUL;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100866
867 while (cmds[idx] != NULL && STRCMP(begin_cmd, cmds[idx]) != 0)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200868 ++idx;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100869
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100870 *end_cmd = save;
871 return idx;
872}
873
874/*
875 * Find a sign by name. Also returns pointer to the previous sign.
876 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100877static sign_T *
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100878sign_find(char_u *name, sign_T **sp_prev)
879{
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100880 if (sp_prev != NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200881 *sp_prev = NULL;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100882
883 sign_T *sp = NULL;
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200884 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100885 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200886 if (STRCMP(sp->sn_name, name) == 0)
887 break;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100888
Hirohito Higashi38972d82025-05-06 18:13:29 +0200889 if (sp_prev != NULL)
890 *sp_prev = sp;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100891 }
892
893 return sp;
894}
895
896/*
Bram Moolenaar03142362019-01-18 22:01:42 +0100897 * Allocate a new sign
898 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100899static sign_T *
Bram Moolenaar03142362019-01-18 22:01:42 +0100900alloc_new_sign(char_u *name)
901{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100902 int start = next_sign_typenr;
Bram Moolenaar03142362019-01-18 22:01:42 +0100903
904 // Allocate a new sign.
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100905 sign_T *sp = alloc_clear_id(sizeof(sign_T), aid_sign_define_by_name);
Bram Moolenaar03142362019-01-18 22:01:42 +0100906 if (sp == NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200907 return NULL;
Bram Moolenaar03142362019-01-18 22:01:42 +0100908
909 // Check that next_sign_typenr is not already being used.
910 // This only happens after wrapping around. Hopefully
911 // another one got deleted and we can use its number.
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100912 sign_T *lp = first_sign;
913 while (lp != NULL)
Bram Moolenaar03142362019-01-18 22:01:42 +0100914 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200915 if (lp->sn_typenr == next_sign_typenr)
916 {
917 ++next_sign_typenr;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100918
Hirohito Higashi38972d82025-05-06 18:13:29 +0200919 if (next_sign_typenr == MAX_TYPENR)
920 next_sign_typenr = 1;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100921
Hirohito Higashi38972d82025-05-06 18:13:29 +0200922 if (next_sign_typenr == start)
923 {
924 vim_free(sp);
925 emsg(_(e_too_many_signs_defined));
926 return NULL;
927 }
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100928
Hirohito Higashi38972d82025-05-06 18:13:29 +0200929 lp = first_sign; // start all over
930 continue;
931 }
932 lp = lp->sn_next;
Bram Moolenaar03142362019-01-18 22:01:42 +0100933 }
934
935 sp->sn_typenr = next_sign_typenr;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100936
Bram Moolenaar03142362019-01-18 22:01:42 +0100937 if (++next_sign_typenr == MAX_TYPENR)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200938 next_sign_typenr = 1; // wrap around
Bram Moolenaar03142362019-01-18 22:01:42 +0100939
940 sp->sn_name = vim_strsave(name);
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100941 if (sp->sn_name == NULL) // out of memory
Bram Moolenaar03142362019-01-18 22:01:42 +0100942 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200943 vim_free(sp);
944 return NULL;
Bram Moolenaar03142362019-01-18 22:01:42 +0100945 }
946
947 return sp;
948}
949
950/*
951 * Initialize the icon information for a new sign
952 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100953static void
Bram Moolenaar03142362019-01-18 22:01:42 +0100954sign_define_init_icon(sign_T *sp, char_u *icon)
955{
956 vim_free(sp->sn_icon);
957 sp->sn_icon = vim_strsave(icon);
958 backslash_halve(sp->sn_icon);
959# ifdef FEAT_SIGN_ICONS
960 if (gui.in_use)
961 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200962 out_flush();
963 if (sp->sn_image != NULL)
964 gui_mch_destroy_sign(sp->sn_image);
965 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
Bram Moolenaar03142362019-01-18 22:01:42 +0100966 }
967# endif
968}
969
970/*
971 * Initialize the text for a new sign
972 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100973static int
Bram Moolenaar03142362019-01-18 22:01:42 +0100974sign_define_init_text(sign_T *sp, char_u *text)
975{
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100976 char_u *s = NULL;
977 char_u *endp = text + (int)STRLEN(text);
978 int cells = 0;
Bram Moolenaar03142362019-01-18 22:01:42 +0100979
980 // Remove backslashes so that it is possible to use a space.
981 for (s = text; s + 1 < endp; ++s)
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100982 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200983 if (*s == '\\')
984 {
985 STRMOVE(s, s + 1);
986 --endp;
987 }
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100988 }
Bram Moolenaar03142362019-01-18 22:01:42 +0100989
990 // Count cells and check for non-printable chars
Bram Moolenaar03142362019-01-18 22:01:42 +0100991 if (has_mbyte)
992 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200993 for (s = text; s < endp; s += (*mb_ptr2len)(s))
994 {
995 if (!vim_isprintc((*mb_ptr2char)(s)))
996 break;
Luca Saccarola78ca80f2024-11-24 14:25:06 +0100997
Hirohito Higashi38972d82025-05-06 18:13:29 +0200998 cells += (*mb_ptr2cells)(s);
999 }
Bram Moolenaar03142362019-01-18 22:01:42 +01001000 }
1001 else
Bram Moolenaar03142362019-01-18 22:01:42 +01001002 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001003 for (s = text; s < endp; ++s)
1004 {
1005 if (!vim_isprintc(*s))
1006 break;
1007 }
1008 cells = (int)(s - text);
Bram Moolenaar03142362019-01-18 22:01:42 +01001009 }
1010
1011 // Currently sign text must be one or two display cells
1012 if (s != endp || cells < 1 || cells > 2)
1013 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001014 semsg(_(e_invalid_sign_text_str), text);
1015 return FAIL;
Bram Moolenaar03142362019-01-18 22:01:42 +01001016 }
1017
1018 vim_free(sp->sn_text);
1019 // Allocate one byte more if we need to pad up
1020 // with a space.
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001021 int len = (int)(endp - text + ((cells == 1) ? 1 : 0));
Bram Moolenaar03142362019-01-18 22:01:42 +01001022 sp->sn_text = vim_strnsave(text, len);
1023
1024 // For single character sign text, pad with a space.
1025 if (sp->sn_text != NULL && cells == 1)
Hirohito Higashi38972d82025-05-06 18:13:29 +02001026 STRCPY(sp->sn_text + len - 1, " ");
Bram Moolenaar03142362019-01-18 22:01:42 +01001027
1028 return OK;
1029}
1030
1031/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001032 * Define a new sign or update an existing sign
1033 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001034int
1035sign_define_by_name(char_u *name,
Hirohito Higashi38972d82025-05-06 18:13:29 +02001036 char_u *icon,
1037 char_u *linehl,
1038 char_u *text,
1039 char_u *texthl,
1040 char_u *culhl,
1041 char_u *numhl,
1042 int prio)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001043{
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001044 sign_T *sp_prev = NULL;
1045 sign_T *sp = sign_find(name, &sp_prev);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001046 if (sp == NULL)
1047 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001048 sp = alloc_new_sign(name);
1049 if (sp == NULL)
1050 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001051
Hirohito Higashi38972d82025-05-06 18:13:29 +02001052 // add the new sign to the list of signs
1053 if (sp_prev == NULL)
1054 first_sign = sp;
1055 else
1056 sp_prev->sn_next = sp;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001057 }
Bram Moolenaarbf0acff2020-01-09 21:01:59 +01001058 else
1059 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001060 win_T *wp = NULL;
1061 // Signs may already exist, a redraw is needed in windows with a
1062 // non-empty sign list.
1063 FOR_ALL_WINDOWS(wp)
1064 {
1065 if (wp->w_buffer->b_signlist != NULL)
1066 redraw_buf_later(wp->w_buffer, UPD_NOT_VALID);
1067 }
Bram Moolenaarbf0acff2020-01-09 21:01:59 +01001068 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001069
1070 // set values for a defined sign.
1071 if (icon != NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02001072 sign_define_init_icon(sp, icon);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001073
Bram Moolenaar03142362019-01-18 22:01:42 +01001074 if (text != NULL && (sign_define_init_text(sp, text) == FAIL))
Hirohito Higashi38972d82025-05-06 18:13:29 +02001075 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001076
LemonBoyb975ddf2024-07-06 18:04:09 +02001077 sp->sn_priority = prio;
1078
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001079 if (linehl != NULL)
Bram Moolenaar0bac5fc2021-12-05 17:45:49 +00001080 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001081 if (*linehl == NUL)
1082 sp->sn_line_hl = 0;
1083 else
1084 sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl));
Bram Moolenaar0bac5fc2021-12-05 17:45:49 +00001085 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001086
1087 if (texthl != NULL)
Bram Moolenaar0bac5fc2021-12-05 17:45:49 +00001088 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001089 if (*texthl == NUL)
1090 sp->sn_text_hl = 0;
1091 else
1092 sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl));
Bram Moolenaar0bac5fc2021-12-05 17:45:49 +00001093 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001094
Bram Moolenaare413ea02021-11-24 16:20:13 +00001095 if (culhl != NULL)
Bram Moolenaar0bac5fc2021-12-05 17:45:49 +00001096 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001097 if (*culhl == NUL)
1098 sp->sn_cul_hl = 0;
1099 else
1100 sp->sn_cul_hl = syn_check_group(culhl, (int)STRLEN(culhl));
Bram Moolenaar0bac5fc2021-12-05 17:45:49 +00001101 }
Bram Moolenaare413ea02021-11-24 16:20:13 +00001102
James McCoya80aad72021-12-22 19:45:28 +00001103 if (numhl != NULL)
1104 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001105 if (*numhl == NUL)
1106 sp->sn_num_hl = 0;
1107 else
1108 sp->sn_num_hl = syn_check_group(numhl, (int)STRLEN(numhl));
James McCoya80aad72021-12-22 19:45:28 +00001109 }
1110
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001111 return OK;
1112}
1113
1114/*
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001115 * Return TRUE if sign "name" exists.
1116 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001117int
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001118sign_exists_by_name(char_u *name)
1119{
1120 return sign_find(name, NULL) != NULL;
1121}
1122
1123/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001124 * Free the sign specified by 'name'.
1125 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001126int
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001127sign_undefine_by_name(char_u *name, int give_error)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001128{
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001129 sign_T *sp_prev = NULL;
1130 sign_T *sp = sign_find(name, &sp_prev);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001131 if (sp == NULL)
1132 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001133 if (give_error)
1134 semsg(_(e_unknown_sign_str), name);
1135 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001136 }
1137 sign_undefine(sp, sp_prev);
1138
1139 return OK;
1140}
1141
1142/*
1143 * List the signs matching 'name'
1144 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001145static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001146sign_list_by_name(char_u *name)
1147{
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001148 sign_T *sp = sign_find(name, NULL);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001149 if (sp != NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02001150 sign_list_defined(sp);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001151 else
Hirohito Higashi38972d82025-05-06 18:13:29 +02001152 semsg(_(e_unknown_sign_str), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001153}
1154
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001155static void
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001156may_force_numberwidth_recompute(buf_T *buf, int unplace)
1157{
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001158 tabpage_T *tp = NULL;
1159 win_T *wp = NULL;
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001160
1161 FOR_ALL_TAB_WINDOWS(tp, wp)
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001162 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001163 if (wp->w_buffer == buf && (wp->w_p_nu || wp->w_p_rnu) &&
1164 (unplace || wp->w_nrwidth_width < 2) &&
1165 (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u'))
1166 wp->w_nrwidth_line_count = 0;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001167 }
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001168}
1169
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001170/*
Bram Moolenaar8144acb2019-01-14 23:08:18 +01001171 * Place a sign at the specified file location or update a sign.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001172 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001173int
1174sign_place(int *sign_id,
Hirohito Higashi38972d82025-05-06 18:13:29 +02001175 char_u *sign_group,
1176 char_u *sign_name,
1177 buf_T *buf,
1178 linenr_T lnum,
1179 int prio)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001180{
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001181 // Check for reserved character '*' in group name
1182 if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0'))
Hirohito Higashi38972d82025-05-06 18:13:29 +02001183 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001184
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001185 sign_T *sp = NULL;
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001186 FOR_ALL_SIGNS(sp)
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001187 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001188 if (STRCMP(sp->sn_name, sign_name) == 0)
1189 break;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001190 }
1191
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001192 if (sp == NULL)
1193 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001194 semsg(_(e_unknown_sign_str), sign_name);
1195 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001196 }
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001197
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001198 if (*sign_id == 0)
Hirohito Higashi38972d82025-05-06 18:13:29 +02001199 *sign_id = sign_group_get_next_signid(buf, sign_group);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001200
LemonBoyb975ddf2024-07-06 18:04:09 +02001201 // Use the default priority value for this sign.
1202 if (prio == -1)
Hirohito Higashi38972d82025-05-06 18:13:29 +02001203 prio = (sp->sn_priority != -1) ? sp->sn_priority : SIGN_DEF_PRIO;
LemonBoyb975ddf2024-07-06 18:04:09 +02001204
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001205 if (lnum > 0)
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001206 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001207 // ":sign place {id} line={lnum} name={name} file={fname}":
1208 // place a sign
1209 buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr);
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001210 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001211 else
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001212 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001213 // ":sign place {id} file={fname}": change sign type and/or priority
1214 lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr,
1215 prio);
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001216 }
1217
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001218 if (lnum > 0)
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001219 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001220 redraw_buf_line_later(buf, lnum);
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001221
Hirohito Higashi38972d82025-05-06 18:13:29 +02001222 // When displaying signs in the 'number' column, if the width of the
1223 // number column is less than 2, then force recomputing the width.
1224 may_force_numberwidth_recompute(buf, FALSE);
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001225 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001226 else
1227 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001228 semsg(_(e_not_possible_to_change_sign_str), sign_name);
1229 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001230 }
1231
1232 return OK;
1233}
1234
1235/*
1236 * Unplace the specified sign
1237 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001238static int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001239sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum)
1240{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001241 if (buf->b_signlist == NULL) // No signs in the buffer
Hirohito Higashi38972d82025-05-06 18:13:29 +02001242 return OK;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001243
1244 if (sign_id == 0)
1245 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001246 // Delete all the signs in the specified buffer
1247 redraw_buf_later(buf, UPD_NOT_VALID);
1248 buf_delete_signs(buf, sign_group);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001249 }
1250 else
1251 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001252 // Delete only the specified signs
1253 linenr_T lnum = buf_delsign(buf, atlnum, sign_id, sign_group);
1254 if (lnum == 0)
1255 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001256 }
1257
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001258 // When all the signs in a buffer are removed, force recomputing the
1259 // number column width (if enabled) in all the windows displaying the
1260 // buffer if 'signcolumn' is set to 'number' in that window.
1261 if (buf->b_signlist == NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02001262 may_force_numberwidth_recompute(buf, TRUE);
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001263
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001264 return OK;
1265}
1266
1267/*
1268 * Unplace the sign at the current cursor line.
1269 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001270static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001271sign_unplace_at_cursor(char_u *groupname)
1272{
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001273 int id =
Hirohito Higashi38972d82025-05-06 18:13:29 +02001274 buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001275
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001276 if (id > 0)
Hirohito Higashi38972d82025-05-06 18:13:29 +02001277 sign_unplace(id, groupname, curwin->w_buffer, curwin->w_cursor.lnum);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001278 else
Hirohito Higashi38972d82025-05-06 18:13:29 +02001279 emsg(_(e_missing_sign_number));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001280}
1281
1282/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001283 * Jump to a sign.
1284 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001285static linenr_T
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001286sign_jump(int sign_id, char_u *sign_group, buf_T *buf)
1287{
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001288 linenr_T lnum = buf_findsign(buf, sign_id, sign_group);
1289 if (lnum <= 0)
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001290 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001291 semsg(_(e_invalid_sign_id_nr), sign_id);
1292 return -1;
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001293 }
1294
1295 // goto a sign ...
1296 if (buf_jump_open_win(buf) != NULL)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001297 { // ... in a current window
Hirohito Higashi38972d82025-05-06 18:13:29 +02001298 curwin->w_cursor.lnum = lnum;
1299 check_cursor_lnum();
1300 beginline(BL_WHITE);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001301 }
1302 else
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001303 { // ... not currently in a window
Hirohito Higashi38972d82025-05-06 18:13:29 +02001304 if (buf->b_fname == NULL)
1305 {
1306 emsg(_(e_cannot_jump_to_buffer_that_does_not_have_name));
1307 return -1;
1308 }
1309 char_u *cmd = alloc(STRLEN(buf->b_fname) + 25);
1310 if (cmd == NULL)
1311 return -1;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001312
Hirohito Higashi38972d82025-05-06 18:13:29 +02001313 sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname);
1314 do_cmdline_cmd(cmd);
1315 vim_free(cmd);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001316 }
1317# ifdef FEAT_FOLDING
1318 foldOpenCursor();
1319# endif
1320
1321 return lnum;
1322}
1323
1324/*
1325 * ":sign define {name} ..." command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001326 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001327static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001328sign_define_cmd(char_u *sign_name, char_u *cmdline)
1329{
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001330 char_u *arg = NULL;
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001331 char_u *p = cmdline;
1332 char_u *icon = NULL;
1333 char_u *text = NULL;
1334 char_u *linehl = NULL;
1335 char_u *texthl = NULL;
1336 char_u *culhl = NULL;
1337 char_u *numhl = NULL;
1338 int prio = -1;
1339 int failed = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001340
1341 // set values for a defined sign.
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001342 while (TRUE)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001343 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001344 arg = skipwhite(p);
1345 if (*arg == NUL)
1346 break;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001347
Hirohito Higashi38972d82025-05-06 18:13:29 +02001348 p = skiptowhite_esc(arg);
1349 if (STRNCMP(arg, "icon=", 5) == 0)
1350 {
1351 arg += 5;
1352 icon = vim_strnsave(arg, p - arg);
1353 }
1354 else if (STRNCMP(arg, "text=", 5) == 0)
1355 {
1356 arg += 5;
1357 text = vim_strnsave(arg, p - arg);
1358 }
1359 else if (STRNCMP(arg, "linehl=", 7) == 0)
1360 {
1361 arg += 7;
1362 linehl = vim_strnsave(arg, p - arg);
1363 }
1364 else if (STRNCMP(arg, "texthl=", 7) == 0)
1365 {
1366 arg += 7;
1367 texthl = vim_strnsave(arg, p - arg);
1368 }
1369 else if (STRNCMP(arg, "culhl=", 6) == 0)
1370 {
1371 arg += 6;
1372 culhl = vim_strnsave(arg, p - arg);
1373 }
1374 else if (STRNCMP(arg, "numhl=", 6) == 0)
1375 {
1376 arg += 6;
1377 numhl = vim_strnsave(arg, p - arg);
1378 }
1379 else if (STRNCMP(arg, "priority=", 9) == 0)
1380 {
1381 arg += 9;
1382 prio = atoi((char *)arg);
1383 }
1384 else
1385 {
1386 semsg(_(e_invalid_argument_str), arg);
1387 failed = TRUE;
1388 break;
1389 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001390 }
1391
1392 if (!failed)
Hirohito Higashi38972d82025-05-06 18:13:29 +02001393 sign_define_by_name(sign_name, icon, linehl, text, texthl, culhl, numhl,
1394 prio);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001395
1396 vim_free(icon);
1397 vim_free(text);
1398 vim_free(linehl);
1399 vim_free(texthl);
Bram Moolenaare413ea02021-11-24 16:20:13 +00001400 vim_free(culhl);
James McCoya80aad72021-12-22 19:45:28 +00001401 vim_free(numhl);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001402}
1403
1404/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001405 * ":sign place" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001406 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001407static void
1408sign_place_cmd(buf_T *buf,
Hirohito Higashi38972d82025-05-06 18:13:29 +02001409 linenr_T lnum,
1410 char_u *sign_name,
1411 int id,
1412 char_u *group,
1413 int prio)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001414{
1415 if (id <= 0)
1416 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001417 // List signs placed in a file/buffer
1418 // :sign place file={fname}
1419 // :sign place group={group} file={fname}
1420 // :sign place group=* file={fname}
1421 // :sign place buffer={nr}
1422 // :sign place group={group} buffer={nr}
1423 // :sign place group=* buffer={nr}
1424 // :sign place
1425 // :sign place group={group}
1426 // :sign place group=*
1427 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0'))
1428 emsg(_(e_invalid_argument));
1429 else
1430 sign_list_placed(buf, group);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001431 }
1432 else
1433 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001434 // Place a new sign
1435 if (sign_name == NULL || buf == NULL ||
1436 (group != NULL && *group == '\0'))
1437 {
1438 emsg(_(e_invalid_argument));
1439 return;
1440 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001441
Hirohito Higashi38972d82025-05-06 18:13:29 +02001442 sign_place(&id, group, sign_name, buf, lnum, prio);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001443 }
1444}
1445
1446/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001447 * ":sign unplace" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001448 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001449static void
1450sign_unplace_cmd(buf_T *buf,
Hirohito Higashi38972d82025-05-06 18:13:29 +02001451 linenr_T lnum,
1452 char_u *sign_name,
1453 int id,
1454 char_u *group)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001455{
1456 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0'))
1457 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001458 emsg(_(e_invalid_argument));
1459 return;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001460 }
1461
1462 if (id == -2)
1463 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001464 if (buf != NULL)
1465 {
1466 // :sign unplace * file={fname}
1467 // :sign unplace * group={group} file={fname}
1468 // :sign unplace * group=* file={fname}
1469 // :sign unplace * buffer={nr}
1470 // :sign unplace * group={group} buffer={nr}
1471 // :sign unplace * group=* buffer={nr}
1472 sign_unplace(0, group, buf, 0);
1473 }
1474 else
1475 {
1476 // :sign unplace *
1477 // :sign unplace * group={group}
1478 // :sign unplace * group=*
1479 FOR_ALL_BUFFERS(buf)
1480 {
1481 if (buf->b_signlist != NULL)
1482 buf_delete_signs(buf, group);
1483 }
1484 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001485 }
1486 else
1487 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001488 if (buf != NULL)
1489 {
1490 // :sign unplace {id} file={fname}
1491 // :sign unplace {id} group={group} file={fname}
1492 // :sign unplace {id} group=* file={fname}
1493 // :sign unplace {id} buffer={nr}
1494 // :sign unplace {id} group={group} buffer={nr}
1495 // :sign unplace {id} group=* buffer={nr}
1496 sign_unplace(id, group, buf, 0);
1497 }
1498 else
1499 {
1500 if (id == -1)
1501 {
1502 // :sign unplace group={group}
1503 // :sign unplace group=*
1504 sign_unplace_at_cursor(group);
1505 }
1506 else
1507 {
1508 // :sign unplace {id}
1509 // :sign unplace {id} group={group}
1510 // :sign unplace {id} group=*
1511 FOR_ALL_BUFFERS(buf)
1512 sign_unplace(id, group, buf, 0);
1513 }
1514 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001515 }
1516}
1517
1518/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001519 * Jump to a placed sign commands:
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001520 * :sign jump {id} file={fname}
1521 * :sign jump {id} buffer={nr}
1522 * :sign jump {id} group={group} file={fname}
1523 * :sign jump {id} group={group} buffer={nr}
1524 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001525static void
1526sign_jump_cmd(buf_T *buf,
Hirohito Higashi38972d82025-05-06 18:13:29 +02001527 linenr_T lnum,
1528 char_u *sign_name,
1529 int id,
1530 char_u *group)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001531{
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001532 if (sign_name == NULL && group == NULL && id == -1)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001533 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001534 emsg(_(e_argument_required));
1535 return;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001536 }
1537
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001538 if (buf == NULL || (group != NULL && *group == '\0') || lnum >= 0 ||
Hirohito Higashi38972d82025-05-06 18:13:29 +02001539 sign_name != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001540 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001541 // File or buffer is not specified or an empty group is used
1542 // or a line number or a sign name is specified.
1543 emsg(_(e_invalid_argument));
1544 return;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001545 }
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001546
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001547 (void)sign_jump(id, group, buf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001548}
1549
1550/*
1551 * Parse the command line arguments for the ":sign place", ":sign unplace" and
1552 * ":sign jump" commands.
1553 * The supported arguments are: line={lnum} name={name} group={group}
1554 * priority={prio} and file={fname} or buffer={nr}.
1555 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001556static int
1557parse_sign_cmd_args(int cmd,
Hirohito Higashi38972d82025-05-06 18:13:29 +02001558 char_u *arg,
1559 char_u **sign_name,
1560 int *signid,
1561 char_u **group,
1562 int *prio,
1563 buf_T **buf,
1564 linenr_T *lnum)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001565{
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001566 char_u *arg1 = arg;
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001567 char_u *filename = NULL;
1568 int lnum_arg = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001569
1570 // first arg could be placed sign id
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001571 if (VIM_ISDIGIT(*arg))
1572 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001573 *signid = getdigits(&arg);
1574 if (!VIM_ISWHITE(*arg) && *arg != NUL)
1575 {
1576 *signid = -1;
1577 arg = arg1;
1578 }
1579 else
1580 {
1581 arg = skipwhite(arg);
1582 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001583 }
1584
1585 while (*arg != NUL)
1586 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001587 if (STRNCMP(arg, "line=", 5) == 0)
1588 {
1589 arg += 5;
1590 *lnum = atoi((char *)arg);
1591 arg = skiptowhite(arg);
1592 lnum_arg = TRUE;
1593 }
1594 else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE)
1595 {
1596 if (*signid != -1)
1597 {
1598 emsg(_(e_invalid_argument));
1599 return FAIL;
1600 }
1601 *signid = -2;
1602 arg = skiptowhite(arg + 1);
1603 }
1604 else if (STRNCMP(arg, "name=", 5) == 0)
1605 {
1606 arg += 5;
1607 char_u *name = arg;
1608 arg = skiptowhite(arg);
1609 if (*arg != NUL)
1610 *arg++ = NUL;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001611
Hirohito Higashi38972d82025-05-06 18:13:29 +02001612 while (name[0] == '0' && name[1] != NUL)
1613 ++name;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001614
Hirohito Higashi38972d82025-05-06 18:13:29 +02001615 *sign_name = name;
1616 }
1617 else if (STRNCMP(arg, "group=", 6) == 0)
1618 {
1619 arg += 6;
1620 *group = arg;
1621 arg = skiptowhite(arg);
1622 if (*arg != NUL)
1623 *arg++ = NUL;
1624 }
1625 else if (STRNCMP(arg, "priority=", 9) == 0)
1626 {
1627 arg += 9;
1628 *prio = atoi((char *)arg);
1629 arg = skiptowhite(arg);
1630 }
1631 else if (STRNCMP(arg, "file=", 5) == 0)
1632 {
1633 arg += 5;
1634 filename = arg;
1635 *buf = buflist_findname_exp(arg);
1636 break;
1637 }
1638 else if (STRNCMP(arg, "buffer=", 7) == 0)
1639 {
1640 arg += 7;
1641 filename = arg;
1642 *buf = buflist_findnr((int)getdigits(&arg));
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001643
Hirohito Higashi38972d82025-05-06 18:13:29 +02001644 if (*skipwhite(arg) != NUL)
1645 semsg(_(e_trailing_characters_str), arg);
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001646
Hirohito Higashi38972d82025-05-06 18:13:29 +02001647 break;
1648 }
1649 else
1650 {
1651 emsg(_(e_invalid_argument));
1652 return FAIL;
1653 }
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001654
Hirohito Higashi38972d82025-05-06 18:13:29 +02001655 arg = skipwhite(arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001656 }
1657
1658 if (filename != NULL && *buf == NULL)
1659 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001660 semsg(_(e_invalid_buffer_name_str), filename);
1661 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001662 }
1663
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001664 // If the filename is not supplied for the sign place or the sign jump
1665 // command, then use the current buffer.
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001666 if (filename == NULL &&
Hirohito Higashi38972d82025-05-06 18:13:29 +02001667 ((cmd == SIGNCMD_PLACE && lnum_arg) || cmd == SIGNCMD_JUMP))
1668 *buf = curwin->w_buffer;
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001669
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001670 return OK;
1671}
1672
1673/*
1674 * ":sign" command
1675 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001676void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001677ex_sign(exarg_T *eap)
1678{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001679 char_u *arg = eap->arg;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001680
1681 // Parse the subcommand.
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001682 char_u *p = skiptowhite(arg);
1683 int idx = sign_cmd_idx(arg, p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001684 if (idx == SIGNCMD_LAST)
1685 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001686 semsg(_(e_unknown_sign_command_str), arg);
1687 return;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001688 }
1689 arg = skipwhite(p);
1690
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001691 if (idx > SIGNCMD_LIST)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001692 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001693 char_u *sign_name = NULL;
1694 int id = -1;
1695 char_u *group = NULL;
1696 int prio = -1;
1697 buf_T *buf = NULL;
1698 linenr_T lnum = -1;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001699
Hirohito Higashi38972d82025-05-06 18:13:29 +02001700 // Parse command line arguments
1701 if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio, &buf,
1702 &lnum) == FAIL)
1703 return;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001704
Hirohito Higashi38972d82025-05-06 18:13:29 +02001705 if (idx == SIGNCMD_PLACE)
1706 sign_place_cmd(buf, lnum, sign_name, id, group, prio);
1707 else if (idx == SIGNCMD_UNPLACE)
1708 sign_unplace_cmd(buf, lnum, sign_name, id, group);
1709 else if (idx == SIGNCMD_JUMP)
1710 sign_jump_cmd(buf, lnum, sign_name, id, group);
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001711
Hirohito Higashi38972d82025-05-06 18:13:29 +02001712 return;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001713 }
1714
1715 // Define, undefine or list signs.
1716 if (idx == SIGNCMD_LIST && *arg == NUL)
1717 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001718 // ":sign list": list all defined signs
1719 for (sign_T *sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next)
1720 sign_list_defined(sp);
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001721 }
1722 else if (*arg == NUL)
1723 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001724 emsg(_(e_missing_sign_name));
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001725 }
1726 else
1727 {
1728
Hirohito Higashi38972d82025-05-06 18:13:29 +02001729 // Isolate the sign name. If it's a number skip leading zeroes,
1730 // so that "099" and "99" are the same sign. But keep "0".
1731 p = skiptowhite(arg);
1732 if (*p != NUL)
1733 *p++ = NUL;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001734
Hirohito Higashi38972d82025-05-06 18:13:29 +02001735 while (arg[0] == '0' && arg[1] != NUL)
1736 ++arg;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001737
Hirohito Higashi38972d82025-05-06 18:13:29 +02001738 char_u *name = vim_strsave(arg);
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001739
Hirohito Higashi38972d82025-05-06 18:13:29 +02001740 if (idx == SIGNCMD_DEFINE)
1741 sign_define_cmd(name, p);
1742 else if (idx == SIGNCMD_LIST)
1743 // ":sign list {name}"
1744 sign_list_by_name(name);
1745 else
1746 // ":sign undefine {name}"
1747 sign_undefine_by_name(name, TRUE);
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001748
Hirohito Higashi38972d82025-05-06 18:13:29 +02001749 vim_free(name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001750 }
1751}
1752
1753/*
1754 * Return information about a specified sign
1755 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001756static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001757sign_getinfo(sign_T *sp, dict_T *retdict)
1758{
=?UTF-8?q?Dundar=20G=C3=B6c?=420fabc2022-01-28 15:28:04 +00001759 dict_add_string(retdict, "name", sp->sn_name);
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001760
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001761 if (sp->sn_icon != NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02001762 dict_add_string(retdict, "icon", sp->sn_icon);
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001763
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001764 if (sp->sn_text != NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02001765 dict_add_string(retdict, "text", sp->sn_text);
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001766
LemonBoyb975ddf2024-07-06 18:04:09 +02001767 if (sp->sn_priority > 0)
Hirohito Higashi38972d82025-05-06 18:13:29 +02001768 dict_add_number(retdict, "priority", sp->sn_priority);
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001769
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001770 if (sp->sn_line_hl > 0)
1771 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001772 char_u *p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1773 if (p == NULL)
1774 p = (char_u *)"NONE";
1775 dict_add_string(retdict, "linehl", p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001776 }
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001777
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001778 if (sp->sn_text_hl > 0)
1779 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001780 char_u *p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1781 if (p == NULL)
1782 p = (char_u *)"NONE";
1783 dict_add_string(retdict, "texthl", p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001784 }
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001785
Bram Moolenaare413ea02021-11-24 16:20:13 +00001786 if (sp->sn_cul_hl > 0)
1787 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001788 char_u *p = get_highlight_name_ext(NULL, sp->sn_cul_hl - 1, FALSE);
1789 if (p == NULL)
1790 p = (char_u *)"NONE";
1791 dict_add_string(retdict, "culhl", p);
Bram Moolenaare413ea02021-11-24 16:20:13 +00001792 }
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001793
James McCoya80aad72021-12-22 19:45:28 +00001794 if (sp->sn_num_hl > 0)
1795 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001796 char_u *p = get_highlight_name_ext(NULL, sp->sn_num_hl - 1, FALSE);
1797 if (p == NULL)
1798 p = (char_u *)"NONE";
1799 dict_add_string(retdict, "numhl", p);
James McCoya80aad72021-12-22 19:45:28 +00001800 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001801}
1802
1803/*
1804 * If 'name' is NULL, return a list of all the defined signs.
1805 * Otherwise, return information about the specified sign.
1806 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001807static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001808sign_getlist(char_u *name, list_T *retlist)
1809{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001810 sign_T *sp = first_sign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001811
1812 if (name != NULL)
1813 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001814 sp = sign_find(name, NULL);
1815 if (sp == NULL)
1816 return;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001817 }
1818
1819 for (; sp != NULL && !got_int; sp = sp->sn_next)
1820 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001821 dict_T *dict = dict_alloc_id(aid_sign_getlist);
1822 if (dict == NULL)
1823 return;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001824
Hirohito Higashi38972d82025-05-06 18:13:29 +02001825 if (list_append_dict(retlist, dict) == FAIL)
1826 return;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001827
Hirohito Higashi38972d82025-05-06 18:13:29 +02001828 sign_getinfo(sp, dict);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001829
Hirohito Higashi38972d82025-05-06 18:13:29 +02001830 if (name != NULL) // handle only the specified sign
1831 break;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001832 }
1833}
1834
1835/*
1836 * Returns information about signs placed in a buffer as list of dicts.
1837 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001838void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001839get_buffer_signs(buf_T *buf, list_T *l)
1840{
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001841 sign_entry_T *sign = NULL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001842 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1843 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001844 dict_T *d = sign_get_info(sign);
1845 if (d != NULL)
1846 list_append_dict(l, d);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001847 }
1848}
1849
1850/*
1851 * Return information about all the signs placed in a buffer
1852 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001853static void
1854sign_get_placed_in_buf(buf_T *buf,
Hirohito Higashi38972d82025-05-06 18:13:29 +02001855 linenr_T lnum,
1856 int sign_id,
1857 char_u *sign_group,
1858 list_T *retlist)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001859{
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001860 dict_T *d = dict_alloc_id(aid_sign_getplaced_dict);
1861 if (d == NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02001862 return;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001863
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001864 list_append_dict(retlist, d);
1865
1866 dict_add_number(d, "bufnr", (long)buf->b_fnum);
1867
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001868 list_T *l = list_alloc_id(aid_sign_getplaced_list);
1869 if (l == NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02001870 return;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001871
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001872 dict_add_list(d, "signs", l);
1873
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001874 sign_entry_T *sign = NULL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001875 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1876 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001877 if (!sign_in_group(sign, sign_group))
1878 continue;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001879
Hirohito Higashi38972d82025-05-06 18:13:29 +02001880 if ((lnum == 0 && sign_id == 0) ||
1881 (sign_id == 0 && lnum == sign->se_lnum) ||
1882 (lnum == 0 && sign_id == sign->se_id) ||
1883 (lnum == sign->se_lnum && sign_id == sign->se_id))
1884 {
1885 dict_T *sdict = sign_get_info(sign);
1886 if (sdict != NULL)
1887 list_append_dict(l, sdict);
1888 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001889 }
1890}
1891
1892/*
1893 * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the
1894 * sign placed at the line number. If 'lnum' is zero, return all the signs
1895 * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers.
1896 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001897static void
1898sign_get_placed(buf_T *buf,
Hirohito Higashi38972d82025-05-06 18:13:29 +02001899 linenr_T lnum,
1900 int sign_id,
1901 char_u *sign_group,
1902 list_T *retlist)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001903{
1904 if (buf != NULL)
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001905 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001906 sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist);
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001907 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001908 else
1909 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001910 FOR_ALL_BUFFERS(buf)
1911 {
1912 if (buf->b_signlist != NULL)
1913 sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist);
1914 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001915 }
1916}
1917
1918# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1919/*
1920 * Allocate the icons. Called when the GUI has started. Allows defining
1921 * signs before it starts.
1922 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001923void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001924sign_gui_started(void)
1925{
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001926 sign_T *sp = NULL;
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001927 FOR_ALL_SIGNS(sp)
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001928 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001929 if (sp->sn_icon != NULL)
1930 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001931 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001932}
1933# endif
1934
1935/*
1936 * List one sign.
1937 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001938static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001939sign_list_defined(sign_T *sp)
1940{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01001941 char lbuf[MSG_BUF_LEN];
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001942
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001943 smsg("sign %s", sp->sn_name);
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001944
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001945 if (sp->sn_icon != NULL)
1946 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001947 msg_puts(" icon=");
1948 msg_outtrans(sp->sn_icon);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001949# ifdef FEAT_SIGN_ICONS
Hirohito Higashi38972d82025-05-06 18:13:29 +02001950 if (sp->sn_image == NULL)
1951 msg_puts(_(" (NOT FOUND)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001952# else
Hirohito Higashi38972d82025-05-06 18:13:29 +02001953 msg_puts(_(" (not supported)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001954# endif
1955 }
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001956
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001957 if (sp->sn_text != NULL)
1958 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001959 msg_puts(" text=");
1960 msg_outtrans(sp->sn_text);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001961 }
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001962
LemonBoyb975ddf2024-07-06 18:04:09 +02001963 if (sp->sn_priority > 0)
1964 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001965 vim_snprintf(lbuf, MSG_BUF_LEN, " priority=%d", sp->sn_priority);
1966 msg_puts(lbuf);
LemonBoyb975ddf2024-07-06 18:04:09 +02001967 }
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001968
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001969 if (sp->sn_line_hl > 0)
1970 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001971 msg_puts(" linehl=");
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001972
Hirohito Higashi38972d82025-05-06 18:13:29 +02001973 char_u *p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1974 if (p == NULL)
1975 msg_puts("NONE");
1976 else
1977 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001978 }
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001979
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001980 if (sp->sn_text_hl > 0)
1981 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001982 msg_puts(" texthl=");
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001983
Hirohito Higashi38972d82025-05-06 18:13:29 +02001984 char_u *p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1985 if (p == NULL)
1986 msg_puts("NONE");
1987 else
1988 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001989 }
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001990
Bram Moolenaare413ea02021-11-24 16:20:13 +00001991 if (sp->sn_cul_hl > 0)
1992 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02001993 msg_puts(" culhl=");
Luca Saccarola78ca80f2024-11-24 14:25:06 +01001994
Hirohito Higashi38972d82025-05-06 18:13:29 +02001995 char_u *p = get_highlight_name_ext(NULL, sp->sn_cul_hl - 1, FALSE);
1996 if (p == NULL)
1997 msg_puts("NONE");
1998 else
1999 msg_puts((char *)p);
Bram Moolenaare413ea02021-11-24 16:20:13 +00002000 }
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002001
James McCoya80aad72021-12-22 19:45:28 +00002002 if (sp->sn_num_hl > 0)
2003 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002004 msg_puts(" numhl=");
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002005
Hirohito Higashi38972d82025-05-06 18:13:29 +02002006 char_u *p = get_highlight_name_ext(NULL, sp->sn_num_hl - 1, FALSE);
2007 if (p == NULL)
2008 msg_puts("NONE");
2009 else
2010 msg_puts((char *)p);
James McCoya80aad72021-12-22 19:45:28 +00002011 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002012}
2013
2014/*
2015 * Undefine a sign and free its memory.
2016 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002017static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002018sign_undefine(sign_T *sp, sign_T *sp_prev)
2019{
2020 vim_free(sp->sn_name);
2021 vim_free(sp->sn_icon);
2022# ifdef FEAT_SIGN_ICONS
2023 if (sp->sn_image != NULL)
2024 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002025 out_flush();
2026 gui_mch_destroy_sign(sp->sn_image);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002027 }
2028# endif
2029 vim_free(sp->sn_text);
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002030
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002031 if (sp_prev == NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002032 first_sign = sp->sn_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002033 else
Hirohito Higashi38972d82025-05-06 18:13:29 +02002034 sp_prev->sn_next = sp->sn_next;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002035
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002036 vim_free(sp);
2037}
2038
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002039# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002040void *
2041sign_get_image(int typenr) // the attribute which may have a sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002042{
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002043 sign_T *sp = NULL;
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002044 FOR_ALL_SIGNS(sp)
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002045 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002046 if (sp->sn_typenr == typenr)
2047 return sp->sn_image;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002048 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002049 return NULL;
2050}
2051# endif
2052
2053/*
2054 * Undefine/free all signs.
2055 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002056void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002057free_signs(void)
2058{
2059 while (first_sign != NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002060 sign_undefine(first_sign, NULL);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002061}
2062
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002063static enum
2064{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002065 EXP_SUBCMD, // expand :sign sub-commands
2066 EXP_DEFINE, // expand :sign define {name} args
2067 EXP_PLACE, // expand :sign place {id} args
2068 EXP_LIST, // expand :sign place args
2069 EXP_UNPLACE, // expand :sign unplace"
2070 EXP_SIGN_NAMES, // expand with name of placed signs
2071 EXP_SIGN_GROUPS // expand with name of placed sign groups
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002072} expand_what;
2073
2074/*
Bram Moolenaar3678f652019-02-17 14:50:25 +01002075 * Return the n'th sign name (used for command line completion)
2076 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002077static char_u *
Bram Moolenaar3678f652019-02-17 14:50:25 +01002078get_nth_sign_name(int idx)
2079{
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002080 int current_idx = 0;
2081 sign_T *sp = NULL;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002082
2083 // Complete with name of signs already defined
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002084 FOR_ALL_SIGNS(sp)
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002085 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002086 if (current_idx++ == idx)
2087 return sp->sn_name;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002088 }
Bram Moolenaar3678f652019-02-17 14:50:25 +01002089 return NULL;
2090}
2091
2092/*
2093 * Return the n'th sign group name (used for command line completion)
2094 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002095static char_u *
Bram Moolenaar3678f652019-02-17 14:50:25 +01002096get_nth_sign_group_name(int idx)
2097{
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002098 int current_idx = 0;
2099 int todo = (int)sg_table.ht_used;
2100 hashitem_T *hi = NULL;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002101
2102 // Complete with name of sign groups already defined
Yegappan Lakshmanan14113fd2023-03-07 17:13:51 +00002103 FOR_ALL_HASHTAB_ITEMS(&sg_table, hi, todo)
Bram Moolenaar3678f652019-02-17 14:50:25 +01002104 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002105 if (!HASHITEM_EMPTY(hi))
2106 {
2107 --todo;
2108 if (current_idx++ == idx)
2109 {
2110 signgroup_T *group = HI2SG(hi);
2111 return group->sg_name;
2112 }
2113 }
Bram Moolenaar3678f652019-02-17 14:50:25 +01002114 }
2115 return NULL;
2116}
2117
2118/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002119 * Function given to ExpandGeneric() to obtain the sign command
2120 * expansion.
2121 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002122char_u *
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002123get_sign_name(expand_T *xp UNUSED, int idx)
2124{
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002125 switch (expand_what)
2126 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002127 case EXP_SUBCMD:
2128 return (char_u *)cmds[idx];
2129 case EXP_DEFINE:
2130 {
2131 char *define_arg[] = { "culhl=", "icon=", "linehl=", "numhl=",
2132 "text=", "texthl=", "priority=", NULL };
2133 return (char_u *)define_arg[idx];
2134 }
2135 case EXP_PLACE:
2136 {
2137 char *place_arg[] = { "line=", "name=", "group=", "priority=",
2138 "file=", "buffer=", NULL };
2139 return (char_u *)place_arg[idx];
2140 }
2141 case EXP_LIST:
2142 {
2143 char *list_arg[] = { "group=", "file=", "buffer=", NULL };
2144 return (char_u *)list_arg[idx];
2145 }
2146 case EXP_UNPLACE:
2147 {
2148 char *unplace_arg[] = { "group=", "file=", "buffer=", NULL };
2149 return (char_u *)unplace_arg[idx];
2150 }
2151 case EXP_SIGN_NAMES:
2152 return get_nth_sign_name(idx);
2153 case EXP_SIGN_GROUPS:
2154 return get_nth_sign_group_name(idx);
2155 default:
2156 return NULL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002157 }
2158}
2159
2160/*
2161 * Handle command line completion for :sign command.
2162 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002163void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002164set_context_in_sign_cmd(expand_T *xp, char_u *arg)
2165{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002166 char_u *p;
2167 char_u *end_subcmd;
2168 char_u *last;
2169 int cmd_idx;
2170 char_u *begin_subcmd_args;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002171
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002172 // Default: expand subcommands.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002173 xp->xp_context = EXPAND_SIGN;
2174 expand_what = EXP_SUBCMD;
2175 xp->xp_pattern = arg;
2176
2177 end_subcmd = skiptowhite(arg);
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002178 // expand subcmd name
2179 // :sign {subcmd}<CTRL-D>
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002180 if (*end_subcmd == NUL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002181 return;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002182
2183 cmd_idx = sign_cmd_idx(arg, end_subcmd);
2184
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002185 // :sign {subcmd} {subcmd_args}
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002186 // |
2187 // begin_subcmd_args
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002188 begin_subcmd_args = skipwhite(end_subcmd);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002189
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002190 // expand last argument of subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002191
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002192 // :sign define {name} {args}...
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002193 // |
2194 // p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002195
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002196 // Loop until reaching last argument.
Bram Moolenaar3678f652019-02-17 14:50:25 +01002197 p = begin_subcmd_args;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002198 do
2199 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002200 p = skipwhite(p);
2201 last = p;
2202 p = skiptowhite(p);
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002203 }
2204 while (*p != NUL);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002205
2206 p = vim_strchr(last, '=');
2207
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002208 // :sign define {name} {args}... {last}=
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002209 // | |
2210 // last p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002211 if (p == NULL)
2212 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002213 // Expand last argument name (before equal sign).
2214 xp->xp_pattern = last;
2215 switch (cmd_idx)
2216 {
2217 case SIGNCMD_DEFINE:
2218 expand_what = EXP_DEFINE;
2219 break;
2220 case SIGNCMD_PLACE:
2221 // List placed signs
2222 if (VIM_ISDIGIT(*begin_subcmd_args))
2223 // :sign place {id} {args}...
2224 expand_what = EXP_PLACE;
2225 else
2226 // :sign place {args}...
2227 expand_what = EXP_LIST;
2228 break;
2229 case SIGNCMD_LIST:
2230 case SIGNCMD_UNDEFINE:
2231 // :sign list <CTRL-D>
2232 // :sign undefine <CTRL-D>
2233 expand_what = EXP_SIGN_NAMES;
2234 break;
2235 case SIGNCMD_JUMP:
2236 case SIGNCMD_UNPLACE:
2237 expand_what = EXP_UNPLACE;
2238 break;
2239 default:
2240 xp->xp_context = EXPAND_NOTHING;
2241 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002242 }
2243 else
2244 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002245 // Expand last argument value (after equal sign).
2246 xp->xp_pattern = p + 1;
2247 switch (cmd_idx)
2248 {
2249 case SIGNCMD_DEFINE:
2250 if (STRNCMP(last, "texthl", 6) == 0 ||
2251 STRNCMP(last, "linehl", 6) == 0 ||
2252 STRNCMP(last, "culhl", 5) == 0 ||
2253 STRNCMP(last, "numhl", 5) == 0)
2254 xp->xp_context = EXPAND_HIGHLIGHT;
2255 else if (STRNCMP(last, "icon", 4) == 0)
2256 xp->xp_context = EXPAND_FILES;
2257 else
2258 xp->xp_context = EXPAND_NOTHING;
2259 break;
2260 case SIGNCMD_PLACE:
2261 if (STRNCMP(last, "name", 4) == 0)
2262 expand_what = EXP_SIGN_NAMES;
2263 else if (STRNCMP(last, "group", 5) == 0)
2264 expand_what = EXP_SIGN_GROUPS;
2265 else if (STRNCMP(last, "file", 4) == 0)
2266 xp->xp_context = EXPAND_BUFFERS;
2267 else
2268 xp->xp_context = EXPAND_NOTHING;
2269 break;
2270 case SIGNCMD_UNPLACE:
2271 case SIGNCMD_JUMP:
2272 if (STRNCMP(last, "group", 5) == 0)
2273 expand_what = EXP_SIGN_GROUPS;
2274 else if (STRNCMP(last, "file", 4) == 0)
2275 xp->xp_context = EXPAND_BUFFERS;
2276 else
2277 xp->xp_context = EXPAND_NOTHING;
2278 break;
2279 default:
2280 xp->xp_context = EXPAND_NOTHING;
2281 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002282 }
2283}
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002284
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002285/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002286 * Define a sign using the attributes in 'dict'. Returns 0 on success and -1 on
2287 * failure.
2288 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002289static int
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002290sign_define_from_dict(char_u *name_arg, dict_T *dict)
2291{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002292 char_u *name = NULL;
2293 char_u *icon = NULL;
2294 char_u *linehl = NULL;
2295 char_u *text = NULL;
2296 char_u *texthl = NULL;
2297 char_u *culhl = NULL;
2298 char_u *numhl = NULL;
2299 int prio = -1;
2300 int retval = -1;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002301
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002302 if (name_arg == NULL && dict == NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002303 return retval;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002304
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002305 if (name_arg == NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002306 name = dict_get_string(dict, "name", TRUE);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002307 else
Hirohito Higashi38972d82025-05-06 18:13:29 +02002308 name = vim_strsave(name_arg);
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002309
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002310 if (name == NULL || name[0] == NUL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002311 goto cleanup;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002312
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002313 if (dict != NULL)
2314 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002315 icon = dict_get_string(dict, "icon", TRUE);
2316 linehl = dict_get_string(dict, "linehl", TRUE);
2317 text = dict_get_string(dict, "text", TRUE);
2318 texthl = dict_get_string(dict, "texthl", TRUE);
2319 culhl = dict_get_string(dict, "culhl", TRUE);
2320 numhl = dict_get_string(dict, "numhl", TRUE);
2321 prio = dict_get_number_def(dict, "priority", -1);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002322 }
2323
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002324 if (sign_define_by_name(name, icon, linehl, text, texthl, culhl, numhl,
Hirohito Higashi38972d82025-05-06 18:13:29 +02002325 prio) == OK)
2326 retval = 0;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002327
2328cleanup:
2329 vim_free(name);
2330 vim_free(icon);
2331 vim_free(linehl);
2332 vim_free(text);
2333 vim_free(texthl);
Bram Moolenaare413ea02021-11-24 16:20:13 +00002334 vim_free(culhl);
James McCoya80aad72021-12-22 19:45:28 +00002335 vim_free(numhl);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002336
2337 return retval;
2338}
2339
2340/*
2341 * Define multiple signs using attributes from list 'l' and store the return
2342 * values in 'retlist'.
2343 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002344static void
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002345sign_define_multiple(list_T *l, list_T *retlist)
2346{
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002347 listitem_T *li = NULL;
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002348 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002349 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002350 int retval = -1;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002351
Hirohito Higashi38972d82025-05-06 18:13:29 +02002352 if (li->li_tv.v_type == VAR_DICT)
2353 retval = sign_define_from_dict(NULL, li->li_tv.vval.v_dict);
2354 else
2355 emsg(_(e_dictionary_required));
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002356
Hirohito Higashi38972d82025-05-06 18:13:29 +02002357 list_append_number(retlist, retval);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002358 }
2359}
2360
2361/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002362 * "sign_define()" function
2363 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002364void
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002365f_sign_define(typval_T *argvars, typval_T *rettv)
2366{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002367 if (in_vim9script() && (check_for_string_or_list_arg(argvars, 0) == FAIL ||
Hirohito Higashi38972d82025-05-06 18:13:29 +02002368 check_for_opt_dict_arg(argvars, 1) == FAIL))
2369 return;
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002370
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002371 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2372 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002373 // Define multiple signs
2374 if (rettv_list_alloc(rettv) == FAIL)
2375 return;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002376
Hirohito Higashi38972d82025-05-06 18:13:29 +02002377 sign_define_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2378 return;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002379 }
2380
2381 // Define a single sign
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002382 rettv->vval.v_number = -1;
2383
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002384 char_u *name = tv_get_string_chk(&argvars[0]);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002385 if (name == NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002386 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002387
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +01002388 if (check_for_opt_dict_arg(argvars, 1) == FAIL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002389 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002390
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002391 rettv->vval.v_number = sign_define_from_dict(
Hirohito Higashi38972d82025-05-06 18:13:29 +02002392 name, argvars[1].v_type == VAR_DICT ? argvars[1].vval.v_dict : NULL);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002393}
2394
2395/*
2396 * "sign_getdefined()" function
2397 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002398void
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002399f_sign_getdefined(typval_T *argvars, typval_T *rettv)
2400{
Bram Moolenaar93a10962022-06-16 11:42:09 +01002401 if (rettv_list_alloc_id(rettv, aid_sign_getdefined) == FAIL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002402 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002403
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002404 if (in_vim9script() && check_for_opt_string_arg(argvars, 0) == FAIL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002405 return;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002406
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002407 char_u *name = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002408 if (argvars[0].v_type != VAR_UNKNOWN)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002409 name = tv_get_string(&argvars[0]);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002410
2411 sign_getlist(name, rettv->vval.v_list);
2412}
2413
2414/*
2415 * "sign_getplaced()" function
2416 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002417void
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002418f_sign_getplaced(typval_T *argvars, typval_T *rettv)
2419{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002420 buf_T *buf = NULL;
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002421 linenr_T lnum = 0;
2422 int sign_id = 0;
2423 char_u *group = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002424
Bram Moolenaar93a10962022-06-16 11:42:09 +01002425 if (rettv_list_alloc_id(rettv, aid_sign_getplaced) == FAIL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002426 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002427
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002428 if (in_vim9script() && (check_for_opt_buffer_arg(argvars, 0) == FAIL ||
Hirohito Higashi38972d82025-05-06 18:13:29 +02002429 (argvars[0].v_type != VAR_UNKNOWN &&
2430 check_for_opt_dict_arg(argvars, 1) == FAIL)))
2431 return;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002432
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002433 if (argvars[0].v_type != VAR_UNKNOWN)
2434 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002435 // get signs placed in the specified buffer
2436 buf = get_buf_arg(&argvars[0]);
2437 if (buf == NULL)
2438 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002439
Hirohito Higashi38972d82025-05-06 18:13:29 +02002440 if (argvars[1].v_type != VAR_UNKNOWN)
2441 {
2442 if (check_for_nonnull_dict_arg(argvars, 1) == FAIL)
2443 return;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002444
Hirohito Higashi38972d82025-05-06 18:13:29 +02002445 dictitem_T *di = NULL;
2446 dict_T *dict = argvars[1].vval.v_dict;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002447
Hirohito Higashi38972d82025-05-06 18:13:29 +02002448 if ((di = dict_find(dict, (char_u *)"lnum", -1)) != NULL)
2449 {
2450 // get signs placed at this line
2451 int notanum = FALSE;
2452 tv_get_number_chk(&di->di_tv, &notanum);
2453 if (notanum)
2454 return;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002455
Hirohito Higashi38972d82025-05-06 18:13:29 +02002456 lnum = tv_get_lnum(&di->di_tv);
2457 }
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002458
Hirohito Higashi38972d82025-05-06 18:13:29 +02002459 if ((di = dict_find(dict, (char_u *)"id", -1)) != NULL)
2460 {
2461 // get sign placed with this identifier
2462 int notanum = FALSE;
2463 sign_id = (int)tv_get_number_chk(&di->di_tv, &notanum);
2464 if (notanum)
2465 return;
2466 }
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002467
Hirohito Higashi38972d82025-05-06 18:13:29 +02002468 if ((di = dict_find(dict, (char_u *)"group", -1)) != NULL)
2469 {
2470 group = tv_get_string_chk(&di->di_tv);
2471 if (group == NULL)
2472 return;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002473
Hirohito Higashi38972d82025-05-06 18:13:29 +02002474 if (*group == '\0') // empty string means global group
2475 group = NULL;
2476 }
2477 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002478 }
2479
2480 sign_get_placed(buf, lnum, sign_id, group, rettv->vval.v_list);
2481}
2482
2483/*
2484 * "sign_jump()" function
2485 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002486void
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002487f_sign_jump(typval_T *argvars, typval_T *rettv)
2488{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002489 char_u *sign_group = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002490 rettv->vval.v_number = -1;
2491
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002492 if (in_vim9script() && (check_for_number_arg(argvars, 0) == FAIL ||
Hirohito Higashi38972d82025-05-06 18:13:29 +02002493 check_for_string_arg(argvars, 1) == FAIL ||
2494 check_for_buffer_arg(argvars, 2) == FAIL))
2495 return;
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002496
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002497 int notanum = FALSE;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002498 // Sign identifier
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002499 int sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002500 if (notanum)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002501 return;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002502
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002503 if (sign_id <= 0)
2504 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002505 emsg(_(e_invalid_argument));
2506 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002507 }
2508
2509 // Sign group
2510 sign_group = tv_get_string_chk(&argvars[1]);
2511 if (sign_group == NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002512 return;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002513
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002514 if (sign_group[0] == '\0')
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002515 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002516 sign_group = NULL; // global sign group
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002517 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002518 else
2519 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002520 sign_group = vim_strsave(sign_group);
2521 if (sign_group == NULL)
2522 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002523 }
2524
2525 // Buffer to place the sign
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002526 buf_T *buf = get_buf_arg(&argvars[2]);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002527 if (buf == NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002528 goto cleanup;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002529
2530 rettv->vval.v_number = sign_jump(sign_id, sign_group, buf);
2531
2532cleanup:
2533 vim_free(sign_group);
2534}
2535
2536/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002537 * Place a new sign using the values specified in dict 'dict'. Returns the sign
2538 * identifier if successfully placed, otherwise returns 0.
2539 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002540static int
2541sign_place_from_dict(typval_T *id_tv,
Hirohito Higashi38972d82025-05-06 18:13:29 +02002542 typval_T *group_tv,
2543 typval_T *name_tv,
2544 typval_T *buf_tv,
2545 dict_T *dict)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002546{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002547 int sign_id = 0;
2548 char_u *group = NULL;
2549 char_u *sign_name = NULL;
2550 buf_T *buf = NULL;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002551 dictitem_T *di = NULL;
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002552 linenr_T lnum = 0;
2553 int prio = -1;
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002554 int ret_sign_id = -1;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002555
2556 // sign identifier
2557 if (id_tv == NULL)
2558 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002559 di = dict_find(dict, (char_u *)"id", -1);
2560 if (di != NULL)
2561 id_tv = &di->di_tv;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002562 }
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002563
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002564 if (id_tv == NULL)
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002565 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002566 sign_id = 0;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002567 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002568 else
2569 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002570 int notanum = FALSE;
2571 sign_id = tv_get_number_chk(id_tv, &notanum);
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002572
Hirohito Higashi38972d82025-05-06 18:13:29 +02002573 if (notanum)
2574 return -1;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002575
Hirohito Higashi38972d82025-05-06 18:13:29 +02002576 if (sign_id < 0)
2577 {
2578 emsg(_(e_invalid_argument));
2579 return -1;
2580 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002581 }
2582
2583 // sign group
2584 if (group_tv == NULL)
2585 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002586 di = dict_find(dict, (char_u *)"group", -1);
2587 if (di != NULL)
2588 group_tv = &di->di_tv;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002589 }
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002590
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002591 if (group_tv == NULL)
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002592 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002593 group = NULL; // global group
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002594 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002595 else
2596 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002597 group = tv_get_string_chk(group_tv);
2598 if (group == NULL)
2599 goto cleanup;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002600
Hirohito Higashi38972d82025-05-06 18:13:29 +02002601 if (group[0] == '\0') // global sign group
2602 {
2603 group = NULL;
2604 }
2605 else
2606 {
2607 group = vim_strsave(group);
2608 if (group == NULL)
2609 return -1;
2610 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002611 }
2612
2613 // sign name
2614 if (name_tv == NULL)
2615 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002616 di = dict_find(dict, (char_u *)"name", -1);
2617 if (di != NULL)
2618 name_tv = &di->di_tv;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002619 }
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002620
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002621 if (name_tv == NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002622 goto cleanup;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002623
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002624 sign_name = tv_get_string_chk(name_tv);
2625 if (sign_name == NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002626 goto cleanup;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002627
2628 // buffer to place the sign
2629 if (buf_tv == NULL)
2630 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002631 di = dict_find(dict, (char_u *)"buffer", -1);
2632 if (di != NULL)
2633 buf_tv = &di->di_tv;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002634 }
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002635
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002636 if (buf_tv == NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002637 goto cleanup;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002638
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002639 buf = get_buf_arg(buf_tv);
2640 if (buf == NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002641 goto cleanup;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002642
2643 // line number of the sign
2644 di = dict_find(dict, (char_u *)"lnum", -1);
2645 if (di != NULL)
2646 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002647 lnum = tv_get_lnum(&di->di_tv);
2648 if (lnum <= 0)
2649 {
2650 emsg(_(e_invalid_argument));
2651 goto cleanup;
2652 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002653 }
2654
2655 // sign priority
2656 di = dict_find(dict, (char_u *)"priority", -1);
2657 if (di != NULL)
2658 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002659 int notanum = FALSE;
2660 prio = (int)tv_get_number_chk(&di->di_tv, &notanum);
2661 if (notanum)
2662 goto cleanup;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002663 }
2664
2665 if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002666 ret_sign_id = sign_id;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002667
2668cleanup:
2669 vim_free(group);
2670
2671 return ret_sign_id;
2672}
2673
2674/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002675 * "sign_place()" function
2676 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002677void
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002678f_sign_place(typval_T *argvars, typval_T *rettv)
2679{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002680 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002681 rettv->vval.v_number = -1;
2682
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002683 if (in_vim9script() && (check_for_number_arg(argvars, 0) == FAIL ||
Hirohito Higashi38972d82025-05-06 18:13:29 +02002684 check_for_string_arg(argvars, 1) == FAIL ||
2685 check_for_string_arg(argvars, 2) == FAIL ||
2686 check_for_buffer_arg(argvars, 3) == FAIL ||
2687 check_for_opt_dict_arg(argvars, 4) == FAIL))
2688 return;
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02002689
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +01002690 if (argvars[4].v_type != VAR_UNKNOWN)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002691 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002692 if (check_for_nonnull_dict_arg(argvars, 4) == FAIL)
2693 return;
2694 dict = argvars[4].vval.v_dict;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002695 }
2696
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002697 rettv->vval.v_number = sign_place_from_dict(&argvars[0], &argvars[1],
Hirohito Higashi38972d82025-05-06 18:13:29 +02002698 &argvars[2], &argvars[3], dict);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002699}
2700
2701/*
2702 * "sign_placelist()" function. Place multiple signs.
2703 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002704void
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002705f_sign_placelist(typval_T *argvars, typval_T *rettv)
2706{
Bram Moolenaar93a10962022-06-16 11:42:09 +01002707 if (rettv_list_alloc(rettv) == FAIL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002708 return;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002709
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002710 if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002711 return;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002712
Bram Moolenaard83392a2022-09-01 12:22:46 +01002713 if (check_for_list_arg(argvars, 0) == FAIL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002714 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002715
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002716 // Process the List of sign attributes
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002717 listitem_T *li = NULL;
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002718 FOR_ALL_LIST_ITEMS(argvars[0].vval.v_list, li)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002719 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002720 int sign_id = -1;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002721
Hirohito Higashi38972d82025-05-06 18:13:29 +02002722 if (li->li_tv.v_type == VAR_DICT)
2723 sign_id = sign_place_from_dict(NULL, NULL, NULL, NULL,
2724 li->li_tv.vval.v_dict);
2725 else
2726 emsg(_(e_dictionary_required));
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002727
Hirohito Higashi38972d82025-05-06 18:13:29 +02002728 list_append_number(rettv->vval.v_list, sign_id);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002729 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002730}
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002731
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002732/*
2733 * Undefine multiple signs
2734 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002735static void
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002736sign_undefine_multiple(list_T *l, list_T *retlist)
2737{
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002738 listitem_T *li = NULL;
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002739 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002740 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002741 int retval = -1;
2742 char_u *name = tv_get_string_chk(&li->li_tv);
2743 if (name != NULL && (sign_undefine_by_name(name, TRUE) == OK))
2744 retval = 0;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002745
Hirohito Higashi38972d82025-05-06 18:13:29 +02002746 list_append_number(retlist, retval);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002747 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002748}
2749
2750/*
2751 * "sign_undefine()" function
2752 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002753void
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002754f_sign_undefine(typval_T *argvars, typval_T *rettv)
2755{
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002756
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002757 if (in_vim9script() && check_for_opt_string_or_list_arg(argvars, 0) == FAIL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002758 return;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002759
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002760 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2761 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002762 // Undefine multiple signs
2763 if (rettv_list_alloc(rettv) == FAIL)
2764 return;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002765
Hirohito Higashi38972d82025-05-06 18:13:29 +02002766 sign_undefine_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2767 return;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002768 }
2769
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002770 rettv->vval.v_number = -1;
2771
2772 if (argvars[0].v_type == VAR_UNKNOWN)
2773 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002774 // Free all the signs
2775 free_signs();
2776 rettv->vval.v_number = 0;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002777 }
2778 else
2779 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002780 // Free only the specified sign
2781 char_u *name = tv_get_string_chk(&argvars[0]);
2782 if (name == NULL)
2783 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002784
Hirohito Higashi38972d82025-05-06 18:13:29 +02002785 if (sign_undefine_by_name(name, TRUE) == OK)
2786 rettv->vval.v_number = 0;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002787 }
2788}
2789
2790/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002791 * Unplace the sign with attributes specified in 'dict'. Returns 0 on success
2792 * and -1 on failure.
2793 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002794static int
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002795sign_unplace_from_dict(typval_T *group_tv, dict_T *dict)
2796{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002797 int sign_id = 0;
2798 buf_T *buf = NULL;
2799 char_u *group = NULL;
2800 int retval = -1;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002801
2802 // sign group
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002803 group = (group_tv != NULL) ? tv_get_string(group_tv)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002804 : dict_get_string(dict, "group", FALSE);
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002805
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002806 if (group != NULL)
2807 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002808 if (group[0] == '\0') // global sign group
2809 {
2810 group = NULL;
2811 }
2812 else
2813 {
2814 group = vim_strsave(group);
2815 if (group == NULL)
2816 return retval;
2817 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002818 }
2819
2820 if (dict != NULL)
2821 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002822 dictitem_T *di = dict_find(dict, (char_u *)"buffer", -1);
2823 if (di != NULL)
2824 {
2825 buf = get_buf_arg(&di->di_tv);
2826 if (buf == NULL)
2827 goto cleanup;
2828 }
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002829
Hirohito Higashi38972d82025-05-06 18:13:29 +02002830 if (dict_has_key(dict, "id"))
2831 {
2832 sign_id = dict_get_number(dict, "id");
2833 if (sign_id <= 0)
2834 {
2835 emsg(_(e_invalid_argument));
2836 goto cleanup;
2837 }
2838 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002839 }
2840
2841 if (buf == NULL)
2842 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002843 // Delete the sign in all the buffers
2844 retval = 0;
2845 FOR_ALL_BUFFERS(buf)
2846 if (sign_unplace(sign_id, group, buf, 0) != OK)
2847 retval = -1;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002848 }
2849 else if (sign_unplace(sign_id, group, buf, 0) == OK)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002850 retval = 0;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002851
2852cleanup:
2853 vim_free(group);
2854
2855 return retval;
2856}
2857
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002858sign_entry_T *
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002859get_first_valid_sign(win_T *wp)
2860{
2861 sign_entry_T *sign = wp->w_buffer->b_signlist;
2862
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002863# ifdef FEAT_PROP_POPUP
Bram Moolenaar72570732019-11-30 14:21:53 +01002864 while (sign != NULL && !sign_group_for_window(sign, wp))
Hirohito Higashi38972d82025-05-06 18:13:29 +02002865 sign = sign->se_next;
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002866# endif
2867 return sign;
2868}
2869
2870/*
2871 * Return TRUE when window "wp" has a column to draw signs in.
2872 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002873int
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002874signcolumn_on(win_T *wp)
2875{
2876 // If 'signcolumn' is set to 'number', signs are displayed in the 'number'
2877 // column (if present). Otherwise signs are to be displayed in the sign
2878 // column.
2879 if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u')
Hirohito Higashi38972d82025-05-06 18:13:29 +02002880 return get_first_valid_sign(wp) != NULL && !wp->w_p_nu && !wp->w_p_rnu;
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002881
2882 if (*wp->w_p_scl == 'n')
Hirohito Higashi38972d82025-05-06 18:13:29 +02002883 return FALSE;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002884
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002885 if (*wp->w_p_scl == 'y')
Hirohito Higashi38972d82025-05-06 18:13:29 +02002886 return TRUE;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002887
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002888 return (get_first_valid_sign(wp) != NULL
2889# ifdef FEAT_NETBEANS_INTG
Hirohito Higashi38972d82025-05-06 18:13:29 +02002890 || wp->w_buffer->b_has_sign_column
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002891# endif
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002892 );
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002893}
2894
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002895/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002896 * "sign_unplace()" function
2897 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002898void
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002899f_sign_unplace(typval_T *argvars, typval_T *rettv)
2900{
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002901 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002902 rettv->vval.v_number = -1;
2903
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002904 if ((check_for_string_arg(argvars, 0) == FAIL ||
Hirohito Higashi38972d82025-05-06 18:13:29 +02002905 check_for_opt_dict_arg(argvars, 1) == FAIL))
2906 return;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002907
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002908 if (argvars[1].v_type != VAR_UNKNOWN)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002909 dict = argvars[1].vval.v_dict;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002910
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002911 rettv->vval.v_number = sign_unplace_from_dict(&argvars[0], dict);
2912}
2913
2914/*
2915 * "sign_unplacelist()" function
2916 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +01002917void
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002918f_sign_unplacelist(typval_T *argvars, typval_T *rettv)
2919{
Bram Moolenaar93a10962022-06-16 11:42:09 +01002920 if (rettv_list_alloc(rettv) == FAIL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002921 return;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002922
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002923 if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002924 return;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02002925
Bram Moolenaard83392a2022-09-01 12:22:46 +01002926 if (check_for_list_arg(argvars, 0) == FAIL)
Hirohito Higashi38972d82025-05-06 18:13:29 +02002927 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002928
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002929 listitem_T *li = NULL;
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002930 FOR_ALL_LIST_ITEMS(argvars[0].vval.v_list, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002931 {
Hirohito Higashi38972d82025-05-06 18:13:29 +02002932 int retval = -1;
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002933
Hirohito Higashi38972d82025-05-06 18:13:29 +02002934 if (li->li_tv.v_type == VAR_DICT)
2935 retval = sign_unplace_from_dict(NULL, li->li_tv.vval.v_dict);
2936 else
2937 emsg(_(e_dictionary_required));
Luca Saccarola78ca80f2024-11-24 14:25:06 +01002938
Hirohito Higashi38972d82025-05-06 18:13:29 +02002939 list_append_number(rettv->vval.v_list, retval);
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002940 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002941}
2942
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002943#endif // FEAT_SIGNS