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