blob: 41a562036d51d2567614ad0f9659d5d1475089e7 [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001/* 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. */
65static char *ExtEdProtocolVersion = "2.2";
66
67static long pos2off __ARGS((buf_T *, pos_T *));
68static pos_T *off2pos __ARGS((buf_T *, long));
69static pos_T *get_off_or_lnum __ARGS((buf_T *buf, char_u **argp));
70static long get_buf_size __ARGS((buf_T *));
71
72static void netbeans_connect __ARGS((void));
73static int getConnInfo __ARGS((char *file, char **host, char **port, char **password));
74
75static void nb_init_graphics __ARGS((void));
76static void coloncmd __ARGS((char *cmd, ...));
77#ifdef FEAT_GUI_MOTIF
78static void messageFromNetbeans __ARGS((XtPointer, int *, XtInputId *));
79#endif
80#ifdef FEAT_GUI_GTK
81static void messageFromNetbeans __ARGS((gpointer, gint, GdkInputCondition));
82#endif
83static void nb_parse_cmd __ARGS((char_u *));
84static int nb_do_cmd __ARGS((int, char_u *, int, int, char_u *));
85static void nb_send __ARGS((char *buf, char *fun));
86#ifdef FEAT_BEVAL
87static void netbeans_beval_cb __ARGS((BalloonEval *beval, int state));
88#endif
89
90static int sd = -1; /* socket fd for Netbeans connection */
91#ifdef FEAT_GUI_MOTIF
92static XtInputId inputHandler; /* Cookie for input */
93#endif
94#ifdef FEAT_GUI_GTK
95static gint inputHandler; /* Cookie for input */
96#endif
97#ifdef FEAT_GUI_W32
98static int inputHandler = -1; /* simply ret.value of WSAAsyncSelect() */
99extern HWND s_hwnd; /* Gvim's Window handle */
100#endif
101static int cmdno; /* current command number for reply */
102static int haveConnection = FALSE; /* socket is connected and
103 initialization is done */
104static int oldFire = 1;
105static int exit_delay = 2; /* exit delay in seconds */
106
107#ifdef FEAT_BEVAL
108# if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)
109extern Widget textArea;
110# endif
111BalloonEval *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
124netbeans_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
139netbeans_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
153netbeans_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
180netbeans_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
194netbeans_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
208netbeans_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
229netbeans_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
421theend:
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
435getConnInfo(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
491struct keyqueue
492{
493 int key;
494 struct keyqueue *next;
495 struct keyqueue *prev;
496};
497
498typedef struct keyqueue keyQ_T;
499
500static keyQ_T keyHead; /* dummy node, header for circular queue */
501
502
503/*
504 * Queue up key commands sent from netbeans.
505 */
506 static void
507postpone_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
532handle_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
550struct cmdqueue
551{
552 char_u *buffer;
553 struct cmdqueue *next;
554 struct cmdqueue *prev;
555};
556
557typedef struct cmdqueue queue_T;
558
559static 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
566save(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
622nb_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
695messageFromNetbeansW32()
696#else
697# ifdef FEAT_GUI_MOTIF
698 static void
699messageFromNetbeans(XtPointer clientData, int *unused1, XtInputId *unused2)
700# endif
701# ifdef FEAT_GUI_GTK
702 static void
703messageFromNetbeans(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
777nb_parse_cmd(char_u *cmd)
778{
Bram Moolenaar349b2f62004-10-11 10:00:50 +0000779 char *verb;
780 char *q;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000781 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
Bram Moolenaar349b2f62004-10-11 10:00:50 +0000805 bufno = strtol((char *)cmd, &verb, 10);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000806
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
Bram Moolenaar349b2f62004-10-11 10:00:50 +0000836 cmdno = strtol(q, &q, 10);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000837
Bram Moolenaar349b2f62004-10-11 10:00:50 +0000838 q = (char *)skipwhite((char_u *)q);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000839
Bram Moolenaar349b2f62004-10-11 10:00:50 +0000840 if (nb_do_cmd(bufno, (char_u *)verb, isfunc, cmdno, (char_u *)q) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000841 {
842 nbdebug(("nb_parse_cmd: Command error for \"%s\"\n", cmd));
843 EMSG("E629: bad return from nb_do_cmd");
844 }
845}
846
847struct 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
860typedef struct nbbuf_struct nbbuf_T;
861
862static nbbuf_T *buf_list = 0;
863int buf_list_size = 0; /* size of buf_list */
864int buf_list_used = 0; /* nr of entries in buf_list actually in use */
865
866static char **globalsignmap;
867static int globalsignmaplen;
868static int globalsignmapused;
869
870static int mapsigntype __ARGS((nbbuf_T *, int localsigntype));
871static 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
875static int curPCtype = -1;
876
877/*
878 * Get the Netbeans buffer number for the specified buffer.
879 */
880 static int
881nb_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
895isNetbeansBuffer(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
910isNetbeansModified(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 *
926nb_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
966count_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
982netbeans_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
1018nb_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
1045nb_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
1065nb_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
1086nb_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 *
1105nb_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 *
1144nb_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)
1202static int needupdate = 0;
1203static 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
1212nb_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;
Bram Moolenaar349b2f62004-10-11 10:00:50 +00001224 char *cp; /* for when a char pointer is needed */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001225
1226 nbdebug(("%s %d: (%d) %s %s\n", (func) ? "FUN" : "CMD", cmdno, bufno, cmd,
1227 STRCMP(cmd, "insert") == 0 ? "<text>" : (char *)args));
1228
1229 if (func)
1230 {
1231/* =====================================================================*/
1232 if (streq((char *)cmd, "getModified"))
1233 {
1234 if (buf == NULL || buf->bufp == NULL)
1235 /* Return the number of buffers that are modified. */
1236 nb_reply_nr(cmdno, (long)count_changed_buffers());
1237 else
1238 /* Return whether the buffer is modified. */
1239 nb_reply_nr(cmdno, (long)(buf->bufp->b_changed
1240 || isNetbeansModified(buf->bufp)));
1241/* =====================================================================*/
1242 }
1243 else if (streq((char *)cmd, "saveAndExit"))
1244 {
1245 /* Note: this will exit Vim if successful. */
1246 coloncmd(":confirm qall");
1247
1248 /* We didn't exit: return the number of changed buffers. */
1249 nb_reply_nr(cmdno, (long)count_changed_buffers());
1250/* =====================================================================*/
1251 }
1252 else if (streq((char *)cmd, "getCursor"))
1253 {
1254 char_u text[200];
1255
1256 /* Note: nb_getbufno() may return -1. This indicates the IDE
1257 * didn't assign a number to the current buffer in response to a
1258 * fileOpened event. */
1259 sprintf((char *)text, "%d %ld %d %ld",
1260 nb_getbufno(curbuf),
1261 (long)curwin->w_cursor.lnum,
1262 (int)curwin->w_cursor.col,
1263 pos2off(curbuf, &curwin->w_cursor));
1264 nb_reply_text(cmdno, text);
1265/* =====================================================================*/
1266 }
1267 else if (streq((char *)cmd, "getLength"))
1268 {
1269 long len = 0;
1270
1271 if (buf == NULL || buf->bufp == NULL)
1272 {
1273 nbdebug((" null bufp in getLength"));
1274 EMSG("E632: null bufp in getLength");
1275 retval = FAIL;
1276 }
1277 else
1278 {
1279 len = get_buf_size(buf->bufp);
1280 /* adjust for a partial last line */
1281 if (buf->partial_line != NULL)
1282 {
1283 nbdebug((" Adjusting buffer len for partial last line: %d\n",
1284 STRLEN(buf->partial_line)));
1285 len += STRLEN(buf->partial_line);
1286 }
1287 }
1288 nb_reply_nr(cmdno, len);
1289/* =====================================================================*/
1290 }
1291 else if (streq((char *)cmd, "getText"))
1292 {
1293 long len;
1294 linenr_T nlines;
1295 char_u *text = NULL;
1296 linenr_T lno = 1;
1297 char_u *p;
1298 char_u *line;
1299
1300 if (buf == NULL || buf->bufp == NULL)
1301 {
1302 nbdebug((" null bufp in getText"));
1303 EMSG("E633: null bufp in getText");
1304 retval = FAIL;
1305 }
1306 else
1307 {
1308 len = get_buf_size(buf->bufp);
1309 nlines = buf->bufp->b_ml.ml_line_count;
1310 text = alloc((unsigned)((len > 0)
1311 ? ((len + nlines) * 2) : 4));
1312 if (text == NULL)
1313 {
1314 nbdebug((" nb_do_cmd: getText has null text field\n"));
1315 retval = FAIL;
1316 }
1317 else
1318 {
1319 p = text;
1320 *p++ = '\"';
1321 for (; lno <= nlines ; lno++)
1322 {
1323 line = nb_quote(ml_get_buf(buf->bufp, lno, FALSE));
1324 if (line != NULL)
1325 {
1326 STRCPY(p, line);
1327 p += STRLEN(line);
1328 *p++ = '\\';
1329 *p++ = 'n';
1330 }
1331 vim_free(line);
1332 }
1333 *p++ = '\"';
1334 *p = '\0';
1335 }
1336 }
1337 if (text == NULL)
1338 nb_reply_text(cmdno, (char_u *)"");
1339 else
1340 {
1341 nb_reply_text(cmdno, text);
1342 vim_free(text);
1343 }
1344/* =====================================================================*/
1345 }
1346 else if (streq((char *)cmd, "remove"))
1347 {
1348 long count;
1349 pos_T first, last;
1350 pos_T *pos;
1351 int oldFire = netbeansFireChanges;
1352 int oldSuppress = netbeansSuppressNoLines;
1353 int wasChanged;
1354
1355 if (skip >= SKIP_STOP)
1356 {
1357 nbdebug((" Skipping %s command\n", (char *) cmd));
1358 nb_reply_nil(cmdno);
1359 return OK;
1360 }
1361
1362 if (buf == NULL || buf->bufp == NULL)
1363 {
1364 nbdebug((" null bufp in remove"));
1365 EMSG("E634: null bufp in remove");
1366 retval = FAIL;
1367 }
1368 else
1369 {
1370 netbeansFireChanges = FALSE;
1371 netbeansSuppressNoLines = TRUE;
1372
1373 if (curbuf != buf->bufp)
1374 set_curbuf(buf->bufp, DOBUF_GOTO);
1375 wasChanged = buf->bufp->b_changed;
Bram Moolenaar349b2f62004-10-11 10:00:50 +00001376 cp = (char *)args;
1377 off = strtol(cp, &cp, 10);
1378 count = strtol(cp, &cp, 10);
1379 args = (char_u *)cp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001380 /* delete "count" chars, starting at "off" */
1381 pos = off2pos(buf->bufp, off);
1382 if (!pos)
1383 {
1384 nb_reply_text(cmdno, (char_u *)"!bad position");
1385 netbeansFireChanges = oldFire;
1386 netbeansSuppressNoLines = oldSuppress;
1387 return FAIL;
1388 }
1389 first = *pos;
1390 nbdebug((" FIRST POS: line %d, col %d\n", first.lnum, first.col));
1391 pos = off2pos(buf->bufp, off+count-1);
1392 if (!pos)
1393 {
1394 nb_reply_text(cmdno, (char_u *)"!bad count");
1395 netbeansFireChanges = oldFire;
1396 netbeansSuppressNoLines = oldSuppress;
1397 return FAIL;
1398 }
1399 last = *pos;
1400 nbdebug((" LAST POS: line %d, col %d\n", last.lnum, last.col));
1401 curwin->w_cursor = first;
1402 doupdate = 1;
1403
1404 /* keep part of first line */
1405 if (first.lnum == last.lnum && first.col != last.col)
1406 {
1407 /* deletion is within one line */
1408 char_u *p = ml_get(first.lnum);
1409 mch_memmove(p + first.col, p + last.col + 1, STRLEN(p + last.col) + 1);
1410 nbdebug((" NEW LINE %d: %s\n", first.lnum, p));
1411 ml_replace(first.lnum, p, TRUE);
1412 }
1413
1414 if (first.lnum < last.lnum)
1415 {
1416 int i;
1417
1418 /* delete signs from the lines being deleted */
1419 for (i = first.lnum; i <= last.lnum; i++)
1420 {
1421 int id = buf_findsign_id(buf->bufp, (linenr_T)i);
1422 if (id > 0)
1423 {
1424 nbdebug((" Deleting sign %d on line %d\n", id, i));
1425 buf_delsign(buf->bufp, id);
1426 }
1427 else
1428 nbdebug((" No sign on line %d\n", i));
1429 }
1430
1431 /* delete whole lines */
1432 nbdebug((" Deleting lines %d through %d\n", first.lnum, last.lnum));
1433 del_lines(last.lnum - first.lnum + 1, FALSE);
1434 }
1435 buf->bufp->b_changed = wasChanged; /* logically unchanged */
1436 netbeansFireChanges = oldFire;
1437 netbeansSuppressNoLines = oldSuppress;
1438
1439 u_blockfree(buf->bufp);
1440 u_clearall(buf->bufp);
1441 }
1442 nb_reply_nil(cmdno);
1443/* =====================================================================*/
1444 }
1445 else if (streq((char *)cmd, "insert"))
1446 {
1447 pos_T *pos;
1448 pos_T mypos;
1449 char_u *to_free;
1450 char_u *nl;
1451 int lnum;
1452 pos_T old_w_cursor;
1453 int old_b_changed;
1454
1455 if (skip >= SKIP_STOP)
1456 {
1457 nbdebug((" Skipping %s command\n", (char *) cmd));
1458 nb_reply_nil(cmdno);
1459 return OK;
1460 }
1461
1462 /* get offset */
Bram Moolenaar349b2f62004-10-11 10:00:50 +00001463 cp = (char *)args;
1464 off = strtol(cp, &cp, 10);
1465 args = (char_u *)cp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001466
1467 /* get text to be inserted */
1468 args = skipwhite(args);
1469 args = to_free = (char_u *)nb_unquote(args, NULL);
1470
1471 if (buf == NULL || buf->bufp == NULL)
1472 {
1473 nbdebug((" null bufp in insert"));
1474 EMSG("E635: null bufp in insert");
1475 retval = FAIL;
1476 }
1477 else if (args != NULL)
1478 {
1479 /* We need to detect EOL style
1480 * because addAnno passes char-offset
1481 */
1482 int ff_detected = EOL_UNKNOWN;
1483 int buf_was_empty = (buf->bufp->b_ml.ml_flags & ML_EMPTY);
1484
1485 oldFire = netbeansFireChanges;
1486 netbeansFireChanges = 0;
1487
1488 if (curbuf != buf->bufp)
1489 set_curbuf(buf->bufp, DOBUF_GOTO);
1490 old_b_changed = buf->bufp->b_changed;
1491
1492 if (buf->partial_line != NULL)
1493 {
1494 nbdebug((" Combining with partial line\n"));
1495 off -= STRLEN(buf->partial_line);
1496 pos = off2pos(buf->bufp, off);
1497 if (pos && pos->col != 0)
1498 off -= pos->col; /* want start of line */
1499 buf->partial_line = vim_realloc(buf->partial_line,
1500 STRLEN(buf->partial_line) + STRLEN(args) + 1);
1501 STRCAT(buf->partial_line, args);
1502 vim_free(to_free);
1503 args = buf->partial_line;
1504 buf->partial_line = NULL;
1505 to_free = args;
1506 }
1507 pos = off2pos(buf->bufp, off);
1508 if (pos)
1509 {
1510 if (pos->lnum == 0)
1511 pos->lnum = 1;
1512 nbdebug((" POSITION: line = %d, col = %d\n",
1513 pos->lnum, pos->col));
1514 }
1515 else
1516 {
1517 /* if the given position is not found, assume we want
1518 * the end of the file. See setLocAndSize HACK. */
1519 pos = &mypos;
1520 pos->col = 0;
1521#ifdef FEAT_VIRTUALEDIT
1522 pos->coladd = 0;
1523#endif
1524 pos->lnum = buf->bufp->b_ml.ml_line_count;
1525 nbdebug((" POSITION: line = %d (EOF)\n", pos->lnum));
1526 }
1527 lnum = pos->lnum;
1528 old_w_cursor = curwin->w_cursor;
1529 curwin->w_cursor = *pos;
1530
1531 doupdate = 1;
1532 while (*args)
1533 {
1534 nl = (char_u *)strchr((char *)args, '\n');
1535 if (!nl)
1536 {
1537 nbdebug((" PARTIAL[%d]: %s\n", lnum, args));
1538 break;
1539 }
1540 /* EOL detecting.
1541 * Not sure how to deal with '\n' on Mac
1542 * it will fail already in nl = ... above
1543 */
1544 if (buf_was_empty && /* There is need to detect EOLs */
1545 /* AND: string is empty */
1546 (args == nl
1547 /* OR hasn't '\r' at the end */
1548 || *(nl - 1) != '\r'))
1549 ff_detected = EOL_UNIX;
1550
1551 *nl = '\0';
1552 nbdebug((" INSERT[%d]: %s\n", lnum, args));
1553 ml_append((linenr_T)(lnum++ - 1), args,
1554 STRLEN(args) + 1, FALSE);
1555 args = nl + 1;
1556 }
1557
1558 appended_lines_mark(pos->lnum - 1, lnum - pos->lnum);
1559
1560 /* We can change initial ff without consequences
1561 * Isn't it a kind of hacking?
1562 */
1563 if (buf_was_empty)
1564 {
1565 if (ff_detected == EOL_UNKNOWN)
1566 ff_detected = EOL_DOS;
1567 set_fileformat(ff_detected, OPT_LOCAL);
1568 buf->bufp->b_start_ffc = *buf->bufp->b_p_ff;
1569 }
1570
1571 if (*args)
1572 {
1573 /*
1574 * Incomplete line, squirrel away and wait for next insert.
1575 */
1576 nbdebug((" PARTIAL-SAVED: %s\n", args));
1577 buf->partial_line = vim_realloc(buf->partial_line,
1578 STRLEN(args) + 1);
1579 STRCPY(buf->partial_line, args);
1580 }
1581 curwin->w_cursor = old_w_cursor;
1582
1583 /*
1584 * XXX - GRP - Is the next line right? If I've inserted
1585 * text the buffer has been updated but not written. Will
1586 * netbeans guarantee to write it? Even if I do a :q! ?
1587 */
1588 buf->bufp->b_changed = old_b_changed; /* logically unchanged */
1589 netbeansFireChanges = oldFire;
1590
1591 u_blockfree(buf->bufp);
1592 u_clearall(buf->bufp);
1593 }
1594 vim_free(to_free);
1595 nb_reply_nil(cmdno); /* or !error */
1596 }
1597 else
1598 {
1599 nbdebug(("UNIMPLEMENTED FUNCTION: %s\n", cmd));
1600 nb_reply_nil(cmdno);
1601 retval = FAIL;
1602 }
1603 }
1604 else /* Not a function; no reply required. */
1605 {
1606/* =====================================================================*/
1607 if (streq((char *)cmd, "create"))
1608 {
1609 /* Create a buffer without a name. */
1610 if (buf == NULL)
1611 {
1612 EMSG("E636: null buf in create");
1613 return FAIL;
1614 }
1615 vim_free(buf->displayname);
1616 buf->displayname = NULL;
1617 nbdebug((" CREATE %d\n", bufno));
1618
1619 netbeansReadFile = 0; /* don't try to open disk file */
1620 do_ecmd(0, NULL, 0, 0, ECMD_ONE, ECMD_HIDE + ECMD_OLDBUF);
1621 netbeansReadFile = 1;
1622 buf->bufp = curbuf;
1623 maketitle();
1624 gui_update_menus(0);
1625/* =====================================================================*/
1626 }
1627 else if (streq((char *)cmd, "startDocumentListen"))
1628 {
1629 if (buf == NULL)
1630 {
1631 EMSG("E637: null buf in startDocumentListen");
1632 return FAIL;
1633 }
1634 buf->fireChanges = 1;
1635/* =====================================================================*/
1636 }
1637 else if (streq((char *)cmd, "stopDocumentListen"))
1638 {
1639 if (buf == NULL)
1640 {
1641 EMSG("E638: null buf in stopDocumentListen");
1642 return FAIL;
1643 }
1644 buf->fireChanges = 0;
1645 if (buf->bufp != NULL
1646 && buf->bufp->b_was_netbeans_file
1647 && !buf->bufp->b_netbeans_file)
1648 EMSGN(_("E658: NetBeans connection lost for buffer %ld"),
1649 buf->bufp->b_fnum);
1650/* =====================================================================*/
1651 }
1652 else if (streq((char *)cmd, "setTitle"))
1653 {
1654 if (buf == NULL)
1655 {
1656 EMSG("E639: null buf in setTitle");
1657 return FAIL;
1658 }
1659 vim_free(buf->displayname);
1660 buf->displayname = nb_unquote(args, NULL);
1661 nbdebug((" SETTITLE %d %s\n", bufno, buf->displayname));
1662/* =====================================================================*/
1663 }
1664 else if (streq((char *)cmd, "initDone"))
1665 {
1666 if (buf == NULL || buf->bufp == NULL)
1667 {
1668 EMSG("E640: null buf in initDone");
1669 return FAIL;
1670 }
1671 doupdate = 1;
1672 buf->initDone = 1;
1673 if (curbuf != buf->bufp)
1674 set_curbuf(buf->bufp, DOBUF_GOTO);
1675#if defined(FEAT_AUTOCMD)
1676 apply_autocmds(EVENT_BUFREADPOST, 0, 0, FALSE, buf->bufp);
1677#endif
1678
1679 /* handle any postponed key commands */
1680 handle_key_queue();
1681/* =====================================================================*/
1682 }
1683 else if (streq((char *)cmd, "setBufferNumber")
1684 || streq((char *)cmd, "putBufferNumber"))
1685 {
1686 char_u *to_free;
1687 buf_T *bufp;
1688
1689 if (buf == NULL)
1690 {
1691 EMSG("E641: null buf in setBufferNumber");
1692 return FAIL;
1693 }
1694 to_free = (char_u *)nb_unquote(args, NULL);
1695 if (to_free == NULL)
1696 return FAIL;
1697 bufp = buflist_findname(to_free);
1698 vim_free(to_free);
1699 if (bufp == NULL)
1700 {
1701 EMSG2("E642: File %s not found in setBufferNumber", args);
1702 return FAIL;
1703 }
1704 buf->bufp = bufp;
1705
1706 /* "setBufferNumber" has the side effect of jumping to the buffer
1707 * (don't know why!). Don't do that for "putBufferNumber". */
1708 if (*cmd != 'p')
1709 coloncmd(":buffer %d", bufp->b_fnum);
1710 else
1711 {
1712 buf->initDone = 1;
1713
1714 /* handle any postponed key commands */
1715 handle_key_queue();
1716 }
1717
1718#if 0 /* never used */
1719 buf->internalname = (char *)alloc_clear(8);
1720 sprintf(buf->internalname, "<%d>", bufno);
1721 buf->netbeansOwns = 0;
1722#endif
1723/* =====================================================================*/
1724 }
1725 else if (streq((char *)cmd, "setFullName"))
1726 {
1727 if (buf == NULL)
1728 {
1729 EMSG("E643: null buf in setFullName");
1730 return FAIL;
1731 }
1732 vim_free(buf->displayname);
1733 buf->displayname = nb_unquote(args, NULL);
1734 nbdebug((" SETFULLNAME %d %s\n", bufno, buf->displayname));
1735
1736 netbeansReadFile = 0; /* don't try to open disk file */
1737 do_ecmd(0, (char_u *)buf->displayname, 0, 0, ECMD_ONE,
1738 ECMD_HIDE + ECMD_OLDBUF);
1739 netbeansReadFile = 1;
1740 buf->bufp = curbuf;
1741 maketitle();
1742 gui_update_menus(0);
1743/* =====================================================================*/
1744 }
1745 else if (streq((char *)cmd, "editFile"))
1746 {
1747 if (buf == NULL)
1748 {
1749 EMSG("E644: null buf in editFile");
1750 return FAIL;
1751 }
1752 /* Edit a file: like create + setFullName + read the file. */
1753 vim_free(buf->displayname);
1754 buf->displayname = nb_unquote(args, NULL);
1755 nbdebug((" EDITFILE %d %s\n", bufno, buf->displayname));
1756 do_ecmd(0, (char_u *)buf->displayname, NULL, NULL, ECMD_ONE,
1757 ECMD_HIDE + ECMD_OLDBUF);
1758 buf->bufp = curbuf;
1759 buf->initDone = 1;
1760 doupdate = 1;
1761#if defined(FEAT_TITLE)
1762 maketitle();
1763#endif
1764 gui_update_menus(0);
1765/* =====================================================================*/
1766 }
1767 else if (streq((char *)cmd, "setVisible"))
1768 {
1769 if (buf == NULL || buf->bufp == NULL)
1770 {
1771/* EMSG("E645: null bufp in setVisible"); */
1772 return FAIL;
1773 }
1774 if (streq((char *)args, "T"))
1775 {
1776 exarg_T exarg;
1777 exarg.cmd = (char_u *)"goto";
1778 exarg.forceit = FALSE;
1779 goto_buffer(&exarg, DOBUF_FIRST, FORWARD, buf->bufp->b_fnum);
1780 doupdate = 1;
1781
1782 /* Side effect!!!. */
1783 if (!gui.starting)
1784 gui_mch_set_foreground();
1785 }
1786 else
1787 {
1788 /* bury the buffer - not yet */
1789 }
1790/* =====================================================================*/
1791 }
1792 else if (streq((char *)cmd, "raise"))
1793 {
1794 /* Bring gvim to the foreground. */
1795 if (!gui.starting)
1796 gui_mch_set_foreground();
1797/* =====================================================================*/
1798 }
1799 else if (streq((char *)cmd, "setModified"))
1800 {
1801 if (buf == NULL || buf->bufp == NULL)
1802 {
1803/* EMSG("E646: null bufp in setModified"); */
1804 return FAIL;
1805 }
1806 if (streq((char *)args, "T"))
1807 buf->bufp->b_changed = 1;
1808 else
1809 {
1810 struct stat st;
1811
1812 /* Assume NetBeans stored the file. Reset the timestamp to
1813 * avoid "file changed" warnings. */
1814 if (buf->bufp->b_ffname != NULL
1815 && mch_stat((char *)buf->bufp->b_ffname, &st) >= 0)
1816 buf_store_time(buf->bufp, &st, buf->bufp->b_ffname);
1817 buf->bufp->b_changed = 0;
1818 }
1819 buf->modified = buf->bufp->b_changed;
1820/* =====================================================================*/
1821 }
1822 else if (streq((char *)cmd, "setMark"))
1823 {
1824 /* not yet */
1825/* =====================================================================*/
1826 }
1827 else if (streq((char *)cmd, "showBalloon"))
1828 {
1829#if defined(FEAT_BEVAL)
1830 static char *text = NULL;
1831
1832 /*
1833 * Set up the Balloon Expression Evaluation area.
1834 * Ignore 'ballooneval' here.
1835 * The text pointer must remain valid for a while.
1836 */
1837 if (balloonEval != NULL)
1838 {
1839 vim_free(text);
1840 text = nb_unquote(args, NULL);
1841 if (text != NULL)
1842 gui_mch_post_balloon(balloonEval, (char_u *)text);
1843 }
1844#endif
1845/* =====================================================================*/
1846 }
1847 else if (streq((char *)cmd, "setDot"))
1848 {
1849 pos_T *pos;
1850#ifdef NBDEBUG
1851 char_u *s;
1852#endif
1853
1854 if (buf == NULL || buf->bufp == NULL)
1855 {
1856 EMSG("E647: null bufp in setDot");
1857 return FAIL;
1858 }
1859
1860 if (curbuf != buf->bufp)
1861 set_curbuf(buf->bufp, DOBUF_GOTO);
1862#ifdef FEAT_VISUAL
1863 /* Don't want Visual mode now. */
1864 if (VIsual_active)
1865 end_visual_mode();
1866#endif
1867#ifdef NBDEBUG
1868 s = args;
1869#endif
1870 pos = get_off_or_lnum(buf->bufp, &args);
1871 if (pos)
1872 {
1873 curwin->w_cursor = *pos;
1874 check_cursor();
1875#ifdef FEAT_FOLDING
1876 foldOpenCursor();
1877#endif
1878 }
1879 else
1880 nbdebug((" BAD POSITION in setDot: %s\n", s));
1881
1882 /* gui_update_cursor(TRUE, FALSE); */
1883 /* update_curbuf(NOT_VALID); */
1884 update_topline(); /* scroll to show the line */
1885 update_screen(VALID);
1886 setcursor();
1887 out_flush();
1888 gui_update_cursor(TRUE, FALSE);
1889 gui_mch_flush();
1890 /* Quit a hit-return or more prompt. */
1891 if (State == HITRETURN || State == ASKMORE)
1892 {
1893 add_to_input_buf((char_u *)"\003", 1);
1894#ifdef FEAT_GUI_GTK
1895 if (gtk_main_level() > 0)
1896 gtk_main_quit();
1897#endif
1898 }
1899/* =====================================================================*/
1900 }
1901 else if (streq((char *)cmd, "close"))
1902 {
1903#ifdef NBDEBUG
1904 char *name = "<NONE>";
1905#endif
1906
1907 if (buf == NULL)
1908 {
1909 EMSG("E648: null buf in close");
1910 return FAIL;
1911 }
1912
1913#ifdef NBDEBUG
1914 if (buf->displayname != NULL)
1915 name = buf->displayname;
1916#endif
1917/* if (buf->bufp == NULL) */
1918/* EMSG("E649: null bufp in close"); */
1919 nbdebug((" CLOSE %d: %s\n", bufno, name));
1920 need_mouse_correct = TRUE;
1921 if (buf->bufp != NULL)
1922 do_buffer(DOBUF_WIPE, DOBUF_FIRST, FORWARD,
1923 buf->bufp->b_fnum, TRUE);
1924 doupdate = 1;
1925/* =====================================================================*/
1926 }
1927 else if (streq((char *)cmd, "setStyle")) /* obsolete... */
1928 {
1929 nbdebug((" setStyle is obsolete!"));
1930/* =====================================================================*/
1931 }
1932 else if (streq((char *)cmd, "setExitDelay"))
1933 {
1934 /* New in version 2.1. */
Bram Moolenaar349b2f62004-10-11 10:00:50 +00001935 cp = (char *)args;
1936 exit_delay = strtol(cp, &cp, 10);
1937 args = (char_u *)cp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001938/* =====================================================================*/
1939 }
1940 else if (streq((char *)cmd, "defineAnnoType"))
1941 {
1942#ifdef FEAT_SIGNS
1943 int typeNum;
1944 char_u *typeName;
1945 char_u *tooltip;
1946 char_u *p;
1947 char_u *glyphFile;
1948 int use_fg = 0;
1949 int use_bg = 0;
1950 int fg = -1;
1951 int bg = -1;
1952
1953 if (buf == NULL)
1954 {
1955 EMSG("E650: null buf in defineAnnoType");
1956 return FAIL;
1957 }
1958
Bram Moolenaar349b2f62004-10-11 10:00:50 +00001959 cp = (char *)args;
1960 typeNum = strtol(cp, &cp, 10);
1961 args = (char_u *)cp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001962 args = skipwhite(args);
1963 typeName = (char_u *)nb_unquote(args, &args);
1964 args = skipwhite(args + 1);
1965 tooltip = (char_u *)nb_unquote(args, &args);
1966 args = skipwhite(args + 1);
1967
1968 p = (char_u *)nb_unquote(args, &args);
1969 glyphFile = vim_strsave_escaped(p, escape_chars);
1970 vim_free(p);
1971
1972 args = skipwhite(args + 1);
1973 if (STRNCMP(args, "none", 4) == 0)
1974 args += 5;
1975 else
1976 {
1977 use_fg = 1;
Bram Moolenaar349b2f62004-10-11 10:00:50 +00001978 cp = (char *)args;
1979 fg = strtol(cp, &cp, 10);
1980 args = (char_u *)cp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001981 }
1982 if (STRNCMP(args, "none", 4) == 0)
1983 args += 5;
1984 else
1985 {
1986 use_bg = 1;
Bram Moolenaar349b2f62004-10-11 10:00:50 +00001987 cp = (char *)args;
1988 bg = strtol(cp, &cp, 10);
1989 args = (char_u *)cp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001990 }
1991 if (typeName != NULL && tooltip != NULL && glyphFile != NULL)
1992 addsigntype(buf, typeNum, typeName, tooltip, glyphFile,
1993 use_fg, fg, use_bg, bg);
1994 else
1995 vim_free(typeName);
1996
1997 /* don't free typeName; it's used directly in addsigntype() */
1998 vim_free(tooltip);
1999 vim_free(glyphFile);
2000
2001#endif
2002/* =====================================================================*/
2003 }
2004 else if (streq((char *)cmd, "addAnno"))
2005 {
2006#ifdef FEAT_SIGNS
2007 int serNum;
2008 int localTypeNum;
2009 int typeNum;
2010# ifdef NBDEBUG
2011 int len;
2012# endif
2013 pos_T *pos;
2014
2015 if (buf == NULL || buf->bufp == NULL)
2016 {
2017 EMSG("E651: null bufp in addAnno");
2018 return FAIL;
2019 }
2020
2021 doupdate = 1;
2022
Bram Moolenaar349b2f62004-10-11 10:00:50 +00002023 cp = (char *)args;
2024 serNum = strtol(cp, &cp, 10);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002025
2026 /* Get the typenr specific for this buffer and convert it to
2027 * the global typenumber, as used for the sign name. */
Bram Moolenaar349b2f62004-10-11 10:00:50 +00002028 localTypeNum = strtol(cp, &cp, 10);
2029 args = (char_u *)cp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002030 typeNum = mapsigntype(buf, localTypeNum);
2031
2032 pos = get_off_or_lnum(buf->bufp, &args);
2033
Bram Moolenaar349b2f62004-10-11 10:00:50 +00002034 cp = (char *)args;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002035# ifdef NBDEBUG
2036 len =
2037# endif
Bram Moolenaar349b2f62004-10-11 10:00:50 +00002038 strtol(cp, &cp, 10);
2039 args = (char_u *)cp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002040# ifdef NBDEBUG
2041 if (len != -1)
2042 {
2043 nbdebug((" partial line annotation -- Not Yet Implemented!"));
2044 }
2045# endif
2046 if (serNum >= GUARDEDOFFSET)
2047 {
2048 nbdebug((" too many annotations! ignoring..."));
2049 return FAIL;
2050 }
2051 if (pos)
2052 {
2053 coloncmd(":sign place %d line=%d name=%d buffer=%d",
2054 serNum, pos->lnum, typeNum, buf->bufp->b_fnum);
2055 if (typeNum == curPCtype)
2056 coloncmd(":sign jump %d buffer=%d", serNum,
2057 buf->bufp->b_fnum);
2058 }
2059 /* XXX only redraw what changed. */
2060 redraw_later(CLEAR);
2061#endif
2062/* =====================================================================*/
2063 }
2064 else if (streq((char *)cmd, "removeAnno"))
2065 {
2066#ifdef FEAT_SIGNS
2067 int serNum;
2068
2069 if (buf == NULL || buf->bufp == NULL)
2070 {
2071 nbdebug((" null bufp in removeAnno"));
2072 return FAIL;
2073 }
2074 doupdate = 1;
Bram Moolenaar349b2f62004-10-11 10:00:50 +00002075 cp = (char *)args;
2076 serNum = strtol(cp, &cp, 10);
2077 args = (char_u *)cp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002078 coloncmd(":sign unplace %d buffer=%d",
2079 serNum, buf->bufp->b_fnum);
2080 redraw_buf_later(buf->bufp, NOT_VALID);
2081#endif
2082/* =====================================================================*/
2083 }
2084 else if (streq((char *)cmd, "moveAnnoToFront"))
2085 {
2086#ifdef FEAT_SIGNS
2087 nbdebug((" moveAnnoToFront: Not Yet Implemented!"));
2088#endif
2089/* =====================================================================*/
2090 }
2091 else if (streq((char *)cmd, "guard") || streq((char *)cmd, "unguard"))
2092 {
2093 int len;
2094 pos_T first;
2095 pos_T last;
2096 pos_T *pos;
2097 int un = (cmd[0] == 'u');
2098 static int guardId = GUARDEDOFFSET;
2099
2100 if (skip >= SKIP_STOP)
2101 {
2102 nbdebug((" Skipping %s command\n", (char *) cmd));
2103 return OK;
2104 }
2105
2106 nb_init_graphics();
2107
2108 if (buf == NULL || buf->bufp == NULL)
2109 {
2110 nbdebug((" null bufp in %s command", cmd));
2111 return FAIL;
2112 }
2113 if (curbuf != buf->bufp)
2114 set_curbuf(buf->bufp, DOBUF_GOTO);
Bram Moolenaar349b2f62004-10-11 10:00:50 +00002115 cp = (char *)args;
2116 off = strtol(cp, &cp, 10);
2117 len = strtol(cp, NULL, 10);
2118 args = (char_u *)cp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002119 pos = off2pos(buf->bufp, off);
2120 doupdate = 1;
2121 if (!pos)
2122 nbdebug((" no such start pos in %s, %ld\n", cmd, off));
2123 else
2124 {
2125 first = *pos;
2126 pos = off2pos(buf->bufp, off + len - 1);
2127 if (pos != NULL && pos->col == 0) {
2128 /*
2129 * In Java Swing the offset is a position between 2
2130 * characters. If col == 0 then we really want the
2131 * previous line as the end.
2132 */
2133 pos = off2pos(buf->bufp, off + len - 2);
2134 }
2135 if (!pos)
2136 nbdebug((" no such end pos in %s, %ld\n",
2137 cmd, off + len - 1));
2138 else
2139 {
2140 long lnum;
2141 last = *pos;
2142 /* set highlight for region */
2143 nbdebug((" %sGUARD %ld,%d to %ld,%d\n", (un) ? "UN" : "",
2144 first.lnum, first.col,
2145 last.lnum, last.col));
2146#ifdef FEAT_SIGNS
2147 for (lnum = first.lnum; lnum <= last.lnum; lnum++)
2148 {
2149 if (un)
2150 {
2151 /* never used */
2152 }
2153 else
2154 {
2155 if (buf_findsigntype_id(buf->bufp, lnum,
2156 GUARDED) == 0)
2157 {
2158 coloncmd(
2159 ":sign place %d line=%d name=%d buffer=%d",
2160 guardId++, lnum, GUARDED,
2161 buf->bufp->b_fnum);
2162 }
2163 }
2164 }
2165#endif
2166 redraw_buf_later(buf->bufp, NOT_VALID);
2167 }
2168 }
2169/* =====================================================================*/
2170 }
2171 else if (streq((char *)cmd, "startAtomic"))
2172 {
2173 inAtomic = 1;
2174/* =====================================================================*/
2175 }
2176 else if (streq((char *)cmd, "endAtomic"))
2177 {
2178 inAtomic = 0;
2179 if (needupdate)
2180 {
2181 doupdate = 1;
2182 needupdate = 0;
2183 }
2184/* =====================================================================*/
2185 }
2186 else if (streq((char *)cmd, "save"))
2187 {
2188 if (buf == NULL || buf->bufp == NULL)
2189 {
2190 nbdebug((" null bufp in %s command", cmd));
2191 return FAIL;
2192 }
2193
2194 /* the following is taken from ex_cmds.c (do_wqall function) */
2195 if (bufIsChanged(buf->bufp))
2196 {
2197 /* Only write if the buffer can be written. */
2198 if (p_write
2199 && !buf->bufp->b_p_ro
2200 && buf->bufp->b_ffname != NULL
2201#ifdef FEAT_QUICKFIX
2202 && !bt_dontwrite(buf->bufp)
2203#endif
2204 )
2205 {
2206 buf_write_all(buf->bufp, FALSE);
2207#ifdef FEAT_AUTOCMD
2208 /* an autocommand may have deleted the buffer */
2209 if (!buf_valid(buf->bufp))
2210 buf->bufp = NULL;
2211#endif
2212 }
2213 }
2214/* =====================================================================*/
2215 }
2216 else if (streq((char *)cmd, "netbeansBuffer"))
2217 {
2218 if (buf == NULL || buf->bufp == NULL)
2219 {
2220 nbdebug((" null bufp in %s command", cmd));
2221 return FAIL;
2222 }
2223 if (*args == 'T')
2224 {
2225 buf->bufp->b_netbeans_file = TRUE;
2226 buf->bufp->b_was_netbeans_file = TRUE;
2227 }
2228 else
2229 buf->bufp->b_netbeans_file = FALSE;
2230/* =====================================================================*/
2231 }
2232 else if (streq((char *)cmd, "version"))
2233 {
2234 nbdebug((" Version = %s\n", (char *) args));
2235 }
2236 /*
2237 * Unrecognized command is ignored.
2238 */
2239 }
2240 if (inAtomic && doupdate)
2241 {
2242 needupdate = 1;
2243 doupdate = 0;
2244 }
2245
2246 if (buf != NULL && buf->initDone && doupdate)
2247 {
2248 update_screen(NOT_VALID);
2249 setcursor();
2250 out_flush();
2251 gui_update_cursor(TRUE, FALSE);
2252 gui_mch_flush();
2253 /* Quit a hit-return or more prompt. */
2254 if (State == HITRETURN || State == ASKMORE)
2255 {
2256 add_to_input_buf((char_u *)"\003", 1);
2257#ifdef FEAT_GUI_GTK
2258 if (gtk_main_level() > 0)
2259 gtk_main_quit();
2260#endif
2261 }
2262 }
2263
2264 return retval;
2265}
2266
2267
2268/*
2269 * Process a vim colon command.
2270 */
2271 static void
2272coloncmd(char *cmd, ...)
2273{
2274 char buf[1024];
2275 va_list ap;
2276
2277 va_start(ap, cmd);
2278 vsprintf(buf, cmd, ap);
2279 va_end(ap);
2280
2281 nbdebug((" COLONCMD %s\n", buf));
2282
2283/* ALT_INPUT_LOCK_ON; */
2284 do_cmdline((char_u *)buf, NULL, NULL, DOCMD_NOWAIT | DOCMD_KEYTYPED);
2285/* ALT_INPUT_LOCK_OFF; */
2286
2287 setcursor(); /* restore the cursor position */
2288 out_flush(); /* make sure output has been written */
2289
2290 gui_update_cursor(TRUE, FALSE);
2291 gui_mch_flush();
2292}
2293
2294
2295/*
2296 * Initialize highlights and signs for use by netbeans (mostly obsolete)
2297 */
2298 static void
2299nb_init_graphics(void)
2300{
2301 static int did_init = FALSE;
2302
2303 if (!did_init)
2304 {
2305 coloncmd(":highlight NBGuarded guibg=Cyan guifg=Black");
2306 coloncmd(":sign define %d linehl=NBGuarded", GUARDED);
2307
2308 did_init = TRUE;
2309 }
2310}
2311
2312/*
2313 * Convert key to netbeans name.
2314 */
2315 static void
2316netbeans_keyname(int key, char *buf)
2317{
2318 char *name = 0;
2319 char namebuf[2];
2320 int ctrl = 0;
2321 int shift = 0;
2322 int alt = 0;
2323
2324 if (mod_mask & MOD_MASK_CTRL)
2325 ctrl = 1;
2326 if (mod_mask & MOD_MASK_SHIFT)
2327 shift = 1;
2328 if (mod_mask & MOD_MASK_ALT)
2329 alt = 1;
2330
2331
2332 switch (key)
2333 {
2334 case K_F1: name = "F1"; break;
2335 case K_S_F1: name = "F1"; shift = 1; break;
2336 case K_F2: name = "F2"; break;
2337 case K_S_F2: name = "F2"; shift = 1; break;
2338 case K_F3: name = "F3"; break;
2339 case K_S_F3: name = "F3"; shift = 1; break;
2340 case K_F4: name = "F4"; break;
2341 case K_S_F4: name = "F4"; shift = 1; break;
2342 case K_F5: name = "F5"; break;
2343 case K_S_F5: name = "F5"; shift = 1; break;
2344 case K_F6: name = "F6"; break;
2345 case K_S_F6: name = "F6"; shift = 1; break;
2346 case K_F7: name = "F7"; break;
2347 case K_S_F7: name = "F7"; shift = 1; break;
2348 case K_F8: name = "F8"; break;
2349 case K_S_F8: name = "F8"; shift = 1; break;
2350 case K_F9: name = "F9"; break;
2351 case K_S_F9: name = "F9"; shift = 1; break;
2352 case K_F10: name = "F10"; break;
2353 case K_S_F10: name = "F10"; shift = 1; break;
2354 case K_F11: name = "F11"; break;
2355 case K_S_F11: name = "F11"; shift = 1; break;
2356 case K_F12: name = "F12"; break;
2357 case K_S_F12: name = "F12"; shift = 1; break;
2358 default:
2359 if (key >= ' ' && key <= '~')
2360 {
2361 /* Allow ASCII characters. */
2362 name = namebuf;
2363 namebuf[0] = key;
2364 namebuf[1] = NUL;
2365 }
2366 else
2367 name = "X";
2368 break;
2369 }
2370
2371 buf[0] = '\0';
2372 if (ctrl)
2373 strcat(buf, "C");
2374 if (shift)
2375 strcat(buf, "S");
2376 if (alt)
2377 strcat(buf, "M"); /* META */
2378 if (ctrl || shift || alt)
2379 strcat(buf, "-");
2380 strcat(buf, name);
2381}
2382
2383#ifdef FEAT_BEVAL
2384/*
2385 * Function to be called for balloon evaluation. Grabs the text under the
2386 * cursor and sends it to the debugger for evaluation. The debugger should
2387 * respond with a showBalloon command when there is a useful result.
2388 */
2389/*ARGSUSED*/
2390 static void
2391netbeans_beval_cb(
2392 BalloonEval *beval,
2393 int state)
2394{
2395 char_u *filename;
2396 char_u *text;
2397 int line;
2398 int col;
2399 char buf[MAXPATHL * 2 + 25];
2400 char_u *p;
2401
2402 /* Don't do anything when 'ballooneval' is off, messages scrolled the
2403 * windows up or we have no connection. */
2404 if (!p_beval || msg_scrolled > 0 || !haveConnection)
2405 return;
2406
2407 if (gui_mch_get_beval_info(beval, &filename, &line, &text, &col) == OK)
2408 {
2409 /* Send debugger request. Only when the text is of reasonable
2410 * length. */
2411 if (text != NULL && text[0] != NUL && STRLEN(text) < MAXPATHL)
2412 {
2413 p = nb_quote(text);
2414 if (p != NULL)
2415 sprintf(buf, "0:balloonText=%d \"%s\"\n", cmdno, p);
2416 vim_free(p);
2417 nbdebug(("EVT: %s", buf));
2418 nb_send(buf, "netbeans_beval_cb");
2419 }
2420 vim_free(text);
2421 }
2422}
2423#endif
2424
2425/*
2426 * Tell netbeans that the window was opened, ready for commands.
2427 */
2428 void
2429netbeans_startup_done(void)
2430{
2431 char *cmd = "0:startupDone=0\n";
2432
2433 if (!haveConnection)
2434 return;
2435
2436 nbdebug(("EVT: %s", cmd));
2437 nb_send(cmd, "netbeans_startup_done");
2438
2439#ifdef FEAT_BEVAL
2440# ifdef FEAT_GUI_MOTIF
2441 if (gui.in_use)
2442 {
2443 /*
2444 * Set up the Balloon Expression Evaluation area for Motif.
2445 * GTK can do it earlier...
2446 * Always create it but disable it when 'ballooneval' isn't set.
2447 */
2448 balloonEval = gui_mch_create_beval_area(textArea, NULL,
2449 &netbeans_beval_cb, NULL);
2450 if (!p_beval)
2451 gui_mch_disable_beval_area(balloonEval);
2452 }
2453# else
2454# if defined(FEAT_GUI_W32) && defined(FEAT_BEVAL)
2455 balloonEval = gui_mch_create_beval_area(NULL, NULL,
2456 &netbeans_beval_cb, NULL);
2457 if (!p_beval)
2458 gui_mch_disable_beval_area(balloonEval);
2459# endif
2460# endif
2461#endif
2462}
2463
2464#if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_W32) || defined(PROTO)
2465/*
2466 * Tell netbeans that the window was moved or resized.
2467 */
2468 void
2469netbeans_frame_moved(int new_x, int new_y)
2470{
2471 char buf[128];
2472
2473 if (!haveConnection)
2474 return;
2475
2476 sprintf(buf, "0:geometry=%d %d %d %d %d\n",
2477 cmdno, (int)Columns, (int)Rows, new_x, new_y);
2478 nbdebug(("EVT: %s", buf));
2479 nb_send(buf, "netbeans_frame_moved");
2480}
2481#endif
2482
2483/*
2484 * Tell netbeans the user opened a file.
2485 */
2486 void
2487netbeans_file_opened(char *filename)
2488{
2489 char buffer[2*MAXPATHL];
2490 char_u *q;
2491
2492 if (!haveConnection)
2493 return;
2494
2495 q = nb_quote((char_u *)filename);
2496 if (q == NULL)
2497 return;
2498 sprintf(buffer, "0:fileOpened=%d \"%s\" %s %s\n",
2499 0,
2500 (char *)q,
2501 "T", /* open in NetBeans */
2502 "F"); /* modified */
2503
2504 vim_free(q);
2505 nbdebug(("EVT: %s", buffer));
2506
2507 nb_send(buffer, "netbeans_file_opened");
2508 if (p_acd && vim_chdirfile((char_u *)filename) == OK)
2509 shorten_fnames(TRUE);
2510}
2511
2512/*
2513 * Tell netbeans a file was closed.
2514 */
2515 void
2516netbeans_file_closed(buf_T *bufp)
2517{
2518 int bufno = nb_getbufno(bufp);
2519 nbbuf_T *nbbuf = nb_get_buf(bufno);
2520 char buffer[2*MAXPATHL];
2521
2522 if (!haveConnection || bufno < 0)
2523 return;
2524
2525 if (!netbeansCloseFile)
2526 {
2527 nbdebug(("ignoring file_closed for %s\n", bufp->b_ffname));
2528 return;
2529 }
2530
2531 nbdebug(("netbeans_file_closed() bufno = %d, file = %s, displayname = %s\n",
2532 bufno, bufp->b_ffname,
2533 (nbbuf != NULL) ? nbbuf->displayname : "<>"));
2534
2535 if (bufno <= 0)
2536 return;
2537
2538 sprintf(buffer, "%d:killed=%d\n", bufno, cmdno);
2539
2540 nbdebug(("EVT: %s", buffer));
2541
2542 nb_send(buffer, "netbeans_file_closed");
2543
2544 if (nbbuf != NULL)
2545 nbbuf->bufp = NULL;
2546}
2547
2548/*
2549 * Get a pointer to the Netbeans buffer for Vim buffer "bufp".
2550 * Return NULL if there is no such buffer or changes are not to be reported.
2551 * Otherwise store the buffer number in "*bufnop".
2552 */
2553 static nbbuf_T *
2554nb_bufp2nbbuf_fire(buf_T *bufp, int *bufnop)
2555{
2556 int bufno;
2557 nbbuf_T *nbbuf;
2558
2559 if (!haveConnection || !netbeansFireChanges)
2560 return NULL; /* changes are not reported at all */
2561
2562 bufno = nb_getbufno(bufp);
2563 if (bufno <= 0)
2564 return NULL; /* file is not known to NetBeans */
2565
2566 nbbuf = nb_get_buf(bufno);
2567 if (nbbuf != NULL && !nbbuf->fireChanges)
2568 return NULL; /* changes in this buffer are not reported */
2569
2570 *bufnop = bufno;
2571 return nbbuf;
2572}
2573
2574/*
2575 * Tell netbeans the user inserted some text.
2576 */
2577 void
2578netbeans_inserted(
2579 buf_T *bufp,
2580 linenr_T linenr,
2581 colnr_T col,
2582 int oldlen,
2583 char_u *txt,
2584 int newlen)
2585{
2586 char_u *buf;
2587 int bufno;
2588 nbbuf_T *nbbuf;
2589 pos_T pos;
2590 long off;
2591 char_u *p;
2592 char_u *newtxt;
2593
2594 nbbuf = nb_bufp2nbbuf_fire(bufp, &bufno);
2595 if (nbbuf == NULL)
2596 return;
2597
2598 nbbuf->modified = 1;
2599
2600 pos.lnum = linenr;
2601 pos.col = col;
2602
2603 off = pos2off(bufp, &pos);
2604
2605/* nbdebug(("linenr = %d, col = %d, off = %ld\n", linenr, col, off)); */
2606
2607 buf = alloc(128 + 2*newlen);
2608
2609 if (oldlen > 0)
2610 {
2611 /* some chars were replaced; send "remove" EVT */
2612 sprintf((char *)buf, "%d:remove=%d %ld %d\n",
2613 bufno, cmdno, off, oldlen);
2614 nbdebug(("EVT: %s", buf));
2615 nb_send((char *)buf, "netbeans_inserted");
2616 }
2617 else if (oldlen < 0)
2618 {
2619 /* can't happen? */
2620 nbdebug(("unexpected: oldlen < 0 in netbeans_inserted"));
2621 }
2622
2623 /* send the "insert" EVT */
2624 newtxt = alloc(newlen + 1);
2625 STRNCPY(newtxt, txt, newlen);
2626 newtxt[newlen] = '\0';
2627 p = nb_quote(newtxt);
2628 if (p != NULL)
2629 {
2630 sprintf((char *)buf, "%d:insert=%d %ld \"%s\"\n", bufno, cmdno, off, p);
2631 nbdebug(("EVT: %s", buf));
2632 nb_send((char *)buf, "netbeans_inserted");
2633 }
2634 vim_free(p);
2635 vim_free(newtxt);
2636 vim_free(buf);
2637}
2638
2639/*
2640 * Tell netbeans some bytes have been removed.
2641 */
2642 void
2643netbeans_removed(
2644 buf_T *bufp,
2645 linenr_T linenr,
2646 colnr_T col,
2647 long len)
2648{
2649 char_u buf[128];
2650 int bufno;
2651 nbbuf_T *nbbuf;
2652 pos_T pos;
2653 long off;
2654
2655 nbbuf = nb_bufp2nbbuf_fire(bufp, &bufno);
2656 if (nbbuf == NULL)
2657 return;
2658
2659 if (len < 0)
2660 {
2661 nbdebug(("Negative len %ld in netbeans_removed()!", len));
2662 return;
2663 }
2664
2665 nbbuf->modified = 1;
2666
2667 pos.lnum = linenr;
2668 pos.col = col;
2669
2670 off = pos2off(bufp, &pos);
2671
2672 sprintf((char *)buf, "%d:remove=%d %ld %ld\n", bufno, cmdno, off, len);
2673 nbdebug(("EVT: %s", buf));
2674 nb_send((char *)buf, "netbeans_removed");
2675}
2676
2677/*
2678 * Send netbeans an unmodufied command.
2679 */
2680/*ARGSUSED*/
2681 void
2682netbeans_unmodified(buf_T *bufp)
2683{
2684#if 0
2685 char_u buf[128];
2686 int bufno;
2687 nbbuf_T *nbbuf;
2688
2689 /* This has been disabled, because NetBeans considers a buffer modified
2690 * even when all changes have been undone. */
2691 nbbuf = nb_bufp2nbbuf_fire(bufp, &bufno);
2692 if (nbbuf == NULL)
2693 return;
2694
2695 nbbuf->modified = 0;
2696
2697 sprintf((char *)buf, "%d:unmodified=%d\n", bufno, cmdno);
2698 nbdebug(("EVT: %s", buf));
2699 nb_send((char *)buf, "netbeans_unmodified");
2700#endif
2701}
2702
2703/*
2704 * Send a button release event back to netbeans. Its up to netbeans
2705 * to decide what to do (if anything) with this event.
2706 */
2707 void
2708netbeans_button_release(int button)
2709{
2710 char buf[128];
2711 int bufno;
2712
2713 bufno = nb_getbufno(curbuf);
2714
2715 if (bufno >= 0 && curwin != NULL && curwin->w_buffer == curbuf)
2716 {
2717 int col = mouse_col - curwin->w_wincol - (curwin->w_p_nu ? 9 : 1);
2718 long off = pos2off(curbuf, &curwin->w_cursor);
2719
2720 /* sync the cursor position */
2721 sprintf(buf, "%d:newDotAndMark=%d %ld %ld\n", bufno, cmdno, off, off);
2722 nbdebug(("EVT: %s", buf));
2723 nb_send(buf, "netbeans_button_release[newDotAndMark]");
2724
2725 sprintf(buf, "%d:buttonRelease=%d %d %ld %d\n", bufno, cmdno,
2726 button, (long)curwin->w_cursor.lnum, col);
2727 nbdebug(("EVT: %s", buf));
2728 nb_send(buf, "netbeans_button_release");
2729 }
2730}
2731
2732
2733/*
2734 * Send a keypress event back to netbeans. This usualy simulates some
2735 * kind of function key press.
2736 */
2737 void
2738netbeans_keycommand(int key)
2739{
2740 char buf[2*MAXPATHL];
2741 int bufno;
2742 char keyName[60];
2743 long off;
2744 char_u *q;
2745
2746 if (!haveConnection)
2747 return;
2748
2749 /* convert key to netbeans name */
2750 netbeans_keyname(key, keyName);
2751
2752 bufno = nb_getbufno(curbuf);
2753
2754 if (bufno == -1)
2755 {
2756 nbdebug(("got keycommand for non-NetBeans buffer, opening...\n"));
2757 q = curbuf->b_ffname == NULL ? (char_u *)""
2758 : nb_quote(curbuf->b_ffname);
2759 if (q == NULL)
2760 return;
2761 sprintf(buf, "0:fileOpened=%d \"%s\" %s %s\n", 0,
2762 q,
2763 "T", /* open in NetBeans */
2764 "F"); /* modified */
2765 if (curbuf->b_ffname != NULL)
2766 vim_free(q);
2767 nbdebug(("EVT: %s", buf));
2768 nb_send(buf, "netbeans_keycommand");
2769
2770 postpone_keycommand(key);
2771 return;
2772 }
2773
2774 /* sync the cursor position */
2775 off = pos2off(curbuf, &curwin->w_cursor);
2776 sprintf(buf, "%d:newDotAndMark=%d %ld %ld\n", bufno, cmdno, off, off);
2777 nbdebug(("EVT: %s", buf));
2778 nb_send(buf, "netbeans_keycommand");
2779
2780 /* To work on Win32 you must apply patch to ExtEditor module
2781 * from ExtEdCaret.java.diff - make EVT_newDotAndMark handler
2782 * more synchronous
2783 */
2784
2785 /* now send keyCommand event */
2786 sprintf(buf, "%d:keyCommand=%d \"%s\"\n", bufno, cmdno, keyName);
2787 nbdebug(("EVT: %s", buf));
2788 nb_send(buf, "netbeans_keycommand");
2789
2790 /* New: do both at once and include the lnum/col. */
2791 sprintf(buf, "%d:keyAtPos=%d \"%s\" %ld %ld/%ld\n", bufno, cmdno, keyName,
2792 off, (long)curwin->w_cursor.lnum, (long)curwin->w_cursor.col);
2793 nbdebug(("EVT: %s", buf));
2794 nb_send(buf, "netbeans_keycommand");
2795}
2796
2797
2798/*
2799 * Send a save event to netbeans.
2800 */
2801 void
2802netbeans_save_buffer(buf_T *bufp)
2803{
2804 char_u buf[64];
2805 int bufno;
2806 nbbuf_T *nbbuf;
2807
2808 nbbuf = nb_bufp2nbbuf_fire(bufp, &bufno);
2809 if (nbbuf == NULL)
2810 return;
2811
2812 nbbuf->modified = 0;
2813
2814 sprintf((char *)buf, "%d:save=%d\n", bufno, cmdno);
2815 nbdebug(("EVT: %s", buf));
2816 nb_send((char *)buf, "netbeans_save_buffer");
2817}
2818
2819
2820/*
2821 * Send remove command to netbeans (this command has been turned off).
2822 */
2823 void
2824netbeans_deleted_all_lines(buf_T *bufp)
2825{
2826 char_u buf[64];
2827 int bufno;
2828 nbbuf_T *nbbuf;
2829
2830 nbbuf = nb_bufp2nbbuf_fire(bufp, &bufno);
2831 if (nbbuf == NULL)
2832 return;
2833
2834 nbbuf->modified = 1;
2835
2836 sprintf((char *)buf, "%d:remove=%d 0 -1\n", bufno, cmdno);
2837 nbdebug(("EVT(suppressed): %s", buf));
2838/* nb_send(buf, "netbeans_deleted_all_lines"); */
2839}
2840
2841
2842/*
2843 * See if the lines are guarded. The top and bot parameters are from
2844 * u_savecommon(), these are the line above the change and the line below the
2845 * change.
2846 */
2847 int
2848netbeans_is_guarded(linenr_T top, linenr_T bot)
2849{
2850 signlist_T *p;
2851 int lnum;
2852
2853 for (p = curbuf->b_signlist; p != NULL; p = p->next)
2854 if (p->id >= GUARDEDOFFSET)
2855 for (lnum = top + 1; lnum < bot; lnum++)
2856 if (lnum == p->lnum)
2857 return TRUE;
2858
2859 return FALSE;
2860}
2861
2862#if defined(FEAT_GUI_MOTIF) || defined(PROTO)
2863/*
2864 * We have multiple signs to draw at the same location. Draw the
2865 * multi-sign indicator instead. This is the Motif version.
2866 */
2867 void
2868netbeans_draw_multisign_indicator(int row)
2869{
2870 int i;
2871 int y;
2872 int x;
2873
2874 x = 0;
2875 y = row * gui.char_height + 2;
2876
2877 for (i = 0; i < gui.char_height - 3; i++)
2878 XDrawPoint(gui.dpy, gui.wid, gui.text_gc, x+2, y++);
2879
2880 XDrawPoint(gui.dpy, gui.wid, gui.text_gc, x+0, y);
2881 XDrawPoint(gui.dpy, gui.wid, gui.text_gc, x+2, y);
2882 XDrawPoint(gui.dpy, gui.wid, gui.text_gc, x+4, y++);
2883 XDrawPoint(gui.dpy, gui.wid, gui.text_gc, x+1, y);
2884 XDrawPoint(gui.dpy, gui.wid, gui.text_gc, x+2, y);
2885 XDrawPoint(gui.dpy, gui.wid, gui.text_gc, x+3, y++);
2886 XDrawPoint(gui.dpy, gui.wid, gui.text_gc, x+2, y);
2887}
2888#endif /* FEAT_GUI_MOTIF */
2889
2890#ifdef FEAT_GUI_GTK
2891/*
2892 * We have multiple signs to draw at the same location. Draw the
2893 * multi-sign indicator instead. This is the GTK/Gnome version.
2894 */
2895 void
2896netbeans_draw_multisign_indicator(int row)
2897{
2898 int i;
2899 int y;
2900 int x;
2901 GdkDrawable *drawable = gui.drawarea->window;
2902
2903 x = 0;
2904 y = row * gui.char_height + 2;
2905
2906 for (i = 0; i < gui.char_height - 3; i++)
2907 gdk_draw_point(drawable, gui.text_gc, x+2, y++);
2908
2909 gdk_draw_point(drawable, gui.text_gc, x+0, y);
2910 gdk_draw_point(drawable, gui.text_gc, x+2, y);
2911 gdk_draw_point(drawable, gui.text_gc, x+4, y++);
2912 gdk_draw_point(drawable, gui.text_gc, x+1, y);
2913 gdk_draw_point(drawable, gui.text_gc, x+2, y);
2914 gdk_draw_point(drawable, gui.text_gc, x+3, y++);
2915 gdk_draw_point(drawable, gui.text_gc, x+2, y);
2916}
2917#endif /* FEAT_GUI_GTK */
2918
2919/*
2920 * If the mouse is clicked in the gutter of a line with multiple
2921 * annotations, cycle through the set of signs.
2922 */
2923 void
2924netbeans_gutter_click(linenr_T lnum)
2925{
2926 signlist_T *p;
2927
2928 for (p = curbuf->b_signlist; p != NULL; p = p->next)
2929 {
2930 if (p->lnum == lnum && p->next && p->next->lnum == lnum)
2931 {
2932 signlist_T *tail;
2933
2934 /* remove "p" from list, reinsert it at the tail of the sublist */
2935 if (p->prev)
2936 p->prev->next = p->next;
2937 else
2938 curbuf->b_signlist = p->next;
2939 p->next->prev = p->prev;
2940 /* now find end of sublist and insert p */
2941 for (tail = p->next;
2942 tail->next && tail->next->lnum == lnum
2943 && tail->next->id < GUARDEDOFFSET;
2944 tail = tail->next)
2945 ;
2946 /* tail now points to last entry with same lnum (except
2947 * that "guarded" annotations are always last) */
2948 p->next = tail->next;
2949 if (tail->next)
2950 tail->next->prev = p;
2951 p->prev = tail;
2952 tail->next = p;
2953 update_debug_sign(curbuf, lnum);
2954 break;
2955 }
2956 }
2957}
2958
2959
2960/*
2961 * Add a sign of the reqested type at the requested location.
2962 *
2963 * Reverse engineering:
2964 * Apparently an annotation is defined the first time it is used in a buffer.
2965 * When the same annotation is used in two buffers, the second time we do not
2966 * need to define a new sign name but reuse the existing one. But since the
2967 * ID number used in the second buffer starts counting at one again, a mapping
2968 * is made from the ID specifically for the buffer to the global sign name
2969 * (which is a number).
2970 *
2971 * globalsignmap[] stores the signs that have been defined globally.
2972 * buf->signmapused[] maps buffer-local annotation IDs to an index in
2973 * globalsignmap[].
2974 */
2975/*ARGSUSED*/
2976 static void
2977addsigntype(
2978 nbbuf_T *buf,
2979 int typeNum,
2980 char_u *typeName,
2981 char_u *tooltip,
2982 char_u *glyphFile,
2983 int use_fg,
2984 int fg,
2985 int use_bg,
2986 int bg)
2987{
2988 char fgbuf[32];
2989 char bgbuf[32];
2990 int i, j;
2991
2992 for (i = 0; i < globalsignmapused; i++)
2993 if (STRCMP(typeName, globalsignmap[i]) == 0)
2994 break;
2995
2996 if (i == globalsignmapused) /* not found; add it to global map */
2997 {
2998 nbdebug(("DEFINEANNOTYPE(%d,%s,%s,%s,%d,%d)\n",
2999 typeNum, typeName, tooltip, glyphFile, fg, bg));
3000 if (use_fg || use_bg)
3001 {
3002 sprintf(fgbuf, "guifg=#%06x", fg & 0xFFFFFF);
3003 sprintf(bgbuf, "guibg=#%06x", bg & 0xFFFFFF);
3004
3005 coloncmd(":highlight NB_%s %s %s", typeName, (use_fg) ? fgbuf : "",
3006 (use_bg) ? bgbuf : "");
3007 if (*glyphFile == NUL)
3008 /* no glyph, line highlighting only */
3009 coloncmd(":sign define %d linehl=NB_%s", i + 1, typeName);
3010 else if (vim_strsize(glyphFile) <= 2)
3011 /* one- or two-character glyph name, use as text glyph with
3012 * texthl */
3013 coloncmd(":sign define %d text=%s texthl=NB_%s", i + 1,
3014 glyphFile, typeName);
3015 else
3016 /* glyph, line highlighting */
3017 coloncmd(":sign define %d icon=%s linehl=NB_%s", i + 1,
3018 glyphFile, typeName);
3019 }
3020 else
3021 /* glyph, no line highlighting */
3022 coloncmd(":sign define %d icon=%s", i + 1, glyphFile);
3023
3024 if (STRCMP(typeName,"CurrentPC") == 0)
3025 curPCtype = typeNum;
3026
3027 if (globalsignmapused == globalsignmaplen)
3028 {
3029 if (globalsignmaplen == 0) /* first allocation */
3030 {
3031 globalsignmaplen = 20;
3032 globalsignmap = (char **)alloc_clear(globalsignmaplen*sizeof(char *));
3033 }
3034 else /* grow it */
3035 {
3036 int incr;
3037 int oldlen = globalsignmaplen;
3038
3039 globalsignmaplen *= 2;
3040 incr = globalsignmaplen - oldlen;
3041 globalsignmap = (char **)vim_realloc(globalsignmap,
3042 globalsignmaplen * sizeof(char *));
3043 memset(globalsignmap + oldlen, 0, incr * sizeof(char *));
3044 }
3045 }
3046
3047 globalsignmap[i] = (char *)typeName;
3048 globalsignmapused = i + 1;
3049 }
3050
3051 /* check local map; should *not* be found! */
3052 for (j = 0; j < buf->signmapused; j++)
3053 if (buf->signmap[j] == i + 1)
3054 return;
3055
3056 /* add to local map */
3057 if (buf->signmapused == buf->signmaplen)
3058 {
3059 if (buf->signmaplen == 0) /* first allocation */
3060 {
3061 buf->signmaplen = 5;
3062 buf->signmap = (int *)alloc_clear(buf->signmaplen * sizeof(int *));
3063 }
3064 else /* grow it */
3065 {
3066 int incr;
3067 int oldlen = buf->signmaplen;
3068 buf->signmaplen *= 2;
3069 incr = buf->signmaplen - oldlen;
3070 buf->signmap = (int *)vim_realloc(buf->signmap,
3071 buf->signmaplen*sizeof(int *));
3072 memset(buf->signmap + oldlen, 0, incr * sizeof(int *));
3073 }
3074 }
3075
3076 buf->signmap[buf->signmapused++] = i + 1;
3077
3078}
3079
3080
3081/*
3082 * See if we have the requested sign type in the buffer.
3083 */
3084 static int
3085mapsigntype(nbbuf_T *buf, int localsigntype)
3086{
3087 if (--localsigntype >= 0 && localsigntype < buf->signmapused)
3088 return buf->signmap[localsigntype];
3089
3090 return 0;
3091}
3092
3093
3094/*
3095 * Compute length of buffer, don't print anything.
3096 */
3097 static long
3098get_buf_size(buf_T *bufp)
3099{
3100 linenr_T lnum;
3101 long char_count = 0;
3102 int eol_size;
3103 long last_check = 100000L;
3104
3105 if (bufp->b_ml.ml_flags & ML_EMPTY)
3106 return 0;
3107 else
3108 {
3109 if (get_fileformat(bufp) == EOL_DOS)
3110 eol_size = 2;
3111 else
3112 eol_size = 1;
3113 for (lnum = 1; lnum <= bufp->b_ml.ml_line_count; ++lnum)
3114 {
3115 char_count += STRLEN(ml_get(lnum)) + eol_size;
3116 /* Check for a CTRL-C every 100000 characters */
3117 if (char_count > last_check)
3118 {
3119 ui_breakcheck();
3120 if (got_int)
3121 return char_count;
3122 last_check = char_count + 100000L;
3123 }
3124 }
3125 /* Correction for when last line doesn't have an EOL. */
3126 if (!bufp->b_p_eol && bufp->b_p_bin)
3127 char_count -= eol_size;
3128 }
3129
3130 return char_count;
3131}
3132
3133/*
3134 * Convert character offset to lnum,col
3135 */
3136 static pos_T *
3137off2pos(buf_T *buf, long offset)
3138{
3139 linenr_T lnum;
3140 static pos_T pos;
3141
3142 pos.lnum = 0;
3143 pos.col = 0;
3144#ifdef FEAT_VIRTUALEDIT
3145 pos.coladd = 0;
3146#endif
3147
3148 if (!(buf->b_ml.ml_flags & ML_EMPTY))
3149 {
3150 if ((lnum = ml_find_line_or_offset(buf, (linenr_T)0, &offset)) < 0)
3151 return NULL;
3152 pos.lnum = lnum;
3153 pos.col = offset;
3154 }
3155
3156 return &pos;
3157}
3158
3159/*
3160 * Convert an argument in the form "1234" to an offset and compute the
3161 * lnum/col from it. Convert an argument in the form "123/12" directly to a
3162 * lnum/col.
3163 * "argp" is advanced to after the argument.
3164 * Return a pointer to the position, NULL if something is wrong.
3165 */
3166 static pos_T *
3167get_off_or_lnum(buf_T *buf, char_u **argp)
3168{
3169 static pos_T mypos;
3170 long off;
3171
3172 off = strtol((char *)*argp, (char **)argp, 10);
3173 if (**argp == '/')
3174 {
3175 mypos.lnum = (linenr_T)off;
3176 ++*argp;
3177 mypos.col = strtol((char *)*argp, (char **)argp, 10);
3178#ifdef FEAT_VIRTUALEDIT
3179 mypos.coladd = 0;
3180#endif
3181 return &mypos;
3182 }
3183 return off2pos(buf, off);
3184}
3185
3186
3187/*
3188 * Convert lnum,col to character offset
3189 */
3190 static long
3191pos2off(buf_T *buf, pos_T *pos)
3192{
3193 long offset = 0;
3194
3195 if (!(buf->b_ml.ml_flags & ML_EMPTY))
3196 {
3197 if ((offset = ml_find_line_or_offset(buf, pos->lnum, 0)) < 0)
3198 return 0;
3199 offset += pos->col;
3200 }
3201
3202 return offset;
3203}
3204
3205
3206#endif /* defined(FEAT_NETBEANS_INTG) */