Annotation of src/usr.bin/vim/normal.c, Revision 1.1.1.1
1.1 downsj 1: /* $OpenBSD$ */
2: /* vi:set ts=4 sw=4:
3: *
4: * VIM - Vi IMproved by Bram Moolenaar
5: *
6: * Do ":help uganda" in Vim to read copying and usage conditions.
7: * Do ":help credits" in Vim to see a list of people who contributed.
8: */
9:
10: /*
11: * Contains the main routine for processing characters in command mode.
12: * Communicates closely with the code in ops.c to handle the operators.
13: */
14:
15: #include "vim.h"
16: #include "globals.h"
17: #include "proto.h"
18: #include "option.h"
19:
20: #undef EXTERN
21: #undef INIT
22: #define EXTERN
23: #define INIT(x) x
24: #include "ops.h"
25:
26: /*
27: * Generally speaking, every command in normal() should either clear any
28: * pending operator (with clearop()), or set the motion type variable.
29: */
30:
31: /*
32: * If a count is given before the operator, it is saved in opnum.
33: */
34: static linenr_t opnum = 0;
35: static linenr_t Prenum; /* The (optional) number before a command. */
36: static int prechar = NUL; /* prepended command char */
37: /*
38: * The visual area is remembered for reselection.
39: */
40: static int resel_VIsual_mode = NUL; /* 'v', 'V', or Ctrl-V */
41: static linenr_t resel_VIsual_line_count; /* number of lines */
42: static colnr_t resel_VIsual_col; /* number of cols or end column */
43:
44: #ifdef USE_MOUSE
45: static void find_start_of_word __ARGS((FPOS *));
46: static void find_end_of_word __ARGS((FPOS *));
47: static int get_mouse_class __ARGS((int));
48: #endif
49: static void prep_redo __ARGS((long, int, int, int, int));
50: static int checkclearop __ARGS((void));
51: static int checkclearopq __ARGS((void));
52: static void clearop __ARGS((void));
53: static void clearopbeep __ARGS((void));
54: static void del_from_showcmd __ARGS((int));
55: static void do_gd __ARGS((int nchar));
56:
57: /*
58: * normal
59: *
60: * Execute a command in normal mode.
61: *
62: * This is basically a big switch with the cases arranged in rough categories
63: * in the following order:
64: *
65: * 0. Macros (q, @)
66: * 1. Screen positioning commands (^U, ^D, ^F, ^B, ^E, ^Y, z)
67: * 2. Control commands (:, <help>, ^L, ^G, ^^, ZZ, *, ^], ^T)
68: * 3. Cursor motions (G, H, M, L, l, K_RIGHT, , h, K_LEFT, ^H, k, K_UP,
69: * ^P, +, CR, LF, j, K_DOWN, ^N, _, |, B, b, W, w, E, e, $, ^, 0)
70: * 4. Searches (?, /, n, N, T, t, F, f, ,, ;, ], [, %, (, ), {, })
71: * 5. Edits (., u, K_UNDO, ^R, U, r, J, p, P, ^A, ^S)
72: * 6. Inserts (A, a, I, i, o, O, R)
73: * 7. Operators (~, d, c, y, >, <, !, =, Q)
74: * 8. Abbreviations (x, X, D, C, s, S, Y, &)
75: * 9. Marks (m, ', `, ^O, ^I)
76: * 10. Buffer setting (")
77: * 11. Visual (v, V, ^V)
78: * 12. Suspend (^Z)
79: * 13. Window commands (^W)
80: * 14. extended commands (starting with 'g')
81: * 15. mouse click
82: * 16. scrollbar movement
83: * 17. The end (ESC)
84: */
85:
86: void
87: normal()
88: {
89: register int c;
90: long n = 0; /* init for GCC */
91: int flag = FALSE;
92: int flag2 = FALSE;
93: int type = 0; /* type of operation */
94: int dir = FORWARD; /* search direction */
95: int nchar = NUL; /* next command char */
96: int finish_op;
97: linenr_t Prenum1;
98: char_u *searchbuff = NULL; /* buffer for search string */
99: FPOS *pos = NULL; /* init for gcc */
100: char_u *ptr = NULL;
101: int command_busy = FALSE;
102: int ctrl_w = FALSE; /* got CTRL-W command */
103: int old_col = 0;
104: int dont_adjust_op_end = FALSE;
105:
106: Prenum = 0;
107: /*
108: * If there is an operator pending, then the command we take this time
109: * will terminate it. Finish_op tells us to finish the operation before
110: * returning this time (unless the operation was cancelled).
111: */
112: finish_op = (op_type != NOP);
113:
114: if (!finish_op && !yankbuffer)
115: opnum = 0;
116:
117: State = NORMAL_BUSY;
118: c = vgetc();
119: #ifdef HAVE_LANGMAP
120: LANGMAP_ADJUST(c, TRUE);
121: #endif
122: if (c == NUL)
123: c = K_ZERO;
124: (void)add_to_showcmd(c, FALSE);
125:
126: getcount:
127: /* Pick up any leading digits and compute 'Prenum' */
128: while ((c >= '1' && c <= '9') || (Prenum != 0 && (c == K_DEL || c == '0')))
129: {
130: if (c == K_DEL)
131: {
132: Prenum /= 10;
133: del_from_showcmd(4); /* delete the digit and ~@% */
134: }
135: else
136: Prenum = Prenum * 10 + (c - '0');
137: if (Prenum < 0) /* got too large! */
138: Prenum = 999999999;
139: c = vgetc();
140: #ifdef HAVE_LANGMAP
141: LANGMAP_ADJUST(c, TRUE);
142: #endif
143: (void)add_to_showcmd(c, FALSE);
144: }
145:
146: /*
147: * If we got CTRL-W there may be a/another count
148: */
149: if (c == Ctrl('W') && !ctrl_w && op_type == NOP)
150: {
151: ctrl_w = TRUE;
152: opnum = Prenum; /* remember first count */
153: Prenum = 0;
154: ++no_mapping;
155: ++allow_keys; /* no mapping for nchar, but keys */
156: c = vgetc(); /* get next character */
157: #ifdef HAVE_LANGMAP
158: LANGMAP_ADJUST(c, TRUE);
159: #endif
160: --no_mapping;
161: --allow_keys;
162: (void)add_to_showcmd(c, FALSE);
163: goto getcount; /* jump back */
164: }
165:
166: /*
167: * If we're in the middle of an operator (including after entering a yank
168: * buffer with ") AND we had a count before the
169: * operator, then that count overrides the current value of Prenum. What
170: * this means effectively, is that commands like "3dw" get turned into
171: * "d3w" which makes things fall into place pretty neatly.
172: * If you give a count before AND after the operator, they are multiplied.
173: */
174: if (opnum != 0)
175: {
176: if (Prenum)
177: Prenum *= opnum;
178: else
179: Prenum = opnum;
180: opnum = 0;
181: }
182:
183: Prenum1 = (Prenum == 0 ? 1 : Prenum); /* Prenum often defaults to 1 */
184:
185: /*
186: * Get an additional character if we need one.
187: * For CTRL-W we already got it when looking for a count.
188: */
189: if (ctrl_w)
190: {
191: nchar = c;
192: c = Ctrl('W');
193: }
194: else if ((op_type == NOP && vim_strchr((char_u *)"@zm\"", c) != NULL) ||
195: (op_type == NOP && !VIsual_active &&
196: vim_strchr((char_u *)"rZ", c) != NULL) ||
197: vim_strchr((char_u *)"tTfF[]g'`", c) != NULL ||
198: (c == 'q' && !Recording && !Exec_reg))
199: {
200: ++no_mapping;
201: ++allow_keys; /* no mapping for nchar, but allow key codes */
202: nchar = vgetc();
203: #ifdef HAVE_LANGMAP
204: /* adjust chars > 127: tTfFr should leave lang of nchar unchanged! */
205: LANGMAP_ADJUST(nchar, vim_strchr((char_u *)"tTfFr", c) == NULL);
206: #endif
207: #ifdef RIGHTLEFT
208: if (p_hkmap && strchr("tTfFr", c) && KeyTyped) /* Hebrew mapped char */
209: nchar = hkmap(nchar);
210: #endif
211: --no_mapping;
212: --allow_keys;
213: (void)add_to_showcmd(nchar, FALSE);
214: }
215: if (p_sc)
216: flushbuf(); /* flush the showcmd characters onto the
217: * screen so we can see them while the command
218: * is being executed
219: */
220:
221: State = NORMAL;
222: if (nchar == ESC)
223: {
224: clearop();
225: goto normal_end;
226: }
227: msg_didout = FALSE; /* don't scroll screen up for normal command */
228: msg_col = 0;
229:
230: #ifdef RIGHTLEFT
231: if (curwin->w_p_rl && KeyTyped) /* invert horizontal operations */
232: switch (c)
233: {
234: case 'l': c = 'h'; break;
235: case K_RIGHT: c = K_LEFT; break;
236: case 'h': c = 'l'; break;
237: case K_LEFT: c = K_RIGHT; break;
238: case '>': c = '<'; break;
239: case '<': c = '>'; break;
240: }
241: #endif
242: switch (c)
243: {
244:
245: /*
246: * 0: Macros
247: */
248: case 'q': /* (stop) recording into a named register */
249: if (checkclearop())
250: break;
251: /* command is ignored while executing a register */
252: if (!Exec_reg && do_record(nchar) == FAIL)
253: clearopbeep();
254: break;
255:
256: case '@': /* execute a named buffer */
257: if (checkclearop())
258: break;
259: while (Prenum1--)
260: {
261: if (do_execbuf(nchar, FALSE, FALSE) == FAIL)
262: {
263: clearopbeep();
264: break;
265: }
266: }
267: break;
268:
269: /*
270: * 1: Screen positioning commands
271: */
272: case Ctrl('D'):
273: flag = TRUE;
274:
275: case Ctrl('U'):
276: if ((c == Ctrl('U') && curwin->w_cursor.lnum == 1) ||
277: (c == Ctrl('D') && curwin->w_cursor.lnum ==
278: curbuf->b_ml.ml_line_count))
279: clearopbeep();
280: else
281: {
282: if (checkclearop())
283: break;
284: halfpage(flag, Prenum);
285: }
286: break;
287:
288: case Ctrl('B'):
289: case K_S_UP:
290: case K_PAGEUP:
291: dir = BACKWARD;
292:
293: case Ctrl('F'):
294: case K_S_DOWN:
295: case K_PAGEDOWN:
296: if (checkclearop())
297: break;
298: (void)onepage(dir, Prenum1);
299: break;
300:
301: case Ctrl('E'):
302: if (checkclearop())
303: break;
304: scrollup(Prenum1);
305: if (p_so)
306: cursor_correct();
307: /* We may have moved to another line -- webb */
308: coladvance(curwin->w_curswant);
309: cursupdate();
310: updateScreen(VALID);
311: break;
312:
313: case Ctrl('Y'):
314: if (checkclearop())
315: break;
316: scrolldown(Prenum1);
317: if (p_so)
318: cursor_correct();
319: /* We may have moved to another line -- webb */
320: coladvance(curwin->w_curswant);
321: updateScreen(VALID);
322: break;
323:
324: case 'z':
325: if (checkclearop())
326: break;
327: if (nchar < 0x100 && isdigit(nchar))
328: {
329: Prenum = nchar - '0';
330: for (;;)
331: {
332: ++no_mapping;
333: ++allow_keys; /* no mapping for nchar, but allow key codes */
334: nchar = vgetc();
335: #ifdef HAVE_LANGMAP
336: LANGMAP_ADJUST(c, TRUE);
337: #endif
338: --no_mapping;
339: --allow_keys;
340: (void)add_to_showcmd(nchar, FALSE);
341: if (c == K_DEL)
342: Prenum /= 10;
343: else if (nchar < 0x100 && isdigit(nchar))
344: Prenum = Prenum * 10 + (nchar - '0');
345: else if (nchar == CR)
346: {
347: win_setheight((int)Prenum);
348: break;
349: }
350: else if (nchar == 'l' || nchar == 'h' ||
351: nchar == K_LEFT || nchar == K_RIGHT)
352: {
353: Prenum1 = Prenum ? Prenum : 1;
354: goto dozet;
355: }
356: else
357: {
358: clearopbeep();
359: break;
360: }
361: }
362: op_type = NOP;
363: break;
364: }
365: dozet:
366: /*
367: * If line number given, set cursor, except for "zh", "zl", "ze" and
368: * "zs"
369: */
370: if (vim_strchr((char_u *)"hles", nchar) == NULL &&
371: nchar != K_LEFT && nchar != K_RIGHT &&
372: Prenum && Prenum != curwin->w_cursor.lnum)
373: {
374: setpcmark();
375: if (Prenum > curbuf->b_ml.ml_line_count)
376: curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
377: else
378: curwin->w_cursor.lnum = Prenum;
379: }
380: switch (nchar)
381: {
382: case NL: /* put curwin->w_cursor at top of screen */
383: case CR:
384: beginline(TRUE);
385: /* FALLTHROUGH */
386: case 't':
387: scroll_cursor_top(0, TRUE);
388: break;
389:
390: case '.': /* put curwin->w_cursor in middle of screen */
391: beginline(TRUE);
392: /* FALLTHROUGH */
393: case 'z':
394: scroll_cursor_halfway(TRUE);
395: break;
396:
397: case '-': /* put curwin->w_cursor at bottom of screen */
398: beginline(TRUE);
399: /* FALLTHROUGH */
400: case 'b':
401: scroll_cursor_bot(0, TRUE);
402: break;
403:
404: /* "zh" - scroll screen to the right */
405: case 'h':
406: case K_LEFT:
407: if (!curwin->w_p_wrap)
408: {
409: colnr_t s, e;
410:
411: if ((colnr_t)Prenum1 > curwin->w_leftcol)
412: curwin->w_leftcol = 0;
413: else
414: curwin->w_leftcol -= (colnr_t)Prenum1;
415: n = curwin->w_leftcol + Columns -
416: (curwin->w_p_nu ? 8 : 0) - 1;
417: if (curwin->w_virtcol > (colnr_t)n)
418: coladvance((colnr_t)n);
419:
420: getvcol(curwin, &curwin->w_cursor, &s, NULL, &e);
421: if (e > (colnr_t)n)
422: coladvance(s - 1);
423: redraw_later(NOT_VALID);
424: }
425: break;
426:
427: /* "zl" - scroll screen to the left */
428: case 'l':
429: case K_RIGHT:
430: if (!curwin->w_p_wrap)
431: {
432: colnr_t s, e;
433:
434: /* scroll the window left */
435: curwin->w_leftcol += (colnr_t)Prenum1;
436:
437: /* If the cursor has moved off the screen, put it at the
438: * first char on the screen */
439: if (curwin->w_leftcol > curwin->w_virtcol)
440: (void)coladvance(curwin->w_leftcol);
441:
442: /* If the start of the character under the cursor is not
443: * on the screen, advance the cursor one more char. If
444: * this fails (last char of the line) adjust the
445: * scrolling. */
446: getvcol(curwin, &curwin->w_cursor, &s, NULL, &e);
447: if (s < curwin->w_leftcol)
448: if (coladvance(e + 1) == FAIL)
449: curwin->w_leftcol = s;
450:
451: redraw_later(NOT_VALID);
452: }
453: break;
454:
455: /* "zs" - scroll screen, cursor at the start */
456: case 's':
457: if (!curwin->w_p_wrap)
458: {
459: colnr_t s;
460:
461: getvcol(curwin, &curwin->w_cursor, &s, NULL, NULL);
462: curwin->w_leftcol = s;
463: redraw_later(NOT_VALID);
464: }
465: break;
466:
467: /* "ze" - scroll screen, cursor at the end */
468: case 'e':
469: if (!curwin->w_p_wrap)
470: {
471: colnr_t e;
472:
473: getvcol(curwin, &curwin->w_cursor, NULL, NULL, &e);
474: if ((long)e < Columns)
475: curwin->w_leftcol = 0;
476: else
477: curwin->w_leftcol = e - Columns + 1;
478: redraw_later(NOT_VALID);
479: }
480: break;
481:
482: case Ctrl('S'): /* ignore CTRL-S and CTRL-Q to avoid problems */
483: case Ctrl('Q'): /* with terminals that use xon/xoff */
484: break;
485:
486: default:
487: clearopbeep();
488: }
489: updateScreen(VALID);
490: break;
491:
492: /*
493: * 2: Control commands
494: */
495: case ':':
496: if (VIsual_active)
497: goto dooperator;
498: if (checkclearop())
499: break;
500: /*
501: * translate "count:" into ":.,.+(count - 1)"
502: */
503: if (Prenum)
504: {
505: stuffReadbuff((char_u *)".");
506: if (Prenum > 1)
507: {
508: stuffReadbuff((char_u *)",.+");
509: stuffnumReadbuff((long)Prenum - 1L);
510: }
511: }
512: do_cmdline(NULL, FALSE, FALSE);
513: break;
514:
515: case K_HELP:
516: case K_F1:
517: if (checkclearopq())
518: break;
519: do_help((char_u *)"");
520: break;
521:
522: case Ctrl('L'):
523: if (checkclearop())
524: break;
525: updateScreen(CLEAR);
526: break;
527:
528: case Ctrl('G'):
529: if (checkclearop())
530: break;
531: /* print full name if count given or :cd used */
532: fileinfo(did_cd | (int)Prenum, FALSE, FALSE);
533:
534: /*
535: * In Visual mode and "^O^G" in Insert mode, the message will be
536: * overwritten by the mode message. Wait a bit, until a key is hit.
537: */
538: if ((VIsual_active || (restart_edit && p_smd)) && KeyTyped)
539: {
540: setcursor();
541: flushbuf();
542: mch_delay(10000L, FALSE);
543: }
544: break;
545:
546: case K_CCIRCM: /* CTRL-^, short for ":e #" */
547: if (checkclearopq())
548: break;
549: (void)buflist_getfile((int)Prenum, (linenr_t)0, GETF_SETMARK|GETF_ALT);
550: break;
551:
552: case 'Z': /* write, if changed, and exit */
553: if (checkclearopq())
554: break;
555: if (nchar != 'Z')
556: {
557: clearopbeep();
558: break;
559: }
560: stuffReadbuff((char_u *)":x\n");
561: break;
562:
563: case Ctrl(']'): /* :ta to current identifier */
564: case 'K': /* run program for current identifier */
565: if (VIsual_active) /* :ta to visual highlighted text */
566: {
567: if (VIsual.lnum != curwin->w_cursor.lnum)
568: {
569: clearopbeep();
570: break;
571: }
572: if (lt(curwin->w_cursor, VIsual))
573: {
574: ptr = ml_get_pos(&curwin->w_cursor);
575: n = VIsual.col - curwin->w_cursor.col + 1;
576: }
577: else
578: {
579: ptr = ml_get_pos(&VIsual);
580: n = curwin->w_cursor.col - VIsual.col + 1;
581: }
582: end_visual_mode();
583: ++RedrawingDisabled;
584: update_curbuf(NOT_VALID); /* update the inversion later */
585: --RedrawingDisabled;
586: }
587: if (checkclearopq())
588: break;
589: /*FALLTHROUGH*/
590:
591: case 163: /* the pound sign, '#' for English keyboards */
592: if (c == 163)
593: c = '#';
594: /*FALLTHROUGH*/
595:
596: case '*': /* / to current identifier or string */
597: case '#': /* ? to current identifier or string */
598: search_word:
599: if (c == 'g')
600: type = nchar; /* "g*" or "g#" */
601: else
602: type = c;
603: if (ptr == NULL && (n = find_ident_under_cursor(&ptr, (type == '*' ||
604: type == '#') ? FIND_IDENT|FIND_STRING : FIND_IDENT)) == 0)
605: {
606: clearop();
607: break;
608: }
609:
610: if (Prenum)
611: stuffnumReadbuff(Prenum);
612: switch (type)
613: {
614: case '*':
615: stuffReadbuff((char_u *)"/");
616: /* FALLTHROUGH */
617:
618: case '#':
619: if (type == '#')
620: stuffReadbuff((char_u *)"?");
621:
622: /*
623: * put cursor at start of word, makes search skip the word
624: * under the cursor
625: */
626: curwin->w_cursor.col = ptr - ml_get_curline();
627:
628: if (c != 'g' && iswordchar(*ptr))
629: stuffReadbuff((char_u *)"\\<");
630: no_smartcase = TRUE; /* don't use 'smartcase' now */
631: break;
632:
633: case 'K':
634: if (*p_kp == NUL)
635: stuffReadbuff((char_u *)":he ");
636: else
637: {
638: stuffReadbuff((char_u *)":! ");
639: stuffReadbuff(p_kp);
640: stuffReadbuff((char_u *)" ");
641: }
642: break;
643: default:
644: if (curbuf->b_help)
645: stuffReadbuff((char_u *)":he ");
646: else
647: stuffReadbuff((char_u *)":ta ");
648: }
649:
650: /*
651: * Now grab the chars in the identifier
652: */
653: while (n--)
654: {
655: /* put a backslash before \ and some others */
656: if (*ptr == '\\' || (!(type == '*' || type == '#') &&
657: vim_strchr(escape_chars, *ptr) != NULL))
658: stuffcharReadbuff('\\');
659: /* don't interpret the characters as edit commands */
660: if (*ptr < ' ' || *ptr > '~')
661: stuffcharReadbuff(Ctrl('V'));
662: stuffcharReadbuff(*ptr++);
663: }
664:
665: if (c != 'g' && (type == '*' || type == '#') && iswordchar(ptr[-1]))
666: stuffReadbuff((char_u *)"\\>");
667: stuffReadbuff((char_u *)"\n");
668: break;
669:
670: case Ctrl('T'): /* backwards in tag stack */
671: if (checkclearopq())
672: break;
673: do_tag((char_u *)"", 2, (int)Prenum1);
674: break;
675:
676: /*
677: * Cursor motions
678: */
679: case 'G':
680: goto_line:
681: op_motion_type = MLINE;
682: setpcmark();
683: if (Prenum == 0 || Prenum > curbuf->b_ml.ml_line_count)
684: curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
685: else
686: curwin->w_cursor.lnum = Prenum;
687: beginline(MAYBE);
688: break;
689:
690: case 'H':
691: case 'M':
692: if (c == 'M')
693: {
694: int used = 0;
695:
696: for (n = 0; curwin->w_topline + n < curbuf->b_ml.ml_line_count; ++n)
697: if ((used += plines(curwin->w_topline + n)) >=
698: (curwin->w_height - curwin->w_empty_rows + 1) / 2)
699: break;
700: if (n && used > curwin->w_height)
701: --n;
702: }
703: else
704: n = Prenum;
705: op_motion_type = MLINE;
706: setpcmark();
707: curwin->w_cursor.lnum = curwin->w_topline + n;
708: if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
709: curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
710: cursor_correct(); /* correct for 'so' */
711: beginline(MAYBE);
712: break;
713:
714: case 'L':
715: op_motion_type = MLINE;
716: setpcmark();
717: curwin->w_cursor.lnum = curwin->w_botline - 1;
718: if (Prenum >= curwin->w_cursor.lnum)
719: curwin->w_cursor.lnum = 1;
720: else
721: curwin->w_cursor.lnum -= Prenum;
722: cursor_correct(); /* correct for 'so' */
723: beginline(MAYBE);
724: break;
725:
726: case 'l':
727: case K_RIGHT:
728: case ' ':
729: op_motion_type = MCHAR;
730: op_inclusive = FALSE;
731: n = Prenum1;
732: while (n--)
733: {
734: if (oneright() == FAIL)
735: {
736: /* space wraps to next line if 'whichwrap' bit 1 set */
737: /* 'l' wraps to next line if 'whichwrap' bit 2 set */
738: /* CURS_RIGHT wraps to next line if 'whichwrap' bit 3 set */
739: if (((c == ' ' && vim_strchr(p_ww, 's') != NULL) ||
740: (c == 'l' && vim_strchr(p_ww, 'l') != NULL) ||
741: (c == K_RIGHT && vim_strchr(p_ww, '>') != NULL)) &&
742: curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
743: {
744: /* When deleting we also count the NL as a character.
745: * Set op_inclusive when last char in the line is
746: * included, move to next line after that */
747: if ((op_type == DELETE || op_type == CHANGE) &&
748: !op_inclusive && !lineempty(curwin->w_cursor.lnum))
749: op_inclusive = TRUE;
750: else
751: {
752: ++curwin->w_cursor.lnum;
753: curwin->w_cursor.col = 0;
754: curwin->w_set_curswant = TRUE;
755: op_inclusive = FALSE;
756: }
757: continue;
758: }
759: if (op_type == NOP)
760: beep_flush();
761: else
762: {
763: if (lineempty(curwin->w_cursor.lnum))
764: clearopbeep();
765: else
766: {
767: op_inclusive = TRUE;
768: if (n)
769: beep_flush();
770: }
771: }
772: break;
773: }
774: }
775: break;
776:
777: case 'h':
778: case K_LEFT:
779: case K_BS:
780: case Ctrl('H'):
781: op_motion_type = MCHAR;
782: op_inclusive = FALSE;
783: n = Prenum1;
784: while (n--)
785: {
786: if (oneleft() == FAIL)
787: {
788: /* backspace and del wrap to previous line if 'whichwrap'
789: * bit 0 set.
790: * 'h' wraps to previous line if 'whichwrap' bit 2 set.
791: * CURS_LEFT wraps to previous line if 'whichwrap' bit 3
792: * set. */
793: if ( (((c == K_BS || c == Ctrl('H'))
794: && vim_strchr(p_ww, 'b') != NULL) ||
795: (c == 'h' && vim_strchr(p_ww, 'h') != NULL) ||
796: (c == K_LEFT && vim_strchr(p_ww, '<') != NULL)) &&
797: curwin->w_cursor.lnum > 1)
798: {
799: --(curwin->w_cursor.lnum);
800: coladvance(MAXCOL);
801: curwin->w_set_curswant = TRUE;
802:
803: /* When the NL before the first char has to be deleted we
804: * put the cursor on the NUL after the previous line.
805: * This is a very special case, be careful!
806: * don't adjust op_end now, otherwise it won't work */
807: if ((op_type == DELETE || op_type == CHANGE) &&
808: !lineempty(curwin->w_cursor.lnum))
809: {
810: ++curwin->w_cursor.col;
811: dont_adjust_op_end = TRUE;
812: }
813: continue;
814: }
815: else if (op_type != DELETE && op_type != CHANGE)
816: beep_flush();
817: else if (Prenum1 == 1)
818: clearopbeep();
819: break;
820: }
821: }
822: break;
823:
824: case '-':
825: flag = TRUE;
826: /* FALLTHROUGH */
827:
828: case 'k':
829: case K_UP:
830: case Ctrl('P'):
831: normal_k:
832: op_motion_type = MLINE;
833: if (cursor_up(Prenum1) == FAIL)
834: clearopbeep();
835: else if (flag)
836: beginline(TRUE);
837: break;
838:
839: case '+':
840: case CR:
841: flag = TRUE;
842: /* FALLTHROUGH */
843:
844: case 'j':
845: case K_DOWN:
846: case Ctrl('N'):
847: case NL:
848: normal_j:
849: op_motion_type = MLINE;
850: if (cursor_down(Prenum1) == FAIL)
851: clearopbeep();
852: else if (flag)
853: beginline(TRUE);
854: break;
855:
856: /*
857: * This is a strange motion command that helps make operators more
858: * logical. It is actually implemented, but not documented in the
859: * real 'vi'. This motion command actually refers to "the current
860: * line". Commands like "dd" and "yy" are really an alternate form of
861: * "d_" and "y_". It does accept a count, so "d3_" works to delete 3
862: * lines.
863: */
864: case '_':
865: lineop:
866: old_col = curwin->w_curswant;
867: op_motion_type = MLINE;
868: if (cursor_down((long)(Prenum1 - 1)) == FAIL)
869: clearopbeep();
870: if (op_type == DELETE || op_type == LSHIFT || op_type == RSHIFT)
871: beginline(MAYBE);
872: else if (op_type != YANK) /* 'Y' does not move cursor */
873: beginline(TRUE);
874: break;
875:
876: case K_HOME:
877: if ((mod_mask & MOD_MASK_CTRL))
878: goto goto_line_one;
879: Prenum = 1;
880: /* FALLTHROUGH */
881:
882: case '|':
883: op_motion_type = MCHAR;
884: op_inclusive = FALSE;
885: beginline(FALSE);
886: if (Prenum > 0)
887: {
888: coladvance((colnr_t)(Prenum - 1));
889: curwin->w_curswant = (colnr_t)(Prenum - 1);
890: }
891: else
892: curwin->w_curswant = 0;
893: /* keep curswant at the column where we wanted to go, not where
894: we ended; differs is line is too short */
895: curwin->w_set_curswant = FALSE;
896: break;
897:
898: /*
899: * Word Motions
900: */
901:
902: case 'B':
903: type = 1;
904: /* FALLTHROUGH */
905:
906: case 'b':
907: case K_S_LEFT:
908: op_motion_type = MCHAR;
909: op_inclusive = FALSE;
910: curwin->w_set_curswant = TRUE;
911: if (bck_word(Prenum1, type, FALSE) == FAIL)
912: clearopbeep();
913: break;
914:
915: case 'E':
916: type = 1;
917: /* FALLTHROUGH */
918:
919: case 'e':
920: op_inclusive = TRUE;
921: goto dowrdcmd;
922:
923: case 'W':
924: type = 1;
925: /* FALLTHROUGH */
926:
927: case 'w':
928: case K_S_RIGHT:
929: op_inclusive = FALSE;
930: flag = TRUE;
931: /*
932: * This is a little strange. To match what the real vi does, we
933: * effectively map 'cw' to 'ce', and 'cW' to 'cE', provided that we
934: * are not on a space or a TAB. This seems impolite at first, but it's
935: * really more what we mean when we say 'cw'.
936: * Another strangeness: When standing on the end of a word "ce" will
937: * change until the end of the next wordt, but "cw" will change only
938: * one character! This is done by setting type to 2.
939: */
940: if (op_type == CHANGE && (n = gchar_cursor()) != ' ' && n != TAB &&
941: n != NUL)
942: {
943: op_inclusive = TRUE;
944: flag = FALSE;
945: flag2 = TRUE;
946: }
947:
948: dowrdcmd:
949: op_motion_type = MCHAR;
950: curwin->w_set_curswant = TRUE;
951: if (flag)
952: n = fwd_word(Prenum1, type, op_type != NOP);
953: else
954: n = end_word(Prenum1, type, flag2, FALSE);
955: if (n == FAIL)
956: clearopbeep();
957: break;
958:
959: case K_END:
960: if ((mod_mask & MOD_MASK_CTRL))
961: goto goto_line;
962: /* FALLTHROUGH */
963:
964: case '$':
965: op_motion_type = MCHAR;
966: op_inclusive = TRUE;
967: curwin->w_curswant = MAXCOL; /* so we stay at the end */
968: if (cursor_down((long)(Prenum1 - 1)) == FAIL)
969: {
970: clearopbeep();
971: break;
972: }
973: break;
974:
975: case '^':
976: flag = TRUE;
977: /* FALLTHROUGH */
978:
979: case '0':
980: op_motion_type = MCHAR;
981: op_inclusive = FALSE;
982: beginline(flag);
983: break;
984:
985: /*
986: * 4: Searches
987: */
988: case '?':
989: case '/':
990: if ((searchbuff = getcmdline(c, Prenum1)) == NULL)
991: {
992: clearop();
993: break;
994: }
995: op_motion_type = MCHAR;
996: op_inclusive = FALSE;
997: curwin->w_set_curswant = TRUE;
998:
999: n = do_search(c, searchbuff, Prenum1,
1000: SEARCH_MARK | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG);
1001: if (n == 0)
1002: clearop();
1003: else if (n == 2)
1004: op_motion_type = MLINE;
1005: break;
1006:
1007: case 'N':
1008: flag = SEARCH_REV;
1009:
1010: case 'n':
1011: op_motion_type = MCHAR;
1012: op_inclusive = FALSE;
1013: curwin->w_set_curswant = TRUE;
1014: if (!do_search(0, NULL, Prenum1,
1015: SEARCH_MARK | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG | flag))
1016: clearop();
1017: break;
1018:
1019: /*
1020: * Character searches
1021: */
1022: case 'T':
1023: dir = BACKWARD;
1024: /* FALLTHROUGH */
1025:
1026: case 't':
1027: type = 1;
1028: goto docsearch;
1029:
1030: case 'F':
1031: dir = BACKWARD;
1032: /* FALLTHROUGH */
1033:
1034: case 'f':
1035: docsearch:
1036: op_motion_type = MCHAR;
1037: if (dir == BACKWARD)
1038: op_inclusive = FALSE;
1039: else
1040: op_inclusive = TRUE;
1041: curwin->w_set_curswant = TRUE;
1042: if (nchar >= 0x100 || !searchc(nchar, dir, type, Prenum1))
1043: clearopbeep();
1044: break;
1045:
1046: case ',':
1047: flag = 1;
1048: /* FALLTHROUGH */
1049:
1050: case ';':
1051: dir = flag;
1052: goto docsearch; /* nchar == NUL, thus repeat previous search */
1053:
1054: /*
1055: * section or C function searches
1056: */
1057: case '[':
1058: dir = BACKWARD;
1059: /* FALLTHROUGH */
1060:
1061: case ']':
1062: op_motion_type = MCHAR;
1063: op_inclusive = FALSE;
1064:
1065: /*
1066: * "[f" or "]f" : Edit file under the cursor (same as "gf")
1067: */
1068: if (nchar == 'f')
1069: goto gotofile;
1070:
1071: /*
1072: * Find the occurence(s) of the identifier or define under cursor
1073: * in current and included files or jump to the first occurence.
1074: *
1075: * search list jump
1076: * fwd bwd fwd bwd fwd bwd
1077: * identifier "]i" "[i" "]I" "[I" "]^I" "[^I"
1078: * define "]d" "[d" "]D" "[D" "]^D" "[^D"
1079: */
1080: if (nchar == 'i' || nchar == 'I' || nchar == Ctrl('I') ||
1081: nchar == 'd' || nchar == 'D' || nchar == Ctrl('D'))
1082: {
1083: int len;
1084:
1085: if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0)
1086: {
1087: clearop();
1088: break;
1089: }
1090: find_pattern_in_path(ptr, len, TRUE,
1091: Prenum == 0 ? !isupper(nchar) : FALSE,
1092: ((nchar & 0xf) == ('d' & 0xf)) ? FIND_DEFINE : FIND_ANY,
1093: Prenum1,
1094: isupper(nchar) ? ACTION_SHOW_ALL :
1095: islower(nchar) ? ACTION_SHOW : ACTION_GOTO,
1096: c == ']' ? curwin->w_cursor.lnum : (linenr_t)1,
1097: (linenr_t)MAXLNUM);
1098: curwin->w_set_curswant = TRUE;
1099: break;
1100: }
1101:
1102: /*
1103: * "[{", "[(", "]}" or "])": go to Nth unclosed '{', '(', '}' or ')'
1104: * "[#", "]#": go to start/end of Nth innermost #if..#endif construct.
1105: * "[/", "[*", "]/", "]*": go to Nth comment start/end.
1106: */
1107: if ((c == '[' && vim_strchr((char_u *)"{(*/#", nchar) != NULL) ||
1108: (c == ']' && vim_strchr((char_u *)"})*/#", nchar) != NULL))
1109: {
1110: FPOS old_pos;
1111: FPOS new_pos;
1112:
1113: if (nchar == '*')
1114: nchar = '/';
1115: old_pos = curwin->w_cursor;
1116: new_pos.lnum = 0;
1117: while (Prenum1--)
1118: {
1119: if ((pos = findmatchlimit(nchar,
1120: (c == '[') ? FM_BACKWARD : FM_FORWARD, 0)) == NULL)
1121: {
1122: if (new_pos.lnum == 0) /* nothing found */
1123: clearopbeep();
1124: else
1125: pos = &new_pos; /* use last one found */
1126: break;
1127: }
1128: curwin->w_cursor = *pos;
1129: new_pos= *pos;
1130: }
1131: curwin->w_cursor = old_pos;
1132: if (pos != NULL)
1133: {
1134: setpcmark();
1135: curwin->w_cursor = *pos;
1136: curwin->w_set_curswant = TRUE;
1137: }
1138: break;
1139: }
1140:
1141: /*
1142: * "[[", "[]", "]]" and "][": move to start or end of function
1143: */
1144: if (nchar == '[' || nchar == ']')
1145: {
1146: if (nchar == c) /* "]]" or "[[" */
1147: flag = '{';
1148: else
1149: flag = '}'; /* "][" or "[]" */
1150:
1151: curwin->w_set_curswant = TRUE;
1152: /*
1153: * Imitate strange vi behaviour: When using "]]" with an operator
1154: * we also stop at '}'.
1155: */
1156: if (!findpar(dir, Prenum1, flag,
1157: (op_type != NOP && dir == FORWARD && flag == '{')))
1158: clearopbeep();
1159: else if (op_type == NOP)
1160: beginline(TRUE);
1161: break;
1162: }
1163:
1164: /*
1165: * "[p", "[P", "]P" and "]p": put with indent adjustment
1166: */
1167: if (nchar == 'p' || nchar == 'P')
1168: {
1169: if (checkclearopq())
1170: break;
1171: prep_redo(Prenum, NUL, c, nchar, NUL);
1172: do_put((c == ']' && nchar == 'p') ? FORWARD : BACKWARD,
1173: Prenum1, TRUE);
1174: break;
1175: }
1176:
1177: #ifdef USE_MOUSE
1178: /*
1179: * [ or ] followed by a middle mouse click: put selected text with
1180: * indent adjustment. Any other button just does as usual.
1181: */
1182: if (nchar >= K_LEFTMOUSE && nchar <= K_RIGHTRELEASE)
1183: {
1184: (void)do_mouse(nchar, (c == ']') ? FORWARD : BACKWARD,
1185: Prenum1, TRUE);
1186: break;
1187: }
1188: #endif /* USE_MOUSE */
1189:
1190: /*
1191: * end of '[' and ']': not a valid nchar
1192: */
1193: clearopbeep();
1194: break;
1195:
1196: case '%':
1197: op_inclusive = TRUE;
1198: if (Prenum) /* {cnt}% : goto {cnt} percentage in file */
1199: {
1200: if (Prenum > 100)
1201: clearopbeep();
1202: else
1203: {
1204: op_motion_type = MLINE;
1205: setpcmark();
1206: /* round up, so CTRL-G will give same value */
1207: curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count *
1208: Prenum + 99) / 100;
1209: beginline(MAYBE);
1210: }
1211: }
1212: else /* % : go to matching paren */
1213: {
1214: op_motion_type = MCHAR;
1215: if ((pos = findmatch(NUL)) == NULL)
1216: clearopbeep();
1217: else
1218: {
1219: setpcmark();
1220: curwin->w_cursor = *pos;
1221: curwin->w_set_curswant = TRUE;
1222: }
1223: }
1224: break;
1225:
1226: case '(':
1227: dir = BACKWARD;
1228: /* FALLTHROUGH */
1229:
1230: case ')':
1231: op_motion_type = MCHAR;
1232: if (c == ')')
1233: op_inclusive = FALSE;
1234: else
1235: op_inclusive = TRUE;
1236: curwin->w_set_curswant = TRUE;
1237:
1238: if (findsent(dir, Prenum1) == FAIL)
1239: clearopbeep();
1240: break;
1241:
1242: case '{':
1243: dir = BACKWARD;
1244: /* FALLTHROUGH */
1245:
1246: case '}':
1247: op_motion_type = MCHAR;
1248: op_inclusive = FALSE;
1249: curwin->w_set_curswant = TRUE;
1250: if (!findpar(dir, Prenum1, NUL, FALSE))
1251: clearopbeep();
1252: break;
1253:
1254: /*
1255: * 5: Edits
1256: */
1257: case '.': /* redo command */
1258: if (checkclearopq())
1259: break;
1260: /*
1261: * if restart_edit is TRUE, the last but one command is repeated
1262: * instead of the last command (inserting text). This is used for
1263: * CTRL-O <.> in insert mode
1264: */
1265: if (start_redo(Prenum, restart_edit && !arrow_used) == FAIL)
1266: clearopbeep();
1267: break;
1268:
1269: case 'u': /* undo */
1270: if (VIsual_active || op_type == vim_strchr(opchars, 'u') - opchars + 1)
1271: goto dooperator;
1272: case K_UNDO:
1273: if (checkclearopq())
1274: break;
1275: u_undo((int)Prenum1);
1276: curwin->w_set_curswant = TRUE;
1277: break;
1278:
1279: case Ctrl('R'): /* undo undo */
1280: if (checkclearopq())
1281: break;
1282: u_redo((int)Prenum1);
1283: curwin->w_set_curswant = TRUE;
1284: break;
1285:
1286: case 'U': /* Undo line */
1287: if (VIsual_active || op_type == vim_strchr(opchars, 'U') - opchars + 1)
1288: goto dooperator;
1289: if (checkclearopq())
1290: break;
1291: u_undoline();
1292: curwin->w_set_curswant = TRUE;
1293: break;
1294:
1295: case 'r':
1296: if (VIsual_active)
1297: {
1298: c = 'c';
1299: goto dooperator;
1300: }
1301: if (checkclearop())
1302: break;
1303: ptr = ml_get_cursor();
1304: /* special key or not enough characters to replace */
1305: if (nchar >= 0x100 || STRLEN(ptr) < (unsigned)Prenum1)
1306: {
1307: clearopbeep();
1308: break;
1309: }
1310: /*
1311: * Replacing with a TAB is done by edit(), because it is complicated
1312: * when 'expandtab' is set.
1313: * Other characters are done below to avoid problems with things like
1314: * CTRL-V 048 (for edit() this would be R CTRL-V 0 ESC).
1315: */
1316: if (nchar == '\t' && curbuf->b_p_et)
1317: {
1318: prep_redo(Prenum1, NUL, 'r', '\t', NUL);
1319: stuffnumReadbuff(Prenum1);
1320: stuffcharReadbuff('R');
1321: stuffcharReadbuff('\t');
1322: stuffcharReadbuff(ESC);
1323: break;
1324: }
1325:
1326: if (nchar == Ctrl('V')) /* get another character */
1327: {
1328: c = Ctrl('V');
1329: nchar = get_literal();
1330: }
1331: else
1332: c = NUL;
1333: prep_redo(Prenum1, NUL, 'r', c, nchar);
1334: if (u_save_cursor() == FAIL) /* save line for undo */
1335: break;
1336: /*
1337: * Replace characters by a newline.
1338: * Strange vi behaviour: Only one newline is inserted.
1339: * Delete the characters here.
1340: * Insert the newline with an insert command, takes care of
1341: * autoindent.
1342: */
1343: if (c != Ctrl('V') && (nchar == '\r' || nchar == '\n'))
1344: {
1345: while (Prenum1--) /* delete the characters */
1346: delchar(FALSE);
1347: /* replacing the last character of a line is different */
1348: if (curwin->w_cursor.col > 0 && gchar_cursor() == NUL)
1349: {
1350: --curwin->w_cursor.col;
1351: stuffcharReadbuff('a');
1352: }
1353: else
1354: stuffcharReadbuff('i');
1355: stuffcharReadbuff('\r');
1356: stuffcharReadbuff(ESC);
1357: }
1358: else
1359: {
1360: while (Prenum1--) /* replace the characters */
1361: {
1362: /*
1363: * Replace a 'normal' character.
1364: * Get ptr again, because u_save and/or showmatch() will have
1365: * released the line. At the same time we let know that the
1366: * line will be changed.
1367: */
1368: ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE);
1369: ptr[curwin->w_cursor.col] = nchar;
1370: if (p_sm && (nchar == ')' || nchar == '}' || nchar == ']'))
1371: showmatch();
1372: ++curwin->w_cursor.col;
1373: }
1374: --curwin->w_cursor.col; /* cursor on the last replaced char */
1375: }
1376: curwin->w_set_curswant = TRUE;
1377: CHANGED;
1378: updateline();
1379: set_last_insert(nchar);
1380: break;
1381:
1382: case 'J':
1383: if (VIsual_active) /* join the visual lines */
1384: goto dooperator;
1385: if (checkclearop())
1386: break;
1387: if (Prenum <= 1)
1388: Prenum = 2; /* default for join is two lines! */
1389: if (curwin->w_cursor.lnum + Prenum - 1 > curbuf->b_ml.ml_line_count)
1390: {
1391: clearopbeep(); /* beyond last line */
1392: break;
1393: }
1394:
1395: prep_redo(Prenum, NUL, 'J', NUL, NUL);
1396: do_do_join(Prenum, TRUE, TRUE);
1397: break;
1398:
1399: case 'P':
1400: dir = BACKWARD;
1401: /* FALLTHROUGH */
1402:
1403: case 'p':
1404: /*
1405: * 'P' after an operator or with Visual: Set current block.
1406: * 'p' after an operator or with Visual: Set current paragraph.
1407: */
1408: if (op_type != NOP || VIsual_active)
1409: {
1410: if (c == 'P')
1411: {
1412: if (current_block('{', Prenum1) == FAIL)
1413: clearopbeep();
1414: }
1415: else
1416: {
1417: if (current_par(c, Prenum1) == FAIL)
1418: clearopbeep();
1419: }
1420: curwin->w_set_curswant = TRUE;
1421: }
1422: else
1423: {
1424: prep_redo(Prenum, NUL, c, NUL, NUL);
1425: do_put(dir, Prenum1, FALSE);
1426: }
1427: break;
1428:
1429: case Ctrl('A'): /* add to number */
1430: case Ctrl('X'): /* subtract from number */
1431: if (checkclearopq())
1432: break;
1433: if (do_addsub((int)c, Prenum1) == OK)
1434: prep_redo(Prenum1, NUL, c, NUL, NUL);
1435: break;
1436:
1437: /*
1438: * 6: Inserts
1439: */
1440: case 'A':
1441: type = 1;
1442: /* FALLTHROUGH */
1443:
1444: case 'a':
1445: if (op_type != NOP || VIsual_active)
1446: {
1447: if (current_word(Prenum1, type) == FAIL)
1448: clearopbeep();
1449: curwin->w_set_curswant = TRUE;
1450: }
1451: else
1452: {
1453: if (c == 'A')
1454: {
1455: curwin->w_set_curswant = TRUE;
1456: while (oneright() == OK)
1457: ;
1458: }
1459:
1460: /* Works just like an 'i'nsert on the next character. */
1461: if (u_save_cursor() == OK)
1462: {
1463: if (!lineempty(curwin->w_cursor.lnum))
1464: inc_cursor();
1465: command_busy = edit(c, FALSE, Prenum1);
1466: }
1467: }
1468: break;
1469:
1470: case 'I':
1471: if (checkclearopq())
1472: break;
1473: beginline(TRUE);
1474: /* FALLTHROUGH */
1475:
1476: case 'i':
1477: case K_INS:
1478: insert_command:
1479: if (checkclearopq())
1480: break;
1481: if (u_save_cursor() == OK)
1482: command_busy = edit(c, FALSE, Prenum1);
1483: break;
1484:
1485: case 'o':
1486: if (VIsual_active) /* switch start and end of visual */
1487: {
1488: Prenum = VIsual.lnum;
1489: VIsual.lnum = curwin->w_cursor.lnum;
1490: curwin->w_cursor.lnum = Prenum;
1491: n = VIsual.col;
1492: VIsual.col = curwin->w_cursor.col;
1493: curwin->w_cursor.col = (int)n;
1494: curwin->w_set_curswant = TRUE;
1495: break;
1496: }
1497: if (checkclearop())
1498: break;
1499: if (has_format_option(FO_OPEN_COMS))
1500: fo_do_comments = TRUE;
1501: if (u_save(curwin->w_cursor.lnum,
1502: (linenr_t)(curwin->w_cursor.lnum + 1)) == OK &&
1503: Opencmd(FORWARD, TRUE, FALSE))
1504: command_busy = edit('o', TRUE, Prenum1);
1505: fo_do_comments = FALSE;
1506: break;
1507:
1508: case 'O':
1509: if (checkclearopq())
1510: break;
1511: if (has_format_option(FO_OPEN_COMS))
1512: fo_do_comments = TRUE;
1513: if (u_save((linenr_t)(curwin->w_cursor.lnum - 1),
1514: curwin->w_cursor.lnum) == OK && Opencmd(BACKWARD, TRUE, FALSE))
1515: command_busy = edit('O', TRUE, Prenum1);
1516: fo_do_comments = FALSE;
1517: break;
1518:
1519: case 'R':
1520: if (VIsual_active)
1521: {
1522: c = 'c';
1523: VIsual_mode = 'V';
1524: goto dooperator;
1525: }
1526: if (checkclearopq())
1527: break;
1528: if (u_save_cursor() == OK)
1529: command_busy = edit('R', FALSE, Prenum1);
1530: break;
1531:
1532: /*
1533: * 7: Operators
1534: */
1535: case '~': /* swap case */
1536: /*
1537: * if tilde is not an operator and Visual is off: swap case
1538: * of a single character
1539: */
1540: if (!p_to && !VIsual_active &&
1541: op_type != vim_strchr(opchars, '~') - opchars + 1)
1542: {
1543: if (checkclearopq())
1544: break;
1545: if (lineempty(curwin->w_cursor.lnum))
1546: {
1547: clearopbeep();
1548: break;
1549: }
1550: prep_redo(Prenum, NUL, '~', NUL, NUL);
1551:
1552: if (u_save_cursor() == FAIL)
1553: break;
1554:
1555: for (; Prenum1 > 0; --Prenum1)
1556: {
1557: if (gchar_cursor() == NUL)
1558: break;
1559: swapchar(&curwin->w_cursor);
1560: inc_cursor();
1561: }
1562:
1563: curwin->w_set_curswant = TRUE;
1564: CHANGED;
1565: updateline();
1566: break;
1567: }
1568: /*FALLTHROUGH*/
1569:
1570: case 'd':
1571: case 'c':
1572: case 'y':
1573: case '>':
1574: case '<':
1575: case '!':
1576: case '=':
1577: case 'Q': /* should start Ex mode */
1578: dooperator:
1579: n = vim_strchr(opchars, c) - opchars + 1;
1580: if (n == op_type) /* double operator works on lines */
1581: goto lineop;
1582: if (checkclearop())
1583: break;
1584: if (Prenum != 0)
1585: opnum = Prenum;
1586: curbuf->b_op_start = curwin->w_cursor;
1587: op_type = (int)n;
1588: break;
1589:
1590: /*
1591: * 8: Abbreviations
1592: */
1593:
1594: /* when Visual the next commands are operators */
1595: case K_DEL:
1596: c = 'x'; /* DEL key behaves like 'x' */
1597: case 'S':
1598: case 'Y':
1599: case 'D':
1600: case 'C':
1601: case 'x':
1602: case 'X':
1603: case 's':
1604: /*
1605: * 's' or 'S' with an operator: Operate on sentence or section.
1606: */
1607: if (op_type != NOP || VIsual_active)
1608: {
1609: if (c == 's') /* sentence */
1610: {
1611: if (current_sent(Prenum1) == FAIL)
1612: clearopbeep();
1613: curwin->w_set_curswant = TRUE;
1614: break;
1615: }
1616: if (c == 'S') /* block with () */
1617: {
1618: if (current_block('(', Prenum1) == FAIL)
1619: clearopbeep();
1620: curwin->w_set_curswant = TRUE;
1621: break;
1622: }
1623: }
1624: if (VIsual_active)
1625: {
1626: static char_u trans[] = "YyDdCcxdXd";
1627:
1628: /* uppercase means linewise */
1629: if (isupper(c) && VIsual_mode != Ctrl('V'))
1630: VIsual_mode = 'V';
1631: c = *(vim_strchr(trans, c) + 1);
1632: goto dooperator;
1633: }
1634:
1635: case '&':
1636: if (checkclearopq())
1637: break;
1638: if (Prenum)
1639: stuffnumReadbuff(Prenum);
1640:
1641: {
1642: static char_u *(ar[8]) = {(char_u *)"dl", (char_u *)"dh",
1643: (char_u *)"d$", (char_u *)"c$",
1644: (char_u *)"cl", (char_u *)"cc",
1645: (char_u *)"yy", (char_u *)":s\r"};
1646: static char_u *str = (char_u *)"xXDCsSY&";
1647:
1648: stuffReadbuff(ar[(int)(vim_strchr(str, c) - str)]);
1649: }
1650: break;
1651:
1652: /*
1653: * 9: Marks
1654: */
1655:
1656: case 'm':
1657: if (checkclearop())
1658: break;
1659: if (setmark(nchar) == FAIL)
1660: clearopbeep();
1661: break;
1662:
1663: case '\'':
1664: flag = TRUE;
1665: /* FALLTHROUGH */
1666:
1667: case '`':
1668: pos = getmark(nchar, (op_type == NOP));
1669: if (pos == (FPOS *)-1) /* jumped to other file */
1670: {
1671: if (flag)
1672: beginline(TRUE);
1673: break;
1674: }
1675:
1676: cursormark:
1677: if (check_mark(pos) == FAIL)
1678: clearop();
1679: else
1680: {
1681: if (c == '\'' || c == '`')
1682: setpcmark();
1683: curwin->w_cursor = *pos;
1684: if (flag)
1685: beginline(TRUE);
1686: }
1687: op_motion_type = flag ? MLINE : MCHAR;
1688: op_inclusive = FALSE; /* ignored if not MCHAR */
1689: curwin->w_set_curswant = TRUE;
1690: break;
1691:
1692: case Ctrl('O'): /* goto older pcmark */
1693: Prenum1 = -Prenum1;
1694: /* FALLTHROUGH */
1695:
1696: case Ctrl('I'): /* goto newer pcmark */
1697: if (checkclearopq())
1698: break;
1699: pos = movemark((int)Prenum1);
1700: if (pos == (FPOS *)-1) /* jump to other file */
1701: {
1702: curwin->w_set_curswant = TRUE;
1703: break;
1704: }
1705: if (pos != NULL) /* can jump */
1706: goto cursormark;
1707: clearopbeep();
1708: break;
1709:
1710: /*
1711: * 10. Buffer setting
1712: */
1713: case '"':
1714: if (checkclearop())
1715: break;
1716: if (nchar != NUL && is_yank_buffer(nchar, FALSE))
1717: {
1718: yankbuffer = nchar;
1719: opnum = Prenum; /* remember count before '"' */
1720: }
1721: else
1722: clearopbeep();
1723: break;
1724:
1725: /*
1726: * 11. Visual
1727: */
1728: case 'v':
1729: case 'V':
1730: case Ctrl('V'):
1731: if (checkclearop())
1732: break;
1733:
1734: /* change Visual mode */
1735: if (VIsual_active)
1736: {
1737: if (VIsual_mode == c) /* stop visual mode */
1738: {
1739: end_visual_mode();
1740: }
1741: else /* toggle char/block mode */
1742: { /* or char/line mode */
1743: VIsual_mode = c;
1744: showmode();
1745: }
1746: update_curbuf(NOT_VALID); /* update the inversion */
1747: }
1748: /* start Visual mode */
1749: else
1750: {
1751: VIsual_save = VIsual; /* keep for "gv" */
1752: VIsual_mode_save = VIsual_mode;
1753: start_visual_highlight();
1754: if (Prenum) /* use previously selected part */
1755: {
1756: if (resel_VIsual_mode == NUL) /* there is none */
1757: {
1758: beep_flush();
1759: break;
1760: }
1761: VIsual = curwin->w_cursor;
1762: VIsual_active = TRUE;
1763: #ifdef USE_MOUSE
1764: setmouse();
1765: #endif
1766: if (p_smd)
1767: redraw_cmdline = TRUE; /* show visual mode later */
1768: /*
1769: * For V and ^V, we multiply the number of lines even if there
1770: * was only one -- webb
1771: */
1772: if (resel_VIsual_mode != 'v' || resel_VIsual_line_count > 1)
1773: {
1774: curwin->w_cursor.lnum += resel_VIsual_line_count * Prenum - 1;
1775: if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
1776: curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
1777: }
1778: VIsual_mode = resel_VIsual_mode;
1779: if (VIsual_mode == 'v')
1780: {
1781: if (resel_VIsual_line_count <= 1)
1782: curwin->w_cursor.col += resel_VIsual_col * Prenum - 1;
1783: else
1784: curwin->w_cursor.col = resel_VIsual_col;
1785: }
1786: if (resel_VIsual_col == MAXCOL)
1787: {
1788: curwin->w_curswant = MAXCOL;
1789: coladvance(MAXCOL);
1790: }
1791: else if (VIsual_mode == Ctrl('V'))
1792: {
1793: curwin->w_curswant = curwin->w_virtcol +
1794: resel_VIsual_col * Prenum - 1;
1795: coladvance((colnr_t)curwin->w_curswant);
1796: }
1797: else
1798: curwin->w_set_curswant = TRUE;
1799: curs_columns(TRUE); /* recompute w_virtcol */
1800: update_curbuf(NOT_VALID); /* show the inversion */
1801: }
1802: else
1803: {
1804: VIsual = curwin->w_cursor;
1805: VIsual_mode = c;
1806: VIsual_active = TRUE;
1807: #ifdef USE_MOUSE
1808: setmouse();
1809: #endif
1810: if (p_smd)
1811: redraw_cmdline = TRUE; /* show visual mode later */
1812: updateline(); /* start the inversion */
1813: }
1814: }
1815: break;
1816:
1817: /*
1818: * 12. Suspend
1819: */
1820:
1821: case Ctrl('Z'):
1822: clearop();
1823: if (VIsual_active)
1824: end_visual_mode(); /* stop Visual */
1825: stuffReadbuff((char_u *)":st\r"); /* with autowrite */
1826: break;
1827:
1828: /*
1829: * 13. Window commands
1830: */
1831:
1832: case Ctrl('W'):
1833: if (checkclearop())
1834: break;
1835: do_window(nchar, Prenum); /* everything is in window.c */
1836: break;
1837:
1838: /*
1839: * 14. extended commands (starting with 'g')
1840: */
1841: case 'g':
1842: switch (nchar)
1843: {
1844: /*
1845: * "gv": reselect the previous visual area
1846: */
1847: case 'v':
1848: if (checkclearop())
1849: break;
1850: if (VIsual_active)
1851: pos = &VIsual_save;
1852: else
1853: pos = &VIsual;
1854: if (pos->lnum == 0 || pos->lnum > curbuf->b_ml.ml_line_count ||
1855: VIsual_end.lnum == 0)
1856: beep_flush();
1857: else
1858: {
1859: FPOS tt;
1860: int t;
1861:
1862: /* exchange previous and current visual area */
1863: if (VIsual_active)
1864: {
1865: tt = VIsual;
1866: VIsual = VIsual_save;
1867: VIsual_save = tt;
1868: t = VIsual_mode;
1869: VIsual_mode = VIsual_mode_save;
1870: VIsual_mode_save = t;
1871: tt = curwin->w_cursor;
1872: }
1873: curwin->w_cursor = VIsual_end;
1874: if (VIsual_active)
1875: VIsual_end = tt;
1876: check_cursor();
1877: VIsual_active = TRUE;
1878: #ifdef USE_MOUSE
1879: setmouse();
1880: #endif
1881: update_curbuf(NOT_VALID);
1882: showmode();
1883: }
1884: break;
1885:
1886: /*
1887: * "gj" and "gk" two new funny movement keys -- up and down
1888: * movement based on *screen* line rather than *file* line.
1889: */
1890: case 'j':
1891: case K_DOWN:
1892: if (!curwin->w_p_wrap)
1893: goto normal_j;
1894: if (screengo(FORWARD, Prenum1) == FAIL)
1895: clearopbeep();
1896: break;
1897:
1898: case 'k':
1899: case K_UP:
1900: if (!curwin->w_p_wrap)
1901: goto normal_k;
1902: if (screengo(BACKWARD, Prenum1) == FAIL)
1903: clearopbeep();
1904: break;
1905:
1906: /*
1907: * "g0", "g^" and "g$": Like "0", "^" and "$" but for screen lines.
1908: */
1909: case '^':
1910: flag = TRUE;
1911: /* FALLTHROUGH */
1912:
1913: case '0':
1914: case K_HOME:
1915: op_motion_type = MCHAR;
1916: op_inclusive = FALSE;
1917: if (curwin->w_p_wrap)
1918: {
1919: n = ((curwin->w_virtcol + (curwin->w_p_nu ? 8 : 0)) /
1920: Columns) * Columns;
1921: if (curwin->w_p_nu && n > 8)
1922: n -= 8;
1923: }
1924: else
1925: n = curwin->w_leftcol;
1926: coladvance((colnr_t)n);
1927: if (flag)
1928: while (vim_iswhite(gchar_cursor()) && oneright() == OK)
1929: ;
1930: curwin->w_set_curswant = TRUE;
1931: break;
1932:
1933: case '$':
1934: case K_END:
1935: op_motion_type = MCHAR;
1936: op_inclusive = TRUE;
1937: if (curwin->w_p_wrap)
1938: {
1939: curwin->w_curswant = MAXCOL; /* so we stay at the end */
1940: if (Prenum1 == 1)
1941: {
1942: n = ((curwin->w_virtcol + (curwin->w_p_nu ? 8 : 0)) /
1943: Columns + 1) * Columns - 1;
1944: if (curwin->w_p_nu && n > 8)
1945: n -= 8;
1946: coladvance((colnr_t)n);
1947: }
1948: else if (screengo(FORWARD, Prenum1 - 1) == FAIL)
1949: clearopbeep();
1950: }
1951: else
1952: {
1953: n = curwin->w_leftcol + Columns - 1;
1954: if (curwin->w_p_nu)
1955: n -= 8;
1956: coladvance((colnr_t)n);
1957: curwin->w_set_curswant = TRUE;
1958: }
1959: break;
1960:
1961: /*
1962: * "g*" and "g#", like "*" and "#" but without using "\<" and "\>"
1963: */
1964: case '*':
1965: case '#':
1966: goto search_word;
1967:
1968: /*
1969: * ge and gE: go back to end of word
1970: */
1971: case 'e':
1972: case 'E':
1973: op_motion_type = MCHAR;
1974: curwin->w_set_curswant = TRUE;
1975: op_inclusive = TRUE;
1976: if (bckend_word(Prenum1, nchar == 'E', FALSE) == FAIL)
1977: clearopbeep();
1978: break;
1979:
1980: /*
1981: * g CTRL-G: display info about cursor position
1982: */
1983: case Ctrl('G'):
1984: cursor_pos_info();
1985: break;
1986:
1987: /*
1988: * "gI": Start insert in column 1.
1989: */
1990: case 'I':
1991: beginline(FALSE);
1992: goto insert_command;
1993:
1994: /*
1995: * "gf": goto file, edit file under cursor
1996: * "]f" and "[f": can also be used.
1997: */
1998: case 'f':
1999: gotofile:
2000: ptr = file_name_at_cursor(FNAME_MESS|FNAME_HYP|FNAME_EXP);
2001: if (ptr != NULL)
2002: {
2003: /* do autowrite if necessary */
2004: if (curbuf->b_changed && curbuf->b_nwindows <= 1 && !p_hid)
2005: autowrite(curbuf);
2006: setpcmark();
2007: (void)do_ecmd(0, ptr, NULL, NULL, p_hid, (linenr_t)0,
2008: FALSE);
2009: vim_free(ptr);
2010: }
2011: else
2012: clearop();
2013: break;
2014:
2015: /*
2016: * "gs": Goto sleep, but keep on checking for CTRL-C
2017: */
2018: case 's':
2019: while (Prenum1-- && !got_int)
2020: {
2021: mch_delay(1000L, TRUE);
2022: mch_breakcheck();
2023: }
2024: break;
2025:
2026: /*
2027: * "ga": Display the ascii value of the character under the
2028: * cursor. It is displayed in decimal, hex, and octal. -- webb
2029: */
2030: case 'a':
2031: do_ascii();
2032: break;
2033:
2034: /*
2035: * "gg": Goto the first line in file. With a count it goes to
2036: * that line number like for G. -- webb
2037: */
2038: case 'g':
2039: goto_line_one:
2040: if (Prenum == 0)
2041: Prenum = 1;
2042: goto goto_line;
2043:
2044: /*
2045: * Operater to format text:
2046: * gq same as 'Q' operator.
2047: * Operators to change the case of text:
2048: * g~ Toggle the case of the text.
2049: * gu Change text to lower case.
2050: * gU Change text to upper case.
2051: * --webb
2052: */
2053: case 'q':
2054: case '~':
2055: case 'u':
2056: case 'U':
2057: prechar = c;
2058: c = nchar;
2059: goto dooperator;
2060:
2061: /*
2062: * "gd": Find first occurence of pattern under the cursor in the
2063: * current function
2064: * "gD": idem, but in the current file.
2065: */
2066: case 'd':
2067: case 'D':
2068: do_gd(nchar);
2069: break;
2070:
2071: #ifdef USE_MOUSE
2072: /*
2073: * g<*Mouse> : <C-*mouse>
2074: */
2075: case K_MIDDLEMOUSE:
2076: case K_MIDDLEDRAG:
2077: case K_MIDDLERELEASE:
2078: case K_LEFTMOUSE:
2079: case K_LEFTDRAG:
2080: case K_LEFTRELEASE:
2081: case K_RIGHTMOUSE:
2082: case K_RIGHTDRAG:
2083: case K_RIGHTRELEASE:
2084: mod_mask = MOD_MASK_CTRL;
2085: (void)do_mouse(nchar, BACKWARD, Prenum1, FALSE);
2086: break;
2087:
2088: case K_IGNORE:
2089: break;
2090: #endif
2091:
2092: default:
2093: clearopbeep();
2094: break;
2095: }
2096: break;
2097:
2098: /*
2099: * 15. mouse click
2100: */
2101: #ifdef USE_MOUSE
2102: case K_MIDDLEMOUSE:
2103: case K_MIDDLEDRAG:
2104: case K_MIDDLERELEASE:
2105: case K_LEFTMOUSE:
2106: case K_LEFTDRAG:
2107: case K_LEFTRELEASE:
2108: case K_RIGHTMOUSE:
2109: case K_RIGHTDRAG:
2110: case K_RIGHTRELEASE:
2111: (void)do_mouse(c, BACKWARD, Prenum1, FALSE);
2112: break;
2113:
2114: case K_IGNORE:
2115: break;
2116: #endif
2117:
2118: #ifdef USE_GUI
2119: /*
2120: * 16. scrollbar movement
2121: */
2122: case K_SCROLLBAR:
2123: if (op_type != NOP)
2124: clearopbeep();
2125:
2126: /* Even if an operator was pending, we still want to scroll */
2127: gui_do_scroll();
2128: break;
2129:
2130: case K_HORIZ_SCROLLBAR:
2131: if (op_type != NOP)
2132: clearopbeep();
2133:
2134: /* Even if an operator was pending, we still want to scroll */
2135: gui_do_horiz_scroll();
2136: break;
2137: #endif
2138:
2139: /*
2140: * 17. The end
2141: */
2142: case ESC:
2143: /* Don't drop through and beep if we are canceling a command: */
2144: if (!VIsual_active && (op_type != NOP ||
2145: opnum || Prenum || yankbuffer))
2146: {
2147: clearop(); /* don't beep */
2148: break;
2149: }
2150: if (VIsual_active)
2151: {
2152: end_visual_mode(); /* stop Visual */
2153: update_curbuf(NOT_VALID);
2154: clearop(); /* don't beep */
2155: break;
2156: }
2157: /* ESC in normal mode: beep, but don't flush buffers */
2158: clearop();
2159: vim_beep();
2160: break;
2161:
2162: default: /* not a known command */
2163: clearopbeep();
2164: break;
2165:
2166: } /* end of switch on command character */
2167:
2168: /*
2169: * if we didn't start or finish an operator, reset yankbuffer, unless we
2170: * need it later.
2171: */
2172: if (!finish_op && !op_type && vim_strchr((char_u *)"\"DCYSsXx.", c) == NULL)
2173: yankbuffer = 0;
2174:
2175: /*
2176: * If an operation is pending, handle it...
2177: */
2178: do_pending_operator(c, nchar, finish_op, searchbuff,
2179: &command_busy, old_col, FALSE, dont_adjust_op_end);
2180:
2181: normal_end:
2182: if (op_type == NOP && yankbuffer == 0)
2183: clear_showcmd();
2184:
2185: if (restart_edit && op_type == NOP && !VIsual_active
2186: && !command_busy && stuff_empty() && yankbuffer == 0)
2187: (void)edit(restart_edit, FALSE, 1L);
2188:
2189: checkpcmark(); /* check if we moved since setting pcmark */
2190: vim_free(searchbuff);
2191:
2192: /*
2193: * Update the other windows for the current buffer if modified has been set in
2194: * set_Changed() (This should be done more efficiently)
2195: */
2196: if (modified)
2197: {
2198: WIN *wp;
2199:
2200: for (wp = firstwin; wp; wp = wp->w_next)
2201: if (wp != curwin && wp->w_buffer == curbuf)
2202: {
2203: cursor_off();
2204: wp->w_redr_type = NOT_VALID;
2205: /*
2206: * don't do the actual redraw if wait_return() has just been
2207: * called and the user typed a ":"
2208: */
2209: if (!skip_redraw)
2210: win_update(wp);
2211: }
2212: modified = FALSE;
2213: }
2214: }
2215:
2216: /*
2217: * Handle an operator after visual mode or when the movement is finished
2218: */
2219: void
2220: do_pending_operator(c, nchar, finish_op, searchbuff, command_busy,
2221: old_col, gui_yank, dont_adjust_op_end)
2222: register int c;
2223: int nchar;
2224: int finish_op;
2225: char_u *searchbuff;
2226: int *command_busy;
2227: int old_col;
2228: int gui_yank; /* yanking visual area for GUI */
2229: int dont_adjust_op_end;
2230: {
2231: /* The visual area is remembered for redo */
2232: static int redo_VIsual_mode = NUL; /* 'v', 'V', or Ctrl-V */
2233: static linenr_t redo_VIsual_line_count; /* number of lines */
2234: static colnr_t redo_VIsual_col; /* number of cols or end column */
2235: static long redo_VIsual_Prenum; /* Prenum for operator */
2236:
2237: linenr_t Prenum1 = 1L;
2238: FPOS old_cursor;
2239: int VIsual_was_active = VIsual_active;
2240: int redraw;
2241:
2242: #ifdef USE_GUI
2243: /*
2244: * Yank the visual area into the GUI selection register before we operate
2245: * on it and lose it forever. This could call do_pending_operator()
2246: * recursively, but that's OK because gui_yank will be TRUE for the
2247: * nested call. Note also that we call gui_copy_selection() and not
2248: * gui_auto_select(). This is because even when 'autoselect' is not set,
2249: * if we operate on the text, eg by deleting it, then this is considered to
2250: * be an explicit request for it to be put in the global cut buffer, so we
2251: * always want to do it here. -- webb
2252: */
2253: if (gui.in_use && op_type != NOP && !gui_yank && VIsual_active
2254: && !redo_VIsual_busy)
2255: gui_copy_selection();
2256: #endif
2257: old_cursor = curwin->w_cursor;
2258:
2259: /*
2260: * If an operation is pending, handle it...
2261: */
2262: if ((VIsual_active || finish_op) && op_type != NOP)
2263: {
2264: op_is_VIsual = VIsual_active;
2265: if (op_type != YANK && !VIsual_active) /* can't redo yank */
2266: {
2267: prep_redo(Prenum, prechar, opchars[op_type - 1], c, nchar);
2268: if (c == '/' || c == '?') /* was a search */
2269: {
2270: /*
2271: * If 'cpoptions' does not contain 'r', insert the search
2272: * pattern to really repeat the same command.
2273: */
2274: if (vim_strchr(p_cpo, CPO_REDO) == NULL)
2275: AppendToRedobuff(searchbuff);
2276: AppendToRedobuff(NL_STR);
2277: }
2278: }
2279:
2280: if (redo_VIsual_busy)
2281: {
2282: curbuf->b_op_start = curwin->w_cursor;
2283: curwin->w_cursor.lnum += redo_VIsual_line_count - 1;
2284: if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
2285: curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
2286: VIsual_mode = redo_VIsual_mode;
2287: if (VIsual_mode == 'v')
2288: {
2289: if (redo_VIsual_line_count <= 1)
2290: curwin->w_cursor.col += redo_VIsual_col - 1;
2291: else
2292: curwin->w_cursor.col = redo_VIsual_col;
2293: }
2294: if (redo_VIsual_col == MAXCOL)
2295: {
2296: curwin->w_curswant = MAXCOL;
2297: coladvance(MAXCOL);
2298: }
2299: Prenum = redo_VIsual_Prenum;
2300: }
2301: else if (VIsual_active)
2302: {
2303: curbuf->b_op_start = VIsual;
2304: VIsual_end = curwin->w_cursor;
2305: if (VIsual_mode == 'V')
2306: curbuf->b_op_start.col = 0;
2307: }
2308:
2309: if (lt(curbuf->b_op_start, curwin->w_cursor))
2310: {
2311: curbuf->b_op_end = curwin->w_cursor;
2312: curwin->w_cursor = curbuf->b_op_start;
2313: }
2314: else
2315: {
2316: curbuf->b_op_end = curbuf->b_op_start;
2317: curbuf->b_op_start = curwin->w_cursor;
2318: }
2319: op_line_count = curbuf->b_op_end.lnum - curbuf->b_op_start.lnum + 1;
2320:
2321: if (VIsual_active || redo_VIsual_busy)
2322: {
2323: if (VIsual_mode == Ctrl('V')) /* block mode */
2324: {
2325: colnr_t start, end;
2326:
2327: op_block_mode = TRUE;
2328: getvcol(curwin, &(curbuf->b_op_start),
2329: &op_start_vcol, NULL, &op_end_vcol);
2330: if (!redo_VIsual_busy)
2331: {
2332: getvcol(curwin, &(curbuf->b_op_end), &start, NULL, &end);
2333: if (start < op_start_vcol)
2334: op_start_vcol = start;
2335: if (end > op_end_vcol)
2336: op_end_vcol = end;
2337: }
2338:
2339: /* if '$' was used, get op_end_vcol from longest line */
2340: if (curwin->w_curswant == MAXCOL)
2341: {
2342: curwin->w_cursor.col = MAXCOL;
2343: op_end_vcol = 0;
2344: for (curwin->w_cursor.lnum = curbuf->b_op_start.lnum;
2345: curwin->w_cursor.lnum <= curbuf->b_op_end.lnum;
2346: ++curwin->w_cursor.lnum)
2347: {
2348: getvcol(curwin, &curwin->w_cursor, NULL, NULL, &end);
2349: if (end > op_end_vcol)
2350: op_end_vcol = end;
2351: }
2352: curwin->w_cursor = curbuf->b_op_start;
2353: }
2354: else if (redo_VIsual_busy)
2355: op_end_vcol = op_start_vcol + redo_VIsual_col - 1;
2356: coladvance(op_start_vcol);
2357: }
2358:
2359: if (!redo_VIsual_busy)
2360: {
2361: /*
2362: * Prepare to reselect and redo Visual: this is based on the
2363: * size of the Visual text
2364: */
2365: resel_VIsual_mode = VIsual_mode;
2366: if (curwin->w_curswant == MAXCOL)
2367: resel_VIsual_col = MAXCOL;
2368: else if (VIsual_mode == Ctrl('V'))
2369: resel_VIsual_col = op_end_vcol - op_start_vcol + 1;
2370: else if (op_line_count > 1)
2371: resel_VIsual_col = curbuf->b_op_end.col;
2372: else
2373: resel_VIsual_col = curbuf->b_op_end.col -
2374: curbuf->b_op_start.col + 1;
2375: resel_VIsual_line_count = op_line_count;
2376: }
2377: /* can't redo yank and : */
2378: if (op_type != YANK && op_type != COLON)
2379: {
2380: prep_redo(0L, NUL, 'v', prechar, opchars[op_type - 1]);
2381: redo_VIsual_mode = resel_VIsual_mode;
2382: redo_VIsual_col = resel_VIsual_col;
2383: redo_VIsual_line_count = resel_VIsual_line_count;
2384: redo_VIsual_Prenum = Prenum;
2385: }
2386:
2387: /*
2388: * Mincl defaults to TRUE.
2389: * If op_end is on a NUL (empty line) op_inclusive becomes FALSE
2390: * This makes "d}P" and "v}dP" work the same.
2391: */
2392: op_inclusive = TRUE;
2393: if (VIsual_mode == 'V')
2394: op_motion_type = MLINE;
2395: else
2396: {
2397: op_motion_type = MCHAR;
2398: if (*ml_get_pos(&(curbuf->b_op_end)) == NUL)
2399: op_inclusive = FALSE;
2400: }
2401:
2402: redo_VIsual_busy = FALSE;
2403: /*
2404: * Switch Visual off now, so screen updating does
2405: * not show inverted text when the screen is redrawn.
2406: * With YANK and sometimes with COLON and FILTER there is no screen
2407: * redraw, so it is done here to remove the inverted part.
2408: */
2409: if (!gui_yank)
2410: {
2411: VIsual_active = FALSE;
2412: #ifdef USE_MOUSE
2413: setmouse();
2414: #endif
2415: if (p_smd)
2416: clear_cmdline = TRUE; /* unshow visual mode later */
2417: if (op_type == YANK || op_type == COLON || op_type == FILTER)
2418: update_curbuf(NOT_VALID);
2419: }
2420:
2421: /* set Prenum1 for LSHIFT and RSHIFT, e.g. "V3j2>" */
2422: if (Prenum == 0)
2423: Prenum1 = 1L;
2424: else
2425: Prenum1 = Prenum;
2426: }
2427:
2428: curwin->w_set_curswant = TRUE;
2429:
2430: /* op_empty is set when start and end are the same */
2431: op_empty = (op_motion_type == MCHAR && !op_inclusive &&
2432: equal(curbuf->b_op_start, curbuf->b_op_end));
2433:
2434: /*
2435: * If the end of an operator is in column one while op_motion_type is
2436: * MCHAR and op_inclusive is FALSE, we put op_end after the last character
2437: * in the previous line. If op_start is on or before the first non-blank
2438: * in the line, the operator becomes linewise (strange, but that's the way
2439: * vi does it).
2440: */
2441: if (op_motion_type == MCHAR && op_inclusive == FALSE &&
2442: !dont_adjust_op_end && curbuf->b_op_end.col == 0 &&
2443: op_line_count > 1)
2444: {
2445: op_end_adjusted = TRUE; /* remember that we did this */
2446: --op_line_count;
2447: --curbuf->b_op_end.lnum;
2448: if (inindent(0))
2449: op_motion_type = MLINE;
2450: else
2451: {
2452: curbuf->b_op_end.col = STRLEN(ml_get(curbuf->b_op_end.lnum));
2453: if (curbuf->b_op_end.col)
2454: {
2455: --curbuf->b_op_end.col;
2456: op_inclusive = TRUE;
2457: }
2458: }
2459: }
2460: else
2461: op_end_adjusted = FALSE;
2462: switch (op_type)
2463: {
2464: case LSHIFT:
2465: case RSHIFT:
2466: do_shift(op_type, TRUE, (int)Prenum1);
2467: break;
2468:
2469: case JOIN:
2470: if (op_line_count < 2)
2471: op_line_count = 2;
2472: if (curwin->w_cursor.lnum + op_line_count - 1 >
2473: curbuf->b_ml.ml_line_count)
2474: beep_flush();
2475: else
2476: {
2477: /*
2478: * If the cursor position has been changed, recompute the
2479: * current cursor position in the window. If it's not visible,
2480: * don't keep the window updated when joining the lines.
2481: */
2482: if (old_cursor.lnum != curwin->w_cursor.lnum ||
2483: old_cursor.col != curwin->w_cursor.col)
2484: redraw = (curs_rows() == OK);
2485: else
2486: redraw = TRUE;
2487: do_do_join(op_line_count, TRUE, redraw);
2488: }
2489: break;
2490:
2491: case DELETE:
2492: if (!op_empty)
2493: do_delete();
2494: break;
2495:
2496: case YANK:
2497: if (!op_empty)
2498: (void)do_yank(FALSE, !gui_yank);
2499: break;
2500:
2501: case CHANGE:
2502: *command_busy = do_change(); /* will set op_type to NOP */
2503: break;
2504:
2505: case FILTER:
2506: if (vim_strchr(p_cpo, CPO_FILTER) != NULL)
2507: AppendToRedobuff((char_u *)"!\r"); /* use any last used !cmd */
2508: else
2509: bangredo = TRUE; /* do_bang() will put cmd in redo buffer */
2510:
2511: case INDENT:
2512: case COLON:
2513:
2514: #if defined(LISPINDENT) || defined(CINDENT)
2515: /*
2516: * If 'equalprg' is empty, do the indenting internally.
2517: */
2518: if (op_type == INDENT && *p_ep == NUL)
2519: {
2520: # ifdef LISPINDENT
2521: if (curbuf->b_p_lisp)
2522: {
2523: do_reindent(get_lisp_indent);
2524: break;
2525: }
2526: # endif
2527: # ifdef CINDENT
2528: do_reindent(get_c_indent);
2529: break;
2530: # endif
2531: }
2532: #endif /* defined(LISPINDENT) || defined(CINDENT) */
2533:
2534: dofilter:
2535: if (VIsual_was_active)
2536: sprintf((char *)IObuff, ":'<,'>");
2537: else
2538: sprintf((char *)IObuff, ":%ld,%ld",
2539: (long)curbuf->b_op_start.lnum,
2540: (long)curbuf->b_op_end.lnum);
2541: stuffReadbuff(IObuff);
2542: if (op_type != COLON)
2543: stuffReadbuff((char_u *)"!");
2544: if (op_type == INDENT)
2545: {
2546: #ifndef CINDENT
2547: if (*p_ep == NUL)
2548: stuffReadbuff((char_u *)"indent");
2549: else
2550: #endif
2551: stuffReadbuff(p_ep);
2552: stuffReadbuff((char_u *)"\n");
2553: }
2554: else if (op_type == FORMAT || op_type == GFORMAT)
2555: {
2556: if (*p_fp == NUL)
2557: stuffReadbuff((char_u *)"fmt");
2558: else
2559: stuffReadbuff(p_fp);
2560: stuffReadbuff((char_u *)"\n");
2561: }
2562: /* do_cmdline() does the rest */
2563: break;
2564:
2565: case TILDE:
2566: case UPPER:
2567: case LOWER:
2568: if (!op_empty)
2569: do_tilde();
2570: break;
2571:
2572: case FORMAT:
2573: case GFORMAT:
2574: if (*p_fp != NUL)
2575: goto dofilter; /* use external command */
2576: do_format(); /* use internal function */
2577: break;
2578:
2579: default:
2580: clearopbeep();
2581: }
2582: prechar = NUL;
2583: if (!gui_yank)
2584: {
2585: /*
2586: * if 'sol' not set, go back to old column for some commands
2587: */
2588: if (!p_sol && op_motion_type == MLINE && (op_type == LSHIFT ||
2589: op_type == RSHIFT || op_type == DELETE))
2590: coladvance(curwin->w_curswant = old_col);
2591: op_type = NOP;
2592: }
2593: else
2594: curwin->w_cursor = old_cursor;
2595: op_block_mode = FALSE;
2596: yankbuffer = 0;
2597: }
2598: }
2599:
2600: #ifdef USE_MOUSE
2601: /*
2602: * Do the appropriate action for the current mouse click in the current mode.
2603: *
2604: * Normal Mode:
2605: * event modi- position visual change action
2606: * fier cursor window
2607: * left press - yes end yes
2608: * left press C yes end yes "^]" (2)
2609: * left press S yes end yes "*" (2)
2610: * left drag - yes start if moved no
2611: * left relse - yes start if moved no
2612: * middle press - yes if not active no put register
2613: * middle press - yes if active no yank and put
2614: * right press - yes start or extend yes
2615: * right press S yes no change yes "#" (2)
2616: * right drag - yes extend no
2617: * right relse - yes extend no
2618: *
2619: * Insert or Replace Mode:
2620: * event modi- position visual change action
2621: * fier cursor window
2622: * left press - yes (cannot be active) yes
2623: * left press C yes (cannot be active) yes "CTRL-O^]" (2)
2624: * left press S yes (cannot be active) yes "CTRL-O*" (2)
2625: * left drag - yes start or extend (1) no CTRL-O (1)
2626: * left relse - yes start or extend (1) no CTRL-O (1)
2627: * middle press - no (cannot be active) no put register
2628: * right press - yes start or extend yes CTRL-O
2629: * right press S yes (cannot be active) yes "CTRL-O#" (2)
2630: *
2631: * (1) only if mouse pointer moved since press
2632: * (2) only if click is in same buffer
2633: *
2634: * Return TRUE if start_arrow() should be called for edit mode.
2635: */
2636: int
2637: do_mouse(c, dir, count, fix_indent)
2638: int c; /* K_LEFTMOUSE, etc */
2639: int dir; /* Direction to 'put' if necessary */
2640: long count;
2641: int fix_indent; /* Do we fix indent for 'put' if necessary? */
2642: {
2643: static int ignore_drag_release = FALSE;
2644: static FPOS orig_cursor;
2645: static int do_always = FALSE; /* ignore 'mouse' setting next time */
2646: static int got_click = FALSE; /* got a click some time back */
2647:
2648: int which_button; /* MOUSE_LEFT, _MIDDLE or _RIGHT */
2649: int is_click; /* If FALSE it's a drag or release event */
2650: int is_drag; /* If TRUE it's a drag event */
2651: int jump_flags = 0; /* flags for jump_to_mouse() */
2652: FPOS start_visual;
2653: FPOS end_visual;
2654: BUF *save_buffer;
2655: int diff;
2656: int moved; /* Has cursor moved? */
2657: int c1, c2;
2658: int VIsual_was_active = VIsual_active;
2659:
2660: /*
2661: * When GUI is active, always recognize mouse events, otherwise:
2662: * - Ignore mouse event in normal mode if 'mouse' doesn't include 'n'.
2663: * - Ignore mouse event in visual mode if 'mouse' doesn't include 'v'.
2664: * - For command line and insert mode 'mouse' is checked before calling
2665: * do_mouse().
2666: */
2667: if (do_always)
2668: do_always = FALSE;
2669: else
2670: #ifdef USE_GUI
2671: if (!gui.in_use)
2672: #endif
2673: {
2674: if (VIsual_active)
2675: {
2676: if (!mouse_has(MOUSE_VISUAL))
2677: return FALSE;
2678: }
2679: else if (State == NORMAL && !mouse_has(MOUSE_NORMAL))
2680: return FALSE;
2681: }
2682:
2683: which_button = get_mouse_button(KEY2TERMCAP1(c), &is_click, &is_drag);
2684:
2685: /*
2686: * Ignore drag and release events if we didn't get a click.
2687: */
2688: if (is_click)
2689: got_click = TRUE;
2690: else
2691: {
2692: if (!got_click) /* didn't get click, ignore */
2693: return FALSE;
2694: if (!is_drag) /* release, reset got_click */
2695: got_click = FALSE;
2696: }
2697:
2698: /*
2699: * ALT is currently ignored
2700: */
2701: if ((mod_mask & MOD_MASK_ALT))
2702: return FALSE;
2703:
2704: /*
2705: * CTRL right mouse button does CTRL-T
2706: */
2707: if (is_click && (mod_mask & MOD_MASK_CTRL) && which_button == MOUSE_RIGHT)
2708: {
2709: if (State & INSERT)
2710: stuffcharReadbuff(Ctrl('O'));
2711: stuffcharReadbuff(Ctrl('T'));
2712: got_click = FALSE; /* ignore drag&release now */
2713: return FALSE;
2714: }
2715:
2716: /*
2717: * CTRL only works with left mouse button
2718: */
2719: if ((mod_mask & MOD_MASK_CTRL) && which_button != MOUSE_LEFT)
2720: return FALSE;
2721:
2722: /*
2723: * When a modifier is down, ignore drag and release events, as well as
2724: * multiple clicks and the middle mouse button.
2725: */
2726: if ((mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT)) &&
2727: (!is_click || (mod_mask & MOD_MASK_MULTI_CLICK) ||
2728: which_button == MOUSE_MIDDLE))
2729: return FALSE;
2730:
2731: /*
2732: * If the button press was used as the movement command for an operator
2733: * (eg "d<MOUSE>"), or it is the middle button that is held down, ignore
2734: * drag/release events.
2735: */
2736: if (!is_click && (ignore_drag_release || which_button == MOUSE_MIDDLE))
2737: return FALSE;
2738:
2739: /*
2740: * Middle mouse button does a 'put' of the selected text
2741: */
2742: if (which_button == MOUSE_MIDDLE)
2743: {
2744: if (State == NORMAL)
2745: {
2746: /*
2747: * If an operator was pending, we don't know what the user wanted
2748: * to do. Go back to normal mode: Clear the operator and beep().
2749: */
2750: if (op_type != NOP)
2751: {
2752: clearopbeep();
2753: return FALSE;
2754: }
2755:
2756: /*
2757: * If visual was active, yank the highlighted text and put it
2758: * before the mouse pointer position.
2759: */
2760: if (VIsual_active)
2761: {
2762: stuffcharReadbuff('y');
2763: stuffcharReadbuff(K_MIDDLEMOUSE);
2764: do_always = TRUE; /* ignore 'mouse' setting next time */
2765: return FALSE;
2766: }
2767: /*
2768: * The rest is below jump_to_mouse()
2769: */
2770: }
2771:
2772: /*
2773: * Middle click in insert mode doesn't move the mouse, just insert the
2774: * contents of a register. '.' register is special, can't insert that
2775: * with do_put().
2776: */
2777: else if (State & INSERT)
2778: {
2779: if (yankbuffer == '.')
2780: insertbuf(yankbuffer);
2781: else
2782: {
2783: #ifdef USE_GUI
2784: if (gui.in_use && yankbuffer == 0)
2785: yankbuffer = '*';
2786: #endif
2787: do_put(BACKWARD, 1L, fix_indent);
2788:
2789: /* Put cursor after the end of the just pasted text. */
2790: curwin->w_cursor = curbuf->b_op_end;
2791: if (gchar_cursor() != NUL)
2792: ++curwin->w_cursor.col;
2793:
2794: /* Repeat it with CTRL-R x, not exactly the same, but mostly
2795: * works fine. */
2796: AppendCharToRedobuff(Ctrl('R'));
2797: if (yankbuffer == 0)
2798: AppendCharToRedobuff('"');
2799: else
2800: AppendCharToRedobuff(yankbuffer);
2801: }
2802: return FALSE;
2803: }
2804: else
2805: return FALSE;
2806: }
2807:
2808: if (!is_click)
2809: jump_flags |= MOUSE_FOCUS;
2810:
2811: start_visual.lnum = 0;
2812:
2813: if ((State & (NORMAL | INSERT)) &&
2814: !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)))
2815: {
2816: if (which_button == MOUSE_LEFT)
2817: {
2818: if (is_click)
2819: {
2820: if (VIsual_active)
2821: {
2822: end_visual_mode();
2823: update_curbuf(NOT_VALID);
2824: }
2825: }
2826: else
2827: jump_flags |= MOUSE_MAY_VIS;
2828: }
2829: else if (which_button == MOUSE_RIGHT)
2830: {
2831: if (is_click && VIsual_active)
2832: {
2833: /*
2834: * Remember the start and end of visual before moving the
2835: * cursor.
2836: */
2837: if (lt(curwin->w_cursor, VIsual))
2838: {
2839: start_visual = curwin->w_cursor;
2840: end_visual = VIsual;
2841: }
2842: else
2843: {
2844: start_visual = VIsual;
2845: end_visual = curwin->w_cursor;
2846: }
2847: }
2848: jump_flags |= MOUSE_MAY_VIS;
2849: }
2850: }
2851:
2852: if (!is_drag)
2853: {
2854: /*
2855: * If an operator is pending, ignore all drags and releases until the
2856: * next mouse click.
2857: */
2858: ignore_drag_release = (op_type != NOP);
2859: }
2860:
2861: /*
2862: * Jump!
2863: */
2864: if (!is_click)
2865: jump_flags |= MOUSE_DID_MOVE;
2866: save_buffer = curbuf;
2867: moved = (jump_to_mouse(jump_flags) & CURSOR_MOVED);
2868:
2869: /* When jumping to another buffer, stop visual mode */
2870: if (curbuf != save_buffer && VIsual_active)
2871: {
2872: end_visual_mode();
2873: update_curbuf(NOT_VALID); /* delete the inversion */
2874: }
2875: else if (start_visual.lnum) /* right click in visual mode */
2876: {
2877: /*
2878: * If the click is before the start of visual, change the start. If
2879: * the click is after the end of visual, change the end. If the click
2880: * is inside the visual, change the closest side.
2881: */
2882: if (lt(curwin->w_cursor, start_visual))
2883: VIsual = end_visual;
2884: else if (lt(end_visual, curwin->w_cursor))
2885: VIsual = start_visual;
2886: else
2887: {
2888: /* In the same line, compare column number */
2889: if (end_visual.lnum == start_visual.lnum)
2890: {
2891: if (curwin->w_cursor.col - start_visual.col >
2892: end_visual.col - curwin->w_cursor.col)
2893: VIsual = start_visual;
2894: else
2895: VIsual = end_visual;
2896: }
2897:
2898: /* In different lines, compare line number */
2899: else
2900: {
2901: diff = (curwin->w_cursor.lnum - start_visual.lnum) -
2902: (end_visual.lnum - curwin->w_cursor.lnum);
2903:
2904: if (diff > 0) /* closest to end */
2905: VIsual = start_visual;
2906: else if (diff < 0) /* closest to start */
2907: VIsual = end_visual;
2908: else /* in the middle line */
2909: {
2910: if (curwin->w_cursor.col <
2911: (start_visual.col + end_visual.col) / 2)
2912: VIsual = end_visual;
2913: else
2914: VIsual = start_visual;
2915: }
2916: }
2917: }
2918: }
2919: /*
2920: * If Visual mode started in insert mode, execute "CTRL-O"
2921: */
2922: else if ((State & INSERT) && VIsual_active)
2923: stuffcharReadbuff(Ctrl('O'));
2924: /*
2925: * If cursor has moved, need to update Cline_row
2926: */
2927: else if (moved)
2928: cursupdate();
2929:
2930: /*
2931: * Middle mouse click: Put text before cursor.
2932: */
2933: if (which_button == MOUSE_MIDDLE)
2934: {
2935: #ifdef USE_GUI
2936: if (gui.in_use && yankbuffer == 0)
2937: yankbuffer = '*';
2938: #endif
2939: if (yank_buffer_mline())
2940: {
2941: if (mouse_past_bottom)
2942: dir = FORWARD;
2943: }
2944: else if (mouse_past_eol)
2945: dir = FORWARD;
2946:
2947: if (fix_indent)
2948: {
2949: c1 = (dir == BACKWARD) ? '[' : ']';
2950: c2 = 'p';
2951: }
2952: else
2953: {
2954: c1 = (dir == FORWARD) ? 'p' : 'P';
2955: c2 = NUL;
2956: }
2957: prep_redo(Prenum, NUL, c1, c2, NUL);
2958: /*
2959: * Remember where the paste started, so in edit() Insstart can be set
2960: * to this position
2961: */
2962: if (restart_edit)
2963: where_paste_started = curwin->w_cursor;
2964: do_put(dir, count, fix_indent);
2965:
2966: /* Put cursor at the end of the just pasted text. */
2967: curwin->w_cursor = curbuf->b_op_end;
2968: if (restart_edit && gchar_cursor() != NUL)
2969: ++curwin->w_cursor.col; /* put cursor after the text */
2970: }
2971:
2972: /*
2973: * Ctrl-Mouse click jumps to the tag under the mouse pointer
2974: */
2975: else if ((mod_mask & MOD_MASK_CTRL))
2976: {
2977: if (State & INSERT)
2978: stuffcharReadbuff(Ctrl('O'));
2979: stuffcharReadbuff(Ctrl(']'));
2980: ignore_drag_release = TRUE; /* ignore drag and release now */
2981: }
2982:
2983: /*
2984: * Shift-Mouse click searches for the next occurrence of the word under
2985: * the mouse pointer
2986: */
2987: else if ((mod_mask & MOD_MASK_SHIFT))
2988: {
2989: if (State & INSERT)
2990: stuffcharReadbuff(Ctrl('O'));
2991: if (which_button == MOUSE_LEFT)
2992: stuffcharReadbuff('*');
2993: else /* MOUSE_RIGHT */
2994: stuffcharReadbuff('#');
2995: }
2996:
2997: /* Handle double clicks */
2998: else if ((mod_mask & MOD_MASK_MULTI_CLICK) && (State & (NORMAL | INSERT)))
2999: {
3000: if (is_click || !VIsual_active)
3001: {
3002: if (VIsual_active)
3003: orig_cursor = VIsual;
3004: else
3005: {
3006: start_visual_highlight();
3007: VIsual = curwin->w_cursor;
3008: orig_cursor = VIsual;
3009: VIsual_active = TRUE;
3010: #ifdef USE_MOUSE
3011: setmouse();
3012: #endif
3013: if (p_smd)
3014: redraw_cmdline = TRUE; /* show visual mode later */
3015: }
3016: if (mod_mask & MOD_MASK_2CLICK)
3017: VIsual_mode = 'v';
3018: else if (mod_mask & MOD_MASK_3CLICK)
3019: VIsual_mode = 'V';
3020: else if (mod_mask & MOD_MASK_4CLICK)
3021: VIsual_mode = Ctrl('V');
3022: }
3023: if (mod_mask & MOD_MASK_2CLICK)
3024: {
3025: if (lt(curwin->w_cursor, orig_cursor))
3026: {
3027: find_start_of_word(&curwin->w_cursor);
3028: find_end_of_word(&VIsual);
3029: }
3030: else
3031: {
3032: find_start_of_word(&VIsual);
3033: find_end_of_word(&curwin->w_cursor);
3034: }
3035: curwin->w_set_curswant = TRUE;
3036: }
3037: if (is_click)
3038: {
3039: curs_columns(TRUE); /* recompute w_virtcol */
3040: update_curbuf(NOT_VALID); /* update the inversion */
3041: }
3042: }
3043: else if (VIsual_active && VIsual_was_active != VIsual_active)
3044: VIsual_mode = 'v';
3045:
3046: return moved;
3047: }
3048:
3049: static void
3050: find_start_of_word(pos)
3051: FPOS *pos;
3052: {
3053: char_u *ptr;
3054: int cclass;
3055:
3056: ptr = ml_get(pos->lnum);
3057: cclass = get_mouse_class(ptr[pos->col]);
3058:
3059: /* Can't test pos->col >= 0 because pos->col is unsigned */
3060: while (pos->col > 0 && get_mouse_class(ptr[pos->col]) == cclass)
3061: pos->col--;
3062: if (pos->col != 0 || get_mouse_class(ptr[0]) != cclass)
3063: pos->col++;
3064: }
3065:
3066: static void
3067: find_end_of_word(pos)
3068: FPOS *pos;
3069: {
3070: char_u *ptr;
3071: int cclass;
3072:
3073: ptr = ml_get(pos->lnum);
3074: cclass = get_mouse_class(ptr[pos->col]);
3075: while (ptr[pos->col] && get_mouse_class(ptr[pos->col]) == cclass)
3076: pos->col++;
3077: pos->col--;
3078: }
3079:
3080: static int
3081: get_mouse_class(c)
3082: int c;
3083: {
3084: if (c == ' ' || c == '\t')
3085: return ' ';
3086:
3087: if (isidchar(c))
3088: return 'a';
3089:
3090: /*
3091: * There are a few special cases where we want certain combinations of
3092: * characters to be considered as a single word. These are things like
3093: * "->", "/ *", "*=", "+=", "&=", "<=", ">=", "!=" etc. Otherwise, each
3094: * character is in it's own class.
3095: */
3096: if (c != NUL && vim_strchr((char_u *)"-+*/%<>&|^!=", c) != NULL)
3097: return '=';
3098: return c;
3099: }
3100: #endif /* USE_MOUSE */
3101:
3102: /*
3103: * start highlighting for visual mode
3104: */
3105: void
3106: start_visual_highlight()
3107: {
3108: static int didwarn = FALSE; /* warned for broken inversion */
3109:
3110: if (!didwarn && set_highlight('v') == FAIL)/* cannot highlight */
3111: {
3112: EMSG("Warning: terminal cannot highlight");
3113: didwarn = TRUE;
3114: }
3115: }
3116:
3117: /*
3118: * End visual mode. If we are using the GUI, and autoselect is set, then
3119: * remember what was selected in case we need to paste it somewhere while we
3120: * still own the selection. This function should ALWAYS be called to end
3121: * visual mode.
3122: */
3123: void
3124: end_visual_mode()
3125: {
3126: #ifdef USE_GUI
3127: if (gui.in_use)
3128: gui_auto_select();
3129: #endif
3130: VIsual_active = FALSE;
3131: #ifdef USE_MOUSE
3132: setmouse();
3133: #endif
3134: VIsual_end = curwin->w_cursor; /* remember for '> mark */
3135: if (p_smd)
3136: clear_cmdline = TRUE; /* unshow visual mode later */
3137: }
3138:
3139: /*
3140: * Find the identifier under or to the right of the cursor. If none is
3141: * found and find_type has FIND_STRING, then find any non-white string. The
3142: * length of the string is returned, or zero if no string is found. If a
3143: * string is found, a pointer to the string is put in *string, but note that
3144: * the caller must use the length returned as this string may not be NUL
3145: * terminated.
3146: */
3147: int
3148: find_ident_under_cursor(string, find_type)
3149: char_u **string;
3150: int find_type;
3151: {
3152: char_u *ptr;
3153: int col = 0; /* init to shut up GCC */
3154: int i;
3155:
3156: /*
3157: * if i == 0: try to find an identifier
3158: * if i == 1: try to find any string
3159: */
3160: ptr = ml_get_curline();
3161: for (i = (find_type & FIND_IDENT) ? 0 : 1; i < 2; ++i)
3162: {
3163: /*
3164: * skip to start of identifier/string
3165: */
3166: col = curwin->w_cursor.col;
3167: while (ptr[col] != NUL &&
3168: (i == 0 ? !iswordchar(ptr[col]) : vim_iswhite(ptr[col])))
3169: ++col;
3170:
3171: /*
3172: * Back up to start of identifier/string. This doesn't match the
3173: * real vi but I like it a little better and it shouldn't bother
3174: * anyone.
3175: * When FIND_IDENT isn't defined, we backup until a blank.
3176: */
3177: while (col > 0 && (i == 0 ? iswordchar(ptr[col - 1]) :
3178: (!vim_iswhite(ptr[col - 1]) &&
3179: (!(find_type & FIND_IDENT) || !iswordchar(ptr[col - 1])))))
3180: --col;
3181:
3182: /*
3183: * if we don't want just any old string, or we've found an identifier,
3184: * stop searching.
3185: */
3186: if (!(find_type & FIND_STRING) || iswordchar(ptr[col]))
3187: break;
3188: }
3189: /*
3190: * didn't find an identifier or string
3191: */
3192: if (ptr[col] == NUL || (!iswordchar(ptr[col]) && i == 0))
3193: {
3194: if (find_type & FIND_STRING)
3195: EMSG("No string under cursor");
3196: else
3197: EMSG("No identifier under cursor");
3198: return 0;
3199: }
3200: ptr += col;
3201: *string = ptr;
3202: col = 0;
3203: while (i == 0 ? iswordchar(*ptr) : (*ptr != NUL && !vim_iswhite(*ptr)))
3204: {
3205: ++ptr;
3206: ++col;
3207: }
3208: return col;
3209: }
3210:
3211: static void
3212: prep_redo(num, pre_char, cmd, c, nchar)
3213: long num;
3214: int pre_char;
3215: int cmd;
3216: int c;
3217: int nchar;
3218: {
3219: ResetRedobuff();
3220: if (yankbuffer != 0) /* yank from specified buffer */
3221: {
3222: AppendCharToRedobuff('\"');
3223: AppendCharToRedobuff(yankbuffer);
3224: }
3225: if (num)
3226: AppendNumberToRedobuff(num);
3227: if (pre_char != NUL)
3228: AppendCharToRedobuff(pre_char);
3229: AppendCharToRedobuff(cmd);
3230: if (c != NUL)
3231: AppendCharToRedobuff(c);
3232: if (nchar != NUL)
3233: AppendCharToRedobuff(nchar);
3234: }
3235:
3236: /*
3237: * check for operator active and clear it
3238: *
3239: * return TRUE if operator was active
3240: */
3241: static int
3242: checkclearop()
3243: {
3244: if (op_type == NOP)
3245: return (FALSE);
3246: clearopbeep();
3247: return (TRUE);
3248: }
3249:
3250: /*
3251: * check for operator or Visual active and clear it
3252: *
3253: * return TRUE if operator was active
3254: */
3255: static int
3256: checkclearopq()
3257: {
3258: if (op_type == NOP && !VIsual_active)
3259: return (FALSE);
3260: clearopbeep();
3261: return (TRUE);
3262: }
3263:
3264: static void
3265: clearop()
3266: {
3267: op_type = NOP;
3268: yankbuffer = 0;
3269: prechar = NUL;
3270: }
3271:
3272: static void
3273: clearopbeep()
3274: {
3275: clearop();
3276: beep_flush();
3277: }
3278:
3279: /*
3280: * Routines for displaying a partly typed command
3281: */
3282:
3283: static char_u showcmd_buf[SHOWCMD_COLS + 1];
3284: static char_u old_showcmd_buf[SHOWCMD_COLS + 1]; /* For push_showcmd() */
3285: static int is_showcmd_clear = TRUE;
3286:
3287: static void display_showcmd __ARGS((void));
3288:
3289: void
3290: clear_showcmd()
3291: {
3292: if (!p_sc)
3293: return;
3294:
3295: showcmd_buf[0] = NUL;
3296:
3297: /*
3298: * Don't actually display something if there is nothing to clear.
3299: */
3300: if (is_showcmd_clear)
3301: return;
3302:
3303: display_showcmd();
3304: }
3305:
3306: /*
3307: * Add 'c' to string of shown command chars.
3308: * Return TRUE if setcursor() has been called.
3309: */
3310: int
3311: add_to_showcmd(c, display_always)
3312: int c;
3313: int display_always;
3314: {
3315: char_u *p;
3316: int old_len;
3317: int extra_len;
3318: int overflow;
3319:
3320: if (!p_sc)
3321: return FALSE;
3322:
3323: p = transchar(c);
3324: old_len = STRLEN(showcmd_buf);
3325: extra_len = STRLEN(p);
3326: overflow = old_len + extra_len - SHOWCMD_COLS;
3327: if (overflow > 0)
3328: STRCPY(showcmd_buf, showcmd_buf + overflow);
3329: STRCAT(showcmd_buf, p);
3330:
3331: if (!display_always && char_avail())
3332: return FALSE;
3333:
3334: display_showcmd();
3335:
3336: return TRUE;
3337: }
3338:
3339: /*
3340: * Delete 'len' characters from the end of the shown command.
3341: */
3342: static void
3343: del_from_showcmd(len)
3344: int len;
3345: {
3346: int old_len;
3347:
3348: if (!p_sc)
3349: return;
3350:
3351: old_len = STRLEN(showcmd_buf);
3352: if (len > old_len)
3353: len = old_len;
3354: showcmd_buf[old_len - len] = NUL;
3355:
3356: if (!char_avail())
3357: display_showcmd();
3358: }
3359:
3360: void
3361: push_showcmd()
3362: {
3363: if (p_sc)
3364: STRCPY(old_showcmd_buf, showcmd_buf);
3365: }
3366:
3367: void
3368: pop_showcmd()
3369: {
3370: if (!p_sc)
3371: return;
3372:
3373: STRCPY(showcmd_buf, old_showcmd_buf);
3374:
3375: display_showcmd();
3376: }
3377:
3378: static void
3379: display_showcmd()
3380: {
3381: int len;
3382:
3383: cursor_off();
3384:
3385: len = STRLEN(showcmd_buf);
3386: if (len == 0)
3387: is_showcmd_clear = TRUE;
3388: else
3389: {
3390: screen_msg(showcmd_buf, (int)Rows - 1, sc_col);
3391: is_showcmd_clear = FALSE;
3392: }
3393:
3394: /*
3395: * clear the rest of an old message by outputing up to SHOWCMD_COLS spaces
3396: */
3397: screen_msg((char_u *)" " + len, (int)Rows - 1, sc_col + len);
3398:
3399: setcursor(); /* put cursor back where it belongs */
3400: }
3401:
3402: /*
3403: * Implementation of "gd" and "gD" command.
3404: */
3405: static void
3406: do_gd(nchar)
3407: int nchar;
3408: {
3409: int len;
3410: char_u *pat;
3411: FPOS old_pos;
3412: int t;
3413: int save_p_ws;
3414: int save_p_scs;
3415: char_u *ptr;
3416:
3417: if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0 ||
3418: (pat = alloc(len + 5)) == NULL)
3419: {
3420: clearopbeep();
3421: return;
3422: }
3423: sprintf((char *)pat, iswordchar(*ptr) ? "\\<%.*s\\>" :
3424: "%.*s", len, ptr);
3425: old_pos = curwin->w_cursor;
3426: save_p_ws = p_ws;
3427: save_p_scs = p_scs;
3428: p_ws = FALSE; /* don't wrap around end of file now */
3429: p_scs = FALSE; /* don't switch ignorecase off now */
3430: fo_do_comments = TRUE;
3431:
3432: /*
3433: * Search back for the end of the previous function.
3434: * If this fails, and with "gD", go to line 1.
3435: * Search forward for the identifier, ignore comment lines.
3436: */
3437: if (nchar == 'D' || !findpar(BACKWARD, 1L, '}', FALSE))
3438: {
3439: setpcmark(); /* Set in findpar() otherwise */
3440: curwin->w_cursor.lnum = 1;
3441: }
3442:
3443: while ((t = searchit(&curwin->w_cursor, FORWARD, pat, 1L, 0, RE_LAST))
3444: == OK &&
3445: get_leader_len(ml_get_curline(), NULL) &&
3446: old_pos.lnum > curwin->w_cursor.lnum)
3447: ++curwin->w_cursor.lnum;
3448: if (t == FAIL || old_pos.lnum <= curwin->w_cursor.lnum)
3449: {
3450: clearopbeep();
3451: curwin->w_cursor = old_pos;
3452: }
3453: else
3454: curwin->w_set_curswant = TRUE;
3455:
3456: vim_free(pat);
3457: p_ws = save_p_ws;
3458: p_scs = save_p_scs;
3459: fo_do_comments = FALSE;
3460: }