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