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