1 /* vi:set ts=8 sts=4 sw=4 noet: 2 * 3 * VIM - Vi IMproved by Bram Moolenaar 4 * Netbeans integration by David Weatherford 5 * Adopted for Win32 by Sergey Khorev 6 * 7 * Do ":help uganda" in Vim to read copying and usage conditions. 8 * Do ":help credits" in Vim to see a list of people who contributed. 9 */ 10 11 /* 12 * Implements client side of org.netbeans.modules.emacs editor 13 * integration protocol. Be careful! The protocol uses offsets 14 * which are *between* characters, whereas vim uses line number 15 * and column number which are *on* characters. 16 * See ":help netbeans-protocol" for explanation. 17 * 18 * The Netbeans messages are received and queued in the gui event loop, or in 19 * the select loop when Vim runs in a terminal. These messages are processed 20 * by netbeans_parse_messages() which is invoked in the idle loop when Vim is 21 * waiting for user input. The function netbeans_parse_messages() is also 22 * called from the ":sleep" command, to allow the execution of test cases that 23 * may not invoke the idle loop. 24 */ 25 26 #include "vim.h" 27 28 #if defined(FEAT_NETBEANS_INTG) || defined(PROTO) 29 30 #ifndef MSWIN 31 # include <netdb.h> 32 # ifdef HAVE_LIBGEN_H 33 # include <libgen.h> 34 # endif 35 #endif 36 37 #include "version.h" 38 39 #define GUARDED 10000 /* typenr for "guarded" annotation */ 40 #define GUARDEDOFFSET 1000000 /* base for "guarded" sign id's */ 41 #define MAX_COLOR_LENGTH 32 /* max length of color name in defineAnnoType */ 42 43 /* The first implementation (working only with Netbeans) returned "1.1". The 44 * protocol implemented here also supports A-A-P. */ 45 static char *ExtEdProtocolVersion = "2.5"; 46 47 static long pos2off(buf_T *, pos_T *); 48 static pos_T *off2pos(buf_T *, long); 49 static pos_T *get_off_or_lnum(buf_T *buf, char_u **argp); 50 static long get_buf_size(buf_T *); 51 static int netbeans_keystring(char_u *keystr); 52 static void special_keys(char_u *args); 53 54 static int getConnInfo(char *file, char **host, char **port, char **password); 55 56 static void nb_init_graphics(void); 57 static void coloncmd(char *cmd, ...); 58 static void nb_set_curbuf(buf_T *buf); 59 static void nb_parse_cmd(char_u *); 60 static int nb_do_cmd(int, char_u *, int, int, char_u *); 61 static void nb_send(char *buf, char *fun); 62 static void nb_free(void); 63 64 #define NETBEANS_OPEN (channel_can_write_to(nb_channel)) 65 static channel_T *nb_channel = NULL; 66 67 static int r_cmdno; /* current command number for reply */ 68 static int dosetvisible = FALSE; 69 70 /* 71 * Include the debugging code if wanted. 72 */ 73 #ifdef NBDEBUG 74 # include "nbdebug.c" 75 #endif 76 77 static int needupdate = 0; 78 static int inAtomic = 0; 79 80 /* 81 * Callback invoked when the channel is closed. 82 */ 83 static void 84 nb_channel_closed(void) 85 { 86 nb_channel = NULL; 87 } 88 89 /* 90 * Close the connection and cleanup. 91 * May be called when the socket was closed earlier. 92 */ 93 static void 94 netbeans_close(void) 95 { 96 if (NETBEANS_OPEN) 97 { 98 netbeans_send_disconnect(); 99 if (nb_channel != NULL) 100 { 101 /* Close the socket and remove the input handlers. */ 102 channel_close(nb_channel, TRUE); 103 channel_clear(nb_channel); 104 } 105 nb_channel = NULL; 106 } 107 108 #ifdef FEAT_BEVAL_GUI 109 bevalServers &= ~BEVAL_NETBEANS; 110 #endif 111 112 needupdate = 0; 113 inAtomic = 0; 114 nb_free(); 115 116 /* remove all signs and update the screen after gutter removal */ 117 coloncmd(":sign unplace *"); 118 changed_window_setting(); 119 update_screen(CLEAR); 120 setcursor(); 121 cursor_on(); 122 out_flush_cursor(TRUE, FALSE); 123 } 124 125 #define NB_DEF_HOST "localhost" 126 #define NB_DEF_ADDR "3219" 127 #define NB_DEF_PASS "changeme" 128 129 static int 130 netbeans_connect(char *params, int doabort) 131 { 132 int port; 133 char buf[32]; 134 char *hostname = NULL; 135 char *address = NULL; 136 char *password = NULL; 137 char *fname; 138 char *arg = NULL; 139 140 if (*params == '=') 141 { 142 /* "=fname": Read info from specified file. */ 143 if (getConnInfo(params + 1, &hostname, &address, &password) == FAIL) 144 return FAIL; 145 } 146 else 147 { 148 if (*params == ':') 149 /* ":<host>:<addr>:<password>": get info from argument */ 150 arg = params + 1; 151 if (arg == NULL && (fname = getenv("__NETBEANS_CONINFO")) != NULL) 152 { 153 /* "": get info from file specified in environment */ 154 if (getConnInfo(fname, &hostname, &address, &password) == FAIL) 155 return FAIL; 156 } 157 else 158 { 159 if (arg != NULL) 160 { 161 /* ":<host>:<addr>:<password>": get info from argument */ 162 hostname = arg; 163 address = strchr(hostname, ':'); 164 if (address != NULL) 165 { 166 *address++ = '\0'; 167 password = strchr(address, ':'); 168 if (password != NULL) 169 *password++ = '\0'; 170 } 171 } 172 173 /* Get the missing values from the environment. */ 174 if (hostname == NULL || *hostname == '\0') 175 hostname = getenv("__NETBEANS_HOST"); 176 if (address == NULL) 177 address = getenv("__NETBEANS_SOCKET"); 178 if (password == NULL) 179 password = getenv("__NETBEANS_VIM_PASSWORD"); 180 181 /* Move values to allocated memory. */ 182 if (hostname != NULL) 183 hostname = (char *)vim_strsave((char_u *)hostname); 184 if (address != NULL) 185 address = (char *)vim_strsave((char_u *)address); 186 if (password != NULL) 187 password = (char *)vim_strsave((char_u *)password); 188 } 189 } 190 191 /* Use the default when a value is missing. */ 192 if (hostname == NULL || *hostname == '\0') 193 { 194 vim_free(hostname); 195 hostname = (char *)vim_strsave((char_u *)NB_DEF_HOST); 196 } 197 if (address == NULL || *address == '\0') 198 { 199 vim_free(address); 200 address = (char *)vim_strsave((char_u *)NB_DEF_ADDR); 201 } 202 if (password == NULL || *password == '\0') 203 { 204 vim_free(password); 205 password = (char *)vim_strsave((char_u *)NB_DEF_PASS); 206 } 207 if (hostname != NULL && address != NULL && password != NULL) 208 { 209 port = atoi(address); 210 nb_channel = channel_open(hostname, port, 3000, nb_channel_closed); 211 if (nb_channel != NULL) 212 { 213 /* success */ 214 # ifdef FEAT_BEVAL_GUI 215 bevalServers |= BEVAL_NETBEANS; 216 # endif 217 218 /* success, login */ 219 vim_snprintf(buf, sizeof(buf), "AUTH %s\n", password); 220 nb_send(buf, "netbeans_connect"); 221 222 sprintf(buf, "0:version=0 \"%s\"\n", ExtEdProtocolVersion); 223 nb_send(buf, "externaleditor_version"); 224 } 225 } 226 227 if (nb_channel == NULL && doabort) 228 getout(1); 229 230 vim_free(hostname); 231 vim_free(address); 232 vim_free(password); 233 return NETBEANS_OPEN ? OK : FAIL; 234 } 235 236 /* 237 * Obtain the NetBeans hostname, port address and password from a file. 238 * Return the strings in allocated memory. 239 * Return FAIL if the file could not be read, OK otherwise (no matter what it 240 * contains). 241 */ 242 static int 243 getConnInfo(char *file, char **host, char **port, char **auth) 244 { 245 FILE *fp; 246 char_u buf[BUFSIZ]; 247 char_u *lp; 248 char_u *nlp; 249 #ifdef UNIX 250 stat_T st; 251 252 /* 253 * For Unix only accept the file when it's not accessible by others. 254 * The open will then fail if we don't own the file. 255 */ 256 if (mch_stat(file, &st) == 0 && (st.st_mode & 0077) != 0) 257 { 258 nbdebug(("Wrong access mode for NetBeans connection info file: \"%s\"\n", 259 file)); 260 semsg(_("E668: Wrong access mode for NetBeans connection info file: \"%s\""), 261 file); 262 return FAIL; 263 } 264 #endif 265 266 fp = mch_fopen(file, "r"); 267 if (fp == NULL) 268 { 269 nbdebug(("Cannot open NetBeans connection info file\n")); 270 PERROR("E660: Cannot open NetBeans connection info file"); 271 return FAIL; 272 } 273 274 /* Read the file. There should be one of each parameter */ 275 while ((lp = (char_u *)fgets((char *)buf, BUFSIZ, fp)) != NULL) 276 { 277 if ((nlp = vim_strchr(lp, '\n')) != NULL) 278 *nlp = 0; /* strip off the trailing newline */ 279 280 if (STRNCMP(lp, "host=", 5) == 0) 281 { 282 vim_free(*host); 283 *host = (char *)vim_strsave(&buf[5]); 284 } 285 else if (STRNCMP(lp, "port=", 5) == 0) 286 { 287 vim_free(*port); 288 *port = (char *)vim_strsave(&buf[5]); 289 } 290 else if (STRNCMP(lp, "auth=", 5) == 0) 291 { 292 vim_free(*auth); 293 *auth = (char *)vim_strsave(&buf[5]); 294 } 295 } 296 fclose(fp); 297 298 return OK; 299 } 300 301 302 struct keyqueue 303 { 304 char_u *keystr; 305 struct keyqueue *next; 306 struct keyqueue *prev; 307 }; 308 309 typedef struct keyqueue keyQ_T; 310 311 static keyQ_T keyHead; /* dummy node, header for circular queue */ 312 313 314 /* 315 * Queue up key commands sent from netbeans. 316 * We store the string, because it may depend on the global mod_mask and 317 * :nbkey doesn't have a key number. 318 */ 319 static void 320 postpone_keycommand(char_u *keystr) 321 { 322 keyQ_T *node; 323 324 node = ALLOC_ONE(keyQ_T); 325 if (node == NULL) 326 return; /* out of memory, drop the key */ 327 328 if (keyHead.next == NULL) /* initialize circular queue */ 329 { 330 keyHead.next = &keyHead; 331 keyHead.prev = &keyHead; 332 } 333 334 /* insert node at tail of queue */ 335 node->next = &keyHead; 336 node->prev = keyHead.prev; 337 keyHead.prev->next = node; 338 keyHead.prev = node; 339 340 node->keystr = vim_strsave(keystr); 341 } 342 343 /* 344 * Handle any queued-up NetBeans keycommands to be send. 345 */ 346 static void 347 handle_key_queue(void) 348 { 349 int postponed = FALSE; 350 351 while (!postponed && keyHead.next && keyHead.next != &keyHead) 352 { 353 /* first, unlink the node */ 354 keyQ_T *node = keyHead.next; 355 keyHead.next = node->next; 356 node->next->prev = node->prev; 357 358 /* Now, send the keycommand. This may cause it to be postponed again 359 * and change keyHead. */ 360 if (node->keystr != NULL) 361 postponed = !netbeans_keystring(node->keystr); 362 vim_free(node->keystr); 363 364 /* Finally, dispose of the node */ 365 vim_free(node); 366 } 367 } 368 369 370 /* 371 * While there's still a command in the work queue, parse and execute it. 372 */ 373 void 374 netbeans_parse_messages(void) 375 { 376 readq_T *node; 377 char_u *buffer; 378 char_u *p; 379 int own_node; 380 381 while (nb_channel != NULL) 382 { 383 node = channel_peek(nb_channel, PART_SOCK); 384 if (node == NULL) 385 break; /* nothing to read */ 386 387 /* Locate the end of the first line in the first buffer. */ 388 p = channel_first_nl(node); 389 if (p == NULL) 390 { 391 /* Command isn't complete. If there is no following buffer, 392 * return (wait for more). If there is another buffer following, 393 * prepend the text to that buffer and delete this one. */ 394 if (channel_collapse(nb_channel, PART_SOCK, TRUE) == FAIL) 395 return; 396 continue; 397 } 398 399 /* There is a complete command at the start of the buffer. 400 * Terminate it with a NUL. When no more text is following unlink 401 * the buffer. Do this before executing, because new buffers can 402 * be added while busy handling the command. */ 403 *p++ = NUL; 404 if (*p == NUL) 405 { 406 own_node = TRUE; 407 buffer = channel_get(nb_channel, PART_SOCK, NULL); 408 /* "node" is now invalid! */ 409 } 410 else 411 { 412 own_node = FALSE; 413 buffer = node->rq_buffer; 414 } 415 416 /* Now, parse and execute the commands. This may set nb_channel to 417 * NULL if the channel is closed. */ 418 nb_parse_cmd(buffer); 419 420 if (own_node) 421 /* buffer finished, dispose of it */ 422 vim_free(buffer); 423 else if (nb_channel != NULL) 424 /* more follows, move it to the start */ 425 channel_consume(nb_channel, PART_SOCK, (int)(p - buffer)); 426 } 427 } 428 429 /* 430 * Handle one NUL terminated command. 431 * 432 * format of a command from netbeans: 433 * 434 * 6:setTitle!84 "a.c" 435 * 436 * bufno 437 * colon 438 * cmd 439 * ! 440 * cmdno 441 * args 442 * 443 * for function calls, the ! is replaced by a / 444 */ 445 static void 446 nb_parse_cmd(char_u *cmd) 447 { 448 char *verb; 449 char *q; 450 int bufno; 451 int isfunc = -1; 452 453 if (STRCMP(cmd, "DISCONNECT") == 0) 454 { 455 /* We assume the server knows that we can safely exit! */ 456 /* Disconnect before exiting, Motif hangs in a Select error 457 * message otherwise. */ 458 netbeans_close(); 459 getout(0); 460 /* NOTREACHED */ 461 } 462 463 if (STRCMP(cmd, "DETACH") == 0) 464 { 465 buf_T *buf; 466 467 FOR_ALL_BUFFERS(buf) 468 buf->b_has_sign_column = FALSE; 469 470 /* The IDE is breaking the connection. */ 471 netbeans_close(); 472 return; 473 } 474 475 bufno = strtol((char *)cmd, &verb, 10); 476 477 if (*verb != ':') 478 { 479 nbdebug((" missing colon: %s\n", cmd)); 480 semsg("E627: missing colon: %s", cmd); 481 return; 482 } 483 ++verb; /* skip colon */ 484 485 for (q = verb; *q; q++) 486 { 487 if (*q == '!') 488 { 489 *q++ = NUL; 490 isfunc = 0; 491 break; 492 } 493 else if (*q == '/') 494 { 495 *q++ = NUL; 496 isfunc = 1; 497 break; 498 } 499 } 500 501 if (isfunc < 0) 502 { 503 nbdebug((" missing ! or / in: %s\n", cmd)); 504 semsg("E628: missing ! or / in: %s", cmd); 505 return; 506 } 507 508 r_cmdno = strtol(q, &q, 10); 509 510 q = (char *)skipwhite((char_u *)q); 511 512 if (nb_do_cmd(bufno, (char_u *)verb, isfunc, r_cmdno, (char_u *)q) == FAIL) 513 { 514 #ifdef NBDEBUG 515 /* 516 * This happens because the ExtEd can send a command or 2 after 517 * doing a stopDocumentListen command. It doesn't harm anything 518 * so I'm disabling it except for debugging. 519 */ 520 nbdebug(("nb_parse_cmd: Command error for \"%s\"\n", cmd)); 521 emsg("E629: bad return from nb_do_cmd"); 522 #endif 523 } 524 } 525 526 struct nbbuf_struct 527 { 528 buf_T *bufp; 529 unsigned int fireChanges:1; 530 unsigned int initDone:1; 531 unsigned int insertDone:1; 532 unsigned int modified:1; 533 int nbbuf_number; 534 char *displayname; 535 int *signmap; 536 short_u signmaplen; 537 short_u signmapused; 538 }; 539 540 typedef struct nbbuf_struct nbbuf_T; 541 542 static nbbuf_T *buf_list = NULL; 543 static int buf_list_size = 0; /* size of buf_list */ 544 static int buf_list_used = 0; /* nr of entries in buf_list actually in use */ 545 546 static char **globalsignmap = NULL; 547 static int globalsignmaplen = 0; 548 static int globalsignmapused = 0; 549 550 static int mapsigntype(nbbuf_T *, int localsigntype); 551 static void addsigntype(nbbuf_T *, int localsigntype, char_u *typeName, 552 char_u *tooltip, char_u *glyphfile, 553 char_u *fg, char_u *bg); 554 static void print_read_msg(nbbuf_T *buf); 555 static void print_save_msg(nbbuf_T *buf, off_T nchars); 556 557 static int curPCtype = -1; 558 559 /* 560 * Free netbeans resources. 561 */ 562 static void 563 nb_free(void) 564 { 565 keyQ_T *key_node = keyHead.next; 566 nbbuf_T buf; 567 int i; 568 569 /* free the netbeans buffer list */ 570 for (i = 0; i < buf_list_used; i++) 571 { 572 buf = buf_list[i]; 573 vim_free(buf.displayname); 574 vim_free(buf.signmap); 575 if (buf.bufp != NULL) 576 { 577 buf.bufp->b_netbeans_file = FALSE; 578 buf.bufp->b_was_netbeans_file = FALSE; 579 } 580 } 581 VIM_CLEAR(buf_list); 582 buf_list_size = 0; 583 buf_list_used = 0; 584 585 /* free the queued key commands */ 586 while (key_node != NULL && key_node != &keyHead) 587 { 588 keyQ_T *next = key_node->next; 589 vim_free(key_node->keystr); 590 vim_free(key_node); 591 if (next == &keyHead) 592 { 593 keyHead.next = &keyHead; 594 keyHead.prev = &keyHead; 595 break; 596 } 597 key_node = next; 598 } 599 600 /* free the queued netbeans commands */ 601 if (nb_channel != NULL) 602 channel_clear(nb_channel); 603 } 604 605 /* 606 * Get the Netbeans buffer number for the specified buffer. 607 */ 608 static int 609 nb_getbufno(buf_T *bufp) 610 { 611 int i; 612 613 for (i = 0; i < buf_list_used; i++) 614 if (buf_list[i].bufp == bufp) 615 return i; 616 return -1; 617 } 618 619 /* 620 * Is this a NetBeans-owned buffer? 621 */ 622 int 623 isNetbeansBuffer(buf_T *bufp) 624 { 625 return NETBEANS_OPEN && bufp->b_netbeans_file; 626 } 627 628 /* 629 * NetBeans and Vim have different undo models. In Vim, the file isn't 630 * changed if changes are undone via the undo command. In NetBeans, once 631 * a change has been made the file is marked as modified until saved. It 632 * doesn't matter if the change was undone. 633 * 634 * So this function is for the corner case where Vim thinks a buffer is 635 * unmodified but NetBeans thinks it IS modified. 636 */ 637 int 638 isNetbeansModified(buf_T *bufp) 639 { 640 if (isNetbeansBuffer(bufp)) 641 { 642 int bufno = nb_getbufno(bufp); 643 644 if (bufno > 0) 645 return buf_list[bufno].modified; 646 else 647 return FALSE; 648 } 649 else 650 return FALSE; 651 } 652 653 /* 654 * Given a Netbeans buffer number, return the netbeans buffer. 655 * Returns NULL for 0 or a negative number. A 0 bufno means a 656 * non-buffer related command has been sent. 657 */ 658 static nbbuf_T * 659 nb_get_buf(int bufno) 660 { 661 /* find or create a buffer with the given number */ 662 int incr; 663 664 if (bufno <= 0) 665 return NULL; 666 667 if (!buf_list) 668 { 669 /* initialize */ 670 buf_list = alloc_clear(100 * sizeof(nbbuf_T)); 671 buf_list_size = 100; 672 } 673 if (bufno >= buf_list_used) /* new */ 674 { 675 if (bufno >= buf_list_size) /* grow list */ 676 { 677 nbbuf_T *t_buf_list = buf_list; 678 679 incr = bufno - buf_list_size + 90; 680 buf_list_size += incr; 681 buf_list = vim_realloc(buf_list, buf_list_size * sizeof(nbbuf_T)); 682 if (buf_list == NULL) 683 { 684 vim_free(t_buf_list); 685 buf_list_size = 0; 686 return NULL; 687 } 688 vim_memset(buf_list + buf_list_size - incr, 0, 689 incr * sizeof(nbbuf_T)); 690 } 691 692 while (buf_list_used <= bufno) 693 { 694 /* Default is to fire text changes. */ 695 buf_list[buf_list_used].fireChanges = 1; 696 ++buf_list_used; 697 } 698 } 699 700 return buf_list + bufno; 701 } 702 703 /* 704 * Return the number of buffers that are modified. 705 */ 706 static int 707 count_changed_buffers(void) 708 { 709 buf_T *bufp; 710 int n; 711 712 n = 0; 713 FOR_ALL_BUFFERS(bufp) 714 if (bufp->b_changed) 715 ++n; 716 return n; 717 } 718 719 /* 720 * End the netbeans session. 721 */ 722 void 723 netbeans_end(void) 724 { 725 int i; 726 static char buf[128]; 727 728 if (!NETBEANS_OPEN) 729 return; 730 731 for (i = 0; i < buf_list_used; i++) 732 { 733 if (!buf_list[i].bufp) 734 continue; 735 if (netbeansForcedQuit) 736 { 737 /* mark as unmodified so NetBeans won't put up dialog on "killed" */ 738 sprintf(buf, "%d:unmodified=%d\n", i, r_cmdno); 739 nbdebug(("EVT: %s", buf)); 740 nb_send(buf, "netbeans_end"); 741 } 742 sprintf(buf, "%d:killed=%d\n", i, r_cmdno); 743 nbdebug(("EVT: %s", buf)); 744 /* nb_send(buf, "netbeans_end"); avoid "write failed" messages */ 745 nb_send(buf, NULL); 746 } 747 } 748 749 /* 750 * Send a message to netbeans. 751 * When "fun" is NULL no error is given. 752 */ 753 static void 754 nb_send(char *buf, char *fun) 755 { 756 if (nb_channel != NULL) 757 channel_send(nb_channel, PART_SOCK, (char_u *)buf, 758 (int)STRLEN(buf), fun); 759 } 760 761 /* 762 * Some input received from netbeans requires a response. This function 763 * handles a response with no information (except the command number). 764 */ 765 static void 766 nb_reply_nil(int cmdno) 767 { 768 char reply[32]; 769 770 nbdebug(("REP %d: <none>\n", cmdno)); 771 772 /* Avoid printing an annoying error message. */ 773 if (!NETBEANS_OPEN) 774 return; 775 776 sprintf(reply, "%d\n", cmdno); 777 nb_send(reply, "nb_reply_nil"); 778 } 779 780 781 /* 782 * Send a response with text. 783 * "result" must have been quoted already (using nb_quote()). 784 */ 785 static void 786 nb_reply_text(int cmdno, char_u *result) 787 { 788 char_u *reply; 789 790 nbdebug(("REP %d: %s\n", cmdno, (char *)result)); 791 792 reply = alloc(STRLEN(result) + 32); 793 sprintf((char *)reply, "%d %s\n", cmdno, (char *)result); 794 nb_send((char *)reply, "nb_reply_text"); 795 796 vim_free(reply); 797 } 798 799 800 /* 801 * Send a response with a number result code. 802 */ 803 static void 804 nb_reply_nr(int cmdno, long result) 805 { 806 char reply[32]; 807 808 nbdebug(("REP %d: %ld\n", cmdno, result)); 809 810 sprintf(reply, "%d %ld\n", cmdno, result); 811 nb_send(reply, "nb_reply_nr"); 812 } 813 814 815 /* 816 * Encode newline, ret, backslash, double quote for transmission to NetBeans. 817 */ 818 static char_u * 819 nb_quote(char_u *txt) 820 { 821 char_u *buf = alloc(2 * STRLEN(txt) + 1); 822 char_u *p = txt; 823 char_u *q = buf; 824 825 if (buf == NULL) 826 return NULL; 827 for (; *p; p++) 828 { 829 switch (*p) 830 { 831 case '\"': 832 case '\\': 833 *q++ = '\\'; *q++ = *p; break; 834 /* case '\t': */ 835 /* *q++ = '\\'; *q++ = 't'; break; */ 836 case '\n': 837 *q++ = '\\'; *q++ = 'n'; break; 838 case '\r': 839 *q++ = '\\'; *q++ = 'r'; break; 840 default: 841 *q++ = *p; 842 break; 843 } 844 } 845 *q = '\0'; 846 847 return buf; 848 } 849 850 851 /* 852 * Remove top level double quotes; convert backslashed chars. 853 * Returns an allocated string (NULL for failure). 854 * If "endp" is not NULL it is set to the character after the terminating 855 * quote. 856 */ 857 static char * 858 nb_unquote(char_u *p, char_u **endp) 859 { 860 char *result = 0; 861 char *q; 862 int done = 0; 863 864 /* result is never longer than input */ 865 result = alloc_clear(STRLEN(p) + 1); 866 if (result == NULL) 867 return NULL; 868 869 if (*p++ != '"') 870 { 871 nbdebug(("nb_unquote called with string that doesn't start with a quote!: %s\n", 872 p)); 873 result[0] = NUL; 874 return result; 875 } 876 877 for (q = result; !done && *p != NUL;) 878 { 879 switch (*p) 880 { 881 case '"': 882 /* 883 * Unbackslashed dquote marks the end, if first char was dquote. 884 */ 885 done = 1; 886 break; 887 888 case '\\': 889 ++p; 890 switch (*p) 891 { 892 case '\\': *q++ = '\\'; break; 893 case 'n': *q++ = '\n'; break; 894 case 't': *q++ = '\t'; break; 895 case 'r': *q++ = '\r'; break; 896 case '"': *q++ = '"'; break; 897 case NUL: --p; break; 898 /* default: skip over illegal chars */ 899 } 900 ++p; 901 break; 902 903 default: 904 *q++ = *p++; 905 } 906 } 907 908 if (endp != NULL) 909 *endp = p; 910 911 return result; 912 } 913 914 /* 915 * Remove from "first" byte to "last" byte (inclusive), at line "lnum" of the 916 * current buffer. Remove to end of line when "last" is MAXCOL. 917 */ 918 static void 919 nb_partialremove(linenr_T lnum, colnr_T first, colnr_T last) 920 { 921 char_u *oldtext, *newtext; 922 int oldlen; 923 int lastbyte = last; 924 925 oldtext = ml_get(lnum); 926 oldlen = (int)STRLEN(oldtext); 927 if (first >= (colnr_T)oldlen || oldlen == 0) /* just in case */ 928 return; 929 if (lastbyte >= oldlen) 930 lastbyte = oldlen - 1; 931 newtext = alloc(oldlen - (int)(lastbyte - first)); 932 if (newtext != NULL) 933 { 934 mch_memmove(newtext, oldtext, first); 935 STRMOVE(newtext + first, oldtext + lastbyte + 1); 936 nbdebug((" NEW LINE %ld: %s\n", lnum, newtext)); 937 ml_replace(lnum, newtext, FALSE); 938 } 939 } 940 941 /* 942 * Replace the "first" line with the concatenation of the "first" and 943 * the "other" line. The "other" line is not removed. 944 */ 945 static void 946 nb_joinlines(linenr_T first, linenr_T other) 947 { 948 int len_first, len_other; 949 char_u *p; 950 951 len_first = (int)STRLEN(ml_get(first)); 952 len_other = (int)STRLEN(ml_get(other)); 953 p = alloc(len_first + len_other + 1); 954 if (p != NULL) 955 { 956 mch_memmove(p, ml_get(first), len_first); 957 mch_memmove(p + len_first, ml_get(other), len_other + 1); 958 ml_replace(first, p, FALSE); 959 } 960 } 961 962 #define SKIP_STOP 2 963 #define streq(a,b) (strcmp(a,b) == 0) 964 965 /* 966 * Do the actual processing of a single netbeans command or function. 967 * The difference between a command and function is that a function 968 * gets a response (it's required) but a command does not. 969 * For arguments see comment for nb_parse_cmd(). 970 */ 971 static int 972 nb_do_cmd( 973 int bufno, 974 char_u *cmd, 975 int func, 976 int cmdno, 977 char_u *args) /* points to space before arguments or NUL */ 978 { 979 int do_update = 0; 980 long off = 0; 981 nbbuf_T *buf = nb_get_buf(bufno); 982 static int skip = 0; 983 int retval = OK; 984 char *cp; /* for when a char pointer is needed */ 985 986 nbdebug(("%s %d: (%d) %s %s\n", (func) ? "FUN" : "CMD", cmdno, bufno, cmd, 987 STRCMP(cmd, "insert") == 0 ? "<text>" : (char *)args)); 988 989 if (func) 990 { 991 /* =====================================================================*/ 992 if (streq((char *)cmd, "getModified")) 993 { 994 if (buf == NULL || buf->bufp == NULL) 995 /* Return the number of buffers that are modified. */ 996 nb_reply_nr(cmdno, (long)count_changed_buffers()); 997 else 998 /* Return whether the buffer is modified. */ 999 nb_reply_nr(cmdno, (long)(buf->bufp->b_changed 1000 || isNetbeansModified(buf->bufp))); 1001 /* =====================================================================*/ 1002 } 1003 else if (streq((char *)cmd, "saveAndExit")) 1004 { 1005 /* Note: this will exit Vim if successful. */ 1006 coloncmd(":confirm qall"); 1007 1008 /* We didn't exit: return the number of changed buffers. */ 1009 nb_reply_nr(cmdno, (long)count_changed_buffers()); 1010 /* =====================================================================*/ 1011 } 1012 else if (streq((char *)cmd, "getCursor")) 1013 { 1014 char_u text[200]; 1015 1016 /* Note: nb_getbufno() may return -1. This indicates the IDE 1017 * didn't assign a number to the current buffer in response to a 1018 * fileOpened event. */ 1019 sprintf((char *)text, "%d %ld %d %ld", 1020 nb_getbufno(curbuf), 1021 (long)curwin->w_cursor.lnum, 1022 (int)curwin->w_cursor.col, 1023 pos2off(curbuf, &curwin->w_cursor)); 1024 nb_reply_text(cmdno, text); 1025 /* =====================================================================*/ 1026 } 1027 else if (streq((char *)cmd, "getAnno")) 1028 { 1029 long linenum = 0; 1030 #ifdef FEAT_SIGNS 1031 if (buf == NULL || buf->bufp == NULL) 1032 { 1033 nbdebug((" Invalid buffer identifier in getAnno\n")); 1034 emsg("E652: Invalid buffer identifier in getAnno"); 1035 retval = FAIL; 1036 } 1037 else 1038 { 1039 int serNum; 1040 1041 cp = (char *)args; 1042 serNum = strtol(cp, &cp, 10); 1043 /* If the sign isn't found linenum will be zero. */ 1044 linenum = (long)buf_findsign(buf->bufp, serNum, NULL); 1045 } 1046 #endif 1047 nb_reply_nr(cmdno, linenum); 1048 /* =====================================================================*/ 1049 } 1050 else if (streq((char *)cmd, "getLength")) 1051 { 1052 long len = 0; 1053 1054 if (buf == NULL || buf->bufp == NULL) 1055 { 1056 nbdebug((" invalid buffer identifier in getLength\n")); 1057 emsg("E632: invalid buffer identifier in getLength"); 1058 retval = FAIL; 1059 } 1060 else 1061 { 1062 len = get_buf_size(buf->bufp); 1063 } 1064 nb_reply_nr(cmdno, len); 1065 /* =====================================================================*/ 1066 } 1067 else if (streq((char *)cmd, "getText")) 1068 { 1069 long len; 1070 linenr_T nlines; 1071 char_u *text = NULL; 1072 linenr_T lno = 1; 1073 char_u *p; 1074 char_u *line; 1075 1076 if (buf == NULL || buf->bufp == NULL) 1077 { 1078 nbdebug((" invalid buffer identifier in getText\n")); 1079 emsg("E633: invalid buffer identifier in getText"); 1080 retval = FAIL; 1081 } 1082 else 1083 { 1084 len = get_buf_size(buf->bufp); 1085 nlines = buf->bufp->b_ml.ml_line_count; 1086 text = alloc((len > 0) ? ((len + nlines) * 2) : 4); 1087 if (text == NULL) 1088 { 1089 nbdebug((" nb_do_cmd: getText has null text field\n")); 1090 retval = FAIL; 1091 } 1092 else 1093 { 1094 p = text; 1095 *p++ = '\"'; 1096 for (; lno <= nlines ; lno++) 1097 { 1098 line = nb_quote(ml_get_buf(buf->bufp, lno, FALSE)); 1099 if (line != NULL) 1100 { 1101 STRCPY(p, line); 1102 p += STRLEN(line); 1103 *p++ = '\\'; 1104 *p++ = 'n'; 1105 vim_free(line); 1106 } 1107 } 1108 *p++ = '\"'; 1109 *p = '\0'; 1110 } 1111 } 1112 if (text == NULL) 1113 nb_reply_text(cmdno, (char_u *)""); 1114 else 1115 { 1116 nb_reply_text(cmdno, text); 1117 vim_free(text); 1118 } 1119 /* =====================================================================*/ 1120 } 1121 else if (streq((char *)cmd, "remove")) 1122 { 1123 long count; 1124 pos_T first, last; 1125 pos_T *pos; 1126 pos_T *next; 1127 linenr_T del_from_lnum, del_to_lnum; /* lines to be deleted as a whole */ 1128 int oldFire = netbeansFireChanges; 1129 int oldSuppress = netbeansSuppressNoLines; 1130 int wasChanged; 1131 1132 if (skip >= SKIP_STOP) 1133 { 1134 nbdebug((" Skipping %s command\n", (char *) cmd)); 1135 nb_reply_nil(cmdno); 1136 return OK; 1137 } 1138 1139 if (buf == NULL || buf->bufp == NULL) 1140 { 1141 nbdebug((" invalid buffer identifier in remove\n")); 1142 emsg("E634: invalid buffer identifier in remove"); 1143 retval = FAIL; 1144 } 1145 else 1146 { 1147 netbeansFireChanges = FALSE; 1148 netbeansSuppressNoLines = TRUE; 1149 1150 nb_set_curbuf(buf->bufp); 1151 wasChanged = buf->bufp->b_changed; 1152 cp = (char *)args; 1153 off = strtol(cp, &cp, 10); 1154 count = strtol(cp, &cp, 10); 1155 args = (char_u *)cp; 1156 /* delete "count" chars, starting at "off" */ 1157 pos = off2pos(buf->bufp, off); 1158 if (!pos) 1159 { 1160 nbdebug((" !bad position\n")); 1161 nb_reply_text(cmdno, (char_u *)"!bad position"); 1162 netbeansFireChanges = oldFire; 1163 netbeansSuppressNoLines = oldSuppress; 1164 return FAIL; 1165 } 1166 first = *pos; 1167 nbdebug((" FIRST POS: line %ld, col %d\n", 1168 first.lnum, first.col)); 1169 pos = off2pos(buf->bufp, off+count-1); 1170 if (!pos) 1171 { 1172 nbdebug((" !bad count\n")); 1173 nb_reply_text(cmdno, (char_u *)"!bad count"); 1174 netbeansFireChanges = oldFire; 1175 netbeansSuppressNoLines = oldSuppress; 1176 return FAIL; 1177 } 1178 last = *pos; 1179 nbdebug((" LAST POS: line %ld, col %d\n", 1180 last.lnum, last.col)); 1181 del_from_lnum = first.lnum; 1182 del_to_lnum = last.lnum; 1183 do_update = 1; 1184 1185 /* Get the position of the first byte after the deleted 1186 * section. "next" is NULL when deleting to the end of the 1187 * file. */ 1188 next = off2pos(buf->bufp, off + count); 1189 1190 /* Remove part of the first line. */ 1191 if (first.col != 0 1192 || (next != NULL && first.lnum == next->lnum)) 1193 { 1194 if (first.lnum != last.lnum 1195 || (next != NULL && first.lnum != next->lnum)) 1196 { 1197 /* remove to the end of the first line */ 1198 nb_partialremove(first.lnum, first.col, 1199 (colnr_T)MAXCOL); 1200 if (first.lnum == last.lnum) 1201 { 1202 /* Partial line to remove includes the end of 1203 * line. Join the line with the next one, have 1204 * the next line deleted below. */ 1205 nb_joinlines(first.lnum, next->lnum); 1206 del_to_lnum = next->lnum; 1207 } 1208 } 1209 else 1210 { 1211 /* remove within one line */ 1212 nb_partialremove(first.lnum, first.col, last.col); 1213 } 1214 ++del_from_lnum; /* don't delete the first line */ 1215 } 1216 1217 /* Remove part of the last line. */ 1218 if (first.lnum != last.lnum && next != NULL 1219 && next->col != 0 && last.lnum == next->lnum) 1220 { 1221 nb_partialremove(last.lnum, 0, last.col); 1222 if (del_from_lnum > first.lnum) 1223 { 1224 /* Join end of last line to start of first line; last 1225 * line is deleted below. */ 1226 nb_joinlines(first.lnum, last.lnum); 1227 } 1228 else 1229 /* First line is deleted as a whole, keep the last 1230 * line. */ 1231 --del_to_lnum; 1232 } 1233 1234 /* First is partial line; last line to remove includes 1235 * the end of line; join first line to line following last 1236 * line; line following last line is deleted below. */ 1237 if (first.lnum != last.lnum && del_from_lnum > first.lnum 1238 && next != NULL && last.lnum != next->lnum) 1239 { 1240 nb_joinlines(first.lnum, next->lnum); 1241 del_to_lnum = next->lnum; 1242 } 1243 1244 /* Delete whole lines if there are any. */ 1245 if (del_to_lnum >= del_from_lnum) 1246 { 1247 int i; 1248 1249 /* delete signs from the lines being deleted */ 1250 for (i = del_from_lnum; i <= del_to_lnum; i++) 1251 { 1252 int id = buf_findsign_id(buf->bufp, (linenr_T)i, NULL); 1253 if (id > 0) 1254 { 1255 nbdebug((" Deleting sign %d on line %d\n", 1256 id, i)); 1257 buf_delsign(buf->bufp, 0, id, NULL); 1258 } 1259 else 1260 { 1261 nbdebug((" No sign on line %d\n", i)); 1262 } 1263 } 1264 1265 nbdebug((" Deleting lines %ld through %ld\n", 1266 del_from_lnum, del_to_lnum)); 1267 curwin->w_cursor.lnum = del_from_lnum; 1268 curwin->w_cursor.col = 0; 1269 del_lines(del_to_lnum - del_from_lnum + 1, FALSE); 1270 } 1271 1272 /* Leave cursor at first deleted byte. */ 1273 curwin->w_cursor = first; 1274 check_cursor_lnum(); 1275 buf->bufp->b_changed = wasChanged; /* logically unchanged */ 1276 netbeansFireChanges = oldFire; 1277 netbeansSuppressNoLines = oldSuppress; 1278 1279 u_blockfree(buf->bufp); 1280 u_clearall(buf->bufp); 1281 } 1282 nb_reply_nil(cmdno); 1283 /* =====================================================================*/ 1284 } 1285 else if (streq((char *)cmd, "insert")) 1286 { 1287 char_u *to_free; 1288 1289 if (skip >= SKIP_STOP) 1290 { 1291 nbdebug((" Skipping %s command\n", (char *) cmd)); 1292 nb_reply_nil(cmdno); 1293 return OK; 1294 } 1295 1296 /* get offset */ 1297 cp = (char *)args; 1298 off = strtol(cp, &cp, 10); 1299 args = (char_u *)cp; 1300 1301 /* get text to be inserted */ 1302 args = skipwhite(args); 1303 args = to_free = (char_u *)nb_unquote(args, NULL); 1304 /* 1305 nbdebug((" CHUNK[%d]: %d bytes at offset %d\n", 1306 buf->bufp->b_ml.ml_line_count, STRLEN(args), off)); 1307 */ 1308 1309 if (buf == NULL || buf->bufp == NULL) 1310 { 1311 nbdebug((" invalid buffer identifier in insert\n")); 1312 emsg("E635: invalid buffer identifier in insert"); 1313 retval = FAIL; 1314 } 1315 else if (args != NULL) 1316 { 1317 int ff_detected = EOL_UNKNOWN; 1318 int buf_was_empty = (buf->bufp->b_ml.ml_flags & ML_EMPTY); 1319 size_t len = 0; 1320 int added = 0; 1321 int oldFire = netbeansFireChanges; 1322 int old_b_changed; 1323 char_u *nlp; 1324 linenr_T lnum; 1325 linenr_T lnum_start; 1326 pos_T *pos; 1327 1328 netbeansFireChanges = 0; 1329 1330 /* Jump to the buffer where we insert. After this "curbuf" 1331 * can be used. */ 1332 nb_set_curbuf(buf->bufp); 1333 old_b_changed = curbuf->b_changed; 1334 1335 /* Convert the specified character offset into a lnum/col 1336 * position. */ 1337 pos = off2pos(curbuf, off); 1338 if (pos != NULL) 1339 { 1340 if (pos->lnum <= 0) 1341 lnum_start = 1; 1342 else 1343 lnum_start = pos->lnum; 1344 } 1345 else 1346 { 1347 /* If the given position is not found, assume we want 1348 * the end of the file. See setLocAndSize HACK. */ 1349 if (buf_was_empty) 1350 lnum_start = 1; /* above empty line */ 1351 else 1352 lnum_start = curbuf->b_ml.ml_line_count + 1; 1353 } 1354 1355 /* "lnum" is the line where we insert: either append to it or 1356 * insert a new line above it. */ 1357 lnum = lnum_start; 1358 1359 /* Loop over the "\n" separated lines of the argument. */ 1360 do_update = 1; 1361 while (*args != NUL) 1362 { 1363 nlp = vim_strchr(args, '\n'); 1364 if (nlp == NULL) 1365 { 1366 /* Incomplete line, probably truncated. Next "insert" 1367 * command should append to this one. */ 1368 len = STRLEN(args); 1369 } 1370 else 1371 { 1372 len = nlp - args; 1373 1374 /* 1375 * We need to detect EOL style, because the commands 1376 * use a character offset. 1377 */ 1378 if (nlp > args && nlp[-1] == '\r') 1379 { 1380 ff_detected = EOL_DOS; 1381 --len; 1382 } 1383 else 1384 ff_detected = EOL_UNIX; 1385 } 1386 args[len] = NUL; 1387 1388 if (lnum == lnum_start 1389 && ((pos != NULL && pos->col > 0) 1390 || (lnum == 1 && buf_was_empty))) 1391 { 1392 char_u *oldline = ml_get(lnum); 1393 char_u *newline; 1394 1395 /* Insert halfway a line. */ 1396 newline = alloc(STRLEN(oldline) + len + 1); 1397 if (newline != NULL) 1398 { 1399 mch_memmove(newline, oldline, (size_t)pos->col); 1400 newline[pos->col] = NUL; 1401 STRCAT(newline, args); 1402 STRCAT(newline, oldline + pos->col); 1403 ml_replace(lnum, newline, FALSE); 1404 } 1405 } 1406 else 1407 { 1408 /* Append a new line. Not that we always do this, 1409 * also when the text doesn't end in a "\n". */ 1410 ml_append((linenr_T)(lnum - 1), args, 1411 (colnr_T)(len + 1), FALSE); 1412 ++added; 1413 } 1414 1415 if (nlp == NULL) 1416 break; 1417 ++lnum; 1418 args = nlp + 1; 1419 } 1420 1421 /* Adjust the marks below the inserted lines. */ 1422 appended_lines_mark(lnum_start - 1, (long)added); 1423 1424 /* 1425 * When starting with an empty buffer set the fileformat. 1426 * This is just guessing... 1427 */ 1428 if (buf_was_empty) 1429 { 1430 if (ff_detected == EOL_UNKNOWN) 1431 #if defined(MSWIN) 1432 ff_detected = EOL_DOS; 1433 #else 1434 ff_detected = EOL_UNIX; 1435 #endif 1436 set_fileformat(ff_detected, OPT_LOCAL); 1437 curbuf->b_start_ffc = *curbuf->b_p_ff; 1438 } 1439 1440 /* 1441 * XXX - GRP - Is the next line right? If I've inserted 1442 * text the buffer has been updated but not written. Will 1443 * netbeans guarantee to write it? Even if I do a :q! ? 1444 */ 1445 curbuf->b_changed = old_b_changed; /* logically unchanged */ 1446 netbeansFireChanges = oldFire; 1447 1448 /* Undo info is invalid now... */ 1449 u_blockfree(curbuf); 1450 u_clearall(curbuf); 1451 } 1452 vim_free(to_free); 1453 nb_reply_nil(cmdno); /* or !error */ 1454 } 1455 else 1456 { 1457 nbdebug(("UNIMPLEMENTED FUNCTION: %s\n", cmd)); 1458 nb_reply_nil(cmdno); 1459 retval = FAIL; 1460 } 1461 } 1462 else /* Not a function; no reply required. */ 1463 { 1464 /* =====================================================================*/ 1465 if (streq((char *)cmd, "create")) 1466 { 1467 /* Create a buffer without a name. */ 1468 if (buf == NULL) 1469 { 1470 nbdebug((" invalid buffer identifier in create\n")); 1471 emsg("E636: invalid buffer identifier in create"); 1472 return FAIL; 1473 } 1474 VIM_CLEAR(buf->displayname); 1475 1476 netbeansReadFile = 0; /* don't try to open disk file */ 1477 do_ecmd(0, NULL, 0, 0, ECMD_ONE, ECMD_HIDE + ECMD_OLDBUF, curwin); 1478 netbeansReadFile = 1; 1479 buf->bufp = curbuf; 1480 maketitle(); 1481 buf->insertDone = FALSE; 1482 #if defined(FEAT_MENU) && defined(FEAT_GUI) 1483 if (gui.in_use) 1484 gui_update_menus(0); 1485 #endif 1486 /* =====================================================================*/ 1487 } 1488 else if (streq((char *)cmd, "insertDone")) 1489 { 1490 if (buf == NULL || buf->bufp == NULL) 1491 { 1492 nbdebug((" invalid buffer identifier in insertDone\n")); 1493 } 1494 else 1495 { 1496 buf->bufp->b_start_eol = *args == 'T'; 1497 buf->insertDone = TRUE; 1498 args += 2; 1499 buf->bufp->b_p_ro = *args == 'T'; 1500 print_read_msg(buf); 1501 } 1502 /* =====================================================================*/ 1503 } 1504 else if (streq((char *)cmd, "saveDone")) 1505 { 1506 long savedChars = atol((char *)args); 1507 1508 if (buf == NULL || buf->bufp == NULL) 1509 nbdebug((" invalid buffer identifier in saveDone\n")); 1510 else 1511 print_save_msg(buf, savedChars); 1512 /* =====================================================================*/ 1513 } 1514 else if (streq((char *)cmd, "startDocumentListen")) 1515 { 1516 if (buf == NULL) 1517 { 1518 nbdebug((" invalid buffer identifier in startDocumentListen\n")); 1519 emsg("E637: invalid buffer identifier in startDocumentListen"); 1520 return FAIL; 1521 } 1522 buf->fireChanges = 1; 1523 /* =====================================================================*/ 1524 } 1525 else if (streq((char *)cmd, "stopDocumentListen")) 1526 { 1527 if (buf == NULL) 1528 { 1529 nbdebug((" invalid buffer identifier in stopDocumentListen\n")); 1530 emsg("E638: invalid buffer identifier in stopDocumentListen"); 1531 return FAIL; 1532 } 1533 buf->fireChanges = 0; 1534 if (buf->bufp != NULL && buf->bufp->b_was_netbeans_file) 1535 { 1536 if (!buf->bufp->b_netbeans_file) 1537 { 1538 nbdebug(("E658: NetBeans connection lost for buffer %d\n", buf->bufp->b_fnum)); 1539 semsg(_("E658: NetBeans connection lost for buffer %d"), 1540 buf->bufp->b_fnum); 1541 } 1542 else 1543 { 1544 /* NetBeans uses stopDocumentListen when it stops editing 1545 * a file. It then expects the buffer in Vim to 1546 * disappear. */ 1547 do_bufdel(DOBUF_DEL, (char_u *)"", 1, 1548 buf->bufp->b_fnum, buf->bufp->b_fnum, TRUE); 1549 vim_memset(buf, 0, sizeof(nbbuf_T)); 1550 } 1551 } 1552 /* =====================================================================*/ 1553 } 1554 else if (streq((char *)cmd, "setTitle")) 1555 { 1556 if (buf == NULL) 1557 { 1558 nbdebug((" invalid buffer identifier in setTitle\n")); 1559 emsg("E639: invalid buffer identifier in setTitle"); 1560 return FAIL; 1561 } 1562 vim_free(buf->displayname); 1563 buf->displayname = nb_unquote(args, NULL); 1564 /* =====================================================================*/ 1565 } 1566 else if (streq((char *)cmd, "initDone")) 1567 { 1568 if (buf == NULL || buf->bufp == NULL) 1569 { 1570 nbdebug((" invalid buffer identifier in initDone\n")); 1571 emsg("E640: invalid buffer identifier in initDone"); 1572 return FAIL; 1573 } 1574 do_update = 1; 1575 buf->initDone = TRUE; 1576 nb_set_curbuf(buf->bufp); 1577 apply_autocmds(EVENT_BUFREADPOST, 0, 0, FALSE, buf->bufp); 1578 1579 /* handle any postponed key commands */ 1580 handle_key_queue(); 1581 /* =====================================================================*/ 1582 } 1583 else if (streq((char *)cmd, "setBufferNumber") 1584 || streq((char *)cmd, "putBufferNumber")) 1585 { 1586 char_u *path; 1587 buf_T *bufp; 1588 1589 if (buf == NULL) 1590 { 1591 nbdebug((" invalid buffer identifier in setBufferNumber\n")); 1592 emsg("E641: invalid buffer identifier in setBufferNumber"); 1593 return FAIL; 1594 } 1595 path = (char_u *)nb_unquote(args, NULL); 1596 if (path == NULL) 1597 return FAIL; 1598 bufp = buflist_findname(path); 1599 vim_free(path); 1600 if (bufp == NULL) 1601 { 1602 nbdebug((" File %s not found in setBufferNumber\n", args)); 1603 semsg("E642: File %s not found in setBufferNumber", args); 1604 return FAIL; 1605 } 1606 buf->bufp = bufp; 1607 buf->nbbuf_number = bufp->b_fnum; 1608 1609 /* "setBufferNumber" has the side effect of jumping to the buffer 1610 * (don't know why!). Don't do that for "putBufferNumber". */ 1611 if (*cmd != 'p') 1612 coloncmd(":buffer %d", bufp->b_fnum); 1613 else 1614 { 1615 buf->initDone = TRUE; 1616 1617 /* handle any postponed key commands */ 1618 handle_key_queue(); 1619 } 1620 1621 /* =====================================================================*/ 1622 } 1623 else if (streq((char *)cmd, "setFullName")) 1624 { 1625 if (buf == NULL) 1626 { 1627 nbdebug((" invalid buffer identifier in setFullName\n")); 1628 emsg("E643: invalid buffer identifier in setFullName"); 1629 return FAIL; 1630 } 1631 vim_free(buf->displayname); 1632 buf->displayname = nb_unquote(args, NULL); 1633 1634 netbeansReadFile = 0; /* don't try to open disk file */ 1635 do_ecmd(0, (char_u *)buf->displayname, 0, 0, ECMD_ONE, 1636 ECMD_HIDE + ECMD_OLDBUF, curwin); 1637 netbeansReadFile = 1; 1638 buf->bufp = curbuf; 1639 maketitle(); 1640 #if defined(FEAT_MENU) && defined(FEAT_GUI) 1641 if (gui.in_use) 1642 gui_update_menus(0); 1643 #endif 1644 /* =====================================================================*/ 1645 } 1646 else if (streq((char *)cmd, "editFile")) 1647 { 1648 if (buf == NULL) 1649 { 1650 nbdebug((" invalid buffer identifier in editFile\n")); 1651 emsg("E644: invalid buffer identifier in editFile"); 1652 return FAIL; 1653 } 1654 /* Edit a file: like create + setFullName + read the file. */ 1655 vim_free(buf->displayname); 1656 buf->displayname = nb_unquote(args, NULL); 1657 do_ecmd(0, (char_u *)buf->displayname, NULL, NULL, ECMD_ONE, 1658 ECMD_HIDE + ECMD_OLDBUF, curwin); 1659 buf->bufp = curbuf; 1660 buf->initDone = TRUE; 1661 do_update = 1; 1662 #if defined(FEAT_TITLE) 1663 maketitle(); 1664 #endif 1665 #if defined(FEAT_MENU) && defined(FEAT_GUI) 1666 if (gui.in_use) 1667 gui_update_menus(0); 1668 #endif 1669 /* =====================================================================*/ 1670 } 1671 else if (streq((char *)cmd, "setVisible")) 1672 { 1673 if (buf == NULL || buf->bufp == NULL) 1674 { 1675 nbdebug((" invalid buffer identifier in setVisible\n")); 1676 /* This message was commented out, probably because it can 1677 * happen when shutting down. */ 1678 if (p_verbose > 0) 1679 emsg("E645: invalid buffer identifier in setVisible"); 1680 return FAIL; 1681 } 1682 if (streq((char *)args, "T") && buf->bufp != curbuf) 1683 { 1684 exarg_T exarg; 1685 exarg.cmd = (char_u *)"goto"; 1686 exarg.forceit = FALSE; 1687 dosetvisible = TRUE; 1688 goto_buffer(&exarg, DOBUF_FIRST, FORWARD, buf->bufp->b_fnum); 1689 do_update = 1; 1690 dosetvisible = FALSE; 1691 1692 #ifdef FEAT_GUI 1693 /* Side effect!!!. */ 1694 if (gui.in_use) 1695 gui_mch_set_foreground(); 1696 #endif 1697 } 1698 /* =====================================================================*/ 1699 } 1700 else if (streq((char *)cmd, "raise")) 1701 { 1702 #ifdef FEAT_GUI 1703 /* Bring gvim to the foreground. */ 1704 if (gui.in_use) 1705 gui_mch_set_foreground(); 1706 #endif 1707 /* =====================================================================*/ 1708 } 1709 else if (streq((char *)cmd, "setModified")) 1710 { 1711 int prev_b_changed; 1712 1713 if (buf == NULL || buf->bufp == NULL) 1714 { 1715 nbdebug((" invalid buffer identifier in setModified\n")); 1716 /* This message was commented out, probably because it can 1717 * happen when shutting down. */ 1718 if (p_verbose > 0) 1719 emsg("E646: invalid buffer identifier in setModified"); 1720 return FAIL; 1721 } 1722 prev_b_changed = buf->bufp->b_changed; 1723 if (streq((char *)args, "T")) 1724 buf->bufp->b_changed = TRUE; 1725 else 1726 { 1727 stat_T st; 1728 1729 /* Assume NetBeans stored the file. Reset the timestamp to 1730 * avoid "file changed" warnings. */ 1731 if (buf->bufp->b_ffname != NULL 1732 && mch_stat((char *)buf->bufp->b_ffname, &st) >= 0) 1733 buf_store_time(buf->bufp, &st, buf->bufp->b_ffname); 1734 buf->bufp->b_changed = FALSE; 1735 } 1736 buf->modified = buf->bufp->b_changed; 1737 if (prev_b_changed != buf->bufp->b_changed) 1738 { 1739 check_status(buf->bufp); 1740 redraw_tabline = TRUE; 1741 #ifdef FEAT_TITLE 1742 maketitle(); 1743 #endif 1744 update_screen(0); 1745 } 1746 /* =====================================================================*/ 1747 } 1748 else if (streq((char *)cmd, "setModtime")) 1749 { 1750 if (buf == NULL || buf->bufp == NULL) 1751 nbdebug((" invalid buffer identifier in setModtime\n")); 1752 else 1753 buf->bufp->b_mtime = atoi((char *)args); 1754 /* =====================================================================*/ 1755 } 1756 else if (streq((char *)cmd, "setReadOnly")) 1757 { 1758 if (buf == NULL || buf->bufp == NULL) 1759 nbdebug((" invalid buffer identifier in setReadOnly\n")); 1760 else if (streq((char *)args, "T")) 1761 buf->bufp->b_p_ro = TRUE; 1762 else 1763 buf->bufp->b_p_ro = FALSE; 1764 /* =====================================================================*/ 1765 } 1766 else if (streq((char *)cmd, "setMark")) 1767 { 1768 /* not yet */ 1769 /* =====================================================================*/ 1770 } 1771 else if (streq((char *)cmd, "showBalloon")) 1772 { 1773 #if defined(FEAT_BEVAL_GUI) 1774 static char *text = NULL; 1775 1776 /* 1777 * Set up the Balloon Expression Evaluation area. 1778 * Ignore 'ballooneval' here. 1779 * The text pointer must remain valid for a while. 1780 */ 1781 if (balloonEval != NULL) 1782 { 1783 vim_free(text); 1784 text = nb_unquote(args, NULL); 1785 if (text != NULL) 1786 gui_mch_post_balloon(balloonEval, (char_u *)text); 1787 } 1788 #endif 1789 /* =====================================================================*/ 1790 } 1791 else if (streq((char *)cmd, "setDot")) 1792 { 1793 pos_T *pos; 1794 #ifdef NBDEBUG 1795 char_u *s; 1796 #endif 1797 1798 if (buf == NULL || buf->bufp == NULL) 1799 { 1800 nbdebug((" invalid buffer identifier in setDot\n")); 1801 emsg("E647: invalid buffer identifier in setDot"); 1802 return FAIL; 1803 } 1804 1805 nb_set_curbuf(buf->bufp); 1806 1807 /* Don't want Visual mode now. */ 1808 if (VIsual_active) 1809 end_visual_mode(); 1810 #ifdef NBDEBUG 1811 s = args; 1812 #endif 1813 pos = get_off_or_lnum(buf->bufp, &args); 1814 if (pos) 1815 { 1816 curwin->w_cursor = *pos; 1817 check_cursor(); 1818 #ifdef FEAT_FOLDING 1819 foldOpenCursor(); 1820 #endif 1821 } 1822 else 1823 { 1824 nbdebug((" BAD POSITION in setDot: %s\n", s)); 1825 } 1826 1827 /* gui_update_cursor(TRUE, FALSE); */ 1828 /* update_curbuf(NOT_VALID); */ 1829 update_topline(); /* scroll to show the line */ 1830 update_screen(VALID); 1831 setcursor(); 1832 cursor_on(); 1833 out_flush_cursor(TRUE, FALSE); 1834 1835 /* Quit a hit-return or more prompt. */ 1836 if (State == HITRETURN || State == ASKMORE) 1837 { 1838 #ifdef FEAT_GUI_GTK 1839 if (gui.in_use && gtk_main_level() > 0) 1840 gtk_main_quit(); 1841 #endif 1842 } 1843 /* =====================================================================*/ 1844 } 1845 else if (streq((char *)cmd, "close")) 1846 { 1847 #ifdef NBDEBUG 1848 char *name = "<NONE>"; 1849 #endif 1850 1851 if (buf == NULL) 1852 { 1853 nbdebug((" invalid buffer identifier in close\n")); 1854 emsg("E648: invalid buffer identifier in close"); 1855 return FAIL; 1856 } 1857 1858 #ifdef NBDEBUG 1859 if (buf->displayname != NULL) 1860 name = buf->displayname; 1861 #endif 1862 if (buf->bufp == NULL) 1863 { 1864 nbdebug((" invalid buffer identifier in close\n")); 1865 /* This message was commented out, probably because it can 1866 * happen when shutting down. */ 1867 if (p_verbose > 0) 1868 emsg("E649: invalid buffer identifier in close"); 1869 } 1870 nbdebug((" CLOSE %d: %s\n", bufno, name)); 1871 #ifdef FEAT_GUI 1872 need_mouse_correct = TRUE; 1873 #endif 1874 if (buf->bufp != NULL) 1875 do_buffer(DOBUF_WIPE, DOBUF_FIRST, FORWARD, 1876 buf->bufp->b_fnum, TRUE); 1877 buf->bufp = NULL; 1878 buf->initDone = FALSE; 1879 do_update = 1; 1880 /* =====================================================================*/ 1881 } 1882 else if (streq((char *)cmd, "setStyle")) /* obsolete... */ 1883 { 1884 nbdebug((" setStyle is obsolete!\n")); 1885 /* =====================================================================*/ 1886 } 1887 else if (streq((char *)cmd, "setExitDelay")) 1888 { 1889 /* Only used in version 2.1. */ 1890 /* =====================================================================*/ 1891 } 1892 else if (streq((char *)cmd, "defineAnnoType")) 1893 { 1894 #ifdef FEAT_SIGNS 1895 int typeNum; 1896 char_u *typeName; 1897 char_u *tooltip; 1898 char_u *p; 1899 char_u *glyphFile; 1900 int parse_error = FALSE; 1901 char_u *fg; 1902 char_u *bg; 1903 1904 if (buf == NULL) 1905 { 1906 nbdebug((" invalid buffer identifier in defineAnnoType\n")); 1907 emsg("E650: invalid buffer identifier in defineAnnoType"); 1908 return FAIL; 1909 } 1910 1911 cp = (char *)args; 1912 typeNum = strtol(cp, &cp, 10); 1913 args = (char_u *)cp; 1914 args = skipwhite(args); 1915 typeName = (char_u *)nb_unquote(args, &args); 1916 args = skipwhite(args + 1); 1917 tooltip = (char_u *)nb_unquote(args, &args); 1918 args = skipwhite(args + 1); 1919 1920 p = (char_u *)nb_unquote(args, &args); 1921 glyphFile = vim_strsave_escaped(p, escape_chars); 1922 vim_free(p); 1923 1924 args = skipwhite(args + 1); 1925 p = skiptowhite(args); 1926 if (*p != NUL) 1927 { 1928 *p = NUL; 1929 p = skipwhite(p + 1); 1930 } 1931 fg = vim_strsave(args); 1932 bg = vim_strsave(p); 1933 if (STRLEN(fg) > MAX_COLOR_LENGTH || STRLEN(bg) > MAX_COLOR_LENGTH) 1934 { 1935 emsg("E532: highlighting color name too long in defineAnnoType"); 1936 vim_free(typeName); 1937 parse_error = TRUE; 1938 } 1939 else if (typeName != NULL && tooltip != NULL && glyphFile != NULL) 1940 addsigntype(buf, typeNum, typeName, tooltip, glyphFile, fg, bg); 1941 else 1942 vim_free(typeName); 1943 1944 /* don't free typeName; it's used directly in addsigntype() */ 1945 vim_free(fg); 1946 vim_free(bg); 1947 vim_free(tooltip); 1948 vim_free(glyphFile); 1949 if (parse_error) 1950 return FAIL; 1951 1952 #endif 1953 /* =====================================================================*/ 1954 } 1955 else if (streq((char *)cmd, "addAnno")) 1956 { 1957 #ifdef FEAT_SIGNS 1958 int serNum; 1959 int localTypeNum; 1960 int typeNum; 1961 pos_T *pos; 1962 1963 if (buf == NULL || buf->bufp == NULL) 1964 { 1965 nbdebug((" invalid buffer identifier in addAnno\n")); 1966 emsg("E651: invalid buffer identifier in addAnno"); 1967 return FAIL; 1968 } 1969 1970 do_update = 1; 1971 1972 cp = (char *)args; 1973 serNum = strtol(cp, &cp, 10); 1974 1975 /* Get the typenr specific for this buffer and convert it to 1976 * the global typenumber, as used for the sign name. */ 1977 localTypeNum = strtol(cp, &cp, 10); 1978 args = (char_u *)cp; 1979 typeNum = mapsigntype(buf, localTypeNum); 1980 1981 pos = get_off_or_lnum(buf->bufp, &args); 1982 1983 cp = (char *)args; 1984 vim_ignored = (int)strtol(cp, &cp, 10); 1985 args = (char_u *)cp; 1986 # ifdef NBDEBUG 1987 if (vim_ignored != -1) 1988 nbdebug((" partial line annotation -- Not Yet Implemented!\n")); 1989 # endif 1990 if (serNum >= GUARDEDOFFSET) 1991 { 1992 nbdebug((" too many annotations! ignoring...\n")); 1993 return FAIL; 1994 } 1995 if (pos) 1996 { 1997 coloncmd(":sign place %d line=%ld name=%d buffer=%d", 1998 serNum, pos->lnum, typeNum, buf->bufp->b_fnum); 1999 if (typeNum == curPCtype) 2000 coloncmd(":sign jump %d buffer=%d", serNum, 2001 buf->bufp->b_fnum); 2002 } 2003 #endif 2004 /* =====================================================================*/ 2005 } 2006 else if (streq((char *)cmd, "removeAnno")) 2007 { 2008 #ifdef FEAT_SIGNS 2009 int serNum; 2010 2011 if (buf == NULL || buf->bufp == NULL) 2012 { 2013 nbdebug((" invalid buffer identifier in removeAnno\n")); 2014 return FAIL; 2015 } 2016 do_update = 1; 2017 cp = (char *)args; 2018 serNum = strtol(cp, &cp, 10); 2019 args = (char_u *)cp; 2020 coloncmd(":sign unplace %d buffer=%d", 2021 serNum, buf->bufp->b_fnum); 2022 redraw_buf_later(buf->bufp, NOT_VALID); 2023 #endif 2024 /* =====================================================================*/ 2025 } 2026 else if (streq((char *)cmd, "moveAnnoToFront")) 2027 { 2028 #ifdef FEAT_SIGNS 2029 nbdebug((" moveAnnoToFront: Not Yet Implemented!\n")); 2030 #endif 2031 /* =====================================================================*/ 2032 } 2033 else if (streq((char *)cmd, "guard") || streq((char *)cmd, "unguard")) 2034 { 2035 int len; 2036 pos_T first; 2037 pos_T last; 2038 pos_T *pos; 2039 int un = (cmd[0] == 'u'); 2040 static int guardId = GUARDEDOFFSET; 2041 2042 if (skip >= SKIP_STOP) 2043 { 2044 nbdebug((" Skipping %s command\n", (char *) cmd)); 2045 return OK; 2046 } 2047 2048 nb_init_graphics(); 2049 2050 if (buf == NULL || buf->bufp == NULL) 2051 { 2052 nbdebug((" invalid buffer identifier in %s command\n", cmd)); 2053 return FAIL; 2054 } 2055 nb_set_curbuf(buf->bufp); 2056 cp = (char *)args; 2057 off = strtol(cp, &cp, 10); 2058 len = strtol(cp, NULL, 10); 2059 args = (char_u *)cp; 2060 pos = off2pos(buf->bufp, off); 2061 do_update = 1; 2062 if (!pos) 2063 nbdebug((" no such start pos in %s, %ld\n", cmd, off)); 2064 else 2065 { 2066 first = *pos; 2067 pos = off2pos(buf->bufp, off + len - 1); 2068 if (pos != NULL && pos->col == 0) 2069 { 2070 /* 2071 * In Java Swing the offset is a position between 2 2072 * characters. If col == 0 then we really want the 2073 * previous line as the end. 2074 */ 2075 pos = off2pos(buf->bufp, off + len - 2); 2076 } 2077 if (!pos) 2078 nbdebug((" no such end pos in %s, %ld\n", 2079 cmd, off + len - 1)); 2080 else 2081 { 2082 long lnum; 2083 last = *pos; 2084 /* set highlight for region */ 2085 nbdebug((" %sGUARD %ld,%d to %ld,%d\n", (un) ? "UN" : "", 2086 first.lnum, first.col, 2087 last.lnum, last.col)); 2088 #ifdef FEAT_SIGNS 2089 for (lnum = first.lnum; lnum <= last.lnum; lnum++) 2090 { 2091 if (un) 2092 { 2093 /* never used */ 2094 } 2095 else 2096 { 2097 if (buf_findsigntype_id(buf->bufp, lnum, 2098 GUARDED) == 0) 2099 { 2100 coloncmd( 2101 ":sign place %d line=%ld name=%d buffer=%d", 2102 guardId++, lnum, GUARDED, 2103 buf->bufp->b_fnum); 2104 } 2105 } 2106 } 2107 #endif 2108 redraw_buf_later(buf->bufp, NOT_VALID); 2109 } 2110 } 2111 /* =====================================================================*/ 2112 } 2113 else if (streq((char *)cmd, "startAtomic")) 2114 { 2115 inAtomic = 1; 2116 /* =====================================================================*/ 2117 } 2118 else if (streq((char *)cmd, "endAtomic")) 2119 { 2120 inAtomic = 0; 2121 if (needupdate) 2122 { 2123 do_update = 1; 2124 needupdate = 0; 2125 } 2126 /* =====================================================================*/ 2127 } 2128 else if (streq((char *)cmd, "save")) 2129 { 2130 /* 2131 * NOTE - This command is obsolete wrt NetBeans. It's left in 2132 * only for historical reasons. 2133 */ 2134 if (buf == NULL || buf->bufp == NULL) 2135 { 2136 nbdebug((" invalid buffer identifier in %s command\n", cmd)); 2137 return FAIL; 2138 } 2139 2140 /* the following is taken from ex_cmds.c (do_wqall function) */ 2141 if (bufIsChanged(buf->bufp)) 2142 { 2143 /* Only write if the buffer can be written. */ 2144 if (p_write 2145 && !buf->bufp->b_p_ro 2146 && buf->bufp->b_ffname != NULL 2147 #ifdef FEAT_QUICKFIX 2148 && !bt_dontwrite(buf->bufp) 2149 #endif 2150 ) 2151 { 2152 bufref_T bufref; 2153 2154 set_bufref(&bufref, buf->bufp); 2155 buf_write_all(buf->bufp, FALSE); 2156 /* an autocommand may have deleted the buffer */ 2157 if (!bufref_valid(&bufref)) 2158 buf->bufp = NULL; 2159 } 2160 } 2161 else 2162 { 2163 nbdebug((" Buffer has no changes!\n")); 2164 } 2165 /* =====================================================================*/ 2166 } 2167 else if (streq((char *)cmd, "netbeansBuffer")) 2168 { 2169 if (buf == NULL || buf->bufp == NULL) 2170 { 2171 nbdebug((" invalid buffer identifier in %s command\n", cmd)); 2172 return FAIL; 2173 } 2174 if (*args == 'T') 2175 { 2176 buf->bufp->b_netbeans_file = TRUE; 2177 buf->bufp->b_was_netbeans_file = TRUE; 2178 } 2179 else 2180 buf->bufp->b_netbeans_file = FALSE; 2181 /* =====================================================================*/ 2182 } 2183 else if (streq((char *)cmd, "specialKeys")) 2184 { 2185 special_keys(args); 2186 /* =====================================================================*/ 2187 } 2188 else if (streq((char *)cmd, "actionMenuItem")) 2189 { 2190 /* not used yet */ 2191 /* =====================================================================*/ 2192 } 2193 else if (streq((char *)cmd, "version")) 2194 { 2195 /* not used yet */ 2196 } 2197 else 2198 { 2199 nbdebug(("Unrecognised command: %s\n", cmd)); 2200 } 2201 /* 2202 * Unrecognized command is ignored. 2203 */ 2204 } 2205 if (inAtomic && do_update) 2206 { 2207 needupdate = 1; 2208 do_update = 0; 2209 } 2210 2211 /* 2212 * Is this needed? I moved the netbeans_Xt_connect() later during startup 2213 * and it may no longer be necessary. If it's not needed then needupdate 2214 * and do_update can also be removed. 2215 */ 2216 if (buf != NULL && buf->initDone && do_update) 2217 { 2218 update_screen(NOT_VALID); 2219 setcursor(); 2220 cursor_on(); 2221 out_flush_cursor(TRUE, FALSE); 2222 2223 /* Quit a hit-return or more prompt. */ 2224 if (State == HITRETURN || State == ASKMORE) 2225 { 2226 #ifdef FEAT_GUI_GTK 2227 if (gui.in_use && gtk_main_level() > 0) 2228 gtk_main_quit(); 2229 #endif 2230 } 2231 } 2232 2233 return retval; 2234 } 2235 2236 2237 /* 2238 * If "buf" is not the current buffer try changing to a window that edits this 2239 * buffer. If there is no such window then close the current buffer and set 2240 * the current buffer as "buf". 2241 */ 2242 static void 2243 nb_set_curbuf(buf_T *buf) 2244 { 2245 if (curbuf != buf) { 2246 if (buf_jump_open_win(buf) != NULL) 2247 return; 2248 if ((swb_flags & SWB_USETAB) && buf_jump_open_tab(buf) != NULL) 2249 return; 2250 set_curbuf(buf, DOBUF_GOTO); 2251 } 2252 } 2253 2254 /* 2255 * Process a vim colon command. 2256 */ 2257 static void 2258 coloncmd(char *cmd, ...) 2259 { 2260 char buf[1024]; 2261 va_list ap; 2262 2263 va_start(ap, cmd); 2264 vim_vsnprintf(buf, sizeof(buf), cmd, ap); 2265 va_end(ap); 2266 2267 nbdebug((" COLONCMD %s\n", buf)); 2268 2269 do_cmdline((char_u *)buf, NULL, NULL, DOCMD_NOWAIT | DOCMD_KEYTYPED); 2270 2271 setcursor(); /* restore the cursor position */ 2272 out_flush_cursor(TRUE, FALSE); 2273 } 2274 2275 2276 /* 2277 * Parse the specialKeys argument and issue the appropriate map commands. 2278 */ 2279 static void 2280 special_keys(char_u *args) 2281 { 2282 char *save_str = nb_unquote(args, NULL); 2283 char *tok = strtok(save_str, " "); 2284 char *sep; 2285 #define KEYBUFLEN 64 2286 char keybuf[KEYBUFLEN]; 2287 char cmdbuf[256]; 2288 2289 while (tok != NULL) 2290 { 2291 int i = 0; 2292 2293 if ((sep = strchr(tok, '-')) != NULL) 2294 { 2295 *sep = NUL; 2296 while (*tok) 2297 { 2298 switch (*tok) 2299 { 2300 case 'A': 2301 case 'M': 2302 case 'C': 2303 case 'S': 2304 keybuf[i++] = *tok; 2305 keybuf[i++] = '-'; 2306 break; 2307 } 2308 tok++; 2309 } 2310 tok++; 2311 } 2312 2313 if (strlen(tok) + i < KEYBUFLEN) 2314 { 2315 strcpy(&keybuf[i], tok); 2316 vim_snprintf(cmdbuf, sizeof(cmdbuf), 2317 "<silent><%s> :nbkey %s<CR>", keybuf, keybuf); 2318 do_map(0, (char_u *)cmdbuf, NORMAL, FALSE); 2319 } 2320 tok = strtok(NULL, " "); 2321 } 2322 vim_free(save_str); 2323 } 2324 2325 void 2326 ex_nbclose(exarg_T *eap UNUSED) 2327 { 2328 netbeans_close(); 2329 } 2330 2331 void 2332 ex_nbkey(exarg_T *eap) 2333 { 2334 (void)netbeans_keystring(eap->arg); 2335 } 2336 2337 void 2338 ex_nbstart( 2339 exarg_T *eap) 2340 { 2341 #ifdef FEAT_GUI 2342 # if !defined(FEAT_GUI_X11) && !defined(FEAT_GUI_GTK) \ 2343 && !defined(FEAT_GUI_MSWIN) 2344 if (gui.in_use) 2345 { 2346 emsg(_("E838: netbeans is not supported with this GUI")); 2347 return; 2348 } 2349 # endif 2350 #endif 2351 netbeans_open((char *)eap->arg, FALSE); 2352 } 2353 2354 /* 2355 * Initialize highlights and signs for use by netbeans (mostly obsolete) 2356 */ 2357 static void 2358 nb_init_graphics(void) 2359 { 2360 static int did_init = FALSE; 2361 2362 if (!did_init) 2363 { 2364 coloncmd(":highlight NBGuarded guibg=Cyan guifg=Black" 2365 " ctermbg=LightCyan ctermfg=Black"); 2366 coloncmd(":sign define %d linehl=NBGuarded", GUARDED); 2367 2368 did_init = TRUE; 2369 } 2370 } 2371 2372 /* 2373 * Convert key to netbeans name. This uses the global "mod_mask". 2374 */ 2375 static void 2376 netbeans_keyname(int key, char *buf) 2377 { 2378 char *name = 0; 2379 char namebuf[2]; 2380 int ctrl = 0; 2381 int shift = 0; 2382 int alt = 0; 2383 2384 if (mod_mask & MOD_MASK_CTRL) 2385 ctrl = 1; 2386 if (mod_mask & MOD_MASK_SHIFT) 2387 shift = 1; 2388 if (mod_mask & MOD_MASK_ALT) 2389 alt = 1; 2390 2391 2392 switch (key) 2393 { 2394 case K_F1: name = "F1"; break; 2395 case K_S_F1: name = "F1"; shift = 1; break; 2396 case K_F2: name = "F2"; break; 2397 case K_S_F2: name = "F2"; shift = 1; break; 2398 case K_F3: name = "F3"; break; 2399 case K_S_F3: name = "F3"; shift = 1; break; 2400 case K_F4: name = "F4"; break; 2401 case K_S_F4: name = "F4"; shift = 1; break; 2402 case K_F5: name = "F5"; break; 2403 case K_S_F5: name = "F5"; shift = 1; break; 2404 case K_F6: name = "F6"; break; 2405 case K_S_F6: name = "F6"; shift = 1; break; 2406 case K_F7: name = "F7"; break; 2407 case K_S_F7: name = "F7"; shift = 1; break; 2408 case K_F8: name = "F8"; break; 2409 case K_S_F8: name = "F8"; shift = 1; break; 2410 case K_F9: name = "F9"; break; 2411 case K_S_F9: name = "F9"; shift = 1; break; 2412 case K_F10: name = "F10"; break; 2413 case K_S_F10: name = "F10"; shift = 1; break; 2414 case K_F11: name = "F11"; break; 2415 case K_S_F11: name = "F11"; shift = 1; break; 2416 case K_F12: name = "F12"; break; 2417 case K_S_F12: name = "F12"; shift = 1; break; 2418 default: 2419 if (key >= ' ' && key <= '~') 2420 { 2421 /* Allow ASCII characters. */ 2422 name = namebuf; 2423 namebuf[0] = key; 2424 namebuf[1] = NUL; 2425 } 2426 else 2427 name = "X"; 2428 break; 2429 } 2430 2431 buf[0] = '\0'; 2432 if (ctrl) 2433 strcat(buf, "C"); 2434 if (shift) 2435 strcat(buf, "S"); 2436 if (alt) 2437 strcat(buf, "M"); /* META */ 2438 if (ctrl || shift || alt) 2439 strcat(buf, "-"); 2440 strcat(buf, name); 2441 } 2442 2443 #if defined(FEAT_BEVAL) || defined(PROTO) 2444 /* 2445 * Function to be called for balloon evaluation. Grabs the text under the 2446 * cursor and sends it to the debugger for evaluation. The debugger should 2447 * respond with a showBalloon command when there is a useful result. 2448 */ 2449 void 2450 netbeans_beval_cb( 2451 BalloonEval *beval, 2452 int state UNUSED) 2453 { 2454 win_T *wp; 2455 char_u *text; 2456 linenr_T lnum; 2457 int col; 2458 char *buf; 2459 char_u *p; 2460 2461 /* Don't do anything when 'ballooneval' is off, messages scrolled the 2462 * windows up or we have no connection. */ 2463 if (!can_use_beval() || !NETBEANS_OPEN) 2464 return; 2465 2466 if (get_beval_info(beval, TRUE, &wp, &lnum, &text, &col) == OK) 2467 { 2468 /* Send debugger request. Only when the text is of reasonable 2469 * length. */ 2470 if (text != NULL && text[0] != NUL && STRLEN(text) < MAXPATHL) 2471 { 2472 buf = alloc(MAXPATHL * 2 + 25); 2473 if (buf != NULL) 2474 { 2475 p = nb_quote(text); 2476 if (p != NULL) 2477 { 2478 vim_snprintf(buf, MAXPATHL * 2 + 25, 2479 "0:balloonText=%d \"%s\"\n", r_cmdno, p); 2480 vim_free(p); 2481 } 2482 nbdebug(("EVT: %s", buf)); 2483 nb_send(buf, "netbeans_beval_cb"); 2484 vim_free(buf); 2485 } 2486 } 2487 vim_free(text); 2488 } 2489 } 2490 #endif 2491 2492 /* 2493 * Return TRUE when the netbeans connection is active. 2494 */ 2495 int 2496 netbeans_active(void) 2497 { 2498 return NETBEANS_OPEN; 2499 } 2500 2501 /* 2502 * Tell netbeans that the window was opened, ready for commands. 2503 */ 2504 void 2505 netbeans_open(char *params, int doabort) 2506 { 2507 char *cmd = "0:startupDone=0\n"; 2508 2509 if (NETBEANS_OPEN) 2510 { 2511 emsg(_("E511: netbeans already connected")); 2512 return; 2513 } 2514 2515 if (netbeans_connect(params, doabort) != OK) 2516 return; 2517 2518 nbdebug(("EVT: %s", cmd)); 2519 nb_send(cmd, "netbeans_startup_done"); 2520 2521 /* update the screen after having added the gutter */ 2522 changed_window_setting(); 2523 update_screen(CLEAR); 2524 setcursor(); 2525 cursor_on(); 2526 out_flush_cursor(TRUE, FALSE); 2527 } 2528 2529 /* 2530 * Tell netbeans that we're exiting. This should be called right 2531 * before calling exit. 2532 */ 2533 void 2534 netbeans_send_disconnect(void) 2535 { 2536 char buf[128]; 2537 2538 if (NETBEANS_OPEN) 2539 { 2540 sprintf(buf, "0:disconnect=%d\n", r_cmdno); 2541 nbdebug(("EVT: %s", buf)); 2542 nb_send(buf, "netbeans_disconnect"); 2543 } 2544 } 2545 2546 #if defined(FEAT_EVAL) || defined(PROTO) 2547 int 2548 set_ref_in_nb_channel(int copyID) 2549 { 2550 int abort = FALSE; 2551 typval_T tv; 2552 2553 if (nb_channel != NULL) 2554 { 2555 tv.v_type = VAR_CHANNEL; 2556 tv.vval.v_channel = nb_channel; 2557 abort = set_ref_in_item(&tv, copyID, NULL, NULL); 2558 } 2559 return abort; 2560 } 2561 #endif 2562 2563 #if defined(FEAT_GUI_X11) || defined(FEAT_GUI_MSWIN) || defined(PROTO) 2564 /* 2565 * Tell netbeans that the window was moved or resized. 2566 */ 2567 void 2568 netbeans_frame_moved(int new_x, int new_y) 2569 { 2570 char buf[128]; 2571 2572 if (!NETBEANS_OPEN) 2573 return; 2574 2575 sprintf(buf, "0:geometry=%d %d %d %d %d\n", 2576 r_cmdno, (int)Columns, (int)Rows, new_x, new_y); 2577 /*nbdebug(("EVT: %s", buf)); happens too many times during a move */ 2578 nb_send(buf, "netbeans_frame_moved"); 2579 } 2580 #endif 2581 2582 /* 2583 * Tell netbeans the user opened or activated a file. 2584 */ 2585 void 2586 netbeans_file_activated(buf_T *bufp) 2587 { 2588 int bufno = nb_getbufno(bufp); 2589 nbbuf_T *bp = nb_get_buf(bufno); 2590 char buffer[2*MAXPATHL]; 2591 char_u *q; 2592 2593 if (!NETBEANS_OPEN || !bufp->b_netbeans_file || dosetvisible) 2594 return; 2595 2596 q = nb_quote(bufp->b_ffname); 2597 if (q == NULL || bp == NULL) 2598 return; 2599 2600 vim_snprintf(buffer, sizeof(buffer), "%d:fileOpened=%d \"%s\" %s %s\n", 2601 bufno, 2602 bufno, 2603 (char *)q, 2604 "T", /* open in NetBeans */ 2605 "F"); /* modified */ 2606 2607 vim_free(q); 2608 nbdebug(("EVT: %s", buffer)); 2609 2610 nb_send(buffer, "netbeans_file_opened"); 2611 } 2612 2613 /* 2614 * Tell netbeans the user opened a file. 2615 */ 2616 void 2617 netbeans_file_opened(buf_T *bufp) 2618 { 2619 int bufno = nb_getbufno(bufp); 2620 char buffer[2*MAXPATHL]; 2621 char_u *q; 2622 nbbuf_T *bp = nb_get_buf(nb_getbufno(bufp)); 2623 int bnum; 2624 2625 if (!NETBEANS_OPEN) 2626 return; 2627 2628 q = nb_quote(bufp->b_ffname); 2629 if (q == NULL) 2630 return; 2631 if (bp != NULL) 2632 bnum = bufno; 2633 else 2634 bnum = 0; 2635 2636 vim_snprintf(buffer, sizeof(buffer), "%d:fileOpened=%d \"%s\" %s %s\n", 2637 bnum, 2638 0, 2639 (char *)q, 2640 "T", /* open in NetBeans */ 2641 "F"); /* modified */ 2642 2643 vim_free(q); 2644 nbdebug(("EVT: %s", buffer)); 2645 2646 nb_send(buffer, "netbeans_file_opened"); 2647 if (p_acd && vim_chdirfile(bufp->b_ffname, "auto") == OK) 2648 shorten_fnames(TRUE); 2649 } 2650 2651 /* 2652 * Tell netbeans that a file was deleted or wiped out. 2653 */ 2654 void 2655 netbeans_file_killed(buf_T *bufp) 2656 { 2657 int bufno = nb_getbufno(bufp); 2658 nbbuf_T *nbbuf = nb_get_buf(bufno); 2659 char buffer[2*MAXPATHL]; 2660 2661 if (!NETBEANS_OPEN || bufno == -1) 2662 return; 2663 2664 nbdebug(("netbeans_file_killed:\n")); 2665 nbdebug((" Killing bufno: %d", bufno)); 2666 2667 sprintf(buffer, "%d:killed=%d\n", bufno, r_cmdno); 2668 2669 nbdebug(("EVT: %s", buffer)); 2670 2671 nb_send(buffer, "netbeans_file_killed"); 2672 2673 if (nbbuf != NULL) 2674 nbbuf->bufp = NULL; 2675 } 2676 2677 /* 2678 * Get a pointer to the Netbeans buffer for Vim buffer "bufp". 2679 * Return NULL if there is no such buffer or changes are not to be reported. 2680 * Otherwise store the buffer number in "*bufnop". 2681 */ 2682 static nbbuf_T * 2683 nb_bufp2nbbuf_fire(buf_T *bufp, int *bufnop) 2684 { 2685 int bufno; 2686 nbbuf_T *nbbuf; 2687 2688 if (!NETBEANS_OPEN || !netbeansFireChanges) 2689 return NULL; /* changes are not reported at all */ 2690 2691 bufno = nb_getbufno(bufp); 2692 if (bufno <= 0) 2693 return NULL; /* file is not known to NetBeans */ 2694 2695 nbbuf = nb_get_buf(bufno); 2696 if (nbbuf != NULL && !nbbuf->fireChanges) 2697 return NULL; /* changes in this buffer are not reported */ 2698 2699 *bufnop = bufno; 2700 return nbbuf; 2701 } 2702 2703 /* 2704 * Tell netbeans the user inserted some text. 2705 */ 2706 void 2707 netbeans_inserted( 2708 buf_T *bufp, 2709 linenr_T linenr, 2710 colnr_T col, 2711 char_u *txt, 2712 int newlen) 2713 { 2714 char_u *buf; 2715 int bufno; 2716 nbbuf_T *nbbuf; 2717 pos_T pos; 2718 long off; 2719 char_u *p; 2720 char_u *newtxt; 2721 2722 if (!NETBEANS_OPEN) 2723 return; 2724 2725 nbbuf = nb_bufp2nbbuf_fire(bufp, &bufno); 2726 if (nbbuf == NULL) 2727 return; 2728 2729 /* Don't mark as modified for initial read */ 2730 if (nbbuf->insertDone) 2731 nbbuf->modified = 1; 2732 2733 pos.lnum = linenr; 2734 pos.col = col; 2735 off = pos2off(bufp, &pos); 2736 2737 /* send the "insert" EVT */ 2738 newtxt = alloc(newlen + 1); 2739 vim_strncpy(newtxt, txt, newlen); 2740 p = nb_quote(newtxt); 2741 if (p != NULL) 2742 { 2743 buf = alloc(128 + 2*newlen); 2744 sprintf((char *)buf, "%d:insert=%d %ld \"%s\"\n", 2745 bufno, r_cmdno, off, p); 2746 nbdebug(("EVT: %s", buf)); 2747 nb_send((char *)buf, "netbeans_inserted"); 2748 vim_free(p); 2749 vim_free(buf); 2750 } 2751 vim_free(newtxt); 2752 } 2753 2754 /* 2755 * Tell netbeans some bytes have been removed. 2756 */ 2757 void 2758 netbeans_removed( 2759 buf_T *bufp, 2760 linenr_T linenr, 2761 colnr_T col, 2762 long len) 2763 { 2764 char_u buf[128]; 2765 int bufno; 2766 nbbuf_T *nbbuf; 2767 pos_T pos; 2768 long off; 2769 2770 if (!NETBEANS_OPEN) 2771 return; 2772 2773 nbbuf = nb_bufp2nbbuf_fire(bufp, &bufno); 2774 if (nbbuf == NULL) 2775 return; 2776 2777 if (len < 0) 2778 { 2779 nbdebug(("Negative len %ld in netbeans_removed()!\n", len)); 2780 return; 2781 } 2782 2783 nbbuf->modified = 1; 2784 2785 pos.lnum = linenr; 2786 pos.col = col; 2787 2788 off = pos2off(bufp, &pos); 2789 2790 sprintf((char *)buf, "%d:remove=%d %ld %ld\n", bufno, r_cmdno, off, len); 2791 nbdebug(("EVT: %s", buf)); 2792 nb_send((char *)buf, "netbeans_removed"); 2793 } 2794 2795 /* 2796 * Send netbeans an unmodified command. 2797 */ 2798 void 2799 netbeans_unmodified(buf_T *bufp UNUSED) 2800 { 2801 /* This is a no-op, because NetBeans considers a buffer modified 2802 * even when all changes have been undone. */ 2803 } 2804 2805 /* 2806 * Send a button release event back to netbeans. It's up to netbeans 2807 * to decide what to do (if anything) with this event. 2808 */ 2809 void 2810 netbeans_button_release(int button) 2811 { 2812 char buf[128]; 2813 int bufno; 2814 2815 if (!NETBEANS_OPEN) 2816 return; 2817 2818 bufno = nb_getbufno(curbuf); 2819 2820 if (bufno >= 0 && curwin != NULL && curwin->w_buffer == curbuf) 2821 { 2822 int col = mouse_col - curwin->w_wincol 2823 - ((curwin->w_p_nu || curwin->w_p_rnu) ? 9 : 1); 2824 long off = pos2off(curbuf, &curwin->w_cursor); 2825 2826 /* sync the cursor position */ 2827 sprintf(buf, "%d:newDotAndMark=%d %ld %ld\n", bufno, r_cmdno, off, off); 2828 nbdebug(("EVT: %s", buf)); 2829 nb_send(buf, "netbeans_button_release[newDotAndMark]"); 2830 2831 sprintf(buf, "%d:buttonRelease=%d %d %ld %d\n", bufno, r_cmdno, 2832 button, (long)curwin->w_cursor.lnum, col); 2833 nbdebug(("EVT: %s", buf)); 2834 nb_send(buf, "netbeans_button_release"); 2835 } 2836 } 2837 2838 2839 /* 2840 * Send a keypress event back to netbeans. This usually simulates some 2841 * kind of function key press. This function operates on a key code. 2842 * Return TRUE when the key was sent, FALSE when the command has been 2843 * postponed. 2844 */ 2845 int 2846 netbeans_keycommand(int key) 2847 { 2848 char keyName[60]; 2849 2850 netbeans_keyname(key, keyName); 2851 return netbeans_keystring((char_u *)keyName); 2852 } 2853 2854 2855 /* 2856 * Send a keypress event back to netbeans. This usually simulates some 2857 * kind of function key press. This function operates on a key string. 2858 * Return TRUE when the key was sent, FALSE when the command has been 2859 * postponed. 2860 */ 2861 static int 2862 netbeans_keystring(char_u *keyName) 2863 { 2864 char buf[2*MAXPATHL]; 2865 int bufno = nb_getbufno(curbuf); 2866 long off; 2867 char_u *q; 2868 2869 if (!NETBEANS_OPEN) 2870 return TRUE; 2871 2872 if (bufno == -1) 2873 { 2874 nbdebug(("got keycommand for non-NetBeans buffer, opening...\n")); 2875 q = curbuf->b_ffname == NULL ? (char_u *)"" 2876 : nb_quote(curbuf->b_ffname); 2877 if (q == NULL) 2878 return TRUE; 2879 vim_snprintf(buf, sizeof(buf), "0:fileOpened=%d \"%s\" %s %s\n", 0, 2880 q, 2881 "T", /* open in NetBeans */ 2882 "F"); /* modified */ 2883 if (curbuf->b_ffname != NULL) 2884 vim_free(q); 2885 nbdebug(("EVT: %s", buf)); 2886 nb_send(buf, "netbeans_keycommand"); 2887 2888 postpone_keycommand(keyName); 2889 return FALSE; 2890 } 2891 2892 /* sync the cursor position */ 2893 off = pos2off(curbuf, &curwin->w_cursor); 2894 sprintf(buf, "%d:newDotAndMark=%d %ld %ld\n", bufno, r_cmdno, off, off); 2895 nbdebug(("EVT: %s", buf)); 2896 nb_send(buf, "netbeans_keycommand"); 2897 2898 /* To work on Win32 you must apply patch to ExtEditor module 2899 * from ExtEdCaret.java.diff - make EVT_newDotAndMark handler 2900 * more synchronous 2901 */ 2902 2903 /* now send keyCommand event */ 2904 vim_snprintf(buf, sizeof(buf), "%d:keyCommand=%d \"%s\"\n", 2905 bufno, r_cmdno, keyName); 2906 nbdebug(("EVT: %s", buf)); 2907 nb_send(buf, "netbeans_keycommand"); 2908 2909 /* New: do both at once and include the lnum/col. */ 2910 vim_snprintf(buf, sizeof(buf), "%d:keyAtPos=%d \"%s\" %ld %ld/%ld\n", 2911 bufno, r_cmdno, keyName, 2912 off, (long)curwin->w_cursor.lnum, (long)curwin->w_cursor.col); 2913 nbdebug(("EVT: %s", buf)); 2914 nb_send(buf, "netbeans_keycommand"); 2915 return TRUE; 2916 } 2917 2918 2919 /* 2920 * Send a save event to netbeans. 2921 */ 2922 void 2923 netbeans_save_buffer(buf_T *bufp) 2924 { 2925 char_u buf[64]; 2926 int bufno; 2927 nbbuf_T *nbbuf; 2928 2929 if (!NETBEANS_OPEN) 2930 return; 2931 2932 nbbuf = nb_bufp2nbbuf_fire(bufp, &bufno); 2933 if (nbbuf == NULL) 2934 return; 2935 2936 nbbuf->modified = 0; 2937 2938 sprintf((char *)buf, "%d:save=%d\n", bufno, r_cmdno); 2939 nbdebug(("EVT: %s", buf)); 2940 nb_send((char *)buf, "netbeans_save_buffer"); 2941 } 2942 2943 2944 /* 2945 * Send remove command to netbeans (this command has been turned off). 2946 */ 2947 void 2948 netbeans_deleted_all_lines(buf_T *bufp) 2949 { 2950 char_u buf[64]; 2951 int bufno; 2952 nbbuf_T *nbbuf; 2953 2954 if (!NETBEANS_OPEN) 2955 return; 2956 2957 nbbuf = nb_bufp2nbbuf_fire(bufp, &bufno); 2958 if (nbbuf == NULL) 2959 return; 2960 2961 /* Don't mark as modified for initial read */ 2962 if (nbbuf->insertDone) 2963 nbbuf->modified = 1; 2964 2965 sprintf((char *)buf, "%d:remove=%d 0 -1\n", bufno, r_cmdno); 2966 nbdebug(("EVT(suppressed): %s", buf)); 2967 /* nb_send(buf, "netbeans_deleted_all_lines"); */ 2968 } 2969 2970 2971 /* 2972 * See if the lines are guarded. The top and bot parameters are from 2973 * u_savecommon(), these are the line above the change and the line below the 2974 * change. 2975 */ 2976 int 2977 netbeans_is_guarded(linenr_T top, linenr_T bot) 2978 { 2979 signlist_T *p; 2980 int lnum; 2981 2982 if (!NETBEANS_OPEN) 2983 return FALSE; 2984 2985 for (p = curbuf->b_signlist; p != NULL; p = p->next) 2986 if (p->id >= GUARDEDOFFSET) 2987 for (lnum = top + 1; lnum < bot; lnum++) 2988 if (lnum == p->lnum) 2989 return TRUE; 2990 2991 return FALSE; 2992 } 2993 2994 #if defined(FEAT_GUI_X11) || defined(PROTO) 2995 /* 2996 * We have multiple signs to draw at the same location. Draw the 2997 * multi-sign indicator instead. This is the Motif version. 2998 */ 2999 void 3000 netbeans_draw_multisign_indicator(int row) 3001 { 3002 int i; 3003 int y; 3004 int x; 3005 3006 if (!NETBEANS_OPEN) 3007 return; 3008 3009 x = 0; 3010 y = row * gui.char_height + 2; 3011 3012 for (i = 0; i < gui.char_height - 3; i++) 3013 XDrawPoint(gui.dpy, gui.wid, gui.text_gc, x+2, y++); 3014 3015 XDrawPoint(gui.dpy, gui.wid, gui.text_gc, x+0, y); 3016 XDrawPoint(gui.dpy, gui.wid, gui.text_gc, x+2, y); 3017 XDrawPoint(gui.dpy, gui.wid, gui.text_gc, x+4, y++); 3018 XDrawPoint(gui.dpy, gui.wid, gui.text_gc, x+1, y); 3019 XDrawPoint(gui.dpy, gui.wid, gui.text_gc, x+2, y); 3020 XDrawPoint(gui.dpy, gui.wid, gui.text_gc, x+3, y++); 3021 XDrawPoint(gui.dpy, gui.wid, gui.text_gc, x+2, y); 3022 } 3023 #endif /* FEAT_GUI_X11 */ 3024 3025 #if defined(FEAT_GUI_GTK) && !defined(PROTO) 3026 /* 3027 * We have multiple signs to draw at the same location. Draw the 3028 * multi-sign indicator instead. This is the GTK/Gnome version. 3029 */ 3030 void 3031 netbeans_draw_multisign_indicator(int row) 3032 { 3033 int i; 3034 int y; 3035 int x; 3036 #if GTK_CHECK_VERSION(3,0,0) 3037 cairo_t *cr = NULL; 3038 #else 3039 GdkDrawable *drawable = gui.drawarea->window; 3040 #endif 3041 3042 if (!NETBEANS_OPEN) 3043 return; 3044 3045 #if GTK_CHECK_VERSION(3,0,0) 3046 cr = cairo_create(gui.surface); 3047 cairo_set_source_rgba(cr, 3048 gui.fgcolor->red, gui.fgcolor->green, gui.fgcolor->blue, 3049 gui.fgcolor->alpha); 3050 #endif 3051 3052 x = 0; 3053 y = row * gui.char_height + 2; 3054 3055 for (i = 0; i < gui.char_height - 3; i++) 3056 #if GTK_CHECK_VERSION(3,0,0) 3057 cairo_rectangle(cr, x+2, y++, 1, 1); 3058 #else 3059 gdk_draw_point(drawable, gui.text_gc, x+2, y++); 3060 #endif 3061 3062 #if GTK_CHECK_VERSION(3,0,0) 3063 cairo_rectangle(cr, x+0, y, 1, 1); 3064 cairo_rectangle(cr, x+2, y, 1, 1); 3065 cairo_rectangle(cr, x+4, y++, 1, 1); 3066 cairo_rectangle(cr, x+1, y, 1, 1); 3067 cairo_rectangle(cr, x+2, y, 1, 1); 3068 cairo_rectangle(cr, x+3, y++, 1, 1); 3069 cairo_rectangle(cr, x+2, y, 1, 1); 3070 #else 3071 gdk_draw_point(drawable, gui.text_gc, x+0, y); 3072 gdk_draw_point(drawable, gui.text_gc, x+2, y); 3073 gdk_draw_point(drawable, gui.text_gc, x+4, y++); 3074 gdk_draw_point(drawable, gui.text_gc, x+1, y); 3075 gdk_draw_point(drawable, gui.text_gc, x+2, y); 3076 gdk_draw_point(drawable, gui.text_gc, x+3, y++); 3077 gdk_draw_point(drawable, gui.text_gc, x+2, y); 3078 #endif 3079 3080 #if GTK_CHECK_VERSION(3,0,0) 3081 cairo_destroy(cr); 3082 #endif 3083 } 3084 #endif /* FEAT_GUI_GTK */ 3085 3086 /* 3087 * If the mouse is clicked in the gutter of a line with multiple 3088 * annotations, cycle through the set of signs. 3089 */ 3090 void 3091 netbeans_gutter_click(linenr_T lnum) 3092 { 3093 signlist_T *p; 3094 3095 if (!NETBEANS_OPEN) 3096 return; 3097 3098 for (p = curbuf->b_signlist; p != NULL; p = p->next) 3099 { 3100 if (p->lnum == lnum && p->next && p->next->lnum == lnum) 3101 { 3102 signlist_T *tail; 3103 3104 /* remove "p" from list, reinsert it at the tail of the sublist */ 3105 if (p->prev) 3106 p->prev->next = p->next; 3107 else 3108 curbuf->b_signlist = p->next; 3109 p->next->prev = p->prev; 3110 /* now find end of sublist and insert p */ 3111 for (tail = p->next; 3112 tail->next && tail->next->lnum == lnum 3113 && tail->next->id < GUARDEDOFFSET; 3114 tail = tail->next) 3115 ; 3116 /* tail now points to last entry with same lnum (except 3117 * that "guarded" annotations are always last) */ 3118 p->next = tail->next; 3119 if (tail->next) 3120 tail->next->prev = p; 3121 p->prev = tail; 3122 tail->next = p; 3123 update_debug_sign(curbuf, lnum); 3124 break; 3125 } 3126 } 3127 } 3128 3129 /* 3130 * Add a sign of the requested type at the requested location. 3131 * 3132 * Reverse engineering: 3133 * Apparently an annotation is defined the first time it is used in a buffer. 3134 * When the same annotation is used in two buffers, the second time we do not 3135 * need to define a new sign name but reuse the existing one. But since the 3136 * ID number used in the second buffer starts counting at one again, a mapping 3137 * is made from the ID specifically for the buffer to the global sign name 3138 * (which is a number). 3139 * 3140 * globalsignmap[] stores the signs that have been defined globally. 3141 * buf->signmapused[] maps buffer-local annotation IDs to an index in 3142 * globalsignmap[]. 3143 */ 3144 static void 3145 addsigntype( 3146 nbbuf_T *buf, 3147 int typeNum, 3148 char_u *typeName, 3149 char_u *tooltip UNUSED, 3150 char_u *glyphFile, 3151 char_u *fg, 3152 char_u *bg) 3153 { 3154 int i, j; 3155 int use_fg = (*fg && STRCMP(fg, "none") != 0); 3156 int use_bg = (*bg && STRCMP(bg, "none") != 0); 3157 3158 for (i = 0; i < globalsignmapused; i++) 3159 if (STRCMP(typeName, globalsignmap[i]) == 0) 3160 break; 3161 3162 if (i == globalsignmapused) /* not found; add it to global map */ 3163 { 3164 nbdebug(("DEFINEANNOTYPE(%d,%s,%s,%s,%s,%s)\n", 3165 typeNum, typeName, tooltip, glyphFile, fg, bg)); 3166 if (use_fg || use_bg) 3167 { 3168 char fgbuf[2 * (8 + MAX_COLOR_LENGTH) + 1]; 3169 char bgbuf[2 * (8 + MAX_COLOR_LENGTH) + 1]; 3170 char *ptr; 3171 int value; 3172 3173 value = strtol((char *)fg, &ptr, 10); 3174 if (ptr != (char *)fg) 3175 sprintf(fgbuf, "guifg=#%06x", value & 0xFFFFFF); 3176 else 3177 sprintf(fgbuf, "guifg=%s ctermfg=%s", fg, fg); 3178 3179 value = strtol((char *)bg, &ptr, 10); 3180 if (ptr != (char *)bg) 3181 sprintf(bgbuf, "guibg=#%06x", value & 0xFFFFFF); 3182 else 3183 sprintf(bgbuf, "guibg=%s ctermbg=%s", bg, bg); 3184 3185 coloncmd(":highlight NB_%s %s %s", typeName, (use_fg) ? fgbuf : "", 3186 (use_bg) ? bgbuf : ""); 3187 if (*glyphFile == NUL) 3188 /* no glyph, line highlighting only */ 3189 coloncmd(":sign define %d linehl=NB_%s", i + 1, typeName); 3190 else if (vim_strsize(glyphFile) <= 2) 3191 /* one- or two-character glyph name, use as text glyph with 3192 * texthl */ 3193 coloncmd(":sign define %d text=%s texthl=NB_%s", i + 1, 3194 glyphFile, typeName); 3195 else 3196 /* glyph, line highlighting */ 3197 coloncmd(":sign define %d icon=%s linehl=NB_%s", i + 1, 3198 glyphFile, typeName); 3199 } 3200 else 3201 /* glyph, no line highlighting */ 3202 coloncmd(":sign define %d icon=%s", i + 1, glyphFile); 3203 3204 if (STRCMP(typeName,"CurrentPC") == 0) 3205 curPCtype = typeNum; 3206 3207 if (globalsignmapused == globalsignmaplen) 3208 { 3209 if (globalsignmaplen == 0) /* first allocation */ 3210 { 3211 globalsignmaplen = 20; 3212 globalsignmap = ALLOC_CLEAR_MULT(char *, globalsignmaplen); 3213 } 3214 else /* grow it */ 3215 { 3216 int incr; 3217 int oldlen = globalsignmaplen; 3218 char **t_globalsignmap = globalsignmap; 3219 3220 globalsignmaplen *= 2; 3221 incr = globalsignmaplen - oldlen; 3222 globalsignmap = vim_realloc(globalsignmap, 3223 globalsignmaplen * sizeof(char *)); 3224 if (globalsignmap == NULL) 3225 { 3226 vim_free(t_globalsignmap); 3227 globalsignmaplen = 0; 3228 return; 3229 } 3230 vim_memset(globalsignmap + oldlen, 0, incr * sizeof(char *)); 3231 } 3232 } 3233 3234 globalsignmap[i] = (char *)typeName; 3235 globalsignmapused = i + 1; 3236 } 3237 3238 /* check local map; should *not* be found! */ 3239 for (j = 0; j < buf->signmapused; j++) 3240 if (buf->signmap[j] == i + 1) 3241 return; 3242 3243 /* add to local map */ 3244 if (buf->signmapused == buf->signmaplen) 3245 { 3246 if (buf->signmaplen == 0) /* first allocation */ 3247 { 3248 buf->signmaplen = 5; 3249 buf->signmap = ALLOC_CLEAR_MULT(int, buf->signmaplen); 3250 } 3251 else /* grow it */ 3252 { 3253 int incr; 3254 int oldlen = buf->signmaplen; 3255 int *t_signmap = buf->signmap; 3256 3257 buf->signmaplen *= 2; 3258 incr = buf->signmaplen - oldlen; 3259 buf->signmap = vim_realloc(buf->signmap, 3260 buf->signmaplen * sizeof(int)); 3261 if (buf->signmap == NULL) 3262 { 3263 vim_free(t_signmap); 3264 buf->signmaplen = 0; 3265 return; 3266 } 3267 vim_memset(buf->signmap + oldlen, 0, incr * sizeof(int)); 3268 } 3269 } 3270 3271 buf->signmap[buf->signmapused++] = i + 1; 3272 3273 } 3274 3275 3276 /* 3277 * See if we have the requested sign type in the buffer. 3278 */ 3279 static int 3280 mapsigntype(nbbuf_T *buf, int localsigntype) 3281 { 3282 if (--localsigntype >= 0 && localsigntype < buf->signmapused) 3283 return buf->signmap[localsigntype]; 3284 3285 return 0; 3286 } 3287 3288 3289 /* 3290 * Compute length of buffer, don't print anything. 3291 */ 3292 static long 3293 get_buf_size(buf_T *bufp) 3294 { 3295 linenr_T lnum; 3296 long char_count = 0; 3297 int eol_size; 3298 long last_check = 100000L; 3299 3300 if (bufp->b_ml.ml_flags & ML_EMPTY) 3301 return 0; 3302 else 3303 { 3304 if (get_fileformat(bufp) == EOL_DOS) 3305 eol_size = 2; 3306 else 3307 eol_size = 1; 3308 for (lnum = 1; lnum <= bufp->b_ml.ml_line_count; ++lnum) 3309 { 3310 char_count += (long)STRLEN(ml_get_buf(bufp, lnum, FALSE)) 3311 + eol_size; 3312 /* Check for a CTRL-C every 100000 characters */ 3313 if (char_count > last_check) 3314 { 3315 ui_breakcheck(); 3316 if (got_int) 3317 return char_count; 3318 last_check = char_count + 100000L; 3319 } 3320 } 3321 /* Correction for when last line doesn't have an EOL. */ 3322 if (!bufp->b_p_eol && (bufp->b_p_bin || !bufp->b_p_fixeol)) 3323 char_count -= eol_size; 3324 } 3325 3326 return char_count; 3327 } 3328 3329 /* 3330 * Convert character offset to lnum,col 3331 */ 3332 static pos_T * 3333 off2pos(buf_T *buf, long offset) 3334 { 3335 linenr_T lnum; 3336 static pos_T pos; 3337 3338 pos.lnum = 0; 3339 pos.col = 0; 3340 pos.coladd = 0; 3341 3342 if (!(buf->b_ml.ml_flags & ML_EMPTY)) 3343 { 3344 if ((lnum = ml_find_line_or_offset(buf, (linenr_T)0, &offset)) < 0) 3345 return NULL; 3346 pos.lnum = lnum; 3347 pos.col = offset; 3348 } 3349 3350 return &pos; 3351 } 3352 3353 /* 3354 * Convert an argument in the form "1234" to an offset and compute the 3355 * lnum/col from it. Convert an argument in the form "123/12" directly to a 3356 * lnum/col. 3357 * "argp" is advanced to after the argument. 3358 * Return a pointer to the position, NULL if something is wrong. 3359 */ 3360 static pos_T * 3361 get_off_or_lnum(buf_T *buf, char_u **argp) 3362 { 3363 static pos_T mypos; 3364 long off; 3365 3366 off = strtol((char *)*argp, (char **)argp, 10); 3367 if (**argp == '/') 3368 { 3369 mypos.lnum = (linenr_T)off; 3370 ++*argp; 3371 mypos.col = strtol((char *)*argp, (char **)argp, 10); 3372 mypos.coladd = 0; 3373 return &mypos; 3374 } 3375 return off2pos(buf, off); 3376 } 3377 3378 3379 /* 3380 * Convert (lnum,col) to byte offset in the file. 3381 */ 3382 static long 3383 pos2off(buf_T *buf, pos_T *pos) 3384 { 3385 long offset = 0; 3386 3387 if (!(buf->b_ml.ml_flags & ML_EMPTY)) 3388 { 3389 if ((offset = ml_find_line_or_offset(buf, pos->lnum, 0)) < 0) 3390 return 0; 3391 offset += pos->col; 3392 } 3393 3394 return offset; 3395 } 3396 3397 3398 /* 3399 * This message is printed after NetBeans opens a new file. It's 3400 * similar to the message readfile() uses, but since NetBeans 3401 * doesn't normally call readfile, we do our own. 3402 */ 3403 static void 3404 print_read_msg(nbbuf_T *buf) 3405 { 3406 int lnum = buf->bufp->b_ml.ml_line_count; 3407 off_T nchars = buf->bufp->b_orig_size; 3408 char_u c; 3409 3410 msg_add_fname(buf->bufp, buf->bufp->b_ffname); 3411 c = FALSE; 3412 3413 if (buf->bufp->b_p_ro) 3414 { 3415 STRCAT(IObuff, shortmess(SHM_RO) ? _("[RO]") : _("[readonly]")); 3416 c = TRUE; 3417 } 3418 if (!buf->bufp->b_start_eol) 3419 { 3420 STRCAT(IObuff, shortmess(SHM_LAST) ? _("[noeol]") 3421 : _("[Incomplete last line]")); 3422 c = TRUE; 3423 } 3424 msg_add_lines(c, (long)lnum, nchars); 3425 3426 /* Now display it */ 3427 VIM_CLEAR(keep_msg); 3428 msg_scrolled_ign = TRUE; 3429 msg_trunc_attr((char *)IObuff, FALSE, 0); 3430 msg_scrolled_ign = FALSE; 3431 } 3432 3433 3434 /* 3435 * Print a message after NetBeans writes the file. This message should be 3436 * identical to the standard message a non-netbeans user would see when 3437 * writing a file. 3438 */ 3439 static void 3440 print_save_msg(nbbuf_T *buf, off_T nchars) 3441 { 3442 char_u c; 3443 char_u *p; 3444 3445 if (nchars >= 0) 3446 { 3447 /* put fname in IObuff with quotes */ 3448 msg_add_fname(buf->bufp, buf->bufp->b_ffname); 3449 c = FALSE; 3450 3451 msg_add_lines(c, buf->bufp->b_ml.ml_line_count, 3452 buf->bufp->b_orig_size); 3453 3454 VIM_CLEAR(keep_msg); 3455 msg_scrolled_ign = TRUE; 3456 p = (char_u *)msg_trunc_attr((char *)IObuff, FALSE, 0); 3457 if ((msg_scrolled && !need_wait_return) || !buf->initDone) 3458 { 3459 /* Need to repeat the message after redrawing when: 3460 * - When reading from stdin (the screen will be cleared next). 3461 * - When restart_edit is set (otherwise there will be a delay 3462 * before redrawing). 3463 * - When the screen was scrolled but there is no wait-return 3464 * prompt. */ 3465 set_keep_msg(p, 0); 3466 } 3467 msg_scrolled_ign = FALSE; 3468 /* add_to_input_buf((char_u *)"\f", 1); */ 3469 } 3470 else 3471 { 3472 char msgbuf[IOSIZE]; 3473 3474 vim_snprintf(msgbuf, IOSIZE, 3475 _("E505: %s is read-only (add ! to override)"), IObuff); 3476 nbdebug((" %s\n", msgbuf)); 3477 emsg(msgbuf); 3478 } 3479 } 3480 3481 #endif /* defined(FEAT_NETBEANS_INTG) */ 3482