blob: c208a0e56d992ece517c14e42752289833a70e8b [file] [log] [blame]
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * sign.c: functions for managing signs
12 */
13
14#include "vim.h"
15
16#if defined(FEAT_SIGNS) || defined(PROTO)
17
18/*
19 * Struct to hold the sign properties.
20 */
21typedef struct sign sign_T;
22
23struct sign
24{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +010025 sign_T *sn_next; // next sign in list
26 int sn_typenr; // type number of sign
27 char_u *sn_name; // name of sign
28 char_u *sn_icon; // name of pixmap
Bram Moolenaarbbea4702019-01-01 13:20:31 +010029# ifdef FEAT_SIGN_ICONS
Bram Moolenaar6b7b7192019-01-11 13:42:41 +010030 void *sn_image; // icon image
Bram Moolenaarbbea4702019-01-01 13:20:31 +010031# endif
Bram Moolenaar6b7b7192019-01-11 13:42:41 +010032 char_u *sn_text; // text used instead of pixmap
33 int sn_line_hl; // highlight ID for line
34 int sn_text_hl; // highlight ID for text
Bram Moolenaarbbea4702019-01-01 13:20:31 +010035};
36
37static sign_T *first_sign = NULL;
38static int next_sign_typenr = 1;
39
40static void sign_list_defined(sign_T *sp);
41static void sign_undefine(sign_T *sp, sign_T *sp_prev);
42
43static char *cmds[] = {
44 "define",
45# define SIGNCMD_DEFINE 0
46 "undefine",
47# define SIGNCMD_UNDEFINE 1
48 "list",
49# define SIGNCMD_LIST 2
50 "place",
51# define SIGNCMD_PLACE 3
52 "unplace",
53# define SIGNCMD_UNPLACE 4
54 "jump",
55# define SIGNCMD_JUMP 5
56 NULL
57# define SIGNCMD_LAST 6
58};
59
Bram Moolenaaraeea7212020-04-02 18:50:46 +020060#define FOR_ALL_SIGNS(sp) \
61 for ((sp) = first_sign; (sp) != NULL; (sp) = (sp)->sn_next)
62
Bram Moolenaarbbea4702019-01-01 13:20:31 +010063static hashtab_T sg_table; // sign group (signgroup_T) hashtable
64static int next_sign_id = 1; // next sign id in the global group
65
66/*
67 * Initialize data needed for managing signs
68 */
69 void
70init_signs(void)
71{
72 hash_init(&sg_table); // sign group hash table
73}
74
75/*
76 * A new sign in group 'groupname' is added. If the group is not present,
77 * create it. Otherwise reference the group.
78 */
79 static signgroup_T *
80sign_group_ref(char_u *groupname)
81{
82 hash_T hash;
83 hashitem_T *hi;
84 signgroup_T *group;
85
86 hash = hash_hash(groupname);
87 hi = hash_lookup(&sg_table, groupname, hash);
88 if (HASHITEM_EMPTY(hi))
89 {
90 // new group
Bram Moolenaar47ed5532019-08-08 20:49:14 +020091 group = alloc(offsetof(signgroup_T, sg_name) + STRLEN(groupname) + 1);
Bram Moolenaarbbea4702019-01-01 13:20:31 +010092 if (group == NULL)
93 return NULL;
94 STRCPY(group->sg_name, groupname);
Bram Moolenaar6656c2e2019-10-24 15:00:04 +020095 group->sg_refcount = 1;
96 group->sg_next_sign_id = 1;
Bram Moolenaarbbea4702019-01-01 13:20:31 +010097 hash_add_item(&sg_table, hi, group->sg_name, hash);
98 }
99 else
100 {
101 // existing group
102 group = HI2SG(hi);
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200103 group->sg_refcount++;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100104 }
105
106 return group;
107}
108
109/*
110 * A sign in group 'groupname' is removed. If all the signs in this group are
111 * removed, then remove the group.
112 */
113 static void
114sign_group_unref(char_u *groupname)
115{
116 hashitem_T *hi;
117 signgroup_T *group;
118
119 hi = hash_find(&sg_table, groupname);
120 if (!HASHITEM_EMPTY(hi))
121 {
122 group = HI2SG(hi);
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200123 group->sg_refcount--;
124 if (group->sg_refcount == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100125 {
126 // All the signs in this group are removed
127 hash_remove(&sg_table, hi);
128 vim_free(group);
129 }
130 }
131}
132
133/*
134 * Returns TRUE if 'sign' is in 'group'.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200135 * A sign can either be in the global group (sign->se_group == NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100136 * or in a named group. If 'group' is '*', then the sign is part of the group.
137 */
138 static int
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200139sign_in_group(sign_entry_T *sign, char_u *group)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100140{
141 return ((group != NULL && STRCMP(group, "*") == 0)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200142 || (group == NULL && sign->se_group == NULL)
143 || (group != NULL && sign->se_group != NULL
Bram Moolenaar72570732019-11-30 14:21:53 +0100144 && STRCMP(group, sign->se_group->sg_name) == 0));
145}
146
147/*
148 * Return TRUE if "sign" is to be displayed in window "wp".
149 * If the group name starts with "PopUp" it only shows in a popup window.
150 */
151 static int
152sign_group_for_window(sign_entry_T *sign, win_T *wp)
153{
154 int for_popup = sign->se_group != NULL
155 && STRNCMP("PopUp", sign->se_group->sg_name, 5) == 0;
156
157 return WIN_IS_POPUP(wp) ? for_popup : !for_popup;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100158}
159
160/*
161 * Get the next free sign identifier in the specified group
162 */
163 static int
164sign_group_get_next_signid(buf_T *buf, char_u *groupname)
165{
166 int id = 1;
167 signgroup_T *group = NULL;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200168 sign_entry_T *sign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100169 hashitem_T *hi;
170 int found = FALSE;
171
172 if (groupname != NULL)
173 {
174 hi = hash_find(&sg_table, groupname);
175 if (HASHITEM_EMPTY(hi))
176 return id;
177 group = HI2SG(hi);
178 }
179
Bram Moolenaarb5443cc2019-01-15 20:19:40 +0100180 // Search for the next usable sign identifier
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100181 while (!found)
182 {
183 if (group == NULL)
184 id = next_sign_id++; // global group
185 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200186 id = group->sg_next_sign_id++;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100187
188 // Check whether this sign is already placed in the buffer
189 found = TRUE;
190 FOR_ALL_SIGNS_IN_BUF(buf, sign)
191 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200192 if (id == sign->se_id && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100193 {
194 found = FALSE; // sign identifier is in use
195 break;
196 }
197 }
198 }
199
200 return id;
201}
202
203/*
204 * Insert a new sign into the signlist for buffer 'buf' between the 'prev' and
205 * 'next' signs.
206 */
207 static void
208insert_sign(
209 buf_T *buf, // buffer to store sign in
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200210 sign_entry_T *prev, // previous sign entry
211 sign_entry_T *next, // next sign entry
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100212 int id, // sign ID
213 char_u *group, // sign group; NULL for global group
214 int prio, // sign priority
215 linenr_T lnum, // line number which gets the mark
216 int typenr) // typenr of sign we are adding
217{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200218 sign_entry_T *newsign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100219
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200220 newsign = lalloc_id(sizeof(sign_entry_T), FALSE, aid_insert_sign);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100221 if (newsign != NULL)
222 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200223 newsign->se_id = id;
224 newsign->se_lnum = lnum;
225 newsign->se_typenr = typenr;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100226 if (group != NULL)
227 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200228 newsign->se_group = sign_group_ref(group);
229 if (newsign->se_group == NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100230 {
231 vim_free(newsign);
232 return;
233 }
234 }
235 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200236 newsign->se_group = NULL;
237 newsign->se_priority = prio;
238 newsign->se_next = next;
239 newsign->se_prev = prev;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100240 if (next != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200241 next->se_prev = newsign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100242
243 if (prev == NULL)
244 {
245 // 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, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200250 changed_line_abv_curs();
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100251 }
252
253 // first sign in signlist
254 buf->b_signlist = newsign;
255#ifdef FEAT_NETBEANS_INTG
256 if (netbeans_active())
257 buf->b_has_sign_column = TRUE;
258#endif
259 }
260 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200261 prev->se_next = newsign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100262 }
263}
264
265/*
266 * Insert a new sign sorted by line number and sign priority.
267 */
268 static void
269insert_sign_by_lnum_prio(
270 buf_T *buf, // buffer to store sign in
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200271 sign_entry_T *prev, // previous sign entry
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100272 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
277{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200278 sign_entry_T *sign;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100279
280 // keep signs sorted by lnum and by priority: insert new sign at
281 // the proper position in the list for this lnum.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200282 while (prev != NULL && prev->se_lnum == lnum && prev->se_priority <= prio)
283 prev = prev->se_prev;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100284 if (prev == NULL)
285 sign = buf->b_signlist;
286 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200287 sign = prev->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100288
289 insert_sign(buf, prev, sign, id, group, prio, lnum, typenr);
290}
291
292/*
Bram Moolenaar4e038572019-07-04 18:28:35 +0200293 * Lookup a sign by typenr. Returns NULL if sign is not found.
294 */
295 static sign_T *
296find_sign_by_typenr(int typenr)
297{
298 sign_T *sp;
299
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200300 FOR_ALL_SIGNS(sp)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200301 if (sp->sn_typenr == typenr)
302 return sp;
303 return NULL;
304}
305
306/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100307 * Get the name of a sign by its typenr.
308 */
309 static char_u *
310sign_typenr2name(int typenr)
311{
312 sign_T *sp;
313
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200314 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100315 if (sp->sn_typenr == typenr)
316 return sp->sn_name;
317 return (char_u *)_("[Deleted]");
318}
319
320/*
321 * Return information about a sign in a Dict
322 */
323 static dict_T *
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200324sign_get_info(sign_entry_T *sign)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100325{
326 dict_T *d;
327
328 if ((d = dict_alloc_id(aid_sign_getinfo)) == NULL)
329 return NULL;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200330 dict_add_number(d, "id", sign->se_id);
331 dict_add_string(d, "group", (sign->se_group == NULL) ?
332 (char_u *)"" : sign->se_group->sg_name);
333 dict_add_number(d, "lnum", sign->se_lnum);
334 dict_add_string(d, "name", sign_typenr2name(sign->se_typenr));
335 dict_add_number(d, "priority", sign->se_priority);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100336
337 return d;
338}
339
340/*
Bram Moolenaar64416122019-06-07 21:37:13 +0200341 * Sort the signs placed on the same line as "sign" by priority. Invoked after
342 * changing the priority of an already placed sign. Assumes the signs in the
343 * buffer are sorted by line number and priority.
344 */
345 static void
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200346sign_sort_by_prio_on_line(buf_T *buf, sign_entry_T *sign)
Bram Moolenaar64416122019-06-07 21:37:13 +0200347{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200348 sign_entry_T *p = NULL;
Bram Moolenaar64416122019-06-07 21:37:13 +0200349
350 // If there is only one sign in the buffer or only one sign on the line or
351 // the sign is already sorted by priority, then return.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200352 if ((sign->se_prev == NULL
353 || sign->se_prev->se_lnum != sign->se_lnum
354 || sign->se_prev->se_priority > sign->se_priority)
355 && (sign->se_next == NULL
356 || sign->se_next->se_lnum != sign->se_lnum
357 || sign->se_next->se_priority < sign->se_priority))
Bram Moolenaar64416122019-06-07 21:37:13 +0200358 return;
359
360 // One or more signs on the same line as 'sign'
361 // Find a sign after which 'sign' should be inserted
362
363 // First search backward for a sign with higher priority on the same line
364 p = sign;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200365 while (p->se_prev != NULL && p->se_prev->se_lnum == sign->se_lnum
366 && p->se_prev->se_priority <= sign->se_priority)
367 p = p->se_prev;
Bram Moolenaar64416122019-06-07 21:37:13 +0200368
369 if (p == sign)
370 {
371 // Sign not found. Search forward for a sign with priority just before
372 // 'sign'.
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200373 p = sign->se_next;
374 while (p->se_next != NULL && p->se_next->se_lnum == sign->se_lnum
375 && p->se_next->se_priority > sign->se_priority)
376 p = p->se_next;
Bram Moolenaar64416122019-06-07 21:37:13 +0200377 }
378
379 // Remove 'sign' from the list
380 if (buf->b_signlist == sign)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200381 buf->b_signlist = sign->se_next;
382 if (sign->se_prev != NULL)
383 sign->se_prev->se_next = sign->se_next;
384 if (sign->se_next != NULL)
385 sign->se_next->se_prev = sign->se_prev;
386 sign->se_prev = NULL;
387 sign->se_next = NULL;
Bram Moolenaar64416122019-06-07 21:37:13 +0200388
389 // Re-insert 'sign' at the right place
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200390 if (p->se_priority <= sign->se_priority)
Bram Moolenaar64416122019-06-07 21:37:13 +0200391 {
392 // 'sign' has a higher priority and should be inserted before 'p'
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200393 sign->se_prev = p->se_prev;
394 sign->se_next = p;
395 p->se_prev = sign;
396 if (sign->se_prev != NULL)
397 sign->se_prev->se_next = sign;
Bram Moolenaar64416122019-06-07 21:37:13 +0200398 if (buf->b_signlist == p)
399 buf->b_signlist = sign;
400 }
401 else
402 {
403 // 'sign' has a lower priority and should be inserted after 'p'
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200404 sign->se_prev = p;
405 sign->se_next = p->se_next;
406 p->se_next = sign;
407 if (sign->se_next != NULL)
408 sign->se_next->se_prev = sign;
Bram Moolenaar64416122019-06-07 21:37:13 +0200409 }
410}
411
412/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100413 * Add the sign into the signlist. Find the right spot to do it though.
414 */
415 static void
416buf_addsign(
417 buf_T *buf, // buffer to store sign in
418 int id, // sign ID
419 char_u *groupname, // sign group
420 int prio, // sign priority
421 linenr_T lnum, // line number which gets the mark
422 int typenr) // typenr of sign we are adding
423{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200424 sign_entry_T *sign; // a sign in the signlist
425 sign_entry_T *prev; // the previous sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100426
427 prev = NULL;
428 FOR_ALL_SIGNS_IN_BUF(buf, sign)
429 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200430 if (lnum == sign->se_lnum && id == sign->se_id
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100431 && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100432 {
433 // Update an existing sign
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200434 sign->se_typenr = typenr;
435 sign->se_priority = prio;
Bram Moolenaar64416122019-06-07 21:37:13 +0200436 sign_sort_by_prio_on_line(buf, sign);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100437 return;
438 }
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200439 else if (lnum < sign->se_lnum)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100440 {
441 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio,
442 lnum, typenr);
443 return;
444 }
445 prev = sign;
446 }
447
448 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr);
449 return;
450}
451
452/*
453 * For an existing, placed sign "markId" change the type to "typenr".
454 * Returns the line number of the sign, or zero if the sign is not found.
455 */
456 static linenr_T
457buf_change_sign_type(
458 buf_T *buf, // buffer to store sign in
459 int markId, // sign ID
460 char_u *group, // sign group
Bram Moolenaar809ce4d2019-07-13 21:21:40 +0200461 int typenr, // typenr of sign we are adding
462 int prio) // sign priority
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100463{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200464 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100465
466 FOR_ALL_SIGNS_IN_BUF(buf, sign)
467 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200468 if (sign->se_id == markId && sign_in_group(sign, group))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100469 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200470 sign->se_typenr = typenr;
471 sign->se_priority = prio;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +0200472 sign_sort_by_prio_on_line(buf, sign);
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200473 return sign->se_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100474 }
475 }
476
477 return (linenr_T)0;
478}
479
480/*
Bram Moolenaar4e038572019-07-04 18:28:35 +0200481 * Return the attributes of the first sign placed on line 'lnum' in buffer
482 * 'buf'. Used when refreshing the screen. Returns TRUE if a sign is found on
483 * 'lnum', FALSE otherwise.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100484 */
485 int
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100486buf_get_signattrs(win_T *wp, linenr_T lnum, sign_attrs_T *sattr)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100487{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200488 sign_entry_T *sign;
489 sign_T *sp;
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100490 buf_T *buf = wp->w_buffer;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200491
Bram Moolenaara80faa82020-04-12 19:37:17 +0200492 CLEAR_POINTER(sattr);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100493
494 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200495 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200496 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200497 // Signs are sorted by line number in the buffer. No need to check
498 // for signs after the specified line number 'lnum'.
499 break;
500
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100501 if (sign->se_lnum == lnum
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100502# ifdef FEAT_PROP_POPUP
Bram Moolenaar72570732019-11-30 14:21:53 +0100503 && sign_group_for_window(sign, wp)
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +0100504# endif
505 )
Bram Moolenaar4e038572019-07-04 18:28:35 +0200506 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200507 sattr->sat_typenr = sign->se_typenr;
508 sp = find_sign_by_typenr(sign->se_typenr);
Bram Moolenaar4e038572019-07-04 18:28:35 +0200509 if (sp == NULL)
510 return FALSE;
511
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100512# ifdef FEAT_SIGN_ICONS
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200513 sattr->sat_icon = sp->sn_image;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100514# endif
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200515 sattr->sat_text = sp->sn_text;
516 if (sattr->sat_text != NULL && sp->sn_text_hl > 0)
517 sattr->sat_texthl = syn_id2attr(sp->sn_text_hl);
Bram Moolenaar4e038572019-07-04 18:28:35 +0200518 if (sp->sn_line_hl > 0)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200519 sattr->sat_linehl = syn_id2attr(sp->sn_line_hl);
Bram Moolenaar2f122842020-08-31 23:18:00 +0200520 sattr->sat_priority = sign->se_priority;
Bram Moolenaara2f6e422020-02-19 17:13:04 +0100521
522 // If there is another sign next with the same priority, may
523 // combine the text and the line highlighting.
524 if (sign->se_next != NULL
525 && sign->se_next->se_priority == sign->se_priority
526 && sign->se_next->se_lnum == sign->se_lnum)
527 {
528 sign_T *next_sp = find_sign_by_typenr(sign->se_next->se_typenr);
529
530 if (next_sp != NULL)
531 {
532 if (sattr->sat_icon == NULL && sattr->sat_text == NULL)
533 {
534# ifdef FEAT_SIGN_ICONS
535 sattr->sat_icon = next_sp->sn_image;
536# endif
537 sattr->sat_text = next_sp->sn_text;
538 }
539 if (sp->sn_text_hl <= 0 && next_sp->sn_text_hl > 0)
540 sattr->sat_texthl = syn_id2attr(next_sp->sn_text_hl);
541 if (sp->sn_line_hl <= 0 && next_sp->sn_line_hl > 0)
542 sattr->sat_linehl = syn_id2attr(next_sp->sn_line_hl);
543 }
544 }
Bram Moolenaar4e038572019-07-04 18:28:35 +0200545 return TRUE;
546 }
547 }
548 return FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100549}
550
551/*
552 * Delete sign 'id' in group 'group' from buffer 'buf'.
553 * If 'id' is zero, then delete all the signs in group 'group'. Otherwise
554 * delete only the specified sign.
555 * If 'group' is '*', then delete the sign in all the groups. If 'group' is
556 * NULL, then delete the sign in the global group. Otherwise delete the sign in
557 * the specified group.
558 * Returns the line number of the deleted sign. If multiple signs are deleted,
559 * then returns the line number of the last sign deleted.
560 */
561 linenr_T
562buf_delsign(
563 buf_T *buf, // buffer sign is stored in
564 linenr_T atlnum, // sign at this line, 0 - at any line
565 int id, // sign id
566 char_u *group) // sign group
567{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200568 sign_entry_T **lastp; // pointer to pointer to current sign
569 sign_entry_T *sign; // a sign in a b_signlist
570 sign_entry_T *next; // the next sign in a b_signlist
571 linenr_T lnum; // line number whose sign was deleted
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100572
573 lastp = &buf->b_signlist;
574 lnum = 0;
575 for (sign = buf->b_signlist; sign != NULL; sign = next)
576 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200577 next = sign->se_next;
578 if ((id == 0 || sign->se_id == id)
579 && (atlnum == 0 || sign->se_lnum == atlnum)
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100580 && sign_in_group(sign, group))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100581
582 {
583 *lastp = next;
584 if (next != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200585 next->se_prev = sign->se_prev;
586 lnum = sign->se_lnum;
587 if (sign->se_group != NULL)
588 sign_group_unref(sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100589 vim_free(sign);
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100590 redraw_buf_line_later(buf, lnum);
591
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100592 // Check whether only one sign needs to be deleted
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100593 // If deleting a sign with a specific identifier in a particular
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100594 // group or deleting any sign at a particular line number, delete
595 // only one sign.
596 if (group == NULL
597 || (*group != '*' && id != 0)
598 || (*group == '*' && atlnum != 0))
599 break;
600 }
601 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200602 lastp = &sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100603 }
604
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100605 // When deleting the last sign the cursor position may change, because the
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100606 // sign columns no longer shows. And the 'signcolumn' may be hidden.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100607 if (buf->b_signlist == NULL)
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100608 {
609 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200610 changed_line_abv_curs();
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100611 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100612
613 return lnum;
614}
615
616
617/*
618 * Find the line number of the sign with the requested id in group 'group'. If
619 * the sign does not exist, return 0 as the line number. This will still let
620 * the correct file get loaded.
621 */
622 int
623buf_findsign(
624 buf_T *buf, // buffer to store sign in
625 int id, // sign ID
626 char_u *group) // sign group
627{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200628 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100629
630 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200631 if (sign->se_id == id && sign_in_group(sign, group))
632 return sign->se_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100633
634 return 0;
635}
636
637/*
638 * Return the sign at line 'lnum' in buffer 'buf'. Returns NULL if a sign is
639 * not found at the line. If 'groupname' is NULL, searches in the global group.
640 */
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200641 static sign_entry_T *
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100642buf_getsign_at_line(
643 buf_T *buf, // buffer whose sign we are searching for
644 linenr_T lnum, // line number of sign
645 char_u *groupname) // sign group name
646{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200647 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100648
649 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200650 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200651 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200652 // Signs are sorted by line number in the buffer. No need to check
653 // for signs after the specified line number 'lnum'.
654 break;
655
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200656 if (sign->se_lnum == lnum && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100657 return sign;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200658 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100659
660 return NULL;
661}
662
663/*
664 * Return the identifier of the sign at line number 'lnum' in buffer 'buf'.
665 */
666 int
667buf_findsign_id(
668 buf_T *buf, // buffer whose sign we are searching for
669 linenr_T lnum, // line number of sign
670 char_u *groupname) // sign group name
671{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200672 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100673
674 sign = buf_getsign_at_line(buf, lnum, groupname);
675 if (sign != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200676 return sign->se_id;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100677
678 return 0;
679}
680
681# if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
682/*
683 * See if a given type of sign exists on a specific line.
684 */
685 int
686buf_findsigntype_id(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100687 buf_T *buf, // buffer whose sign we are searching for
688 linenr_T lnum, // line number of sign
689 int typenr) // sign type number
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100690{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200691 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100692
693 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200694 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200695 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200696 // Signs are sorted by line number in the buffer. No need to check
697 // for signs after the specified line number 'lnum'.
698 break;
699
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200700 if (sign->se_lnum == lnum && sign->se_typenr == typenr)
701 return sign->se_id;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200702 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100703
704 return 0;
705}
706
707
708# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
709/*
710 * Return the number of icons on the given line.
711 */
712 int
713buf_signcount(buf_T *buf, linenr_T lnum)
714{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200715 sign_entry_T *sign; // a sign in the signlist
716 int count = 0;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100717
718 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200719 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200720 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200721 // Signs are sorted by line number in the buffer. No need to check
722 // for signs after the specified line number 'lnum'.
723 break;
724
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200725 if (sign->se_lnum == lnum)
726 if (sign_get_image(sign->se_typenr) != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100727 count++;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200728 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100729
730 return count;
731}
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100732# endif // FEAT_SIGN_ICONS
733# endif // FEAT_NETBEANS_INTG
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100734
735/*
736 * Delete signs in group 'group' in buffer "buf". If 'group' is '*', then
737 * delete all the signs.
738 */
739 void
740buf_delete_signs(buf_T *buf, char_u *group)
741{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200742 sign_entry_T *sign;
743 sign_entry_T **lastp; // pointer to pointer to current sign
744 sign_entry_T *next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100745
746 // When deleting the last sign need to redraw the windows to remove the
747 // sign column. Not when curwin is NULL (this means we're exiting).
748 if (buf->b_signlist != NULL && curwin != NULL)
749 {
750 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200751 changed_line_abv_curs();
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100752 }
753
754 lastp = &buf->b_signlist;
755 for (sign = buf->b_signlist; sign != NULL; sign = next)
756 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200757 next = sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100758 if (sign_in_group(sign, group))
759 {
760 *lastp = next;
761 if (next != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200762 next->se_prev = sign->se_prev;
763 if (sign->se_group != NULL)
764 sign_group_unref(sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100765 vim_free(sign);
766 }
767 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200768 lastp = &sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100769 }
770}
771
772/*
773 * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers.
774 */
775 static void
776sign_list_placed(buf_T *rbuf, char_u *sign_group)
777{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200778 buf_T *buf;
779 sign_entry_T *sign;
780 char lbuf[MSG_BUF_LEN];
781 char group[MSG_BUF_LEN];
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100782
Bram Moolenaar32526b32019-01-19 17:43:09 +0100783 msg_puts_title(_("\n--- Signs ---"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100784 msg_putchar('\n');
785 if (rbuf == NULL)
786 buf = firstbuf;
787 else
788 buf = rbuf;
789 while (buf != NULL && !got_int)
790 {
791 if (buf->b_signlist != NULL)
792 {
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100793 vim_snprintf(lbuf, MSG_BUF_LEN, _("Signs for %s:"), buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100794 msg_puts_attr(lbuf, HL_ATTR(HLF_D));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100795 msg_putchar('\n');
796 }
797 FOR_ALL_SIGNS_IN_BUF(buf, sign)
798 {
799 if (got_int)
800 break;
801 if (!sign_in_group(sign, sign_group))
802 continue;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200803 if (sign->se_group != NULL)
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100804 vim_snprintf(group, MSG_BUF_LEN, _(" group=%s"),
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200805 sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100806 else
807 group[0] = '\0';
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100808 vim_snprintf(lbuf, MSG_BUF_LEN,
809 _(" line=%ld id=%d%s name=%s priority=%d"),
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200810 (long)sign->se_lnum, sign->se_id, group,
811 sign_typenr2name(sign->se_typenr), sign->se_priority);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100812 msg_puts(lbuf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100813 msg_putchar('\n');
814 }
815 if (rbuf != NULL)
816 break;
817 buf = buf->b_next;
818 }
819}
820
821/*
822 * Adjust a placed sign for inserted/deleted lines.
823 */
824 void
825sign_mark_adjust(
826 linenr_T line1,
827 linenr_T line2,
828 long amount,
829 long amount_after)
830{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200831 sign_entry_T *sign; // a sign in a b_signlist
832 linenr_T new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100833
834 FOR_ALL_SIGNS_IN_BUF(curbuf, sign)
835 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100836 // Ignore changes to lines after the sign
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200837 if (sign->se_lnum < line1)
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100838 continue;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200839 new_lnum = sign->se_lnum;
840 if (sign->se_lnum >= line1 && sign->se_lnum <= line2)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100841 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100842 if (amount != MAXLNUM)
843 new_lnum += amount;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100844 }
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200845 else if (sign->se_lnum > line2)
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100846 // Lines inserted or deleted before the sign
847 new_lnum += amount_after;
848
849 // If the new sign line number is past the last line in the buffer,
850 // then don't adjust the line number. Otherwise, it will always be past
851 // the last line and will not be visible.
852 if (new_lnum <= curbuf->b_ml.ml_line_count)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200853 sign->se_lnum = new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100854 }
855}
856
857/*
858 * Find index of a ":sign" subcmd from its name.
859 * "*end_cmd" must be writable.
860 */
861 static int
862sign_cmd_idx(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100863 char_u *begin_cmd, // begin of sign subcmd
864 char_u *end_cmd) // just after sign subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100865{
866 int idx;
867 char save = *end_cmd;
868
869 *end_cmd = NUL;
870 for (idx = 0; ; ++idx)
871 if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0)
872 break;
873 *end_cmd = save;
874 return idx;
875}
876
877/*
878 * Find a sign by name. Also returns pointer to the previous sign.
879 */
880 static sign_T *
881sign_find(char_u *name, sign_T **sp_prev)
882{
883 sign_T *sp;
884
885 if (sp_prev != NULL)
886 *sp_prev = NULL;
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200887 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100888 {
889 if (STRCMP(sp->sn_name, name) == 0)
890 break;
891 if (sp_prev != NULL)
892 *sp_prev = sp;
893 }
894
895 return sp;
896}
897
898/*
Bram Moolenaar03142362019-01-18 22:01:42 +0100899 * Allocate a new sign
900 */
901 static sign_T *
902alloc_new_sign(char_u *name)
903{
904 sign_T *sp;
905 sign_T *lp;
906 int start = next_sign_typenr;
907
908 // Allocate a new sign.
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200909 sp = alloc_clear_id(sizeof(sign_T), aid_sign_define_by_name);
Bram Moolenaar03142362019-01-18 22:01:42 +0100910 if (sp == NULL)
911 return NULL;
912
913 // Check that next_sign_typenr is not already being used.
914 // This only happens after wrapping around. Hopefully
915 // another one got deleted and we can use its number.
916 for (lp = first_sign; lp != NULL; )
917 {
918 if (lp->sn_typenr == next_sign_typenr)
919 {
920 ++next_sign_typenr;
921 if (next_sign_typenr == MAX_TYPENR)
922 next_sign_typenr = 1;
923 if (next_sign_typenr == start)
924 {
925 vim_free(sp);
926 emsg(_("E612: Too many signs defined"));
927 return NULL;
928 }
929 lp = first_sign; // start all over
930 continue;
931 }
932 lp = lp->sn_next;
933 }
934
935 sp->sn_typenr = next_sign_typenr;
936 if (++next_sign_typenr == MAX_TYPENR)
937 next_sign_typenr = 1; // wrap around
938
939 sp->sn_name = vim_strsave(name);
940 if (sp->sn_name == NULL) // out of memory
941 {
942 vim_free(sp);
943 return NULL;
944 }
945
946 return sp;
947}
948
949/*
950 * Initialize the icon information for a new sign
951 */
952 static void
953sign_define_init_icon(sign_T *sp, char_u *icon)
954{
955 vim_free(sp->sn_icon);
956 sp->sn_icon = vim_strsave(icon);
957 backslash_halve(sp->sn_icon);
958# ifdef FEAT_SIGN_ICONS
959 if (gui.in_use)
960 {
961 out_flush();
962 if (sp->sn_image != NULL)
963 gui_mch_destroy_sign(sp->sn_image);
964 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
965 }
966# endif
967}
968
969/*
970 * Initialize the text for a new sign
971 */
972 static int
973sign_define_init_text(sign_T *sp, char_u *text)
974{
975 char_u *s;
976 char_u *endp;
977 int cells;
978 int len;
979
980 endp = text + (int)STRLEN(text);
981
982 // Remove backslashes so that it is possible to use a space.
983 for (s = text; s + 1 < endp; ++s)
984 if (*s == '\\')
985 {
986 STRMOVE(s, s + 1);
987 --endp;
988 }
989
990 // Count cells and check for non-printable chars
Bram Moolenaar03142362019-01-18 22:01:42 +0100991 if (has_mbyte)
992 {
993 cells = 0;
994 for (s = text; s < endp; s += (*mb_ptr2len)(s))
995 {
996 if (!vim_isprintc((*mb_ptr2char)(s)))
997 break;
998 cells += (*mb_ptr2cells)(s);
999 }
1000 }
1001 else
Bram Moolenaar03142362019-01-18 22:01:42 +01001002 {
1003 for (s = text; s < endp; ++s)
1004 if (!vim_isprintc(*s))
1005 break;
1006 cells = (int)(s - text);
1007 }
1008
1009 // Currently sign text must be one or two display cells
1010 if (s != endp || cells < 1 || cells > 2)
1011 {
1012 semsg(_("E239: Invalid sign text: %s"), text);
1013 return FAIL;
1014 }
1015
1016 vim_free(sp->sn_text);
1017 // Allocate one byte more if we need to pad up
1018 // with a space.
1019 len = (int)(endp - text + ((cells == 1) ? 1 : 0));
1020 sp->sn_text = vim_strnsave(text, len);
1021
1022 // For single character sign text, pad with a space.
1023 if (sp->sn_text != NULL && cells == 1)
1024 STRCPY(sp->sn_text + len - 1, " ");
1025
1026 return OK;
1027}
1028
1029/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001030 * Define a new sign or update an existing sign
1031 */
1032 int
1033sign_define_by_name(
1034 char_u *name,
1035 char_u *icon,
1036 char_u *linehl,
1037 char_u *text,
1038 char_u *texthl)
1039{
1040 sign_T *sp_prev;
1041 sign_T *sp;
1042
1043 sp = sign_find(name, &sp_prev);
1044 if (sp == NULL)
1045 {
Bram Moolenaar03142362019-01-18 22:01:42 +01001046 sp = alloc_new_sign(name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001047 if (sp == NULL)
1048 return FAIL;
1049
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001050 // add the new sign to the list of signs
1051 if (sp_prev == NULL)
1052 first_sign = sp;
1053 else
1054 sp_prev->sn_next = sp;
1055 }
Bram Moolenaarbf0acff2020-01-09 21:01:59 +01001056 else
1057 {
1058 win_T *wp;
1059
1060 // Signs may already exist, a redraw is needed in windows with a
1061 // non-empty sign list.
1062 FOR_ALL_WINDOWS(wp)
1063 if (wp->w_buffer->b_signlist != NULL)
1064 redraw_buf_later(wp->w_buffer, NOT_VALID);
1065 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001066
1067 // set values for a defined sign.
1068 if (icon != NULL)
Bram Moolenaar03142362019-01-18 22:01:42 +01001069 sign_define_init_icon(sp, icon);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001070
Bram Moolenaar03142362019-01-18 22:01:42 +01001071 if (text != NULL && (sign_define_init_text(sp, text) == FAIL))
1072 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001073
1074 if (linehl != NULL)
1075 sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl));
1076
1077 if (texthl != NULL)
1078 sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl));
1079
1080 return OK;
1081}
1082
1083/*
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001084 * Return TRUE if sign "name" exists.
1085 */
1086 int
1087sign_exists_by_name(char_u *name)
1088{
1089 return sign_find(name, NULL) != NULL;
1090}
1091
1092/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001093 * Free the sign specified by 'name'.
1094 */
1095 int
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001096sign_undefine_by_name(char_u *name, int give_error)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001097{
1098 sign_T *sp_prev;
1099 sign_T *sp;
1100
1101 sp = sign_find(name, &sp_prev);
1102 if (sp == NULL)
1103 {
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001104 if (give_error)
1105 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001106 return FAIL;
1107 }
1108 sign_undefine(sp, sp_prev);
1109
1110 return OK;
1111}
1112
1113/*
1114 * List the signs matching 'name'
1115 */
1116 static void
1117sign_list_by_name(char_u *name)
1118{
1119 sign_T *sp;
1120
1121 sp = sign_find(name, NULL);
1122 if (sp != NULL)
1123 sign_list_defined(sp);
1124 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001125 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001126}
1127
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001128 static void
1129may_force_numberwidth_recompute(buf_T *buf, int unplace)
1130{
1131 tabpage_T *tp;
1132 win_T *wp;
1133
1134 FOR_ALL_TAB_WINDOWS(tp, wp)
1135 if (wp->w_buffer == buf
1136 && (wp->w_p_nu || wp->w_p_rnu)
1137 && (unplace || wp->w_nrwidth_width < 2)
1138 && (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u'))
1139 wp->w_nrwidth_line_count = 0;
1140}
1141
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001142/*
Bram Moolenaar8144acb2019-01-14 23:08:18 +01001143 * Place a sign at the specified file location or update a sign.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001144 */
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001145 int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001146sign_place(
1147 int *sign_id,
1148 char_u *sign_group,
1149 char_u *sign_name,
1150 buf_T *buf,
1151 linenr_T lnum,
1152 int prio)
1153{
1154 sign_T *sp;
1155
1156 // Check for reserved character '*' in group name
1157 if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0'))
1158 return FAIL;
1159
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001160 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001161 if (STRCMP(sp->sn_name, sign_name) == 0)
1162 break;
1163 if (sp == NULL)
1164 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001165 semsg(_("E155: Unknown sign: %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001166 return FAIL;
1167 }
1168 if (*sign_id == 0)
1169 *sign_id = sign_group_get_next_signid(buf, sign_group);
1170
1171 if (lnum > 0)
1172 // ":sign place {id} line={lnum} name={name} file={fname}":
1173 // place a sign
1174 buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr);
1175 else
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02001176 // ":sign place {id} file={fname}": change sign type and/or priority
1177 lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr,
1178 prio);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001179 if (lnum > 0)
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001180 {
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001181 redraw_buf_line_later(buf, lnum);
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001182
1183 // When displaying signs in the 'number' column, if the width of the
1184 // number column is less than 2, then force recomputing the width.
1185 may_force_numberwidth_recompute(buf, FALSE);
1186 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001187 else
1188 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001189 semsg(_("E885: Not possible to change sign %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001190 return FAIL;
1191 }
1192
1193 return OK;
1194}
1195
1196/*
1197 * Unplace the specified sign
1198 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001199 static int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001200sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum)
1201{
1202 if (buf->b_signlist == NULL) // No signs in the buffer
1203 return OK;
1204
1205 if (sign_id == 0)
1206 {
1207 // Delete all the signs in the specified buffer
1208 redraw_buf_later(buf, NOT_VALID);
1209 buf_delete_signs(buf, sign_group);
1210 }
1211 else
1212 {
1213 linenr_T lnum;
1214
1215 // Delete only the specified signs
1216 lnum = buf_delsign(buf, atlnum, sign_id, sign_group);
1217 if (lnum == 0)
1218 return FAIL;
1219 }
1220
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001221 // When all the signs in a buffer are removed, force recomputing the
1222 // number column width (if enabled) in all the windows displaying the
1223 // buffer if 'signcolumn' is set to 'number' in that window.
1224 if (buf->b_signlist == NULL)
1225 may_force_numberwidth_recompute(buf, TRUE);
1226
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001227 return OK;
1228}
1229
1230/*
1231 * Unplace the sign at the current cursor line.
1232 */
1233 static void
1234sign_unplace_at_cursor(char_u *groupname)
1235{
1236 int id = -1;
1237
1238 id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname);
1239 if (id > 0)
1240 sign_unplace(id, groupname, curwin->w_buffer, curwin->w_cursor.lnum);
1241 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001242 emsg(_("E159: Missing sign number"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001243}
1244
1245/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001246 * Jump to a sign.
1247 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001248 static linenr_T
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001249sign_jump(int sign_id, char_u *sign_group, buf_T *buf)
1250{
1251 linenr_T lnum;
1252
1253 if ((lnum = buf_findsign(buf, sign_id, sign_group)) <= 0)
1254 {
Bram Moolenaarb5443cc2019-01-15 20:19:40 +01001255 semsg(_("E157: Invalid sign ID: %d"), sign_id);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001256 return -1;
1257 }
1258
1259 // goto a sign ...
1260 if (buf_jump_open_win(buf) != NULL)
1261 { // ... in a current window
1262 curwin->w_cursor.lnum = lnum;
1263 check_cursor_lnum();
1264 beginline(BL_WHITE);
1265 }
1266 else
1267 { // ... not currently in a window
1268 char_u *cmd;
1269
1270 if (buf->b_fname == NULL)
1271 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001272 emsg(_("E934: Cannot jump to a buffer that does not have a name"));
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001273 return -1;
1274 }
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02001275 cmd = alloc(STRLEN(buf->b_fname) + 25);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001276 if (cmd == NULL)
1277 return -1;
1278 sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname);
1279 do_cmdline_cmd(cmd);
1280 vim_free(cmd);
1281 }
1282# ifdef FEAT_FOLDING
1283 foldOpenCursor();
1284# endif
1285
1286 return lnum;
1287}
1288
1289/*
1290 * ":sign define {name} ..." command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001291 */
1292 static void
1293sign_define_cmd(char_u *sign_name, char_u *cmdline)
1294{
1295 char_u *arg;
1296 char_u *p = cmdline;
1297 char_u *icon = NULL;
1298 char_u *text = NULL;
1299 char_u *linehl = NULL;
1300 char_u *texthl = NULL;
1301 int failed = FALSE;
1302
1303 // set values for a defined sign.
1304 for (;;)
1305 {
1306 arg = skipwhite(p);
1307 if (*arg == NUL)
1308 break;
1309 p = skiptowhite_esc(arg);
1310 if (STRNCMP(arg, "icon=", 5) == 0)
1311 {
1312 arg += 5;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001313 icon = vim_strnsave(arg, p - arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001314 }
1315 else if (STRNCMP(arg, "text=", 5) == 0)
1316 {
1317 arg += 5;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001318 text = vim_strnsave(arg, p - arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001319 }
1320 else if (STRNCMP(arg, "linehl=", 7) == 0)
1321 {
1322 arg += 7;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001323 linehl = vim_strnsave(arg, p - arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001324 }
1325 else if (STRNCMP(arg, "texthl=", 7) == 0)
1326 {
1327 arg += 7;
Bram Moolenaar71ccd032020-06-12 22:59:11 +02001328 texthl = vim_strnsave(arg, p - arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001329 }
1330 else
1331 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001332 semsg(_(e_invarg2), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001333 failed = TRUE;
1334 break;
1335 }
1336 }
1337
1338 if (!failed)
1339 sign_define_by_name(sign_name, icon, linehl, text, texthl);
1340
1341 vim_free(icon);
1342 vim_free(text);
1343 vim_free(linehl);
1344 vim_free(texthl);
1345}
1346
1347/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001348 * ":sign place" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001349 */
1350 static void
1351sign_place_cmd(
1352 buf_T *buf,
1353 linenr_T lnum,
1354 char_u *sign_name,
1355 int id,
1356 char_u *group,
1357 int prio)
1358{
1359 if (id <= 0)
1360 {
1361 // List signs placed in a file/buffer
1362 // :sign place file={fname}
1363 // :sign place group={group} file={fname}
1364 // :sign place group=* file={fname}
1365 // :sign place buffer={nr}
1366 // :sign place group={group} buffer={nr}
1367 // :sign place group=* buffer={nr}
1368 // :sign place
1369 // :sign place group={group}
1370 // :sign place group=*
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001371 if (lnum >= 0 || sign_name != NULL
1372 || (group != NULL && *group == '\0'))
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001373 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001374 else
1375 sign_list_placed(buf, group);
1376 }
1377 else
1378 {
1379 // Place a new sign
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001380 if (sign_name == NULL || buf == NULL
1381 || (group != NULL && *group == '\0'))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001382 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001383 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001384 return;
1385 }
1386
1387 sign_place(&id, group, sign_name, buf, lnum, prio);
1388 }
1389}
1390
1391/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001392 * ":sign unplace" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001393 */
1394 static void
1395sign_unplace_cmd(
1396 buf_T *buf,
1397 linenr_T lnum,
1398 char_u *sign_name,
1399 int id,
1400 char_u *group)
1401{
1402 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0'))
1403 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001404 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001405 return;
1406 }
1407
1408 if (id == -2)
1409 {
1410 if (buf != NULL)
1411 // :sign unplace * file={fname}
1412 // :sign unplace * group={group} file={fname}
1413 // :sign unplace * group=* file={fname}
1414 // :sign unplace * buffer={nr}
1415 // :sign unplace * group={group} buffer={nr}
1416 // :sign unplace * group=* buffer={nr}
1417 sign_unplace(0, group, buf, 0);
1418 else
1419 // :sign unplace *
1420 // :sign unplace * group={group}
1421 // :sign unplace * group=*
1422 FOR_ALL_BUFFERS(buf)
1423 if (buf->b_signlist != NULL)
1424 buf_delete_signs(buf, group);
1425 }
1426 else
1427 {
1428 if (buf != NULL)
1429 // :sign unplace {id} file={fname}
1430 // :sign unplace {id} group={group} file={fname}
1431 // :sign unplace {id} group=* file={fname}
1432 // :sign unplace {id} buffer={nr}
1433 // :sign unplace {id} group={group} buffer={nr}
1434 // :sign unplace {id} group=* buffer={nr}
1435 sign_unplace(id, group, buf, 0);
1436 else
1437 {
1438 if (id == -1)
1439 {
1440 // :sign unplace group={group}
1441 // :sign unplace group=*
1442 sign_unplace_at_cursor(group);
1443 }
1444 else
1445 {
1446 // :sign unplace {id}
1447 // :sign unplace {id} group={group}
1448 // :sign unplace {id} group=*
1449 FOR_ALL_BUFFERS(buf)
1450 sign_unplace(id, group, buf, 0);
1451 }
1452 }
1453 }
1454}
1455
1456/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001457 * Jump to a placed sign commands:
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001458 * :sign jump {id} file={fname}
1459 * :sign jump {id} buffer={nr}
1460 * :sign jump {id} group={group} file={fname}
1461 * :sign jump {id} group={group} buffer={nr}
1462 */
1463 static void
1464sign_jump_cmd(
1465 buf_T *buf,
1466 linenr_T lnum,
1467 char_u *sign_name,
1468 int id,
1469 char_u *group)
1470{
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001471 if (sign_name == NULL && group == NULL && id == -1)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001472 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001473 emsg(_(e_argreq));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001474 return;
1475 }
1476
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001477 if (buf == NULL || (group != NULL && *group == '\0')
1478 || lnum >= 0 || sign_name != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001479 {
1480 // File or buffer is not specified or an empty group is used
1481 // or a line number or a sign name is specified.
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001482 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001483 return;
1484 }
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001485 (void)sign_jump(id, group, buf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001486}
1487
1488/*
1489 * Parse the command line arguments for the ":sign place", ":sign unplace" and
1490 * ":sign jump" commands.
1491 * The supported arguments are: line={lnum} name={name} group={group}
1492 * priority={prio} and file={fname} or buffer={nr}.
1493 */
1494 static int
1495parse_sign_cmd_args(
1496 int cmd,
1497 char_u *arg,
1498 char_u **sign_name,
1499 int *signid,
1500 char_u **group,
1501 int *prio,
1502 buf_T **buf,
1503 linenr_T *lnum)
1504{
1505 char_u *arg1;
1506 char_u *name;
1507 char_u *filename = NULL;
Bram Moolenaarb589f952019-01-07 22:10:00 +01001508 int lnum_arg = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001509
1510 // first arg could be placed sign id
1511 arg1 = arg;
1512 if (VIM_ISDIGIT(*arg))
1513 {
1514 *signid = getdigits(&arg);
1515 if (!VIM_ISWHITE(*arg) && *arg != NUL)
1516 {
1517 *signid = -1;
1518 arg = arg1;
1519 }
1520 else
1521 arg = skipwhite(arg);
1522 }
1523
1524 while (*arg != NUL)
1525 {
1526 if (STRNCMP(arg, "line=", 5) == 0)
1527 {
1528 arg += 5;
1529 *lnum = atoi((char *)arg);
1530 arg = skiptowhite(arg);
Bram Moolenaarb589f952019-01-07 22:10:00 +01001531 lnum_arg = TRUE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001532 }
1533 else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE)
1534 {
1535 if (*signid != -1)
1536 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001537 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001538 return FAIL;
1539 }
1540 *signid = -2;
1541 arg = skiptowhite(arg + 1);
1542 }
1543 else if (STRNCMP(arg, "name=", 5) == 0)
1544 {
1545 arg += 5;
1546 name = arg;
1547 arg = skiptowhite(arg);
1548 if (*arg != NUL)
1549 *arg++ = NUL;
1550 while (name[0] == '0' && name[1] != NUL)
1551 ++name;
1552 *sign_name = name;
1553 }
1554 else if (STRNCMP(arg, "group=", 6) == 0)
1555 {
1556 arg += 6;
1557 *group = arg;
1558 arg = skiptowhite(arg);
1559 if (*arg != NUL)
1560 *arg++ = NUL;
1561 }
1562 else if (STRNCMP(arg, "priority=", 9) == 0)
1563 {
1564 arg += 9;
1565 *prio = atoi((char *)arg);
1566 arg = skiptowhite(arg);
1567 }
1568 else if (STRNCMP(arg, "file=", 5) == 0)
1569 {
1570 arg += 5;
1571 filename = arg;
1572 *buf = buflist_findname_exp(arg);
1573 break;
1574 }
1575 else if (STRNCMP(arg, "buffer=", 7) == 0)
1576 {
1577 arg += 7;
1578 filename = arg;
1579 *buf = buflist_findnr((int)getdigits(&arg));
1580 if (*skipwhite(arg) != NUL)
Bram Moolenaar2d06bfd2020-07-23 17:16:18 +02001581 semsg(_(e_trailing_arg), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001582 break;
1583 }
1584 else
1585 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001586 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001587 return FAIL;
1588 }
1589 arg = skipwhite(arg);
1590 }
1591
1592 if (filename != NULL && *buf == NULL)
1593 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001594 semsg(_("E158: Invalid buffer name: %s"), filename);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001595 return FAIL;
1596 }
1597
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001598 // If the filename is not supplied for the sign place or the sign jump
1599 // command, then use the current buffer.
Bram Moolenaarb589f952019-01-07 22:10:00 +01001600 if (filename == NULL && ((cmd == SIGNCMD_PLACE && lnum_arg)
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001601 || cmd == SIGNCMD_JUMP))
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001602 *buf = curwin->w_buffer;
1603
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001604 return OK;
1605}
1606
1607/*
1608 * ":sign" command
1609 */
1610 void
1611ex_sign(exarg_T *eap)
1612{
1613 char_u *arg = eap->arg;
1614 char_u *p;
1615 int idx;
1616 sign_T *sp;
1617 buf_T *buf = NULL;
1618
1619 // Parse the subcommand.
1620 p = skiptowhite(arg);
1621 idx = sign_cmd_idx(arg, p);
1622 if (idx == SIGNCMD_LAST)
1623 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001624 semsg(_("E160: Unknown sign command: %s"), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001625 return;
1626 }
1627 arg = skipwhite(p);
1628
1629 if (idx <= SIGNCMD_LIST)
1630 {
1631 // Define, undefine or list signs.
1632 if (idx == SIGNCMD_LIST && *arg == NUL)
1633 {
1634 // ":sign list": list all defined signs
1635 for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next)
1636 sign_list_defined(sp);
1637 }
1638 else if (*arg == NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001639 emsg(_("E156: Missing sign name"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001640 else
1641 {
1642 char_u *name;
1643
1644 // Isolate the sign name. If it's a number skip leading zeroes,
1645 // so that "099" and "99" are the same sign. But keep "0".
1646 p = skiptowhite(arg);
1647 if (*p != NUL)
1648 *p++ = NUL;
1649 while (arg[0] == '0' && arg[1] != NUL)
1650 ++arg;
1651 name = vim_strsave(arg);
1652
1653 if (idx == SIGNCMD_DEFINE)
1654 sign_define_cmd(name, p);
1655 else if (idx == SIGNCMD_LIST)
1656 // ":sign list {name}"
1657 sign_list_by_name(name);
1658 else
1659 // ":sign undefine {name}"
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001660 sign_undefine_by_name(name, TRUE);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001661
1662 vim_free(name);
1663 return;
1664 }
1665 }
1666 else
1667 {
1668 int id = -1;
1669 linenr_T lnum = -1;
1670 char_u *sign_name = NULL;
1671 char_u *group = NULL;
1672 int prio = SIGN_DEF_PRIO;
1673
1674 // Parse command line arguments
1675 if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio,
1676 &buf, &lnum) == FAIL)
1677 return;
1678
1679 if (idx == SIGNCMD_PLACE)
1680 sign_place_cmd(buf, lnum, sign_name, id, group, prio);
1681 else if (idx == SIGNCMD_UNPLACE)
1682 sign_unplace_cmd(buf, lnum, sign_name, id, group);
1683 else if (idx == SIGNCMD_JUMP)
1684 sign_jump_cmd(buf, lnum, sign_name, id, group);
1685 }
1686}
1687
1688/*
1689 * Return information about a specified sign
1690 */
1691 static void
1692sign_getinfo(sign_T *sp, dict_T *retdict)
1693{
1694 char_u *p;
1695
1696 dict_add_string(retdict, "name", (char_u *)sp->sn_name);
1697 if (sp->sn_icon != NULL)
1698 dict_add_string(retdict, "icon", (char_u *)sp->sn_icon);
1699 if (sp->sn_text != NULL)
1700 dict_add_string(retdict, "text", (char_u *)sp->sn_text);
1701 if (sp->sn_line_hl > 0)
1702 {
1703 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1704 if (p == NULL)
1705 p = (char_u *)"NONE";
1706 dict_add_string(retdict, "linehl", (char_u *)p);
1707 }
1708 if (sp->sn_text_hl > 0)
1709 {
1710 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1711 if (p == NULL)
1712 p = (char_u *)"NONE";
1713 dict_add_string(retdict, "texthl", (char_u *)p);
1714 }
1715}
1716
1717/*
1718 * If 'name' is NULL, return a list of all the defined signs.
1719 * Otherwise, return information about the specified sign.
1720 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001721 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001722sign_getlist(char_u *name, list_T *retlist)
1723{
1724 sign_T *sp = first_sign;
1725 dict_T *dict;
1726
1727 if (name != NULL)
1728 {
1729 sp = sign_find(name, NULL);
1730 if (sp == NULL)
1731 return;
1732 }
1733
1734 for (; sp != NULL && !got_int; sp = sp->sn_next)
1735 {
1736 if ((dict = dict_alloc_id(aid_sign_getlist)) == NULL)
1737 return;
1738 if (list_append_dict(retlist, dict) == FAIL)
1739 return;
1740 sign_getinfo(sp, dict);
1741
1742 if (name != NULL) // handle only the specified sign
1743 break;
1744 }
1745}
1746
1747/*
1748 * Returns information about signs placed in a buffer as list of dicts.
1749 */
1750 void
1751get_buffer_signs(buf_T *buf, list_T *l)
1752{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001753 sign_entry_T *sign;
1754 dict_T *d;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001755
1756 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1757 {
1758 if ((d = sign_get_info(sign)) != NULL)
1759 list_append_dict(l, d);
1760 }
1761}
1762
1763/*
1764 * Return information about all the signs placed in a buffer
1765 */
1766 static void
1767sign_get_placed_in_buf(
1768 buf_T *buf,
1769 linenr_T lnum,
1770 int sign_id,
1771 char_u *sign_group,
1772 list_T *retlist)
1773{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001774 dict_T *d;
1775 list_T *l;
1776 sign_entry_T *sign;
1777 dict_T *sdict;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001778
1779 if ((d = dict_alloc_id(aid_sign_getplaced_dict)) == NULL)
1780 return;
1781 list_append_dict(retlist, d);
1782
1783 dict_add_number(d, "bufnr", (long)buf->b_fnum);
1784
1785 if ((l = list_alloc_id(aid_sign_getplaced_list)) == NULL)
1786 return;
1787 dict_add_list(d, "signs", l);
1788
1789 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1790 {
1791 if (!sign_in_group(sign, sign_group))
1792 continue;
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001793 if ((lnum == 0 && sign_id == 0)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001794 || (sign_id == 0 && lnum == sign->se_lnum)
1795 || (lnum == 0 && sign_id == sign->se_id)
1796 || (lnum == sign->se_lnum && sign_id == sign->se_id))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001797 {
1798 if ((sdict = sign_get_info(sign)) != NULL)
1799 list_append_dict(l, sdict);
1800 }
1801 }
1802}
1803
1804/*
1805 * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the
1806 * sign placed at the line number. If 'lnum' is zero, return all the signs
1807 * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers.
1808 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001809 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001810sign_get_placed(
1811 buf_T *buf,
1812 linenr_T lnum,
1813 int sign_id,
1814 char_u *sign_group,
1815 list_T *retlist)
1816{
1817 if (buf != NULL)
1818 sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist);
1819 else
1820 {
1821 FOR_ALL_BUFFERS(buf)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001822 if (buf->b_signlist != NULL)
1823 sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001824 }
1825}
1826
1827# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1828/*
1829 * Allocate the icons. Called when the GUI has started. Allows defining
1830 * signs before it starts.
1831 */
1832 void
1833sign_gui_started(void)
1834{
1835 sign_T *sp;
1836
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001837 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001838 if (sp->sn_icon != NULL)
1839 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
1840}
1841# endif
1842
1843/*
1844 * List one sign.
1845 */
1846 static void
1847sign_list_defined(sign_T *sp)
1848{
1849 char_u *p;
1850
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001851 smsg("sign %s", sp->sn_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001852 if (sp->sn_icon != NULL)
1853 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001854 msg_puts(" icon=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001855 msg_outtrans(sp->sn_icon);
1856# ifdef FEAT_SIGN_ICONS
1857 if (sp->sn_image == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001858 msg_puts(_(" (NOT FOUND)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001859# else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001860 msg_puts(_(" (not supported)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001861# endif
1862 }
1863 if (sp->sn_text != NULL)
1864 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001865 msg_puts(" text=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001866 msg_outtrans(sp->sn_text);
1867 }
1868 if (sp->sn_line_hl > 0)
1869 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001870 msg_puts(" linehl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001871 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1872 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001873 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001874 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001875 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001876 }
1877 if (sp->sn_text_hl > 0)
1878 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001879 msg_puts(" texthl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001880 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1881 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001882 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001883 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001884 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001885 }
1886}
1887
1888/*
1889 * Undefine a sign and free its memory.
1890 */
1891 static void
1892sign_undefine(sign_T *sp, sign_T *sp_prev)
1893{
1894 vim_free(sp->sn_name);
1895 vim_free(sp->sn_icon);
1896# ifdef FEAT_SIGN_ICONS
1897 if (sp->sn_image != NULL)
1898 {
1899 out_flush();
1900 gui_mch_destroy_sign(sp->sn_image);
1901 }
1902# endif
1903 vim_free(sp->sn_text);
1904 if (sp_prev == NULL)
1905 first_sign = sp->sn_next;
1906 else
1907 sp_prev->sn_next = sp->sn_next;
1908 vim_free(sp);
1909}
1910
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001911# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1912 void *
1913sign_get_image(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001914 int typenr) // the attribute which may have a sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001915{
1916 sign_T *sp;
1917
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001918 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001919 if (sp->sn_typenr == typenr)
1920 return sp->sn_image;
1921 return NULL;
1922}
1923# endif
1924
1925/*
1926 * Undefine/free all signs.
1927 */
1928 void
1929free_signs(void)
1930{
1931 while (first_sign != NULL)
1932 sign_undefine(first_sign, NULL);
1933}
1934
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001935static enum
1936{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001937 EXP_SUBCMD, // expand :sign sub-commands
1938 EXP_DEFINE, // expand :sign define {name} args
1939 EXP_PLACE, // expand :sign place {id} args
Bram Moolenaar3678f652019-02-17 14:50:25 +01001940 EXP_LIST, // expand :sign place args
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001941 EXP_UNPLACE, // expand :sign unplace"
Bram Moolenaar3678f652019-02-17 14:50:25 +01001942 EXP_SIGN_NAMES, // expand with name of placed signs
1943 EXP_SIGN_GROUPS // expand with name of placed sign groups
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001944} expand_what;
1945
1946/*
Bram Moolenaar3678f652019-02-17 14:50:25 +01001947 * Return the n'th sign name (used for command line completion)
1948 */
1949 static char_u *
1950get_nth_sign_name(int idx)
1951{
1952 int current_idx;
1953 sign_T *sp;
1954
1955 // Complete with name of signs already defined
1956 current_idx = 0;
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001957 FOR_ALL_SIGNS(sp)
Bram Moolenaar3678f652019-02-17 14:50:25 +01001958 if (current_idx++ == idx)
1959 return sp->sn_name;
1960 return NULL;
1961}
1962
1963/*
1964 * Return the n'th sign group name (used for command line completion)
1965 */
1966 static char_u *
1967get_nth_sign_group_name(int idx)
1968{
1969 int current_idx;
1970 int todo;
1971 hashitem_T *hi;
1972 signgroup_T *group;
1973
1974 // Complete with name of sign groups already defined
1975 current_idx = 0;
1976 todo = (int)sg_table.ht_used;
1977 for (hi = sg_table.ht_array; todo > 0; ++hi)
1978 {
1979 if (!HASHITEM_EMPTY(hi))
1980 {
1981 --todo;
1982 if (current_idx++ == idx)
1983 {
1984 group = HI2SG(hi);
1985 return group->sg_name;
1986 }
1987 }
1988 }
1989 return NULL;
1990}
1991
1992/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001993 * Function given to ExpandGeneric() to obtain the sign command
1994 * expansion.
1995 */
1996 char_u *
1997get_sign_name(expand_T *xp UNUSED, int idx)
1998{
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001999 switch (expand_what)
2000 {
2001 case EXP_SUBCMD:
2002 return (char_u *)cmds[idx];
2003 case EXP_DEFINE:
2004 {
2005 char *define_arg[] =
2006 {
2007 "icon=", "linehl=", "text=", "texthl=", NULL
2008 };
2009 return (char_u *)define_arg[idx];
2010 }
2011 case EXP_PLACE:
2012 {
2013 char *place_arg[] =
2014 {
2015 "line=", "name=", "group=", "priority=", "file=",
2016 "buffer=", NULL
2017 };
2018 return (char_u *)place_arg[idx];
2019 }
Bram Moolenaar3678f652019-02-17 14:50:25 +01002020 case EXP_LIST:
2021 {
2022 char *list_arg[] =
2023 {
2024 "group=", "file=", "buffer=", NULL
2025 };
2026 return (char_u *)list_arg[idx];
2027 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002028 case EXP_UNPLACE:
2029 {
2030 char *unplace_arg[] = { "group=", "file=", "buffer=", NULL };
2031 return (char_u *)unplace_arg[idx];
2032 }
2033 case EXP_SIGN_NAMES:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002034 return get_nth_sign_name(idx);
2035 case EXP_SIGN_GROUPS:
2036 return get_nth_sign_group_name(idx);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002037 default:
2038 return NULL;
2039 }
2040}
2041
2042/*
2043 * Handle command line completion for :sign command.
2044 */
2045 void
2046set_context_in_sign_cmd(expand_T *xp, char_u *arg)
2047{
2048 char_u *p;
2049 char_u *end_subcmd;
2050 char_u *last;
2051 int cmd_idx;
2052 char_u *begin_subcmd_args;
2053
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002054 // Default: expand subcommands.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002055 xp->xp_context = EXPAND_SIGN;
2056 expand_what = EXP_SUBCMD;
2057 xp->xp_pattern = arg;
2058
2059 end_subcmd = skiptowhite(arg);
2060 if (*end_subcmd == NUL)
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002061 // expand subcmd name
2062 // :sign {subcmd}<CTRL-D>
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002063 return;
2064
2065 cmd_idx = sign_cmd_idx(arg, end_subcmd);
2066
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002067 // :sign {subcmd} {subcmd_args}
2068 // |
2069 // begin_subcmd_args
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002070 begin_subcmd_args = skipwhite(end_subcmd);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002071
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002072 // expand last argument of subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002073
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002074 // :sign define {name} {args}...
2075 // |
2076 // p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002077
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002078 // Loop until reaching last argument.
Bram Moolenaar3678f652019-02-17 14:50:25 +01002079 p = begin_subcmd_args;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002080 do
2081 {
2082 p = skipwhite(p);
2083 last = p;
2084 p = skiptowhite(p);
2085 } while (*p != NUL);
2086
2087 p = vim_strchr(last, '=');
2088
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002089 // :sign define {name} {args}... {last}=
2090 // | |
2091 // last p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002092 if (p == NULL)
2093 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002094 // Expand last argument name (before equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002095 xp->xp_pattern = last;
2096 switch (cmd_idx)
2097 {
2098 case SIGNCMD_DEFINE:
2099 expand_what = EXP_DEFINE;
2100 break;
2101 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002102 // List placed signs
2103 if (VIM_ISDIGIT(*begin_subcmd_args))
2104 // :sign place {id} {args}...
2105 expand_what = EXP_PLACE;
2106 else
2107 // :sign place {args}...
2108 expand_what = EXP_LIST;
2109 break;
2110 case SIGNCMD_LIST:
2111 case SIGNCMD_UNDEFINE:
2112 // :sign list <CTRL-D>
2113 // :sign undefine <CTRL-D>
2114 expand_what = EXP_SIGN_NAMES;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002115 break;
2116 case SIGNCMD_JUMP:
2117 case SIGNCMD_UNPLACE:
2118 expand_what = EXP_UNPLACE;
2119 break;
2120 default:
2121 xp->xp_context = EXPAND_NOTHING;
2122 }
2123 }
2124 else
2125 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002126 // Expand last argument value (after equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002127 xp->xp_pattern = p + 1;
2128 switch (cmd_idx)
2129 {
2130 case SIGNCMD_DEFINE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002131 if (STRNCMP(last, "texthl", 6) == 0
2132 || STRNCMP(last, "linehl", 6) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002133 xp->xp_context = EXPAND_HIGHLIGHT;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002134 else if (STRNCMP(last, "icon", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002135 xp->xp_context = EXPAND_FILES;
2136 else
2137 xp->xp_context = EXPAND_NOTHING;
2138 break;
2139 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002140 if (STRNCMP(last, "name", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002141 expand_what = EXP_SIGN_NAMES;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002142 else if (STRNCMP(last, "group", 5) == 0)
2143 expand_what = EXP_SIGN_GROUPS;
2144 else if (STRNCMP(last, "file", 4) == 0)
2145 xp->xp_context = EXPAND_BUFFERS;
2146 else
2147 xp->xp_context = EXPAND_NOTHING;
2148 break;
2149 case SIGNCMD_UNPLACE:
2150 case SIGNCMD_JUMP:
2151 if (STRNCMP(last, "group", 5) == 0)
2152 expand_what = EXP_SIGN_GROUPS;
2153 else if (STRNCMP(last, "file", 4) == 0)
2154 xp->xp_context = EXPAND_BUFFERS;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002155 else
2156 xp->xp_context = EXPAND_NOTHING;
2157 break;
2158 default:
2159 xp->xp_context = EXPAND_NOTHING;
2160 }
2161 }
2162}
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002163
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002164/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002165 * Define a sign using the attributes in 'dict'. Returns 0 on success and -1 on
2166 * failure.
2167 */
2168 static int
2169sign_define_from_dict(char_u *name_arg, dict_T *dict)
2170{
2171 char_u *name = NULL;
2172 char_u *icon = NULL;
2173 char_u *linehl = NULL;
2174 char_u *text = NULL;
2175 char_u *texthl = NULL;
2176 int retval = -1;
2177
2178 if (name_arg == NULL)
2179 {
2180 if (dict == NULL)
2181 return -1;
2182 name = dict_get_string(dict, (char_u *)"name", TRUE);
2183 }
2184 else
2185 name = vim_strsave(name_arg);
2186 if (name == NULL || name[0] == NUL)
2187 goto cleanup;
2188 if (dict != NULL)
2189 {
2190 icon = dict_get_string(dict, (char_u *)"icon", TRUE);
2191 linehl = dict_get_string(dict, (char_u *)"linehl", TRUE);
2192 text = dict_get_string(dict, (char_u *)"text", TRUE);
2193 texthl = dict_get_string(dict, (char_u *)"texthl", TRUE);
2194 }
2195
2196 if (sign_define_by_name(name, icon, linehl, text, texthl) == OK)
2197 retval = 0;
2198
2199cleanup:
2200 vim_free(name);
2201 vim_free(icon);
2202 vim_free(linehl);
2203 vim_free(text);
2204 vim_free(texthl);
2205
2206 return retval;
2207}
2208
2209/*
2210 * Define multiple signs using attributes from list 'l' and store the return
2211 * values in 'retlist'.
2212 */
2213 static void
2214sign_define_multiple(list_T *l, list_T *retlist)
2215{
2216 listitem_T *li;
2217 int retval;
2218
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002219 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002220 {
2221 retval = -1;
2222 if (li->li_tv.v_type == VAR_DICT)
2223 retval = sign_define_from_dict(NULL, li->li_tv.vval.v_dict);
2224 else
2225 emsg(_(e_dictreq));
2226 list_append_number(retlist, retval);
2227 }
2228}
2229
2230/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002231 * "sign_define()" function
2232 */
2233 void
2234f_sign_define(typval_T *argvars, typval_T *rettv)
2235{
2236 char_u *name;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002237
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002238 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2239 {
2240 // Define multiple signs
2241 if (rettv_list_alloc(rettv) != OK)
2242 return;
2243
2244 sign_define_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2245 return;
2246 }
2247
2248 // Define a single sign
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002249 rettv->vval.v_number = -1;
2250
2251 name = tv_get_string_chk(&argvars[0]);
2252 if (name == NULL)
2253 return;
2254
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002255 if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_DICT)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002256 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002257 emsg(_(e_dictreq));
2258 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002259 }
2260
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002261 rettv->vval.v_number = sign_define_from_dict(name,
2262 argvars[1].v_type == VAR_DICT ? argvars[1].vval.v_dict : NULL);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002263}
2264
2265/*
2266 * "sign_getdefined()" function
2267 */
2268 void
2269f_sign_getdefined(typval_T *argvars, typval_T *rettv)
2270{
2271 char_u *name = NULL;
2272
2273 if (rettv_list_alloc_id(rettv, aid_sign_getdefined) != OK)
2274 return;
2275
2276 if (argvars[0].v_type != VAR_UNKNOWN)
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +02002277 {
2278 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
2279 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002280 name = tv_get_string(&argvars[0]);
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +02002281 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002282
2283 sign_getlist(name, rettv->vval.v_list);
2284}
2285
2286/*
2287 * "sign_getplaced()" function
2288 */
2289 void
2290f_sign_getplaced(typval_T *argvars, typval_T *rettv)
2291{
2292 buf_T *buf = NULL;
2293 dict_T *dict;
2294 dictitem_T *di;
2295 linenr_T lnum = 0;
2296 int sign_id = 0;
2297 char_u *group = NULL;
2298 int notanum = FALSE;
2299
2300 if (rettv_list_alloc_id(rettv, aid_sign_getplaced) != OK)
2301 return;
2302
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02002303 if (in_vim9script()
2304 && (check_for_opt_string_or_number_arg(argvars, 0) == FAIL
2305 || (argvars[0].v_type != VAR_UNKNOWN
2306 && check_for_opt_dict_arg(argvars, 1) == FAIL)))
2307 return;
2308
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002309 if (argvars[0].v_type != VAR_UNKNOWN)
2310 {
2311 // get signs placed in the specified buffer
2312 buf = get_buf_arg(&argvars[0]);
2313 if (buf == NULL)
2314 return;
2315
2316 if (argvars[1].v_type != VAR_UNKNOWN)
2317 {
2318 if (argvars[1].v_type != VAR_DICT ||
2319 ((dict = argvars[1].vval.v_dict) == NULL))
2320 {
2321 emsg(_(e_dictreq));
2322 return;
2323 }
2324 if ((di = dict_find(dict, (char_u *)"lnum", -1)) != NULL)
2325 {
2326 // get signs placed at this line
2327 (void)tv_get_number_chk(&di->di_tv, &notanum);
2328 if (notanum)
2329 return;
2330 lnum = tv_get_lnum(&di->di_tv);
2331 }
2332 if ((di = dict_find(dict, (char_u *)"id", -1)) != NULL)
2333 {
2334 // get sign placed with this identifier
2335 sign_id = (int)tv_get_number_chk(&di->di_tv, &notanum);
2336 if (notanum)
2337 return;
2338 }
2339 if ((di = dict_find(dict, (char_u *)"group", -1)) != NULL)
2340 {
2341 group = tv_get_string_chk(&di->di_tv);
2342 if (group == NULL)
2343 return;
2344 if (*group == '\0') // empty string means global group
2345 group = NULL;
2346 }
2347 }
2348 }
2349
2350 sign_get_placed(buf, lnum, sign_id, group, rettv->vval.v_list);
2351}
2352
2353/*
2354 * "sign_jump()" function
2355 */
2356 void
2357f_sign_jump(typval_T *argvars, typval_T *rettv)
2358{
2359 int sign_id;
2360 char_u *sign_group = NULL;
2361 buf_T *buf;
2362 int notanum = FALSE;
2363
2364 rettv->vval.v_number = -1;
2365
2366 // Sign identifier
2367 sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
2368 if (notanum)
2369 return;
2370 if (sign_id <= 0)
2371 {
2372 emsg(_(e_invarg));
2373 return;
2374 }
2375
2376 // Sign group
2377 sign_group = tv_get_string_chk(&argvars[1]);
2378 if (sign_group == NULL)
2379 return;
2380 if (sign_group[0] == '\0')
2381 sign_group = NULL; // global sign group
2382 else
2383 {
2384 sign_group = vim_strsave(sign_group);
2385 if (sign_group == NULL)
2386 return;
2387 }
2388
2389 // Buffer to place the sign
2390 buf = get_buf_arg(&argvars[2]);
2391 if (buf == NULL)
2392 goto cleanup;
2393
2394 rettv->vval.v_number = sign_jump(sign_id, sign_group, buf);
2395
2396cleanup:
2397 vim_free(sign_group);
2398}
2399
2400/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002401 * Place a new sign using the values specified in dict 'dict'. Returns the sign
2402 * identifier if successfully placed, otherwise returns 0.
2403 */
2404 static int
2405sign_place_from_dict(
2406 typval_T *id_tv,
2407 typval_T *group_tv,
2408 typval_T *name_tv,
2409 typval_T *buf_tv,
2410 dict_T *dict)
2411{
2412 int sign_id = 0;
2413 char_u *group = NULL;
2414 char_u *sign_name = NULL;
2415 buf_T *buf = NULL;
2416 dictitem_T *di;
2417 linenr_T lnum = 0;
2418 int prio = SIGN_DEF_PRIO;
2419 int notanum = FALSE;
2420 int ret_sign_id = -1;
2421
2422 // sign identifier
2423 if (id_tv == NULL)
2424 {
2425 di = dict_find(dict, (char_u *)"id", -1);
2426 if (di != NULL)
2427 id_tv = &di->di_tv;
2428 }
2429 if (id_tv == NULL)
2430 sign_id = 0;
2431 else
2432 {
2433 sign_id = tv_get_number_chk(id_tv, &notanum);
2434 if (notanum)
2435 return -1;
2436 if (sign_id < 0)
2437 {
2438 emsg(_(e_invarg));
2439 return -1;
2440 }
2441 }
2442
2443 // sign group
2444 if (group_tv == NULL)
2445 {
2446 di = dict_find(dict, (char_u *)"group", -1);
2447 if (di != NULL)
2448 group_tv = &di->di_tv;
2449 }
2450 if (group_tv == NULL)
2451 group = NULL; // global group
2452 else
2453 {
2454 group = tv_get_string_chk(group_tv);
2455 if (group == NULL)
2456 goto cleanup;
2457 if (group[0] == '\0') // global sign group
2458 group = NULL;
2459 else
2460 {
2461 group = vim_strsave(group);
2462 if (group == NULL)
2463 return -1;
2464 }
2465 }
2466
2467 // sign name
2468 if (name_tv == NULL)
2469 {
2470 di = dict_find(dict, (char_u *)"name", -1);
2471 if (di != NULL)
2472 name_tv = &di->di_tv;
2473 }
2474 if (name_tv == NULL)
2475 goto cleanup;
2476 sign_name = tv_get_string_chk(name_tv);
2477 if (sign_name == NULL)
2478 goto cleanup;
2479
2480 // buffer to place the sign
2481 if (buf_tv == NULL)
2482 {
2483 di = dict_find(dict, (char_u *)"buffer", -1);
2484 if (di != NULL)
2485 buf_tv = &di->di_tv;
2486 }
2487 if (buf_tv == NULL)
2488 goto cleanup;
2489 buf = get_buf_arg(buf_tv);
2490 if (buf == NULL)
2491 goto cleanup;
2492
2493 // line number of the sign
2494 di = dict_find(dict, (char_u *)"lnum", -1);
2495 if (di != NULL)
2496 {
Bram Moolenaar42aff462019-08-21 13:20:29 +02002497 lnum = tv_get_lnum(&di->di_tv);
2498 if (lnum <= 0)
2499 {
2500 emsg(_(e_invarg));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002501 goto cleanup;
Bram Moolenaar42aff462019-08-21 13:20:29 +02002502 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002503 }
2504
2505 // sign priority
2506 di = dict_find(dict, (char_u *)"priority", -1);
2507 if (di != NULL)
2508 {
2509 prio = (int)tv_get_number_chk(&di->di_tv, &notanum);
2510 if (notanum)
2511 goto cleanup;
2512 }
2513
2514 if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK)
2515 ret_sign_id = sign_id;
2516
2517cleanup:
2518 vim_free(group);
2519
2520 return ret_sign_id;
2521}
2522
2523/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002524 * "sign_place()" function
2525 */
2526 void
2527f_sign_place(typval_T *argvars, typval_T *rettv)
2528{
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002529 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002530
2531 rettv->vval.v_number = -1;
2532
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002533 if (argvars[4].v_type != VAR_UNKNOWN
2534 && (argvars[4].v_type != VAR_DICT
2535 || ((dict = argvars[4].vval.v_dict) == NULL)))
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002536 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002537 emsg(_(e_dictreq));
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002538 return;
2539 }
2540
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002541 rettv->vval.v_number = sign_place_from_dict(&argvars[0], &argvars[1],
2542 &argvars[2], &argvars[3], dict);
2543}
2544
2545/*
2546 * "sign_placelist()" function. Place multiple signs.
2547 */
2548 void
2549f_sign_placelist(typval_T *argvars, typval_T *rettv)
2550{
2551 listitem_T *li;
2552 int sign_id;
2553
2554 if (rettv_list_alloc(rettv) != OK)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002555 return;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002556
2557 if (argvars[0].v_type != VAR_LIST)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002558 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002559 emsg(_(e_listreq));
2560 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002561 }
2562
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002563 // Process the List of sign attributes
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002564 FOR_ALL_LIST_ITEMS(argvars[0].vval.v_list, li)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002565 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002566 sign_id = -1;
2567 if (li->li_tv.v_type == VAR_DICT)
2568 sign_id = sign_place_from_dict(NULL, NULL, NULL, NULL,
2569 li->li_tv.vval.v_dict);
2570 else
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002571 emsg(_(e_dictreq));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002572 list_append_number(rettv->vval.v_list, sign_id);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002573 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002574}
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002575
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002576/*
2577 * Undefine multiple signs
2578 */
2579 static void
2580sign_undefine_multiple(list_T *l, list_T *retlist)
2581{
2582 char_u *name;
2583 listitem_T *li;
2584 int retval;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002585
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002586 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002587 {
2588 retval = -1;
2589 name = tv_get_string_chk(&li->li_tv);
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002590 if (name != NULL && (sign_undefine_by_name(name, TRUE) == OK))
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002591 retval = 0;
2592 list_append_number(retlist, retval);
2593 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002594}
2595
2596/*
2597 * "sign_undefine()" function
2598 */
2599 void
2600f_sign_undefine(typval_T *argvars, typval_T *rettv)
2601{
2602 char_u *name;
2603
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002604 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2605 {
2606 // Undefine multiple signs
2607 if (rettv_list_alloc(rettv) != OK)
2608 return;
2609
2610 sign_undefine_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2611 return;
2612 }
2613
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002614 rettv->vval.v_number = -1;
2615
2616 if (argvars[0].v_type == VAR_UNKNOWN)
2617 {
2618 // Free all the signs
2619 free_signs();
2620 rettv->vval.v_number = 0;
2621 }
2622 else
2623 {
2624 // Free only the specified sign
2625 name = tv_get_string_chk(&argvars[0]);
2626 if (name == NULL)
2627 return;
2628
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002629 if (sign_undefine_by_name(name, TRUE) == OK)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002630 rettv->vval.v_number = 0;
2631 }
2632}
2633
2634/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002635 * Unplace the sign with attributes specified in 'dict'. Returns 0 on success
2636 * and -1 on failure.
2637 */
2638 static int
2639sign_unplace_from_dict(typval_T *group_tv, dict_T *dict)
2640{
2641 dictitem_T *di;
2642 int sign_id = 0;
2643 buf_T *buf = NULL;
2644 char_u *group = NULL;
2645 int retval = -1;
2646
2647 // sign group
2648 if (group_tv != NULL)
2649 group = tv_get_string(group_tv);
2650 else
2651 group = dict_get_string(dict, (char_u *)"group", FALSE);
2652 if (group != NULL)
2653 {
2654 if (group[0] == '\0') // global sign group
2655 group = NULL;
2656 else
2657 {
2658 group = vim_strsave(group);
2659 if (group == NULL)
2660 return -1;
2661 }
2662 }
2663
2664 if (dict != NULL)
2665 {
2666 if ((di = dict_find(dict, (char_u *)"buffer", -1)) != NULL)
2667 {
2668 buf = get_buf_arg(&di->di_tv);
2669 if (buf == NULL)
2670 goto cleanup;
2671 }
2672 if (dict_find(dict, (char_u *)"id", -1) != NULL)
2673 {
2674 sign_id = dict_get_number(dict, (char_u *)"id");
2675 if (sign_id <= 0)
2676 {
2677 emsg(_(e_invarg));
2678 goto cleanup;
2679 }
2680 }
2681 }
2682
2683 if (buf == NULL)
2684 {
2685 // Delete the sign in all the buffers
2686 retval = 0;
2687 FOR_ALL_BUFFERS(buf)
2688 if (sign_unplace(sign_id, group, buf, 0) != OK)
2689 retval = -1;
2690 }
2691 else if (sign_unplace(sign_id, group, buf, 0) == OK)
2692 retval = 0;
2693
2694cleanup:
2695 vim_free(group);
2696
2697 return retval;
2698}
2699
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002700 sign_entry_T *
2701get_first_valid_sign(win_T *wp)
2702{
2703 sign_entry_T *sign = wp->w_buffer->b_signlist;
2704
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002705# ifdef FEAT_PROP_POPUP
Bram Moolenaar72570732019-11-30 14:21:53 +01002706 while (sign != NULL && !sign_group_for_window(sign, wp))
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002707 sign = sign->se_next;
2708# endif
2709 return sign;
2710}
2711
2712/*
2713 * Return TRUE when window "wp" has a column to draw signs in.
2714 */
2715 int
2716signcolumn_on(win_T *wp)
2717{
2718 // If 'signcolumn' is set to 'number', signs are displayed in the 'number'
2719 // column (if present). Otherwise signs are to be displayed in the sign
2720 // column.
2721 if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u')
2722 return get_first_valid_sign(wp) != NULL && !wp->w_p_nu && !wp->w_p_rnu;
2723
2724 if (*wp->w_p_scl == 'n')
2725 return FALSE;
2726 if (*wp->w_p_scl == 'y')
2727 return TRUE;
2728 return (get_first_valid_sign(wp) != NULL
2729# ifdef FEAT_NETBEANS_INTG
2730 || wp->w_buffer->b_has_sign_column
2731# endif
2732 );
2733}
2734
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002735/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002736 * "sign_unplace()" function
2737 */
2738 void
2739f_sign_unplace(typval_T *argvars, typval_T *rettv)
2740{
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002741 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002742
2743 rettv->vval.v_number = -1;
2744
2745 if (argvars[0].v_type != VAR_STRING)
2746 {
2747 emsg(_(e_invarg));
2748 return;
2749 }
2750
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002751 if (argvars[1].v_type != VAR_UNKNOWN)
2752 {
2753 if (argvars[1].v_type != VAR_DICT)
2754 {
2755 emsg(_(e_dictreq));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002756 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002757 }
2758 dict = argvars[1].vval.v_dict;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002759 }
2760
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002761 rettv->vval.v_number = sign_unplace_from_dict(&argvars[0], dict);
2762}
2763
2764/*
2765 * "sign_unplacelist()" function
2766 */
2767 void
2768f_sign_unplacelist(typval_T *argvars, typval_T *rettv)
2769{
2770 listitem_T *li;
2771 int retval;
2772
2773 if (rettv_list_alloc(rettv) != OK)
2774 return;
2775
2776 if (argvars[0].v_type != VAR_LIST)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002777 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002778 emsg(_(e_listreq));
2779 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002780 }
2781
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002782 FOR_ALL_LIST_ITEMS(argvars[0].vval.v_list, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002783 {
2784 retval = -1;
2785 if (li->li_tv.v_type == VAR_DICT)
2786 retval = sign_unplace_from_dict(NULL, li->li_tv.vval.v_dict);
2787 else
2788 emsg(_(e_dictreq));
2789 list_append_number(rettv->vval.v_list, retval);
2790 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002791}
2792
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002793#endif // FEAT_SIGNS