comparison src/netbeans.c @ 7:3fc0f57ecb91 v7.0001

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