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