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 #define FOR_ALL_SIGNS(sp) \ 61 for ((sp) = first_sign; (sp) != NULL; (sp) = (sp)->sn_next) 62 63 static hashtab_T sg_table; // sign group (signgroup_T) hashtable 64 static int next_sign_id = 1; // next sign id in the global group 65 66 /* 67 * Initialize data needed for managing signs 68 */ 69 void 70 init_signs(void) 71 { 72 hash_init(&sg_table); // sign group hash table 73 } 74 75 /* 76 * A new sign in group 'groupname' is added. If the group is not present, 77 * create it. Otherwise reference the group. 78 */ 79 static signgroup_T * 80 sign_group_ref(char_u *groupname) 81 { 82 hash_T hash; 83 hashitem_T *hi; 84 signgroup_T *group; 85 86 hash = hash_hash(groupname); 87 hi = hash_lookup(&sg_table, groupname, hash); 88 if (HASHITEM_EMPTY(hi)) 89 { 90 // new group 91 group = alloc(offsetof(signgroup_T, sg_name) + STRLEN(groupname) + 1); 92 if (group == NULL) 93 return NULL; 94 STRCPY(group->sg_name, groupname); 95 group->sg_refcount = 1; 96 group->sg_next_sign_id = 1; 97 hash_add_item(&sg_table, hi, group->sg_name, hash); 98 } 99 else 100 { 101 // existing group 102 group = HI2SG(hi); 103 group->sg_refcount++; 104 } 105 106 return group; 107 } 108 109 /* 110 * A sign in group 'groupname' is removed. If all the signs in this group are 111 * removed, then remove the group. 112 */ 113 static void 114 sign_group_unref(char_u *groupname) 115 { 116 hashitem_T *hi; 117 signgroup_T *group; 118 119 hi = hash_find(&sg_table, groupname); 120 if (!HASHITEM_EMPTY(hi)) 121 { 122 group = HI2SG(hi); 123 group->sg_refcount--; 124 if (group->sg_refcount == 0) 125 { 126 // All the signs in this group are removed 127 hash_remove(&sg_table, hi); 128 vim_free(group); 129 } 130 } 131 } 132 133 /* 134 * Returns TRUE if 'sign' is in 'group'. 135 * A sign can either be in the global group (sign->se_group == NULL) 136 * or in a named group. If 'group' is '*', then the sign is part of the group. 137 */ 138 static int 139 sign_in_group(sign_entry_T *sign, char_u *group) 140 { 141 return ((group != NULL && STRCMP(group, "*") == 0) 142 || (group == NULL && sign->se_group == NULL) 143 || (group != NULL && sign->se_group != NULL 144 && STRCMP(group, sign->se_group->sg_name) == 0)); 145 } 146 147 /* 148 * Return TRUE if "sign" is to be displayed in window "wp". 149 * If the group name starts with "PopUp" it only shows in a popup window. 150 */ 151 static int 152 sign_group_for_window(sign_entry_T *sign, win_T *wp) 153 { 154 int for_popup = sign->se_group != NULL 155 && STRNCMP("PopUp", sign->se_group->sg_name, 5) == 0; 156 157 return WIN_IS_POPUP(wp) ? for_popup : !for_popup; 158 } 159 160 /* 161 * Get the next free sign identifier in the specified group 162 */ 163 static int 164 sign_group_get_next_signid(buf_T *buf, char_u *groupname) 165 { 166 int id = 1; 167 signgroup_T *group = NULL; 168 sign_entry_T *sign; 169 hashitem_T *hi; 170 int found = FALSE; 171 172 if (groupname != NULL) 173 { 174 hi = hash_find(&sg_table, groupname); 175 if (HASHITEM_EMPTY(hi)) 176 return id; 177 group = HI2SG(hi); 178 } 179 180 // Search for the next usable sign identifier 181 while (!found) 182 { 183 if (group == NULL) 184 id = next_sign_id++; // global group 185 else 186 id = group->sg_next_sign_id++; 187 188 // Check whether this sign is already placed in the buffer 189 found = TRUE; 190 FOR_ALL_SIGNS_IN_BUF(buf, sign) 191 { 192 if (id == sign->se_id && sign_in_group(sign, groupname)) 193 { 194 found = FALSE; // sign identifier is in use 195 break; 196 } 197 } 198 } 199 200 return id; 201 } 202 203 /* 204 * Insert a new sign into the signlist for buffer 'buf' between the 'prev' and 205 * 'next' signs. 206 */ 207 static void 208 insert_sign( 209 buf_T *buf, // buffer to store sign in 210 sign_entry_T *prev, // previous sign entry 211 sign_entry_T *next, // next sign entry 212 int id, // sign ID 213 char_u *group, // sign group; NULL for global group 214 int prio, // sign priority 215 linenr_T lnum, // line number which gets the mark 216 int typenr) // typenr of sign we are adding 217 { 218 sign_entry_T *newsign; 219 220 newsign = lalloc_id(sizeof(sign_entry_T), FALSE, aid_insert_sign); 221 if (newsign != NULL) 222 { 223 newsign->se_id = id; 224 newsign->se_lnum = lnum; 225 newsign->se_typenr = typenr; 226 if (group != NULL) 227 { 228 newsign->se_group = sign_group_ref(group); 229 if (newsign->se_group == NULL) 230 { 231 vim_free(newsign); 232 return; 233 } 234 } 235 else 236 newsign->se_group = NULL; 237 newsign->se_priority = prio; 238 newsign->se_next = next; 239 newsign->se_prev = prev; 240 if (next != NULL) 241 next->se_prev = newsign; 242 243 if (prev == NULL) 244 { 245 // When adding first sign need to redraw the windows to create the 246 // column for signs. 247 if (buf->b_signlist == NULL) 248 { 249 redraw_buf_later(buf, NOT_VALID); 250 changed_line_abv_curs(); 251 } 252 253 // first sign in signlist 254 buf->b_signlist = newsign; 255 #ifdef FEAT_NETBEANS_INTG 256 if (netbeans_active()) 257 buf->b_has_sign_column = TRUE; 258 #endif 259 } 260 else 261 prev->se_next = newsign; 262 } 263 } 264 265 /* 266 * Insert a new sign sorted by line number and sign priority. 267 */ 268 static void 269 insert_sign_by_lnum_prio( 270 buf_T *buf, // buffer to store sign in 271 sign_entry_T *prev, // previous sign entry 272 int id, // sign ID 273 char_u *group, // sign group; NULL for global group 274 int prio, // sign priority 275 linenr_T lnum, // line number which gets the mark 276 int typenr) // typenr of sign we are adding 277 { 278 sign_entry_T *sign; 279 280 // keep signs sorted by lnum and by priority: insert new sign at 281 // the proper position in the list for this lnum. 282 while (prev != NULL && prev->se_lnum == lnum && prev->se_priority <= prio) 283 prev = prev->se_prev; 284 if (prev == NULL) 285 sign = buf->b_signlist; 286 else 287 sign = prev->se_next; 288 289 insert_sign(buf, prev, sign, id, group, prio, lnum, typenr); 290 } 291 292 /* 293 * Lookup a sign by typenr. Returns NULL if sign is not found. 294 */ 295 static sign_T * 296 find_sign_by_typenr(int typenr) 297 { 298 sign_T *sp; 299 300 FOR_ALL_SIGNS(sp) 301 if (sp->sn_typenr == typenr) 302 return sp; 303 return NULL; 304 } 305 306 /* 307 * Get the name of a sign by its typenr. 308 */ 309 static char_u * 310 sign_typenr2name(int typenr) 311 { 312 sign_T *sp; 313 314 FOR_ALL_SIGNS(sp) 315 if (sp->sn_typenr == typenr) 316 return sp->sn_name; 317 return (char_u *)_("[Deleted]"); 318 } 319 320 /* 321 * Return information about a sign in a Dict 322 */ 323 static dict_T * 324 sign_get_info(sign_entry_T *sign) 325 { 326 dict_T *d; 327 328 if ((d = dict_alloc_id(aid_sign_getinfo)) == NULL) 329 return NULL; 330 dict_add_number(d, "id", sign->se_id); 331 dict_add_string(d, "group", (sign->se_group == NULL) ? 332 (char_u *)"" : sign->se_group->sg_name); 333 dict_add_number(d, "lnum", sign->se_lnum); 334 dict_add_string(d, "name", sign_typenr2name(sign->se_typenr)); 335 dict_add_number(d, "priority", sign->se_priority); 336 337 return d; 338 } 339 340 /* 341 * Sort the signs placed on the same line as "sign" by priority. Invoked after 342 * changing the priority of an already placed sign. Assumes the signs in the 343 * buffer are sorted by line number and priority. 344 */ 345 static void 346 sign_sort_by_prio_on_line(buf_T *buf, sign_entry_T *sign) 347 { 348 sign_entry_T *p = NULL; 349 350 // If there is only one sign in the buffer or only one sign on the line or 351 // the sign is already sorted by priority, then return. 352 if ((sign->se_prev == NULL 353 || sign->se_prev->se_lnum != sign->se_lnum 354 || sign->se_prev->se_priority > sign->se_priority) 355 && (sign->se_next == NULL 356 || sign->se_next->se_lnum != sign->se_lnum 357 || sign->se_next->se_priority < sign->se_priority)) 358 return; 359 360 // One or more signs on the same line as 'sign' 361 // Find a sign after which 'sign' should be inserted 362 363 // First search backward for a sign with higher priority on the same line 364 p = sign; 365 while (p->se_prev != NULL && p->se_prev->se_lnum == sign->se_lnum 366 && p->se_prev->se_priority <= sign->se_priority) 367 p = p->se_prev; 368 369 if (p == sign) 370 { 371 // Sign not found. Search forward for a sign with priority just before 372 // 'sign'. 373 p = sign->se_next; 374 while (p->se_next != NULL && p->se_next->se_lnum == sign->se_lnum 375 && p->se_next->se_priority > sign->se_priority) 376 p = p->se_next; 377 } 378 379 // Remove 'sign' from the list 380 if (buf->b_signlist == sign) 381 buf->b_signlist = sign->se_next; 382 if (sign->se_prev != NULL) 383 sign->se_prev->se_next = sign->se_next; 384 if (sign->se_next != NULL) 385 sign->se_next->se_prev = sign->se_prev; 386 sign->se_prev = NULL; 387 sign->se_next = NULL; 388 389 // Re-insert 'sign' at the right place 390 if (p->se_priority <= sign->se_priority) 391 { 392 // 'sign' has a higher priority and should be inserted before 'p' 393 sign->se_prev = p->se_prev; 394 sign->se_next = p; 395 p->se_prev = sign; 396 if (sign->se_prev != NULL) 397 sign->se_prev->se_next = sign; 398 if (buf->b_signlist == p) 399 buf->b_signlist = sign; 400 } 401 else 402 { 403 // 'sign' has a lower priority and should be inserted after 'p' 404 sign->se_prev = p; 405 sign->se_next = p->se_next; 406 p->se_next = sign; 407 if (sign->se_next != NULL) 408 sign->se_next->se_prev = sign; 409 } 410 } 411 412 /* 413 * Add the sign into the signlist. Find the right spot to do it though. 414 */ 415 static void 416 buf_addsign( 417 buf_T *buf, // buffer to store sign in 418 int id, // sign ID 419 char_u *groupname, // sign group 420 int prio, // sign priority 421 linenr_T lnum, // line number which gets the mark 422 int typenr) // typenr of sign we are adding 423 { 424 sign_entry_T *sign; // a sign in the signlist 425 sign_entry_T *prev; // the previous sign 426 427 prev = NULL; 428 FOR_ALL_SIGNS_IN_BUF(buf, sign) 429 { 430 if (lnum == sign->se_lnum && id == sign->se_id 431 && sign_in_group(sign, groupname)) 432 { 433 // Update an existing sign 434 sign->se_typenr = typenr; 435 sign->se_priority = prio; 436 sign_sort_by_prio_on_line(buf, sign); 437 return; 438 } 439 else if (lnum < sign->se_lnum) 440 { 441 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, 442 lnum, typenr); 443 return; 444 } 445 prev = sign; 446 } 447 448 insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr); 449 return; 450 } 451 452 /* 453 * For an existing, placed sign "markId" change the type to "typenr". 454 * Returns the line number of the sign, or zero if the sign is not found. 455 */ 456 static linenr_T 457 buf_change_sign_type( 458 buf_T *buf, // buffer to store sign in 459 int markId, // sign ID 460 char_u *group, // sign group 461 int typenr, // typenr of sign we are adding 462 int prio) // sign priority 463 { 464 sign_entry_T *sign; // a sign in the signlist 465 466 FOR_ALL_SIGNS_IN_BUF(buf, sign) 467 { 468 if (sign->se_id == markId && sign_in_group(sign, group)) 469 { 470 sign->se_typenr = typenr; 471 sign->se_priority = prio; 472 sign_sort_by_prio_on_line(buf, sign); 473 return sign->se_lnum; 474 } 475 } 476 477 return (linenr_T)0; 478 } 479 480 /* 481 * Return the attributes of the first sign placed on line 'lnum' in buffer 482 * 'buf'. Used when refreshing the screen. Returns TRUE if a sign is found on 483 * 'lnum', FALSE otherwise. 484 */ 485 int 486 buf_get_signattrs(win_T *wp, linenr_T lnum, sign_attrs_T *sattr) 487 { 488 sign_entry_T *sign; 489 sign_T *sp; 490 buf_T *buf = wp->w_buffer; 491 492 CLEAR_POINTER(sattr); 493 494 FOR_ALL_SIGNS_IN_BUF(buf, sign) 495 { 496 if (sign->se_lnum > lnum) 497 // Signs are sorted by line number in the buffer. No need to check 498 // for signs after the specified line number 'lnum'. 499 break; 500 501 if (sign->se_lnum == lnum 502 # ifdef FEAT_PROP_POPUP 503 && sign_group_for_window(sign, wp) 504 # endif 505 ) 506 { 507 sattr->sat_typenr = sign->se_typenr; 508 sp = find_sign_by_typenr(sign->se_typenr); 509 if (sp == NULL) 510 return FALSE; 511 512 # ifdef FEAT_SIGN_ICONS 513 sattr->sat_icon = sp->sn_image; 514 # endif 515 sattr->sat_text = sp->sn_text; 516 if (sattr->sat_text != NULL && sp->sn_text_hl > 0) 517 sattr->sat_texthl = syn_id2attr(sp->sn_text_hl); 518 if (sp->sn_line_hl > 0) 519 sattr->sat_linehl = syn_id2attr(sp->sn_line_hl); 520 521 // If there is another sign next with the same priority, may 522 // combine the text and the line highlighting. 523 if (sign->se_next != NULL 524 && sign->se_next->se_priority == sign->se_priority 525 && sign->se_next->se_lnum == sign->se_lnum) 526 { 527 sign_T *next_sp = find_sign_by_typenr(sign->se_next->se_typenr); 528 529 if (next_sp != NULL) 530 { 531 if (sattr->sat_icon == NULL && sattr->sat_text == NULL) 532 { 533 # ifdef FEAT_SIGN_ICONS 534 sattr->sat_icon = next_sp->sn_image; 535 # endif 536 sattr->sat_text = next_sp->sn_text; 537 } 538 if (sp->sn_text_hl <= 0 && next_sp->sn_text_hl > 0) 539 sattr->sat_texthl = syn_id2attr(next_sp->sn_text_hl); 540 if (sp->sn_line_hl <= 0 && next_sp->sn_line_hl > 0) 541 sattr->sat_linehl = syn_id2attr(next_sp->sn_line_hl); 542 } 543 } 544 return TRUE; 545 } 546 } 547 return FALSE; 548 } 549 550 /* 551 * Delete sign 'id' in group 'group' from buffer 'buf'. 552 * If 'id' is zero, then delete all the signs in group 'group'. Otherwise 553 * delete only the specified sign. 554 * If 'group' is '*', then delete the sign in all the groups. If 'group' is 555 * NULL, then delete the sign in the global group. Otherwise delete the sign in 556 * the specified group. 557 * Returns the line number of the deleted sign. If multiple signs are deleted, 558 * then returns the line number of the last sign deleted. 559 */ 560 linenr_T 561 buf_delsign( 562 buf_T *buf, // buffer sign is stored in 563 linenr_T atlnum, // sign at this line, 0 - at any line 564 int id, // sign id 565 char_u *group) // sign group 566 { 567 sign_entry_T **lastp; // pointer to pointer to current sign 568 sign_entry_T *sign; // a sign in a b_signlist 569 sign_entry_T *next; // the next sign in a b_signlist 570 linenr_T lnum; // line number whose sign was deleted 571 572 lastp = &buf->b_signlist; 573 lnum = 0; 574 for (sign = buf->b_signlist; sign != NULL; sign = next) 575 { 576 next = sign->se_next; 577 if ((id == 0 || sign->se_id == id) 578 && (atlnum == 0 || sign->se_lnum == atlnum) 579 && sign_in_group(sign, group)) 580 581 { 582 *lastp = next; 583 if (next != NULL) 584 next->se_prev = sign->se_prev; 585 lnum = sign->se_lnum; 586 if (sign->se_group != NULL) 587 sign_group_unref(sign->se_group->sg_name); 588 vim_free(sign); 589 redraw_buf_line_later(buf, lnum); 590 591 // Check whether only one sign needs to be deleted 592 // If deleting a sign with a specific identifier in a particular 593 // group or deleting any sign at a particular line number, delete 594 // only one sign. 595 if (group == NULL 596 || (*group != '*' && id != 0) 597 || (*group == '*' && atlnum != 0)) 598 break; 599 } 600 else 601 lastp = &sign->se_next; 602 } 603 604 // When deleting the last sign the cursor position may change, because the 605 // sign columns no longer shows. And the 'signcolumn' may be hidden. 606 if (buf->b_signlist == NULL) 607 { 608 redraw_buf_later(buf, NOT_VALID); 609 changed_line_abv_curs(); 610 } 611 612 return lnum; 613 } 614 615 616 /* 617 * Find the line number of the sign with the requested id in group 'group'. If 618 * the sign does not exist, return 0 as the line number. This will still let 619 * the correct file get loaded. 620 */ 621 int 622 buf_findsign( 623 buf_T *buf, // buffer to store sign in 624 int id, // sign ID 625 char_u *group) // sign group 626 { 627 sign_entry_T *sign; // a sign in the signlist 628 629 FOR_ALL_SIGNS_IN_BUF(buf, sign) 630 if (sign->se_id == id && sign_in_group(sign, group)) 631 return sign->se_lnum; 632 633 return 0; 634 } 635 636 /* 637 * Return the sign at line 'lnum' in buffer 'buf'. Returns NULL if a sign is 638 * not found at the line. If 'groupname' is NULL, searches in the global group. 639 */ 640 static sign_entry_T * 641 buf_getsign_at_line( 642 buf_T *buf, // buffer whose sign we are searching for 643 linenr_T lnum, // line number of sign 644 char_u *groupname) // sign group name 645 { 646 sign_entry_T *sign; // a sign in the signlist 647 648 FOR_ALL_SIGNS_IN_BUF(buf, sign) 649 { 650 if (sign->se_lnum > lnum) 651 // Signs are sorted by line number in the buffer. No need to check 652 // for signs after the specified line number 'lnum'. 653 break; 654 655 if (sign->se_lnum == lnum && sign_in_group(sign, groupname)) 656 return sign; 657 } 658 659 return NULL; 660 } 661 662 /* 663 * Return the identifier of the sign at line number 'lnum' in buffer 'buf'. 664 */ 665 int 666 buf_findsign_id( 667 buf_T *buf, // buffer whose sign we are searching for 668 linenr_T lnum, // line number of sign 669 char_u *groupname) // sign group name 670 { 671 sign_entry_T *sign; // a sign in the signlist 672 673 sign = buf_getsign_at_line(buf, lnum, groupname); 674 if (sign != NULL) 675 return sign->se_id; 676 677 return 0; 678 } 679 680 # if defined(FEAT_NETBEANS_INTG) || defined(PROTO) 681 /* 682 * See if a given type of sign exists on a specific line. 683 */ 684 int 685 buf_findsigntype_id( 686 buf_T *buf, // buffer whose sign we are searching for 687 linenr_T lnum, // line number of sign 688 int typenr) // sign type number 689 { 690 sign_entry_T *sign; // a sign in the signlist 691 692 FOR_ALL_SIGNS_IN_BUF(buf, sign) 693 { 694 if (sign->se_lnum > lnum) 695 // Signs are sorted by line number in the buffer. No need to check 696 // for signs after the specified line number 'lnum'. 697 break; 698 699 if (sign->se_lnum == lnum && sign->se_typenr == typenr) 700 return sign->se_id; 701 } 702 703 return 0; 704 } 705 706 707 # if defined(FEAT_SIGN_ICONS) || defined(PROTO) 708 /* 709 * Return the number of icons on the given line. 710 */ 711 int 712 buf_signcount(buf_T *buf, linenr_T lnum) 713 { 714 sign_entry_T *sign; // a sign in the signlist 715 int count = 0; 716 717 FOR_ALL_SIGNS_IN_BUF(buf, sign) 718 { 719 if (sign->se_lnum > lnum) 720 // Signs are sorted by line number in the buffer. No need to check 721 // for signs after the specified line number 'lnum'. 722 break; 723 724 if (sign->se_lnum == lnum) 725 if (sign_get_image(sign->se_typenr) != NULL) 726 count++; 727 } 728 729 return count; 730 } 731 # endif // FEAT_SIGN_ICONS 732 # endif // FEAT_NETBEANS_INTG 733 734 /* 735 * Delete signs in group 'group' in buffer "buf". If 'group' is '*', then 736 * delete all the signs. 737 */ 738 void 739 buf_delete_signs(buf_T *buf, char_u *group) 740 { 741 sign_entry_T *sign; 742 sign_entry_T **lastp; // pointer to pointer to current sign 743 sign_entry_T *next; 744 745 // When deleting the last sign need to redraw the windows to remove the 746 // sign column. Not when curwin is NULL (this means we're exiting). 747 if (buf->b_signlist != NULL && curwin != NULL) 748 { 749 redraw_buf_later(buf, NOT_VALID); 750 changed_line_abv_curs(); 751 } 752 753 lastp = &buf->b_signlist; 754 for (sign = buf->b_signlist; sign != NULL; sign = next) 755 { 756 next = sign->se_next; 757 if (sign_in_group(sign, group)) 758 { 759 *lastp = next; 760 if (next != NULL) 761 next->se_prev = sign->se_prev; 762 if (sign->se_group != NULL) 763 sign_group_unref(sign->se_group->sg_name); 764 vim_free(sign); 765 } 766 else 767 lastp = &sign->se_next; 768 } 769 } 770 771 /* 772 * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers. 773 */ 774 static void 775 sign_list_placed(buf_T *rbuf, char_u *sign_group) 776 { 777 buf_T *buf; 778 sign_entry_T *sign; 779 char lbuf[MSG_BUF_LEN]; 780 char group[MSG_BUF_LEN]; 781 782 msg_puts_title(_("\n--- Signs ---")); 783 msg_putchar('\n'); 784 if (rbuf == NULL) 785 buf = firstbuf; 786 else 787 buf = rbuf; 788 while (buf != NULL && !got_int) 789 { 790 if (buf->b_signlist != NULL) 791 { 792 vim_snprintf(lbuf, MSG_BUF_LEN, _("Signs for %s:"), buf->b_fname); 793 msg_puts_attr(lbuf, HL_ATTR(HLF_D)); 794 msg_putchar('\n'); 795 } 796 FOR_ALL_SIGNS_IN_BUF(buf, sign) 797 { 798 if (got_int) 799 break; 800 if (!sign_in_group(sign, sign_group)) 801 continue; 802 if (sign->se_group != NULL) 803 vim_snprintf(group, MSG_BUF_LEN, _(" group=%s"), 804 sign->se_group->sg_name); 805 else 806 group[0] = '\0'; 807 vim_snprintf(lbuf, MSG_BUF_LEN, 808 _(" line=%ld id=%d%s name=%s priority=%d"), 809 (long)sign->se_lnum, sign->se_id, group, 810 sign_typenr2name(sign->se_typenr), sign->se_priority); 811 msg_puts(lbuf); 812 msg_putchar('\n'); 813 } 814 if (rbuf != NULL) 815 break; 816 buf = buf->b_next; 817 } 818 } 819 820 /* 821 * Adjust a placed sign for inserted/deleted lines. 822 */ 823 void 824 sign_mark_adjust( 825 linenr_T line1, 826 linenr_T line2, 827 long amount, 828 long amount_after) 829 { 830 sign_entry_T *sign; // a sign in a b_signlist 831 linenr_T new_lnum; 832 833 FOR_ALL_SIGNS_IN_BUF(curbuf, sign) 834 { 835 // Ignore changes to lines after the sign 836 if (sign->se_lnum < line1) 837 continue; 838 new_lnum = sign->se_lnum; 839 if (sign->se_lnum >= line1 && sign->se_lnum <= line2) 840 { 841 if (amount != MAXLNUM) 842 new_lnum += amount; 843 } 844 else if (sign->se_lnum > line2) 845 // Lines inserted or deleted before the sign 846 new_lnum += amount_after; 847 848 // If the new sign line number is past the last line in the buffer, 849 // then don't adjust the line number. Otherwise, it will always be past 850 // the last line and will not be visible. 851 if (new_lnum <= curbuf->b_ml.ml_line_count) 852 sign->se_lnum = new_lnum; 853 } 854 } 855 856 /* 857 * Find index of a ":sign" subcmd from its name. 858 * "*end_cmd" must be writable. 859 */ 860 static int 861 sign_cmd_idx( 862 char_u *begin_cmd, // begin of sign subcmd 863 char_u *end_cmd) // just after sign subcmd 864 { 865 int idx; 866 char save = *end_cmd; 867 868 *end_cmd = NUL; 869 for (idx = 0; ; ++idx) 870 if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0) 871 break; 872 *end_cmd = save; 873 return idx; 874 } 875 876 /* 877 * Find a sign by name. Also returns pointer to the previous sign. 878 */ 879 static sign_T * 880 sign_find(char_u *name, sign_T **sp_prev) 881 { 882 sign_T *sp; 883 884 if (sp_prev != NULL) 885 *sp_prev = NULL; 886 FOR_ALL_SIGNS(sp) 887 { 888 if (STRCMP(sp->sn_name, name) == 0) 889 break; 890 if (sp_prev != NULL) 891 *sp_prev = sp; 892 } 893 894 return sp; 895 } 896 897 /* 898 * Allocate a new sign 899 */ 900 static sign_T * 901 alloc_new_sign(char_u *name) 902 { 903 sign_T *sp; 904 sign_T *lp; 905 int start = next_sign_typenr; 906 907 // Allocate a new sign. 908 sp = alloc_clear_id(sizeof(sign_T), aid_sign_define_by_name); 909 if (sp == NULL) 910 return NULL; 911 912 // Check that next_sign_typenr is not already being used. 913 // This only happens after wrapping around. Hopefully 914 // another one got deleted and we can use its number. 915 for (lp = first_sign; lp != NULL; ) 916 { 917 if (lp->sn_typenr == next_sign_typenr) 918 { 919 ++next_sign_typenr; 920 if (next_sign_typenr == MAX_TYPENR) 921 next_sign_typenr = 1; 922 if (next_sign_typenr == start) 923 { 924 vim_free(sp); 925 emsg(_("E612: Too many signs defined")); 926 return NULL; 927 } 928 lp = first_sign; // start all over 929 continue; 930 } 931 lp = lp->sn_next; 932 } 933 934 sp->sn_typenr = next_sign_typenr; 935 if (++next_sign_typenr == MAX_TYPENR) 936 next_sign_typenr = 1; // wrap around 937 938 sp->sn_name = vim_strsave(name); 939 if (sp->sn_name == NULL) // out of memory 940 { 941 vim_free(sp); 942 return NULL; 943 } 944 945 return sp; 946 } 947 948 /* 949 * Initialize the icon information for a new sign 950 */ 951 static void 952 sign_define_init_icon(sign_T *sp, char_u *icon) 953 { 954 vim_free(sp->sn_icon); 955 sp->sn_icon = vim_strsave(icon); 956 backslash_halve(sp->sn_icon); 957 # ifdef FEAT_SIGN_ICONS 958 if (gui.in_use) 959 { 960 out_flush(); 961 if (sp->sn_image != NULL) 962 gui_mch_destroy_sign(sp->sn_image); 963 sp->sn_image = gui_mch_register_sign(sp->sn_icon); 964 } 965 # endif 966 } 967 968 /* 969 * Initialize the text for a new sign 970 */ 971 static int 972 sign_define_init_text(sign_T *sp, char_u *text) 973 { 974 char_u *s; 975 char_u *endp; 976 int cells; 977 int len; 978 979 endp = text + (int)STRLEN(text); 980 981 // Remove backslashes so that it is possible to use a space. 982 for (s = text; s + 1 < endp; ++s) 983 if (*s == '\\') 984 { 985 STRMOVE(s, s + 1); 986 --endp; 987 } 988 989 // Count cells and check for non-printable chars 990 if (has_mbyte) 991 { 992 cells = 0; 993 for (s = text; s < endp; s += (*mb_ptr2len)(s)) 994 { 995 if (!vim_isprintc((*mb_ptr2char)(s))) 996 break; 997 cells += (*mb_ptr2cells)(s); 998 } 999 } 1000 else 1001 { 1002 for (s = text; s < endp; ++s) 1003 if (!vim_isprintc(*s)) 1004 break; 1005 cells = (int)(s - text); 1006 } 1007 1008 // Currently sign text must be one or two display cells 1009 if (s != endp || cells < 1 || cells > 2) 1010 { 1011 semsg(_("E239: Invalid sign text: %s"), text); 1012 return FAIL; 1013 } 1014 1015 vim_free(sp->sn_text); 1016 // Allocate one byte more if we need to pad up 1017 // with a space. 1018 len = (int)(endp - text + ((cells == 1) ? 1 : 0)); 1019 sp->sn_text = vim_strnsave(text, len); 1020 1021 // For single character sign text, pad with a space. 1022 if (sp->sn_text != NULL && cells == 1) 1023 STRCPY(sp->sn_text + len - 1, " "); 1024 1025 return OK; 1026 } 1027 1028 /* 1029 * Define a new sign or update an existing sign 1030 */ 1031 int 1032 sign_define_by_name( 1033 char_u *name, 1034 char_u *icon, 1035 char_u *linehl, 1036 char_u *text, 1037 char_u *texthl) 1038 { 1039 sign_T *sp_prev; 1040 sign_T *sp; 1041 1042 sp = sign_find(name, &sp_prev); 1043 if (sp == NULL) 1044 { 1045 sp = alloc_new_sign(name); 1046 if (sp == NULL) 1047 return FAIL; 1048 1049 // add the new sign to the list of signs 1050 if (sp_prev == NULL) 1051 first_sign = sp; 1052 else 1053 sp_prev->sn_next = sp; 1054 } 1055 else 1056 { 1057 win_T *wp; 1058 1059 // Signs may already exist, a redraw is needed in windows with a 1060 // non-empty sign list. 1061 FOR_ALL_WINDOWS(wp) 1062 if (wp->w_buffer->b_signlist != NULL) 1063 redraw_buf_later(wp->w_buffer, NOT_VALID); 1064 } 1065 1066 // set values for a defined sign. 1067 if (icon != NULL) 1068 sign_define_init_icon(sp, icon); 1069 1070 if (text != NULL && (sign_define_init_text(sp, text) == FAIL)) 1071 return FAIL; 1072 1073 if (linehl != NULL) 1074 sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl)); 1075 1076 if (texthl != NULL) 1077 sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl)); 1078 1079 return OK; 1080 } 1081 1082 /* 1083 * Return TRUE if sign "name" exists. 1084 */ 1085 int 1086 sign_exists_by_name(char_u *name) 1087 { 1088 return sign_find(name, NULL) != NULL; 1089 } 1090 1091 /* 1092 * Free the sign specified by 'name'. 1093 */ 1094 int 1095 sign_undefine_by_name(char_u *name, int give_error) 1096 { 1097 sign_T *sp_prev; 1098 sign_T *sp; 1099 1100 sp = sign_find(name, &sp_prev); 1101 if (sp == NULL) 1102 { 1103 if (give_error) 1104 semsg(_("E155: Unknown sign: %s"), name); 1105 return FAIL; 1106 } 1107 sign_undefine(sp, sp_prev); 1108 1109 return OK; 1110 } 1111 1112 /* 1113 * List the signs matching 'name' 1114 */ 1115 static void 1116 sign_list_by_name(char_u *name) 1117 { 1118 sign_T *sp; 1119 1120 sp = sign_find(name, NULL); 1121 if (sp != NULL) 1122 sign_list_defined(sp); 1123 else 1124 semsg(_("E155: Unknown sign: %s"), name); 1125 } 1126 1127 static void 1128 may_force_numberwidth_recompute(buf_T *buf, int unplace) 1129 { 1130 tabpage_T *tp; 1131 win_T *wp; 1132 1133 FOR_ALL_TAB_WINDOWS(tp, wp) 1134 if (wp->w_buffer == buf 1135 && (wp->w_p_nu || wp->w_p_rnu) 1136 && (unplace || wp->w_nrwidth_width < 2) 1137 && (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u')) 1138 wp->w_nrwidth_line_count = 0; 1139 } 1140 1141 /* 1142 * Place a sign at the specified file location or update a sign. 1143 */ 1144 int 1145 sign_place( 1146 int *sign_id, 1147 char_u *sign_group, 1148 char_u *sign_name, 1149 buf_T *buf, 1150 linenr_T lnum, 1151 int prio) 1152 { 1153 sign_T *sp; 1154 1155 // Check for reserved character '*' in group name 1156 if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0')) 1157 return FAIL; 1158 1159 FOR_ALL_SIGNS(sp) 1160 if (STRCMP(sp->sn_name, sign_name) == 0) 1161 break; 1162 if (sp == NULL) 1163 { 1164 semsg(_("E155: Unknown sign: %s"), sign_name); 1165 return FAIL; 1166 } 1167 if (*sign_id == 0) 1168 *sign_id = sign_group_get_next_signid(buf, sign_group); 1169 1170 if (lnum > 0) 1171 // ":sign place {id} line={lnum} name={name} file={fname}": 1172 // place a sign 1173 buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr); 1174 else 1175 // ":sign place {id} file={fname}": change sign type and/or priority 1176 lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr, 1177 prio); 1178 if (lnum > 0) 1179 { 1180 redraw_buf_line_later(buf, lnum); 1181 1182 // When displaying signs in the 'number' column, if the width of the 1183 // number column is less than 2, then force recomputing the width. 1184 may_force_numberwidth_recompute(buf, FALSE); 1185 } 1186 else 1187 { 1188 semsg(_("E885: Not possible to change sign %s"), sign_name); 1189 return FAIL; 1190 } 1191 1192 return OK; 1193 } 1194 1195 /* 1196 * Unplace the specified sign 1197 */ 1198 static int 1199 sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum) 1200 { 1201 if (buf->b_signlist == NULL) // No signs in the buffer 1202 return OK; 1203 1204 if (sign_id == 0) 1205 { 1206 // Delete all the signs in the specified buffer 1207 redraw_buf_later(buf, NOT_VALID); 1208 buf_delete_signs(buf, sign_group); 1209 } 1210 else 1211 { 1212 linenr_T lnum; 1213 1214 // Delete only the specified signs 1215 lnum = buf_delsign(buf, atlnum, sign_id, sign_group); 1216 if (lnum == 0) 1217 return FAIL; 1218 } 1219 1220 // When all the signs in a buffer are removed, force recomputing the 1221 // number column width (if enabled) in all the windows displaying the 1222 // buffer if 'signcolumn' is set to 'number' in that window. 1223 if (buf->b_signlist == NULL) 1224 may_force_numberwidth_recompute(buf, TRUE); 1225 1226 return OK; 1227 } 1228 1229 /* 1230 * Unplace the sign at the current cursor line. 1231 */ 1232 static void 1233 sign_unplace_at_cursor(char_u *groupname) 1234 { 1235 int id = -1; 1236 1237 id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname); 1238 if (id > 0) 1239 sign_unplace(id, groupname, curwin->w_buffer, curwin->w_cursor.lnum); 1240 else 1241 emsg(_("E159: Missing sign number")); 1242 } 1243 1244 /* 1245 * Jump to a sign. 1246 */ 1247 static linenr_T 1248 sign_jump(int sign_id, char_u *sign_group, buf_T *buf) 1249 { 1250 linenr_T lnum; 1251 1252 if ((lnum = buf_findsign(buf, sign_id, sign_group)) <= 0) 1253 { 1254 semsg(_("E157: Invalid sign ID: %d"), sign_id); 1255 return -1; 1256 } 1257 1258 // goto a sign ... 1259 if (buf_jump_open_win(buf) != NULL) 1260 { // ... in a current window 1261 curwin->w_cursor.lnum = lnum; 1262 check_cursor_lnum(); 1263 beginline(BL_WHITE); 1264 } 1265 else 1266 { // ... not currently in a window 1267 char_u *cmd; 1268 1269 if (buf->b_fname == NULL) 1270 { 1271 emsg(_("E934: Cannot jump to a buffer that does not have a name")); 1272 return -1; 1273 } 1274 cmd = alloc(STRLEN(buf->b_fname) + 25); 1275 if (cmd == NULL) 1276 return -1; 1277 sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname); 1278 do_cmdline_cmd(cmd); 1279 vim_free(cmd); 1280 } 1281 # ifdef FEAT_FOLDING 1282 foldOpenCursor(); 1283 # endif 1284 1285 return lnum; 1286 } 1287 1288 /* 1289 * ":sign define {name} ..." command 1290 */ 1291 static void 1292 sign_define_cmd(char_u *sign_name, char_u *cmdline) 1293 { 1294 char_u *arg; 1295 char_u *p = cmdline; 1296 char_u *icon = NULL; 1297 char_u *text = NULL; 1298 char_u *linehl = NULL; 1299 char_u *texthl = NULL; 1300 int failed = FALSE; 1301 1302 // set values for a defined sign. 1303 for (;;) 1304 { 1305 arg = skipwhite(p); 1306 if (*arg == NUL) 1307 break; 1308 p = skiptowhite_esc(arg); 1309 if (STRNCMP(arg, "icon=", 5) == 0) 1310 { 1311 arg += 5; 1312 icon = vim_strnsave(arg, (int)(p - arg)); 1313 } 1314 else if (STRNCMP(arg, "text=", 5) == 0) 1315 { 1316 arg += 5; 1317 text = vim_strnsave(arg, (int)(p - arg)); 1318 } 1319 else if (STRNCMP(arg, "linehl=", 7) == 0) 1320 { 1321 arg += 7; 1322 linehl = vim_strnsave(arg, (int)(p - arg)); 1323 } 1324 else if (STRNCMP(arg, "texthl=", 7) == 0) 1325 { 1326 arg += 7; 1327 texthl = vim_strnsave(arg, (int)(p - arg)); 1328 } 1329 else 1330 { 1331 semsg(_(e_invarg2), arg); 1332 failed = TRUE; 1333 break; 1334 } 1335 } 1336 1337 if (!failed) 1338 sign_define_by_name(sign_name, icon, linehl, text, texthl); 1339 1340 vim_free(icon); 1341 vim_free(text); 1342 vim_free(linehl); 1343 vim_free(texthl); 1344 } 1345 1346 /* 1347 * ":sign place" command 1348 */ 1349 static void 1350 sign_place_cmd( 1351 buf_T *buf, 1352 linenr_T lnum, 1353 char_u *sign_name, 1354 int id, 1355 char_u *group, 1356 int prio) 1357 { 1358 if (id <= 0) 1359 { 1360 // List signs placed in a file/buffer 1361 // :sign place file={fname} 1362 // :sign place group={group} file={fname} 1363 // :sign place group=* file={fname} 1364 // :sign place buffer={nr} 1365 // :sign place group={group} buffer={nr} 1366 // :sign place group=* buffer={nr} 1367 // :sign place 1368 // :sign place group={group} 1369 // :sign place group=* 1370 if (lnum >= 0 || sign_name != NULL 1371 || (group != NULL && *group == '\0')) 1372 emsg(_(e_invarg)); 1373 else 1374 sign_list_placed(buf, group); 1375 } 1376 else 1377 { 1378 // Place a new sign 1379 if (sign_name == NULL || buf == NULL 1380 || (group != NULL && *group == '\0')) 1381 { 1382 emsg(_(e_invarg)); 1383 return; 1384 } 1385 1386 sign_place(&id, group, sign_name, buf, lnum, prio); 1387 } 1388 } 1389 1390 /* 1391 * ":sign unplace" command 1392 */ 1393 static void 1394 sign_unplace_cmd( 1395 buf_T *buf, 1396 linenr_T lnum, 1397 char_u *sign_name, 1398 int id, 1399 char_u *group) 1400 { 1401 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0')) 1402 { 1403 emsg(_(e_invarg)); 1404 return; 1405 } 1406 1407 if (id == -2) 1408 { 1409 if (buf != NULL) 1410 // :sign unplace * file={fname} 1411 // :sign unplace * group={group} file={fname} 1412 // :sign unplace * group=* file={fname} 1413 // :sign unplace * buffer={nr} 1414 // :sign unplace * group={group} buffer={nr} 1415 // :sign unplace * group=* buffer={nr} 1416 sign_unplace(0, group, buf, 0); 1417 else 1418 // :sign unplace * 1419 // :sign unplace * group={group} 1420 // :sign unplace * group=* 1421 FOR_ALL_BUFFERS(buf) 1422 if (buf->b_signlist != NULL) 1423 buf_delete_signs(buf, group); 1424 } 1425 else 1426 { 1427 if (buf != NULL) 1428 // :sign unplace {id} file={fname} 1429 // :sign unplace {id} group={group} file={fname} 1430 // :sign unplace {id} group=* file={fname} 1431 // :sign unplace {id} buffer={nr} 1432 // :sign unplace {id} group={group} buffer={nr} 1433 // :sign unplace {id} group=* buffer={nr} 1434 sign_unplace(id, group, buf, 0); 1435 else 1436 { 1437 if (id == -1) 1438 { 1439 // :sign unplace group={group} 1440 // :sign unplace group=* 1441 sign_unplace_at_cursor(group); 1442 } 1443 else 1444 { 1445 // :sign unplace {id} 1446 // :sign unplace {id} group={group} 1447 // :sign unplace {id} group=* 1448 FOR_ALL_BUFFERS(buf) 1449 sign_unplace(id, group, buf, 0); 1450 } 1451 } 1452 } 1453 } 1454 1455 /* 1456 * Jump to a placed sign commands: 1457 * :sign jump {id} file={fname} 1458 * :sign jump {id} buffer={nr} 1459 * :sign jump {id} group={group} file={fname} 1460 * :sign jump {id} group={group} buffer={nr} 1461 */ 1462 static void 1463 sign_jump_cmd( 1464 buf_T *buf, 1465 linenr_T lnum, 1466 char_u *sign_name, 1467 int id, 1468 char_u *group) 1469 { 1470 if (sign_name == NULL && group == NULL && id == -1) 1471 { 1472 emsg(_(e_argreq)); 1473 return; 1474 } 1475 1476 if (buf == NULL || (group != NULL && *group == '\0') 1477 || lnum >= 0 || sign_name != NULL) 1478 { 1479 // File or buffer is not specified or an empty group is used 1480 // or a line number or a sign name is specified. 1481 emsg(_(e_invarg)); 1482 return; 1483 } 1484 (void)sign_jump(id, group, buf); 1485 } 1486 1487 /* 1488 * Parse the command line arguments for the ":sign place", ":sign unplace" and 1489 * ":sign jump" commands. 1490 * The supported arguments are: line={lnum} name={name} group={group} 1491 * priority={prio} and file={fname} or buffer={nr}. 1492 */ 1493 static int 1494 parse_sign_cmd_args( 1495 int cmd, 1496 char_u *arg, 1497 char_u **sign_name, 1498 int *signid, 1499 char_u **group, 1500 int *prio, 1501 buf_T **buf, 1502 linenr_T *lnum) 1503 { 1504 char_u *arg1; 1505 char_u *name; 1506 char_u *filename = NULL; 1507 int lnum_arg = FALSE; 1508 1509 // first arg could be placed sign id 1510 arg1 = arg; 1511 if (VIM_ISDIGIT(*arg)) 1512 { 1513 *signid = getdigits(&arg); 1514 if (!VIM_ISWHITE(*arg) && *arg != NUL) 1515 { 1516 *signid = -1; 1517 arg = arg1; 1518 } 1519 else 1520 arg = skipwhite(arg); 1521 } 1522 1523 while (*arg != NUL) 1524 { 1525 if (STRNCMP(arg, "line=", 5) == 0) 1526 { 1527 arg += 5; 1528 *lnum = atoi((char *)arg); 1529 arg = skiptowhite(arg); 1530 lnum_arg = TRUE; 1531 } 1532 else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE) 1533 { 1534 if (*signid != -1) 1535 { 1536 emsg(_(e_invarg)); 1537 return FAIL; 1538 } 1539 *signid = -2; 1540 arg = skiptowhite(arg + 1); 1541 } 1542 else if (STRNCMP(arg, "name=", 5) == 0) 1543 { 1544 arg += 5; 1545 name = arg; 1546 arg = skiptowhite(arg); 1547 if (*arg != NUL) 1548 *arg++ = NUL; 1549 while (name[0] == '0' && name[1] != NUL) 1550 ++name; 1551 *sign_name = name; 1552 } 1553 else if (STRNCMP(arg, "group=", 6) == 0) 1554 { 1555 arg += 6; 1556 *group = arg; 1557 arg = skiptowhite(arg); 1558 if (*arg != NUL) 1559 *arg++ = NUL; 1560 } 1561 else if (STRNCMP(arg, "priority=", 9) == 0) 1562 { 1563 arg += 9; 1564 *prio = atoi((char *)arg); 1565 arg = skiptowhite(arg); 1566 } 1567 else if (STRNCMP(arg, "file=", 5) == 0) 1568 { 1569 arg += 5; 1570 filename = arg; 1571 *buf = buflist_findname_exp(arg); 1572 break; 1573 } 1574 else if (STRNCMP(arg, "buffer=", 7) == 0) 1575 { 1576 arg += 7; 1577 filename = arg; 1578 *buf = buflist_findnr((int)getdigits(&arg)); 1579 if (*skipwhite(arg) != NUL) 1580 emsg(_(e_trailing)); 1581 break; 1582 } 1583 else 1584 { 1585 emsg(_(e_invarg)); 1586 return FAIL; 1587 } 1588 arg = skipwhite(arg); 1589 } 1590 1591 if (filename != NULL && *buf == NULL) 1592 { 1593 semsg(_("E158: Invalid buffer name: %s"), filename); 1594 return FAIL; 1595 } 1596 1597 // If the filename is not supplied for the sign place or the sign jump 1598 // command, then use the current buffer. 1599 if (filename == NULL && ((cmd == SIGNCMD_PLACE && lnum_arg) 1600 || cmd == SIGNCMD_JUMP)) 1601 *buf = curwin->w_buffer; 1602 1603 return OK; 1604 } 1605 1606 /* 1607 * ":sign" command 1608 */ 1609 void 1610 ex_sign(exarg_T *eap) 1611 { 1612 char_u *arg = eap->arg; 1613 char_u *p; 1614 int idx; 1615 sign_T *sp; 1616 buf_T *buf = NULL; 1617 1618 // Parse the subcommand. 1619 p = skiptowhite(arg); 1620 idx = sign_cmd_idx(arg, p); 1621 if (idx == SIGNCMD_LAST) 1622 { 1623 semsg(_("E160: Unknown sign command: %s"), arg); 1624 return; 1625 } 1626 arg = skipwhite(p); 1627 1628 if (idx <= SIGNCMD_LIST) 1629 { 1630 // Define, undefine or list signs. 1631 if (idx == SIGNCMD_LIST && *arg == NUL) 1632 { 1633 // ":sign list": list all defined signs 1634 for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next) 1635 sign_list_defined(sp); 1636 } 1637 else if (*arg == NUL) 1638 emsg(_("E156: Missing sign name")); 1639 else 1640 { 1641 char_u *name; 1642 1643 // Isolate the sign name. If it's a number skip leading zeroes, 1644 // so that "099" and "99" are the same sign. But keep "0". 1645 p = skiptowhite(arg); 1646 if (*p != NUL) 1647 *p++ = NUL; 1648 while (arg[0] == '0' && arg[1] != NUL) 1649 ++arg; 1650 name = vim_strsave(arg); 1651 1652 if (idx == SIGNCMD_DEFINE) 1653 sign_define_cmd(name, p); 1654 else if (idx == SIGNCMD_LIST) 1655 // ":sign list {name}" 1656 sign_list_by_name(name); 1657 else 1658 // ":sign undefine {name}" 1659 sign_undefine_by_name(name, TRUE); 1660 1661 vim_free(name); 1662 return; 1663 } 1664 } 1665 else 1666 { 1667 int id = -1; 1668 linenr_T lnum = -1; 1669 char_u *sign_name = NULL; 1670 char_u *group = NULL; 1671 int prio = SIGN_DEF_PRIO; 1672 1673 // Parse command line arguments 1674 if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio, 1675 &buf, &lnum) == FAIL) 1676 return; 1677 1678 if (idx == SIGNCMD_PLACE) 1679 sign_place_cmd(buf, lnum, sign_name, id, group, prio); 1680 else if (idx == SIGNCMD_UNPLACE) 1681 sign_unplace_cmd(buf, lnum, sign_name, id, group); 1682 else if (idx == SIGNCMD_JUMP) 1683 sign_jump_cmd(buf, lnum, sign_name, id, group); 1684 } 1685 } 1686 1687 /* 1688 * Return information about a specified sign 1689 */ 1690 static void 1691 sign_getinfo(sign_T *sp, dict_T *retdict) 1692 { 1693 char_u *p; 1694 1695 dict_add_string(retdict, "name", (char_u *)sp->sn_name); 1696 if (sp->sn_icon != NULL) 1697 dict_add_string(retdict, "icon", (char_u *)sp->sn_icon); 1698 if (sp->sn_text != NULL) 1699 dict_add_string(retdict, "text", (char_u *)sp->sn_text); 1700 if (sp->sn_line_hl > 0) 1701 { 1702 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE); 1703 if (p == NULL) 1704 p = (char_u *)"NONE"; 1705 dict_add_string(retdict, "linehl", (char_u *)p); 1706 } 1707 if (sp->sn_text_hl > 0) 1708 { 1709 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE); 1710 if (p == NULL) 1711 p = (char_u *)"NONE"; 1712 dict_add_string(retdict, "texthl", (char_u *)p); 1713 } 1714 } 1715 1716 /* 1717 * If 'name' is NULL, return a list of all the defined signs. 1718 * Otherwise, return information about the specified sign. 1719 */ 1720 static void 1721 sign_getlist(char_u *name, list_T *retlist) 1722 { 1723 sign_T *sp = first_sign; 1724 dict_T *dict; 1725 1726 if (name != NULL) 1727 { 1728 sp = sign_find(name, NULL); 1729 if (sp == NULL) 1730 return; 1731 } 1732 1733 for (; sp != NULL && !got_int; sp = sp->sn_next) 1734 { 1735 if ((dict = dict_alloc_id(aid_sign_getlist)) == NULL) 1736 return; 1737 if (list_append_dict(retlist, dict) == FAIL) 1738 return; 1739 sign_getinfo(sp, dict); 1740 1741 if (name != NULL) // handle only the specified sign 1742 break; 1743 } 1744 } 1745 1746 /* 1747 * Returns information about signs placed in a buffer as list of dicts. 1748 */ 1749 void 1750 get_buffer_signs(buf_T *buf, list_T *l) 1751 { 1752 sign_entry_T *sign; 1753 dict_T *d; 1754 1755 FOR_ALL_SIGNS_IN_BUF(buf, sign) 1756 { 1757 if ((d = sign_get_info(sign)) != NULL) 1758 list_append_dict(l, d); 1759 } 1760 } 1761 1762 /* 1763 * Return information about all the signs placed in a buffer 1764 */ 1765 static void 1766 sign_get_placed_in_buf( 1767 buf_T *buf, 1768 linenr_T lnum, 1769 int sign_id, 1770 char_u *sign_group, 1771 list_T *retlist) 1772 { 1773 dict_T *d; 1774 list_T *l; 1775 sign_entry_T *sign; 1776 dict_T *sdict; 1777 1778 if ((d = dict_alloc_id(aid_sign_getplaced_dict)) == NULL) 1779 return; 1780 list_append_dict(retlist, d); 1781 1782 dict_add_number(d, "bufnr", (long)buf->b_fnum); 1783 1784 if ((l = list_alloc_id(aid_sign_getplaced_list)) == NULL) 1785 return; 1786 dict_add_list(d, "signs", l); 1787 1788 FOR_ALL_SIGNS_IN_BUF(buf, sign) 1789 { 1790 if (!sign_in_group(sign, sign_group)) 1791 continue; 1792 if ((lnum == 0 && sign_id == 0) 1793 || (sign_id == 0 && lnum == sign->se_lnum) 1794 || (lnum == 0 && sign_id == sign->se_id) 1795 || (lnum == sign->se_lnum && sign_id == sign->se_id)) 1796 { 1797 if ((sdict = sign_get_info(sign)) != NULL) 1798 list_append_dict(l, sdict); 1799 } 1800 } 1801 } 1802 1803 /* 1804 * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the 1805 * sign placed at the line number. If 'lnum' is zero, return all the signs 1806 * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers. 1807 */ 1808 static void 1809 sign_get_placed( 1810 buf_T *buf, 1811 linenr_T lnum, 1812 int sign_id, 1813 char_u *sign_group, 1814 list_T *retlist) 1815 { 1816 if (buf != NULL) 1817 sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist); 1818 else 1819 { 1820 FOR_ALL_BUFFERS(buf) 1821 if (buf->b_signlist != NULL) 1822 sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist); 1823 } 1824 } 1825 1826 # if defined(FEAT_SIGN_ICONS) || defined(PROTO) 1827 /* 1828 * Allocate the icons. Called when the GUI has started. Allows defining 1829 * signs before it starts. 1830 */ 1831 void 1832 sign_gui_started(void) 1833 { 1834 sign_T *sp; 1835 1836 FOR_ALL_SIGNS(sp) 1837 if (sp->sn_icon != NULL) 1838 sp->sn_image = gui_mch_register_sign(sp->sn_icon); 1839 } 1840 # endif 1841 1842 /* 1843 * List one sign. 1844 */ 1845 static void 1846 sign_list_defined(sign_T *sp) 1847 { 1848 char_u *p; 1849 1850 smsg("sign %s", sp->sn_name); 1851 if (sp->sn_icon != NULL) 1852 { 1853 msg_puts(" icon="); 1854 msg_outtrans(sp->sn_icon); 1855 # ifdef FEAT_SIGN_ICONS 1856 if (sp->sn_image == NULL) 1857 msg_puts(_(" (NOT FOUND)")); 1858 # else 1859 msg_puts(_(" (not supported)")); 1860 # endif 1861 } 1862 if (sp->sn_text != NULL) 1863 { 1864 msg_puts(" text="); 1865 msg_outtrans(sp->sn_text); 1866 } 1867 if (sp->sn_line_hl > 0) 1868 { 1869 msg_puts(" linehl="); 1870 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE); 1871 if (p == NULL) 1872 msg_puts("NONE"); 1873 else 1874 msg_puts((char *)p); 1875 } 1876 if (sp->sn_text_hl > 0) 1877 { 1878 msg_puts(" texthl="); 1879 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE); 1880 if (p == NULL) 1881 msg_puts("NONE"); 1882 else 1883 msg_puts((char *)p); 1884 } 1885 } 1886 1887 /* 1888 * Undefine a sign and free its memory. 1889 */ 1890 static void 1891 sign_undefine(sign_T *sp, sign_T *sp_prev) 1892 { 1893 vim_free(sp->sn_name); 1894 vim_free(sp->sn_icon); 1895 # ifdef FEAT_SIGN_ICONS 1896 if (sp->sn_image != NULL) 1897 { 1898 out_flush(); 1899 gui_mch_destroy_sign(sp->sn_image); 1900 } 1901 # endif 1902 vim_free(sp->sn_text); 1903 if (sp_prev == NULL) 1904 first_sign = sp->sn_next; 1905 else 1906 sp_prev->sn_next = sp->sn_next; 1907 vim_free(sp); 1908 } 1909 1910 # if defined(FEAT_SIGN_ICONS) || defined(PROTO) 1911 void * 1912 sign_get_image( 1913 int typenr) // the attribute which may have a sign 1914 { 1915 sign_T *sp; 1916 1917 FOR_ALL_SIGNS(sp) 1918 if (sp->sn_typenr == typenr) 1919 return sp->sn_image; 1920 return NULL; 1921 } 1922 # endif 1923 1924 /* 1925 * Undefine/free all signs. 1926 */ 1927 void 1928 free_signs(void) 1929 { 1930 while (first_sign != NULL) 1931 sign_undefine(first_sign, NULL); 1932 } 1933 1934 static enum 1935 { 1936 EXP_SUBCMD, // expand :sign sub-commands 1937 EXP_DEFINE, // expand :sign define {name} args 1938 EXP_PLACE, // expand :sign place {id} args 1939 EXP_LIST, // expand :sign place args 1940 EXP_UNPLACE, // expand :sign unplace" 1941 EXP_SIGN_NAMES, // expand with name of placed signs 1942 EXP_SIGN_GROUPS // expand with name of placed sign groups 1943 } expand_what; 1944 1945 /* 1946 * Return the n'th sign name (used for command line completion) 1947 */ 1948 static char_u * 1949 get_nth_sign_name(int idx) 1950 { 1951 int current_idx; 1952 sign_T *sp; 1953 1954 // Complete with name of signs already defined 1955 current_idx = 0; 1956 FOR_ALL_SIGNS(sp) 1957 if (current_idx++ == idx) 1958 return sp->sn_name; 1959 return NULL; 1960 } 1961 1962 /* 1963 * Return the n'th sign group name (used for command line completion) 1964 */ 1965 static char_u * 1966 get_nth_sign_group_name(int idx) 1967 { 1968 int current_idx; 1969 int todo; 1970 hashitem_T *hi; 1971 signgroup_T *group; 1972 1973 // Complete with name of sign groups already defined 1974 current_idx = 0; 1975 todo = (int)sg_table.ht_used; 1976 for (hi = sg_table.ht_array; todo > 0; ++hi) 1977 { 1978 if (!HASHITEM_EMPTY(hi)) 1979 { 1980 --todo; 1981 if (current_idx++ == idx) 1982 { 1983 group = HI2SG(hi); 1984 return group->sg_name; 1985 } 1986 } 1987 } 1988 return NULL; 1989 } 1990 1991 /* 1992 * Function given to ExpandGeneric() to obtain the sign command 1993 * expansion. 1994 */ 1995 char_u * 1996 get_sign_name(expand_T *xp UNUSED, int idx) 1997 { 1998 switch (expand_what) 1999 { 2000 case EXP_SUBCMD: 2001 return (char_u *)cmds[idx]; 2002 case EXP_DEFINE: 2003 { 2004 char *define_arg[] = 2005 { 2006 "icon=", "linehl=", "text=", "texthl=", NULL 2007 }; 2008 return (char_u *)define_arg[idx]; 2009 } 2010 case EXP_PLACE: 2011 { 2012 char *place_arg[] = 2013 { 2014 "line=", "name=", "group=", "priority=", "file=", 2015 "buffer=", NULL 2016 }; 2017 return (char_u *)place_arg[idx]; 2018 } 2019 case EXP_LIST: 2020 { 2021 char *list_arg[] = 2022 { 2023 "group=", "file=", "buffer=", NULL 2024 }; 2025 return (char_u *)list_arg[idx]; 2026 } 2027 case EXP_UNPLACE: 2028 { 2029 char *unplace_arg[] = { "group=", "file=", "buffer=", NULL }; 2030 return (char_u *)unplace_arg[idx]; 2031 } 2032 case EXP_SIGN_NAMES: 2033 return get_nth_sign_name(idx); 2034 case EXP_SIGN_GROUPS: 2035 return get_nth_sign_group_name(idx); 2036 default: 2037 return NULL; 2038 } 2039 } 2040 2041 /* 2042 * Handle command line completion for :sign command. 2043 */ 2044 void 2045 set_context_in_sign_cmd(expand_T *xp, char_u *arg) 2046 { 2047 char_u *p; 2048 char_u *end_subcmd; 2049 char_u *last; 2050 int cmd_idx; 2051 char_u *begin_subcmd_args; 2052 2053 // Default: expand subcommands. 2054 xp->xp_context = EXPAND_SIGN; 2055 expand_what = EXP_SUBCMD; 2056 xp->xp_pattern = arg; 2057 2058 end_subcmd = skiptowhite(arg); 2059 if (*end_subcmd == NUL) 2060 // expand subcmd name 2061 // :sign {subcmd}<CTRL-D> 2062 return; 2063 2064 cmd_idx = sign_cmd_idx(arg, end_subcmd); 2065 2066 // :sign {subcmd} {subcmd_args} 2067 // | 2068 // begin_subcmd_args 2069 begin_subcmd_args = skipwhite(end_subcmd); 2070 2071 // expand last argument of subcmd 2072 2073 // :sign define {name} {args}... 2074 // | 2075 // p 2076 2077 // Loop until reaching last argument. 2078 p = begin_subcmd_args; 2079 do 2080 { 2081 p = skipwhite(p); 2082 last = p; 2083 p = skiptowhite(p); 2084 } while (*p != NUL); 2085 2086 p = vim_strchr(last, '='); 2087 2088 // :sign define {name} {args}... {last}= 2089 // | | 2090 // last p 2091 if (p == NULL) 2092 { 2093 // Expand last argument name (before equal sign). 2094 xp->xp_pattern = last; 2095 switch (cmd_idx) 2096 { 2097 case SIGNCMD_DEFINE: 2098 expand_what = EXP_DEFINE; 2099 break; 2100 case SIGNCMD_PLACE: 2101 // List placed signs 2102 if (VIM_ISDIGIT(*begin_subcmd_args)) 2103 // :sign place {id} {args}... 2104 expand_what = EXP_PLACE; 2105 else 2106 // :sign place {args}... 2107 expand_what = EXP_LIST; 2108 break; 2109 case SIGNCMD_LIST: 2110 case SIGNCMD_UNDEFINE: 2111 // :sign list <CTRL-D> 2112 // :sign undefine <CTRL-D> 2113 expand_what = EXP_SIGN_NAMES; 2114 break; 2115 case SIGNCMD_JUMP: 2116 case SIGNCMD_UNPLACE: 2117 expand_what = EXP_UNPLACE; 2118 break; 2119 default: 2120 xp->xp_context = EXPAND_NOTHING; 2121 } 2122 } 2123 else 2124 { 2125 // Expand last argument value (after equal sign). 2126 xp->xp_pattern = p + 1; 2127 switch (cmd_idx) 2128 { 2129 case SIGNCMD_DEFINE: 2130 if (STRNCMP(last, "texthl", 6) == 0 2131 || STRNCMP(last, "linehl", 6) == 0) 2132 xp->xp_context = EXPAND_HIGHLIGHT; 2133 else if (STRNCMP(last, "icon", 4) == 0) 2134 xp->xp_context = EXPAND_FILES; 2135 else 2136 xp->xp_context = EXPAND_NOTHING; 2137 break; 2138 case SIGNCMD_PLACE: 2139 if (STRNCMP(last, "name", 4) == 0) 2140 expand_what = EXP_SIGN_NAMES; 2141 else if (STRNCMP(last, "group", 5) == 0) 2142 expand_what = EXP_SIGN_GROUPS; 2143 else if (STRNCMP(last, "file", 4) == 0) 2144 xp->xp_context = EXPAND_BUFFERS; 2145 else 2146 xp->xp_context = EXPAND_NOTHING; 2147 break; 2148 case SIGNCMD_UNPLACE: 2149 case SIGNCMD_JUMP: 2150 if (STRNCMP(last, "group", 5) == 0) 2151 expand_what = EXP_SIGN_GROUPS; 2152 else if (STRNCMP(last, "file", 4) == 0) 2153 xp->xp_context = EXPAND_BUFFERS; 2154 else 2155 xp->xp_context = EXPAND_NOTHING; 2156 break; 2157 default: 2158 xp->xp_context = EXPAND_NOTHING; 2159 } 2160 } 2161 } 2162 2163 /* 2164 * Define a sign using the attributes in 'dict'. Returns 0 on success and -1 on 2165 * failure. 2166 */ 2167 static int 2168 sign_define_from_dict(char_u *name_arg, dict_T *dict) 2169 { 2170 char_u *name = NULL; 2171 char_u *icon = NULL; 2172 char_u *linehl = NULL; 2173 char_u *text = NULL; 2174 char_u *texthl = NULL; 2175 int retval = -1; 2176 2177 if (name_arg == NULL) 2178 { 2179 if (dict == NULL) 2180 return -1; 2181 name = dict_get_string(dict, (char_u *)"name", TRUE); 2182 } 2183 else 2184 name = vim_strsave(name_arg); 2185 if (name == NULL || name[0] == NUL) 2186 goto cleanup; 2187 if (dict != NULL) 2188 { 2189 icon = dict_get_string(dict, (char_u *)"icon", TRUE); 2190 linehl = dict_get_string(dict, (char_u *)"linehl", TRUE); 2191 text = dict_get_string(dict, (char_u *)"text", TRUE); 2192 texthl = dict_get_string(dict, (char_u *)"texthl", TRUE); 2193 } 2194 2195 if (sign_define_by_name(name, icon, linehl, text, texthl) == OK) 2196 retval = 0; 2197 2198 cleanup: 2199 vim_free(name); 2200 vim_free(icon); 2201 vim_free(linehl); 2202 vim_free(text); 2203 vim_free(texthl); 2204 2205 return retval; 2206 } 2207 2208 /* 2209 * Define multiple signs using attributes from list 'l' and store the return 2210 * values in 'retlist'. 2211 */ 2212 static void 2213 sign_define_multiple(list_T *l, list_T *retlist) 2214 { 2215 listitem_T *li; 2216 int retval; 2217 2218 FOR_ALL_LIST_ITEMS(l, li) 2219 { 2220 retval = -1; 2221 if (li->li_tv.v_type == VAR_DICT) 2222 retval = sign_define_from_dict(NULL, li->li_tv.vval.v_dict); 2223 else 2224 emsg(_(e_dictreq)); 2225 list_append_number(retlist, retval); 2226 } 2227 } 2228 2229 /* 2230 * "sign_define()" function 2231 */ 2232 void 2233 f_sign_define(typval_T *argvars, typval_T *rettv) 2234 { 2235 char_u *name; 2236 2237 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN) 2238 { 2239 // Define multiple signs 2240 if (rettv_list_alloc(rettv) != OK) 2241 return; 2242 2243 sign_define_multiple(argvars[0].vval.v_list, rettv->vval.v_list); 2244 return; 2245 } 2246 2247 // Define a single sign 2248 rettv->vval.v_number = -1; 2249 2250 name = tv_get_string_chk(&argvars[0]); 2251 if (name == NULL) 2252 return; 2253 2254 if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_DICT) 2255 { 2256 emsg(_(e_dictreq)); 2257 return; 2258 } 2259 2260 rettv->vval.v_number = sign_define_from_dict(name, 2261 argvars[1].v_type == VAR_DICT ? argvars[1].vval.v_dict : NULL); 2262 } 2263 2264 /* 2265 * "sign_getdefined()" function 2266 */ 2267 void 2268 f_sign_getdefined(typval_T *argvars, typval_T *rettv) 2269 { 2270 char_u *name = NULL; 2271 2272 if (rettv_list_alloc_id(rettv, aid_sign_getdefined) != OK) 2273 return; 2274 2275 if (argvars[0].v_type != VAR_UNKNOWN) 2276 name = tv_get_string(&argvars[0]); 2277 2278 sign_getlist(name, rettv->vval.v_list); 2279 } 2280 2281 /* 2282 * "sign_getplaced()" function 2283 */ 2284 void 2285 f_sign_getplaced(typval_T *argvars, typval_T *rettv) 2286 { 2287 buf_T *buf = NULL; 2288 dict_T *dict; 2289 dictitem_T *di; 2290 linenr_T lnum = 0; 2291 int sign_id = 0; 2292 char_u *group = NULL; 2293 int notanum = FALSE; 2294 2295 if (rettv_list_alloc_id(rettv, aid_sign_getplaced) != OK) 2296 return; 2297 2298 if (argvars[0].v_type != VAR_UNKNOWN) 2299 { 2300 // get signs placed in the specified buffer 2301 buf = get_buf_arg(&argvars[0]); 2302 if (buf == NULL) 2303 return; 2304 2305 if (argvars[1].v_type != VAR_UNKNOWN) 2306 { 2307 if (argvars[1].v_type != VAR_DICT || 2308 ((dict = argvars[1].vval.v_dict) == NULL)) 2309 { 2310 emsg(_(e_dictreq)); 2311 return; 2312 } 2313 if ((di = dict_find(dict, (char_u *)"lnum", -1)) != NULL) 2314 { 2315 // get signs placed at this line 2316 (void)tv_get_number_chk(&di->di_tv, ¬anum); 2317 if (notanum) 2318 return; 2319 lnum = tv_get_lnum(&di->di_tv); 2320 } 2321 if ((di = dict_find(dict, (char_u *)"id", -1)) != NULL) 2322 { 2323 // get sign placed with this identifier 2324 sign_id = (int)tv_get_number_chk(&di->di_tv, ¬anum); 2325 if (notanum) 2326 return; 2327 } 2328 if ((di = dict_find(dict, (char_u *)"group", -1)) != NULL) 2329 { 2330 group = tv_get_string_chk(&di->di_tv); 2331 if (group == NULL) 2332 return; 2333 if (*group == '\0') // empty string means global group 2334 group = NULL; 2335 } 2336 } 2337 } 2338 2339 sign_get_placed(buf, lnum, sign_id, group, rettv->vval.v_list); 2340 } 2341 2342 /* 2343 * "sign_jump()" function 2344 */ 2345 void 2346 f_sign_jump(typval_T *argvars, typval_T *rettv) 2347 { 2348 int sign_id; 2349 char_u *sign_group = NULL; 2350 buf_T *buf; 2351 int notanum = FALSE; 2352 2353 rettv->vval.v_number = -1; 2354 2355 // Sign identifier 2356 sign_id = (int)tv_get_number_chk(&argvars[0], ¬anum); 2357 if (notanum) 2358 return; 2359 if (sign_id <= 0) 2360 { 2361 emsg(_(e_invarg)); 2362 return; 2363 } 2364 2365 // Sign group 2366 sign_group = tv_get_string_chk(&argvars[1]); 2367 if (sign_group == NULL) 2368 return; 2369 if (sign_group[0] == '\0') 2370 sign_group = NULL; // global sign group 2371 else 2372 { 2373 sign_group = vim_strsave(sign_group); 2374 if (sign_group == NULL) 2375 return; 2376 } 2377 2378 // Buffer to place the sign 2379 buf = get_buf_arg(&argvars[2]); 2380 if (buf == NULL) 2381 goto cleanup; 2382 2383 rettv->vval.v_number = sign_jump(sign_id, sign_group, buf); 2384 2385 cleanup: 2386 vim_free(sign_group); 2387 } 2388 2389 /* 2390 * Place a new sign using the values specified in dict 'dict'. Returns the sign 2391 * identifier if successfully placed, otherwise returns 0. 2392 */ 2393 static int 2394 sign_place_from_dict( 2395 typval_T *id_tv, 2396 typval_T *group_tv, 2397 typval_T *name_tv, 2398 typval_T *buf_tv, 2399 dict_T *dict) 2400 { 2401 int sign_id = 0; 2402 char_u *group = NULL; 2403 char_u *sign_name = NULL; 2404 buf_T *buf = NULL; 2405 dictitem_T *di; 2406 linenr_T lnum = 0; 2407 int prio = SIGN_DEF_PRIO; 2408 int notanum = FALSE; 2409 int ret_sign_id = -1; 2410 2411 // sign identifier 2412 if (id_tv == NULL) 2413 { 2414 di = dict_find(dict, (char_u *)"id", -1); 2415 if (di != NULL) 2416 id_tv = &di->di_tv; 2417 } 2418 if (id_tv == NULL) 2419 sign_id = 0; 2420 else 2421 { 2422 sign_id = tv_get_number_chk(id_tv, ¬anum); 2423 if (notanum) 2424 return -1; 2425 if (sign_id < 0) 2426 { 2427 emsg(_(e_invarg)); 2428 return -1; 2429 } 2430 } 2431 2432 // sign group 2433 if (group_tv == NULL) 2434 { 2435 di = dict_find(dict, (char_u *)"group", -1); 2436 if (di != NULL) 2437 group_tv = &di->di_tv; 2438 } 2439 if (group_tv == NULL) 2440 group = NULL; // global group 2441 else 2442 { 2443 group = tv_get_string_chk(group_tv); 2444 if (group == NULL) 2445 goto cleanup; 2446 if (group[0] == '\0') // global sign group 2447 group = NULL; 2448 else 2449 { 2450 group = vim_strsave(group); 2451 if (group == NULL) 2452 return -1; 2453 } 2454 } 2455 2456 // sign name 2457 if (name_tv == NULL) 2458 { 2459 di = dict_find(dict, (char_u *)"name", -1); 2460 if (di != NULL) 2461 name_tv = &di->di_tv; 2462 } 2463 if (name_tv == NULL) 2464 goto cleanup; 2465 sign_name = tv_get_string_chk(name_tv); 2466 if (sign_name == NULL) 2467 goto cleanup; 2468 2469 // buffer to place the sign 2470 if (buf_tv == NULL) 2471 { 2472 di = dict_find(dict, (char_u *)"buffer", -1); 2473 if (di != NULL) 2474 buf_tv = &di->di_tv; 2475 } 2476 if (buf_tv == NULL) 2477 goto cleanup; 2478 buf = get_buf_arg(buf_tv); 2479 if (buf == NULL) 2480 goto cleanup; 2481 2482 // line number of the sign 2483 di = dict_find(dict, (char_u *)"lnum", -1); 2484 if (di != NULL) 2485 { 2486 lnum = tv_get_lnum(&di->di_tv); 2487 if (lnum <= 0) 2488 { 2489 emsg(_(e_invarg)); 2490 goto cleanup; 2491 } 2492 } 2493 2494 // sign priority 2495 di = dict_find(dict, (char_u *)"priority", -1); 2496 if (di != NULL) 2497 { 2498 prio = (int)tv_get_number_chk(&di->di_tv, ¬anum); 2499 if (notanum) 2500 goto cleanup; 2501 } 2502 2503 if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK) 2504 ret_sign_id = sign_id; 2505 2506 cleanup: 2507 vim_free(group); 2508 2509 return ret_sign_id; 2510 } 2511 2512 /* 2513 * "sign_place()" function 2514 */ 2515 void 2516 f_sign_place(typval_T *argvars, typval_T *rettv) 2517 { 2518 dict_T *dict = NULL; 2519 2520 rettv->vval.v_number = -1; 2521 2522 if (argvars[4].v_type != VAR_UNKNOWN 2523 && (argvars[4].v_type != VAR_DICT 2524 || ((dict = argvars[4].vval.v_dict) == NULL))) 2525 { 2526 emsg(_(e_dictreq)); 2527 return; 2528 } 2529 2530 rettv->vval.v_number = sign_place_from_dict(&argvars[0], &argvars[1], 2531 &argvars[2], &argvars[3], dict); 2532 } 2533 2534 /* 2535 * "sign_placelist()" function. Place multiple signs. 2536 */ 2537 void 2538 f_sign_placelist(typval_T *argvars, typval_T *rettv) 2539 { 2540 listitem_T *li; 2541 int sign_id; 2542 2543 if (rettv_list_alloc(rettv) != OK) 2544 return; 2545 2546 if (argvars[0].v_type != VAR_LIST) 2547 { 2548 emsg(_(e_listreq)); 2549 return; 2550 } 2551 2552 // Process the List of sign attributes 2553 FOR_ALL_LIST_ITEMS(argvars[0].vval.v_list, li) 2554 { 2555 sign_id = -1; 2556 if (li->li_tv.v_type == VAR_DICT) 2557 sign_id = sign_place_from_dict(NULL, NULL, NULL, NULL, 2558 li->li_tv.vval.v_dict); 2559 else 2560 emsg(_(e_dictreq)); 2561 list_append_number(rettv->vval.v_list, sign_id); 2562 } 2563 } 2564 2565 /* 2566 * Undefine multiple signs 2567 */ 2568 static void 2569 sign_undefine_multiple(list_T *l, list_T *retlist) 2570 { 2571 char_u *name; 2572 listitem_T *li; 2573 int retval; 2574 2575 FOR_ALL_LIST_ITEMS(l, li) 2576 { 2577 retval = -1; 2578 name = tv_get_string_chk(&li->li_tv); 2579 if (name != NULL && (sign_undefine_by_name(name, TRUE) == OK)) 2580 retval = 0; 2581 list_append_number(retlist, retval); 2582 } 2583 } 2584 2585 /* 2586 * "sign_undefine()" function 2587 */ 2588 void 2589 f_sign_undefine(typval_T *argvars, typval_T *rettv) 2590 { 2591 char_u *name; 2592 2593 if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN) 2594 { 2595 // Undefine multiple signs 2596 if (rettv_list_alloc(rettv) != OK) 2597 return; 2598 2599 sign_undefine_multiple(argvars[0].vval.v_list, rettv->vval.v_list); 2600 return; 2601 } 2602 2603 rettv->vval.v_number = -1; 2604 2605 if (argvars[0].v_type == VAR_UNKNOWN) 2606 { 2607 // Free all the signs 2608 free_signs(); 2609 rettv->vval.v_number = 0; 2610 } 2611 else 2612 { 2613 // Free only the specified sign 2614 name = tv_get_string_chk(&argvars[0]); 2615 if (name == NULL) 2616 return; 2617 2618 if (sign_undefine_by_name(name, TRUE) == OK) 2619 rettv->vval.v_number = 0; 2620 } 2621 } 2622 2623 /* 2624 * Unplace the sign with attributes specified in 'dict'. Returns 0 on success 2625 * and -1 on failure. 2626 */ 2627 static int 2628 sign_unplace_from_dict(typval_T *group_tv, dict_T *dict) 2629 { 2630 dictitem_T *di; 2631 int sign_id = 0; 2632 buf_T *buf = NULL; 2633 char_u *group = NULL; 2634 int retval = -1; 2635 2636 // sign group 2637 if (group_tv != NULL) 2638 group = tv_get_string(group_tv); 2639 else 2640 group = dict_get_string(dict, (char_u *)"group", FALSE); 2641 if (group != NULL) 2642 { 2643 if (group[0] == '\0') // global sign group 2644 group = NULL; 2645 else 2646 { 2647 group = vim_strsave(group); 2648 if (group == NULL) 2649 return -1; 2650 } 2651 } 2652 2653 if (dict != NULL) 2654 { 2655 if ((di = dict_find(dict, (char_u *)"buffer", -1)) != NULL) 2656 { 2657 buf = get_buf_arg(&di->di_tv); 2658 if (buf == NULL) 2659 goto cleanup; 2660 } 2661 if (dict_find(dict, (char_u *)"id", -1) != NULL) 2662 { 2663 sign_id = dict_get_number(dict, (char_u *)"id"); 2664 if (sign_id <= 0) 2665 { 2666 emsg(_(e_invarg)); 2667 goto cleanup; 2668 } 2669 } 2670 } 2671 2672 if (buf == NULL) 2673 { 2674 // Delete the sign in all the buffers 2675 retval = 0; 2676 FOR_ALL_BUFFERS(buf) 2677 if (sign_unplace(sign_id, group, buf, 0) != OK) 2678 retval = -1; 2679 } 2680 else if (sign_unplace(sign_id, group, buf, 0) == OK) 2681 retval = 0; 2682 2683 cleanup: 2684 vim_free(group); 2685 2686 return retval; 2687 } 2688 2689 sign_entry_T * 2690 get_first_valid_sign(win_T *wp) 2691 { 2692 sign_entry_T *sign = wp->w_buffer->b_signlist; 2693 2694 # ifdef FEAT_PROP_POPUP 2695 while (sign != NULL && !sign_group_for_window(sign, wp)) 2696 sign = sign->se_next; 2697 # endif 2698 return sign; 2699 } 2700 2701 /* 2702 * Return TRUE when window "wp" has a column to draw signs in. 2703 */ 2704 int 2705 signcolumn_on(win_T *wp) 2706 { 2707 // If 'signcolumn' is set to 'number', signs are displayed in the 'number' 2708 // column (if present). Otherwise signs are to be displayed in the sign 2709 // column. 2710 if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u') 2711 return get_first_valid_sign(wp) != NULL && !wp->w_p_nu && !wp->w_p_rnu; 2712 2713 if (*wp->w_p_scl == 'n') 2714 return FALSE; 2715 if (*wp->w_p_scl == 'y') 2716 return TRUE; 2717 return (get_first_valid_sign(wp) != NULL 2718 # ifdef FEAT_NETBEANS_INTG 2719 || wp->w_buffer->b_has_sign_column 2720 # endif 2721 ); 2722 } 2723 2724 /* 2725 * "sign_unplace()" function 2726 */ 2727 void 2728 f_sign_unplace(typval_T *argvars, typval_T *rettv) 2729 { 2730 dict_T *dict = NULL; 2731 2732 rettv->vval.v_number = -1; 2733 2734 if (argvars[0].v_type != VAR_STRING) 2735 { 2736 emsg(_(e_invarg)); 2737 return; 2738 } 2739 2740 if (argvars[1].v_type != VAR_UNKNOWN) 2741 { 2742 if (argvars[1].v_type != VAR_DICT) 2743 { 2744 emsg(_(e_dictreq)); 2745 return; 2746 } 2747 dict = argvars[1].vval.v_dict; 2748 } 2749 2750 rettv->vval.v_number = sign_unplace_from_dict(&argvars[0], dict); 2751 } 2752 2753 /* 2754 * "sign_unplacelist()" function 2755 */ 2756 void 2757 f_sign_unplacelist(typval_T *argvars, typval_T *rettv) 2758 { 2759 listitem_T *li; 2760 int retval; 2761 2762 if (rettv_list_alloc(rettv) != OK) 2763 return; 2764 2765 if (argvars[0].v_type != VAR_LIST) 2766 { 2767 emsg(_(e_listreq)); 2768 return; 2769 } 2770 2771 FOR_ALL_LIST_ITEMS(argvars[0].vval.v_list, li) 2772 { 2773 retval = -1; 2774 if (li->li_tv.v_type == VAR_DICT) 2775 retval = sign_unplace_from_dict(NULL, li->li_tv.vval.v_dict); 2776 else 2777 emsg(_(e_dictreq)); 2778 list_append_number(rettv->vval.v_list, retval); 2779 } 2780 } 2781 2782 #endif // FEAT_SIGNS 2783