blob: 576c4d01fcc672160a8fd108affe9c469dd0b44c [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 Moolenaara2f6e422020-02-19 17:13:04 +0100520
521 // If there is another sign next with the same priority, may
522 // combine the text and the line highlighting.
523 if (sign->se_next != NULL
524 && sign->se_next->se_priority == sign->se_priority
525 && sign->se_next->se_lnum == sign->se_lnum)
526 {
527 sign_T *next_sp = find_sign_by_typenr(sign->se_next->se_typenr);
528
529 if (next_sp != NULL)
530 {
531 if (sattr->sat_icon == NULL && sattr->sat_text == NULL)
532 {
533# ifdef FEAT_SIGN_ICONS
534 sattr->sat_icon = next_sp->sn_image;
535# endif
536 sattr->sat_text = next_sp->sn_text;
537 }
538 if (sp->sn_text_hl <= 0 && next_sp->sn_text_hl > 0)
539 sattr->sat_texthl = syn_id2attr(next_sp->sn_text_hl);
540 if (sp->sn_line_hl <= 0 && next_sp->sn_line_hl > 0)
541 sattr->sat_linehl = syn_id2attr(next_sp->sn_line_hl);
542 }
543 }
Bram Moolenaar4e038572019-07-04 18:28:35 +0200544 return TRUE;
545 }
546 }
547 return FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100548}
549
550/*
551 * Delete sign 'id' in group 'group' from buffer 'buf'.
552 * If 'id' is zero, then delete all the signs in group 'group'. Otherwise
553 * delete only the specified sign.
554 * If 'group' is '*', then delete the sign in all the groups. If 'group' is
555 * NULL, then delete the sign in the global group. Otherwise delete the sign in
556 * the specified group.
557 * Returns the line number of the deleted sign. If multiple signs are deleted,
558 * then returns the line number of the last sign deleted.
559 */
560 linenr_T
561buf_delsign(
562 buf_T *buf, // buffer sign is stored in
563 linenr_T atlnum, // sign at this line, 0 - at any line
564 int id, // sign id
565 char_u *group) // sign group
566{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200567 sign_entry_T **lastp; // pointer to pointer to current sign
568 sign_entry_T *sign; // a sign in a b_signlist
569 sign_entry_T *next; // the next sign in a b_signlist
570 linenr_T lnum; // line number whose sign was deleted
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100571
572 lastp = &buf->b_signlist;
573 lnum = 0;
574 for (sign = buf->b_signlist; sign != NULL; sign = next)
575 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200576 next = sign->se_next;
577 if ((id == 0 || sign->se_id == id)
578 && (atlnum == 0 || sign->se_lnum == atlnum)
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100579 && sign_in_group(sign, group))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100580
581 {
582 *lastp = next;
583 if (next != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200584 next->se_prev = sign->se_prev;
585 lnum = sign->se_lnum;
586 if (sign->se_group != NULL)
587 sign_group_unref(sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100588 vim_free(sign);
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100589 redraw_buf_line_later(buf, lnum);
590
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100591 // Check whether only one sign needs to be deleted
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100592 // If deleting a sign with a specific identifier in a particular
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100593 // group or deleting any sign at a particular line number, delete
594 // only one sign.
595 if (group == NULL
596 || (*group != '*' && id != 0)
597 || (*group == '*' && atlnum != 0))
598 break;
599 }
600 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200601 lastp = &sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100602 }
603
Bram Moolenaar27a472c2019-01-09 21:47:30 +0100604 // When deleting the last sign the cursor position may change, because the
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100605 // sign columns no longer shows. And the 'signcolumn' may be hidden.
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100606 if (buf->b_signlist == NULL)
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100607 {
608 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200609 changed_line_abv_curs();
Bram Moolenaar8144acb2019-01-14 23:08:18 +0100610 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100611
612 return lnum;
613}
614
615
616/*
617 * Find the line number of the sign with the requested id in group 'group'. If
618 * the sign does not exist, return 0 as the line number. This will still let
619 * the correct file get loaded.
620 */
621 int
622buf_findsign(
623 buf_T *buf, // buffer to store sign in
624 int id, // sign ID
625 char_u *group) // sign group
626{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200627 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100628
629 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200630 if (sign->se_id == id && sign_in_group(sign, group))
631 return sign->se_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100632
633 return 0;
634}
635
636/*
637 * Return the sign at line 'lnum' in buffer 'buf'. Returns NULL if a sign is
638 * not found at the line. If 'groupname' is NULL, searches in the global group.
639 */
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200640 static sign_entry_T *
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100641buf_getsign_at_line(
642 buf_T *buf, // buffer whose sign we are searching for
643 linenr_T lnum, // line number of sign
644 char_u *groupname) // sign group name
645{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200646 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100647
648 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200649 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200650 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200651 // Signs are sorted by line number in the buffer. No need to check
652 // for signs after the specified line number 'lnum'.
653 break;
654
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200655 if (sign->se_lnum == lnum && sign_in_group(sign, groupname))
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100656 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 */
665 int
666buf_findsign_id(
667 buf_T *buf, // buffer whose sign we are searching for
668 linenr_T lnum, // line number of sign
669 char_u *groupname) // sign group name
670{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200671 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100672
673 sign = buf_getsign_at_line(buf, lnum, groupname);
674 if (sign != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200675 return sign->se_id;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100676
677 return 0;
678}
679
680# if defined(FEAT_NETBEANS_INTG) || defined(PROTO)
681/*
682 * See if a given type of sign exists on a specific line.
683 */
684 int
685buf_findsigntype_id(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100686 buf_T *buf, // buffer whose sign we are searching for
687 linenr_T lnum, // line number of sign
688 int typenr) // sign type number
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100689{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200690 sign_entry_T *sign; // a sign in the signlist
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100691
692 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200693 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200694 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200695 // Signs are sorted by line number in the buffer. No need to check
696 // for signs after the specified line number 'lnum'.
697 break;
698
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200699 if (sign->se_lnum == lnum && sign->se_typenr == typenr)
700 return sign->se_id;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200701 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100702
703 return 0;
704}
705
706
707# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
708/*
709 * Return the number of icons on the given line.
710 */
711 int
712buf_signcount(buf_T *buf, linenr_T lnum)
713{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200714 sign_entry_T *sign; // a sign in the signlist
715 int count = 0;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100716
717 FOR_ALL_SIGNS_IN_BUF(buf, sign)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200718 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200719 if (sign->se_lnum > lnum)
Bram Moolenaar4e038572019-07-04 18:28:35 +0200720 // Signs are sorted by line number in the buffer. No need to check
721 // for signs after the specified line number 'lnum'.
722 break;
723
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200724 if (sign->se_lnum == lnum)
725 if (sign_get_image(sign->se_typenr) != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100726 count++;
Bram Moolenaar4e038572019-07-04 18:28:35 +0200727 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100728
729 return count;
730}
Bram Moolenaar63d9e732019-12-05 21:10:38 +0100731# endif // FEAT_SIGN_ICONS
732# endif // FEAT_NETBEANS_INTG
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100733
734/*
735 * Delete signs in group 'group' in buffer "buf". If 'group' is '*', then
736 * delete all the signs.
737 */
738 void
739buf_delete_signs(buf_T *buf, char_u *group)
740{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200741 sign_entry_T *sign;
742 sign_entry_T **lastp; // pointer to pointer to current sign
743 sign_entry_T *next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100744
745 // When deleting the last sign need to redraw the windows to remove the
746 // sign column. Not when curwin is NULL (this means we're exiting).
747 if (buf->b_signlist != NULL && curwin != NULL)
748 {
749 redraw_buf_later(buf, NOT_VALID);
Bram Moolenaarf85e40a2019-06-16 13:55:40 +0200750 changed_line_abv_curs();
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100751 }
752
753 lastp = &buf->b_signlist;
754 for (sign = buf->b_signlist; sign != NULL; sign = next)
755 {
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200756 next = sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100757 if (sign_in_group(sign, group))
758 {
759 *lastp = next;
760 if (next != NULL)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200761 next->se_prev = sign->se_prev;
762 if (sign->se_group != NULL)
763 sign_group_unref(sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100764 vim_free(sign);
765 }
766 else
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200767 lastp = &sign->se_next;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100768 }
769}
770
771/*
772 * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers.
773 */
774 static void
775sign_list_placed(buf_T *rbuf, char_u *sign_group)
776{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200777 buf_T *buf;
778 sign_entry_T *sign;
779 char lbuf[MSG_BUF_LEN];
780 char group[MSG_BUF_LEN];
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100781
Bram Moolenaar32526b32019-01-19 17:43:09 +0100782 msg_puts_title(_("\n--- Signs ---"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100783 msg_putchar('\n');
784 if (rbuf == NULL)
785 buf = firstbuf;
786 else
787 buf = rbuf;
788 while (buf != NULL && !got_int)
789 {
790 if (buf->b_signlist != NULL)
791 {
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100792 vim_snprintf(lbuf, MSG_BUF_LEN, _("Signs for %s:"), buf->b_fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100793 msg_puts_attr(lbuf, HL_ATTR(HLF_D));
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100794 msg_putchar('\n');
795 }
796 FOR_ALL_SIGNS_IN_BUF(buf, sign)
797 {
798 if (got_int)
799 break;
800 if (!sign_in_group(sign, sign_group))
801 continue;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200802 if (sign->se_group != NULL)
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100803 vim_snprintf(group, MSG_BUF_LEN, _(" group=%s"),
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200804 sign->se_group->sg_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100805 else
806 group[0] = '\0';
Bram Moolenaard730c8e2019-01-07 21:16:53 +0100807 vim_snprintf(lbuf, MSG_BUF_LEN,
808 _(" line=%ld id=%d%s name=%s priority=%d"),
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200809 (long)sign->se_lnum, sign->se_id, group,
810 sign_typenr2name(sign->se_typenr), sign->se_priority);
Bram Moolenaar32526b32019-01-19 17:43:09 +0100811 msg_puts(lbuf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100812 msg_putchar('\n');
813 }
814 if (rbuf != NULL)
815 break;
816 buf = buf->b_next;
817 }
818}
819
820/*
821 * Adjust a placed sign for inserted/deleted lines.
822 */
823 void
824sign_mark_adjust(
825 linenr_T line1,
826 linenr_T line2,
827 long amount,
828 long amount_after)
829{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200830 sign_entry_T *sign; // a sign in a b_signlist
831 linenr_T new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100832
833 FOR_ALL_SIGNS_IN_BUF(curbuf, sign)
834 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100835 // Ignore changes to lines after the sign
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200836 if (sign->se_lnum < line1)
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100837 continue;
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200838 new_lnum = sign->se_lnum;
839 if (sign->se_lnum >= line1 && sign->se_lnum <= line2)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100840 {
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100841 if (amount != MAXLNUM)
842 new_lnum += amount;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100843 }
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200844 else if (sign->se_lnum > line2)
Bram Moolenaarc771bf92019-01-17 17:36:45 +0100845 // Lines inserted or deleted before the sign
846 new_lnum += amount_after;
847
848 // If the new sign line number is past the last line in the buffer,
849 // then don't adjust the line number. Otherwise, it will always be past
850 // the last line and will not be visible.
851 if (new_lnum <= curbuf->b_ml.ml_line_count)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +0200852 sign->se_lnum = new_lnum;
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100853 }
854}
855
856/*
857 * Find index of a ":sign" subcmd from its name.
858 * "*end_cmd" must be writable.
859 */
860 static int
861sign_cmd_idx(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +0100862 char_u *begin_cmd, // begin of sign subcmd
863 char_u *end_cmd) // just after sign subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100864{
865 int idx;
866 char save = *end_cmd;
867
868 *end_cmd = NUL;
869 for (idx = 0; ; ++idx)
870 if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0)
871 break;
872 *end_cmd = save;
873 return idx;
874}
875
876/*
877 * Find a sign by name. Also returns pointer to the previous sign.
878 */
879 static sign_T *
880sign_find(char_u *name, sign_T **sp_prev)
881{
882 sign_T *sp;
883
884 if (sp_prev != NULL)
885 *sp_prev = NULL;
Bram Moolenaaraeea7212020-04-02 18:50:46 +0200886 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +0100887 {
888 if (STRCMP(sp->sn_name, name) == 0)
889 break;
890 if (sp_prev != NULL)
891 *sp_prev = sp;
892 }
893
894 return sp;
895}
896
897/*
Bram Moolenaar03142362019-01-18 22:01:42 +0100898 * Allocate a new sign
899 */
900 static sign_T *
901alloc_new_sign(char_u *name)
902{
903 sign_T *sp;
904 sign_T *lp;
905 int start = next_sign_typenr;
906
907 // Allocate a new sign.
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200908 sp = alloc_clear_id(sizeof(sign_T), aid_sign_define_by_name);
Bram Moolenaar03142362019-01-18 22:01:42 +0100909 if (sp == NULL)
910 return NULL;
911
912 // Check that next_sign_typenr is not already being used.
913 // This only happens after wrapping around. Hopefully
914 // another one got deleted and we can use its number.
915 for (lp = first_sign; lp != NULL; )
916 {
917 if (lp->sn_typenr == next_sign_typenr)
918 {
919 ++next_sign_typenr;
920 if (next_sign_typenr == MAX_TYPENR)
921 next_sign_typenr = 1;
922 if (next_sign_typenr == start)
923 {
924 vim_free(sp);
925 emsg(_("E612: Too many signs defined"));
926 return NULL;
927 }
928 lp = first_sign; // start all over
929 continue;
930 }
931 lp = lp->sn_next;
932 }
933
934 sp->sn_typenr = next_sign_typenr;
935 if (++next_sign_typenr == MAX_TYPENR)
936 next_sign_typenr = 1; // wrap around
937
938 sp->sn_name = vim_strsave(name);
939 if (sp->sn_name == NULL) // out of memory
940 {
941 vim_free(sp);
942 return NULL;
943 }
944
945 return sp;
946}
947
948/*
949 * Initialize the icon information for a new sign
950 */
951 static void
952sign_define_init_icon(sign_T *sp, char_u *icon)
953{
954 vim_free(sp->sn_icon);
955 sp->sn_icon = vim_strsave(icon);
956 backslash_halve(sp->sn_icon);
957# ifdef FEAT_SIGN_ICONS
958 if (gui.in_use)
959 {
960 out_flush();
961 if (sp->sn_image != NULL)
962 gui_mch_destroy_sign(sp->sn_image);
963 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
964 }
965# endif
966}
967
968/*
969 * Initialize the text for a new sign
970 */
971 static int
972sign_define_init_text(sign_T *sp, char_u *text)
973{
974 char_u *s;
975 char_u *endp;
976 int cells;
977 int len;
978
979 endp = text + (int)STRLEN(text);
980
981 // Remove backslashes so that it is possible to use a space.
982 for (s = text; s + 1 < endp; ++s)
983 if (*s == '\\')
984 {
985 STRMOVE(s, s + 1);
986 --endp;
987 }
988
989 // Count cells and check for non-printable chars
Bram Moolenaar03142362019-01-18 22:01:42 +0100990 if (has_mbyte)
991 {
992 cells = 0;
993 for (s = text; s < endp; s += (*mb_ptr2len)(s))
994 {
995 if (!vim_isprintc((*mb_ptr2char)(s)))
996 break;
997 cells += (*mb_ptr2cells)(s);
998 }
999 }
1000 else
Bram Moolenaar03142362019-01-18 22:01:42 +01001001 {
1002 for (s = text; s < endp; ++s)
1003 if (!vim_isprintc(*s))
1004 break;
1005 cells = (int)(s - text);
1006 }
1007
1008 // Currently sign text must be one or two display cells
1009 if (s != endp || cells < 1 || cells > 2)
1010 {
1011 semsg(_("E239: Invalid sign text: %s"), text);
1012 return FAIL;
1013 }
1014
1015 vim_free(sp->sn_text);
1016 // Allocate one byte more if we need to pad up
1017 // with a space.
1018 len = (int)(endp - text + ((cells == 1) ? 1 : 0));
1019 sp->sn_text = vim_strnsave(text, len);
1020
1021 // For single character sign text, pad with a space.
1022 if (sp->sn_text != NULL && cells == 1)
1023 STRCPY(sp->sn_text + len - 1, " ");
1024
1025 return OK;
1026}
1027
1028/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001029 * Define a new sign or update an existing sign
1030 */
1031 int
1032sign_define_by_name(
1033 char_u *name,
1034 char_u *icon,
1035 char_u *linehl,
1036 char_u *text,
1037 char_u *texthl)
1038{
1039 sign_T *sp_prev;
1040 sign_T *sp;
1041
1042 sp = sign_find(name, &sp_prev);
1043 if (sp == NULL)
1044 {
Bram Moolenaar03142362019-01-18 22:01:42 +01001045 sp = alloc_new_sign(name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001046 if (sp == NULL)
1047 return FAIL;
1048
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001049 // add the new sign to the list of signs
1050 if (sp_prev == NULL)
1051 first_sign = sp;
1052 else
1053 sp_prev->sn_next = sp;
1054 }
Bram Moolenaarbf0acff2020-01-09 21:01:59 +01001055 else
1056 {
1057 win_T *wp;
1058
1059 // Signs may already exist, a redraw is needed in windows with a
1060 // non-empty sign list.
1061 FOR_ALL_WINDOWS(wp)
1062 if (wp->w_buffer->b_signlist != NULL)
1063 redraw_buf_later(wp->w_buffer, NOT_VALID);
1064 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001065
1066 // set values for a defined sign.
1067 if (icon != NULL)
Bram Moolenaar03142362019-01-18 22:01:42 +01001068 sign_define_init_icon(sp, icon);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001069
Bram Moolenaar03142362019-01-18 22:01:42 +01001070 if (text != NULL && (sign_define_init_text(sp, text) == FAIL))
1071 return FAIL;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001072
1073 if (linehl != NULL)
1074 sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl));
1075
1076 if (texthl != NULL)
1077 sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl));
1078
1079 return OK;
1080}
1081
1082/*
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001083 * Return TRUE if sign "name" exists.
1084 */
1085 int
1086sign_exists_by_name(char_u *name)
1087{
1088 return sign_find(name, NULL) != NULL;
1089}
1090
1091/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001092 * Free the sign specified by 'name'.
1093 */
1094 int
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001095sign_undefine_by_name(char_u *name, int give_error)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001096{
1097 sign_T *sp_prev;
1098 sign_T *sp;
1099
1100 sp = sign_find(name, &sp_prev);
1101 if (sp == NULL)
1102 {
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001103 if (give_error)
1104 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001105 return FAIL;
1106 }
1107 sign_undefine(sp, sp_prev);
1108
1109 return OK;
1110}
1111
1112/*
1113 * List the signs matching 'name'
1114 */
1115 static void
1116sign_list_by_name(char_u *name)
1117{
1118 sign_T *sp;
1119
1120 sp = sign_find(name, NULL);
1121 if (sp != NULL)
1122 sign_list_defined(sp);
1123 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001124 semsg(_("E155: Unknown sign: %s"), name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001125}
1126
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001127 static void
1128may_force_numberwidth_recompute(buf_T *buf, int unplace)
1129{
1130 tabpage_T *tp;
1131 win_T *wp;
1132
1133 FOR_ALL_TAB_WINDOWS(tp, wp)
1134 if (wp->w_buffer == buf
1135 && (wp->w_p_nu || wp->w_p_rnu)
1136 && (unplace || wp->w_nrwidth_width < 2)
1137 && (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u'))
1138 wp->w_nrwidth_line_count = 0;
1139}
1140
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001141/*
Bram Moolenaar8144acb2019-01-14 23:08:18 +01001142 * Place a sign at the specified file location or update a sign.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001143 */
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001144 int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001145sign_place(
1146 int *sign_id,
1147 char_u *sign_group,
1148 char_u *sign_name,
1149 buf_T *buf,
1150 linenr_T lnum,
1151 int prio)
1152{
1153 sign_T *sp;
1154
1155 // Check for reserved character '*' in group name
1156 if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0'))
1157 return FAIL;
1158
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001159 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001160 if (STRCMP(sp->sn_name, sign_name) == 0)
1161 break;
1162 if (sp == NULL)
1163 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001164 semsg(_("E155: Unknown sign: %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001165 return FAIL;
1166 }
1167 if (*sign_id == 0)
1168 *sign_id = sign_group_get_next_signid(buf, sign_group);
1169
1170 if (lnum > 0)
1171 // ":sign place {id} line={lnum} name={name} file={fname}":
1172 // place a sign
1173 buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr);
1174 else
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02001175 // ":sign place {id} file={fname}": change sign type and/or priority
1176 lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr,
1177 prio);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001178 if (lnum > 0)
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001179 {
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001180 redraw_buf_line_later(buf, lnum);
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001181
1182 // When displaying signs in the 'number' column, if the width of the
1183 // number column is less than 2, then force recomputing the width.
1184 may_force_numberwidth_recompute(buf, FALSE);
1185 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001186 else
1187 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001188 semsg(_("E885: Not possible to change sign %s"), sign_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001189 return FAIL;
1190 }
1191
1192 return OK;
1193}
1194
1195/*
1196 * Unplace the specified sign
1197 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001198 static int
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001199sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum)
1200{
1201 if (buf->b_signlist == NULL) // No signs in the buffer
1202 return OK;
1203
1204 if (sign_id == 0)
1205 {
1206 // Delete all the signs in the specified buffer
1207 redraw_buf_later(buf, NOT_VALID);
1208 buf_delete_signs(buf, sign_group);
1209 }
1210 else
1211 {
1212 linenr_T lnum;
1213
1214 // Delete only the specified signs
1215 lnum = buf_delsign(buf, atlnum, sign_id, sign_group);
1216 if (lnum == 0)
1217 return FAIL;
1218 }
1219
Bram Moolenaare4b407f2019-07-04 11:59:28 +02001220 // When all the signs in a buffer are removed, force recomputing the
1221 // number column width (if enabled) in all the windows displaying the
1222 // buffer if 'signcolumn' is set to 'number' in that window.
1223 if (buf->b_signlist == NULL)
1224 may_force_numberwidth_recompute(buf, TRUE);
1225
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001226 return OK;
1227}
1228
1229/*
1230 * Unplace the sign at the current cursor line.
1231 */
1232 static void
1233sign_unplace_at_cursor(char_u *groupname)
1234{
1235 int id = -1;
1236
1237 id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname);
1238 if (id > 0)
1239 sign_unplace(id, groupname, curwin->w_buffer, curwin->w_cursor.lnum);
1240 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001241 emsg(_("E159: Missing sign number"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001242}
1243
1244/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001245 * Jump to a sign.
1246 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001247 static linenr_T
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001248sign_jump(int sign_id, char_u *sign_group, buf_T *buf)
1249{
1250 linenr_T lnum;
1251
1252 if ((lnum = buf_findsign(buf, sign_id, sign_group)) <= 0)
1253 {
Bram Moolenaarb5443cc2019-01-15 20:19:40 +01001254 semsg(_("E157: Invalid sign ID: %d"), sign_id);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001255 return -1;
1256 }
1257
1258 // goto a sign ...
1259 if (buf_jump_open_win(buf) != NULL)
1260 { // ... in a current window
1261 curwin->w_cursor.lnum = lnum;
1262 check_cursor_lnum();
1263 beginline(BL_WHITE);
1264 }
1265 else
1266 { // ... not currently in a window
1267 char_u *cmd;
1268
1269 if (buf->b_fname == NULL)
1270 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001271 emsg(_("E934: Cannot jump to a buffer that does not have a name"));
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001272 return -1;
1273 }
Bram Moolenaar18a4ba22019-05-24 19:39:03 +02001274 cmd = alloc(STRLEN(buf->b_fname) + 25);
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001275 if (cmd == NULL)
1276 return -1;
1277 sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname);
1278 do_cmdline_cmd(cmd);
1279 vim_free(cmd);
1280 }
1281# ifdef FEAT_FOLDING
1282 foldOpenCursor();
1283# endif
1284
1285 return lnum;
1286}
1287
1288/*
1289 * ":sign define {name} ..." command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001290 */
1291 static void
1292sign_define_cmd(char_u *sign_name, char_u *cmdline)
1293{
1294 char_u *arg;
1295 char_u *p = cmdline;
1296 char_u *icon = NULL;
1297 char_u *text = NULL;
1298 char_u *linehl = NULL;
1299 char_u *texthl = NULL;
1300 int failed = FALSE;
1301
1302 // set values for a defined sign.
1303 for (;;)
1304 {
1305 arg = skipwhite(p);
1306 if (*arg == NUL)
1307 break;
1308 p = skiptowhite_esc(arg);
1309 if (STRNCMP(arg, "icon=", 5) == 0)
1310 {
1311 arg += 5;
1312 icon = vim_strnsave(arg, (int)(p - arg));
1313 }
1314 else if (STRNCMP(arg, "text=", 5) == 0)
1315 {
1316 arg += 5;
1317 text = vim_strnsave(arg, (int)(p - arg));
1318 }
1319 else if (STRNCMP(arg, "linehl=", 7) == 0)
1320 {
1321 arg += 7;
1322 linehl = vim_strnsave(arg, (int)(p - arg));
1323 }
1324 else if (STRNCMP(arg, "texthl=", 7) == 0)
1325 {
1326 arg += 7;
1327 texthl = vim_strnsave(arg, (int)(p - arg));
1328 }
1329 else
1330 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001331 semsg(_(e_invarg2), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001332 failed = TRUE;
1333 break;
1334 }
1335 }
1336
1337 if (!failed)
1338 sign_define_by_name(sign_name, icon, linehl, text, texthl);
1339
1340 vim_free(icon);
1341 vim_free(text);
1342 vim_free(linehl);
1343 vim_free(texthl);
1344}
1345
1346/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001347 * ":sign place" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001348 */
1349 static void
1350sign_place_cmd(
1351 buf_T *buf,
1352 linenr_T lnum,
1353 char_u *sign_name,
1354 int id,
1355 char_u *group,
1356 int prio)
1357{
1358 if (id <= 0)
1359 {
1360 // List signs placed in a file/buffer
1361 // :sign place file={fname}
1362 // :sign place group={group} file={fname}
1363 // :sign place group=* file={fname}
1364 // :sign place buffer={nr}
1365 // :sign place group={group} buffer={nr}
1366 // :sign place group=* buffer={nr}
1367 // :sign place
1368 // :sign place group={group}
1369 // :sign place group=*
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001370 if (lnum >= 0 || sign_name != NULL
1371 || (group != NULL && *group == '\0'))
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001372 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001373 else
1374 sign_list_placed(buf, group);
1375 }
1376 else
1377 {
1378 // Place a new sign
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001379 if (sign_name == NULL || buf == NULL
1380 || (group != NULL && *group == '\0'))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001381 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001382 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001383 return;
1384 }
1385
1386 sign_place(&id, group, sign_name, buf, lnum, prio);
1387 }
1388}
1389
1390/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001391 * ":sign unplace" command
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001392 */
1393 static void
1394sign_unplace_cmd(
1395 buf_T *buf,
1396 linenr_T lnum,
1397 char_u *sign_name,
1398 int id,
1399 char_u *group)
1400{
1401 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0'))
1402 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001403 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001404 return;
1405 }
1406
1407 if (id == -2)
1408 {
1409 if (buf != NULL)
1410 // :sign unplace * file={fname}
1411 // :sign unplace * group={group} file={fname}
1412 // :sign unplace * group=* file={fname}
1413 // :sign unplace * buffer={nr}
1414 // :sign unplace * group={group} buffer={nr}
1415 // :sign unplace * group=* buffer={nr}
1416 sign_unplace(0, group, buf, 0);
1417 else
1418 // :sign unplace *
1419 // :sign unplace * group={group}
1420 // :sign unplace * group=*
1421 FOR_ALL_BUFFERS(buf)
1422 if (buf->b_signlist != NULL)
1423 buf_delete_signs(buf, group);
1424 }
1425 else
1426 {
1427 if (buf != NULL)
1428 // :sign unplace {id} file={fname}
1429 // :sign unplace {id} group={group} file={fname}
1430 // :sign unplace {id} group=* file={fname}
1431 // :sign unplace {id} buffer={nr}
1432 // :sign unplace {id} group={group} buffer={nr}
1433 // :sign unplace {id} group=* buffer={nr}
1434 sign_unplace(id, group, buf, 0);
1435 else
1436 {
1437 if (id == -1)
1438 {
1439 // :sign unplace group={group}
1440 // :sign unplace group=*
1441 sign_unplace_at_cursor(group);
1442 }
1443 else
1444 {
1445 // :sign unplace {id}
1446 // :sign unplace {id} group={group}
1447 // :sign unplace {id} group=*
1448 FOR_ALL_BUFFERS(buf)
1449 sign_unplace(id, group, buf, 0);
1450 }
1451 }
1452 }
1453}
1454
1455/*
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001456 * Jump to a placed sign commands:
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001457 * :sign jump {id} file={fname}
1458 * :sign jump {id} buffer={nr}
1459 * :sign jump {id} group={group} file={fname}
1460 * :sign jump {id} group={group} buffer={nr}
1461 */
1462 static void
1463sign_jump_cmd(
1464 buf_T *buf,
1465 linenr_T lnum,
1466 char_u *sign_name,
1467 int id,
1468 char_u *group)
1469{
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001470 if (sign_name == NULL && group == NULL && id == -1)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001471 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001472 emsg(_(e_argreq));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001473 return;
1474 }
1475
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001476 if (buf == NULL || (group != NULL && *group == '\0')
1477 || lnum >= 0 || sign_name != NULL)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001478 {
1479 // File or buffer is not specified or an empty group is used
1480 // or a line number or a sign name is specified.
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001481 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001482 return;
1483 }
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001484 (void)sign_jump(id, group, buf);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001485}
1486
1487/*
1488 * Parse the command line arguments for the ":sign place", ":sign unplace" and
1489 * ":sign jump" commands.
1490 * The supported arguments are: line={lnum} name={name} group={group}
1491 * priority={prio} and file={fname} or buffer={nr}.
1492 */
1493 static int
1494parse_sign_cmd_args(
1495 int cmd,
1496 char_u *arg,
1497 char_u **sign_name,
1498 int *signid,
1499 char_u **group,
1500 int *prio,
1501 buf_T **buf,
1502 linenr_T *lnum)
1503{
1504 char_u *arg1;
1505 char_u *name;
1506 char_u *filename = NULL;
Bram Moolenaarb589f952019-01-07 22:10:00 +01001507 int lnum_arg = FALSE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001508
1509 // first arg could be placed sign id
1510 arg1 = arg;
1511 if (VIM_ISDIGIT(*arg))
1512 {
1513 *signid = getdigits(&arg);
1514 if (!VIM_ISWHITE(*arg) && *arg != NUL)
1515 {
1516 *signid = -1;
1517 arg = arg1;
1518 }
1519 else
1520 arg = skipwhite(arg);
1521 }
1522
1523 while (*arg != NUL)
1524 {
1525 if (STRNCMP(arg, "line=", 5) == 0)
1526 {
1527 arg += 5;
1528 *lnum = atoi((char *)arg);
1529 arg = skiptowhite(arg);
Bram Moolenaarb589f952019-01-07 22:10:00 +01001530 lnum_arg = TRUE;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001531 }
1532 else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE)
1533 {
1534 if (*signid != -1)
1535 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001536 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001537 return FAIL;
1538 }
1539 *signid = -2;
1540 arg = skiptowhite(arg + 1);
1541 }
1542 else if (STRNCMP(arg, "name=", 5) == 0)
1543 {
1544 arg += 5;
1545 name = arg;
1546 arg = skiptowhite(arg);
1547 if (*arg != NUL)
1548 *arg++ = NUL;
1549 while (name[0] == '0' && name[1] != NUL)
1550 ++name;
1551 *sign_name = name;
1552 }
1553 else if (STRNCMP(arg, "group=", 6) == 0)
1554 {
1555 arg += 6;
1556 *group = arg;
1557 arg = skiptowhite(arg);
1558 if (*arg != NUL)
1559 *arg++ = NUL;
1560 }
1561 else if (STRNCMP(arg, "priority=", 9) == 0)
1562 {
1563 arg += 9;
1564 *prio = atoi((char *)arg);
1565 arg = skiptowhite(arg);
1566 }
1567 else if (STRNCMP(arg, "file=", 5) == 0)
1568 {
1569 arg += 5;
1570 filename = arg;
1571 *buf = buflist_findname_exp(arg);
1572 break;
1573 }
1574 else if (STRNCMP(arg, "buffer=", 7) == 0)
1575 {
1576 arg += 7;
1577 filename = arg;
1578 *buf = buflist_findnr((int)getdigits(&arg));
1579 if (*skipwhite(arg) != NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001580 emsg(_(e_trailing));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001581 break;
1582 }
1583 else
1584 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001585 emsg(_(e_invarg));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001586 return FAIL;
1587 }
1588 arg = skipwhite(arg);
1589 }
1590
1591 if (filename != NULL && *buf == NULL)
1592 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001593 semsg(_("E158: Invalid buffer name: %s"), filename);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001594 return FAIL;
1595 }
1596
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001597 // If the filename is not supplied for the sign place or the sign jump
1598 // command, then use the current buffer.
Bram Moolenaarb589f952019-01-07 22:10:00 +01001599 if (filename == NULL && ((cmd == SIGNCMD_PLACE && lnum_arg)
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001600 || cmd == SIGNCMD_JUMP))
Bram Moolenaarb328cca2019-01-06 16:24:01 +01001601 *buf = curwin->w_buffer;
1602
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001603 return OK;
1604}
1605
1606/*
1607 * ":sign" command
1608 */
1609 void
1610ex_sign(exarg_T *eap)
1611{
1612 char_u *arg = eap->arg;
1613 char_u *p;
1614 int idx;
1615 sign_T *sp;
1616 buf_T *buf = NULL;
1617
1618 // Parse the subcommand.
1619 p = skiptowhite(arg);
1620 idx = sign_cmd_idx(arg, p);
1621 if (idx == SIGNCMD_LAST)
1622 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001623 semsg(_("E160: Unknown sign command: %s"), arg);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001624 return;
1625 }
1626 arg = skipwhite(p);
1627
1628 if (idx <= SIGNCMD_LIST)
1629 {
1630 // Define, undefine or list signs.
1631 if (idx == SIGNCMD_LIST && *arg == NUL)
1632 {
1633 // ":sign list": list all defined signs
1634 for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next)
1635 sign_list_defined(sp);
1636 }
1637 else if (*arg == NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001638 emsg(_("E156: Missing sign name"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001639 else
1640 {
1641 char_u *name;
1642
1643 // Isolate the sign name. If it's a number skip leading zeroes,
1644 // so that "099" and "99" are the same sign. But keep "0".
1645 p = skiptowhite(arg);
1646 if (*p != NUL)
1647 *p++ = NUL;
1648 while (arg[0] == '0' && arg[1] != NUL)
1649 ++arg;
1650 name = vim_strsave(arg);
1651
1652 if (idx == SIGNCMD_DEFINE)
1653 sign_define_cmd(name, p);
1654 else if (idx == SIGNCMD_LIST)
1655 // ":sign list {name}"
1656 sign_list_by_name(name);
1657 else
1658 // ":sign undefine {name}"
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001659 sign_undefine_by_name(name, TRUE);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001660
1661 vim_free(name);
1662 return;
1663 }
1664 }
1665 else
1666 {
1667 int id = -1;
1668 linenr_T lnum = -1;
1669 char_u *sign_name = NULL;
1670 char_u *group = NULL;
1671 int prio = SIGN_DEF_PRIO;
1672
1673 // Parse command line arguments
1674 if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio,
1675 &buf, &lnum) == FAIL)
1676 return;
1677
1678 if (idx == SIGNCMD_PLACE)
1679 sign_place_cmd(buf, lnum, sign_name, id, group, prio);
1680 else if (idx == SIGNCMD_UNPLACE)
1681 sign_unplace_cmd(buf, lnum, sign_name, id, group);
1682 else if (idx == SIGNCMD_JUMP)
1683 sign_jump_cmd(buf, lnum, sign_name, id, group);
1684 }
1685}
1686
1687/*
1688 * Return information about a specified sign
1689 */
1690 static void
1691sign_getinfo(sign_T *sp, dict_T *retdict)
1692{
1693 char_u *p;
1694
1695 dict_add_string(retdict, "name", (char_u *)sp->sn_name);
1696 if (sp->sn_icon != NULL)
1697 dict_add_string(retdict, "icon", (char_u *)sp->sn_icon);
1698 if (sp->sn_text != NULL)
1699 dict_add_string(retdict, "text", (char_u *)sp->sn_text);
1700 if (sp->sn_line_hl > 0)
1701 {
1702 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1703 if (p == NULL)
1704 p = (char_u *)"NONE";
1705 dict_add_string(retdict, "linehl", (char_u *)p);
1706 }
1707 if (sp->sn_text_hl > 0)
1708 {
1709 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1710 if (p == NULL)
1711 p = (char_u *)"NONE";
1712 dict_add_string(retdict, "texthl", (char_u *)p);
1713 }
1714}
1715
1716/*
1717 * If 'name' is NULL, return a list of all the defined signs.
1718 * Otherwise, return information about the specified sign.
1719 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001720 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001721sign_getlist(char_u *name, list_T *retlist)
1722{
1723 sign_T *sp = first_sign;
1724 dict_T *dict;
1725
1726 if (name != NULL)
1727 {
1728 sp = sign_find(name, NULL);
1729 if (sp == NULL)
1730 return;
1731 }
1732
1733 for (; sp != NULL && !got_int; sp = sp->sn_next)
1734 {
1735 if ((dict = dict_alloc_id(aid_sign_getlist)) == NULL)
1736 return;
1737 if (list_append_dict(retlist, dict) == FAIL)
1738 return;
1739 sign_getinfo(sp, dict);
1740
1741 if (name != NULL) // handle only the specified sign
1742 break;
1743 }
1744}
1745
1746/*
1747 * Returns information about signs placed in a buffer as list of dicts.
1748 */
1749 void
1750get_buffer_signs(buf_T *buf, list_T *l)
1751{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001752 sign_entry_T *sign;
1753 dict_T *d;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001754
1755 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1756 {
1757 if ((d = sign_get_info(sign)) != NULL)
1758 list_append_dict(l, d);
1759 }
1760}
1761
1762/*
1763 * Return information about all the signs placed in a buffer
1764 */
1765 static void
1766sign_get_placed_in_buf(
1767 buf_T *buf,
1768 linenr_T lnum,
1769 int sign_id,
1770 char_u *sign_group,
1771 list_T *retlist)
1772{
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001773 dict_T *d;
1774 list_T *l;
1775 sign_entry_T *sign;
1776 dict_T *sdict;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001777
1778 if ((d = dict_alloc_id(aid_sign_getplaced_dict)) == NULL)
1779 return;
1780 list_append_dict(retlist, d);
1781
1782 dict_add_number(d, "bufnr", (long)buf->b_fnum);
1783
1784 if ((l = list_alloc_id(aid_sign_getplaced_list)) == NULL)
1785 return;
1786 dict_add_list(d, "signs", l);
1787
1788 FOR_ALL_SIGNS_IN_BUF(buf, sign)
1789 {
1790 if (!sign_in_group(sign, sign_group))
1791 continue;
Bram Moolenaar27a472c2019-01-09 21:47:30 +01001792 if ((lnum == 0 && sign_id == 0)
Bram Moolenaar6656c2e2019-10-24 15:00:04 +02001793 || (sign_id == 0 && lnum == sign->se_lnum)
1794 || (lnum == 0 && sign_id == sign->se_id)
1795 || (lnum == sign->se_lnum && sign_id == sign->se_id))
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001796 {
1797 if ((sdict = sign_get_info(sign)) != NULL)
1798 list_append_dict(l, sdict);
1799 }
1800 }
1801}
1802
1803/*
1804 * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the
1805 * sign placed at the line number. If 'lnum' is zero, return all the signs
1806 * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers.
1807 */
Bram Moolenaarb60d8512019-06-29 07:59:04 +02001808 static void
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001809sign_get_placed(
1810 buf_T *buf,
1811 linenr_T lnum,
1812 int sign_id,
1813 char_u *sign_group,
1814 list_T *retlist)
1815{
1816 if (buf != NULL)
1817 sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist);
1818 else
1819 {
1820 FOR_ALL_BUFFERS(buf)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001821 if (buf->b_signlist != NULL)
1822 sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001823 }
1824}
1825
1826# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1827/*
1828 * Allocate the icons. Called when the GUI has started. Allows defining
1829 * signs before it starts.
1830 */
1831 void
1832sign_gui_started(void)
1833{
1834 sign_T *sp;
1835
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001836 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001837 if (sp->sn_icon != NULL)
1838 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
1839}
1840# endif
1841
1842/*
1843 * List one sign.
1844 */
1845 static void
1846sign_list_defined(sign_T *sp)
1847{
1848 char_u *p;
1849
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001850 smsg("sign %s", sp->sn_name);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001851 if (sp->sn_icon != NULL)
1852 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001853 msg_puts(" icon=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001854 msg_outtrans(sp->sn_icon);
1855# ifdef FEAT_SIGN_ICONS
1856 if (sp->sn_image == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001857 msg_puts(_(" (NOT FOUND)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001858# else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001859 msg_puts(_(" (not supported)"));
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001860# endif
1861 }
1862 if (sp->sn_text != NULL)
1863 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001864 msg_puts(" text=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001865 msg_outtrans(sp->sn_text);
1866 }
1867 if (sp->sn_line_hl > 0)
1868 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001869 msg_puts(" linehl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001870 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
1871 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001872 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001873 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001874 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001875 }
1876 if (sp->sn_text_hl > 0)
1877 {
Bram Moolenaar32526b32019-01-19 17:43:09 +01001878 msg_puts(" texthl=");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001879 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
1880 if (p == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001881 msg_puts("NONE");
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001882 else
Bram Moolenaar32526b32019-01-19 17:43:09 +01001883 msg_puts((char *)p);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001884 }
1885}
1886
1887/*
1888 * Undefine a sign and free its memory.
1889 */
1890 static void
1891sign_undefine(sign_T *sp, sign_T *sp_prev)
1892{
1893 vim_free(sp->sn_name);
1894 vim_free(sp->sn_icon);
1895# ifdef FEAT_SIGN_ICONS
1896 if (sp->sn_image != NULL)
1897 {
1898 out_flush();
1899 gui_mch_destroy_sign(sp->sn_image);
1900 }
1901# endif
1902 vim_free(sp->sn_text);
1903 if (sp_prev == NULL)
1904 first_sign = sp->sn_next;
1905 else
1906 sp_prev->sn_next = sp->sn_next;
1907 vim_free(sp);
1908}
1909
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001910# if defined(FEAT_SIGN_ICONS) || defined(PROTO)
1911 void *
1912sign_get_image(
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001913 int typenr) // the attribute which may have a sign
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001914{
1915 sign_T *sp;
1916
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001917 FOR_ALL_SIGNS(sp)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001918 if (sp->sn_typenr == typenr)
1919 return sp->sn_image;
1920 return NULL;
1921}
1922# endif
1923
1924/*
1925 * Undefine/free all signs.
1926 */
1927 void
1928free_signs(void)
1929{
1930 while (first_sign != NULL)
1931 sign_undefine(first_sign, NULL);
1932}
1933
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001934static enum
1935{
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001936 EXP_SUBCMD, // expand :sign sub-commands
1937 EXP_DEFINE, // expand :sign define {name} args
1938 EXP_PLACE, // expand :sign place {id} args
Bram Moolenaar3678f652019-02-17 14:50:25 +01001939 EXP_LIST, // expand :sign place args
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01001940 EXP_UNPLACE, // expand :sign unplace"
Bram Moolenaar3678f652019-02-17 14:50:25 +01001941 EXP_SIGN_NAMES, // expand with name of placed signs
1942 EXP_SIGN_GROUPS // expand with name of placed sign groups
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001943} expand_what;
1944
1945/*
Bram Moolenaar3678f652019-02-17 14:50:25 +01001946 * Return the n'th sign name (used for command line completion)
1947 */
1948 static char_u *
1949get_nth_sign_name(int idx)
1950{
1951 int current_idx;
1952 sign_T *sp;
1953
1954 // Complete with name of signs already defined
1955 current_idx = 0;
Bram Moolenaaraeea7212020-04-02 18:50:46 +02001956 FOR_ALL_SIGNS(sp)
Bram Moolenaar3678f652019-02-17 14:50:25 +01001957 if (current_idx++ == idx)
1958 return sp->sn_name;
1959 return NULL;
1960}
1961
1962/*
1963 * Return the n'th sign group name (used for command line completion)
1964 */
1965 static char_u *
1966get_nth_sign_group_name(int idx)
1967{
1968 int current_idx;
1969 int todo;
1970 hashitem_T *hi;
1971 signgroup_T *group;
1972
1973 // Complete with name of sign groups already defined
1974 current_idx = 0;
1975 todo = (int)sg_table.ht_used;
1976 for (hi = sg_table.ht_array; todo > 0; ++hi)
1977 {
1978 if (!HASHITEM_EMPTY(hi))
1979 {
1980 --todo;
1981 if (current_idx++ == idx)
1982 {
1983 group = HI2SG(hi);
1984 return group->sg_name;
1985 }
1986 }
1987 }
1988 return NULL;
1989}
1990
1991/*
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001992 * Function given to ExpandGeneric() to obtain the sign command
1993 * expansion.
1994 */
1995 char_u *
1996get_sign_name(expand_T *xp UNUSED, int idx)
1997{
Bram Moolenaarbbea4702019-01-01 13:20:31 +01001998 switch (expand_what)
1999 {
2000 case EXP_SUBCMD:
2001 return (char_u *)cmds[idx];
2002 case EXP_DEFINE:
2003 {
2004 char *define_arg[] =
2005 {
2006 "icon=", "linehl=", "text=", "texthl=", NULL
2007 };
2008 return (char_u *)define_arg[idx];
2009 }
2010 case EXP_PLACE:
2011 {
2012 char *place_arg[] =
2013 {
2014 "line=", "name=", "group=", "priority=", "file=",
2015 "buffer=", NULL
2016 };
2017 return (char_u *)place_arg[idx];
2018 }
Bram Moolenaar3678f652019-02-17 14:50:25 +01002019 case EXP_LIST:
2020 {
2021 char *list_arg[] =
2022 {
2023 "group=", "file=", "buffer=", NULL
2024 };
2025 return (char_u *)list_arg[idx];
2026 }
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002027 case EXP_UNPLACE:
2028 {
2029 char *unplace_arg[] = { "group=", "file=", "buffer=", NULL };
2030 return (char_u *)unplace_arg[idx];
2031 }
2032 case EXP_SIGN_NAMES:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002033 return get_nth_sign_name(idx);
2034 case EXP_SIGN_GROUPS:
2035 return get_nth_sign_group_name(idx);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002036 default:
2037 return NULL;
2038 }
2039}
2040
2041/*
2042 * Handle command line completion for :sign command.
2043 */
2044 void
2045set_context_in_sign_cmd(expand_T *xp, char_u *arg)
2046{
2047 char_u *p;
2048 char_u *end_subcmd;
2049 char_u *last;
2050 int cmd_idx;
2051 char_u *begin_subcmd_args;
2052
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002053 // Default: expand subcommands.
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002054 xp->xp_context = EXPAND_SIGN;
2055 expand_what = EXP_SUBCMD;
2056 xp->xp_pattern = arg;
2057
2058 end_subcmd = skiptowhite(arg);
2059 if (*end_subcmd == NUL)
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002060 // expand subcmd name
2061 // :sign {subcmd}<CTRL-D>
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002062 return;
2063
2064 cmd_idx = sign_cmd_idx(arg, end_subcmd);
2065
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002066 // :sign {subcmd} {subcmd_args}
2067 // |
2068 // begin_subcmd_args
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002069 begin_subcmd_args = skipwhite(end_subcmd);
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002070
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002071 // expand last argument of subcmd
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002072
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002073 // :sign define {name} {args}...
2074 // |
2075 // p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002076
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002077 // Loop until reaching last argument.
Bram Moolenaar3678f652019-02-17 14:50:25 +01002078 p = begin_subcmd_args;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002079 do
2080 {
2081 p = skipwhite(p);
2082 last = p;
2083 p = skiptowhite(p);
2084 } while (*p != NUL);
2085
2086 p = vim_strchr(last, '=');
2087
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002088 // :sign define {name} {args}... {last}=
2089 // | |
2090 // last p
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002091 if (p == NULL)
2092 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002093 // Expand last argument name (before equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002094 xp->xp_pattern = last;
2095 switch (cmd_idx)
2096 {
2097 case SIGNCMD_DEFINE:
2098 expand_what = EXP_DEFINE;
2099 break;
2100 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002101 // List placed signs
2102 if (VIM_ISDIGIT(*begin_subcmd_args))
2103 // :sign place {id} {args}...
2104 expand_what = EXP_PLACE;
2105 else
2106 // :sign place {args}...
2107 expand_what = EXP_LIST;
2108 break;
2109 case SIGNCMD_LIST:
2110 case SIGNCMD_UNDEFINE:
2111 // :sign list <CTRL-D>
2112 // :sign undefine <CTRL-D>
2113 expand_what = EXP_SIGN_NAMES;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002114 break;
2115 case SIGNCMD_JUMP:
2116 case SIGNCMD_UNPLACE:
2117 expand_what = EXP_UNPLACE;
2118 break;
2119 default:
2120 xp->xp_context = EXPAND_NOTHING;
2121 }
2122 }
2123 else
2124 {
Bram Moolenaar6b7b7192019-01-11 13:42:41 +01002125 // Expand last argument value (after equal sign).
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002126 xp->xp_pattern = p + 1;
2127 switch (cmd_idx)
2128 {
2129 case SIGNCMD_DEFINE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002130 if (STRNCMP(last, "texthl", 6) == 0
2131 || STRNCMP(last, "linehl", 6) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002132 xp->xp_context = EXPAND_HIGHLIGHT;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002133 else if (STRNCMP(last, "icon", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002134 xp->xp_context = EXPAND_FILES;
2135 else
2136 xp->xp_context = EXPAND_NOTHING;
2137 break;
2138 case SIGNCMD_PLACE:
Bram Moolenaar3678f652019-02-17 14:50:25 +01002139 if (STRNCMP(last, "name", 4) == 0)
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002140 expand_what = EXP_SIGN_NAMES;
Bram Moolenaar3678f652019-02-17 14:50:25 +01002141 else if (STRNCMP(last, "group", 5) == 0)
2142 expand_what = EXP_SIGN_GROUPS;
2143 else if (STRNCMP(last, "file", 4) == 0)
2144 xp->xp_context = EXPAND_BUFFERS;
2145 else
2146 xp->xp_context = EXPAND_NOTHING;
2147 break;
2148 case SIGNCMD_UNPLACE:
2149 case SIGNCMD_JUMP:
2150 if (STRNCMP(last, "group", 5) == 0)
2151 expand_what = EXP_SIGN_GROUPS;
2152 else if (STRNCMP(last, "file", 4) == 0)
2153 xp->xp_context = EXPAND_BUFFERS;
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002154 else
2155 xp->xp_context = EXPAND_NOTHING;
2156 break;
2157 default:
2158 xp->xp_context = EXPAND_NOTHING;
2159 }
2160 }
2161}
Bram Moolenaarbbea4702019-01-01 13:20:31 +01002162
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002163/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002164 * Define a sign using the attributes in 'dict'. Returns 0 on success and -1 on
2165 * failure.
2166 */
2167 static int
2168sign_define_from_dict(char_u *name_arg, dict_T *dict)
2169{
2170 char_u *name = NULL;
2171 char_u *icon = NULL;
2172 char_u *linehl = NULL;
2173 char_u *text = NULL;
2174 char_u *texthl = NULL;
2175 int retval = -1;
2176
2177 if (name_arg == NULL)
2178 {
2179 if (dict == NULL)
2180 return -1;
2181 name = dict_get_string(dict, (char_u *)"name", TRUE);
2182 }
2183 else
2184 name = vim_strsave(name_arg);
2185 if (name == NULL || name[0] == NUL)
2186 goto cleanup;
2187 if (dict != NULL)
2188 {
2189 icon = dict_get_string(dict, (char_u *)"icon", TRUE);
2190 linehl = dict_get_string(dict, (char_u *)"linehl", TRUE);
2191 text = dict_get_string(dict, (char_u *)"text", TRUE);
2192 texthl = dict_get_string(dict, (char_u *)"texthl", TRUE);
2193 }
2194
2195 if (sign_define_by_name(name, icon, linehl, text, texthl) == OK)
2196 retval = 0;
2197
2198cleanup:
2199 vim_free(name);
2200 vim_free(icon);
2201 vim_free(linehl);
2202 vim_free(text);
2203 vim_free(texthl);
2204
2205 return retval;
2206}
2207
2208/*
2209 * Define multiple signs using attributes from list 'l' and store the return
2210 * values in 'retlist'.
2211 */
2212 static void
2213sign_define_multiple(list_T *l, list_T *retlist)
2214{
2215 listitem_T *li;
2216 int retval;
2217
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002218 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002219 {
2220 retval = -1;
2221 if (li->li_tv.v_type == VAR_DICT)
2222 retval = sign_define_from_dict(NULL, li->li_tv.vval.v_dict);
2223 else
2224 emsg(_(e_dictreq));
2225 list_append_number(retlist, retval);
2226 }
2227}
2228
2229/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002230 * "sign_define()" function
2231 */
2232 void
2233f_sign_define(typval_T *argvars, typval_T *rettv)
2234{
2235 char_u *name;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002236
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002237 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2238 {
2239 // Define multiple signs
2240 if (rettv_list_alloc(rettv) != OK)
2241 return;
2242
2243 sign_define_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2244 return;
2245 }
2246
2247 // Define a single sign
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002248 rettv->vval.v_number = -1;
2249
2250 name = tv_get_string_chk(&argvars[0]);
2251 if (name == NULL)
2252 return;
2253
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002254 if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_DICT)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002255 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002256 emsg(_(e_dictreq));
2257 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002258 }
2259
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002260 rettv->vval.v_number = sign_define_from_dict(name,
2261 argvars[1].v_type == VAR_DICT ? argvars[1].vval.v_dict : NULL);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002262}
2263
2264/*
2265 * "sign_getdefined()" function
2266 */
2267 void
2268f_sign_getdefined(typval_T *argvars, typval_T *rettv)
2269{
2270 char_u *name = NULL;
2271
2272 if (rettv_list_alloc_id(rettv, aid_sign_getdefined) != OK)
2273 return;
2274
2275 if (argvars[0].v_type != VAR_UNKNOWN)
2276 name = tv_get_string(&argvars[0]);
2277
2278 sign_getlist(name, rettv->vval.v_list);
2279}
2280
2281/*
2282 * "sign_getplaced()" function
2283 */
2284 void
2285f_sign_getplaced(typval_T *argvars, typval_T *rettv)
2286{
2287 buf_T *buf = NULL;
2288 dict_T *dict;
2289 dictitem_T *di;
2290 linenr_T lnum = 0;
2291 int sign_id = 0;
2292 char_u *group = NULL;
2293 int notanum = FALSE;
2294
2295 if (rettv_list_alloc_id(rettv, aid_sign_getplaced) != OK)
2296 return;
2297
2298 if (argvars[0].v_type != VAR_UNKNOWN)
2299 {
2300 // get signs placed in the specified buffer
2301 buf = get_buf_arg(&argvars[0]);
2302 if (buf == NULL)
2303 return;
2304
2305 if (argvars[1].v_type != VAR_UNKNOWN)
2306 {
2307 if (argvars[1].v_type != VAR_DICT ||
2308 ((dict = argvars[1].vval.v_dict) == NULL))
2309 {
2310 emsg(_(e_dictreq));
2311 return;
2312 }
2313 if ((di = dict_find(dict, (char_u *)"lnum", -1)) != NULL)
2314 {
2315 // get signs placed at this line
2316 (void)tv_get_number_chk(&di->di_tv, &notanum);
2317 if (notanum)
2318 return;
2319 lnum = tv_get_lnum(&di->di_tv);
2320 }
2321 if ((di = dict_find(dict, (char_u *)"id", -1)) != NULL)
2322 {
2323 // get sign placed with this identifier
2324 sign_id = (int)tv_get_number_chk(&di->di_tv, &notanum);
2325 if (notanum)
2326 return;
2327 }
2328 if ((di = dict_find(dict, (char_u *)"group", -1)) != NULL)
2329 {
2330 group = tv_get_string_chk(&di->di_tv);
2331 if (group == NULL)
2332 return;
2333 if (*group == '\0') // empty string means global group
2334 group = NULL;
2335 }
2336 }
2337 }
2338
2339 sign_get_placed(buf, lnum, sign_id, group, rettv->vval.v_list);
2340}
2341
2342/*
2343 * "sign_jump()" function
2344 */
2345 void
2346f_sign_jump(typval_T *argvars, typval_T *rettv)
2347{
2348 int sign_id;
2349 char_u *sign_group = NULL;
2350 buf_T *buf;
2351 int notanum = FALSE;
2352
2353 rettv->vval.v_number = -1;
2354
2355 // Sign identifier
2356 sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
2357 if (notanum)
2358 return;
2359 if (sign_id <= 0)
2360 {
2361 emsg(_(e_invarg));
2362 return;
2363 }
2364
2365 // Sign group
2366 sign_group = tv_get_string_chk(&argvars[1]);
2367 if (sign_group == NULL)
2368 return;
2369 if (sign_group[0] == '\0')
2370 sign_group = NULL; // global sign group
2371 else
2372 {
2373 sign_group = vim_strsave(sign_group);
2374 if (sign_group == NULL)
2375 return;
2376 }
2377
2378 // Buffer to place the sign
2379 buf = get_buf_arg(&argvars[2]);
2380 if (buf == NULL)
2381 goto cleanup;
2382
2383 rettv->vval.v_number = sign_jump(sign_id, sign_group, buf);
2384
2385cleanup:
2386 vim_free(sign_group);
2387}
2388
2389/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002390 * Place a new sign using the values specified in dict 'dict'. Returns the sign
2391 * identifier if successfully placed, otherwise returns 0.
2392 */
2393 static int
2394sign_place_from_dict(
2395 typval_T *id_tv,
2396 typval_T *group_tv,
2397 typval_T *name_tv,
2398 typval_T *buf_tv,
2399 dict_T *dict)
2400{
2401 int sign_id = 0;
2402 char_u *group = NULL;
2403 char_u *sign_name = NULL;
2404 buf_T *buf = NULL;
2405 dictitem_T *di;
2406 linenr_T lnum = 0;
2407 int prio = SIGN_DEF_PRIO;
2408 int notanum = FALSE;
2409 int ret_sign_id = -1;
2410
2411 // sign identifier
2412 if (id_tv == NULL)
2413 {
2414 di = dict_find(dict, (char_u *)"id", -1);
2415 if (di != NULL)
2416 id_tv = &di->di_tv;
2417 }
2418 if (id_tv == NULL)
2419 sign_id = 0;
2420 else
2421 {
2422 sign_id = tv_get_number_chk(id_tv, &notanum);
2423 if (notanum)
2424 return -1;
2425 if (sign_id < 0)
2426 {
2427 emsg(_(e_invarg));
2428 return -1;
2429 }
2430 }
2431
2432 // sign group
2433 if (group_tv == NULL)
2434 {
2435 di = dict_find(dict, (char_u *)"group", -1);
2436 if (di != NULL)
2437 group_tv = &di->di_tv;
2438 }
2439 if (group_tv == NULL)
2440 group = NULL; // global group
2441 else
2442 {
2443 group = tv_get_string_chk(group_tv);
2444 if (group == NULL)
2445 goto cleanup;
2446 if (group[0] == '\0') // global sign group
2447 group = NULL;
2448 else
2449 {
2450 group = vim_strsave(group);
2451 if (group == NULL)
2452 return -1;
2453 }
2454 }
2455
2456 // sign name
2457 if (name_tv == NULL)
2458 {
2459 di = dict_find(dict, (char_u *)"name", -1);
2460 if (di != NULL)
2461 name_tv = &di->di_tv;
2462 }
2463 if (name_tv == NULL)
2464 goto cleanup;
2465 sign_name = tv_get_string_chk(name_tv);
2466 if (sign_name == NULL)
2467 goto cleanup;
2468
2469 // buffer to place the sign
2470 if (buf_tv == NULL)
2471 {
2472 di = dict_find(dict, (char_u *)"buffer", -1);
2473 if (di != NULL)
2474 buf_tv = &di->di_tv;
2475 }
2476 if (buf_tv == NULL)
2477 goto cleanup;
2478 buf = get_buf_arg(buf_tv);
2479 if (buf == NULL)
2480 goto cleanup;
2481
2482 // line number of the sign
2483 di = dict_find(dict, (char_u *)"lnum", -1);
2484 if (di != NULL)
2485 {
Bram Moolenaar42aff462019-08-21 13:20:29 +02002486 lnum = tv_get_lnum(&di->di_tv);
2487 if (lnum <= 0)
2488 {
2489 emsg(_(e_invarg));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002490 goto cleanup;
Bram Moolenaar42aff462019-08-21 13:20:29 +02002491 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002492 }
2493
2494 // sign priority
2495 di = dict_find(dict, (char_u *)"priority", -1);
2496 if (di != NULL)
2497 {
2498 prio = (int)tv_get_number_chk(&di->di_tv, &notanum);
2499 if (notanum)
2500 goto cleanup;
2501 }
2502
2503 if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK)
2504 ret_sign_id = sign_id;
2505
2506cleanup:
2507 vim_free(group);
2508
2509 return ret_sign_id;
2510}
2511
2512/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002513 * "sign_place()" function
2514 */
2515 void
2516f_sign_place(typval_T *argvars, typval_T *rettv)
2517{
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002518 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002519
2520 rettv->vval.v_number = -1;
2521
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002522 if (argvars[4].v_type != VAR_UNKNOWN
2523 && (argvars[4].v_type != VAR_DICT
2524 || ((dict = argvars[4].vval.v_dict) == NULL)))
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002525 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002526 emsg(_(e_dictreq));
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002527 return;
2528 }
2529
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002530 rettv->vval.v_number = sign_place_from_dict(&argvars[0], &argvars[1],
2531 &argvars[2], &argvars[3], dict);
2532}
2533
2534/*
2535 * "sign_placelist()" function. Place multiple signs.
2536 */
2537 void
2538f_sign_placelist(typval_T *argvars, typval_T *rettv)
2539{
2540 listitem_T *li;
2541 int sign_id;
2542
2543 if (rettv_list_alloc(rettv) != OK)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002544 return;
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002545
2546 if (argvars[0].v_type != VAR_LIST)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002547 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002548 emsg(_(e_listreq));
2549 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002550 }
2551
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002552 // Process the List of sign attributes
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002553 FOR_ALL_LIST_ITEMS(argvars[0].vval.v_list, li)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002554 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002555 sign_id = -1;
2556 if (li->li_tv.v_type == VAR_DICT)
2557 sign_id = sign_place_from_dict(NULL, NULL, NULL, NULL,
2558 li->li_tv.vval.v_dict);
2559 else
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002560 emsg(_(e_dictreq));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002561 list_append_number(rettv->vval.v_list, sign_id);
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002562 }
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002563}
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002564
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002565/*
2566 * Undefine multiple signs
2567 */
2568 static void
2569sign_undefine_multiple(list_T *l, list_T *retlist)
2570{
2571 char_u *name;
2572 listitem_T *li;
2573 int retval;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002574
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002575 FOR_ALL_LIST_ITEMS(l, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002576 {
2577 retval = -1;
2578 name = tv_get_string_chk(&li->li_tv);
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002579 if (name != NULL && (sign_undefine_by_name(name, TRUE) == OK))
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002580 retval = 0;
2581 list_append_number(retlist, retval);
2582 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002583}
2584
2585/*
2586 * "sign_undefine()" function
2587 */
2588 void
2589f_sign_undefine(typval_T *argvars, typval_T *rettv)
2590{
2591 char_u *name;
2592
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002593 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN)
2594 {
2595 // Undefine multiple signs
2596 if (rettv_list_alloc(rettv) != OK)
2597 return;
2598
2599 sign_undefine_multiple(argvars[0].vval.v_list, rettv->vval.v_list);
2600 return;
2601 }
2602
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002603 rettv->vval.v_number = -1;
2604
2605 if (argvars[0].v_type == VAR_UNKNOWN)
2606 {
2607 // Free all the signs
2608 free_signs();
2609 rettv->vval.v_number = 0;
2610 }
2611 else
2612 {
2613 // Free only the specified sign
2614 name = tv_get_string_chk(&argvars[0]);
2615 if (name == NULL)
2616 return;
2617
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002618 if (sign_undefine_by_name(name, TRUE) == OK)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002619 rettv->vval.v_number = 0;
2620 }
2621}
2622
2623/*
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002624 * Unplace the sign with attributes specified in 'dict'. Returns 0 on success
2625 * and -1 on failure.
2626 */
2627 static int
2628sign_unplace_from_dict(typval_T *group_tv, dict_T *dict)
2629{
2630 dictitem_T *di;
2631 int sign_id = 0;
2632 buf_T *buf = NULL;
2633 char_u *group = NULL;
2634 int retval = -1;
2635
2636 // sign group
2637 if (group_tv != NULL)
2638 group = tv_get_string(group_tv);
2639 else
2640 group = dict_get_string(dict, (char_u *)"group", FALSE);
2641 if (group != NULL)
2642 {
2643 if (group[0] == '\0') // global sign group
2644 group = NULL;
2645 else
2646 {
2647 group = vim_strsave(group);
2648 if (group == NULL)
2649 return -1;
2650 }
2651 }
2652
2653 if (dict != NULL)
2654 {
2655 if ((di = dict_find(dict, (char_u *)"buffer", -1)) != NULL)
2656 {
2657 buf = get_buf_arg(&di->di_tv);
2658 if (buf == NULL)
2659 goto cleanup;
2660 }
2661 if (dict_find(dict, (char_u *)"id", -1) != NULL)
2662 {
2663 sign_id = dict_get_number(dict, (char_u *)"id");
2664 if (sign_id <= 0)
2665 {
2666 emsg(_(e_invarg));
2667 goto cleanup;
2668 }
2669 }
2670 }
2671
2672 if (buf == NULL)
2673 {
2674 // Delete the sign in all the buffers
2675 retval = 0;
2676 FOR_ALL_BUFFERS(buf)
2677 if (sign_unplace(sign_id, group, buf, 0) != OK)
2678 retval = -1;
2679 }
2680 else if (sign_unplace(sign_id, group, buf, 0) == OK)
2681 retval = 0;
2682
2683cleanup:
2684 vim_free(group);
2685
2686 return retval;
2687}
2688
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002689 sign_entry_T *
2690get_first_valid_sign(win_T *wp)
2691{
2692 sign_entry_T *sign = wp->w_buffer->b_signlist;
2693
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +01002694# ifdef FEAT_PROP_POPUP
Bram Moolenaar72570732019-11-30 14:21:53 +01002695 while (sign != NULL && !sign_group_for_window(sign, wp))
Bram Moolenaar4eb7dae2019-11-12 22:33:45 +01002696 sign = sign->se_next;
2697# endif
2698 return sign;
2699}
2700
2701/*
2702 * Return TRUE when window "wp" has a column to draw signs in.
2703 */
2704 int
2705signcolumn_on(win_T *wp)
2706{
2707 // If 'signcolumn' is set to 'number', signs are displayed in the 'number'
2708 // column (if present). Otherwise signs are to be displayed in the sign
2709 // column.
2710 if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u')
2711 return get_first_valid_sign(wp) != NULL && !wp->w_p_nu && !wp->w_p_rnu;
2712
2713 if (*wp->w_p_scl == 'n')
2714 return FALSE;
2715 if (*wp->w_p_scl == 'y')
2716 return TRUE;
2717 return (get_first_valid_sign(wp) != NULL
2718# ifdef FEAT_NETBEANS_INTG
2719 || wp->w_buffer->b_has_sign_column
2720# endif
2721 );
2722}
2723
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002724/*
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002725 * "sign_unplace()" function
2726 */
2727 void
2728f_sign_unplace(typval_T *argvars, typval_T *rettv)
2729{
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002730 dict_T *dict = NULL;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002731
2732 rettv->vval.v_number = -1;
2733
2734 if (argvars[0].v_type != VAR_STRING)
2735 {
2736 emsg(_(e_invarg));
2737 return;
2738 }
2739
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002740 if (argvars[1].v_type != VAR_UNKNOWN)
2741 {
2742 if (argvars[1].v_type != VAR_DICT)
2743 {
2744 emsg(_(e_dictreq));
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002745 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002746 }
2747 dict = argvars[1].vval.v_dict;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002748 }
2749
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002750 rettv->vval.v_number = sign_unplace_from_dict(&argvars[0], dict);
2751}
2752
2753/*
2754 * "sign_unplacelist()" function
2755 */
2756 void
2757f_sign_unplacelist(typval_T *argvars, typval_T *rettv)
2758{
2759 listitem_T *li;
2760 int retval;
2761
2762 if (rettv_list_alloc(rettv) != OK)
2763 return;
2764
2765 if (argvars[0].v_type != VAR_LIST)
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002766 {
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002767 emsg(_(e_listreq));
2768 return;
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002769 }
2770
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002771 FOR_ALL_LIST_ITEMS(argvars[0].vval.v_list, li)
Bram Moolenaar809ce4d2019-07-13 21:21:40 +02002772 {
2773 retval = -1;
2774 if (li->li_tv.v_type == VAR_DICT)
2775 retval = sign_unplace_from_dict(NULL, li->li_tv.vval.v_dict);
2776 else
2777 emsg(_(e_dictreq));
2778 list_append_number(rettv->vval.v_list, retval);
2779 }
Bram Moolenaarb60d8512019-06-29 07:59:04 +02002780}
2781
Bram Moolenaar63d9e732019-12-05 21:10:38 +01002782#endif // FEAT_SIGNS