Mercurial > vim
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) */ |