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