Annotation of src/usr.bin/vim/edit.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: * edit.c: functions for insert mode
12: */
13:
14: #include "vim.h"
15: #include "globals.h"
16: #include "proto.h"
17: #include "option.h"
18: #include "ops.h" /* for op_type */
19:
20: #ifdef INSERT_EXPAND
21: /*
22: * definitions used for CTRL-X submode
23: */
24: #define CTRL_X_WANT_IDENT 0x100
25:
26: #define CTRL_X_NOT_DEFINED_YET (1)
27: #define CTRL_X_SCROLL (2)
28: #define CTRL_X_WHOLE_LINE (3)
29: #define CTRL_X_FILES (4)
30: #define CTRL_X_TAGS (5 + CTRL_X_WANT_IDENT)
31: #define CTRL_X_PATH_PATTERNS (6 + CTRL_X_WANT_IDENT)
32: #define CTRL_X_PATH_DEFINES (7 + CTRL_X_WANT_IDENT)
33: #define CTRL_X_FINISHED (8)
34: #define CTRL_X_DICTIONARY (9 + CTRL_X_WANT_IDENT)
35:
36: struct Completion
37: {
38: char_u *str;
39: char_u *fname;
40: int original;
41: struct Completion *next;
42: struct Completion *prev;
43: };
44:
45: struct Completion *first_match = NULL;
46: struct Completion *curr_match = NULL;
47:
48: static int add_completion __ARGS((char_u *str, int len, char_u *, int dir));
49: static int make_cyclic __ARGS((void));
50: static void complete_dictionaries __ARGS((char_u *, int));
51: static void free_completions __ARGS((void));
52: static int count_completions __ARGS((void));
53: #endif /* INSERT_EXPAND */
54:
55: #define BACKSPACE_CHAR 1
56: #define BACKSPACE_WORD 2
57: #define BACKSPACE_WORD_NOT_SPACE 3
58: #define BACKSPACE_LINE 4
59:
60: static void change_indent __ARGS((int type, int amount, int round));
61: static void insert_special __ARGS((int, int));
62: static void start_arrow __ARGS((FPOS *end_insert_pos));
63: static void stop_arrow __ARGS((void));
64: static void stop_insert __ARGS((FPOS *end_insert_pos));
65: static int echeck_abbr __ARGS((int));
66:
67: static FPOS Insstart; /* This is where the latest insert/append
68: * mode started. */
69: static colnr_t Insstart_textlen; /* length of line when insert started */
70: static colnr_t Insstart_blank_vcol; /* vcol for first inserted blank */
71:
72: static char_u *last_insert = NULL;
73: /* the text of the previous insert */
74: static int last_insert_skip;
75: /* number of chars in front of previous insert */
76: static int new_insert_skip;
77: /* number of chars in front of the current insert */
78: #ifdef INSERT_EXPAND
79: static char_u *original_text = NULL;
80: /* Original text typed before completion */
81: #endif
82:
83: #ifdef CINDENT
84: static int can_cindent; /* may do cindenting on this line */
85: #endif
86:
87: /*
88: * edit() returns TRUE if it returns because of a CTRL-O command
89: */
90: int
91: edit(initstr, startln, count)
92: int initstr;
93: int startln; /* if set, insert at start of line */
94: long count;
95: {
96: int c;
97: int cc;
98: char_u *ptr;
99: linenr_t lnum;
100: int temp = 0;
101: int mode;
102: int lastc = 0;
103: colnr_t mincol;
104: static linenr_t o_lnum = 0;
105: static int o_eol = FALSE;
106: int need_redraw = FALSE;
107: int i;
108: int did_backspace = TRUE; /* previous char was backspace */
109: #ifdef RIGHTLEFT
110: int revins; /* reverse insert mode */
111: int revinschars = 0; /* how much to skip after edit */
112: int revinslegal = 0; /* was the last char 'legal'? */
113: int revinsscol = -1; /* start column of revins session */
114: #endif
115: #ifdef INSERT_EXPAND
116: FPOS first_match_pos;
117: FPOS last_match_pos;
118: FPOS *complete_pos;
119: char_u *complete_pat = NULL;
120: char_u *tmp_ptr;
121: char_u *mesg = NULL; /* Message about completion */
122: char_u *quick_m; /* Message without sleep */
123: int started_completion = FALSE;
124: colnr_t complete_col = 0; /* init for gcc */
125: int complete_direction;
126: int done_dir = 0; /* Found all matches in this
127: * direction */
128: int num_matches;
129: char_u **matches;
130: regexp *prog;
131: int save_sm = -1; /* init for gcc */
132: int save_p_scs;
133: #endif
134: #ifdef CINDENT
135: int line_is_white = FALSE; /* line is empty before insert */
136: #endif
137: FPOS tpos;
138:
139: /* sleep before redrawing, needed for "CTRL-O :" that results in an
140: * error message */
141: if (msg_scroll || emsg_on_display)
142: {
143: mch_delay(1000L, TRUE);
144: msg_scroll = FALSE;
145: emsg_on_display = FALSE;
146: }
147: #ifdef SLEEP_IN_EMSG
148: if (need_sleep)
149: {
150: mch_delay(1000L, TRUE);
151: need_sleep = FALSE;
152: }
153: #endif
154:
155: #ifdef USE_MOUSE
156: /*
157: * When doing a paste with the middle mouse button, Insstart is set to
158: * where the paste started.
159: */
160: if (where_paste_started.lnum != 0)
161: Insstart = where_paste_started;
162: else
163: #endif
164: {
165: Insstart = curwin->w_cursor;
166: if (startln)
167: Insstart.col = 0;
168: }
169: Insstart_textlen = linetabsize(ml_get_curline());
170: Insstart_blank_vcol = MAXCOL;
171:
172: if (initstr != NUL && !restart_edit)
173: {
174: ResetRedobuff();
175: AppendNumberToRedobuff(count);
176: AppendCharToRedobuff(initstr);
177: if (initstr == 'g') /* "gI" command */
178: AppendCharToRedobuff('I');
179: }
180:
181: if (initstr == 'R')
182: State = REPLACE;
183: else
184: State = INSERT;
185:
186: #ifdef USE_MOUSE
187: setmouse();
188: #endif
189: clear_showcmd();
190: #ifdef RIGHTLEFT
191: revins = (State == INSERT && p_ri); /* there is no reverse replace mode */
192: if (revins)
193: undisplay_dollar();
194: #endif
195:
196: /*
197: * When CTRL-O . is used to repeat an insert, we get here with
198: * restart_edit non-zero, but something in the stuff buffer
199: */
200: if (restart_edit && stuff_empty())
201: {
202: #ifdef USE_MOUSE
203: /*
204: * After a paste we consider text typed to be part of the insert for
205: * the pasted text. You can backspace over the paste text too.
206: */
207: if (where_paste_started.lnum)
208: arrow_used = FALSE;
209: else
210: #endif
211: arrow_used = TRUE;
212: restart_edit = 0;
213: /*
214: * If the cursor was after the end-of-line before the CTRL-O
215: * and it is now at the end-of-line, put it after the end-of-line
216: * (this is not correct in very rare cases).
217: * Also do this if curswant is greater than the current virtual column.
218: * Eg after "^O$" or "^O80|".
219: */
220: if (((o_eol && curwin->w_cursor.lnum == o_lnum) ||
221: curwin->w_curswant > curwin->w_virtcol) &&
222: *(ptr = ml_get_curline() + curwin->w_cursor.col)
223: != NUL &&
224: *(ptr + 1) == NUL)
225: ++curwin->w_cursor.col;
226: }
227: else
228: {
229: arrow_used = FALSE;
230: o_eol = FALSE;
231: }
232: #ifdef USE_MOUSE
233: where_paste_started.lnum = 0;
234: #endif
235: #ifdef CINDENT
236: can_cindent = TRUE;
237: #endif
238:
239: if (p_smd)
240: showmode();
241:
242: if (!p_im)
243: change_warning(); /* give a warning if readonly */
244:
245: #ifdef DIGRAPHS
246: do_digraph(-1); /* clear digraphs */
247: #endif
248:
249: /*
250: * Get the current length of the redo buffer, those characters have to be
251: * skipped if we want to get to the inserted characters.
252: */
253: ptr = get_inserted();
254: new_insert_skip = STRLEN(ptr);
255: vim_free(ptr);
256:
257: old_indent = 0;
258:
259: for (;;)
260: {
261: #ifdef RIGHTLEFT
262: if (!revinslegal)
263: revinsscol = -1; /* reset on illegal motions */
264: else
265: revinslegal = 0;
266: #endif
267: if (arrow_used) /* don't repeat insert when arrow key used */
268: count = 0;
269:
270: /* set curwin->w_curswant for next K_DOWN or K_UP */
271: if (!arrow_used)
272: curwin->w_set_curswant = TRUE;
273:
274: /* Figure out where the cursor is based on curwin->w_cursor. */
275: mincol = curwin->w_col;
276: i = curwin->w_row;
277: cursupdate();
278:
279: /*
280: * When emsg() was called msg_scroll will have been set.
281: */
282: msg_scroll = FALSE;
283:
284: /*
285: * If we inserted a character at the last position of the last line in
286: * the window, scroll the window one line up. This avoids an extra
287: * redraw.
288: * This is detected when the cursor column is smaller after inserting
289: * something.
290: */
291: if (curwin->w_p_wrap && !did_backspace &&
292: (int)curwin->w_col < (int)mincol - curbuf->b_p_ts &&
293: i == curwin->w_winpos + curwin->w_height - 1 &&
294: curwin->w_cursor.lnum != curwin->w_topline)
295: {
296: ++curwin->w_topline;
297: updateScreen(VALID_TO_CURSCHAR);
298: cursupdate();
299: need_redraw = FALSE;
300: }
301: did_backspace = FALSE;
302:
303: /*
304: * redraw is postponed until after cursupdate() to make 'dollar'
305: * option work correctly.
306: */
307: if (need_redraw)
308: {
309: updateline();
310: need_redraw = FALSE;
311: }
312:
313: showruler(0);
314: setcursor();
315: emsg_on_display = FALSE; /* may remove error message now */
316:
317: c = vgetc();
318: if (c == Ctrl('C'))
319: got_int = FALSE;
320:
321: #ifdef RIGHTLEFT
322: if (p_hkmap && KeyTyped)
323: c = hkmap(c); /* Hebrew mode mapping */
324: #endif
325:
326: #ifdef INSERT_EXPAND
327: if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET)
328: {
329: /* We have just entered ctrl-x mode and aren't quite sure which
330: * ctrl-x mode it will be yet. Now we decide -- webb
331: */
332: switch (c)
333: {
334: case Ctrl('E'):
335: case Ctrl('Y'):
336: ctrl_x_mode = CTRL_X_SCROLL;
337: if (State == INSERT)
338: edit_submode = (char_u *)" (insert) Scroll (^E/^Y)";
339: else
340: edit_submode = (char_u *)" (replace) Scroll (^E/^Y)";
341: break;
342: case Ctrl('L'):
343: ctrl_x_mode = CTRL_X_WHOLE_LINE;
344: edit_submode = (char_u *)" Whole line completion (^L/^N/^P)";
345: break;
346: case Ctrl('F'):
347: ctrl_x_mode = CTRL_X_FILES;
348: edit_submode = (char_u *)" File name completion (^F/^N/^P)";
349: break;
350: case Ctrl('K'):
351: ctrl_x_mode = CTRL_X_DICTIONARY;
352: edit_submode = (char_u *)" Dictionary completion (^K/^N/^P)";
353: break;
354: case Ctrl(']'):
355: ctrl_x_mode = CTRL_X_TAGS;
356: edit_submode = (char_u *)" Tag completion (^]/^N/^P)";
357: break;
358: case Ctrl('I'):
359: ctrl_x_mode = CTRL_X_PATH_PATTERNS;
360: edit_submode = (char_u *)" Path pattern completion (^N/^P)";
361: break;
362: case Ctrl('D'):
363: ctrl_x_mode = CTRL_X_PATH_DEFINES;
364: edit_submode = (char_u *)" Definition completion (^D/^N/^P)";
365: break;
366: default:
367: ctrl_x_mode = 0;
368: break;
369: }
370: showmode();
371: }
372: else if (ctrl_x_mode)
373: {
374: /* We we're already in ctrl-x mode, do we stay in it? */
375: if (!is_ctrl_x_key(c))
376: {
377: if (ctrl_x_mode == CTRL_X_SCROLL)
378: ctrl_x_mode = 0;
379: else
380: ctrl_x_mode = CTRL_X_FINISHED;
381: edit_submode = NULL;
382: }
383: showmode();
384: }
385: if (started_completion || ctrl_x_mode == CTRL_X_FINISHED)
386: {
387: /* Show error message from attempted keyword completion (probably
388: * 'Pattern not found') until another key is hit, then go back to
389: * showing what mode we are in.
390: */
391: showmode();
392: if ((ctrl_x_mode == 0 && c != Ctrl('N') && c != Ctrl('P')) ||
393: ctrl_x_mode == CTRL_X_FINISHED)
394: {
395: /* Get here when we have finished typing a sequence of ^N and
396: * ^P or other completion characters in CTRL-X mode. Free up
397: * memory that was used, and make sure we can redo the insert
398: * -- webb.
399: */
400: if (curr_match != NULL)
401: {
402: /*
403: * If any of the original typed text has been changed,
404: * eg when ignorecase is set, we must add back-spaces to
405: * the redo buffer. We add as few as necessary to delete
406: * just the part of the original text that has changed
407: * -- webb
408: */
409: ptr = curr_match->str;
410: tmp_ptr = original_text;
411: while (*tmp_ptr && *tmp_ptr == *ptr)
412: {
413: ++tmp_ptr;
414: ++ptr;
415: }
416: for (temp = 0; tmp_ptr[temp]; ++temp)
417: AppendCharToRedobuff(K_BS);
418: if (*ptr)
419: AppendToRedobuff(ptr);
420: }
421: /* Break line if it's too long */
422: lnum = curwin->w_cursor.lnum;
423: insertchar(NUL, FALSE, -1);
424: if (lnum != curwin->w_cursor.lnum)
425: updateScreen(CURSUPD);
426: else
427: need_redraw = TRUE;
428:
429: vim_free(complete_pat);
430: complete_pat = NULL;
431: vim_free(original_text);
432: original_text = NULL;
433: free_completions();
434: started_completion = FALSE;
435: ctrl_x_mode = 0;
436: p_sm = save_sm;
437: if (edit_submode != NULL)
438: {
439: edit_submode = NULL;
440: showmode();
441: }
442: }
443: }
444: #endif /* INSERT_EXPAND */
445:
446: if (c != Ctrl('D')) /* remember to detect ^^D and 0^D */
447: lastc = c;
448:
449: #ifdef DIGRAPHS
450: c = do_digraph(c);
451: #endif /* DIGRAPHS */
452:
453: if (c == Ctrl('V') || c == Ctrl('Q'))
454: {
455: if (NextScreen != NULL)
456: screen_outchar('^', curwin->w_winpos + curwin->w_row,
457: #ifdef RIGHTLEFT
458: curwin->w_p_rl ? (int)Columns - 1 - curwin->w_col :
459: #endif
460: curwin->w_col);
461: AppendToRedobuff((char_u *)"\026"); /* CTRL-V */
462: cursupdate();
463:
464: if (!add_to_showcmd(c, FALSE))
465: setcursor();
466:
467: c = get_literal();
468: clear_showcmd();
469: insert_special(c, TRUE);
470: need_redraw = TRUE;
471: #ifdef RIGHTLEFT
472: revinschars++;
473: revinslegal++;
474: #endif
475: continue;
476: }
477:
478: #ifdef CINDENT
479: if (curbuf->b_p_cin
480: # ifdef INSERT_EXPAND
481: && !ctrl_x_mode
482: # endif
483: )
484: {
485: line_is_white = inindent(0);
486:
487: /*
488: * A key name preceded by a bang means that this
489: * key wasn't destined to be inserted. Skip ahead
490: * to the re-indenting if we find one.
491: */
492: if (in_cinkeys(c, '!', line_is_white))
493: goto force_cindent;
494:
495: /*
496: * A key name preceded by a star means that indenting
497: * has to be done before inserting the key.
498: */
499: if (can_cindent && in_cinkeys(c, '*', line_is_white))
500: {
501: stop_arrow();
502:
503: /* re-indent the current line */
504: fixthisline(get_c_indent);
505:
506: /* draw the changes on the screen later */
507: need_redraw = TRUE;
508: }
509: }
510: #endif /* CINDENT */
511:
512: #ifdef RIGHTLEFT
513: if (curwin->w_p_rl)
514: switch (c)
515: {
516: case K_LEFT: c = K_RIGHT; break;
517: case K_S_LEFT: c = K_S_RIGHT; break;
518: case K_RIGHT: c = K_LEFT; break;
519: case K_S_RIGHT: c = K_S_LEFT; break;
520: }
521: #endif
522:
523: switch (c) /* handle character in insert mode */
524: {
525: case K_INS: /* toggle insert/replace mode */
526: if (State == REPLACE)
527: State = INSERT;
528: else
529: State = REPLACE;
530: AppendCharToRedobuff(K_INS);
531: showmode();
532: break;
533:
534: #ifdef INSERT_EXPAND
535: case Ctrl('X'): /* Enter ctrl-x mode */
536: /* We're not sure which ctrl-x mode it will be yet */
537: ctrl_x_mode = CTRL_X_NOT_DEFINED_YET;
538: MSG("^X mode (^E/^Y/^L/^]/^F/^I/^K/^D)");
539: break;
540: #endif /* INSERT_EXPAND */
541:
542: case Ctrl('O'): /* execute one command */
543: if (echeck_abbr(Ctrl('O') + ABBR_OFF))
544: break;
545: count = 0;
546: if (State == INSERT)
547: restart_edit = 'I';
548: else
549: restart_edit = 'R';
550: o_lnum = curwin->w_cursor.lnum;
551: o_eol = (gchar_cursor() == NUL);
552: goto doESCkey;
553:
554: /* Hitting the help key in insert mode is like <ESC> <Help> */
555: case K_HELP:
556: case K_F1:
557: stuffcharReadbuff(K_HELP);
558: /*FALLTHROUGH*/
559:
560: case ESC: /* an escape ends input mode */
561: if (echeck_abbr(ESC + ABBR_OFF))
562: break;
563: /*FALLTHROUGH*/
564:
565: case Ctrl('C'):
566: doESCkey:
567: temp = curwin->w_cursor.col;
568: if (!arrow_used)
569: {
570: AppendToRedobuff(ESC_STR);
571:
572: if (--count > 0) /* repeat what was typed */
573: {
574: (void)start_redo_ins();
575: continue;
576: }
577: stop_insert(&curwin->w_cursor);
578: if (dollar_vcol)
579: {
580: dollar_vcol = 0;
581: /* may have to redraw status line if this was the
582: * first change, show "[+]" */
583: if (curwin->w_redr_status == TRUE)
584: must_redraw = NOT_VALID;
585: else
586: need_redraw = TRUE;
587: }
588: }
589: if (need_redraw)
590: updateline();
591:
592: /* When an autoindent was removed, curswant stays after the
593: * indent */
594: if (!restart_edit && (colnr_t)temp == curwin->w_cursor.col)
595: curwin->w_set_curswant = TRUE;
596:
597: /*
598: * The cursor should end up on the last inserted character.
599: */
600: if (curwin->w_cursor.col != 0 &&
601: (!restart_edit || gchar_cursor() == NUL)
602: #ifdef RIGHTLEFT
603: && !revins
604: #endif
605: )
606: --curwin->w_cursor.col;
607: if (State == REPLACE)
608: replace_flush(); /* free replace stack */
609: State = NORMAL;
610: #ifdef USE_MOUSE
611: setmouse();
612: #endif
613: /* inchar() may have deleted the "INSERT" message */
614: /* for CTRL-O we display -- INSERT COMMAND -- */
615: if (Recording || restart_edit)
616: showmode();
617: else if (p_smd)
618: MSG("");
619: old_indent = 0;
620: return (c == Ctrl('O'));
621:
622: /*
623: * Insert the previously inserted text.
624: * For ^@ the trailing ESC will end the insert, unless there
625: * is an error.
626: */
627: case K_ZERO:
628: case NUL:
629: case Ctrl('A'):
630: if (stuff_inserted(NUL, 1L, (c == Ctrl('A'))) == FAIL &&
631: c != Ctrl('A'))
632: goto doESCkey; /* quit insert mode */
633: break;
634:
635: /*
636: * insert the contents of a register
637: */
638: case Ctrl('R'):
639: if (NextScreen != NULL)
640: screen_outchar('"', curwin->w_winpos + curwin->w_row,
641: #ifdef RIGHTLEFT
642: curwin->w_p_rl ? (int)Columns - 1 - curwin->w_col :
643: #endif
644: curwin->w_col);
645: if (!add_to_showcmd(c, FALSE))
646: setcursor();
647: /* don't map the register name. This also prevents the
648: * mode message to be deleted when ESC is hit */
649: ++no_mapping;
650: #ifdef HAVE_LANGMAP
651: cc = vgetc();
652: LANGMAP_ADJUST(cc, TRUE);
653: if (insertbuf(cc) == FAIL)
654: #else
655: if (insertbuf(vgetc()) == FAIL)
656: #endif
657: {
658: beep_flush();
659: need_redraw = TRUE; /* remove the '"' */
660: }
661: --no_mapping;
662: clear_showcmd();
663: break;
664:
665: #ifdef RIGHTLEFT
666: case Ctrl('B'): /* toggle reverse insert mode */
667: p_ri = !p_ri;
668: revins = (State == INSERT && p_ri);
669: if (revins)
670: undisplay_dollar();
671: showmode();
672: break;
673:
674: case Ctrl('_'): /* toggle language: khmap and revins */
675: /* Move to end of reverse inserted text */
676: if (revins && revinschars && revinsscol >= 0)
677: while (gchar_cursor() != NUL && revinschars--)
678: ++curwin->w_cursor.col;
679: p_ri = !p_ri;
680: revins = (State == INSERT && p_ri);
681: if (revins)
682: {
683: revinsscol = curwin->w_cursor.col;
684: revinslegal++;
685: revinschars = 0;
686: undisplay_dollar();
687: }
688: else
689: revinsscol = -1;
690: p_hkmap = curwin->w_p_rl ^ p_ri; /* be consistent! */
691: showmode();
692: break;
693: #endif
694:
695: /*
696: * If the cursor is on an indent, ^T/^D insert/delete one
697: * shiftwidth. Otherwise ^T/^D behave like a "<<" or ">>".
698: * Always round the indent to 'shiftwith', this is compatible
699: * with vi. But vi only supports ^T and ^D after an
700: * autoindent, we support it everywhere.
701: */
702: case Ctrl('D'): /* make indent one shiftwidth smaller */
703: #ifdef INSERT_EXPAND
704: if (ctrl_x_mode == CTRL_X_PATH_DEFINES)
705: goto docomplete;
706: #endif /* INSERT_EXPAND */
707: /* FALLTHROUGH */
708: case Ctrl('T'): /* make indent one shiftwidth greater */
709: stop_arrow();
710: AppendCharToRedobuff(c);
711:
712: /*
713: * 0^D and ^^D: remove all indent.
714: */
715: if ((lastc == '0' || lastc == '^') && curwin->w_cursor.col)
716: {
717: --curwin->w_cursor.col;
718: (void)delchar(FALSE); /* delete the '^' or '0' */
719: if (lastc == '^')
720: old_indent = get_indent(); /* remember curr. indent */
721: change_indent(INDENT_SET, 0, TRUE);
722: }
723: else
724: change_indent(c == Ctrl('D') ? INDENT_DEC : INDENT_INC,
725: 0, TRUE);
726:
727: did_ai = FALSE;
728: did_si = FALSE;
729: can_si = FALSE;
730: can_si_back = FALSE;
731: #ifdef CINDENT
732: can_cindent = FALSE; /* no cindenting after ^D or ^T */
733: #endif
734: goto redraw;
735:
736: case K_DEL:
737: stop_arrow();
738: if (gchar_cursor() == NUL) /* delete newline */
739: {
740: temp = curwin->w_cursor.col;
741: if (!p_bs || /* only if 'bs' set */
742: u_save((linenr_t)(curwin->w_cursor.lnum - 1),
743: (linenr_t)(curwin->w_cursor.lnum + 2)) == FAIL ||
744: do_join(FALSE, TRUE) == FAIL)
745: beep_flush();
746: else
747: curwin->w_cursor.col = temp;
748: }
749: else if (delchar(FALSE) == FAIL)/* delete char under cursor */
750: beep_flush();
751: did_ai = FALSE;
752: did_si = FALSE;
753: can_si = FALSE;
754: can_si_back = FALSE;
755: AppendCharToRedobuff(c);
756: goto redraw;
757:
758: case K_BS:
759: case Ctrl('H'):
760: mode = BACKSPACE_CHAR;
761: dodel:
762: /* can't delete anything in an empty file */
763: /* can't backup past first character in buffer */
764: /* can't backup past starting point unless 'backspace' > 1 */
765: /* can backup to a previous line if 'backspace' == 0 */
766: if (bufempty() || (
767: #ifdef RIGHTLEFT
768: !revins &&
769: #endif
770: ((curwin->w_cursor.lnum == 1 &&
771: curwin->w_cursor.col <= 0) ||
772: (p_bs < 2 && (arrow_used ||
773: (curwin->w_cursor.lnum == Insstart.lnum &&
774: curwin->w_cursor.col <= Insstart.col) ||
775: (curwin->w_cursor.col <= 0 && p_bs == 0))))))
776: {
777: beep_flush();
778: goto redraw;
779: }
780:
781: stop_arrow();
782: #ifdef CINDENT
783: if (inindent(0))
784: can_cindent = FALSE;
785: #endif
786: #ifdef RIGHTLEFT
787: if (revins) /* put cursor after last inserted char */
788: inc_cursor();
789: #endif
790: if (curwin->w_cursor.col <= 0) /* delete newline! */
791: {
792: lnum = Insstart.lnum;
793: if (curwin->w_cursor.lnum == Insstart.lnum
794: #ifdef RIGHTLEFT
795: || revins
796: #endif
797: )
798: {
799: if (u_save((linenr_t)(curwin->w_cursor.lnum - 2),
800: (linenr_t)(curwin->w_cursor.lnum + 1)) == FAIL)
801: goto redraw;
802: --Insstart.lnum;
803: Insstart.col = 0;
804: }
805: /*
806: * In replace mode:
807: * cc < 0: NL was inserted, delete it
808: * cc >= 0: NL was replaced, put original characters back
809: */
810: cc = -1;
811: if (State == REPLACE)
812: cc = replace_pop();
813: /* in replace mode, in the line we started replacing, we
814: only move the cursor */
815: if (State != REPLACE || curwin->w_cursor.lnum > lnum)
816: {
817: temp = gchar_cursor(); /* remember current char */
818: --curwin->w_cursor.lnum;
819: (void)do_join(FALSE, curs_rows() == OK);
820: if (temp == NUL && gchar_cursor() != NUL)
821: ++curwin->w_cursor.col;
822: /*
823: * in REPLACE mode we have to put back the text that
824: * was replace by the NL. On the replace stack is
825: * first a NUL-terminated sequence of characters that
826: * were deleted and then the character that NL
827: * replaced.
828: */
829: if (State == REPLACE)
830: {
831: /*
832: * Do the next ins_char() in NORMAL state, to
833: * prevent ins_char() from replacing characters and
834: * avoiding showmatch().
835: */
836: State = NORMAL;
837: /*
838: * restore blanks deleted after cursor
839: */
840: while (cc > 0)
841: {
842: temp = curwin->w_cursor.col;
843: ins_char(cc);
844: curwin->w_cursor.col = temp;
845: cc = replace_pop();
846: }
847: cc = replace_pop();
848: if (cc > 0)
849: {
850: ins_char(cc);
851: dec_cursor();
852: }
853: State = REPLACE;
854: }
855: }
856: else
857: dec_cursor();
858: did_ai = FALSE;
859: }
860: else
861: {
862: #ifdef RIGHTLEFT
863: if (revins) /* put cursor on last inserted char */
864: dec_cursor();
865: #endif
866: mincol = 0;
867: /* keep indent */
868: if (mode == BACKSPACE_LINE && curbuf->b_p_ai
869: #ifdef RIGHTLEFT
870: && !revins
871: #endif
872: )
873: {
874: temp = curwin->w_cursor.col;
875: beginline(TRUE);
876: if (curwin->w_cursor.col < (colnr_t)temp)
877: mincol = curwin->w_cursor.col;
878: curwin->w_cursor.col = temp;
879: }
880:
881: /* delete upto starting point, start of line or previous
882: * word */
883: do
884: {
885: #ifdef RIGHTLEFT
886: if (!revins) /* put cursor on char to be deleted */
887: #endif
888: dec_cursor();
889:
890: /* start of word? */
891: if (mode == BACKSPACE_WORD &&
892: !vim_isspace(gchar_cursor()))
893: {
894: mode = BACKSPACE_WORD_NOT_SPACE;
895: temp = iswordchar(gchar_cursor());
896: }
897: /* end of word? */
898: else if (mode == BACKSPACE_WORD_NOT_SPACE &&
899: (vim_isspace(cc = gchar_cursor()) ||
900: iswordchar(cc) != temp))
901: {
902: #ifdef RIGHTLEFT
903: if (!revins)
904: #endif
905: inc_cursor();
906: #ifdef RIGHTLEFT
907: else if (State == REPLACE)
908: dec_cursor();
909: #endif
910: break;
911: }
912: if (State == REPLACE)
913: {
914: /*
915: * cc < 0: replace stack empty, just move cursor
916: * cc == 0: character was inserted, delete it
917: * cc > 0: character was replace, put original back
918: */
919: cc = replace_pop();
920: if (cc > 0)
921: pchar_cursor(cc);
922: else if (cc == 0)
923: (void)delchar(FALSE);
924: }
925: else /* State != REPLACE */
926: {
927: (void)delchar(FALSE);
928: #ifdef RIGHTLEFT
929: if (revinschars)
930: {
931: revinschars--;
932: revinslegal++;
933: }
934: if (revins && gchar_cursor() == NUL)
935: break;
936: #endif
937: }
938: /* Just a single backspace?: */
939: if (mode == BACKSPACE_CHAR)
940: break;
941: } while (
942: #ifdef RIGHTLEFT
943: revins ||
944: #endif
945: (curwin->w_cursor.col > mincol &&
946: (curwin->w_cursor.lnum != Insstart.lnum ||
947: curwin->w_cursor.col != Insstart.col)));
948: did_backspace = TRUE;
949: }
950: did_si = FALSE;
951: can_si = FALSE;
952: can_si_back = FALSE;
953: if (curwin->w_cursor.col <= 1)
954: did_ai = FALSE;
955: /*
956: * It's a little strange to put backspaces into the redo
957: * buffer, but it makes auto-indent a lot easier to deal
958: * with.
959: */
960: AppendCharToRedobuff(c);
961: redraw:
962: need_redraw = TRUE;
963: break;
964:
965: case Ctrl('W'): /* delete word before cursor */
966: mode = BACKSPACE_WORD;
967: goto dodel;
968:
969: case Ctrl('U'): /* delete inserted text in current line */
970: mode = BACKSPACE_LINE;
971: goto dodel;
972:
973: #ifdef USE_MOUSE
974: case K_LEFTMOUSE:
975: case K_LEFTDRAG:
976: case K_LEFTRELEASE:
977: case K_MIDDLEMOUSE:
978: case K_MIDDLEDRAG:
979: case K_MIDDLERELEASE:
980: case K_RIGHTMOUSE:
981: case K_RIGHTDRAG:
982: case K_RIGHTRELEASE:
983: #ifdef USE_GUI
984: /* When GUI is active, also move/paste when 'mouse' is empty */
985: if (!gui.in_use)
986: #endif
987: if (!mouse_has(MOUSE_INSERT))
988: break;
989:
990: undisplay_dollar();
991: tpos = curwin->w_cursor;
992: if (do_mouse(c, BACKWARD, 1L, FALSE))
993: {
994: start_arrow(&tpos);
995: # ifdef CINDENT
996: can_cindent = TRUE;
997: # endif
998: }
999:
1000: break;
1001:
1002: case K_IGNORE:
1003: break;
1004: #endif
1005:
1006: #ifdef USE_GUI
1007: case K_SCROLLBAR:
1008: undisplay_dollar();
1009: tpos = curwin->w_cursor;
1010: if (gui_do_scroll())
1011: {
1012: start_arrow(&tpos);
1013: # ifdef CINDENT
1014: can_cindent = TRUE;
1015: # endif
1016: }
1017: break;
1018:
1019: case K_HORIZ_SCROLLBAR:
1020: undisplay_dollar();
1021: tpos = curwin->w_cursor;
1022: if (gui_do_horiz_scroll())
1023: {
1024: start_arrow(&tpos);
1025: #ifdef CINDENT
1026: can_cindent = TRUE;
1027: #endif
1028: }
1029: break;
1030: #endif
1031:
1032: case K_LEFT:
1033: undisplay_dollar();
1034: tpos = curwin->w_cursor;
1035: if (oneleft() == OK)
1036: {
1037: start_arrow(&tpos);
1038: #ifdef RIGHTLEFT
1039: /* If exit reversed string, position is fixed */
1040: if (revinsscol != -1 &&
1041: (int)curwin->w_cursor.col >= revinsscol)
1042: revinslegal++;
1043: revinschars++;
1044: #endif
1045: }
1046:
1047: /*
1048: * if 'whichwrap' set for cursor in insert mode may go to
1049: * previous line
1050: */
1051: else if (vim_strchr(p_ww, '[') != NULL &&
1052: curwin->w_cursor.lnum > 1)
1053: {
1054: start_arrow(&tpos);
1055: --(curwin->w_cursor.lnum);
1056: coladvance(MAXCOL);
1057: curwin->w_set_curswant = TRUE; /* so we stay at the end */
1058: }
1059: else
1060: beep_flush();
1061: break;
1062:
1063: case K_HOME:
1064: undisplay_dollar();
1065: tpos = curwin->w_cursor;
1066: if ((mod_mask & MOD_MASK_CTRL))
1067: curwin->w_cursor.lnum = 1;
1068: curwin->w_cursor.col = 0;
1069: curwin->w_curswant = 0;
1070: start_arrow(&tpos);
1071: break;
1072:
1073: case K_END:
1074: undisplay_dollar();
1075: tpos = curwin->w_cursor;
1076: if ((mod_mask & MOD_MASK_CTRL))
1077: curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
1078: coladvance(MAXCOL);
1079: curwin->w_curswant = MAXCOL;
1080: start_arrow(&tpos);
1081: break;
1082:
1083: case K_S_LEFT:
1084: undisplay_dollar();
1085: if (curwin->w_cursor.lnum > 1 || curwin->w_cursor.col > 0)
1086: {
1087: start_arrow(&curwin->w_cursor);
1088: (void)bck_word(1L, 0, FALSE);
1089: }
1090: else
1091: beep_flush();
1092: break;
1093:
1094: case K_RIGHT:
1095: undisplay_dollar();
1096: if (gchar_cursor() != NUL)
1097: {
1098: start_arrow(&curwin->w_cursor);
1099: curwin->w_set_curswant = TRUE;
1100: ++curwin->w_cursor.col;
1101: #ifdef RIGHTLEFT
1102: revinslegal++;
1103: if (revinschars)
1104: revinschars--;
1105: #endif
1106: }
1107: /* if 'whichwrap' set for cursor in insert mode may go
1108: * to next line */
1109: else if (vim_strchr(p_ww, ']') != NULL &&
1110: curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
1111: {
1112: start_arrow(&curwin->w_cursor);
1113: curwin->w_set_curswant = TRUE;
1114: ++curwin->w_cursor.lnum;
1115: curwin->w_cursor.col = 0;
1116: }
1117: else
1118: beep_flush();
1119: break;
1120:
1121: case K_S_RIGHT:
1122: undisplay_dollar();
1123: if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count ||
1124: gchar_cursor() != NUL)
1125: {
1126: start_arrow(&curwin->w_cursor);
1127: (void)fwd_word(1L, 0, 0);
1128: }
1129: else
1130: beep_flush();
1131: break;
1132:
1133: case K_UP:
1134: undisplay_dollar();
1135: tpos = curwin->w_cursor;
1136: if (cursor_up(1L) == OK)
1137: {
1138: start_arrow(&tpos);
1139: #ifdef CINDENT
1140: can_cindent = TRUE;
1141: #endif
1142: }
1143: else
1144: beep_flush();
1145: break;
1146:
1147: case K_S_UP:
1148: case K_PAGEUP:
1149: undisplay_dollar();
1150: tpos = curwin->w_cursor;
1151: if (onepage(BACKWARD, 1L) == OK)
1152: {
1153: start_arrow(&tpos);
1154: #ifdef CINDENT
1155: can_cindent = TRUE;
1156: #endif
1157: }
1158: else
1159: beep_flush();
1160: break;
1161:
1162: case K_DOWN:
1163: undisplay_dollar();
1164: tpos = curwin->w_cursor;
1165: if (cursor_down(1L) == OK)
1166: {
1167: start_arrow(&tpos);
1168: #ifdef CINDENT
1169: can_cindent = TRUE;
1170: #endif
1171: }
1172: else
1173: beep_flush();
1174: break;
1175:
1176: case K_S_DOWN:
1177: case K_PAGEDOWN:
1178: undisplay_dollar();
1179: tpos = curwin->w_cursor;
1180: if (onepage(FORWARD, 1L) == OK)
1181: {
1182: start_arrow(&tpos);
1183: #ifdef CINDENT
1184: can_cindent = TRUE;
1185: #endif
1186: }
1187: else
1188: beep_flush();
1189: break;
1190:
1191: case TAB: /* TAB or Complete patterns along path */
1192: #ifdef INSERT_EXPAND
1193: if (ctrl_x_mode == CTRL_X_PATH_PATTERNS)
1194: goto docomplete;
1195: #endif /* INSERT_EXPAND */
1196:
1197: if (Insstart_blank_vcol == MAXCOL &&
1198: curwin->w_cursor.lnum == Insstart.lnum)
1199: Insstart_blank_vcol = curwin->w_virtcol;
1200: if (echeck_abbr(TAB + ABBR_OFF))
1201: break;
1202: i = inindent(0);
1203: #ifdef CINDENT
1204: if (i)
1205: can_cindent = FALSE;
1206: #endif
1207: if (!curbuf->b_p_et && !(p_sta && i))
1208: goto normalchar;
1209:
1210: stop_arrow();
1211: did_ai = FALSE;
1212: did_si = FALSE;
1213: can_si = FALSE;
1214: can_si_back = FALSE;
1215: AppendToRedobuff((char_u *)"\t");
1216:
1217: if (p_sta && i) /* insert tab in indent */
1218: {
1219: change_indent(INDENT_INC, 0, p_sr);
1220: goto redraw;
1221: }
1222:
1223: /*
1224: * p_et is set: expand a tab into spaces
1225: */
1226: temp = (int)curbuf->b_p_ts;
1227: temp -= curwin->w_virtcol % temp;
1228:
1229: /*
1230: * insert the first space with ins_char(); it will delete one
1231: * char in replace mode. Insert the rest with ins_str(); it
1232: * will not delete any chars
1233: */
1234: ins_char(' ');
1235: while (--temp)
1236: {
1237: ins_str((char_u *)" ");
1238: if (State == REPLACE) /* no char replaced */
1239: replace_push(NUL);
1240: }
1241: goto redraw;
1242:
1243: case CR:
1244: case NL:
1245: if (echeck_abbr(c + ABBR_OFF))
1246: break;
1247: stop_arrow();
1248: if (State == REPLACE)
1249: replace_push(NUL);
1250: #ifdef RIGHTLEFT
1251: /* NL in reverse insert will allways start in the end of
1252: * current line. */
1253: if (revins)
1254: while (gchar_cursor() != NUL)
1255: ++curwin->w_cursor.col;
1256: #endif
1257:
1258: AppendToRedobuff(NL_STR);
1259: if (has_format_option(FO_RET_COMS))
1260: fo_do_comments = TRUE;
1261: i = Opencmd(FORWARD, TRUE, FALSE);
1262: fo_do_comments = FALSE;
1263: #ifdef CINDENT
1264: can_cindent = TRUE;
1265: #endif
1266: if (!i)
1267: goto doESCkey; /* out of memory */
1268: break;
1269:
1270: #ifdef DIGRAPHS
1271: case Ctrl('K'):
1272: #ifdef INSERT_EXPAND
1273: if (ctrl_x_mode == CTRL_X_DICTIONARY)
1274: goto docomplete;
1275: #endif
1276: if (NextScreen != NULL)
1277: screen_outchar('?', curwin->w_winpos + curwin->w_row,
1278: #ifdef RIGHTLEFT
1279: curwin->w_p_rl ? (int)Columns - 1 - curwin->w_col :
1280: #endif
1281: curwin->w_col);
1282: if (!add_to_showcmd(c, FALSE))
1283: setcursor();
1284: /* don't map the digraph chars. This also prevents the
1285: * mode message to be deleted when ESC is hit */
1286: ++no_mapping;
1287: ++allow_keys;
1288: c = vgetc();
1289: --no_mapping;
1290: --allow_keys;
1291: if (IS_SPECIAL(c)) /* special key */
1292: {
1293: clear_showcmd();
1294: insert_special(c, TRUE);
1295: need_redraw = TRUE;
1296: break;
1297: }
1298: if (c != ESC)
1299: {
1300: if (charsize(c) == 1 && NextScreen != NULL)
1301: screen_outchar(c, curwin->w_winpos + curwin->w_row,
1302: #ifdef RIGHTLEFT
1303: curwin->w_p_rl ? (int)Columns - 1 - curwin->w_col :
1304: #endif
1305: curwin->w_col);
1306: if (!add_to_showcmd(c, FALSE))
1307: setcursor();
1308: ++no_mapping;
1309: ++allow_keys;
1310: cc = vgetc();
1311: --no_mapping;
1312: --allow_keys;
1313: if (cc != ESC)
1314: {
1315: AppendToRedobuff((char_u *)"\026"); /* CTRL-V */
1316: c = getdigraph(c, cc, TRUE);
1317: clear_showcmd();
1318: goto normalchar;
1319: }
1320: }
1321: clear_showcmd();
1322: need_redraw = TRUE;
1323: break;
1324: #else /* DIGRAPHS */
1325: # ifdef INSERT_EXPAND
1326: case Ctrl('K'):
1327: if (ctrl_x_mode != CTRL_X_DICTIONARY)
1328: goto normalchar;
1329: goto docomplete;
1330: # endif /* INSERT_EXPAND */
1331: #endif /* DIGRAPHS */
1332:
1333: #ifdef INSERT_EXPAND
1334: case Ctrl(']'): /* Tag name completion after ^X */
1335: if (ctrl_x_mode != CTRL_X_TAGS)
1336: goto normalchar;
1337: goto docomplete;
1338:
1339: case Ctrl('F'): /* File name completion after ^X */
1340: if (ctrl_x_mode != CTRL_X_FILES)
1341: goto normalchar;
1342: goto docomplete;
1343:
1344: case Ctrl('L'): /* Whole line completion after ^X */
1345: if (ctrl_x_mode != CTRL_X_WHOLE_LINE)
1346: goto normalchar;
1347: /* FALLTHROUGH */
1348:
1349: case Ctrl('P'): /* Do previous pattern completion */
1350: case Ctrl('N'): /* Do next pattern completion */
1351: docomplete:
1352: if (c == Ctrl('P') || c == Ctrl('L'))
1353: complete_direction = BACKWARD;
1354: else
1355: complete_direction = FORWARD;
1356: quick_m = mesg = NULL; /* No message by default */
1357: if (!started_completion)
1358: {
1359: /* First time we hit ^N or ^P (in a row, I mean) */
1360:
1361: /* Turn off 'sm' so we don't show matches with ^X^L */
1362: save_sm = p_sm;
1363: p_sm = FALSE;
1364:
1365: if (ctrl_x_mode == 0)
1366: {
1367: edit_submode = (char_u *)" Keyword completion (^P/^N)";
1368: showmode();
1369: }
1370: did_ai = FALSE;
1371: did_si = FALSE;
1372: can_si = FALSE;
1373: can_si_back = FALSE;
1374: stop_arrow();
1375: done_dir = 0;
1376: first_match_pos = curwin->w_cursor;
1377: ptr = tmp_ptr = ml_get(first_match_pos.lnum);
1378: complete_col = first_match_pos.col;
1379: temp = (int)complete_col - 1;
1380:
1381: /* Work out completion pattern and original text -- webb */
1382: if (ctrl_x_mode == 0 || (ctrl_x_mode & CTRL_X_WANT_IDENT))
1383: {
1384: if (temp < 0 || !iswordchar(ptr[temp]))
1385: {
1386: /* Match any word of at least two chars */
1387: complete_pat = strsave((char_u *)"\\<\\k\\k");
1388: if (complete_pat == NULL)
1389: break;
1390: tmp_ptr += complete_col;
1391: temp = 0;
1392: }
1393: else
1394: {
1395: while (temp >= 0 && iswordchar(ptr[temp]))
1396: temp--;
1397: tmp_ptr += ++temp;
1398: if ((temp = (int)complete_col - temp) == 1)
1399: {
1400: /* Only match word with at least two
1401: * chars -- webb
1402: */
1403: sprintf((char *)IObuff, "\\<%c\\k", *tmp_ptr);
1404: complete_pat = strsave(IObuff);
1405: if (complete_pat == NULL)
1406: break;
1407: }
1408: else
1409: {
1410: complete_pat = alloc(temp + 3);
1411: if (complete_pat == NULL)
1412: break;
1413: sprintf((char *)complete_pat, "\\<%.*s", temp,
1414: tmp_ptr);
1415: }
1416: }
1417: }
1418: else if (ctrl_x_mode == CTRL_X_WHOLE_LINE)
1419: {
1420: tmp_ptr = skipwhite(ptr);
1421: temp = (int)complete_col - (tmp_ptr - ptr);
1422: complete_pat = strnsave(tmp_ptr, temp);
1423: if (complete_pat == NULL)
1424: break;
1425: }
1426: else if (ctrl_x_mode == CTRL_X_FILES)
1427: {
1428: while (temp >= 0 && isfilechar(ptr[temp]))
1429: temp--;
1430: tmp_ptr += ++temp;
1431: temp = (int)complete_col - temp;
1432: complete_pat = addstar(tmp_ptr, temp);
1433: if (complete_pat == NULL)
1434: break;
1435: }
1436: original_text = strnsave(tmp_ptr, temp);
1437: if (original_text == NULL)
1438: {
1439: vim_free(complete_pat);
1440: complete_pat = NULL;
1441: break;
1442: }
1443:
1444: complete_col = tmp_ptr - ptr;
1445: first_match_pos.col -= temp;
1446:
1447: /* So that ^N can match word immediately after cursor */
1448: if (ctrl_x_mode == 0)
1449: dec(&first_match_pos);
1450:
1451: last_match_pos = first_match_pos;
1452:
1453: /* Get list of all completions now, if appropriate */
1454: if (ctrl_x_mode == CTRL_X_PATH_PATTERNS ||
1455: ctrl_x_mode == CTRL_X_PATH_DEFINES)
1456: {
1457: started_completion = TRUE;
1458: find_pattern_in_path(complete_pat,
1459: (int)STRLEN(complete_pat), FALSE, FALSE,
1460: (ctrl_x_mode == CTRL_X_PATH_DEFINES) ? FIND_DEFINE
1461: : FIND_ANY, 1L, ACTION_EXPAND,
1462: (linenr_t)1, (linenr_t)MAXLNUM);
1463:
1464: if (make_cyclic() > 1)
1465: {
1466: sprintf((char *)IObuff, "There are %d matches",
1467: count_completions());
1468: mesg = IObuff;
1469: }
1470: }
1471: else if (ctrl_x_mode == CTRL_X_DICTIONARY)
1472: {
1473: started_completion = TRUE;
1474: if (*p_dict == NUL)
1475: mesg = (char_u *)"'dictionary' option is empty";
1476: else
1477: {
1478: complete_dictionaries(complete_pat,
1479: complete_direction);
1480: if (make_cyclic() > 1)
1481: {
1482: sprintf((char *)IObuff,
1483: "There are %d matching words",
1484: count_completions());
1485: mesg = IObuff;
1486: }
1487: }
1488: }
1489: else if (ctrl_x_mode == CTRL_X_TAGS)
1490: {
1491: started_completion = TRUE;
1492: /* set reg_ic according to p_ic, p_scs and pat */
1493: set_reg_ic(complete_pat);
1494: prog = vim_regcomp(complete_pat);
1495: if (prog != NULL &&
1496: find_tags(NULL, prog, &num_matches, &matches, FALSE)
1497: == OK && num_matches > 0)
1498: {
1499: for (i = 0; i < num_matches; i++)
1500: if (add_completion(matches[i], -1, NULL,
1501: FORWARD) == RET_ERROR)
1502: break;
1503: FreeWild(num_matches, matches);
1504: vim_free(prog);
1505: if (make_cyclic() > 1)
1506: {
1507: sprintf((char *)IObuff,
1508: "There are %d matching tags",
1509: count_completions());
1510: mesg = IObuff;
1511: }
1512: }
1513: else
1514: {
1515: vim_free(prog);
1516: vim_free(complete_pat);
1517: complete_pat = NULL;
1518: }
1519: }
1520: else if (ctrl_x_mode == CTRL_X_FILES)
1521: {
1522: started_completion = TRUE;
1523: expand_interactively = TRUE;
1524: if (ExpandWildCards(1, &complete_pat, &num_matches,
1525: &matches, FALSE, FALSE) == OK)
1526: {
1527: for (i = 0; i < num_matches; i++)
1528: if (add_completion(matches[i], -1, NULL,
1529: FORWARD) == RET_ERROR)
1530: break;
1531: FreeWild(num_matches, matches);
1532: if (make_cyclic() > 1)
1533: {
1534: sprintf((char *)IObuff,
1535: "There are %d matching file names",
1536: count_completions());
1537: mesg = IObuff;
1538: }
1539: }
1540: else
1541: {
1542: vim_free(complete_pat);
1543: complete_pat = NULL;
1544: }
1545: expand_interactively = FALSE;
1546: }
1547: }
1548: /*
1549: * In insert mode: Delete the typed part.
1550: * In replace mode: Put the old characters back, if any.
1551: */
1552: while (curwin->w_cursor.col > complete_col)
1553: {
1554: curwin->w_cursor.col--;
1555: if (State == REPLACE)
1556: {
1557: if ((cc = replace_pop()) > 0)
1558: pchar(curwin->w_cursor, cc);
1559: }
1560: else
1561: delchar(FALSE);
1562: }
1563: complete_pos = NULL;
1564: if (started_completion && curr_match == NULL &&
1565: (p_ws || done_dir == BOTH_DIRECTIONS))
1566: quick_m = e_patnotf;
1567: else if (curr_match != NULL && complete_direction == FORWARD &&
1568: curr_match->next != NULL)
1569: curr_match = curr_match->next;
1570: else if (curr_match != NULL && complete_direction == BACKWARD &&
1571: curr_match->prev != NULL)
1572: curr_match = curr_match->prev;
1573: else
1574: {
1575: complete_pos = (complete_direction == FORWARD) ?
1576: &last_match_pos : &first_match_pos;
1577: /*
1578: * If 'infercase' is set, don't use 'smartcase' here
1579: */
1580: save_p_scs = p_scs;
1581: if (curbuf->b_p_inf)
1582: p_scs = FALSE;
1583: for (;;)
1584: {
1585: if (ctrl_x_mode == CTRL_X_WHOLE_LINE)
1586: temp = search_for_exact_line(complete_pos,
1587: complete_direction, complete_pat);
1588: else
1589: temp = searchit(complete_pos, complete_direction,
1590: complete_pat, 1L,
1591: SEARCH_KEEP + SEARCH_NFMSG, RE_LAST);
1592: if (temp == FAIL)
1593: {
1594: if (!p_ws && done_dir != -complete_direction)
1595: {
1596: /*
1597: * With nowrapscan, we haven't finished
1598: * looking in the other direction yet -- webb
1599: */
1600: temp = OK;
1601: done_dir = complete_direction;
1602: }
1603: else if (!p_ws)
1604: done_dir = BOTH_DIRECTIONS;
1605: break;
1606: }
1607: if (!started_completion)
1608: {
1609: started_completion = TRUE;
1610: first_match_pos = *complete_pos;
1611: last_match_pos = *complete_pos;
1612: }
1613: else if (first_match_pos.lnum == last_match_pos.lnum &&
1614: first_match_pos.col == last_match_pos.col)
1615: {
1616: /* We have found all the matches in this file */
1617: temp = FAIL;
1618: break;
1619: }
1620: ptr = ml_get_pos(complete_pos);
1621: if (ctrl_x_mode == CTRL_X_WHOLE_LINE)
1622: temp = STRLEN(ptr);
1623: else
1624: {
1625: tmp_ptr = ptr;
1626: temp = 0;
1627: while (*tmp_ptr != NUL && iswordchar(*tmp_ptr++))
1628: temp++;
1629: }
1630: if (add_completion_and_infercase(ptr, temp, NULL,
1631: complete_direction) != FAIL)
1632: {
1633: temp = OK;
1634: break;
1635: }
1636: }
1637: p_scs = save_p_scs;
1638: }
1639: if (complete_pos != NULL && temp == FAIL)
1640: {
1641: int tot;
1642:
1643: tot = count_completions(); /* Total num matches */
1644: if (curr_match != NULL)
1645: (void)make_cyclic();
1646: if (tot > 1)
1647: {
1648: sprintf((char *)IObuff,
1649: "All %d matches have now been found", tot);
1650: mesg = IObuff;
1651: }
1652: else if (tot == 0)
1653: quick_m = e_patnotf;
1654: }
1655:
1656: /* eat the ESC to avoid leaving insert mode */
1657: if (got_int)
1658: {
1659: (void)vgetc();
1660: got_int = FALSE;
1661: }
1662:
1663: /*
1664: * When using match from another file, show the file name.
1665: */
1666: if (curr_match != NULL)
1667: ptr = curr_match->str;
1668: else /* back to what has been typed */
1669: ptr = original_text;
1670:
1671: if (curr_match == NULL || curr_match->original)
1672: {
1673: edit_submode_extra = (char_u *)"Back at original";
1674: edit_submode_highl = TRUE;
1675: }
1676: else if (first_match != NULL && first_match->next != NULL &&
1677: (first_match->next == first_match ||
1678: first_match->next->original))
1679: {
1680: edit_submode_extra = (char_u *)"(the only match)";
1681: edit_submode_highl = FALSE;
1682: }
1683:
1684: /*
1685: * Use ins_char() to insert the text, it is a bit slower than
1686: * ins_str(), but it takes care of replace mode.
1687: */
1688: if (ptr != NULL)
1689: while (*ptr)
1690: ins_char(*ptr++);
1691:
1692: started_completion = TRUE;
1693: need_redraw = TRUE;
1694: (void)set_highlight('r');
1695: msg_highlight = TRUE;
1696: if (mesg != NULL)
1697: {
1698: msg(mesg);
1699: mch_delay(1000L, FALSE);
1700: }
1701: else if (quick_m != NULL)
1702: msg(quick_m);
1703: else if (edit_submode_extra != NULL)
1704: showmode();
1705: edit_submode_extra = NULL;
1706: msg_highlight = FALSE;
1707:
1708: /*
1709: * If there is a file name for the match, overwrite any
1710: * previous message, it's more interesting to know where the
1711: * match comes from, except when using the dictionary.
1712: * Truncate the file name to avoid a wait for return.
1713: */
1714: if (curr_match != NULL && curr_match->fname != NULL &&
1715: (ctrl_x_mode != CTRL_X_DICTIONARY ||
1716: (mesg == NULL && quick_m == NULL)))
1717: {
1718: STRCPY(IObuff, "match in file ");
1719: i = (strsize(curr_match->fname) + 16) - sc_col;
1720: if (i <= 0)
1721: i = 0;
1722: else
1723: STRCAT(IObuff, "<");
1724: STRCAT(IObuff, curr_match->fname + i);
1725: msg(IObuff);
1726: }
1727: break;
1728: #endif /* INSERT_EXPAND */
1729:
1730: case Ctrl('Y'): /* copy from previous line */
1731: #ifdef INSERT_EXPAND
1732: if (ctrl_x_mode == CTRL_X_SCROLL)
1733: {
1734: scrolldown_clamp();
1735: updateScreen(VALID);
1736: break;
1737: }
1738: #endif /* INSERT_EXPAND */
1739: lnum = curwin->w_cursor.lnum - 1;
1740: goto copychar;
1741:
1742: case Ctrl('E'): /* copy from next line */
1743: #ifdef INSERT_EXPAND
1744: if (ctrl_x_mode == CTRL_X_SCROLL)
1745: {
1746: scrollup_clamp();
1747: updateScreen(VALID);
1748: break;
1749: }
1750: #endif /* INSERT_EXPAND */
1751: lnum = curwin->w_cursor.lnum + 1;
1752: copychar:
1753: if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count)
1754: {
1755: beep_flush();
1756: break;
1757: }
1758:
1759: /* try to advance to the cursor column */
1760: temp = 0;
1761: ptr = ml_get(lnum);
1762: while ((colnr_t)temp < curwin->w_virtcol && *ptr)
1763: temp += lbr_chartabsize(ptr++, (colnr_t)temp);
1764:
1765: if ((colnr_t)temp > curwin->w_virtcol)
1766: --ptr;
1767: if ((c = *ptr) == NUL)
1768: {
1769: beep_flush();
1770: break;
1771: }
1772:
1773: /*FALLTHROUGH*/
1774: default:
1775: normalchar:
1776: /*
1777: * do some very smart indenting when entering '{' or '}'
1778: */
1779: if (((did_si || can_si_back) && c == '{') ||
1780: (can_si && c == '}'))
1781: {
1782: FPOS *pos, old_pos;
1783:
1784: /* for '}' set indent equal to indent of line
1785: * containing matching '{'
1786: */
1787: if (c == '}' && (pos = findmatch('{')) != NULL)
1788: {
1789: old_pos = curwin->w_cursor;
1790: /*
1791: * If the matching '{' has a ')' immediately before it
1792: * (ignoring white-space), then line up with the start
1793: * of the line containing the matching '(' if there is
1794: * one. This handles the case where an
1795: * "if (..\n..) {" statement continues over multiple
1796: * lines -- webb
1797: */
1798: ptr = ml_get(pos->lnum);
1799: i = pos->col;
1800: if (i > 0) /* skip blanks before '{' */
1801: while (--i > 0 && vim_iswhite(ptr[i]))
1802: ;
1803: curwin->w_cursor.lnum = pos->lnum;
1804: curwin->w_cursor.col = i;
1805: if (ptr[i] == ')' && (pos = findmatch('(')) != NULL)
1806: curwin->w_cursor = *pos;
1807: i = get_indent();
1808: curwin->w_cursor = old_pos;
1809: set_indent(i, TRUE);
1810: }
1811: else if (curwin->w_cursor.col > 0)
1812: {
1813: /*
1814: * when inserting '{' after "O" reduce indent, but not
1815: * more than indent of previous line
1816: */
1817: temp = TRUE;
1818: if (c == '{' && can_si_back &&
1819: curwin->w_cursor.lnum > 1)
1820: {
1821: old_pos = curwin->w_cursor;
1822: i = get_indent();
1823: while (curwin->w_cursor.lnum > 1)
1824: {
1825: ptr = skipwhite(
1826: ml_get(--(curwin->w_cursor.lnum)));
1827: /* ignore empty lines and lines starting with
1828: * '#'.
1829: */
1830: if (*ptr != '#' && *ptr != NUL)
1831: break;
1832: }
1833: if (get_indent() >= i)
1834: temp = FALSE;
1835: curwin->w_cursor = old_pos;
1836: }
1837: if (temp)
1838: shift_line(TRUE, FALSE, 1);
1839: }
1840: }
1841: /* set indent of '#' always to 0 */
1842: if (curwin->w_cursor.col > 0 && can_si && c == '#')
1843: {
1844: /* remember current indent for next line */
1845: old_indent = get_indent();
1846: set_indent(0, TRUE);
1847: }
1848:
1849: if (c == ' ')
1850: {
1851: #ifdef CINDENT
1852: if (inindent(0))
1853: can_cindent = FALSE;
1854: #endif
1855: if (Insstart_blank_vcol == MAXCOL &&
1856: curwin->w_cursor.lnum == Insstart.lnum)
1857: Insstart_blank_vcol = curwin->w_virtcol;
1858: }
1859:
1860: if (iswordchar(c) || !echeck_abbr(c))
1861: {
1862: insert_special(c, FALSE);
1863: need_redraw = TRUE;
1864: #ifdef RIGHTLEFT
1865: revinslegal++;
1866: revinschars++;
1867: #endif
1868: }
1869: break;
1870: } /* end of switch (c) */
1871:
1872: #ifdef CINDENT
1873: if (curbuf->b_p_cin && can_cindent
1874: # ifdef INSERT_EXPAND
1875: && !ctrl_x_mode
1876: # endif
1877: )
1878: {
1879: force_cindent:
1880: /*
1881: * Indent now if a key was typed that is in 'cinkeys'.
1882: */
1883: if (in_cinkeys(c, ' ', line_is_white))
1884: {
1885: stop_arrow();
1886:
1887: /* re-indent the current line */
1888: fixthisline(get_c_indent);
1889:
1890: /* draw the changes on the screen later */
1891: need_redraw = TRUE;
1892: }
1893: }
1894: #endif /* CINDENT */
1895:
1896: } /* for (;;) */
1897: }
1898:
1899: /*
1900: * Insert an indent (for <Tab> or CTRL-T) or delete an indent (for CTRL-D).
1901: * Keep the cursor on the same character.
1902: * type == INDENT_INC increase indent (for CTRL-T or <Tab>)
1903: * type == INDENT_DEC decrease indent (for CTRL-D)
1904: * type == INDENT_SET set indent to "amount"
1905: * if round is TRUE, round the indent to 'shiftwidth' (only with _INC and _Dec).
1906: */
1907: static void
1908: change_indent(type, amount, round)
1909: int type;
1910: int amount;
1911: int round;
1912: {
1913: int vcol;
1914: int last_vcol;
1915: int insstart_less; /* reduction for Insstart.col */
1916: int new_cursor_col;
1917: int i;
1918: char_u *ptr;
1919: int save_p_list;
1920:
1921: /* for the following tricks we don't want list mode */
1922: save_p_list = curwin->w_p_list;
1923: if (save_p_list)
1924: {
1925: curwin->w_p_list = FALSE;
1926: curs_columns(FALSE); /* recompute w_virtcol */
1927: }
1928: vcol = curwin->w_virtcol;
1929:
1930: /* determine offset from first non-blank */
1931: new_cursor_col = curwin->w_cursor.col;
1932: beginline(TRUE);
1933: new_cursor_col -= curwin->w_cursor.col;
1934:
1935: insstart_less = curwin->w_cursor.col;
1936:
1937: /*
1938: * If the cursor is in the indent, compute how many screen columns the
1939: * cursor is to the left of the first non-blank.
1940: */
1941: if (new_cursor_col < 0)
1942: vcol = get_indent() - vcol;
1943:
1944: /*
1945: * Set the new indent. The cursor will be put on the first non-blank.
1946: */
1947: if (type == INDENT_SET)
1948: set_indent(amount, TRUE);
1949: else
1950: shift_line(type == INDENT_DEC, round, 1);
1951: insstart_less -= curwin->w_cursor.col;
1952:
1953: /*
1954: * Try to put cursor on same character.
1955: * If the cursor is at or after the first non-blank in the line,
1956: * compute the cursor column relative to the column of the first
1957: * non-blank character.
1958: * If we are not in insert mode, leave the cursor on the first non-blank.
1959: * If the cursor is before the first non-blank, position it relative
1960: * to the first non-blank, counted in screen columns.
1961: */
1962: if (new_cursor_col >= 0)
1963: new_cursor_col += curwin->w_cursor.col;
1964: else if (!(State & INSERT))
1965: new_cursor_col = curwin->w_cursor.col;
1966: else
1967: {
1968: /*
1969: * Compute the screen column where the cursor should be.
1970: */
1971: vcol = get_indent() - vcol;
1972: curwin->w_virtcol = (vcol < 0) ? 0 : vcol;
1973:
1974: /*
1975: * Advance the cursor until we reach the right screen column.
1976: */
1977: vcol = last_vcol = 0;
1978: new_cursor_col = -1;
1979: ptr = ml_get_curline();
1980: while (vcol <= (int)curwin->w_virtcol)
1981: {
1982: last_vcol = vcol;
1983: ++new_cursor_col;
1984: vcol += lbr_chartabsize(ptr + new_cursor_col, (colnr_t)vcol);
1985: }
1986: vcol = last_vcol;
1987:
1988: /*
1989: * May need to insert spaces to be able to position the cursor on
1990: * the right screen column.
1991: */
1992: if (vcol != (int)curwin->w_virtcol)
1993: {
1994: curwin->w_cursor.col = new_cursor_col;
1995: i = (int)curwin->w_virtcol - vcol;
1996: ptr = alloc(i + 1);
1997: if (ptr != NULL)
1998: {
1999: new_cursor_col += i;
2000: ptr[i] = NUL;
2001: while (--i >= 0)
2002: ptr[i] = ' ';
2003: ins_str(ptr);
2004: vim_free(ptr);
2005: }
2006: }
2007:
2008: /*
2009: * When changing the indent while the cursor is in it, reset
2010: * Insstart_col to 0.
2011: */
2012: insstart_less = Insstart.col;
2013: }
2014:
2015: curwin->w_p_list = save_p_list;
2016:
2017: if (new_cursor_col <= 0)
2018: curwin->w_cursor.col = 0;
2019: else
2020: curwin->w_cursor.col = new_cursor_col;
2021: curwin->w_set_curswant = TRUE;
2022:
2023: /*
2024: * May have to adjust the start of the insert.
2025: */
2026: if ((State & INSERT) && curwin->w_cursor.lnum == Insstart.lnum &&
2027: Insstart.col != 0)
2028: {
2029: if ((int)Insstart.col <= insstart_less)
2030: Insstart.col = 0;
2031: else
2032: Insstart.col -= insstart_less;
2033: }
2034: }
2035:
2036: #ifdef INSERT_EXPAND
2037: /*
2038: * Is the character 'c' a valid key to keep us in the current ctrl-x mode?
2039: * -- webb
2040: */
2041: int
2042: is_ctrl_x_key(c)
2043: int c;
2044: {
2045: switch (ctrl_x_mode)
2046: {
2047: case 0: /* Not in any ctrl-x mode */
2048: break;
2049: case CTRL_X_NOT_DEFINED_YET:
2050: if (c == Ctrl('X') || c == Ctrl('Y') || c == Ctrl('E') ||
2051: c == Ctrl('L') || c == Ctrl('F') || c == Ctrl(']') ||
2052: c == Ctrl('I') || c == Ctrl('D') || c == Ctrl('P') ||
2053: c == Ctrl('N'))
2054: return TRUE;
2055: break;
2056: case CTRL_X_SCROLL:
2057: if (c == Ctrl('Y') || c == Ctrl('E'))
2058: return TRUE;
2059: break;
2060: case CTRL_X_WHOLE_LINE:
2061: if (c == Ctrl('L') || c == Ctrl('P') || c == Ctrl('N'))
2062: return TRUE;
2063: break;
2064: case CTRL_X_FILES:
2065: if (c == Ctrl('F') || c == Ctrl('P') || c == Ctrl('N'))
2066: return TRUE;
2067: break;
2068: case CTRL_X_DICTIONARY:
2069: if (c == Ctrl('K') || c == Ctrl('P') || c == Ctrl('N'))
2070: return TRUE;
2071: break;
2072: case CTRL_X_TAGS:
2073: if (c == Ctrl(']') || c == Ctrl('P') || c == Ctrl('N'))
2074: return TRUE;
2075: break;
2076: case CTRL_X_PATH_PATTERNS:
2077: if (c == Ctrl('P') || c == Ctrl('N'))
2078: return TRUE;
2079: break;
2080: case CTRL_X_PATH_DEFINES:
2081: if (c == Ctrl('D') || c == Ctrl('P') || c == Ctrl('N'))
2082: return TRUE;
2083: break;
2084: default:
2085: emsg(e_internal);
2086: break;
2087: }
2088: return FALSE;
2089: }
2090:
2091: /*
2092: * This is like add_completion(), but if ic and inf are set, then the
2093: * case of the originally typed text is used, and the case of the completed
2094: * text is infered, ie this tries to work out what case you probably wanted
2095: * the rest of the word to be in -- webb
2096: */
2097: int
2098: add_completion_and_infercase(str, len, fname, dir)
2099: char_u *str;
2100: int len;
2101: char_u *fname;
2102: int dir;
2103: {
2104: int has_lower = FALSE;
2105: int was_letter = FALSE;
2106: int orig_len;
2107: int idx;
2108:
2109: if (p_ic && curbuf->b_p_inf && len < IOSIZE)
2110: {
2111: /* Infer case of completed part -- webb */
2112: orig_len = STRLEN(original_text);
2113:
2114: /* Use IObuff, str would change text in buffer! */
2115: STRNCPY(IObuff, str, len);
2116: IObuff[len] = NUL;
2117:
2118: /* Rule 1: Were any chars converted to lower? */
2119: for (idx = 0; idx < orig_len; ++idx)
2120: {
2121: if (islower(original_text[idx]))
2122: {
2123: has_lower = TRUE;
2124: if (isupper(IObuff[idx]))
2125: {
2126: /* Rule 1 is satisfied */
2127: for (idx = orig_len; idx < len; ++idx)
2128: IObuff[idx] = TO_LOWER(IObuff[idx]);
2129: break;
2130: }
2131: }
2132: }
2133:
2134: /*
2135: * Rule 2: No lower case, 2nd consecutive letter converted to
2136: * upper case.
2137: */
2138: if (!has_lower)
2139: {
2140: for (idx = 0; idx < orig_len; ++idx)
2141: {
2142: if (was_letter && isupper(original_text[idx]) &&
2143: islower(IObuff[idx]))
2144: {
2145: /* Rule 2 is satisfied */
2146: for (idx = orig_len; idx < len; ++idx)
2147: IObuff[idx] = TO_UPPER(IObuff[idx]);
2148: break;
2149: }
2150: was_letter = isalpha(original_text[idx]);
2151: }
2152: }
2153:
2154: /* Copy the original case of the part we typed */
2155: STRNCPY(IObuff, original_text, orig_len);
2156:
2157: return add_completion(IObuff, len, fname, dir);
2158: }
2159: return add_completion(str, len, fname, dir);
2160: }
2161:
2162: /*
2163: * Add a match to the list of matches.
2164: * If the given string is already in the list of completions, then return
2165: * FAIL, otherwise add it to the list and return OK. If there is an error,
2166: * maybe because alloc returns NULL, then RET_ERROR is returned -- webb.
2167: */
2168: static int
2169: add_completion(str, len, fname, dir)
2170: char_u *str;
2171: int len;
2172: char_u *fname;
2173: int dir;
2174: {
2175: struct Completion *match;
2176:
2177: mch_breakcheck();
2178: if (got_int)
2179: return RET_ERROR;
2180: if (len < 0)
2181: len = STRLEN(str);
2182:
2183: /*
2184: * If the same match is already present, don't add it.
2185: */
2186: if (first_match != NULL)
2187: {
2188: match = first_match;
2189: do
2190: {
2191: if (STRNCMP(match->str, str, (size_t)len) == 0 &&
2192: match->str[len] == NUL)
2193: return FAIL;
2194: match = match->next;
2195: } while (match != NULL && match != first_match);
2196: }
2197:
2198: /*
2199: * Allocate a new match structure.
2200: * Copy the values to the new match structure.
2201: */
2202: match = (struct Completion *)alloc((unsigned)sizeof(struct Completion));
2203: if (match == NULL)
2204: return RET_ERROR;
2205: if ((match->str = strnsave(str, len)) == NULL)
2206: {
2207: vim_free(match);
2208: return RET_ERROR;
2209: }
2210: if (fname != NULL)
2211: match->fname = strsave(fname); /* ignore errors */
2212: else
2213: match->fname = NULL;
2214: match->original = FALSE;
2215:
2216: /*
2217: * Link the new match structure in the list of matches.
2218: */
2219: if (first_match == NULL)
2220: {
2221: first_match = curr_match = match;
2222: curr_match->next = curr_match->prev = NULL;
2223: }
2224: else
2225: {
2226: if (dir == FORWARD)
2227: {
2228: match->next = NULL;
2229: match->prev = curr_match;
2230: curr_match->next = match;
2231: curr_match = match;
2232: }
2233: else /* BACKWARD */
2234: {
2235: match->prev = NULL;
2236: match->next = curr_match;
2237: curr_match->prev = match;
2238: first_match = curr_match = match;
2239: }
2240: }
2241:
2242: return OK;
2243: }
2244:
2245: /*
2246: * Make the completion list cyclic. Add the original text at the end.
2247: * Return the number of matches (excluding the original).
2248: */
2249: static int
2250: make_cyclic()
2251: {
2252: struct Completion *match, *orig;
2253: int count = 0;
2254:
2255: if (first_match != NULL)
2256: {
2257: /*
2258: * Find the end of the list.
2259: */
2260: match = first_match;
2261: count = 1;
2262: while (match->next != NULL)
2263: {
2264: match = match->next;
2265: ++count;
2266: }
2267:
2268: if (original_text != NULL)
2269: {
2270: /*
2271: * Allocate a new structure for the original text.
2272: * Copy the original text to the new structure.
2273: * Link it in the list at the end.
2274: */
2275: orig = (struct Completion *)alloc((unsigned)sizeof(
2276: struct Completion));
2277: if (orig != NULL)
2278: {
2279: if ((orig->str = strsave(original_text)) == NULL)
2280: vim_free(orig);
2281: else
2282: {
2283: orig->fname = NULL;
2284: orig->original = TRUE;
2285: orig->prev = match;
2286: match->next = orig;
2287: match = orig;
2288: curr_match = orig;
2289: }
2290: }
2291: }
2292: match->next = first_match;
2293: first_match->prev = match;
2294: }
2295: return count;
2296: }
2297:
2298: /*
2299: * Add any identifiers that match the given pattern to the list of
2300: * completions.
2301: */
2302: static void
2303: complete_dictionaries(pat, dir)
2304: char_u *pat;
2305: int dir;
2306: {
2307: struct Completion *save_curr_match = curr_match;
2308: char_u *dict = p_dict;
2309: char_u *ptr;
2310: char_u *buf;
2311: char_u *fname;
2312: int at_start;
2313: FILE *fp;
2314: struct regexp *prog = NULL;
2315:
2316: if ((buf = alloc(LSIZE)) == NULL)
2317: return;
2318: if (curr_match != NULL)
2319: {
2320: while (curr_match->next != NULL)
2321: curr_match = curr_match->next;
2322: }
2323: if (*dict != NUL)
2324: {
2325: (void)set_highlight('r');
2326: msg_highlight = TRUE;
2327: MSG("Please wait, searching dictionaries");
2328: set_reg_ic(pat); /* set reg_ic according to p_ic, p_scs and pat */
2329: reg_magic = p_magic;
2330: prog = vim_regcomp(pat);
2331: }
2332: while (*dict != NUL && prog != NULL && !got_int)
2333: {
2334: /* copy one dictionary file name into buf */
2335: (void)copy_option_part(&dict, buf, LSIZE, ",");
2336:
2337: fp = fopen((char *)buf, "r"); /* open dictionary file */
2338:
2339: if (fp != NULL)
2340: {
2341: fname = strsave(buf); /* keep name of file */
2342: /*
2343: * Read dictionary file line by line.
2344: * Check each line for a match.
2345: */
2346: while (!got_int && !vim_fgets(buf, LSIZE, fp))
2347: {
2348: ptr = buf;
2349: at_start = TRUE;
2350: while (vim_regexec(prog, ptr, at_start))
2351: {
2352: at_start = FALSE;
2353: ptr = prog->startp[0];
2354: while (iswordchar(*ptr))
2355: ++ptr;
2356: if (add_completion_and_infercase(prog->startp[0],
2357: (int)(ptr - prog->startp[0]), fname, FORWARD)
2358: == RET_ERROR)
2359: break;
2360: }
2361: line_breakcheck();
2362: }
2363: fclose(fp);
2364: vim_free(fname);
2365: }
2366: }
2367: vim_free(prog);
2368: if (save_curr_match != NULL)
2369: curr_match = save_curr_match;
2370: else if (dir == BACKWARD)
2371: curr_match = first_match;
2372: vim_free(buf);
2373: }
2374:
2375: /*
2376: * Free the list of completions
2377: */
2378: static void
2379: free_completions()
2380: {
2381: struct Completion *match;
2382:
2383: if (first_match == NULL)
2384: return;
2385: curr_match = first_match;
2386: do
2387: {
2388: match = curr_match;
2389: curr_match = curr_match->next;
2390: vim_free(match->str);
2391: vim_free(match->fname);
2392: vim_free(match);
2393: } while (curr_match != NULL && curr_match != first_match);
2394: first_match = curr_match = NULL;
2395: }
2396:
2397: /*
2398: * Return the number of items in the Completion list
2399: */
2400: static int
2401: count_completions()
2402: {
2403: struct Completion *match;
2404: int num = 0;
2405:
2406: if (first_match == NULL)
2407: return 0;
2408: match = first_match;
2409: do
2410: {
2411: if (!match->original) /* original string doesn't count */
2412: num++;
2413: match = match->next;
2414: } while (match != NULL && match != first_match);
2415: return num;
2416: }
2417: #endif /* INSERT_EXPAND */
2418:
2419: /*
2420: * Next character is interpreted literally.
2421: * A one, two or three digit decimal number is interpreted as its byte value.
2422: * If one or two digits are entered, the next character is given to vungetc().
2423: */
2424: int
2425: get_literal()
2426: {
2427: int cc;
2428: int nc;
2429: int i;
2430:
2431: if (got_int)
2432: return Ctrl('C');
2433:
2434: #ifdef USE_GUI
2435: /*
2436: * In GUI there is no point inserting the internal code for a special key.
2437: * It is more useful to insert the string "<KEY>" instead. This would
2438: * probably be useful in a text window too, but it would not be
2439: * vi-compatible (maybe there should be an option for it?) -- webb
2440: */
2441: if (gui.in_use)
2442: ++allow_keys;
2443: #endif
2444: ++no_mapping; /* don't map the next key hits */
2445: cc = 0;
2446: for (i = 0; i < 3; ++i)
2447: {
2448: do
2449: nc = vgetc();
2450: while (nc == K_IGNORE || nc == K_SCROLLBAR || nc == K_HORIZ_SCROLLBAR);
2451: if (!(State & CMDLINE))
2452: add_to_showcmd(nc, FALSE);
2453: if (IS_SPECIAL(nc) || !isdigit(nc))
2454: break;
2455: cc = cc * 10 + nc - '0';
2456: if (cc > 255)
2457: cc = 255; /* limit range to 0-255 */
2458: nc = 0;
2459: }
2460: if (i == 0) /* no number entered */
2461: {
2462: if (nc == K_ZERO) /* NUL is stored as NL */
2463: {
2464: cc = '\n';
2465: nc = 0;
2466: }
2467: else
2468: {
2469: cc = nc;
2470: nc = 0;
2471: }
2472: }
2473:
2474: if (cc == 0) /* NUL is stored as NL */
2475: cc = '\n';
2476:
2477: --no_mapping;
2478: #ifdef USE_GUI
2479: if (gui.in_use)
2480: --allow_keys;
2481: #endif
2482: if (nc)
2483: vungetc(nc);
2484: got_int = FALSE; /* CTRL-C typed after CTRL-V is not an interrupt */
2485: return cc;
2486: }
2487:
2488: /*
2489: * Insert character, taking care of special keys and mod_mask
2490: */
2491: static void
2492: insert_special(c, allow_modmask)
2493: int c;
2494: int allow_modmask;
2495: {
2496: char_u *p;
2497: int len;
2498:
2499: /*
2500: * Special function key, translate into "<Key>". Up to the last '>' is
2501: * inserted with ins_str(), so as not to replace characters in replace
2502: * mode.
2503: * Only use mod_mask for special keys, to avoid things like <S-Space>,
2504: * unless 'allow_modmask' is TRUE.
2505: */
2506: if (IS_SPECIAL(c) || (mod_mask && allow_modmask))
2507: {
2508: p = get_special_key_name(c, mod_mask);
2509: len = STRLEN(p);
2510: c = p[len - 1];
2511: if (len > 2)
2512: {
2513: p[len - 1] = NUL;
2514: ins_str(p);
2515: AppendToRedobuff(p);
2516: }
2517: }
2518: insertchar(c, FALSE, -1);
2519: }
2520:
2521: /*
2522: * Special characters in this context are those that need processing other
2523: * than the simple insertion that can be performed here. This includes ESC
2524: * which terminates the insert, and CR/NL which need special processing to
2525: * open up a new line. This routine tries to optimize insertions performed by
2526: * the "redo", "undo" or "put" commands, so it needs to know when it should
2527: * stop and defer processing to the "normal" mechanism.
2528: */
2529: #define ISSPECIAL(c) ((c) < ' ' || (c) >= DEL)
2530:
2531: void
2532: insertchar(c, force_formatting, second_indent)
2533: unsigned c;
2534: int force_formatting; /* format line regardless of p_fo */
2535: int second_indent; /* indent for second line if >= 0 */
2536: {
2537: int haveto_redraw = FALSE;
2538: int textwidth;
2539: colnr_t leader_len;
2540: int first_line = TRUE;
2541: int fo_ins_blank;
2542: int save_char = NUL;
2543:
2544: stop_arrow();
2545:
2546: /*
2547: * find out textwidth to be used:
2548: * if 'textwidth' option is set, use it
2549: * else if 'wrapmargin' option is set, use Columns - 'wrapmargin'
2550: * if invalid value, use 0.
2551: * Set default to window width (maximum 79) for "Q" command.
2552: */
2553: textwidth = curbuf->b_p_tw;
2554: if (textwidth == 0 && curbuf->b_p_wm)
2555: textwidth = Columns - curbuf->b_p_wm;
2556: if (textwidth < 0)
2557: textwidth = 0;
2558: if (force_formatting && textwidth == 0)
2559: {
2560: textwidth = Columns - 1;
2561: if (textwidth > 79)
2562: textwidth = 79;
2563: }
2564:
2565: fo_ins_blank = has_format_option(FO_INS_BLANK);
2566:
2567: /*
2568: * Try to break the line in two or more pieces when:
2569: * - Always do this if we have been called to do formatting only.
2570: * - Otherwise:
2571: * - Don't do this if inserting a blank
2572: * - Don't do this if an existing character is being replaced.
2573: * - Do this if the cursor is not on the line where insert started
2574: * or - 'formatoptions' doesn't have 'l' or the line was not too long
2575: * before the insert.
2576: * - 'formatoptions' doesn't have 'b' or a blank was inserted at or
2577: * before 'textwidth'
2578: */
2579: if (force_formatting || (!vim_iswhite(c) &&
2580: !(State == REPLACE && *ml_get_cursor() != NUL) &&
2581: (curwin->w_cursor.lnum != Insstart.lnum ||
2582: ((!has_format_option(FO_INS_LONG) ||
2583: Insstart_textlen <= (colnr_t)textwidth) &&
2584: (!fo_ins_blank || Insstart_blank_vcol <= (colnr_t)textwidth)))))
2585: {
2586: /*
2587: * When 'ai' is off we don't want a space under the cursor to be
2588: * deleted. Replace it with an 'x' temporarily.
2589: */
2590: if (!curbuf->b_p_ai && vim_iswhite(gchar_cursor()))
2591: {
2592: save_char = gchar_cursor();
2593: pchar_cursor('x');
2594: }
2595: while (textwidth && curwin->w_virtcol >= (colnr_t)textwidth)
2596: {
2597: int startcol; /* Cursor column at entry */
2598: int wantcol; /* column at textwidth border */
2599: int foundcol; /* column for start of spaces */
2600: int end_foundcol = 0; /* column for start of word */
2601: colnr_t len;
2602:
2603: if (!force_formatting && has_format_option(FO_WRAP_COMS))
2604: fo_do_comments = TRUE;
2605:
2606: /* Don't break until after the comment leader */
2607: leader_len = get_leader_len(ml_get_curline(), NULL);
2608: if (!force_formatting && leader_len == 0 &&
2609: !has_format_option(FO_WRAP))
2610:
2611: {
2612: textwidth = 0;
2613: break;
2614: }
2615: if ((startcol = curwin->w_cursor.col) == 0)
2616: break;
2617: /* find column of textwidth border */
2618: coladvance((colnr_t)textwidth);
2619: wantcol = curwin->w_cursor.col;
2620:
2621: curwin->w_cursor.col = startcol - 1;
2622: foundcol = 0;
2623: /*
2624: * Find position to break at.
2625: * Stop at start of line.
2626: * Stop at first entered white when 'formatoptions' has 'v'
2627: */
2628: while (curwin->w_cursor.col > 0 &&
2629: ((!fo_ins_blank && !has_format_option(FO_INS_VI)) ||
2630: curwin->w_cursor.lnum != Insstart.lnum ||
2631: curwin->w_cursor.col >= Insstart.col))
2632: {
2633: if (vim_iswhite(gchar_cursor()))
2634: {
2635: /* remember position of blank just before text */
2636: end_foundcol = curwin->w_cursor.col;
2637: while (curwin->w_cursor.col > 0 &&
2638: vim_iswhite(gchar_cursor()))
2639: --curwin->w_cursor.col;
2640: if (curwin->w_cursor.col == 0 &&
2641: vim_iswhite(gchar_cursor()))
2642: break; /* only spaces in front of text */
2643: /* Don't break until after the comment leader */
2644: if (curwin->w_cursor.col < leader_len)
2645: break;
2646: foundcol = curwin->w_cursor.col + 1;
2647: if (curwin->w_cursor.col < (colnr_t)wantcol)
2648: break;
2649: }
2650: --curwin->w_cursor.col;
2651: }
2652:
2653: if (foundcol == 0) /* no spaces, cannot break line */
2654: {
2655: curwin->w_cursor.col = startcol;
2656: break;
2657: }
2658:
2659: /*
2660: * offset between cursor position and line break is used by
2661: * replace stack functions
2662: */
2663: replace_offset = startcol - end_foundcol - 1;
2664:
2665: /*
2666: * adjust startcol for spaces that will be deleted and
2667: * characters that will remain on top line
2668: */
2669: curwin->w_cursor.col = foundcol;
2670: while (vim_iswhite(gchar_cursor()))
2671: {
2672: ++curwin->w_cursor.col;
2673: --startcol;
2674: }
2675: startcol -= foundcol;
2676: if (startcol < 0)
2677: startcol = 0;
2678:
2679: /* put cursor after pos. to break line */
2680: curwin->w_cursor.col = foundcol;
2681:
2682: Opencmd(FORWARD, FALSE, TRUE);
2683:
2684: replace_offset = 0;
2685: if (second_indent >= 0 && first_line)
2686: set_indent(second_indent, TRUE);
2687: first_line = FALSE;
2688:
2689: /*
2690: * check if cursor is not past the NUL off the line, cindent may
2691: * have added or removed indent.
2692: */
2693: curwin->w_cursor.col += startcol;
2694: len = STRLEN(ml_get_curline());
2695: if (curwin->w_cursor.col > len)
2696: curwin->w_cursor.col = len;
2697:
2698: curs_columns(FALSE); /* update curwin->w_virtcol */
2699: haveto_redraw = TRUE;
2700: #ifdef CINDENT
2701: can_cindent = TRUE;
2702: #endif
2703: }
2704:
2705: if (save_char) /* put back space after cursor */
2706: pchar_cursor(save_char);
2707:
2708: if (c == NUL) /* formatting only */
2709: return;
2710: fo_do_comments = FALSE;
2711: if (haveto_redraw)
2712: {
2713: /*
2714: * If the cursor ended up just below the screen we scroll up here
2715: * to avoid a redraw of the whole screen in the most common cases.
2716: */
2717: if (curwin->w_cursor.lnum == curwin->w_botline &&
2718: !curwin->w_empty_rows)
2719: win_del_lines(curwin, 0, 1, TRUE, TRUE);
2720: updateScreen(CURSUPD);
2721: }
2722: }
2723: if (c == NUL) /* only formatting was wanted */
2724: return;
2725:
2726: did_ai = FALSE;
2727: did_si = FALSE;
2728: can_si = FALSE;
2729: can_si_back = FALSE;
2730:
2731: /*
2732: * If there's any pending input, grab up to INPUT_BUFLEN at once.
2733: * This speeds up normal text input considerably.
2734: */
2735: #define INPUT_BUFLEN 100
2736: if (!ISSPECIAL(c) && vpeekc() != NUL && State != REPLACE
2737: #ifdef RIGHTLEFT
2738: && !p_ri
2739: #endif
2740: )
2741: {
2742: char_u p[INPUT_BUFLEN + 1];
2743: int i;
2744:
2745: p[0] = c;
2746: i = 1;
2747: while ((c = vpeekc()) != NUL && !ISSPECIAL(c) && i < INPUT_BUFLEN &&
2748: (textwidth == 0 ||
2749: (curwin->w_virtcol += charsize(p[i - 1])) < (colnr_t)textwidth) &&
2750: !(!no_abbr && !iswordchar(c) && iswordchar(p[i - 1])))
2751: {
2752: #ifdef RIGHTLEFT
2753: c = vgetc();
2754: if (p_hkmap && KeyTyped)
2755: c = hkmap(c); /* Hebrew mode mapping */
2756: p[i++] = c;
2757: #else
2758: p[i++] = vgetc();
2759: #endif
2760: }
2761:
2762: #ifdef DIGRAPHS
2763: do_digraph(-1); /* clear digraphs */
2764: do_digraph(p[i-1]); /* may be the start of a digraph */
2765: #endif
2766: p[i] = '\0';
2767: ins_str(p);
2768: AppendToRedobuff(p);
2769: }
2770: else
2771: {
2772: ins_char(c);
2773: AppendCharToRedobuff(c);
2774: }
2775: }
2776:
2777: /*
2778: * start_arrow() is called when an arrow key is used in insert mode.
2779: * It resembles hitting the <ESC> key.
2780: */
2781: static void
2782: start_arrow(end_insert_pos)
2783: FPOS *end_insert_pos;
2784: {
2785: if (!arrow_used) /* something has been inserted */
2786: {
2787: AppendToRedobuff(ESC_STR);
2788: arrow_used = TRUE; /* this means we stopped the current insert */
2789: stop_insert(end_insert_pos);
2790: }
2791: }
2792:
2793: /*
2794: * stop_arrow() is called before a change is made in insert mode.
2795: * If an arrow key has been used, start a new insertion.
2796: */
2797: static void
2798: stop_arrow()
2799: {
2800: if (arrow_used)
2801: {
2802: (void)u_save_cursor(); /* errors are ignored! */
2803: Insstart = curwin->w_cursor; /* new insertion starts here */
2804: Insstart_textlen = linetabsize(ml_get_curline());
2805: ResetRedobuff();
2806: AppendToRedobuff((char_u *)"1i"); /* pretend we start an insertion */
2807: arrow_used = FALSE;
2808: }
2809: }
2810:
2811: /*
2812: * do a few things to stop inserting
2813: */
2814: static void
2815: stop_insert(end_insert_pos)
2816: FPOS *end_insert_pos; /* where insert ended */
2817: {
2818: stop_redo_ins();
2819:
2820: /*
2821: * save the inserted text for later redo with ^@
2822: */
2823: vim_free(last_insert);
2824: last_insert = get_inserted();
2825: last_insert_skip = new_insert_skip;
2826:
2827: /*
2828: * If we just did an auto-indent, remove the white space from the end of
2829: * the line, and put the cursor back.
2830: */
2831: if (did_ai && !arrow_used)
2832: {
2833: if (gchar_cursor() == NUL && curwin->w_cursor.col > 0)
2834: --curwin->w_cursor.col;
2835: while (vim_iswhite(gchar_cursor()))
2836: delchar(TRUE);
2837: if (gchar_cursor() != NUL)
2838: ++curwin->w_cursor.col; /* put cursor back on the NUL */
2839: if (curwin->w_p_list) /* the deletion is only seen in list
2840: * mode */
2841: updateline();
2842: }
2843: did_ai = FALSE;
2844: did_si = FALSE;
2845: can_si = FALSE;
2846: can_si_back = FALSE;
2847:
2848: /* set '[ and '] to the inserted text */
2849: curbuf->b_op_start = Insstart;
2850: curbuf->b_op_end = *end_insert_pos;
2851: }
2852:
2853: /*
2854: * Set the last inserted text to a single character.
2855: * Used for the replace command.
2856: */
2857: void
2858: set_last_insert(c)
2859: int c;
2860: {
2861: vim_free(last_insert);
2862: last_insert = alloc(4);
2863: if (last_insert != NULL)
2864: {
2865: last_insert[0] = Ctrl('V');
2866: last_insert[1] = c;
2867: last_insert[2] = ESC;
2868: last_insert[3] = NUL;
2869: /* Use the CTRL-V only when not entering a digit */
2870: last_insert_skip = isdigit(c) ? 1 : 0;
2871: }
2872: }
2873:
2874: /*
2875: * move cursor to start of line
2876: * if flag == TRUE move to first non-white
2877: * if flag == MAYBE then move to first non-white if startofline is set,
2878: * otherwise don't move at all.
2879: */
2880: void
2881: beginline(flag)
2882: int flag;
2883: {
2884: if (flag == MAYBE && !p_sol)
2885: coladvance(curwin->w_curswant);
2886: else
2887: {
2888: curwin->w_cursor.col = 0;
2889: if (flag)
2890: {
2891: register char_u *ptr;
2892:
2893: for (ptr = ml_get_curline(); vim_iswhite(*ptr); ++ptr)
2894: ++curwin->w_cursor.col;
2895: }
2896: curwin->w_set_curswant = TRUE;
2897: }
2898: }
2899:
2900: /*
2901: * oneright oneleft cursor_down cursor_up
2902: *
2903: * Move one char {right,left,down,up}.
2904: * Return OK when sucessful, FAIL when we hit a line of file boundary.
2905: */
2906:
2907: int
2908: oneright()
2909: {
2910: char_u *ptr;
2911:
2912: ptr = ml_get_cursor();
2913: if (*ptr++ == NUL || *ptr == NUL)
2914: return FAIL;
2915: curwin->w_set_curswant = TRUE;
2916: ++curwin->w_cursor.col;
2917: return OK;
2918: }
2919:
2920: int
2921: oneleft()
2922: {
2923: if (curwin->w_cursor.col == 0)
2924: return FAIL;
2925: curwin->w_set_curswant = TRUE;
2926: --curwin->w_cursor.col;
2927: return OK;
2928: }
2929:
2930: int
2931: cursor_up(n)
2932: long n;
2933: {
2934: if (n != 0 && curwin->w_cursor.lnum == 1)
2935: return FAIL;
2936: if (n >= curwin->w_cursor.lnum)
2937: curwin->w_cursor.lnum = 1;
2938: else
2939: curwin->w_cursor.lnum -= n;
2940:
2941: /* try to advance to the column we want to be at */
2942: coladvance(curwin->w_curswant);
2943:
2944: if (op_type == NOP)
2945: cursupdate(); /* make sure curwin->w_topline is valid */
2946:
2947: return OK;
2948: }
2949:
2950: int
2951: cursor_down(n)
2952: long n;
2953: {
2954: if (n != 0 && curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
2955: return FAIL;
2956: curwin->w_cursor.lnum += n;
2957: if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
2958: curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
2959:
2960: /* try to advance to the column we want to be at */
2961: coladvance(curwin->w_curswant);
2962:
2963: if (op_type == NOP)
2964: cursupdate(); /* make sure curwin->w_topline is valid */
2965:
2966: return OK;
2967: }
2968:
2969: /*
2970: * screengo() --
2971: *
2972: * move 'dist' lines in direction 'dir', counting lines by *screen*
2973: * lines rather than lines in the file
2974: * 'dist' must be positive.
2975: *
2976: * return OK if able to move cursor, FAIL otherwise.
2977: */
2978:
2979: int
2980: screengo(dir, dist)
2981: int dir;
2982: long dist;
2983: {
2984: int linelen = linetabsize(ml_get_curline());
2985: int retval = OK;
2986: int atend = FALSE;
2987: int n;
2988:
2989: op_motion_type = MCHAR;
2990: op_inclusive = FALSE;
2991:
2992: /*
2993: * Instead of sticking at the last character of the line in the file we
2994: * try to stick in the last column of the screen
2995: */
2996: if (curwin->w_curswant == MAXCOL)
2997: {
2998: atend = TRUE;
2999: curwin->w_curswant = ((curwin->w_virtcol +
3000: (curwin->w_p_nu ? 8 : 0)) / Columns + 1) * Columns - 1;
3001: if (curwin->w_p_nu && curwin->w_curswant > 8)
3002: curwin->w_curswant -= 8;
3003: }
3004: else
3005: while (curwin->w_curswant >= (colnr_t)(linelen + Columns))
3006: curwin->w_curswant -= Columns;
3007:
3008: while (dist--)
3009: {
3010: if (dir == BACKWARD)
3011: {
3012: /* move back within line */
3013: if ((long)curwin->w_curswant >= Columns)
3014: curwin->w_curswant -= Columns;
3015: else /* to previous line */
3016: {
3017: if (curwin->w_cursor.lnum == 1)
3018: {
3019: retval = FAIL;
3020: break;
3021: }
3022: --curwin->w_cursor.lnum;
3023: linelen = linetabsize(ml_get_curline());
3024: n = ((linelen + (curwin->w_p_nu ? 8 : 0) - 1) / Columns)
3025: * Columns;
3026: if (curwin->w_p_nu &&
3027: (long)curwin->w_curswant >= Columns - 8 && n)
3028: n -= Columns;
3029: curwin->w_curswant += n;
3030: }
3031: }
3032: else /* dir == FORWARD */
3033: {
3034: n = ((linelen + (curwin->w_p_nu ? 8 : 0) - 1) / Columns) * Columns;
3035: if (curwin->w_p_nu && n > 8)
3036: n -= 8;
3037: /* move forward within line */
3038: if (curwin->w_curswant < (colnr_t)n)
3039: curwin->w_curswant += Columns;
3040: else /* to next line */
3041: {
3042: if (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
3043: {
3044: retval = FAIL;
3045: break;
3046: }
3047: curwin->w_cursor.lnum++;
3048: linelen = linetabsize(ml_get_curline());
3049: curwin->w_curswant %= Columns;
3050: }
3051: }
3052: }
3053: coladvance(curwin->w_curswant);
3054: if (atend)
3055: curwin->w_curswant = MAXCOL; /* stick in the last column */
3056: if (op_type == NOP)
3057: cursupdate();
3058: return retval;
3059: }
3060:
3061: /*
3062: * move screen 'count' pages up or down and update screen
3063: *
3064: * return FAIL for failure, OK otherwise
3065: */
3066: int
3067: onepage(dir, count)
3068: int dir;
3069: long count;
3070: {
3071: linenr_t lp;
3072: long n;
3073: int off;
3074:
3075: if (curbuf->b_ml.ml_line_count == 1) /* nothing to do */
3076: return FAIL;
3077: for ( ; count > 0; --count)
3078: {
3079: if (dir == FORWARD ? (curwin->w_topline >=
3080: curbuf->b_ml.ml_line_count - 1) : (curwin->w_topline == 1))
3081: {
3082: beep_flush();
3083: return FAIL;
3084: }
3085: if (dir == FORWARD)
3086: {
3087: /* at end of file */
3088: if (curwin->w_botline > curbuf->b_ml.ml_line_count)
3089: curwin->w_topline = curbuf->b_ml.ml_line_count;
3090: /* next line is big */
3091: /* or just three lines on screen */
3092: else
3093: {
3094: if (plines(curwin->w_botline) >= curwin->w_height - 2 ||
3095: curwin->w_botline - curwin->w_topline <= 3)
3096: off = 0;
3097: else
3098: off = 2;
3099: curwin->w_topline = curwin->w_botline - off;
3100: curwin->w_cursor.lnum = curwin->w_topline;
3101: }
3102: comp_Botline(curwin);
3103: }
3104: else /* dir == BACKWARDS */
3105: {
3106: lp = curwin->w_topline;
3107: /*
3108: * If the first two lines on the screen are not too big, we keep
3109: * them on the screen.
3110: */
3111: if ((n = plines(lp)) > curwin->w_height / 2)
3112: --lp;
3113: else if (lp < curbuf->b_ml.ml_line_count &&
3114: n + plines(lp + 1) < curwin->w_height / 2)
3115: ++lp;
3116: curwin->w_cursor.lnum = lp;
3117: n = 0;
3118: while (n <= curwin->w_height && lp >= 1)
3119: {
3120: n += plines(lp);
3121: --lp;
3122: }
3123: if (n <= curwin->w_height) /* at begin of file */
3124: {
3125: curwin->w_topline = 1;
3126: comp_Botline(curwin);
3127: }
3128: else if (lp >= curwin->w_topline - 2) /* very long lines */
3129: {
3130: --curwin->w_topline;
3131: comp_Botline(curwin);
3132: curwin->w_cursor.lnum = curwin->w_botline - 1;
3133: }
3134: else
3135: {
3136: curwin->w_topline = lp + 2;
3137: comp_Botline(curwin);
3138: }
3139: }
3140: }
3141: cursor_correct();
3142: beginline(MAYBE);
3143: updateScreen(VALID);
3144: return OK;
3145: }
3146:
3147: /* #define KEEP_SCREEN_LINE */
3148:
3149: void
3150: halfpage(flag, Prenum)
3151: int flag;
3152: linenr_t Prenum;
3153: {
3154: long scrolled = 0;
3155: int i;
3156: int n;
3157:
3158: if (Prenum)
3159: curwin->w_p_scroll = (Prenum > curwin->w_height) ?
3160: curwin->w_height : Prenum;
3161: n = (curwin->w_p_scroll <= curwin->w_height) ?
3162: curwin->w_p_scroll : curwin->w_height;
3163:
3164: if (flag) /* scroll down */
3165: {
3166: while (n > 0 && curwin->w_botline <= curbuf->b_ml.ml_line_count)
3167: {
3168: i = plines(curwin->w_topline);
3169: n -= i;
3170: if (n < 0 && scrolled)
3171: break;
3172: scrolled += i;
3173: ++curwin->w_topline;
3174: comp_Botline(curwin); /* compute curwin->w_botline */
3175: #ifndef KEEP_SCREEN_LINE
3176: if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
3177: ++curwin->w_cursor.lnum;
3178: #endif
3179: }
3180: #ifndef KEEP_SCREEN_LINE
3181: /*
3182: * When hit bottom of the file: move cursor down.
3183: */
3184: if (n > 0)
3185: {
3186: curwin->w_cursor.lnum += n;
3187: if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
3188: curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
3189: }
3190: #else
3191: /* try to put the cursor in the same screen line */
3192: while ((curwin->w_cursor.lnum < curwin->w_topline || scrolled > 0)
3193: && curwin->w_cursor.lnum < curwin->w_botline - 1)
3194: {
3195: scrolled -= plines(curwin->w_cursor.lnum);
3196: if (scrolled < 0 && curwin->w_cursor.lnum >= curwin->w_topline)
3197: break;
3198: ++curwin->w_cursor.lnum;
3199: }
3200: #endif
3201: }
3202: else /* scroll up */
3203: {
3204: while (n > 0 && curwin->w_topline > 1)
3205: {
3206: i = plines(curwin->w_topline - 1);
3207: n -= i;
3208: if (n < 0 && scrolled)
3209: break;
3210: scrolled += i;
3211: --curwin->w_topline;
3212: #ifndef KEEP_SCREEN_LINE
3213: if (curwin->w_cursor.lnum > 1)
3214: --curwin->w_cursor.lnum;
3215: #endif
3216: }
3217: comp_Botline(curwin); /* compute curwin->w_botline */
3218: #ifndef KEEP_SCREEN_LINE
3219: /*
3220: * When hit top of the file: move cursor up.
3221: */
3222: if (n > 0)
3223: {
3224: if (curwin->w_cursor.lnum > (linenr_t)n)
3225: curwin->w_cursor.lnum -= n;
3226: else
3227: curwin->w_cursor.lnum = 1;
3228: }
3229: #else
3230: /* try to put the cursor in the same screen line */
3231: scrolled += n; /* move cursor when topline is 1 */
3232: while (curwin->w_cursor.lnum > curwin->w_topline &&
3233: (scrolled > 0 || curwin->w_cursor.lnum >= curwin->w_botline))
3234: {
3235: scrolled -= plines(curwin->w_cursor.lnum - 1);
3236: if (scrolled < 0 && curwin->w_cursor.lnum < curwin->w_botline)
3237: break;
3238: --curwin->w_cursor.lnum;
3239: }
3240: #endif
3241: }
3242: cursor_correct();
3243: beginline(MAYBE);
3244: updateScreen(VALID);
3245: }
3246:
3247: /*
3248: * Stuff the last inserted text in the read buffer.
3249: * Last_insert actually is a copy of the redo buffer, so we
3250: * first have to remove the command.
3251: */
3252: int
3253: stuff_inserted(c, count, no_esc)
3254: int c;
3255: long count;
3256: int no_esc;
3257: {
3258: char_u *esc_ptr = NULL;
3259: char_u *ptr;
3260:
3261: ptr = get_last_insert();
3262: if (ptr == NULL)
3263: {
3264: EMSG(e_noinstext);
3265: return FAIL;
3266: }
3267:
3268: if (c)
3269: stuffcharReadbuff(c);
3270: if (no_esc && (esc_ptr = (char_u *)vim_strrchr(ptr, 27)) != NULL)
3271: *esc_ptr = NUL; /* remove the ESC */
3272:
3273: do
3274: stuffReadbuff(ptr);
3275: while (--count > 0);
3276:
3277: if (esc_ptr != NULL)
3278: *esc_ptr = 27; /* put the ESC back */
3279:
3280: return OK;
3281: }
3282:
3283: char_u *
3284: get_last_insert()
3285: {
3286: if (last_insert == NULL)
3287: return NULL;
3288: return last_insert + last_insert_skip;
3289: }
3290:
3291: /*
3292: * Check the word in front of the cursor for an abbreviation.
3293: * Called when the non-id character "c" has been entered.
3294: * When an abbreviation is recognized it is removed from the text and
3295: * the replacement string is inserted in typebuf[], followed by "c".
3296: */
3297: static int
3298: echeck_abbr(c)
3299: int c;
3300: {
3301: if (p_paste || no_abbr) /* no abbreviations or in paste mode */
3302: return FALSE;
3303:
3304: return check_abbr(c, ml_get_curline(), curwin->w_cursor.col,
3305: curwin->w_cursor.lnum == Insstart.lnum ? Insstart.col : 0);
3306: }
3307:
3308: /*
3309: * replace-stack functions
3310: *
3311: * When replacing characters the replaced character is remembered
3312: * for each new character. This is used to re-insert the old text
3313: * when backspacing.
3314: *
3315: * replace_offset is normally 0, in which case replace_push will add a new
3316: * character at the end of the stack. If replace_offset is not 0, that many
3317: * characters will be left on the stack above the newly inserted character.
3318: */
3319:
3320: char_u *replace_stack = NULL;
3321: long replace_stack_nr = 0; /* next entry in replace stack */
3322: long replace_stack_len = 0; /* max. number of entries */
3323:
3324: void
3325: replace_push(c)
3326: int c; /* character that is replaced (NUL is none) */
3327: {
3328: char_u *p;
3329:
3330: if (replace_stack_nr < replace_offset) /* nothing to do */
3331: return;
3332: if (replace_stack_len <= replace_stack_nr)
3333: {
3334: replace_stack_len += 50;
3335: p = lalloc(sizeof(char_u) * replace_stack_len, TRUE);
3336: if (p == NULL) /* out of memory */
3337: {
3338: replace_stack_len -= 50;
3339: return;
3340: }
3341: if (replace_stack != NULL)
3342: {
3343: vim_memmove(p, replace_stack,
3344: (size_t)(replace_stack_nr * sizeof(char_u)));
3345: vim_free(replace_stack);
3346: }
3347: replace_stack = p;
3348: }
3349: p = replace_stack + replace_stack_nr - replace_offset;
3350: if (replace_offset)
3351: vim_memmove(p + 1, p, (size_t)(replace_offset * sizeof(char_u)));
3352: *p = c;
3353: ++replace_stack_nr;
3354: }
3355:
3356: /*
3357: * pop one item from the replace stack
3358: * return -1 if stack empty
3359: * return 0 if no character was replaced
3360: * return replaced character otherwise
3361: */
3362: int
3363: replace_pop()
3364: {
3365: if (replace_stack_nr == 0)
3366: return -1;
3367: return (int)replace_stack[--replace_stack_nr];
3368: }
3369:
3370: /*
3371: * make the replace stack empty
3372: * (called when exiting replace mode)
3373: */
3374: void
3375: replace_flush()
3376: {
3377: vim_free(replace_stack);
3378: replace_stack = NULL;
3379: replace_stack_len = 0;
3380: replace_stack_nr = 0;
3381: }
3382:
3383: #if defined(LISPINDENT) || defined(CINDENT)
3384: /*
3385: * Re-indent the current line, based on the current contents of it and the
3386: * surrounding lines. Fixing the cursor position seems really easy -- I'm very
3387: * confused what all the part that handles Control-T is doing that I'm not.
3388: * "get_the_indent" should be get_c_indent or get_lisp_indent.
3389: */
3390:
3391: void
3392: fixthisline(get_the_indent)
3393: int (*get_the_indent) __ARGS((void));
3394: {
3395: change_indent(INDENT_SET, get_the_indent(), FALSE);
3396: if (linewhite(curwin->w_cursor.lnum))
3397: did_ai = TRUE; /* delete the indent if the line stays empty */
3398: }
3399: #endif /* defined(LISPINDENT) || defined(CINDENT) */
3400:
3401: #ifdef CINDENT
3402: /*
3403: * return TRUE if 'cinkeys' contains the key "keytyped",
3404: * when == '*': Only if key is preceded with '*' (indent before insert)
3405: * when == '!': Only if key is prededed with '!' (don't insert)
3406: * when == ' ': Only if key is not preceded with '*'(indent afterwards)
3407: *
3408: * If line_is_empty is TRUE accept keys with '0' before them.
3409: */
3410: int
3411: in_cinkeys(keytyped, when, line_is_empty)
3412: int keytyped;
3413: int when;
3414: int line_is_empty;
3415: {
3416: char_u *look;
3417: int try_match;
3418: char_u *p;
3419:
3420: for (look = curbuf->b_p_cink; *look; )
3421: {
3422: /*
3423: * Find out if we want to try a match with this key, depending on
3424: * 'when' and a '*' or '!' before the key.
3425: */
3426: switch (when)
3427: {
3428: case '*': try_match = (*look == '*'); break;
3429: case '!': try_match = (*look == '!'); break;
3430: default: try_match = (*look != '*'); break;
3431: }
3432: if (*look == '*' || *look == '!')
3433: ++look;
3434:
3435: /*
3436: * If there is a '0', only accept a match if the line is empty.
3437: */
3438: if (*look == '0')
3439: {
3440: if (!line_is_empty)
3441: try_match = FALSE;
3442: ++look;
3443: }
3444:
3445: /*
3446: * does it look like a control character?
3447: */
3448: if (*look == '^' && look[1] >= '@' && look[1] <= '_')
3449: {
3450: if (try_match && keytyped == Ctrl(look[1]))
3451: return TRUE;
3452: look += 2;
3453: }
3454: /*
3455: * 'o' means "o" command, open forward.
3456: * 'O' means "O" command, open backward.
3457: */
3458: else if (*look == 'o')
3459: {
3460: if (try_match && keytyped == KEY_OPEN_FORW)
3461: return TRUE;
3462: ++look;
3463: }
3464: else if (*look == 'O')
3465: {
3466: if (try_match && keytyped == KEY_OPEN_BACK)
3467: return TRUE;
3468: ++look;
3469: }
3470:
3471: /*
3472: * 'e' means to check for "else" at start of line and just before the
3473: * cursor.
3474: */
3475: else if (*look == 'e')
3476: {
3477: if (try_match && keytyped == 'e' && curwin->w_cursor.col >= 4)
3478: {
3479: p = ml_get_curline();
3480: if (skipwhite(p) == p + curwin->w_cursor.col - 4 &&
3481: STRNCMP(p + curwin->w_cursor.col - 4, "else", 4) == 0)
3482: return TRUE;
3483: }
3484: ++look;
3485: }
3486:
3487: /*
3488: * ':' only causes an indent if it is at the end of a label or case
3489: * statement.
3490: */
3491: else if (*look == ':')
3492: {
3493: if (try_match && keytyped == ':')
3494: {
3495: p = ml_get_curline();
3496: if (iscase(p) || islabel(30))
3497: return TRUE;
3498: }
3499: ++look;
3500: }
3501:
3502:
3503: /*
3504: * Is it a key in <>, maybe?
3505: */
3506: else if (*look == '<')
3507: {
3508: if (try_match)
3509: {
3510: /*
3511: * make up some named keys <o>, <O>, <e>, <0>, <>>, <<>, <*>
3512: * and <!> so that people can re-indent on o, O, e, 0, <, >, *
3513: * and ! keys if they really really want to.
3514: */
3515: if (vim_strchr((char_u *)"<>!*oOe0", look[1]) != NULL &&
3516: keytyped == look[1])
3517: return TRUE;
3518:
3519: if (keytyped == get_special_key_code(look + 1))
3520: return TRUE;
3521: }
3522: while (*look && *look != '>')
3523: look++;
3524: while (*look == '>')
3525: look++;
3526: }
3527:
3528: /*
3529: * ok, it's a boring generic character.
3530: */
3531: else
3532: {
3533: if (try_match && *look == keytyped)
3534: return TRUE;
3535: ++look;
3536: }
3537:
3538: /*
3539: * Skip over ", ".
3540: */
3541: look = skip_to_option_part(look);
3542: }
3543: return FALSE;
3544: }
3545: #endif /* CINDENT */
3546:
3547: #if defined(RIGHTLEFT) || defined(PROTO)
3548: /*
3549: * Map Hebrew keyboard when in hkmap mode.
3550: */
3551: int
3552: hkmap(c)
3553: int c;
3554: {
3555: switch(c)
3556: {
3557: case '`': return ';';
3558: case '/': return '.';
3559: case '\'': return ',';
3560: case 'q': return '/';
3561: case 'w': return '\'';
3562:
3563: /* Hebrew letters - set offset from 'a' */
3564: case ',': c = '{'; break;
3565: case '.': c = 'v'; break;
3566: case ';': c = 't'; break;
3567: default: {
3568: static char str[] = "zqbcxlsjphmkwonu ydafe rig";
3569:
3570: if (c < 'a' || c > 'z')
3571: return c;
3572: c = str[c - 'a'];
3573: break;
3574: }
3575: }
3576:
3577: return c - 'a' + p_aleph;
3578: }
3579: #endif