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 usable 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 specified by 'type'. Returns 0 if a sign is not 378 * found 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 redraw_buf_line_later(buf, lnum); 443 444 // Check whether only one sign needs to be deleted 445 // If deleting a sign with a specific identifier in a particular 446 // group or deleting any sign at a particular line number, delete 447 // only one sign. 448 if (group == NULL 449 || (*group != '*' && id != 0) 450 || (*group == '*' && atlnum != 0)) 451 break; 452 } 453 else 454 lastp = &sign->next; 455 } 456 457 // When deleting the last sign the cursor position may change, because the 458 // sign columns no longer shows. And the 'signcolumn' may be hidden. 459 if (buf->b_signlist == NULL) 460 { 461 redraw_buf_later(buf, NOT_VALID); 462 changed_cline_bef_curs(); 463 } 464 465 return lnum; 466 } 467 468 469 /* 470 * Find the line number of the sign with the requested id in group 'group'. If 471 * the sign does not exist, return 0 as the line number. This will still let 472 * the correct file get loaded. 473 */ 474 int 475 buf_findsign( 476 buf_T *buf, // buffer to store sign in 477 int id, // sign ID 478 char_u *group) // sign group 479 { 480 signlist_T *sign; // a sign in the signlist 481 482 FOR_ALL_SIGNS_IN_BUF(buf, sign) 483 if (sign->id == id && sign_in_group(sign, group)) 484 return sign->lnum; 485 486 return 0; 487 } 488 489 /* 490 * Return the sign at line 'lnum' in buffer 'buf'. Returns NULL if a sign is 491 * not found at the line. If 'groupname' is NULL, searches in the global group. 492 */ 493 static signlist_T * 494 buf_getsign_at_line( 495 buf_T *buf, // buffer whose sign we are searching for 496 linenr_T lnum, // line number of sign 497 char_u *groupname) // sign group name 498 { 499 signlist_T *sign; // a sign in the signlist 500 501 FOR_ALL_SIGNS_IN_BUF(buf, sign) 502 if (sign->lnum == lnum && sign_in_group(sign, groupname)) 503 return sign; 504 505 return NULL; 506 } 507 508 /* 509 * Return the identifier of the sign at line number 'lnum' in buffer 'buf'. 510 */ 511 int 512 buf_findsign_id( 513 buf_T *buf, // buffer whose sign we are searching for 514 linenr_T lnum, // line number of sign 515 char_u *groupname) // sign group name 516 { 517 signlist_T *sign; // a sign in the signlist 518 519 sign = buf_getsign_at_line(buf, lnum, groupname); 520 if (sign != NULL) 521 return sign->id; 522 523 return 0; 524 } 525 526 # if defined(FEAT_NETBEANS_INTG) || defined(PROTO) 527 /* 528 * See if a given type of sign exists on a specific line. 529 */ 530 int 531 buf_findsigntype_id( 532 buf_T *buf, // buffer whose sign we are searching for 533 linenr_T lnum, // line number of sign 534 int typenr) // sign type number 535 { 536 signlist_T *sign; // a sign in the signlist 537 538 FOR_ALL_SIGNS_IN_BUF(buf, sign) 539 if (sign->lnum == lnum && sign->typenr == typenr) 540 return sign->id; 541 542 return 0; 543 } 544 545 546 # if defined(FEAT_SIGN_ICONS) || defined(PROTO) 547 /* 548 * Return the number of icons on the given line. 549 */ 550 int 551 buf_signcount(buf_T *buf, linenr_T lnum) 552 { 553 signlist_T *sign; // a sign in the signlist 554 int count = 0; 555 556 FOR_ALL_SIGNS_IN_BUF(buf, sign) 557 if (sign->lnum == lnum) 558 if (sign_get_image(sign->typenr) != NULL) 559 count++; 560 561 return count; 562 } 563 # endif /* FEAT_SIGN_ICONS */ 564 # endif /* FEAT_NETBEANS_INTG */ 565 566 /* 567 * Delete signs in group 'group' in buffer "buf". If 'group' is '*', then 568 * delete all the signs. 569 */ 570 void 571 buf_delete_signs(buf_T *buf, char_u *group) 572 { 573 signlist_T *sign; 574 signlist_T **lastp; // pointer to pointer to current sign 575 signlist_T *next; 576 577 // When deleting the last sign need to redraw the windows to remove the 578 // sign column. Not when curwin is NULL (this means we're exiting). 579 if (buf->b_signlist != NULL && curwin != NULL) 580 { 581 redraw_buf_later(buf, NOT_VALID); 582 changed_cline_bef_curs(); 583 } 584 585 lastp = &buf->b_signlist; 586 for (sign = buf->b_signlist; sign != NULL; sign = next) 587 { 588 next = sign->next; 589 if (sign_in_group(sign, group)) 590 { 591 *lastp = next; 592 if (next != NULL) 593 next->prev = sign->prev; 594 if (sign->group != NULL) 595 sign_group_unref(sign->group->sg_name); 596 vim_free(sign); 597 } 598 else 599 lastp = &sign->next; 600 } 601 } 602 603 /* 604 * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers. 605 */ 606 static void 607 sign_list_placed(buf_T *rbuf, char_u *sign_group) 608 { 609 buf_T *buf; 610 signlist_T *sign; 611 char lbuf[MSG_BUF_LEN]; 612 char group[MSG_BUF_LEN]; 613 614 msg_puts_title(_("\n--- Signs ---")); 615 msg_putchar('\n'); 616 if (rbuf == NULL) 617 buf = firstbuf; 618 else 619 buf = rbuf; 620 while (buf != NULL && !got_int) 621 { 622 if (buf->b_signlist != NULL) 623 { 624 vim_snprintf(lbuf, MSG_BUF_LEN, _("Signs for %s:"), buf->b_fname); 625 msg_puts_attr(lbuf, HL_ATTR(HLF_D)); 626 msg_putchar('\n'); 627 } 628 FOR_ALL_SIGNS_IN_BUF(buf, sign) 629 { 630 if (got_int) 631 break; 632 if (!sign_in_group(sign, sign_group)) 633 continue; 634 if (sign->group != NULL) 635 vim_snprintf(group, MSG_BUF_LEN, _(" group=%s"), 636 sign->group->sg_name); 637 else 638 group[0] = '\0'; 639 vim_snprintf(lbuf, MSG_BUF_LEN, 640 _(" line=%ld id=%d%s name=%s priority=%d"), 641 (long)sign->lnum, sign->id, group, 642 sign_typenr2name(sign->typenr), sign->priority); 643 msg_puts(lbuf); 644 msg_putchar('\n'); 645 } 646 if (rbuf != NULL) 647 break; 648 buf = buf->b_next; 649 } 650 } 651 652 /* 653 * Adjust a placed sign for inserted/deleted lines. 654 */ 655 void 656 sign_mark_adjust( 657 linenr_T line1, 658 linenr_T line2, 659 long amount, 660 long amount_after) 661 { 662 signlist_T *sign; // a sign in a b_signlist 663 linenr_T new_lnum; 664 665 FOR_ALL_SIGNS_IN_BUF(curbuf, sign) 666 { 667 // Ignore changes to lines after the sign 668 if (sign->lnum < line1) 669 continue; 670 new_lnum = sign->lnum; 671 if (sign->lnum >= line1 && sign->lnum <= line2) 672 { 673 if (amount != MAXLNUM) 674 new_lnum += amount; 675 } 676 else if (sign->lnum > line2) 677 // Lines inserted or deleted before the sign 678 new_lnum += amount_after; 679 680 // If the new sign line number is past the last line in the buffer, 681 // then don't adjust the line number. Otherwise, it will always be past 682 // the last line and will not be visible. 683 if (new_lnum <= curbuf->b_ml.ml_line_count) 684 sign->lnum = new_lnum; 685 } 686 } 687 688 /* 689 * Find index of a ":sign" subcmd from its name. 690 * "*end_cmd" must be writable. 691 */ 692 static int 693 sign_cmd_idx( 694 char_u *begin_cmd, // begin of sign subcmd 695 char_u *end_cmd) // just after sign subcmd 696 { 697 int idx; 698 char save = *end_cmd; 699 700 *end_cmd = NUL; 701 for (idx = 0; ; ++idx) 702 if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0) 703 break; 704 *end_cmd = save; 705 return idx; 706 } 707 708 /* 709 * Find a sign by name. Also returns pointer to the previous sign. 710 */ 711 static sign_T * 712 sign_find(char_u *name, sign_T **sp_prev) 713 { 714 sign_T *sp; 715 716 if (sp_prev != NULL) 717 *sp_prev = NULL; 718 for (sp = first_sign; sp != NULL; sp = sp->sn_next) 719 { 720 if (STRCMP(sp->sn_name, name) == 0) 721 break; 722 if (sp_prev != NULL) 723 *sp_prev = sp; 724 } 725 726 return sp; 727 } 728 729 /* 730 * Allocate a new sign 731 */ 732 static sign_T * 733 alloc_new_sign(char_u *name) 734 { 735 sign_T *sp; 736 sign_T *lp; 737 int start = next_sign_typenr; 738 739 // Allocate a new sign. 740 sp = (sign_T *)alloc_clear_id((unsigned)sizeof(sign_T), 741 aid_sign_define_by_name); 742 if (sp == NULL) 743 return NULL; 744 745 // Check that next_sign_typenr is not already being used. 746 // This only happens after wrapping around. Hopefully 747 // another one got deleted and we can use its number. 748 for (lp = first_sign; lp != NULL; ) 749 { 750 if (lp->sn_typenr == next_sign_typenr) 751 { 752 ++next_sign_typenr; 753 if (next_sign_typenr == MAX_TYPENR) 754 next_sign_typenr = 1; 755 if (next_sign_typenr == start) 756 { 757 vim_free(sp); 758 emsg(_("E612: Too many signs defined")); 759 return NULL; 760 } 761 lp = first_sign; // start all over 762 continue; 763 } 764 lp = lp->sn_next; 765 } 766 767 sp->sn_typenr = next_sign_typenr; 768 if (++next_sign_typenr == MAX_TYPENR) 769 next_sign_typenr = 1; // wrap around 770 771 sp->sn_name = vim_strsave(name); 772 if (sp->sn_name == NULL) // out of memory 773 { 774 vim_free(sp); 775 return NULL; 776 } 777 778 return sp; 779 } 780 781 /* 782 * Initialize the icon information for a new sign 783 */ 784 static void 785 sign_define_init_icon(sign_T *sp, char_u *icon) 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 /* 802 * Initialize the text for a new sign 803 */ 804 static int 805 sign_define_init_text(sign_T *sp, char_u *text) 806 { 807 char_u *s; 808 char_u *endp; 809 int cells; 810 int len; 811 812 endp = text + (int)STRLEN(text); 813 814 // Remove backslashes so that it is possible to use a space. 815 for (s = text; s + 1 < endp; ++s) 816 if (*s == '\\') 817 { 818 STRMOVE(s, s + 1); 819 --endp; 820 } 821 822 // Count cells and check for non-printable chars 823 if (has_mbyte) 824 { 825 cells = 0; 826 for (s = text; s < endp; s += (*mb_ptr2len)(s)) 827 { 828 if (!vim_isprintc((*mb_ptr2char)(s))) 829 break; 830 cells += (*mb_ptr2cells)(s); 831 } 832 } 833 else 834 { 835 for (s = text; s < endp; ++s) 836 if (!vim_isprintc(*s)) 837 break; 838 cells = (int)(s - text); 839 } 840 841 // Currently sign text must be one or two display cells 842 if (s != endp || cells < 1 || cells > 2) 843 { 844 semsg(_("E239: Invalid sign text: %s"), text); 845 return FAIL; 846 } 847 848 vim_free(sp->sn_text); 849 // Allocate one byte more if we need to pad up 850 // with a space. 851 len = (int)(endp - text + ((cells == 1) ? 1 : 0)); 852 sp->sn_text = vim_strnsave(text, len); 853 854 // For single character sign text, pad with a space. 855 if (sp->sn_text != NULL && cells == 1) 856 STRCPY(sp->sn_text + len - 1, " "); 857 858 return OK; 859 } 860 861 /* 862 * Define a new sign or update an existing sign 863 */ 864 int 865 sign_define_by_name( 866 char_u *name, 867 char_u *icon, 868 char_u *linehl, 869 char_u *text, 870 char_u *texthl) 871 { 872 sign_T *sp_prev; 873 sign_T *sp; 874 875 sp = sign_find(name, &sp_prev); 876 if (sp == NULL) 877 { 878 sp = alloc_new_sign(name); 879 if (sp == NULL) 880 return FAIL; 881 882 // add the new sign to the list of signs 883 if (sp_prev == NULL) 884 first_sign = sp; 885 else 886 sp_prev->sn_next = sp; 887 } 888 889 // set values for a defined sign. 890 if (icon != NULL) 891 sign_define_init_icon(sp, icon); 892 893 if (text != NULL && (sign_define_init_text(sp, text) == FAIL)) 894 return FAIL; 895 896 if (linehl != NULL) 897 sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl)); 898 899 if (texthl != NULL) 900 sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl)); 901 902 return OK; 903 } 904 905 /* 906 * Free the sign specified by 'name'. 907 */ 908 int 909 sign_undefine_by_name(char_u *name) 910 { 911 sign_T *sp_prev; 912 sign_T *sp; 913 914 sp = sign_find(name, &sp_prev); 915 if (sp == NULL) 916 { 917 semsg(_("E155: Unknown sign: %s"), name); 918 return FAIL; 919 } 920 sign_undefine(sp, sp_prev); 921 922 return OK; 923 } 924 925 /* 926 * List the signs matching 'name' 927 */ 928 static void 929 sign_list_by_name(char_u *name) 930 { 931 sign_T *sp; 932 933 sp = sign_find(name, NULL); 934 if (sp != NULL) 935 sign_list_defined(sp); 936 else 937 semsg(_("E155: Unknown sign: %s"), name); 938 } 939 940 /* 941 * Place a sign at the specified file location or update a sign. 942 */ 943 int 944 sign_place( 945 int *sign_id, 946 char_u *sign_group, 947 char_u *sign_name, 948 buf_T *buf, 949 linenr_T lnum, 950 int prio) 951 { 952 sign_T *sp; 953 954 // Check for reserved character '*' in group name 955 if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0')) 956 return FAIL; 957 958 for (sp = first_sign; sp != NULL; sp = sp->sn_next) 959 if (STRCMP(sp->sn_name, sign_name) == 0) 960 break; 961 if (sp == NULL) 962 { 963 semsg(_("E155: Unknown sign: %s"), sign_name); 964 return FAIL; 965 } 966 if (*sign_id == 0) 967 *sign_id = sign_group_get_next_signid(buf, sign_group); 968 969 if (lnum > 0) 970 // ":sign place {id} line={lnum} name={name} file={fname}": 971 // place a sign 972 buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr); 973 else 974 // ":sign place {id} file={fname}": change sign type 975 lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr); 976 if (lnum > 0) 977 redraw_buf_line_later(buf, lnum); 978 else 979 { 980 semsg(_("E885: Not possible to change sign %s"), sign_name); 981 return FAIL; 982 } 983 984 return OK; 985 } 986 987 /* 988 * Unplace the specified sign 989 */ 990 int 991 sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum) 992 { 993 if (buf->b_signlist == NULL) // No signs in the buffer 994 return OK; 995 996 if (sign_id == 0) 997 { 998 // Delete all the signs in the specified buffer 999 redraw_buf_later(buf, NOT_VALID); 1000 buf_delete_signs(buf, sign_group); 1001 } 1002 else 1003 { 1004 linenr_T lnum; 1005 1006 // Delete only the specified signs 1007 lnum = buf_delsign(buf, atlnum, sign_id, sign_group); 1008 if (lnum == 0) 1009 return FAIL; 1010 } 1011 1012 return OK; 1013 } 1014 1015 /* 1016 * Unplace the sign at the current cursor line. 1017 */ 1018 static void 1019 sign_unplace_at_cursor(char_u *groupname) 1020 { 1021 int id = -1; 1022 1023 id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname); 1024 if (id > 0) 1025 sign_unplace(id, groupname, curwin->w_buffer, curwin->w_cursor.lnum); 1026 else 1027 emsg(_("E159: Missing sign number")); 1028 } 1029 1030 /* 1031 * Jump to a sign. 1032 */ 1033 linenr_T 1034 sign_jump(int sign_id, char_u *sign_group, buf_T *buf) 1035 { 1036 linenr_T lnum; 1037 1038 if ((lnum = buf_findsign(buf, sign_id, sign_group)) <= 0) 1039 { 1040 semsg(_("E157: Invalid sign ID: %d"), sign_id); 1041 return -1; 1042 } 1043 1044 // goto a sign ... 1045 if (buf_jump_open_win(buf) != NULL) 1046 { // ... in a current window 1047 curwin->w_cursor.lnum = lnum; 1048 check_cursor_lnum(); 1049 beginline(BL_WHITE); 1050 } 1051 else 1052 { // ... not currently in a window 1053 char_u *cmd; 1054 1055 if (buf->b_fname == NULL) 1056 { 1057 emsg(_("E934: Cannot jump to a buffer that does not have a name")); 1058 return -1; 1059 } 1060 cmd = alloc((unsigned)STRLEN(buf->b_fname) + 25); 1061 if (cmd == NULL) 1062 return -1; 1063 sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname); 1064 do_cmdline_cmd(cmd); 1065 vim_free(cmd); 1066 } 1067 # ifdef FEAT_FOLDING 1068 foldOpenCursor(); 1069 # endif 1070 1071 return lnum; 1072 } 1073 1074 /* 1075 * ":sign define {name} ..." command 1076 */ 1077 static void 1078 sign_define_cmd(char_u *sign_name, char_u *cmdline) 1079 { 1080 char_u *arg; 1081 char_u *p = cmdline; 1082 char_u *icon = NULL; 1083 char_u *text = NULL; 1084 char_u *linehl = NULL; 1085 char_u *texthl = NULL; 1086 int failed = FALSE; 1087 1088 // set values for a defined sign. 1089 for (;;) 1090 { 1091 arg = skipwhite(p); 1092 if (*arg == NUL) 1093 break; 1094 p = skiptowhite_esc(arg); 1095 if (STRNCMP(arg, "icon=", 5) == 0) 1096 { 1097 arg += 5; 1098 icon = vim_strnsave(arg, (int)(p - arg)); 1099 } 1100 else if (STRNCMP(arg, "text=", 5) == 0) 1101 { 1102 arg += 5; 1103 text = vim_strnsave(arg, (int)(p - arg)); 1104 } 1105 else if (STRNCMP(arg, "linehl=", 7) == 0) 1106 { 1107 arg += 7; 1108 linehl = vim_strnsave(arg, (int)(p - arg)); 1109 } 1110 else if (STRNCMP(arg, "texthl=", 7) == 0) 1111 { 1112 arg += 7; 1113 texthl = vim_strnsave(arg, (int)(p - arg)); 1114 } 1115 else 1116 { 1117 semsg(_(e_invarg2), arg); 1118 failed = TRUE; 1119 break; 1120 } 1121 } 1122 1123 if (!failed) 1124 sign_define_by_name(sign_name, icon, linehl, text, texthl); 1125 1126 vim_free(icon); 1127 vim_free(text); 1128 vim_free(linehl); 1129 vim_free(texthl); 1130 } 1131 1132 /* 1133 * ":sign place" command 1134 */ 1135 static void 1136 sign_place_cmd( 1137 buf_T *buf, 1138 linenr_T lnum, 1139 char_u *sign_name, 1140 int id, 1141 char_u *group, 1142 int prio) 1143 { 1144 if (id <= 0) 1145 { 1146 // List signs placed in a file/buffer 1147 // :sign place file={fname} 1148 // :sign place group={group} file={fname} 1149 // :sign place group=* file={fname} 1150 // :sign place buffer={nr} 1151 // :sign place group={group} buffer={nr} 1152 // :sign place group=* buffer={nr} 1153 // :sign place 1154 // :sign place group={group} 1155 // :sign place group=* 1156 if (lnum >= 0 || sign_name != NULL 1157 || (group != NULL && *group == '\0')) 1158 emsg(_(e_invarg)); 1159 else 1160 sign_list_placed(buf, group); 1161 } 1162 else 1163 { 1164 // Place a new sign 1165 if (sign_name == NULL || buf == NULL 1166 || (group != NULL && *group == '\0')) 1167 { 1168 emsg(_(e_invarg)); 1169 return; 1170 } 1171 1172 sign_place(&id, group, sign_name, buf, lnum, prio); 1173 } 1174 } 1175 1176 /* 1177 * ":sign unplace" command 1178 */ 1179 static void 1180 sign_unplace_cmd( 1181 buf_T *buf, 1182 linenr_T lnum, 1183 char_u *sign_name, 1184 int id, 1185 char_u *group) 1186 { 1187 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0')) 1188 { 1189 emsg(_(e_invarg)); 1190 return; 1191 } 1192 1193 if (id == -2) 1194 { 1195 if (buf != NULL) 1196 // :sign unplace * file={fname} 1197 // :sign unplace * group={group} file={fname} 1198 // :sign unplace * group=* file={fname} 1199 // :sign unplace * buffer={nr} 1200 // :sign unplace * group={group} buffer={nr} 1201 // :sign unplace * group=* buffer={nr} 1202 sign_unplace(0, group, buf, 0); 1203 else 1204 // :sign unplace * 1205 // :sign unplace * group={group} 1206 // :sign unplace * group=* 1207 FOR_ALL_BUFFERS(buf) 1208 if (buf->b_signlist != NULL) 1209 buf_delete_signs(buf, group); 1210 } 1211 else 1212 { 1213 if (buf != NULL) 1214 // :sign unplace {id} file={fname} 1215 // :sign unplace {id} group={group} file={fname} 1216 // :sign unplace {id} group=* file={fname} 1217 // :sign unplace {id} buffer={nr} 1218 // :sign unplace {id} group={group} buffer={nr} 1219 // :sign unplace {id} group=* buffer={nr} 1220 sign_unplace(id, group, buf, 0); 1221 else 1222 { 1223 if (id == -1) 1224 { 1225 // :sign unplace group={group} 1226 // :sign unplace group=* 1227 sign_unplace_at_cursor(group); 1228 } 1229 else 1230 { 1231 // :sign unplace {id} 1232 // :sign unplace {id} group={group} 1233 // :sign unplace {id} group=* 1234 FOR_ALL_BUFFERS(buf) 1235 sign_unplace(id, group, buf, 0); 1236 } 1237 } 1238 } 1239 } 1240 1241 /* 1242 * Jump to a placed sign commands: 1243 * :sign jump {id} file={fname} 1244 * :sign jump {id} buffer={nr} 1245 * :sign jump {id} group={group} file={fname} 1246 * :sign jump {id} group={group} buffer={nr} 1247 */ 1248 static void 1249 sign_jump_cmd( 1250 buf_T *buf, 1251 linenr_T lnum, 1252 char_u *sign_name, 1253 int id, 1254 char_u *group) 1255 { 1256 if (sign_name == NULL && group == NULL && id == -1) 1257 { 1258 emsg(_(e_argreq)); 1259 return; 1260 } 1261 1262 if (buf == NULL || (group != NULL && *group == '\0') 1263 || lnum >= 0 || sign_name != NULL) 1264 { 1265 // File or buffer is not specified or an empty group is used 1266 // or a line number or a sign name is specified. 1267 emsg(_(e_invarg)); 1268 return; 1269 } 1270 (void)sign_jump(id, group, buf); 1271 } 1272 1273 /* 1274 * Parse the command line arguments for the ":sign place", ":sign unplace" and 1275 * ":sign jump" commands. 1276 * The supported arguments are: line={lnum} name={name} group={group} 1277 * priority={prio} and file={fname} or buffer={nr}. 1278 */ 1279 static int 1280 parse_sign_cmd_args( 1281 int cmd, 1282 char_u *arg, 1283 char_u **sign_name, 1284 int *signid, 1285 char_u **group, 1286 int *prio, 1287 buf_T **buf, 1288 linenr_T *lnum) 1289 { 1290 char_u *arg1; 1291 char_u *name; 1292 char_u *filename = NULL; 1293 int lnum_arg = FALSE; 1294 1295 // first arg could be placed sign id 1296 arg1 = arg; 1297 if (VIM_ISDIGIT(*arg)) 1298 { 1299 *signid = getdigits(&arg); 1300 if (!VIM_ISWHITE(*arg) && *arg != NUL) 1301 { 1302 *signid = -1; 1303 arg = arg1; 1304 } 1305 else 1306 arg = skipwhite(arg); 1307 } 1308 1309 while (*arg != NUL) 1310 { 1311 if (STRNCMP(arg, "line=", 5) == 0) 1312 { 1313 arg += 5; 1314 *lnum = atoi((char *)arg); 1315 arg = skiptowhite(arg); 1316 lnum_arg = TRUE; 1317 } 1318 else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE) 1319 { 1320 if (*signid != -1) 1321 { 1322 emsg(_(e_invarg)); 1323 return FAIL; 1324 } 1325 *signid = -2; 1326 arg = skiptowhite(arg + 1); 1327 } 1328 else if (STRNCMP(arg, "name=", 5) == 0) 1329 { 1330 arg += 5; 1331 name = arg; 1332 arg = skiptowhite(arg); 1333 if (*arg != NUL) 1334 *arg++ = NUL; 1335 while (name[0] == '0' && name[1] != NUL) 1336 ++name; 1337 *sign_name = name; 1338 } 1339 else if (STRNCMP(arg, "group=", 6) == 0) 1340 { 1341 arg += 6; 1342 *group = arg; 1343 arg = skiptowhite(arg); 1344 if (*arg != NUL) 1345 *arg++ = NUL; 1346 } 1347 else if (STRNCMP(arg, "priority=", 9) == 0) 1348 { 1349 arg += 9; 1350 *prio = atoi((char *)arg); 1351 arg = skiptowhite(arg); 1352 } 1353 else if (STRNCMP(arg, "file=", 5) == 0) 1354 { 1355 arg += 5; 1356 filename = arg; 1357 *buf = buflist_findname_exp(arg); 1358 break; 1359 } 1360 else if (STRNCMP(arg, "buffer=", 7) == 0) 1361 { 1362 arg += 7; 1363 filename = arg; 1364 *buf = buflist_findnr((int)getdigits(&arg)); 1365 if (*skipwhite(arg) != NUL) 1366 emsg(_(e_trailing)); 1367 break; 1368 } 1369 else 1370 { 1371 emsg(_(e_invarg)); 1372 return FAIL; 1373 } 1374 arg = skipwhite(arg); 1375 } 1376 1377 if (filename != NULL && *buf == NULL) 1378 { 1379 semsg(_("E158: Invalid buffer name: %s"), filename); 1380 return FAIL; 1381 } 1382 1383 // If the filename is not supplied for the sign place or the sign jump 1384 // command, then use the current buffer. 1385 if (filename == NULL && ((cmd == SIGNCMD_PLACE && lnum_arg) 1386 || cmd == SIGNCMD_JUMP)) 1387 *buf = curwin->w_buffer; 1388 1389 return OK; 1390 } 1391 1392 /* 1393 * ":sign" command 1394 */ 1395 void 1396 ex_sign(exarg_T *eap) 1397 { 1398 char_u *arg = eap->arg; 1399 char_u *p; 1400 int idx; 1401 sign_T *sp; 1402 buf_T *buf = NULL; 1403 1404 // Parse the subcommand. 1405 p = skiptowhite(arg); 1406 idx = sign_cmd_idx(arg, p); 1407 if (idx == SIGNCMD_LAST) 1408 { 1409 semsg(_("E160: Unknown sign command: %s"), arg); 1410 return; 1411 } 1412 arg = skipwhite(p); 1413 1414 if (idx <= SIGNCMD_LIST) 1415 { 1416 // Define, undefine or list signs. 1417 if (idx == SIGNCMD_LIST && *arg == NUL) 1418 { 1419 // ":sign list": list all defined signs 1420 for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next) 1421 sign_list_defined(sp); 1422 } 1423 else if (*arg == NUL) 1424 emsg(_("E156: Missing sign name")); 1425 else 1426 { 1427 char_u *name; 1428 1429 // Isolate the sign name. If it's a number skip leading zeroes, 1430 // so that "099" and "99" are the same sign. But keep "0". 1431 p = skiptowhite(arg); 1432 if (*p != NUL) 1433 *p++ = NUL; 1434 while (arg[0] == '0' && arg[1] != NUL) 1435 ++arg; 1436 name = vim_strsave(arg); 1437 1438 if (idx == SIGNCMD_DEFINE) 1439 sign_define_cmd(name, p); 1440 else if (idx == SIGNCMD_LIST) 1441 // ":sign list {name}" 1442 sign_list_by_name(name); 1443 else 1444 // ":sign undefine {name}" 1445 sign_undefine_by_name(name); 1446 1447 vim_free(name); 1448 return; 1449 } 1450 } 1451 else 1452 { 1453 int id = -1; 1454 linenr_T lnum = -1; 1455 char_u *sign_name = NULL; 1456 char_u *group = NULL; 1457 int prio = SIGN_DEF_PRIO; 1458 1459 // Parse command line arguments 1460 if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio, 1461 &buf, &lnum) == FAIL) 1462 return; 1463 1464 if (idx == SIGNCMD_PLACE) 1465 sign_place_cmd(buf, lnum, sign_name, id, group, prio); 1466 else if (idx == SIGNCMD_UNPLACE) 1467 sign_unplace_cmd(buf, lnum, sign_name, id, group); 1468 else if (idx == SIGNCMD_JUMP) 1469 sign_jump_cmd(buf, lnum, sign_name, id, group); 1470 } 1471 } 1472 1473 /* 1474 * Return information about a specified sign 1475 */ 1476 static void 1477 sign_getinfo(sign_T *sp, dict_T *retdict) 1478 { 1479 char_u *p; 1480 1481 dict_add_string(retdict, "name", (char_u *)sp->sn_name); 1482 if (sp->sn_icon != NULL) 1483 dict_add_string(retdict, "icon", (char_u *)sp->sn_icon); 1484 if (sp->sn_text != NULL) 1485 dict_add_string(retdict, "text", (char_u *)sp->sn_text); 1486 if (sp->sn_line_hl > 0) 1487 { 1488 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE); 1489 if (p == NULL) 1490 p = (char_u *)"NONE"; 1491 dict_add_string(retdict, "linehl", (char_u *)p); 1492 } 1493 if (sp->sn_text_hl > 0) 1494 { 1495 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE); 1496 if (p == NULL) 1497 p = (char_u *)"NONE"; 1498 dict_add_string(retdict, "texthl", (char_u *)p); 1499 } 1500 } 1501 1502 /* 1503 * If 'name' is NULL, return a list of all the defined signs. 1504 * Otherwise, return information about the specified sign. 1505 */ 1506 void 1507 sign_getlist(char_u *name, list_T *retlist) 1508 { 1509 sign_T *sp = first_sign; 1510 dict_T *dict; 1511 1512 if (name != NULL) 1513 { 1514 sp = sign_find(name, NULL); 1515 if (sp == NULL) 1516 return; 1517 } 1518 1519 for (; sp != NULL && !got_int; sp = sp->sn_next) 1520 { 1521 if ((dict = dict_alloc_id(aid_sign_getlist)) == NULL) 1522 return; 1523 if (list_append_dict(retlist, dict) == FAIL) 1524 return; 1525 sign_getinfo(sp, dict); 1526 1527 if (name != NULL) // handle only the specified sign 1528 break; 1529 } 1530 } 1531 1532 /* 1533 * Returns information about signs placed in a buffer as list of dicts. 1534 */ 1535 void 1536 get_buffer_signs(buf_T *buf, list_T *l) 1537 { 1538 signlist_T *sign; 1539 dict_T *d; 1540 1541 FOR_ALL_SIGNS_IN_BUF(buf, sign) 1542 { 1543 if ((d = sign_get_info(sign)) != NULL) 1544 list_append_dict(l, d); 1545 } 1546 } 1547 1548 /* 1549 * Return information about all the signs placed in a buffer 1550 */ 1551 static void 1552 sign_get_placed_in_buf( 1553 buf_T *buf, 1554 linenr_T lnum, 1555 int sign_id, 1556 char_u *sign_group, 1557 list_T *retlist) 1558 { 1559 dict_T *d; 1560 list_T *l; 1561 signlist_T *sign; 1562 dict_T *sdict; 1563 1564 if ((d = dict_alloc_id(aid_sign_getplaced_dict)) == NULL) 1565 return; 1566 list_append_dict(retlist, d); 1567 1568 dict_add_number(d, "bufnr", (long)buf->b_fnum); 1569 1570 if ((l = list_alloc_id(aid_sign_getplaced_list)) == NULL) 1571 return; 1572 dict_add_list(d, "signs", l); 1573 1574 FOR_ALL_SIGNS_IN_BUF(buf, sign) 1575 { 1576 if (!sign_in_group(sign, sign_group)) 1577 continue; 1578 if ((lnum == 0 && sign_id == 0) 1579 || (sign_id == 0 && lnum == sign->lnum) 1580 || (lnum == 0 && sign_id == sign->id) 1581 || (lnum == sign->lnum && sign_id == sign->id)) 1582 { 1583 if ((sdict = sign_get_info(sign)) != NULL) 1584 list_append_dict(l, sdict); 1585 } 1586 } 1587 } 1588 1589 /* 1590 * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the 1591 * sign placed at the line number. If 'lnum' is zero, return all the signs 1592 * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers. 1593 */ 1594 void 1595 sign_get_placed( 1596 buf_T *buf, 1597 linenr_T lnum, 1598 int sign_id, 1599 char_u *sign_group, 1600 list_T *retlist) 1601 { 1602 if (buf != NULL) 1603 sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist); 1604 else 1605 { 1606 FOR_ALL_BUFFERS(buf) 1607 { 1608 if (buf->b_signlist != NULL) 1609 sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist); 1610 } 1611 } 1612 } 1613 1614 # if defined(FEAT_SIGN_ICONS) || defined(PROTO) 1615 /* 1616 * Allocate the icons. Called when the GUI has started. Allows defining 1617 * signs before it starts. 1618 */ 1619 void 1620 sign_gui_started(void) 1621 { 1622 sign_T *sp; 1623 1624 for (sp = first_sign; sp != NULL; sp = sp->sn_next) 1625 if (sp->sn_icon != NULL) 1626 sp->sn_image = gui_mch_register_sign(sp->sn_icon); 1627 } 1628 # endif 1629 1630 /* 1631 * List one sign. 1632 */ 1633 static void 1634 sign_list_defined(sign_T *sp) 1635 { 1636 char_u *p; 1637 1638 smsg("sign %s", sp->sn_name); 1639 if (sp->sn_icon != NULL) 1640 { 1641 msg_puts(" icon="); 1642 msg_outtrans(sp->sn_icon); 1643 # ifdef FEAT_SIGN_ICONS 1644 if (sp->sn_image == NULL) 1645 msg_puts(_(" (NOT FOUND)")); 1646 # else 1647 msg_puts(_(" (not supported)")); 1648 # endif 1649 } 1650 if (sp->sn_text != NULL) 1651 { 1652 msg_puts(" text="); 1653 msg_outtrans(sp->sn_text); 1654 } 1655 if (sp->sn_line_hl > 0) 1656 { 1657 msg_puts(" linehl="); 1658 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE); 1659 if (p == NULL) 1660 msg_puts("NONE"); 1661 else 1662 msg_puts((char *)p); 1663 } 1664 if (sp->sn_text_hl > 0) 1665 { 1666 msg_puts(" texthl="); 1667 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE); 1668 if (p == NULL) 1669 msg_puts("NONE"); 1670 else 1671 msg_puts((char *)p); 1672 } 1673 } 1674 1675 /* 1676 * Undefine a sign and free its memory. 1677 */ 1678 static void 1679 sign_undefine(sign_T *sp, sign_T *sp_prev) 1680 { 1681 vim_free(sp->sn_name); 1682 vim_free(sp->sn_icon); 1683 # ifdef FEAT_SIGN_ICONS 1684 if (sp->sn_image != NULL) 1685 { 1686 out_flush(); 1687 gui_mch_destroy_sign(sp->sn_image); 1688 } 1689 # endif 1690 vim_free(sp->sn_text); 1691 if (sp_prev == NULL) 1692 first_sign = sp->sn_next; 1693 else 1694 sp_prev->sn_next = sp->sn_next; 1695 vim_free(sp); 1696 } 1697 1698 /* 1699 * Get highlighting attribute for sign "typenr". 1700 * If "line" is TRUE: line highl, if FALSE: text highl. 1701 */ 1702 int 1703 sign_get_attr(int typenr, int line) 1704 { 1705 sign_T *sp; 1706 1707 for (sp = first_sign; sp != NULL; sp = sp->sn_next) 1708 if (sp->sn_typenr == typenr) 1709 { 1710 if (line) 1711 { 1712 if (sp->sn_line_hl > 0) 1713 return syn_id2attr(sp->sn_line_hl); 1714 } 1715 else 1716 { 1717 if (sp->sn_text_hl > 0) 1718 return syn_id2attr(sp->sn_text_hl); 1719 } 1720 break; 1721 } 1722 return 0; 1723 } 1724 1725 /* 1726 * Get text mark for sign "typenr". 1727 * Returns NULL if there isn't one. 1728 */ 1729 char_u * 1730 sign_get_text(int typenr) 1731 { 1732 sign_T *sp; 1733 1734 for (sp = first_sign; sp != NULL; sp = sp->sn_next) 1735 if (sp->sn_typenr == typenr) 1736 return sp->sn_text; 1737 return NULL; 1738 } 1739 1740 # if defined(FEAT_SIGN_ICONS) || defined(PROTO) 1741 void * 1742 sign_get_image( 1743 int typenr) // the attribute which may have a sign 1744 { 1745 sign_T *sp; 1746 1747 for (sp = first_sign; sp != NULL; sp = sp->sn_next) 1748 if (sp->sn_typenr == typenr) 1749 return sp->sn_image; 1750 return NULL; 1751 } 1752 # endif 1753 1754 /* 1755 * Undefine/free all signs. 1756 */ 1757 void 1758 free_signs(void) 1759 { 1760 while (first_sign != NULL) 1761 sign_undefine(first_sign, NULL); 1762 } 1763 1764 # if defined(FEAT_CMDL_COMPL) || defined(PROTO) 1765 static enum 1766 { 1767 EXP_SUBCMD, // expand :sign sub-commands 1768 EXP_DEFINE, // expand :sign define {name} args 1769 EXP_PLACE, // expand :sign place {id} args 1770 EXP_UNPLACE, // expand :sign unplace" 1771 EXP_SIGN_NAMES // expand with name of placed signs 1772 } expand_what; 1773 1774 /* 1775 * Function given to ExpandGeneric() to obtain the sign command 1776 * expansion. 1777 */ 1778 char_u * 1779 get_sign_name(expand_T *xp UNUSED, int idx) 1780 { 1781 sign_T *sp; 1782 int current_idx; 1783 1784 switch (expand_what) 1785 { 1786 case EXP_SUBCMD: 1787 return (char_u *)cmds[idx]; 1788 case EXP_DEFINE: 1789 { 1790 char *define_arg[] = 1791 { 1792 "icon=", "linehl=", "text=", "texthl=", NULL 1793 }; 1794 return (char_u *)define_arg[idx]; 1795 } 1796 case EXP_PLACE: 1797 { 1798 char *place_arg[] = 1799 { 1800 "line=", "name=", "group=", "priority=", "file=", 1801 "buffer=", NULL 1802 }; 1803 return (char_u *)place_arg[idx]; 1804 } 1805 case EXP_UNPLACE: 1806 { 1807 char *unplace_arg[] = { "group=", "file=", "buffer=", NULL }; 1808 return (char_u *)unplace_arg[idx]; 1809 } 1810 case EXP_SIGN_NAMES: 1811 // Complete with name of signs already defined 1812 current_idx = 0; 1813 for (sp = first_sign; sp != NULL; sp = sp->sn_next) 1814 if (current_idx++ == idx) 1815 return sp->sn_name; 1816 return NULL; 1817 default: 1818 return NULL; 1819 } 1820 } 1821 1822 /* 1823 * Handle command line completion for :sign command. 1824 */ 1825 void 1826 set_context_in_sign_cmd(expand_T *xp, char_u *arg) 1827 { 1828 char_u *p; 1829 char_u *end_subcmd; 1830 char_u *last; 1831 int cmd_idx; 1832 char_u *begin_subcmd_args; 1833 1834 // Default: expand subcommands. 1835 xp->xp_context = EXPAND_SIGN; 1836 expand_what = EXP_SUBCMD; 1837 xp->xp_pattern = arg; 1838 1839 end_subcmd = skiptowhite(arg); 1840 if (*end_subcmd == NUL) 1841 // expand subcmd name 1842 // :sign {subcmd}<CTRL-D> 1843 return; 1844 1845 cmd_idx = sign_cmd_idx(arg, end_subcmd); 1846 1847 // :sign {subcmd} {subcmd_args} 1848 // | 1849 // begin_subcmd_args 1850 begin_subcmd_args = skipwhite(end_subcmd); 1851 p = skiptowhite(begin_subcmd_args); 1852 if (*p == NUL) 1853 { 1854 // 1855 // Expand first argument of subcmd when possible. 1856 // For ":jump {id}" and ":unplace {id}", we could 1857 // possibly expand the ids of all signs already placed. 1858 // 1859 xp->xp_pattern = begin_subcmd_args; 1860 switch (cmd_idx) 1861 { 1862 case SIGNCMD_LIST: 1863 case SIGNCMD_UNDEFINE: 1864 // :sign list <CTRL-D> 1865 // :sign undefine <CTRL-D> 1866 expand_what = EXP_SIGN_NAMES; 1867 break; 1868 default: 1869 xp->xp_context = EXPAND_NOTHING; 1870 } 1871 return; 1872 } 1873 1874 // expand last argument of subcmd 1875 1876 // :sign define {name} {args}... 1877 // | 1878 // p 1879 1880 // Loop until reaching last argument. 1881 do 1882 { 1883 p = skipwhite(p); 1884 last = p; 1885 p = skiptowhite(p); 1886 } while (*p != NUL); 1887 1888 p = vim_strchr(last, '='); 1889 1890 // :sign define {name} {args}... {last}= 1891 // | | 1892 // last p 1893 if (p == NULL) 1894 { 1895 // Expand last argument name (before equal sign). 1896 xp->xp_pattern = last; 1897 switch (cmd_idx) 1898 { 1899 case SIGNCMD_DEFINE: 1900 expand_what = EXP_DEFINE; 1901 break; 1902 case SIGNCMD_PLACE: 1903 expand_what = EXP_PLACE; 1904 break; 1905 case SIGNCMD_JUMP: 1906 case SIGNCMD_UNPLACE: 1907 expand_what = EXP_UNPLACE; 1908 break; 1909 default: 1910 xp->xp_context = EXPAND_NOTHING; 1911 } 1912 } 1913 else 1914 { 1915 // Expand last argument value (after equal sign). 1916 xp->xp_pattern = p + 1; 1917 switch (cmd_idx) 1918 { 1919 case SIGNCMD_DEFINE: 1920 if (STRNCMP(last, "texthl", p - last) == 0 1921 || STRNCMP(last, "linehl", p - last) == 0) 1922 xp->xp_context = EXPAND_HIGHLIGHT; 1923 else if (STRNCMP(last, "icon", p - last) == 0) 1924 xp->xp_context = EXPAND_FILES; 1925 else 1926 xp->xp_context = EXPAND_NOTHING; 1927 break; 1928 case SIGNCMD_PLACE: 1929 if (STRNCMP(last, "name", p - last) == 0) 1930 expand_what = EXP_SIGN_NAMES; 1931 else 1932 xp->xp_context = EXPAND_NOTHING; 1933 break; 1934 default: 1935 xp->xp_context = EXPAND_NOTHING; 1936 } 1937 } 1938 } 1939 # endif 1940 1941 #endif /* FEAT_SIGNS */ 1942