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