Mercurial > vim
comparison src/sign.c @ 15330:a6330a49e036 v8.1.0673
patch 8.1.0673: functionality for signs is spread out over several files
commit https://github.com/vim/vim/commit/bbea47075cc4e7826e9f8c203e4272ba023ed7b0
Author: Bram Moolenaar <Bram@vim.org>
Date: Tue Jan 1 13:20:31 2019 +0100
patch 8.1.0673: functionality for signs is spread out over several files
Problem: Functionality for signs is spread out over several files.
Solution: Move most of the sign functionality into sign.c. (Yegappan
Lakshmanan, closes #3751)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Tue, 01 Jan 2019 13:30:09 +0100 |
parents | |
children | 0d76dd701e49 |
comparison
equal
deleted
inserted
replaced
15329:9a739a3d145e | 15330:a6330a49e036 |
---|---|
1 /* 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 */ | |
21 typedef struct sign sign_T; | |
22 | |
23 struct sign | |
24 { | |
25 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 */ | |
29 # ifdef FEAT_SIGN_ICONS | |
30 void *sn_image; /* icon image */ | |
31 # endif | |
32 char_u *sn_text; /* text used instead of pixmap */ | |
33 int sn_line_hl; /* highlight ID for line */ | |
34 int sn_text_hl; /* highlight ID for text */ | |
35 }; | |
36 | |
37 static sign_T *first_sign = NULL; | |
38 static int next_sign_typenr = 1; | |
39 | |
40 static void sign_list_defined(sign_T *sp); | |
41 static void sign_undefine(sign_T *sp, sign_T *sp_prev); | |
42 | |
43 static 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 | |
60 static hashtab_T sg_table; // sign group (signgroup_T) hashtable | |
61 static int next_sign_id = 1; // next sign id in the global group | |
62 | |
63 /* | |
64 * Initialize data needed for managing signs | |
65 */ | |
66 void | |
67 init_signs(void) | |
68 { | |
69 hash_init(&sg_table); // sign group hash table | |
70 } | |
71 | |
72 /* | |
73 * A new sign in group 'groupname' is added. If the group is not present, | |
74 * create it. Otherwise reference the group. | |
75 */ | |
76 static signgroup_T * | |
77 sign_group_ref(char_u *groupname) | |
78 { | |
79 hash_T hash; | |
80 hashitem_T *hi; | |
81 signgroup_T *group; | |
82 | |
83 hash = hash_hash(groupname); | |
84 hi = hash_lookup(&sg_table, groupname, hash); | |
85 if (HASHITEM_EMPTY(hi)) | |
86 { | |
87 // new group | |
88 group = (signgroup_T *)alloc( | |
89 (unsigned)(sizeof(signgroup_T) + STRLEN(groupname))); | |
90 if (group == NULL) | |
91 return NULL; | |
92 STRCPY(group->sg_name, groupname); | |
93 group->refcount = 1; | |
94 group->next_sign_id = 1; | |
95 hash_add_item(&sg_table, hi, group->sg_name, hash); | |
96 } | |
97 else | |
98 { | |
99 // existing group | |
100 group = HI2SG(hi); | |
101 group->refcount++; | |
102 } | |
103 | |
104 return group; | |
105 } | |
106 | |
107 /* | |
108 * A sign in group 'groupname' is removed. If all the signs in this group are | |
109 * removed, then remove the group. | |
110 */ | |
111 static void | |
112 sign_group_unref(char_u *groupname) | |
113 { | |
114 hashitem_T *hi; | |
115 signgroup_T *group; | |
116 | |
117 hi = hash_find(&sg_table, groupname); | |
118 if (!HASHITEM_EMPTY(hi)) | |
119 { | |
120 group = HI2SG(hi); | |
121 group->refcount--; | |
122 if (group->refcount == 0) | |
123 { | |
124 // All the signs in this group are removed | |
125 hash_remove(&sg_table, hi); | |
126 vim_free(group); | |
127 } | |
128 } | |
129 } | |
130 | |
131 /* | |
132 * Returns TRUE if 'sign' is in 'group'. | |
133 * A sign can either be in the global group (sign->group == NULL) | |
134 * or in a named group. If 'group' is '*', then the sign is part of the group. | |
135 */ | |
136 static int | |
137 sign_in_group(signlist_T *sign, char_u *group) | |
138 { | |
139 return ((group != NULL && STRCMP(group, "*") == 0) | |
140 || (group == NULL && sign->group == NULL) | |
141 || (group != NULL && sign->group != NULL | |
142 && STRCMP(group, sign->group->sg_name) == 0)); | |
143 } | |
144 | |
145 /* | |
146 * Get the next free sign identifier in the specified group | |
147 */ | |
148 static int | |
149 sign_group_get_next_signid(buf_T *buf, char_u *groupname) | |
150 { | |
151 int id = 1; | |
152 signgroup_T *group = NULL; | |
153 signlist_T *sign; | |
154 hashitem_T *hi; | |
155 int found = FALSE; | |
156 | |
157 if (groupname != NULL) | |
158 { | |
159 hi = hash_find(&sg_table, groupname); | |
160 if (HASHITEM_EMPTY(hi)) | |
161 return id; | |
162 group = HI2SG(hi); | |
163 } | |
164 | |
165 // Search for the next usuable sign identifier | |
166 while (!found) | |
167 { | |
168 if (group == NULL) | |
169 id = next_sign_id++; // global group | |
170 else | |
171 id = group->next_sign_id++; | |
172 | |
173 // Check whether this sign is already placed in the buffer | |
174 found = TRUE; | |
175 FOR_ALL_SIGNS_IN_BUF(buf, sign) | |
176 { | |
177 if (id == sign->id && sign_in_group(sign, groupname)) | |
178 { | |
179 found = FALSE; // sign identifier is in use | |
180 break; | |
181 } | |
182 } | |
183 } | |
184 | |
185 return id; | |
186 } | |
187 | |
188 /* | |
189 * Insert a new sign into the signlist for buffer 'buf' between the 'prev' and | |
190 * 'next' signs. | |
191 */ | |
192 static void | |
193 insert_sign( | |
194 buf_T *buf, // buffer to store sign in | |
195 signlist_T *prev, // previous sign entry | |
196 signlist_T *next, // next sign entry | |
197 int id, // sign ID | |
198 char_u *group, // sign group; NULL for global group | |
199 int prio, // sign priority | |
200 linenr_T lnum, // line number which gets the mark | |
201 int typenr) // typenr of sign we are adding | |
202 { | |
203 signlist_T *newsign; | |
204 | |
205 newsign = (signlist_T *)lalloc_id((long_u)sizeof(signlist_T), FALSE, | |
206 aid_insert_sign); | |
207 if (newsign != NULL) | |
208 { | |
209 newsign->id = id; | |
210 newsign->lnum = lnum; | |
211 newsign->typenr = typenr; | |
212 if (group != NULL) | |
213 { | |
214 newsign->group = sign_group_ref(group); | |
215 if (newsign->group == NULL) | |
216 { | |
217 vim_free(newsign); | |
218 return; | |
219 } | |
220 } | |
221 else | |
222 newsign->group = NULL; | |
223 newsign->priority = prio; | |
224 newsign->next = next; | |
225 newsign->prev = prev; | |
226 if (next != NULL) | |
227 next->prev = newsign; | |
228 | |
229 if (prev == NULL) | |
230 { | |
231 // When adding first sign need to redraw the windows to create the | |
232 // column for signs. | |
233 if (buf->b_signlist == NULL) | |
234 { | |
235 redraw_buf_later(buf, NOT_VALID); | |
236 changed_cline_bef_curs(); | |
237 } | |
238 | |
239 // first sign in signlist | |
240 buf->b_signlist = newsign; | |
241 #ifdef FEAT_NETBEANS_INTG | |
242 if (netbeans_active()) | |
243 buf->b_has_sign_column = TRUE; | |
244 #endif | |
245 } | |
246 else | |
247 prev->next = newsign; | |
248 } | |
249 } | |
250 | |
251 /* | |
252 * Insert a new sign sorted by line number and sign priority. | |
253 */ | |
254 static void | |
255 insert_sign_by_lnum_prio( | |
256 buf_T *buf, // buffer to store sign in | |
257 signlist_T *prev, // previous sign entry | |
258 int id, // sign ID | |
259 char_u *group, // sign group; NULL for global group | |
260 int prio, // sign priority | |
261 linenr_T lnum, // line number which gets the mark | |
262 int typenr) // typenr of sign we are adding | |
263 { | |
264 signlist_T *sign; | |
265 | |
266 // keep signs sorted by lnum and by priority: insert new sign at | |
267 // the proper position in the list for this lnum. | |
268 while (prev != NULL && prev->lnum == lnum && prev->priority <= prio) | |
269 prev = prev->prev; | |
270 if (prev == NULL) | |
271 sign = buf->b_signlist; | |
272 else | |
273 sign = prev->next; | |
274 | |
275 insert_sign(buf, prev, sign, id, group, prio, lnum, typenr); | |
276 } | |
277 | |
278 /* | |
279 * Get the name of a sign by its typenr. | |
280 */ | |
281 static char_u * | |
282 sign_typenr2name(int typenr) | |
283 { | |
284 sign_T *sp; | |
285 | |
286 for (sp = first_sign; sp != NULL; sp = sp->sn_next) | |
287 if (sp->sn_typenr == typenr) | |
288 return sp->sn_name; | |
289 return (char_u *)_("[Deleted]"); | |
290 } | |
291 | |
292 /* | |
293 * Return information about a sign in a Dict | |
294 */ | |
295 static dict_T * | |
296 sign_get_info(signlist_T *sign) | |
297 { | |
298 dict_T *d; | |
299 | |
300 if ((d = dict_alloc_id(aid_sign_getinfo)) == NULL) | |
301 return NULL; | |
302 dict_add_number(d, "id", sign->id); | |
303 dict_add_string(d, "group", (sign->group == NULL) ? | |
304 (char_u *)"" : sign->group->sg_name); | |
305 dict_add_number(d, "lnum", sign->lnum); | |
306 dict_add_string(d, "name", sign_typenr2name(sign->typenr)); | |
307 dict_add_number(d, "priority", sign->priority); | |
308 | |
309 return d; | |
310 } | |
311 | |
312 /* | |
313 * Add the sign into the signlist. Find the right spot to do it though. | |
314 */ | |
315 static void | |
316 buf_addsign( | |
317 buf_T *buf, // buffer to store sign in | |
318 int id, // sign ID | |
319 char_u *groupname, // sign group | |
320 int prio, // sign priority | |
321 linenr_T lnum, // line number which gets the mark | |
322 int typenr) // typenr of sign we are adding | |
323 { | |
324 signlist_T *sign; // a sign in the signlist | |
325 signlist_T *prev; // the previous sign | |
326 | |
327 prev = NULL; | |
328 FOR_ALL_SIGNS_IN_BUF(buf, sign) | |
329 { | |
330 if (lnum == sign->lnum && id == sign->id && | |
331 sign_in_group(sign, groupname)) | |
332 { | |
333 // Update an existing sign | |
334 sign->typenr = typenr; | |
335 return; | |
336 } | |
337 else if (lnum < sign->lnum) | |
338 { | |
339 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, | |
340 lnum, typenr); | |
341 return; | |
342 } | |
343 prev = sign; | |
344 } | |
345 | |
346 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr); | |
347 return; | |
348 } | |
349 | |
350 /* | |
351 * For an existing, placed sign "markId" change the type to "typenr". | |
352 * Returns the line number of the sign, or zero if the sign is not found. | |
353 */ | |
354 static linenr_T | |
355 buf_change_sign_type( | |
356 buf_T *buf, // buffer to store sign in | |
357 int markId, // sign ID | |
358 char_u *group, // sign group | |
359 int typenr) // typenr of sign we are adding | |
360 { | |
361 signlist_T *sign; // a sign in the signlist | |
362 | |
363 FOR_ALL_SIGNS_IN_BUF(buf, sign) | |
364 { | |
365 if (sign->id == markId && sign_in_group(sign, group)) | |
366 { | |
367 sign->typenr = typenr; | |
368 return sign->lnum; | |
369 } | |
370 } | |
371 | |
372 return (linenr_T)0; | |
373 } | |
374 | |
375 /* | |
376 * Return the type number of the sign at line number 'lnum' in buffer 'buf' | |
377 * which has the attribute specifed by 'type'. Returns 0 if a sign is not found | |
378 * at the line number or it doesn't have the specified attribute. | |
379 */ | |
380 int | |
381 buf_getsigntype( | |
382 buf_T *buf, | |
383 linenr_T lnum, | |
384 int type) /* SIGN_ICON, SIGN_TEXT, SIGN_ANY, SIGN_LINEHL */ | |
385 { | |
386 signlist_T *sign; /* a sign in a b_signlist */ | |
387 | |
388 FOR_ALL_SIGNS_IN_BUF(buf, sign) | |
389 if (sign->lnum == lnum | |
390 && (type == SIGN_ANY | |
391 # ifdef FEAT_SIGN_ICONS | |
392 || (type == SIGN_ICON | |
393 && sign_get_image(sign->typenr) != NULL) | |
394 # endif | |
395 || (type == SIGN_TEXT | |
396 && sign_get_text(sign->typenr) != NULL) | |
397 || (type == SIGN_LINEHL | |
398 && sign_get_attr(sign->typenr, TRUE) != 0))) | |
399 return sign->typenr; | |
400 return 0; | |
401 } | |
402 | |
403 /* | |
404 * Delete sign 'id' in group 'group' from buffer 'buf'. | |
405 * If 'id' is zero, then delete all the signs in group 'group'. Otherwise | |
406 * delete only the specified sign. | |
407 * If 'group' is '*', then delete the sign in all the groups. If 'group' is | |
408 * NULL, then delete the sign in the global group. Otherwise delete the sign in | |
409 * the specified group. | |
410 * Returns the line number of the deleted sign. If multiple signs are deleted, | |
411 * then returns the line number of the last sign deleted. | |
412 */ | |
413 linenr_T | |
414 buf_delsign( | |
415 buf_T *buf, // buffer sign is stored in | |
416 linenr_T atlnum, // sign at this line, 0 - at any line | |
417 int id, // sign id | |
418 char_u *group) // sign group | |
419 { | |
420 signlist_T **lastp; // pointer to pointer to current sign | |
421 signlist_T *sign; // a sign in a b_signlist | |
422 signlist_T *next; // the next sign in a b_signlist | |
423 linenr_T lnum; // line number whose sign was deleted | |
424 | |
425 lastp = &buf->b_signlist; | |
426 lnum = 0; | |
427 for (sign = buf->b_signlist; sign != NULL; sign = next) | |
428 { | |
429 next = sign->next; | |
430 if ((id == 0 || sign->id == id) && | |
431 (atlnum == 0 || sign->lnum == atlnum) && | |
432 sign_in_group(sign, group)) | |
433 | |
434 { | |
435 *lastp = next; | |
436 if (next != NULL) | |
437 next->prev = sign->prev; | |
438 lnum = sign->lnum; | |
439 if (sign->group != NULL) | |
440 sign_group_unref(sign->group->sg_name); | |
441 vim_free(sign); | |
442 update_debug_sign(buf, lnum); | |
443 // Check whether only one sign needs to be deleted | |
444 // If deleting a sign with a specific identifer in a particular | |
445 // group or deleting any sign at a particular line number, delete | |
446 // only one sign. | |
447 if (group == NULL | |
448 || (*group != '*' && id != 0) | |
449 || (*group == '*' && atlnum != 0)) | |
450 break; | |
451 } | |
452 else | |
453 lastp = &sign->next; | |
454 } | |
455 | |
456 // When deleted the last sign need to redraw the windows to remove the | |
457 // sign column. | |
458 if (buf->b_signlist == NULL) | |
459 { | |
460 redraw_buf_later(buf, NOT_VALID); | |
461 changed_cline_bef_curs(); | |
462 } | |
463 | |
464 return lnum; | |
465 } | |
466 | |
467 | |
468 /* | |
469 * Find the line number of the sign with the requested id in group 'group'. If | |
470 * the sign does not exist, return 0 as the line number. This will still let | |
471 * the correct file get loaded. | |
472 */ | |
473 int | |
474 buf_findsign( | |
475 buf_T *buf, // buffer to store sign in | |
476 int id, // sign ID | |
477 char_u *group) // sign group | |
478 { | |
479 signlist_T *sign; // a sign in the signlist | |
480 | |
481 FOR_ALL_SIGNS_IN_BUF(buf, sign) | |
482 if (sign->id == id && sign_in_group(sign, group)) | |
483 return sign->lnum; | |
484 | |
485 return 0; | |
486 } | |
487 | |
488 /* | |
489 * Return the sign at line 'lnum' in buffer 'buf'. Returns NULL if a sign is | |
490 * not found at the line. If 'groupname' is NULL, searches in the global group. | |
491 */ | |
492 static signlist_T * | |
493 buf_getsign_at_line( | |
494 buf_T *buf, // buffer whose sign we are searching for | |
495 linenr_T lnum, // line number of sign | |
496 char_u *groupname) // sign group name | |
497 { | |
498 signlist_T *sign; // a sign in the signlist | |
499 | |
500 FOR_ALL_SIGNS_IN_BUF(buf, sign) | |
501 if (sign->lnum == lnum && sign_in_group(sign, groupname)) | |
502 return sign; | |
503 | |
504 return NULL; | |
505 } | |
506 | |
507 /* | |
508 * Return the identifier of the sign at line number 'lnum' in buffer 'buf'. | |
509 */ | |
510 int | |
511 buf_findsign_id( | |
512 buf_T *buf, // buffer whose sign we are searching for | |
513 linenr_T lnum, // line number of sign | |
514 char_u *groupname) // sign group name | |
515 { | |
516 signlist_T *sign; // a sign in the signlist | |
517 | |
518 sign = buf_getsign_at_line(buf, lnum, groupname); | |
519 if (sign != NULL) | |
520 return sign->id; | |
521 | |
522 return 0; | |
523 } | |
524 | |
525 # if defined(FEAT_NETBEANS_INTG) || defined(PROTO) | |
526 /* | |
527 * See if a given type of sign exists on a specific line. | |
528 */ | |
529 int | |
530 buf_findsigntype_id( | |
531 buf_T *buf, /* buffer whose sign we are searching for */ | |
532 linenr_T lnum, /* line number of sign */ | |
533 int typenr) /* sign type number */ | |
534 { | |
535 signlist_T *sign; /* a sign in the signlist */ | |
536 | |
537 FOR_ALL_SIGNS_IN_BUF(buf, sign) | |
538 if (sign->lnum == lnum && sign->typenr == typenr) | |
539 return sign->id; | |
540 | |
541 return 0; | |
542 } | |
543 | |
544 | |
545 # if defined(FEAT_SIGN_ICONS) || defined(PROTO) | |
546 /* | |
547 * Return the number of icons on the given line. | |
548 */ | |
549 int | |
550 buf_signcount(buf_T *buf, linenr_T lnum) | |
551 { | |
552 signlist_T *sign; // a sign in the signlist | |
553 int count = 0; | |
554 | |
555 FOR_ALL_SIGNS_IN_BUF(buf, sign) | |
556 if (sign->lnum == lnum) | |
557 if (sign_get_image(sign->typenr) != NULL) | |
558 count++; | |
559 | |
560 return count; | |
561 } | |
562 # endif /* FEAT_SIGN_ICONS */ | |
563 # endif /* FEAT_NETBEANS_INTG */ | |
564 | |
565 /* | |
566 * Delete signs in group 'group' in buffer "buf". If 'group' is '*', then | |
567 * delete all the signs. | |
568 */ | |
569 void | |
570 buf_delete_signs(buf_T *buf, char_u *group) | |
571 { | |
572 signlist_T *sign; | |
573 signlist_T **lastp; // pointer to pointer to current sign | |
574 signlist_T *next; | |
575 | |
576 // When deleting the last sign need to redraw the windows to remove the | |
577 // sign column. Not when curwin is NULL (this means we're exiting). | |
578 if (buf->b_signlist != NULL && curwin != NULL) | |
579 { | |
580 redraw_buf_later(buf, NOT_VALID); | |
581 changed_cline_bef_curs(); | |
582 } | |
583 | |
584 lastp = &buf->b_signlist; | |
585 for (sign = buf->b_signlist; sign != NULL; sign = next) | |
586 { | |
587 next = sign->next; | |
588 if (sign_in_group(sign, group)) | |
589 { | |
590 *lastp = next; | |
591 if (next != NULL) | |
592 next->prev = sign->prev; | |
593 if (sign->group != NULL) | |
594 sign_group_unref(sign->group->sg_name); | |
595 vim_free(sign); | |
596 } | |
597 else | |
598 lastp = &sign->next; | |
599 } | |
600 } | |
601 | |
602 /* | |
603 * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers. | |
604 */ | |
605 static void | |
606 sign_list_placed(buf_T *rbuf, char_u *sign_group) | |
607 { | |
608 buf_T *buf; | |
609 signlist_T *sign; | |
610 char lbuf[BUFSIZ]; | |
611 char group[BUFSIZ]; | |
612 | |
613 MSG_PUTS_TITLE(_("\n--- Signs ---")); | |
614 msg_putchar('\n'); | |
615 if (rbuf == NULL) | |
616 buf = firstbuf; | |
617 else | |
618 buf = rbuf; | |
619 while (buf != NULL && !got_int) | |
620 { | |
621 if (buf->b_signlist != NULL) | |
622 { | |
623 vim_snprintf(lbuf, BUFSIZ, _("Signs for %s:"), buf->b_fname); | |
624 MSG_PUTS_ATTR(lbuf, HL_ATTR(HLF_D)); | |
625 msg_putchar('\n'); | |
626 } | |
627 FOR_ALL_SIGNS_IN_BUF(buf, sign) | |
628 { | |
629 if (got_int) | |
630 break; | |
631 if (!sign_in_group(sign, sign_group)) | |
632 continue; | |
633 if (sign->group != NULL) | |
634 vim_snprintf(group, BUFSIZ, " group=%s", | |
635 sign->group->sg_name); | |
636 else | |
637 group[0] = '\0'; | |
638 vim_snprintf(lbuf, BUFSIZ, _(" line=%ld id=%d%s name=%s " | |
639 "priority=%d"), | |
640 (long)sign->lnum, sign->id, group, | |
641 sign_typenr2name(sign->typenr), sign->priority); | |
642 MSG_PUTS(lbuf); | |
643 msg_putchar('\n'); | |
644 } | |
645 if (rbuf != NULL) | |
646 break; | |
647 buf = buf->b_next; | |
648 } | |
649 } | |
650 | |
651 /* | |
652 * Adjust a placed sign for inserted/deleted lines. | |
653 */ | |
654 void | |
655 sign_mark_adjust( | |
656 linenr_T line1, | |
657 linenr_T line2, | |
658 long amount, | |
659 long amount_after) | |
660 { | |
661 signlist_T *sign; /* a sign in a b_signlist */ | |
662 | |
663 FOR_ALL_SIGNS_IN_BUF(curbuf, sign) | |
664 { | |
665 if (sign->lnum >= line1 && sign->lnum <= line2) | |
666 { | |
667 if (amount == MAXLNUM) | |
668 sign->lnum = line1; | |
669 else | |
670 sign->lnum += amount; | |
671 } | |
672 else if (sign->lnum > line2) | |
673 sign->lnum += amount_after; | |
674 } | |
675 } | |
676 | |
677 /* | |
678 * Find index of a ":sign" subcmd from its name. | |
679 * "*end_cmd" must be writable. | |
680 */ | |
681 static int | |
682 sign_cmd_idx( | |
683 char_u *begin_cmd, /* begin of sign subcmd */ | |
684 char_u *end_cmd) /* just after sign subcmd */ | |
685 { | |
686 int idx; | |
687 char save = *end_cmd; | |
688 | |
689 *end_cmd = NUL; | |
690 for (idx = 0; ; ++idx) | |
691 if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0) | |
692 break; | |
693 *end_cmd = save; | |
694 return idx; | |
695 } | |
696 | |
697 /* | |
698 * Find a sign by name. Also returns pointer to the previous sign. | |
699 */ | |
700 static sign_T * | |
701 sign_find(char_u *name, sign_T **sp_prev) | |
702 { | |
703 sign_T *sp; | |
704 | |
705 if (sp_prev != NULL) | |
706 *sp_prev = NULL; | |
707 for (sp = first_sign; sp != NULL; sp = sp->sn_next) | |
708 { | |
709 if (STRCMP(sp->sn_name, name) == 0) | |
710 break; | |
711 if (sp_prev != NULL) | |
712 *sp_prev = sp; | |
713 } | |
714 | |
715 return sp; | |
716 } | |
717 | |
718 /* | |
719 * Define a new sign or update an existing sign | |
720 */ | |
721 int | |
722 sign_define_by_name( | |
723 char_u *name, | |
724 char_u *icon, | |
725 char_u *linehl, | |
726 char_u *text, | |
727 char_u *texthl) | |
728 { | |
729 sign_T *sp_prev; | |
730 sign_T *sp; | |
731 | |
732 sp = sign_find(name, &sp_prev); | |
733 if (sp == NULL) | |
734 { | |
735 sign_T *lp; | |
736 int start = next_sign_typenr; | |
737 | |
738 // Allocate a new sign. | |
739 sp = (sign_T *)alloc_clear_id((unsigned)sizeof(sign_T), | |
740 aid_sign_define_by_name); | |
741 if (sp == NULL) | |
742 return FAIL; | |
743 | |
744 // Check that next_sign_typenr is not already being used. | |
745 // This only happens after wrapping around. Hopefully | |
746 // another one got deleted and we can use its number. | |
747 for (lp = first_sign; lp != NULL; ) | |
748 { | |
749 if (lp->sn_typenr == next_sign_typenr) | |
750 { | |
751 ++next_sign_typenr; | |
752 if (next_sign_typenr == MAX_TYPENR) | |
753 next_sign_typenr = 1; | |
754 if (next_sign_typenr == start) | |
755 { | |
756 vim_free(sp); | |
757 EMSG(_("E612: Too many signs defined")); | |
758 return FAIL; | |
759 } | |
760 lp = first_sign; // start all over | |
761 continue; | |
762 } | |
763 lp = lp->sn_next; | |
764 } | |
765 | |
766 sp->sn_typenr = next_sign_typenr; | |
767 if (++next_sign_typenr == MAX_TYPENR) | |
768 next_sign_typenr = 1; // wrap around | |
769 | |
770 sp->sn_name = vim_strsave(name); | |
771 if (sp->sn_name == NULL) // out of memory | |
772 { | |
773 vim_free(sp); | |
774 return FAIL; | |
775 } | |
776 | |
777 // add the new sign to the list of signs | |
778 if (sp_prev == NULL) | |
779 first_sign = sp; | |
780 else | |
781 sp_prev->sn_next = sp; | |
782 } | |
783 | |
784 // set values for a defined sign. | |
785 if (icon != NULL) | |
786 { | |
787 vim_free(sp->sn_icon); | |
788 sp->sn_icon = vim_strsave(icon); | |
789 backslash_halve(sp->sn_icon); | |
790 # ifdef FEAT_SIGN_ICONS | |
791 if (gui.in_use) | |
792 { | |
793 out_flush(); | |
794 if (sp->sn_image != NULL) | |
795 gui_mch_destroy_sign(sp->sn_image); | |
796 sp->sn_image = gui_mch_register_sign(sp->sn_icon); | |
797 } | |
798 # endif | |
799 } | |
800 | |
801 if (text != NULL) | |
802 { | |
803 char_u *s; | |
804 char_u *endp; | |
805 int cells; | |
806 int len; | |
807 | |
808 endp = text + (int)STRLEN(text); | |
809 for (s = text; s + 1 < endp; ++s) | |
810 if (*s == '\\') | |
811 { | |
812 // Remove a backslash, so that it is possible | |
813 // to use a space. | |
814 STRMOVE(s, s + 1); | |
815 --endp; | |
816 } | |
817 # ifdef FEAT_MBYTE | |
818 // Count cells and check for non-printable chars | |
819 if (has_mbyte) | |
820 { | |
821 cells = 0; | |
822 for (s = text; s < endp; s += (*mb_ptr2len)(s)) | |
823 { | |
824 if (!vim_isprintc((*mb_ptr2char)(s))) | |
825 break; | |
826 cells += (*mb_ptr2cells)(s); | |
827 } | |
828 } | |
829 else | |
830 # endif | |
831 { | |
832 for (s = text; s < endp; ++s) | |
833 if (!vim_isprintc(*s)) | |
834 break; | |
835 cells = (int)(s - text); | |
836 } | |
837 // Currently must be one or two display cells | |
838 if (s != endp || cells < 1 || cells > 2) | |
839 { | |
840 EMSG2(_("E239: Invalid sign text: %s"), text); | |
841 return FAIL; | |
842 } | |
843 | |
844 vim_free(sp->sn_text); | |
845 // Allocate one byte more if we need to pad up | |
846 // with a space. | |
847 len = (int)(endp - text + ((cells == 1) ? 1 : 0)); | |
848 sp->sn_text = vim_strnsave(text, len); | |
849 | |
850 if (sp->sn_text != NULL && cells == 1) | |
851 STRCPY(sp->sn_text + len - 1, " "); | |
852 } | |
853 | |
854 if (linehl != NULL) | |
855 sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl)); | |
856 | |
857 if (texthl != NULL) | |
858 sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl)); | |
859 | |
860 return OK; | |
861 } | |
862 | |
863 /* | |
864 * Free the sign specified by 'name'. | |
865 */ | |
866 int | |
867 sign_undefine_by_name(char_u *name) | |
868 { | |
869 sign_T *sp_prev; | |
870 sign_T *sp; | |
871 | |
872 sp = sign_find(name, &sp_prev); | |
873 if (sp == NULL) | |
874 { | |
875 EMSG2(_("E155: Unknown sign: %s"), name); | |
876 return FAIL; | |
877 } | |
878 sign_undefine(sp, sp_prev); | |
879 | |
880 return OK; | |
881 } | |
882 | |
883 /* | |
884 * List the signs matching 'name' | |
885 */ | |
886 static void | |
887 sign_list_by_name(char_u *name) | |
888 { | |
889 sign_T *sp; | |
890 | |
891 sp = sign_find(name, NULL); | |
892 if (sp != NULL) | |
893 sign_list_defined(sp); | |
894 else | |
895 EMSG2(_("E155: Unknown sign: %s"), name); | |
896 } | |
897 | |
898 /* | |
899 * Place a sign at the specifed file location or update a sign. | |
900 */ | |
901 int | |
902 sign_place( | |
903 int *sign_id, | |
904 char_u *sign_group, | |
905 char_u *sign_name, | |
906 buf_T *buf, | |
907 linenr_T lnum, | |
908 int prio) | |
909 { | |
910 sign_T *sp; | |
911 | |
912 // Check for reserved character '*' in group name | |
913 if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0')) | |
914 return FAIL; | |
915 | |
916 for (sp = first_sign; sp != NULL; sp = sp->sn_next) | |
917 if (STRCMP(sp->sn_name, sign_name) == 0) | |
918 break; | |
919 if (sp == NULL) | |
920 { | |
921 EMSG2(_("E155: Unknown sign: %s"), sign_name); | |
922 return FAIL; | |
923 } | |
924 if (*sign_id == 0) | |
925 *sign_id = sign_group_get_next_signid(buf, sign_group); | |
926 | |
927 if (lnum > 0) | |
928 // ":sign place {id} line={lnum} name={name} file={fname}": | |
929 // place a sign | |
930 buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr); | |
931 else | |
932 // ":sign place {id} file={fname}": change sign type | |
933 lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr); | |
934 if (lnum > 0) | |
935 update_debug_sign(buf, lnum); | |
936 else | |
937 { | |
938 EMSG2(_("E885: Not possible to change sign %s"), sign_name); | |
939 return FAIL; | |
940 } | |
941 | |
942 return OK; | |
943 } | |
944 | |
945 /* | |
946 * Unplace the specified sign | |
947 */ | |
948 int | |
949 sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum) | |
950 { | |
951 if (buf->b_signlist == NULL) // No signs in the buffer | |
952 return OK; | |
953 | |
954 if (sign_id == 0) | |
955 { | |
956 // Delete all the signs in the specified buffer | |
957 redraw_buf_later(buf, NOT_VALID); | |
958 buf_delete_signs(buf, sign_group); | |
959 } | |
960 else | |
961 { | |
962 linenr_T lnum; | |
963 | |
964 // Delete only the specified signs | |
965 lnum = buf_delsign(buf, atlnum, sign_id, sign_group); | |
966 if (lnum == 0) | |
967 return FAIL; | |
968 } | |
969 | |
970 return OK; | |
971 } | |
972 | |
973 /* | |
974 * Unplace the sign at the current cursor line. | |
975 */ | |
976 static void | |
977 sign_unplace_at_cursor(char_u *groupname) | |
978 { | |
979 int id = -1; | |
980 | |
981 id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname); | |
982 if (id > 0) | |
983 sign_unplace(id, groupname, curwin->w_buffer, curwin->w_cursor.lnum); | |
984 else | |
985 EMSG(_("E159: Missing sign number")); | |
986 } | |
987 | |
988 /* | |
989 * sign define command | |
990 * ":sign define {name} ..." | |
991 */ | |
992 static void | |
993 sign_define_cmd(char_u *sign_name, char_u *cmdline) | |
994 { | |
995 char_u *arg; | |
996 char_u *p = cmdline; | |
997 char_u *icon = NULL; | |
998 char_u *text = NULL; | |
999 char_u *linehl = NULL; | |
1000 char_u *texthl = NULL; | |
1001 int failed = FALSE; | |
1002 | |
1003 // set values for a defined sign. | |
1004 for (;;) | |
1005 { | |
1006 arg = skipwhite(p); | |
1007 if (*arg == NUL) | |
1008 break; | |
1009 p = skiptowhite_esc(arg); | |
1010 if (STRNCMP(arg, "icon=", 5) == 0) | |
1011 { | |
1012 arg += 5; | |
1013 icon = vim_strnsave(arg, (int)(p - arg)); | |
1014 } | |
1015 else if (STRNCMP(arg, "text=", 5) == 0) | |
1016 { | |
1017 arg += 5; | |
1018 text = vim_strnsave(arg, (int)(p - arg)); | |
1019 } | |
1020 else if (STRNCMP(arg, "linehl=", 7) == 0) | |
1021 { | |
1022 arg += 7; | |
1023 linehl = vim_strnsave(arg, (int)(p - arg)); | |
1024 } | |
1025 else if (STRNCMP(arg, "texthl=", 7) == 0) | |
1026 { | |
1027 arg += 7; | |
1028 texthl = vim_strnsave(arg, (int)(p - arg)); | |
1029 } | |
1030 else | |
1031 { | |
1032 EMSG2(_(e_invarg2), arg); | |
1033 failed = TRUE; | |
1034 break; | |
1035 } | |
1036 } | |
1037 | |
1038 if (!failed) | |
1039 sign_define_by_name(sign_name, icon, linehl, text, texthl); | |
1040 | |
1041 vim_free(icon); | |
1042 vim_free(text); | |
1043 vim_free(linehl); | |
1044 vim_free(texthl); | |
1045 } | |
1046 | |
1047 /* | |
1048 * :sign place command | |
1049 */ | |
1050 static void | |
1051 sign_place_cmd( | |
1052 buf_T *buf, | |
1053 linenr_T lnum, | |
1054 char_u *sign_name, | |
1055 int id, | |
1056 char_u *group, | |
1057 int prio) | |
1058 { | |
1059 if (id <= 0) | |
1060 { | |
1061 // List signs placed in a file/buffer | |
1062 // :sign place file={fname} | |
1063 // :sign place group={group} file={fname} | |
1064 // :sign place group=* file={fname} | |
1065 // :sign place buffer={nr} | |
1066 // :sign place group={group} buffer={nr} | |
1067 // :sign place group=* buffer={nr} | |
1068 // :sign place | |
1069 // :sign place group={group} | |
1070 // :sign place group=* | |
1071 if (lnum >= 0 || sign_name != NULL || | |
1072 (group != NULL && *group == '\0')) | |
1073 EMSG(_(e_invarg)); | |
1074 else | |
1075 sign_list_placed(buf, group); | |
1076 } | |
1077 else | |
1078 { | |
1079 // Place a new sign | |
1080 if (sign_name == NULL || buf == NULL || | |
1081 (group != NULL && *group == '\0')) | |
1082 { | |
1083 EMSG(_(e_invarg)); | |
1084 return; | |
1085 } | |
1086 | |
1087 sign_place(&id, group, sign_name, buf, lnum, prio); | |
1088 } | |
1089 } | |
1090 | |
1091 /* | |
1092 * :sign unplace command | |
1093 */ | |
1094 static void | |
1095 sign_unplace_cmd( | |
1096 buf_T *buf, | |
1097 linenr_T lnum, | |
1098 char_u *sign_name, | |
1099 int id, | |
1100 char_u *group) | |
1101 { | |
1102 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0')) | |
1103 { | |
1104 EMSG(_(e_invarg)); | |
1105 return; | |
1106 } | |
1107 | |
1108 if (id == -2) | |
1109 { | |
1110 if (buf != NULL) | |
1111 // :sign unplace * file={fname} | |
1112 // :sign unplace * group={group} file={fname} | |
1113 // :sign unplace * group=* file={fname} | |
1114 // :sign unplace * buffer={nr} | |
1115 // :sign unplace * group={group} buffer={nr} | |
1116 // :sign unplace * group=* buffer={nr} | |
1117 sign_unplace(0, group, buf, 0); | |
1118 else | |
1119 // :sign unplace * | |
1120 // :sign unplace * group={group} | |
1121 // :sign unplace * group=* | |
1122 FOR_ALL_BUFFERS(buf) | |
1123 if (buf->b_signlist != NULL) | |
1124 buf_delete_signs(buf, group); | |
1125 } | |
1126 else | |
1127 { | |
1128 if (buf != NULL) | |
1129 // :sign unplace {id} file={fname} | |
1130 // :sign unplace {id} group={group} file={fname} | |
1131 // :sign unplace {id} group=* file={fname} | |
1132 // :sign unplace {id} buffer={nr} | |
1133 // :sign unplace {id} group={group} buffer={nr} | |
1134 // :sign unplace {id} group=* buffer={nr} | |
1135 sign_unplace(id, group, buf, 0); | |
1136 else | |
1137 { | |
1138 if (id == -1) | |
1139 { | |
1140 // :sign unplace group={group} | |
1141 // :sign unplace group=* | |
1142 sign_unplace_at_cursor(group); | |
1143 } | |
1144 else | |
1145 { | |
1146 // :sign unplace {id} | |
1147 // :sign unplace {id} group={group} | |
1148 // :sign unplace {id} group=* | |
1149 FOR_ALL_BUFFERS(buf) | |
1150 sign_unplace(id, group, buf, 0); | |
1151 } | |
1152 } | |
1153 } | |
1154 } | |
1155 | |
1156 /* | |
1157 * Jump to a placed sign | |
1158 * :sign jump {id} file={fname} | |
1159 * :sign jump {id} buffer={nr} | |
1160 * :sign jump {id} group={group} file={fname} | |
1161 * :sign jump {id} group={group} buffer={nr} | |
1162 */ | |
1163 static void | |
1164 sign_jump_cmd( | |
1165 buf_T *buf, | |
1166 linenr_T lnum, | |
1167 char_u *sign_name, | |
1168 int id, | |
1169 char_u *group) | |
1170 { | |
1171 if (buf == NULL && sign_name == NULL && group == NULL && id == -1) | |
1172 { | |
1173 EMSG(_(e_argreq)); | |
1174 return; | |
1175 } | |
1176 | |
1177 if (buf == NULL || (group != NULL && *group == '\0') || | |
1178 lnum >= 0 || sign_name != NULL) | |
1179 { | |
1180 // File or buffer is not specified or an empty group is used | |
1181 // or a line number or a sign name is specified. | |
1182 EMSG(_(e_invarg)); | |
1183 return; | |
1184 } | |
1185 | |
1186 if ((lnum = buf_findsign(buf, id, group)) <= 0) | |
1187 { | |
1188 EMSGN(_("E157: Invalid sign ID: %ld"), id); | |
1189 return; | |
1190 } | |
1191 | |
1192 // goto a sign ... | |
1193 if (buf_jump_open_win(buf) != NULL) | |
1194 { // ... in a current window | |
1195 curwin->w_cursor.lnum = lnum; | |
1196 check_cursor_lnum(); | |
1197 beginline(BL_WHITE); | |
1198 } | |
1199 else | |
1200 { // ... not currently in a window | |
1201 char_u *cmd; | |
1202 | |
1203 if (buf->b_fname == NULL) | |
1204 { | |
1205 EMSG(_("E934: Cannot jump to a buffer that does not have a name")); | |
1206 return; | |
1207 } | |
1208 cmd = alloc((unsigned)STRLEN(buf->b_fname) + 25); | |
1209 if (cmd == NULL) | |
1210 return; | |
1211 sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname); | |
1212 do_cmdline_cmd(cmd); | |
1213 vim_free(cmd); | |
1214 } | |
1215 # ifdef FEAT_FOLDING | |
1216 foldOpenCursor(); | |
1217 # endif | |
1218 } | |
1219 | |
1220 /* | |
1221 * Parse the command line arguments for the ":sign place", ":sign unplace" and | |
1222 * ":sign jump" commands. | |
1223 * The supported arguments are: line={lnum} name={name} group={group} | |
1224 * priority={prio} and file={fname} or buffer={nr}. | |
1225 */ | |
1226 static int | |
1227 parse_sign_cmd_args( | |
1228 int cmd, | |
1229 char_u *arg, | |
1230 char_u **sign_name, | |
1231 int *signid, | |
1232 char_u **group, | |
1233 int *prio, | |
1234 buf_T **buf, | |
1235 linenr_T *lnum) | |
1236 { | |
1237 char_u *arg1; | |
1238 char_u *name; | |
1239 char_u *filename = NULL; | |
1240 | |
1241 // first arg could be placed sign id | |
1242 arg1 = arg; | |
1243 if (VIM_ISDIGIT(*arg)) | |
1244 { | |
1245 *signid = getdigits(&arg); | |
1246 if (!VIM_ISWHITE(*arg) && *arg != NUL) | |
1247 { | |
1248 *signid = -1; | |
1249 arg = arg1; | |
1250 } | |
1251 else | |
1252 arg = skipwhite(arg); | |
1253 } | |
1254 | |
1255 while (*arg != NUL) | |
1256 { | |
1257 if (STRNCMP(arg, "line=", 5) == 0) | |
1258 { | |
1259 arg += 5; | |
1260 *lnum = atoi((char *)arg); | |
1261 arg = skiptowhite(arg); | |
1262 } | |
1263 else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE) | |
1264 { | |
1265 if (*signid != -1) | |
1266 { | |
1267 EMSG(_(e_invarg)); | |
1268 return FAIL; | |
1269 } | |
1270 *signid = -2; | |
1271 arg = skiptowhite(arg + 1); | |
1272 } | |
1273 else if (STRNCMP(arg, "name=", 5) == 0) | |
1274 { | |
1275 arg += 5; | |
1276 name = arg; | |
1277 arg = skiptowhite(arg); | |
1278 if (*arg != NUL) | |
1279 *arg++ = NUL; | |
1280 while (name[0] == '0' && name[1] != NUL) | |
1281 ++name; | |
1282 *sign_name = name; | |
1283 } | |
1284 else if (STRNCMP(arg, "group=", 6) == 0) | |
1285 { | |
1286 arg += 6; | |
1287 *group = arg; | |
1288 arg = skiptowhite(arg); | |
1289 if (*arg != NUL) | |
1290 *arg++ = NUL; | |
1291 } | |
1292 else if (STRNCMP(arg, "priority=", 9) == 0) | |
1293 { | |
1294 arg += 9; | |
1295 *prio = atoi((char *)arg); | |
1296 arg = skiptowhite(arg); | |
1297 } | |
1298 else if (STRNCMP(arg, "file=", 5) == 0) | |
1299 { | |
1300 arg += 5; | |
1301 filename = arg; | |
1302 *buf = buflist_findname_exp(arg); | |
1303 break; | |
1304 } | |
1305 else if (STRNCMP(arg, "buffer=", 7) == 0) | |
1306 { | |
1307 arg += 7; | |
1308 filename = arg; | |
1309 *buf = buflist_findnr((int)getdigits(&arg)); | |
1310 if (*skipwhite(arg) != NUL) | |
1311 EMSG(_(e_trailing)); | |
1312 break; | |
1313 } | |
1314 else | |
1315 { | |
1316 EMSG(_(e_invarg)); | |
1317 return FAIL; | |
1318 } | |
1319 arg = skipwhite(arg); | |
1320 } | |
1321 | |
1322 if (filename != NULL && *buf == NULL) | |
1323 { | |
1324 EMSG2(_("E158: Invalid buffer name: %s"), filename); | |
1325 return FAIL; | |
1326 } | |
1327 | |
1328 return OK; | |
1329 } | |
1330 | |
1331 /* | |
1332 * ":sign" command | |
1333 */ | |
1334 void | |
1335 ex_sign(exarg_T *eap) | |
1336 { | |
1337 char_u *arg = eap->arg; | |
1338 char_u *p; | |
1339 int idx; | |
1340 sign_T *sp; | |
1341 buf_T *buf = NULL; | |
1342 | |
1343 // Parse the subcommand. | |
1344 p = skiptowhite(arg); | |
1345 idx = sign_cmd_idx(arg, p); | |
1346 if (idx == SIGNCMD_LAST) | |
1347 { | |
1348 EMSG2(_("E160: Unknown sign command: %s"), arg); | |
1349 return; | |
1350 } | |
1351 arg = skipwhite(p); | |
1352 | |
1353 if (idx <= SIGNCMD_LIST) | |
1354 { | |
1355 // Define, undefine or list signs. | |
1356 if (idx == SIGNCMD_LIST && *arg == NUL) | |
1357 { | |
1358 // ":sign list": list all defined signs | |
1359 for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next) | |
1360 sign_list_defined(sp); | |
1361 } | |
1362 else if (*arg == NUL) | |
1363 EMSG(_("E156: Missing sign name")); | |
1364 else | |
1365 { | |
1366 char_u *name; | |
1367 | |
1368 // Isolate the sign name. If it's a number skip leading zeroes, | |
1369 // so that "099" and "99" are the same sign. But keep "0". | |
1370 p = skiptowhite(arg); | |
1371 if (*p != NUL) | |
1372 *p++ = NUL; | |
1373 while (arg[0] == '0' && arg[1] != NUL) | |
1374 ++arg; | |
1375 name = vim_strsave(arg); | |
1376 | |
1377 if (idx == SIGNCMD_DEFINE) | |
1378 sign_define_cmd(name, p); | |
1379 else if (idx == SIGNCMD_LIST) | |
1380 // ":sign list {name}" | |
1381 sign_list_by_name(name); | |
1382 else | |
1383 // ":sign undefine {name}" | |
1384 sign_undefine_by_name(name); | |
1385 | |
1386 vim_free(name); | |
1387 return; | |
1388 } | |
1389 } | |
1390 else | |
1391 { | |
1392 int id = -1; | |
1393 linenr_T lnum = -1; | |
1394 char_u *sign_name = NULL; | |
1395 char_u *group = NULL; | |
1396 int prio = SIGN_DEF_PRIO; | |
1397 | |
1398 // Parse command line arguments | |
1399 if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio, | |
1400 &buf, &lnum) == FAIL) | |
1401 return; | |
1402 | |
1403 if (idx == SIGNCMD_PLACE) | |
1404 sign_place_cmd(buf, lnum, sign_name, id, group, prio); | |
1405 else if (idx == SIGNCMD_UNPLACE) | |
1406 sign_unplace_cmd(buf, lnum, sign_name, id, group); | |
1407 else if (idx == SIGNCMD_JUMP) | |
1408 sign_jump_cmd(buf, lnum, sign_name, id, group); | |
1409 } | |
1410 } | |
1411 | |
1412 /* | |
1413 * Return information about a specified sign | |
1414 */ | |
1415 static void | |
1416 sign_getinfo(sign_T *sp, dict_T *retdict) | |
1417 { | |
1418 char_u *p; | |
1419 | |
1420 dict_add_string(retdict, "name", (char_u *)sp->sn_name); | |
1421 if (sp->sn_icon != NULL) | |
1422 dict_add_string(retdict, "icon", (char_u *)sp->sn_icon); | |
1423 if (sp->sn_text != NULL) | |
1424 dict_add_string(retdict, "text", (char_u *)sp->sn_text); | |
1425 if (sp->sn_line_hl > 0) | |
1426 { | |
1427 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE); | |
1428 if (p == NULL) | |
1429 p = (char_u *)"NONE"; | |
1430 dict_add_string(retdict, "linehl", (char_u *)p); | |
1431 } | |
1432 if (sp->sn_text_hl > 0) | |
1433 { | |
1434 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE); | |
1435 if (p == NULL) | |
1436 p = (char_u *)"NONE"; | |
1437 dict_add_string(retdict, "texthl", (char_u *)p); | |
1438 } | |
1439 } | |
1440 | |
1441 /* | |
1442 * If 'name' is NULL, return a list of all the defined signs. | |
1443 * Otherwise, return information about the specified sign. | |
1444 */ | |
1445 void | |
1446 sign_getlist(char_u *name, list_T *retlist) | |
1447 { | |
1448 sign_T *sp = first_sign; | |
1449 dict_T *dict; | |
1450 | |
1451 if (name != NULL) | |
1452 { | |
1453 sp = sign_find(name, NULL); | |
1454 if (sp == NULL) | |
1455 return; | |
1456 } | |
1457 | |
1458 for (; sp != NULL && !got_int; sp = sp->sn_next) | |
1459 { | |
1460 if ((dict = dict_alloc_id(aid_sign_getlist)) == NULL) | |
1461 return; | |
1462 if (list_append_dict(retlist, dict) == FAIL) | |
1463 return; | |
1464 sign_getinfo(sp, dict); | |
1465 | |
1466 if (name != NULL) // handle only the specified sign | |
1467 break; | |
1468 } | |
1469 } | |
1470 | |
1471 /* | |
1472 * Returns information about signs placed in a buffer as list of dicts. | |
1473 */ | |
1474 void | |
1475 get_buffer_signs(buf_T *buf, list_T *l) | |
1476 { | |
1477 signlist_T *sign; | |
1478 dict_T *d; | |
1479 | |
1480 FOR_ALL_SIGNS_IN_BUF(buf, sign) | |
1481 { | |
1482 if ((d = sign_get_info(sign)) != NULL) | |
1483 list_append_dict(l, d); | |
1484 } | |
1485 } | |
1486 | |
1487 /* | |
1488 * Return information about all the signs placed in a buffer | |
1489 */ | |
1490 static void | |
1491 sign_get_placed_in_buf( | |
1492 buf_T *buf, | |
1493 linenr_T lnum, | |
1494 int sign_id, | |
1495 char_u *sign_group, | |
1496 list_T *retlist) | |
1497 { | |
1498 dict_T *d; | |
1499 list_T *l; | |
1500 signlist_T *sign; | |
1501 dict_T *sdict; | |
1502 | |
1503 if ((d = dict_alloc_id(aid_sign_getplaced_dict)) == NULL) | |
1504 return; | |
1505 list_append_dict(retlist, d); | |
1506 | |
1507 dict_add_number(d, "bufnr", (long)buf->b_fnum); | |
1508 | |
1509 if ((l = list_alloc_id(aid_sign_getplaced_list)) == NULL) | |
1510 return; | |
1511 dict_add_list(d, "signs", l); | |
1512 | |
1513 FOR_ALL_SIGNS_IN_BUF(buf, sign) | |
1514 { | |
1515 if (!sign_in_group(sign, sign_group)) | |
1516 continue; | |
1517 if ((lnum == 0 && sign_id == 0) || | |
1518 (sign_id == 0 && lnum == sign->lnum) || | |
1519 (lnum == 0 && sign_id == sign->id) || | |
1520 (lnum == sign->lnum && sign_id == sign->id)) | |
1521 { | |
1522 if ((sdict = sign_get_info(sign)) != NULL) | |
1523 list_append_dict(l, sdict); | |
1524 } | |
1525 } | |
1526 } | |
1527 | |
1528 /* | |
1529 * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the | |
1530 * sign placed at the line number. If 'lnum' is zero, return all the signs | |
1531 * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers. | |
1532 */ | |
1533 void | |
1534 sign_get_placed( | |
1535 buf_T *buf, | |
1536 linenr_T lnum, | |
1537 int sign_id, | |
1538 char_u *sign_group, | |
1539 list_T *retlist) | |
1540 { | |
1541 if (buf != NULL) | |
1542 sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist); | |
1543 else | |
1544 { | |
1545 FOR_ALL_BUFFERS(buf) | |
1546 { | |
1547 if (buf->b_signlist != NULL) | |
1548 sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist); | |
1549 } | |
1550 } | |
1551 } | |
1552 | |
1553 # if defined(FEAT_SIGN_ICONS) || defined(PROTO) | |
1554 /* | |
1555 * Allocate the icons. Called when the GUI has started. Allows defining | |
1556 * signs before it starts. | |
1557 */ | |
1558 void | |
1559 sign_gui_started(void) | |
1560 { | |
1561 sign_T *sp; | |
1562 | |
1563 for (sp = first_sign; sp != NULL; sp = sp->sn_next) | |
1564 if (sp->sn_icon != NULL) | |
1565 sp->sn_image = gui_mch_register_sign(sp->sn_icon); | |
1566 } | |
1567 # endif | |
1568 | |
1569 /* | |
1570 * List one sign. | |
1571 */ | |
1572 static void | |
1573 sign_list_defined(sign_T *sp) | |
1574 { | |
1575 char_u *p; | |
1576 | |
1577 smsg((char_u *)"sign %s", sp->sn_name); | |
1578 if (sp->sn_icon != NULL) | |
1579 { | |
1580 MSG_PUTS(" icon="); | |
1581 msg_outtrans(sp->sn_icon); | |
1582 # ifdef FEAT_SIGN_ICONS | |
1583 if (sp->sn_image == NULL) | |
1584 MSG_PUTS(_(" (NOT FOUND)")); | |
1585 # else | |
1586 MSG_PUTS(_(" (not supported)")); | |
1587 # endif | |
1588 } | |
1589 if (sp->sn_text != NULL) | |
1590 { | |
1591 MSG_PUTS(" text="); | |
1592 msg_outtrans(sp->sn_text); | |
1593 } | |
1594 if (sp->sn_line_hl > 0) | |
1595 { | |
1596 MSG_PUTS(" linehl="); | |
1597 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE); | |
1598 if (p == NULL) | |
1599 MSG_PUTS("NONE"); | |
1600 else | |
1601 msg_puts(p); | |
1602 } | |
1603 if (sp->sn_text_hl > 0) | |
1604 { | |
1605 MSG_PUTS(" texthl="); | |
1606 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE); | |
1607 if (p == NULL) | |
1608 MSG_PUTS("NONE"); | |
1609 else | |
1610 msg_puts(p); | |
1611 } | |
1612 } | |
1613 | |
1614 /* | |
1615 * Undefine a sign and free its memory. | |
1616 */ | |
1617 static void | |
1618 sign_undefine(sign_T *sp, sign_T *sp_prev) | |
1619 { | |
1620 vim_free(sp->sn_name); | |
1621 vim_free(sp->sn_icon); | |
1622 # ifdef FEAT_SIGN_ICONS | |
1623 if (sp->sn_image != NULL) | |
1624 { | |
1625 out_flush(); | |
1626 gui_mch_destroy_sign(sp->sn_image); | |
1627 } | |
1628 # endif | |
1629 vim_free(sp->sn_text); | |
1630 if (sp_prev == NULL) | |
1631 first_sign = sp->sn_next; | |
1632 else | |
1633 sp_prev->sn_next = sp->sn_next; | |
1634 vim_free(sp); | |
1635 } | |
1636 | |
1637 /* | |
1638 * Get highlighting attribute for sign "typenr". | |
1639 * If "line" is TRUE: line highl, if FALSE: text highl. | |
1640 */ | |
1641 int | |
1642 sign_get_attr(int typenr, int line) | |
1643 { | |
1644 sign_T *sp; | |
1645 | |
1646 for (sp = first_sign; sp != NULL; sp = sp->sn_next) | |
1647 if (sp->sn_typenr == typenr) | |
1648 { | |
1649 if (line) | |
1650 { | |
1651 if (sp->sn_line_hl > 0) | |
1652 return syn_id2attr(sp->sn_line_hl); | |
1653 } | |
1654 else | |
1655 { | |
1656 if (sp->sn_text_hl > 0) | |
1657 return syn_id2attr(sp->sn_text_hl); | |
1658 } | |
1659 break; | |
1660 } | |
1661 return 0; | |
1662 } | |
1663 | |
1664 /* | |
1665 * Get text mark for sign "typenr". | |
1666 * Returns NULL if there isn't one. | |
1667 */ | |
1668 char_u * | |
1669 sign_get_text(int typenr) | |
1670 { | |
1671 sign_T *sp; | |
1672 | |
1673 for (sp = first_sign; sp != NULL; sp = sp->sn_next) | |
1674 if (sp->sn_typenr == typenr) | |
1675 return sp->sn_text; | |
1676 return NULL; | |
1677 } | |
1678 | |
1679 # if defined(FEAT_SIGN_ICONS) || defined(PROTO) | |
1680 void * | |
1681 sign_get_image( | |
1682 int typenr) /* the attribute which may have a sign */ | |
1683 { | |
1684 sign_T *sp; | |
1685 | |
1686 for (sp = first_sign; sp != NULL; sp = sp->sn_next) | |
1687 if (sp->sn_typenr == typenr) | |
1688 return sp->sn_image; | |
1689 return NULL; | |
1690 } | |
1691 # endif | |
1692 | |
1693 /* | |
1694 * Undefine/free all signs. | |
1695 */ | |
1696 void | |
1697 free_signs(void) | |
1698 { | |
1699 while (first_sign != NULL) | |
1700 sign_undefine(first_sign, NULL); | |
1701 } | |
1702 | |
1703 # if defined(FEAT_CMDL_COMPL) || defined(PROTO) | |
1704 static enum | |
1705 { | |
1706 EXP_SUBCMD, /* expand :sign sub-commands */ | |
1707 EXP_DEFINE, /* expand :sign define {name} args */ | |
1708 EXP_PLACE, /* expand :sign place {id} args */ | |
1709 EXP_UNPLACE, /* expand :sign unplace" */ | |
1710 EXP_SIGN_NAMES /* expand with name of placed signs */ | |
1711 } expand_what; | |
1712 | |
1713 /* | |
1714 * Function given to ExpandGeneric() to obtain the sign command | |
1715 * expansion. | |
1716 */ | |
1717 char_u * | |
1718 get_sign_name(expand_T *xp UNUSED, int idx) | |
1719 { | |
1720 sign_T *sp; | |
1721 int current_idx; | |
1722 | |
1723 switch (expand_what) | |
1724 { | |
1725 case EXP_SUBCMD: | |
1726 return (char_u *)cmds[idx]; | |
1727 case EXP_DEFINE: | |
1728 { | |
1729 char *define_arg[] = | |
1730 { | |
1731 "icon=", "linehl=", "text=", "texthl=", NULL | |
1732 }; | |
1733 return (char_u *)define_arg[idx]; | |
1734 } | |
1735 case EXP_PLACE: | |
1736 { | |
1737 char *place_arg[] = | |
1738 { | |
1739 "line=", "name=", "group=", "priority=", "file=", | |
1740 "buffer=", NULL | |
1741 }; | |
1742 return (char_u *)place_arg[idx]; | |
1743 } | |
1744 case EXP_UNPLACE: | |
1745 { | |
1746 char *unplace_arg[] = { "group=", "file=", "buffer=", NULL }; | |
1747 return (char_u *)unplace_arg[idx]; | |
1748 } | |
1749 case EXP_SIGN_NAMES: | |
1750 /* Complete with name of signs already defined */ | |
1751 current_idx = 0; | |
1752 for (sp = first_sign; sp != NULL; sp = sp->sn_next) | |
1753 if (current_idx++ == idx) | |
1754 return sp->sn_name; | |
1755 return NULL; | |
1756 default: | |
1757 return NULL; | |
1758 } | |
1759 } | |
1760 | |
1761 /* | |
1762 * Handle command line completion for :sign command. | |
1763 */ | |
1764 void | |
1765 set_context_in_sign_cmd(expand_T *xp, char_u *arg) | |
1766 { | |
1767 char_u *p; | |
1768 char_u *end_subcmd; | |
1769 char_u *last; | |
1770 int cmd_idx; | |
1771 char_u *begin_subcmd_args; | |
1772 | |
1773 /* Default: expand subcommands. */ | |
1774 xp->xp_context = EXPAND_SIGN; | |
1775 expand_what = EXP_SUBCMD; | |
1776 xp->xp_pattern = arg; | |
1777 | |
1778 end_subcmd = skiptowhite(arg); | |
1779 if (*end_subcmd == NUL) | |
1780 /* expand subcmd name | |
1781 * :sign {subcmd}<CTRL-D>*/ | |
1782 return; | |
1783 | |
1784 cmd_idx = sign_cmd_idx(arg, end_subcmd); | |
1785 | |
1786 /* :sign {subcmd} {subcmd_args} | |
1787 * | | |
1788 * begin_subcmd_args */ | |
1789 begin_subcmd_args = skipwhite(end_subcmd); | |
1790 p = skiptowhite(begin_subcmd_args); | |
1791 if (*p == NUL) | |
1792 { | |
1793 /* | |
1794 * Expand first argument of subcmd when possible. | |
1795 * For ":jump {id}" and ":unplace {id}", we could | |
1796 * possibly expand the ids of all signs already placed. | |
1797 */ | |
1798 xp->xp_pattern = begin_subcmd_args; | |
1799 switch (cmd_idx) | |
1800 { | |
1801 case SIGNCMD_LIST: | |
1802 case SIGNCMD_UNDEFINE: | |
1803 /* :sign list <CTRL-D> | |
1804 * :sign undefine <CTRL-D> */ | |
1805 expand_what = EXP_SIGN_NAMES; | |
1806 break; | |
1807 default: | |
1808 xp->xp_context = EXPAND_NOTHING; | |
1809 } | |
1810 return; | |
1811 } | |
1812 | |
1813 /* expand last argument of subcmd */ | |
1814 | |
1815 /* :sign define {name} {args}... | |
1816 * | | |
1817 * p */ | |
1818 | |
1819 /* Loop until reaching last argument. */ | |
1820 do | |
1821 { | |
1822 p = skipwhite(p); | |
1823 last = p; | |
1824 p = skiptowhite(p); | |
1825 } while (*p != NUL); | |
1826 | |
1827 p = vim_strchr(last, '='); | |
1828 | |
1829 /* :sign define {name} {args}... {last}= | |
1830 * | | | |
1831 * last p */ | |
1832 if (p == NULL) | |
1833 { | |
1834 /* Expand last argument name (before equal sign). */ | |
1835 xp->xp_pattern = last; | |
1836 switch (cmd_idx) | |
1837 { | |
1838 case SIGNCMD_DEFINE: | |
1839 expand_what = EXP_DEFINE; | |
1840 break; | |
1841 case SIGNCMD_PLACE: | |
1842 expand_what = EXP_PLACE; | |
1843 break; | |
1844 case SIGNCMD_JUMP: | |
1845 case SIGNCMD_UNPLACE: | |
1846 expand_what = EXP_UNPLACE; | |
1847 break; | |
1848 default: | |
1849 xp->xp_context = EXPAND_NOTHING; | |
1850 } | |
1851 } | |
1852 else | |
1853 { | |
1854 /* Expand last argument value (after equal sign). */ | |
1855 xp->xp_pattern = p + 1; | |
1856 switch (cmd_idx) | |
1857 { | |
1858 case SIGNCMD_DEFINE: | |
1859 if (STRNCMP(last, "texthl", p - last) == 0 || | |
1860 STRNCMP(last, "linehl", p - last) == 0) | |
1861 xp->xp_context = EXPAND_HIGHLIGHT; | |
1862 else if (STRNCMP(last, "icon", p - last) == 0) | |
1863 xp->xp_context = EXPAND_FILES; | |
1864 else | |
1865 xp->xp_context = EXPAND_NOTHING; | |
1866 break; | |
1867 case SIGNCMD_PLACE: | |
1868 if (STRNCMP(last, "name", p - last) == 0) | |
1869 expand_what = EXP_SIGN_NAMES; | |
1870 else | |
1871 xp->xp_context = EXPAND_NOTHING; | |
1872 break; | |
1873 default: | |
1874 xp->xp_context = EXPAND_NOTHING; | |
1875 } | |
1876 } | |
1877 } | |
1878 # endif | |
1879 | |
1880 #endif /* FEAT_SIGNS */ |