summaryrefslogtreecommitdiff
path: root/usr.bin/vim/edit.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/vim/edit.c')
-rw-r--r--usr.bin/vim/edit.c3579
1 files changed, 3579 insertions, 0 deletions
diff --git a/usr.bin/vim/edit.c b/usr.bin/vim/edit.c
new file mode 100644
index 00000000000..1c2d604b5ed
--- /dev/null
+++ b/usr.bin/vim/edit.c
@@ -0,0 +1,3579 @@
+/* $OpenBSD: edit.c,v 1.1 1996/09/07 21:40:26 downsj Exp $ */
+/* vi:set ts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ */
+
+/*
+ * edit.c: functions for insert mode
+ */
+
+#include "vim.h"
+#include "globals.h"
+#include "proto.h"
+#include "option.h"
+#include "ops.h" /* for op_type */
+
+#ifdef INSERT_EXPAND
+/*
+ * definitions used for CTRL-X submode
+ */
+#define CTRL_X_WANT_IDENT 0x100
+
+#define CTRL_X_NOT_DEFINED_YET (1)
+#define CTRL_X_SCROLL (2)
+#define CTRL_X_WHOLE_LINE (3)
+#define CTRL_X_FILES (4)
+#define CTRL_X_TAGS (5 + CTRL_X_WANT_IDENT)
+#define CTRL_X_PATH_PATTERNS (6 + CTRL_X_WANT_IDENT)
+#define CTRL_X_PATH_DEFINES (7 + CTRL_X_WANT_IDENT)
+#define CTRL_X_FINISHED (8)
+#define CTRL_X_DICTIONARY (9 + CTRL_X_WANT_IDENT)
+
+struct Completion
+{
+ char_u *str;
+ char_u *fname;
+ int original;
+ struct Completion *next;
+ struct Completion *prev;
+};
+
+struct Completion *first_match = NULL;
+struct Completion *curr_match = NULL;
+
+static int add_completion __ARGS((char_u *str, int len, char_u *, int dir));
+static int make_cyclic __ARGS((void));
+static void complete_dictionaries __ARGS((char_u *, int));
+static void free_completions __ARGS((void));
+static int count_completions __ARGS((void));
+#endif /* INSERT_EXPAND */
+
+#define BACKSPACE_CHAR 1
+#define BACKSPACE_WORD 2
+#define BACKSPACE_WORD_NOT_SPACE 3
+#define BACKSPACE_LINE 4
+
+static void change_indent __ARGS((int type, int amount, int round));
+static void insert_special __ARGS((int, int));
+static void start_arrow __ARGS((FPOS *end_insert_pos));
+static void stop_arrow __ARGS((void));
+static void stop_insert __ARGS((FPOS *end_insert_pos));
+static int echeck_abbr __ARGS((int));
+
+static FPOS Insstart; /* This is where the latest insert/append
+ * mode started. */
+static colnr_t Insstart_textlen; /* length of line when insert started */
+static colnr_t Insstart_blank_vcol; /* vcol for first inserted blank */
+
+static char_u *last_insert = NULL;
+ /* the text of the previous insert */
+static int last_insert_skip;
+ /* number of chars in front of previous insert */
+static int new_insert_skip;
+ /* number of chars in front of the current insert */
+#ifdef INSERT_EXPAND
+static char_u *original_text = NULL;
+ /* Original text typed before completion */
+#endif
+
+#ifdef CINDENT
+static int can_cindent; /* may do cindenting on this line */
+#endif
+
+/*
+ * edit() returns TRUE if it returns because of a CTRL-O command
+ */
+ int
+edit(initstr, startln, count)
+ int initstr;
+ int startln; /* if set, insert at start of line */
+ long count;
+{
+ int c;
+ int cc;
+ char_u *ptr;
+ linenr_t lnum;
+ int temp = 0;
+ int mode;
+ int lastc = 0;
+ colnr_t mincol;
+ static linenr_t o_lnum = 0;
+ static int o_eol = FALSE;
+ int need_redraw = FALSE;
+ int i;
+ int did_backspace = TRUE; /* previous char was backspace */
+#ifdef RIGHTLEFT
+ int revins; /* reverse insert mode */
+ int revinschars = 0; /* how much to skip after edit */
+ int revinslegal = 0; /* was the last char 'legal'? */
+ int revinsscol = -1; /* start column of revins session */
+#endif
+#ifdef INSERT_EXPAND
+ FPOS first_match_pos;
+ FPOS last_match_pos;
+ FPOS *complete_pos;
+ char_u *complete_pat = NULL;
+ char_u *tmp_ptr;
+ char_u *mesg = NULL; /* Message about completion */
+ char_u *quick_m; /* Message without sleep */
+ int started_completion = FALSE;
+ colnr_t complete_col = 0; /* init for gcc */
+ int complete_direction;
+ int done_dir = 0; /* Found all matches in this
+ * direction */
+ int num_matches;
+ char_u **matches;
+ regexp *prog;
+ int save_sm = -1; /* init for gcc */
+ int save_p_scs;
+#endif
+#ifdef CINDENT
+ int line_is_white = FALSE; /* line is empty before insert */
+#endif
+ FPOS tpos;
+
+ /* sleep before redrawing, needed for "CTRL-O :" that results in an
+ * error message */
+ if (msg_scroll || emsg_on_display)
+ {
+ mch_delay(1000L, TRUE);
+ msg_scroll = FALSE;
+ emsg_on_display = FALSE;
+ }
+#ifdef SLEEP_IN_EMSG
+ if (need_sleep)
+ {
+ mch_delay(1000L, TRUE);
+ need_sleep = FALSE;
+ }
+#endif
+
+#ifdef USE_MOUSE
+ /*
+ * When doing a paste with the middle mouse button, Insstart is set to
+ * where the paste started.
+ */
+ if (where_paste_started.lnum != 0)
+ Insstart = where_paste_started;
+ else
+#endif
+ {
+ Insstart = curwin->w_cursor;
+ if (startln)
+ Insstart.col = 0;
+ }
+ Insstart_textlen = linetabsize(ml_get_curline());
+ Insstart_blank_vcol = MAXCOL;
+
+ if (initstr != NUL && !restart_edit)
+ {
+ ResetRedobuff();
+ AppendNumberToRedobuff(count);
+ AppendCharToRedobuff(initstr);
+ if (initstr == 'g') /* "gI" command */
+ AppendCharToRedobuff('I');
+ }
+
+ if (initstr == 'R')
+ State = REPLACE;
+ else
+ State = INSERT;
+
+#ifdef USE_MOUSE
+ setmouse();
+#endif
+ clear_showcmd();
+#ifdef RIGHTLEFT
+ revins = (State == INSERT && p_ri); /* there is no reverse replace mode */
+ if (revins)
+ undisplay_dollar();
+#endif
+
+ /*
+ * When CTRL-O . is used to repeat an insert, we get here with
+ * restart_edit non-zero, but something in the stuff buffer
+ */
+ if (restart_edit && stuff_empty())
+ {
+#ifdef USE_MOUSE
+ /*
+ * After a paste we consider text typed to be part of the insert for
+ * the pasted text. You can backspace over the paste text too.
+ */
+ if (where_paste_started.lnum)
+ arrow_used = FALSE;
+ else
+#endif
+ arrow_used = TRUE;
+ restart_edit = 0;
+ /*
+ * If the cursor was after the end-of-line before the CTRL-O
+ * and it is now at the end-of-line, put it after the end-of-line
+ * (this is not correct in very rare cases).
+ * Also do this if curswant is greater than the current virtual column.
+ * Eg after "^O$" or "^O80|".
+ */
+ if (((o_eol && curwin->w_cursor.lnum == o_lnum) ||
+ curwin->w_curswant > curwin->w_virtcol) &&
+ *(ptr = ml_get_curline() + curwin->w_cursor.col)
+ != NUL &&
+ *(ptr + 1) == NUL)
+ ++curwin->w_cursor.col;
+ }
+ else
+ {
+ arrow_used = FALSE;
+ o_eol = FALSE;
+ }
+#ifdef USE_MOUSE
+ where_paste_started.lnum = 0;
+#endif
+#ifdef CINDENT
+ can_cindent = TRUE;
+#endif
+
+ if (p_smd)
+ showmode();
+
+ if (!p_im)
+ change_warning(); /* give a warning if readonly */
+
+#ifdef DIGRAPHS
+ do_digraph(-1); /* clear digraphs */
+#endif
+
+/*
+ * Get the current length of the redo buffer, those characters have to be
+ * skipped if we want to get to the inserted characters.
+ */
+ ptr = get_inserted();
+ new_insert_skip = STRLEN(ptr);
+ vim_free(ptr);
+
+ old_indent = 0;
+
+ for (;;)
+ {
+#ifdef RIGHTLEFT
+ if (!revinslegal)
+ revinsscol = -1; /* reset on illegal motions */
+ else
+ revinslegal = 0;
+#endif
+ if (arrow_used) /* don't repeat insert when arrow key used */
+ count = 0;
+
+ /* set curwin->w_curswant for next K_DOWN or K_UP */
+ if (!arrow_used)
+ curwin->w_set_curswant = TRUE;
+
+ /* Figure out where the cursor is based on curwin->w_cursor. */
+ mincol = curwin->w_col;
+ i = curwin->w_row;
+ cursupdate();
+
+ /*
+ * When emsg() was called msg_scroll will have been set.
+ */
+ msg_scroll = FALSE;
+
+ /*
+ * If we inserted a character at the last position of the last line in
+ * the window, scroll the window one line up. This avoids an extra
+ * redraw.
+ * This is detected when the cursor column is smaller after inserting
+ * something.
+ */
+ if (curwin->w_p_wrap && !did_backspace &&
+ (int)curwin->w_col < (int)mincol - curbuf->b_p_ts &&
+ i == curwin->w_winpos + curwin->w_height - 1 &&
+ curwin->w_cursor.lnum != curwin->w_topline)
+ {
+ ++curwin->w_topline;
+ updateScreen(VALID_TO_CURSCHAR);
+ cursupdate();
+ need_redraw = FALSE;
+ }
+ did_backspace = FALSE;
+
+ /*
+ * redraw is postponed until after cursupdate() to make 'dollar'
+ * option work correctly.
+ */
+ if (need_redraw)
+ {
+ updateline();
+ need_redraw = FALSE;
+ }
+
+ showruler(0);
+ setcursor();
+ emsg_on_display = FALSE; /* may remove error message now */
+
+ c = vgetc();
+ if (c == Ctrl('C'))
+ got_int = FALSE;
+
+#ifdef RIGHTLEFT
+ if (p_hkmap && KeyTyped)
+ c = hkmap(c); /* Hebrew mode mapping */
+#endif
+
+#ifdef INSERT_EXPAND
+ if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET)
+ {
+ /* We have just entered ctrl-x mode and aren't quite sure which
+ * ctrl-x mode it will be yet. Now we decide -- webb
+ */
+ switch (c)
+ {
+ case Ctrl('E'):
+ case Ctrl('Y'):
+ ctrl_x_mode = CTRL_X_SCROLL;
+ if (State == INSERT)
+ edit_submode = (char_u *)" (insert) Scroll (^E/^Y)";
+ else
+ edit_submode = (char_u *)" (replace) Scroll (^E/^Y)";
+ break;
+ case Ctrl('L'):
+ ctrl_x_mode = CTRL_X_WHOLE_LINE;
+ edit_submode = (char_u *)" Whole line completion (^L/^N/^P)";
+ break;
+ case Ctrl('F'):
+ ctrl_x_mode = CTRL_X_FILES;
+ edit_submode = (char_u *)" File name completion (^F/^N/^P)";
+ break;
+ case Ctrl('K'):
+ ctrl_x_mode = CTRL_X_DICTIONARY;
+ edit_submode = (char_u *)" Dictionary completion (^K/^N/^P)";
+ break;
+ case Ctrl(']'):
+ ctrl_x_mode = CTRL_X_TAGS;
+ edit_submode = (char_u *)" Tag completion (^]/^N/^P)";
+ break;
+ case Ctrl('I'):
+ ctrl_x_mode = CTRL_X_PATH_PATTERNS;
+ edit_submode = (char_u *)" Path pattern completion (^N/^P)";
+ break;
+ case Ctrl('D'):
+ ctrl_x_mode = CTRL_X_PATH_DEFINES;
+ edit_submode = (char_u *)" Definition completion (^D/^N/^P)";
+ break;
+ default:
+ ctrl_x_mode = 0;
+ break;
+ }
+ showmode();
+ }
+ else if (ctrl_x_mode)
+ {
+ /* We we're already in ctrl-x mode, do we stay in it? */
+ if (!is_ctrl_x_key(c))
+ {
+ if (ctrl_x_mode == CTRL_X_SCROLL)
+ ctrl_x_mode = 0;
+ else
+ ctrl_x_mode = CTRL_X_FINISHED;
+ edit_submode = NULL;
+ }
+ showmode();
+ }
+ if (started_completion || ctrl_x_mode == CTRL_X_FINISHED)
+ {
+ /* Show error message from attempted keyword completion (probably
+ * 'Pattern not found') until another key is hit, then go back to
+ * showing what mode we are in.
+ */
+ showmode();
+ if ((ctrl_x_mode == 0 && c != Ctrl('N') && c != Ctrl('P')) ||
+ ctrl_x_mode == CTRL_X_FINISHED)
+ {
+ /* Get here when we have finished typing a sequence of ^N and
+ * ^P or other completion characters in CTRL-X mode. Free up
+ * memory that was used, and make sure we can redo the insert
+ * -- webb.
+ */
+ if (curr_match != NULL)
+ {
+ /*
+ * If any of the original typed text has been changed,
+ * eg when ignorecase is set, we must add back-spaces to
+ * the redo buffer. We add as few as necessary to delete
+ * just the part of the original text that has changed
+ * -- webb
+ */
+ ptr = curr_match->str;
+ tmp_ptr = original_text;
+ while (*tmp_ptr && *tmp_ptr == *ptr)
+ {
+ ++tmp_ptr;
+ ++ptr;
+ }
+ for (temp = 0; tmp_ptr[temp]; ++temp)
+ AppendCharToRedobuff(K_BS);
+ if (*ptr)
+ AppendToRedobuff(ptr);
+ }
+ /* Break line if it's too long */
+ lnum = curwin->w_cursor.lnum;
+ insertchar(NUL, FALSE, -1);
+ if (lnum != curwin->w_cursor.lnum)
+ updateScreen(CURSUPD);
+ else
+ need_redraw = TRUE;
+
+ vim_free(complete_pat);
+ complete_pat = NULL;
+ vim_free(original_text);
+ original_text = NULL;
+ free_completions();
+ started_completion = FALSE;
+ ctrl_x_mode = 0;
+ p_sm = save_sm;
+ if (edit_submode != NULL)
+ {
+ edit_submode = NULL;
+ showmode();
+ }
+ }
+ }
+#endif /* INSERT_EXPAND */
+
+ if (c != Ctrl('D')) /* remember to detect ^^D and 0^D */
+ lastc = c;
+
+#ifdef DIGRAPHS
+ c = do_digraph(c);
+#endif /* DIGRAPHS */
+
+ if (c == Ctrl('V') || c == Ctrl('Q'))
+ {
+ if (NextScreen != NULL)
+ screen_outchar('^', curwin->w_winpos + curwin->w_row,
+#ifdef RIGHTLEFT
+ curwin->w_p_rl ? (int)Columns - 1 - curwin->w_col :
+#endif
+ curwin->w_col);
+ AppendToRedobuff((char_u *)"\026"); /* CTRL-V */
+ cursupdate();
+
+ if (!add_to_showcmd(c, FALSE))
+ setcursor();
+
+ c = get_literal();
+ clear_showcmd();
+ insert_special(c, TRUE);
+ need_redraw = TRUE;
+#ifdef RIGHTLEFT
+ revinschars++;
+ revinslegal++;
+#endif
+ continue;
+ }
+
+#ifdef CINDENT
+ if (curbuf->b_p_cin
+# ifdef INSERT_EXPAND
+ && !ctrl_x_mode
+# endif
+ )
+ {
+ line_is_white = inindent(0);
+
+ /*
+ * A key name preceded by a bang means that this
+ * key wasn't destined to be inserted. Skip ahead
+ * to the re-indenting if we find one.
+ */
+ if (in_cinkeys(c, '!', line_is_white))
+ goto force_cindent;
+
+ /*
+ * A key name preceded by a star means that indenting
+ * has to be done before inserting the key.
+ */
+ if (can_cindent && in_cinkeys(c, '*', line_is_white))
+ {
+ stop_arrow();
+
+ /* re-indent the current line */
+ fixthisline(get_c_indent);
+
+ /* draw the changes on the screen later */
+ need_redraw = TRUE;
+ }
+ }
+#endif /* CINDENT */
+
+#ifdef RIGHTLEFT
+ if (curwin->w_p_rl)
+ switch (c)
+ {
+ case K_LEFT: c = K_RIGHT; break;
+ case K_S_LEFT: c = K_S_RIGHT; break;
+ case K_RIGHT: c = K_LEFT; break;
+ case K_S_RIGHT: c = K_S_LEFT; break;
+ }
+#endif
+
+ switch (c) /* handle character in insert mode */
+ {
+ case K_INS: /* toggle insert/replace mode */
+ if (State == REPLACE)
+ State = INSERT;
+ else
+ State = REPLACE;
+ AppendCharToRedobuff(K_INS);
+ showmode();
+ break;
+
+#ifdef INSERT_EXPAND
+ case Ctrl('X'): /* Enter ctrl-x mode */
+ /* We're not sure which ctrl-x mode it will be yet */
+ ctrl_x_mode = CTRL_X_NOT_DEFINED_YET;
+ MSG("^X mode (^E/^Y/^L/^]/^F/^I/^K/^D)");
+ break;
+#endif /* INSERT_EXPAND */
+
+ case Ctrl('O'): /* execute one command */
+ if (echeck_abbr(Ctrl('O') + ABBR_OFF))
+ break;
+ count = 0;
+ if (State == INSERT)
+ restart_edit = 'I';
+ else
+ restart_edit = 'R';
+ o_lnum = curwin->w_cursor.lnum;
+ o_eol = (gchar_cursor() == NUL);
+ goto doESCkey;
+
+ /* Hitting the help key in insert mode is like <ESC> <Help> */
+ case K_HELP:
+ case K_F1:
+ stuffcharReadbuff(K_HELP);
+ /*FALLTHROUGH*/
+
+ case ESC: /* an escape ends input mode */
+ if (echeck_abbr(ESC + ABBR_OFF))
+ break;
+ /*FALLTHROUGH*/
+
+ case Ctrl('C'):
+doESCkey:
+ temp = curwin->w_cursor.col;
+ if (!arrow_used)
+ {
+ AppendToRedobuff(ESC_STR);
+
+ if (--count > 0) /* repeat what was typed */
+ {
+ (void)start_redo_ins();
+ continue;
+ }
+ stop_insert(&curwin->w_cursor);
+ if (dollar_vcol)
+ {
+ dollar_vcol = 0;
+ /* may have to redraw status line if this was the
+ * first change, show "[+]" */
+ if (curwin->w_redr_status == TRUE)
+ must_redraw = NOT_VALID;
+ else
+ need_redraw = TRUE;
+ }
+ }
+ if (need_redraw)
+ updateline();
+
+ /* When an autoindent was removed, curswant stays after the
+ * indent */
+ if (!restart_edit && (colnr_t)temp == curwin->w_cursor.col)
+ curwin->w_set_curswant = TRUE;
+
+ /*
+ * The cursor should end up on the last inserted character.
+ */
+ if (curwin->w_cursor.col != 0 &&
+ (!restart_edit || gchar_cursor() == NUL)
+#ifdef RIGHTLEFT
+ && !revins
+#endif
+ )
+ --curwin->w_cursor.col;
+ if (State == REPLACE)
+ replace_flush(); /* free replace stack */
+ State = NORMAL;
+#ifdef USE_MOUSE
+ setmouse();
+#endif
+ /* inchar() may have deleted the "INSERT" message */
+ /* for CTRL-O we display -- INSERT COMMAND -- */
+ if (Recording || restart_edit)
+ showmode();
+ else if (p_smd)
+ MSG("");
+ old_indent = 0;
+ return (c == Ctrl('O'));
+
+ /*
+ * Insert the previously inserted text.
+ * For ^@ the trailing ESC will end the insert, unless there
+ * is an error.
+ */
+ case K_ZERO:
+ case NUL:
+ case Ctrl('A'):
+ if (stuff_inserted(NUL, 1L, (c == Ctrl('A'))) == FAIL &&
+ c != Ctrl('A'))
+ goto doESCkey; /* quit insert mode */
+ break;
+
+ /*
+ * insert the contents of a register
+ */
+ case Ctrl('R'):
+ if (NextScreen != NULL)
+ screen_outchar('"', curwin->w_winpos + curwin->w_row,
+#ifdef RIGHTLEFT
+ curwin->w_p_rl ? (int)Columns - 1 - curwin->w_col :
+#endif
+ curwin->w_col);
+ if (!add_to_showcmd(c, FALSE))
+ setcursor();
+ /* don't map the register name. This also prevents the
+ * mode message to be deleted when ESC is hit */
+ ++no_mapping;
+#ifdef HAVE_LANGMAP
+ cc = vgetc();
+ LANGMAP_ADJUST(cc, TRUE);
+ if (insertbuf(cc) == FAIL)
+#else
+ if (insertbuf(vgetc()) == FAIL)
+#endif
+ {
+ beep_flush();
+ need_redraw = TRUE; /* remove the '"' */
+ }
+ --no_mapping;
+ clear_showcmd();
+ break;
+
+#ifdef RIGHTLEFT
+ case Ctrl('B'): /* toggle reverse insert mode */
+ p_ri = !p_ri;
+ revins = (State == INSERT && p_ri);
+ if (revins)
+ undisplay_dollar();
+ showmode();
+ break;
+
+ case Ctrl('_'): /* toggle language: khmap and revins */
+ /* Move to end of reverse inserted text */
+ if (revins && revinschars && revinsscol >= 0)
+ while (gchar_cursor() != NUL && revinschars--)
+ ++curwin->w_cursor.col;
+ p_ri = !p_ri;
+ revins = (State == INSERT && p_ri);
+ if (revins)
+ {
+ revinsscol = curwin->w_cursor.col;
+ revinslegal++;
+ revinschars = 0;
+ undisplay_dollar();
+ }
+ else
+ revinsscol = -1;
+ p_hkmap = curwin->w_p_rl ^ p_ri; /* be consistent! */
+ showmode();
+ break;
+#endif
+
+ /*
+ * If the cursor is on an indent, ^T/^D insert/delete one
+ * shiftwidth. Otherwise ^T/^D behave like a "<<" or ">>".
+ * Always round the indent to 'shiftwith', this is compatible
+ * with vi. But vi only supports ^T and ^D after an
+ * autoindent, we support it everywhere.
+ */
+ case Ctrl('D'): /* make indent one shiftwidth smaller */
+#ifdef INSERT_EXPAND
+ if (ctrl_x_mode == CTRL_X_PATH_DEFINES)
+ goto docomplete;
+#endif /* INSERT_EXPAND */
+ /* FALLTHROUGH */
+ case Ctrl('T'): /* make indent one shiftwidth greater */
+ stop_arrow();
+ AppendCharToRedobuff(c);
+
+ /*
+ * 0^D and ^^D: remove all indent.
+ */
+ if ((lastc == '0' || lastc == '^') && curwin->w_cursor.col)
+ {
+ --curwin->w_cursor.col;
+ (void)delchar(FALSE); /* delete the '^' or '0' */
+ if (lastc == '^')
+ old_indent = get_indent(); /* remember curr. indent */
+ change_indent(INDENT_SET, 0, TRUE);
+ }
+ else
+ change_indent(c == Ctrl('D') ? INDENT_DEC : INDENT_INC,
+ 0, TRUE);
+
+ did_ai = FALSE;
+ did_si = FALSE;
+ can_si = FALSE;
+ can_si_back = FALSE;
+#ifdef CINDENT
+ can_cindent = FALSE; /* no cindenting after ^D or ^T */
+#endif
+ goto redraw;
+
+ case K_DEL:
+ stop_arrow();
+ if (gchar_cursor() == NUL) /* delete newline */
+ {
+ temp = curwin->w_cursor.col;
+ if (!p_bs || /* only if 'bs' set */
+ u_save((linenr_t)(curwin->w_cursor.lnum - 1),
+ (linenr_t)(curwin->w_cursor.lnum + 2)) == FAIL ||
+ do_join(FALSE, TRUE) == FAIL)
+ beep_flush();
+ else
+ curwin->w_cursor.col = temp;
+ }
+ else if (delchar(FALSE) == FAIL)/* delete char under cursor */
+ beep_flush();
+ did_ai = FALSE;
+ did_si = FALSE;
+ can_si = FALSE;
+ can_si_back = FALSE;
+ AppendCharToRedobuff(c);
+ goto redraw;
+
+ case K_BS:
+ case Ctrl('H'):
+ mode = BACKSPACE_CHAR;
+dodel:
+ /* can't delete anything in an empty file */
+ /* can't backup past first character in buffer */
+ /* can't backup past starting point unless 'backspace' > 1 */
+ /* can backup to a previous line if 'backspace' == 0 */
+ if (bufempty() || (
+#ifdef RIGHTLEFT
+ !revins &&
+#endif
+ ((curwin->w_cursor.lnum == 1 &&
+ curwin->w_cursor.col <= 0) ||
+ (p_bs < 2 && (arrow_used ||
+ (curwin->w_cursor.lnum == Insstart.lnum &&
+ curwin->w_cursor.col <= Insstart.col) ||
+ (curwin->w_cursor.col <= 0 && p_bs == 0))))))
+ {
+ beep_flush();
+ goto redraw;
+ }
+
+ stop_arrow();
+#ifdef CINDENT
+ if (inindent(0))
+ can_cindent = FALSE;
+#endif
+#ifdef RIGHTLEFT
+ if (revins) /* put cursor after last inserted char */
+ inc_cursor();
+#endif
+ if (curwin->w_cursor.col <= 0) /* delete newline! */
+ {
+ lnum = Insstart.lnum;
+ if (curwin->w_cursor.lnum == Insstart.lnum
+#ifdef RIGHTLEFT
+ || revins
+#endif
+ )
+ {
+ if (u_save((linenr_t)(curwin->w_cursor.lnum - 2),
+ (linenr_t)(curwin->w_cursor.lnum + 1)) == FAIL)
+ goto redraw;
+ --Insstart.lnum;
+ Insstart.col = 0;
+ }
+ /*
+ * In replace mode:
+ * cc < 0: NL was inserted, delete it
+ * cc >= 0: NL was replaced, put original characters back
+ */
+ cc = -1;
+ if (State == REPLACE)
+ cc = replace_pop();
+ /* in replace mode, in the line we started replacing, we
+ only move the cursor */
+ if (State != REPLACE || curwin->w_cursor.lnum > lnum)
+ {
+ temp = gchar_cursor(); /* remember current char */
+ --curwin->w_cursor.lnum;
+ (void)do_join(FALSE, curs_rows() == OK);
+ if (temp == NUL && gchar_cursor() != NUL)
+ ++curwin->w_cursor.col;
+ /*
+ * in REPLACE mode we have to put back the text that
+ * was replace by the NL. On the replace stack is
+ * first a NUL-terminated sequence of characters that
+ * were deleted and then the character that NL
+ * replaced.
+ */
+ if (State == REPLACE)
+ {
+ /*
+ * Do the next ins_char() in NORMAL state, to
+ * prevent ins_char() from replacing characters and
+ * avoiding showmatch().
+ */
+ State = NORMAL;
+ /*
+ * restore blanks deleted after cursor
+ */
+ while (cc > 0)
+ {
+ temp = curwin->w_cursor.col;
+ ins_char(cc);
+ curwin->w_cursor.col = temp;
+ cc = replace_pop();
+ }
+ cc = replace_pop();
+ if (cc > 0)
+ {
+ ins_char(cc);
+ dec_cursor();
+ }
+ State = REPLACE;
+ }
+ }
+ else
+ dec_cursor();
+ did_ai = FALSE;
+ }
+ else
+ {
+#ifdef RIGHTLEFT
+ if (revins) /* put cursor on last inserted char */
+ dec_cursor();
+#endif
+ mincol = 0;
+ /* keep indent */
+ if (mode == BACKSPACE_LINE && curbuf->b_p_ai
+#ifdef RIGHTLEFT
+ && !revins
+#endif
+ )
+ {
+ temp = curwin->w_cursor.col;
+ beginline(TRUE);
+ if (curwin->w_cursor.col < (colnr_t)temp)
+ mincol = curwin->w_cursor.col;
+ curwin->w_cursor.col = temp;
+ }
+
+ /* delete upto starting point, start of line or previous
+ * word */
+ do
+ {
+#ifdef RIGHTLEFT
+ if (!revins) /* put cursor on char to be deleted */
+#endif
+ dec_cursor();
+
+ /* start of word? */
+ if (mode == BACKSPACE_WORD &&
+ !vim_isspace(gchar_cursor()))
+ {
+ mode = BACKSPACE_WORD_NOT_SPACE;
+ temp = iswordchar(gchar_cursor());
+ }
+ /* end of word? */
+ else if (mode == BACKSPACE_WORD_NOT_SPACE &&
+ (vim_isspace(cc = gchar_cursor()) ||
+ iswordchar(cc) != temp))
+ {
+#ifdef RIGHTLEFT
+ if (!revins)
+#endif
+ inc_cursor();
+#ifdef RIGHTLEFT
+ else if (State == REPLACE)
+ dec_cursor();
+#endif
+ break;
+ }
+ if (State == REPLACE)
+ {
+ /*
+ * cc < 0: replace stack empty, just move cursor
+ * cc == 0: character was inserted, delete it
+ * cc > 0: character was replace, put original back
+ */
+ cc = replace_pop();
+ if (cc > 0)
+ pchar_cursor(cc);
+ else if (cc == 0)
+ (void)delchar(FALSE);
+ }
+ else /* State != REPLACE */
+ {
+ (void)delchar(FALSE);
+#ifdef RIGHTLEFT
+ if (revinschars)
+ {
+ revinschars--;
+ revinslegal++;
+ }
+ if (revins && gchar_cursor() == NUL)
+ break;
+#endif
+ }
+ /* Just a single backspace?: */
+ if (mode == BACKSPACE_CHAR)
+ break;
+ } while (
+#ifdef RIGHTLEFT
+ revins ||
+#endif
+ (curwin->w_cursor.col > mincol &&
+ (curwin->w_cursor.lnum != Insstart.lnum ||
+ curwin->w_cursor.col != Insstart.col)));
+ did_backspace = TRUE;
+ }
+ did_si = FALSE;
+ can_si = FALSE;
+ can_si_back = FALSE;
+ if (curwin->w_cursor.col <= 1)
+ did_ai = FALSE;
+ /*
+ * It's a little strange to put backspaces into the redo
+ * buffer, but it makes auto-indent a lot easier to deal
+ * with.
+ */
+ AppendCharToRedobuff(c);
+redraw:
+ need_redraw = TRUE;
+ break;
+
+ case Ctrl('W'): /* delete word before cursor */
+ mode = BACKSPACE_WORD;
+ goto dodel;
+
+ case Ctrl('U'): /* delete inserted text in current line */
+ mode = BACKSPACE_LINE;
+ goto dodel;
+
+#ifdef USE_MOUSE
+ case K_LEFTMOUSE:
+ case K_LEFTDRAG:
+ case K_LEFTRELEASE:
+ case K_MIDDLEMOUSE:
+ case K_MIDDLEDRAG:
+ case K_MIDDLERELEASE:
+ case K_RIGHTMOUSE:
+ case K_RIGHTDRAG:
+ case K_RIGHTRELEASE:
+#ifdef USE_GUI
+ /* When GUI is active, also move/paste when 'mouse' is empty */
+ if (!gui.in_use)
+#endif
+ if (!mouse_has(MOUSE_INSERT))
+ break;
+
+ undisplay_dollar();
+ tpos = curwin->w_cursor;
+ if (do_mouse(c, BACKWARD, 1L, FALSE))
+ {
+ start_arrow(&tpos);
+# ifdef CINDENT
+ can_cindent = TRUE;
+# endif
+ }
+
+ break;
+
+ case K_IGNORE:
+ break;
+#endif
+
+#ifdef USE_GUI
+ case K_SCROLLBAR:
+ undisplay_dollar();
+ tpos = curwin->w_cursor;
+ if (gui_do_scroll())
+ {
+ start_arrow(&tpos);
+# ifdef CINDENT
+ can_cindent = TRUE;
+# endif
+ }
+ break;
+
+ case K_HORIZ_SCROLLBAR:
+ undisplay_dollar();
+ tpos = curwin->w_cursor;
+ if (gui_do_horiz_scroll())
+ {
+ start_arrow(&tpos);
+#ifdef CINDENT
+ can_cindent = TRUE;
+#endif
+ }
+ break;
+#endif
+
+ case K_LEFT:
+ undisplay_dollar();
+ tpos = curwin->w_cursor;
+ if (oneleft() == OK)
+ {
+ start_arrow(&tpos);
+#ifdef RIGHTLEFT
+ /* If exit reversed string, position is fixed */
+ if (revinsscol != -1 &&
+ (int)curwin->w_cursor.col >= revinsscol)
+ revinslegal++;
+ revinschars++;
+#endif
+ }
+
+ /*
+ * if 'whichwrap' set for cursor in insert mode may go to
+ * previous line
+ */
+ else if (vim_strchr(p_ww, '[') != NULL &&
+ curwin->w_cursor.lnum > 1)
+ {
+ start_arrow(&tpos);
+ --(curwin->w_cursor.lnum);
+ coladvance(MAXCOL);
+ curwin->w_set_curswant = TRUE; /* so we stay at the end */
+ }
+ else
+ beep_flush();
+ break;
+
+ case K_HOME:
+ undisplay_dollar();
+ tpos = curwin->w_cursor;
+ if ((mod_mask & MOD_MASK_CTRL))
+ curwin->w_cursor.lnum = 1;
+ curwin->w_cursor.col = 0;
+ curwin->w_curswant = 0;
+ start_arrow(&tpos);
+ break;
+
+ case K_END:
+ undisplay_dollar();
+ tpos = curwin->w_cursor;
+ if ((mod_mask & MOD_MASK_CTRL))
+ curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
+ coladvance(MAXCOL);
+ curwin->w_curswant = MAXCOL;
+ start_arrow(&tpos);
+ break;
+
+ case K_S_LEFT:
+ undisplay_dollar();
+ if (curwin->w_cursor.lnum > 1 || curwin->w_cursor.col > 0)
+ {
+ start_arrow(&curwin->w_cursor);
+ (void)bck_word(1L, 0, FALSE);
+ }
+ else
+ beep_flush();
+ break;
+
+ case K_RIGHT:
+ undisplay_dollar();
+ if (gchar_cursor() != NUL)
+ {
+ start_arrow(&curwin->w_cursor);
+ curwin->w_set_curswant = TRUE;
+ ++curwin->w_cursor.col;
+#ifdef RIGHTLEFT
+ revinslegal++;
+ if (revinschars)
+ revinschars--;
+#endif
+ }
+ /* if 'whichwrap' set for cursor in insert mode may go
+ * to next line */
+ else if (vim_strchr(p_ww, ']') != NULL &&
+ curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
+ {
+ start_arrow(&curwin->w_cursor);
+ curwin->w_set_curswant = TRUE;
+ ++curwin->w_cursor.lnum;
+ curwin->w_cursor.col = 0;
+ }
+ else
+ beep_flush();
+ break;
+
+ case K_S_RIGHT:
+ undisplay_dollar();
+ if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count ||
+ gchar_cursor() != NUL)
+ {
+ start_arrow(&curwin->w_cursor);
+ (void)fwd_word(1L, 0, 0);
+ }
+ else
+ beep_flush();
+ break;
+
+ case K_UP:
+ undisplay_dollar();
+ tpos = curwin->w_cursor;
+ if (cursor_up(1L) == OK)
+ {
+ start_arrow(&tpos);
+#ifdef CINDENT
+ can_cindent = TRUE;
+#endif
+ }
+ else
+ beep_flush();
+ break;
+
+ case K_S_UP:
+ case K_PAGEUP:
+ undisplay_dollar();
+ tpos = curwin->w_cursor;
+ if (onepage(BACKWARD, 1L) == OK)
+ {
+ start_arrow(&tpos);
+#ifdef CINDENT
+ can_cindent = TRUE;
+#endif
+ }
+ else
+ beep_flush();
+ break;
+
+ case K_DOWN:
+ undisplay_dollar();
+ tpos = curwin->w_cursor;
+ if (cursor_down(1L) == OK)
+ {
+ start_arrow(&tpos);
+#ifdef CINDENT
+ can_cindent = TRUE;
+#endif
+ }
+ else
+ beep_flush();
+ break;
+
+ case K_S_DOWN:
+ case K_PAGEDOWN:
+ undisplay_dollar();
+ tpos = curwin->w_cursor;
+ if (onepage(FORWARD, 1L) == OK)
+ {
+ start_arrow(&tpos);
+#ifdef CINDENT
+ can_cindent = TRUE;
+#endif
+ }
+ else
+ beep_flush();
+ break;
+
+ case TAB: /* TAB or Complete patterns along path */
+#ifdef INSERT_EXPAND
+ if (ctrl_x_mode == CTRL_X_PATH_PATTERNS)
+ goto docomplete;
+#endif /* INSERT_EXPAND */
+
+ if (Insstart_blank_vcol == MAXCOL &&
+ curwin->w_cursor.lnum == Insstart.lnum)
+ Insstart_blank_vcol = curwin->w_virtcol;
+ if (echeck_abbr(TAB + ABBR_OFF))
+ break;
+ i = inindent(0);
+#ifdef CINDENT
+ if (i)
+ can_cindent = FALSE;
+#endif
+ if (!curbuf->b_p_et && !(p_sta && i))
+ goto normalchar;
+
+ stop_arrow();
+ did_ai = FALSE;
+ did_si = FALSE;
+ can_si = FALSE;
+ can_si_back = FALSE;
+ AppendToRedobuff((char_u *)"\t");
+
+ if (p_sta && i) /* insert tab in indent */
+ {
+ change_indent(INDENT_INC, 0, p_sr);
+ goto redraw;
+ }
+
+ /*
+ * p_et is set: expand a tab into spaces
+ */
+ temp = (int)curbuf->b_p_ts;
+ temp -= curwin->w_virtcol % temp;
+
+ /*
+ * insert the first space with ins_char(); it will delete one
+ * char in replace mode. Insert the rest with ins_str(); it
+ * will not delete any chars
+ */
+ ins_char(' ');
+ while (--temp)
+ {
+ ins_str((char_u *)" ");
+ if (State == REPLACE) /* no char replaced */
+ replace_push(NUL);
+ }
+ goto redraw;
+
+ case CR:
+ case NL:
+ if (echeck_abbr(c + ABBR_OFF))
+ break;
+ stop_arrow();
+ if (State == REPLACE)
+ replace_push(NUL);
+#ifdef RIGHTLEFT
+ /* NL in reverse insert will allways start in the end of
+ * current line. */
+ if (revins)
+ while (gchar_cursor() != NUL)
+ ++curwin->w_cursor.col;
+#endif
+
+ AppendToRedobuff(NL_STR);
+ if (has_format_option(FO_RET_COMS))
+ fo_do_comments = TRUE;
+ i = Opencmd(FORWARD, TRUE, FALSE);
+ fo_do_comments = FALSE;
+#ifdef CINDENT
+ can_cindent = TRUE;
+#endif
+ if (!i)
+ goto doESCkey; /* out of memory */
+ break;
+
+#ifdef DIGRAPHS
+ case Ctrl('K'):
+#ifdef INSERT_EXPAND
+ if (ctrl_x_mode == CTRL_X_DICTIONARY)
+ goto docomplete;
+#endif
+ if (NextScreen != NULL)
+ screen_outchar('?', curwin->w_winpos + curwin->w_row,
+#ifdef RIGHTLEFT
+ curwin->w_p_rl ? (int)Columns - 1 - curwin->w_col :
+#endif
+ curwin->w_col);
+ if (!add_to_showcmd(c, FALSE))
+ setcursor();
+ /* don't map the digraph chars. This also prevents the
+ * mode message to be deleted when ESC is hit */
+ ++no_mapping;
+ ++allow_keys;
+ c = vgetc();
+ --no_mapping;
+ --allow_keys;
+ if (IS_SPECIAL(c)) /* special key */
+ {
+ clear_showcmd();
+ insert_special(c, TRUE);
+ need_redraw = TRUE;
+ break;
+ }
+ if (c != ESC)
+ {
+ if (charsize(c) == 1 && NextScreen != NULL)
+ screen_outchar(c, curwin->w_winpos + curwin->w_row,
+#ifdef RIGHTLEFT
+ curwin->w_p_rl ? (int)Columns - 1 - curwin->w_col :
+#endif
+ curwin->w_col);
+ if (!add_to_showcmd(c, FALSE))
+ setcursor();
+ ++no_mapping;
+ ++allow_keys;
+ cc = vgetc();
+ --no_mapping;
+ --allow_keys;
+ if (cc != ESC)
+ {
+ AppendToRedobuff((char_u *)"\026"); /* CTRL-V */
+ c = getdigraph(c, cc, TRUE);
+ clear_showcmd();
+ goto normalchar;
+ }
+ }
+ clear_showcmd();
+ need_redraw = TRUE;
+ break;
+#else /* DIGRAPHS */
+# ifdef INSERT_EXPAND
+ case Ctrl('K'):
+ if (ctrl_x_mode != CTRL_X_DICTIONARY)
+ goto normalchar;
+ goto docomplete;
+# endif /* INSERT_EXPAND */
+#endif /* DIGRAPHS */
+
+#ifdef INSERT_EXPAND
+ case Ctrl(']'): /* Tag name completion after ^X */
+ if (ctrl_x_mode != CTRL_X_TAGS)
+ goto normalchar;
+ goto docomplete;
+
+ case Ctrl('F'): /* File name completion after ^X */
+ if (ctrl_x_mode != CTRL_X_FILES)
+ goto normalchar;
+ goto docomplete;
+
+ case Ctrl('L'): /* Whole line completion after ^X */
+ if (ctrl_x_mode != CTRL_X_WHOLE_LINE)
+ goto normalchar;
+ /* FALLTHROUGH */
+
+ case Ctrl('P'): /* Do previous pattern completion */
+ case Ctrl('N'): /* Do next pattern completion */
+docomplete:
+ if (c == Ctrl('P') || c == Ctrl('L'))
+ complete_direction = BACKWARD;
+ else
+ complete_direction = FORWARD;
+ quick_m = mesg = NULL; /* No message by default */
+ if (!started_completion)
+ {
+ /* First time we hit ^N or ^P (in a row, I mean) */
+
+ /* Turn off 'sm' so we don't show matches with ^X^L */
+ save_sm = p_sm;
+ p_sm = FALSE;
+
+ if (ctrl_x_mode == 0)
+ {
+ edit_submode = (char_u *)" Keyword completion (^P/^N)";
+ showmode();
+ }
+ did_ai = FALSE;
+ did_si = FALSE;
+ can_si = FALSE;
+ can_si_back = FALSE;
+ stop_arrow();
+ done_dir = 0;
+ first_match_pos = curwin->w_cursor;
+ ptr = tmp_ptr = ml_get(first_match_pos.lnum);
+ complete_col = first_match_pos.col;
+ temp = (int)complete_col - 1;
+
+ /* Work out completion pattern and original text -- webb */
+ if (ctrl_x_mode == 0 || (ctrl_x_mode & CTRL_X_WANT_IDENT))
+ {
+ if (temp < 0 || !iswordchar(ptr[temp]))
+ {
+ /* Match any word of at least two chars */
+ complete_pat = strsave((char_u *)"\\<\\k\\k");
+ if (complete_pat == NULL)
+ break;
+ tmp_ptr += complete_col;
+ temp = 0;
+ }
+ else
+ {
+ while (temp >= 0 && iswordchar(ptr[temp]))
+ temp--;
+ tmp_ptr += ++temp;
+ if ((temp = (int)complete_col - temp) == 1)
+ {
+ /* Only match word with at least two
+ * chars -- webb
+ */
+ sprintf((char *)IObuff, "\\<%c\\k", *tmp_ptr);
+ complete_pat = strsave(IObuff);
+ if (complete_pat == NULL)
+ break;
+ }
+ else
+ {
+ complete_pat = alloc(temp + 3);
+ if (complete_pat == NULL)
+ break;
+ sprintf((char *)complete_pat, "\\<%.*s", temp,
+ tmp_ptr);
+ }
+ }
+ }
+ else if (ctrl_x_mode == CTRL_X_WHOLE_LINE)
+ {
+ tmp_ptr = skipwhite(ptr);
+ temp = (int)complete_col - (tmp_ptr - ptr);
+ complete_pat = strnsave(tmp_ptr, temp);
+ if (complete_pat == NULL)
+ break;
+ }
+ else if (ctrl_x_mode == CTRL_X_FILES)
+ {
+ while (temp >= 0 && isfilechar(ptr[temp]))
+ temp--;
+ tmp_ptr += ++temp;
+ temp = (int)complete_col - temp;
+ complete_pat = addstar(tmp_ptr, temp);
+ if (complete_pat == NULL)
+ break;
+ }
+ original_text = strnsave(tmp_ptr, temp);
+ if (original_text == NULL)
+ {
+ vim_free(complete_pat);
+ complete_pat = NULL;
+ break;
+ }
+
+ complete_col = tmp_ptr - ptr;
+ first_match_pos.col -= temp;
+
+ /* So that ^N can match word immediately after cursor */
+ if (ctrl_x_mode == 0)
+ dec(&first_match_pos);
+
+ last_match_pos = first_match_pos;
+
+ /* Get list of all completions now, if appropriate */
+ if (ctrl_x_mode == CTRL_X_PATH_PATTERNS ||
+ ctrl_x_mode == CTRL_X_PATH_DEFINES)
+ {
+ started_completion = TRUE;
+ find_pattern_in_path(complete_pat,
+ (int)STRLEN(complete_pat), FALSE, FALSE,
+ (ctrl_x_mode == CTRL_X_PATH_DEFINES) ? FIND_DEFINE
+ : FIND_ANY, 1L, ACTION_EXPAND,
+ (linenr_t)1, (linenr_t)MAXLNUM);
+
+ if (make_cyclic() > 1)
+ {
+ sprintf((char *)IObuff, "There are %d matches",
+ count_completions());
+ mesg = IObuff;
+ }
+ }
+ else if (ctrl_x_mode == CTRL_X_DICTIONARY)
+ {
+ started_completion = TRUE;
+ if (*p_dict == NUL)
+ mesg = (char_u *)"'dictionary' option is empty";
+ else
+ {
+ complete_dictionaries(complete_pat,
+ complete_direction);
+ if (make_cyclic() > 1)
+ {
+ sprintf((char *)IObuff,
+ "There are %d matching words",
+ count_completions());
+ mesg = IObuff;
+ }
+ }
+ }
+ else if (ctrl_x_mode == CTRL_X_TAGS)
+ {
+ started_completion = TRUE;
+ /* set reg_ic according to p_ic, p_scs and pat */
+ set_reg_ic(complete_pat);
+ prog = vim_regcomp(complete_pat);
+ if (prog != NULL &&
+ find_tags(NULL, prog, &num_matches, &matches, FALSE)
+ == OK && num_matches > 0)
+ {
+ for (i = 0; i < num_matches; i++)
+ if (add_completion(matches[i], -1, NULL,
+ FORWARD) == RET_ERROR)
+ break;
+ FreeWild(num_matches, matches);
+ vim_free(prog);
+ if (make_cyclic() > 1)
+ {
+ sprintf((char *)IObuff,
+ "There are %d matching tags",
+ count_completions());
+ mesg = IObuff;
+ }
+ }
+ else
+ {
+ vim_free(prog);
+ vim_free(complete_pat);
+ complete_pat = NULL;
+ }
+ }
+ else if (ctrl_x_mode == CTRL_X_FILES)
+ {
+ started_completion = TRUE;
+ expand_interactively = TRUE;
+ if (ExpandWildCards(1, &complete_pat, &num_matches,
+ &matches, FALSE, FALSE) == OK)
+ {
+ for (i = 0; i < num_matches; i++)
+ if (add_completion(matches[i], -1, NULL,
+ FORWARD) == RET_ERROR)
+ break;
+ FreeWild(num_matches, matches);
+ if (make_cyclic() > 1)
+ {
+ sprintf((char *)IObuff,
+ "There are %d matching file names",
+ count_completions());
+ mesg = IObuff;
+ }
+ }
+ else
+ {
+ vim_free(complete_pat);
+ complete_pat = NULL;
+ }
+ expand_interactively = FALSE;
+ }
+ }
+ /*
+ * In insert mode: Delete the typed part.
+ * In replace mode: Put the old characters back, if any.
+ */
+ while (curwin->w_cursor.col > complete_col)
+ {
+ curwin->w_cursor.col--;
+ if (State == REPLACE)
+ {
+ if ((cc = replace_pop()) > 0)
+ pchar(curwin->w_cursor, cc);
+ }
+ else
+ delchar(FALSE);
+ }
+ complete_pos = NULL;
+ if (started_completion && curr_match == NULL &&
+ (p_ws || done_dir == BOTH_DIRECTIONS))
+ quick_m = e_patnotf;
+ else if (curr_match != NULL && complete_direction == FORWARD &&
+ curr_match->next != NULL)
+ curr_match = curr_match->next;
+ else if (curr_match != NULL && complete_direction == BACKWARD &&
+ curr_match->prev != NULL)
+ curr_match = curr_match->prev;
+ else
+ {
+ complete_pos = (complete_direction == FORWARD) ?
+ &last_match_pos : &first_match_pos;
+ /*
+ * If 'infercase' is set, don't use 'smartcase' here
+ */
+ save_p_scs = p_scs;
+ if (curbuf->b_p_inf)
+ p_scs = FALSE;
+ for (;;)
+ {
+ if (ctrl_x_mode == CTRL_X_WHOLE_LINE)
+ temp = search_for_exact_line(complete_pos,
+ complete_direction, complete_pat);
+ else
+ temp = searchit(complete_pos, complete_direction,
+ complete_pat, 1L,
+ SEARCH_KEEP + SEARCH_NFMSG, RE_LAST);
+ if (temp == FAIL)
+ {
+ if (!p_ws && done_dir != -complete_direction)
+ {
+ /*
+ * With nowrapscan, we haven't finished
+ * looking in the other direction yet -- webb
+ */
+ temp = OK;
+ done_dir = complete_direction;
+ }
+ else if (!p_ws)
+ done_dir = BOTH_DIRECTIONS;
+ break;
+ }
+ if (!started_completion)
+ {
+ started_completion = TRUE;
+ first_match_pos = *complete_pos;
+ last_match_pos = *complete_pos;
+ }
+ else if (first_match_pos.lnum == last_match_pos.lnum &&
+ first_match_pos.col == last_match_pos.col)
+ {
+ /* We have found all the matches in this file */
+ temp = FAIL;
+ break;
+ }
+ ptr = ml_get_pos(complete_pos);
+ if (ctrl_x_mode == CTRL_X_WHOLE_LINE)
+ temp = STRLEN(ptr);
+ else
+ {
+ tmp_ptr = ptr;
+ temp = 0;
+ while (*tmp_ptr != NUL && iswordchar(*tmp_ptr++))
+ temp++;
+ }
+ if (add_completion_and_infercase(ptr, temp, NULL,
+ complete_direction) != FAIL)
+ {
+ temp = OK;
+ break;
+ }
+ }
+ p_scs = save_p_scs;
+ }
+ if (complete_pos != NULL && temp == FAIL)
+ {
+ int tot;
+
+ tot = count_completions(); /* Total num matches */
+ if (curr_match != NULL)
+ (void)make_cyclic();
+ if (tot > 1)
+ {
+ sprintf((char *)IObuff,
+ "All %d matches have now been found", tot);
+ mesg = IObuff;
+ }
+ else if (tot == 0)
+ quick_m = e_patnotf;
+ }
+
+ /* eat the ESC to avoid leaving insert mode */
+ if (got_int)
+ {
+ (void)vgetc();
+ got_int = FALSE;
+ }
+
+ /*
+ * When using match from another file, show the file name.
+ */
+ if (curr_match != NULL)
+ ptr = curr_match->str;
+ else /* back to what has been typed */
+ ptr = original_text;
+
+ if (curr_match == NULL || curr_match->original)
+ {
+ edit_submode_extra = (char_u *)"Back at original";
+ edit_submode_highl = TRUE;
+ }
+ else if (first_match != NULL && first_match->next != NULL &&
+ (first_match->next == first_match ||
+ first_match->next->original))
+ {
+ edit_submode_extra = (char_u *)"(the only match)";
+ edit_submode_highl = FALSE;
+ }
+
+ /*
+ * Use ins_char() to insert the text, it is a bit slower than
+ * ins_str(), but it takes care of replace mode.
+ */
+ if (ptr != NULL)
+ while (*ptr)
+ ins_char(*ptr++);
+
+ started_completion = TRUE;
+ need_redraw = TRUE;
+ (void)set_highlight('r');
+ msg_highlight = TRUE;
+ if (mesg != NULL)
+ {
+ msg(mesg);
+ mch_delay(1000L, FALSE);
+ }
+ else if (quick_m != NULL)
+ msg(quick_m);
+ else if (edit_submode_extra != NULL)
+ showmode();
+ edit_submode_extra = NULL;
+ msg_highlight = FALSE;
+
+ /*
+ * If there is a file name for the match, overwrite any
+ * previous message, it's more interesting to know where the
+ * match comes from, except when using the dictionary.
+ * Truncate the file name to avoid a wait for return.
+ */
+ if (curr_match != NULL && curr_match->fname != NULL &&
+ (ctrl_x_mode != CTRL_X_DICTIONARY ||
+ (mesg == NULL && quick_m == NULL)))
+ {
+ STRCPY(IObuff, "match in file ");
+ i = (strsize(curr_match->fname) + 16) - sc_col;
+ if (i <= 0)
+ i = 0;
+ else
+ STRCAT(IObuff, "<");
+ STRCAT(IObuff, curr_match->fname + i);
+ msg(IObuff);
+ }
+ break;
+#endif /* INSERT_EXPAND */
+
+ case Ctrl('Y'): /* copy from previous line */
+#ifdef INSERT_EXPAND
+ if (ctrl_x_mode == CTRL_X_SCROLL)
+ {
+ scrolldown_clamp();
+ updateScreen(VALID);
+ break;
+ }
+#endif /* INSERT_EXPAND */
+ lnum = curwin->w_cursor.lnum - 1;
+ goto copychar;
+
+ case Ctrl('E'): /* copy from next line */
+#ifdef INSERT_EXPAND
+ if (ctrl_x_mode == CTRL_X_SCROLL)
+ {
+ scrollup_clamp();
+ updateScreen(VALID);
+ break;
+ }
+#endif /* INSERT_EXPAND */
+ lnum = curwin->w_cursor.lnum + 1;
+copychar:
+ if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count)
+ {
+ beep_flush();
+ break;
+ }
+
+ /* try to advance to the cursor column */
+ temp = 0;
+ ptr = ml_get(lnum);
+ while ((colnr_t)temp < curwin->w_virtcol && *ptr)
+ temp += lbr_chartabsize(ptr++, (colnr_t)temp);
+
+ if ((colnr_t)temp > curwin->w_virtcol)
+ --ptr;
+ if ((c = *ptr) == NUL)
+ {
+ beep_flush();
+ break;
+ }
+
+ /*FALLTHROUGH*/
+ default:
+normalchar:
+ /*
+ * do some very smart indenting when entering '{' or '}'
+ */
+ if (((did_si || can_si_back) && c == '{') ||
+ (can_si && c == '}'))
+ {
+ FPOS *pos, old_pos;
+
+ /* for '}' set indent equal to indent of line
+ * containing matching '{'
+ */
+ if (c == '}' && (pos = findmatch('{')) != NULL)
+ {
+ old_pos = curwin->w_cursor;
+ /*
+ * If the matching '{' has a ')' immediately before it
+ * (ignoring white-space), then line up with the start
+ * of the line containing the matching '(' if there is
+ * one. This handles the case where an
+ * "if (..\n..) {" statement continues over multiple
+ * lines -- webb
+ */
+ ptr = ml_get(pos->lnum);
+ i = pos->col;
+ if (i > 0) /* skip blanks before '{' */
+ while (--i > 0 && vim_iswhite(ptr[i]))
+ ;
+ curwin->w_cursor.lnum = pos->lnum;
+ curwin->w_cursor.col = i;
+ if (ptr[i] == ')' && (pos = findmatch('(')) != NULL)
+ curwin->w_cursor = *pos;
+ i = get_indent();
+ curwin->w_cursor = old_pos;
+ set_indent(i, TRUE);
+ }
+ else if (curwin->w_cursor.col > 0)
+ {
+ /*
+ * when inserting '{' after "O" reduce indent, but not
+ * more than indent of previous line
+ */
+ temp = TRUE;
+ if (c == '{' && can_si_back &&
+ curwin->w_cursor.lnum > 1)
+ {
+ old_pos = curwin->w_cursor;
+ i = get_indent();
+ while (curwin->w_cursor.lnum > 1)
+ {
+ ptr = skipwhite(
+ ml_get(--(curwin->w_cursor.lnum)));
+ /* ignore empty lines and lines starting with
+ * '#'.
+ */
+ if (*ptr != '#' && *ptr != NUL)
+ break;
+ }
+ if (get_indent() >= i)
+ temp = FALSE;
+ curwin->w_cursor = old_pos;
+ }
+ if (temp)
+ shift_line(TRUE, FALSE, 1);
+ }
+ }
+ /* set indent of '#' always to 0 */
+ if (curwin->w_cursor.col > 0 && can_si && c == '#')
+ {
+ /* remember current indent for next line */
+ old_indent = get_indent();
+ set_indent(0, TRUE);
+ }
+
+ if (c == ' ')
+ {
+#ifdef CINDENT
+ if (inindent(0))
+ can_cindent = FALSE;
+#endif
+ if (Insstart_blank_vcol == MAXCOL &&
+ curwin->w_cursor.lnum == Insstart.lnum)
+ Insstart_blank_vcol = curwin->w_virtcol;
+ }
+
+ if (iswordchar(c) || !echeck_abbr(c))
+ {
+ insert_special(c, FALSE);
+ need_redraw = TRUE;
+#ifdef RIGHTLEFT
+ revinslegal++;
+ revinschars++;
+#endif
+ }
+ break;
+ } /* end of switch (c) */
+
+#ifdef CINDENT
+ if (curbuf->b_p_cin && can_cindent
+# ifdef INSERT_EXPAND
+ && !ctrl_x_mode
+# endif
+ )
+ {
+force_cindent:
+ /*
+ * Indent now if a key was typed that is in 'cinkeys'.
+ */
+ if (in_cinkeys(c, ' ', line_is_white))
+ {
+ stop_arrow();
+
+ /* re-indent the current line */
+ fixthisline(get_c_indent);
+
+ /* draw the changes on the screen later */
+ need_redraw = TRUE;
+ }
+ }
+#endif /* CINDENT */
+
+ } /* for (;;) */
+}
+
+/*
+ * Insert an indent (for <Tab> or CTRL-T) or delete an indent (for CTRL-D).
+ * Keep the cursor on the same character.
+ * type == INDENT_INC increase indent (for CTRL-T or <Tab>)
+ * type == INDENT_DEC decrease indent (for CTRL-D)
+ * type == INDENT_SET set indent to "amount"
+ * if round is TRUE, round the indent to 'shiftwidth' (only with _INC and _Dec).
+ */
+ static void
+change_indent(type, amount, round)
+ int type;
+ int amount;
+ int round;
+{
+ int vcol;
+ int last_vcol;
+ int insstart_less; /* reduction for Insstart.col */
+ int new_cursor_col;
+ int i;
+ char_u *ptr;
+ int save_p_list;
+
+ /* for the following tricks we don't want list mode */
+ save_p_list = curwin->w_p_list;
+ if (save_p_list)
+ {
+ curwin->w_p_list = FALSE;
+ curs_columns(FALSE); /* recompute w_virtcol */
+ }
+ vcol = curwin->w_virtcol;
+
+ /* determine offset from first non-blank */
+ new_cursor_col = curwin->w_cursor.col;
+ beginline(TRUE);
+ new_cursor_col -= curwin->w_cursor.col;
+
+ insstart_less = curwin->w_cursor.col;
+
+ /*
+ * If the cursor is in the indent, compute how many screen columns the
+ * cursor is to the left of the first non-blank.
+ */
+ if (new_cursor_col < 0)
+ vcol = get_indent() - vcol;
+
+ /*
+ * Set the new indent. The cursor will be put on the first non-blank.
+ */
+ if (type == INDENT_SET)
+ set_indent(amount, TRUE);
+ else
+ shift_line(type == INDENT_DEC, round, 1);
+ insstart_less -= curwin->w_cursor.col;
+
+ /*
+ * Try to put cursor on same character.
+ * If the cursor is at or after the first non-blank in the line,
+ * compute the cursor column relative to the column of the first
+ * non-blank character.
+ * If we are not in insert mode, leave the cursor on the first non-blank.
+ * If the cursor is before the first non-blank, position it relative
+ * to the first non-blank, counted in screen columns.
+ */
+ if (new_cursor_col >= 0)
+ new_cursor_col += curwin->w_cursor.col;
+ else if (!(State & INSERT))
+ new_cursor_col = curwin->w_cursor.col;
+ else
+ {
+ /*
+ * Compute the screen column where the cursor should be.
+ */
+ vcol = get_indent() - vcol;
+ curwin->w_virtcol = (vcol < 0) ? 0 : vcol;
+
+ /*
+ * Advance the cursor until we reach the right screen column.
+ */
+ vcol = last_vcol = 0;
+ new_cursor_col = -1;
+ ptr = ml_get_curline();
+ while (vcol <= (int)curwin->w_virtcol)
+ {
+ last_vcol = vcol;
+ ++new_cursor_col;
+ vcol += lbr_chartabsize(ptr + new_cursor_col, (colnr_t)vcol);
+ }
+ vcol = last_vcol;
+
+ /*
+ * May need to insert spaces to be able to position the cursor on
+ * the right screen column.
+ */
+ if (vcol != (int)curwin->w_virtcol)
+ {
+ curwin->w_cursor.col = new_cursor_col;
+ i = (int)curwin->w_virtcol - vcol;
+ ptr = alloc(i + 1);
+ if (ptr != NULL)
+ {
+ new_cursor_col += i;
+ ptr[i] = NUL;
+ while (--i >= 0)
+ ptr[i] = ' ';
+ ins_str(ptr);
+ vim_free(ptr);
+ }
+ }
+
+ /*
+ * When changing the indent while the cursor is in it, reset
+ * Insstart_col to 0.
+ */
+ insstart_less = Insstart.col;
+ }
+
+ curwin->w_p_list = save_p_list;
+
+ if (new_cursor_col <= 0)
+ curwin->w_cursor.col = 0;
+ else
+ curwin->w_cursor.col = new_cursor_col;
+ curwin->w_set_curswant = TRUE;
+
+ /*
+ * May have to adjust the start of the insert.
+ */
+ if ((State & INSERT) && curwin->w_cursor.lnum == Insstart.lnum &&
+ Insstart.col != 0)
+ {
+ if ((int)Insstart.col <= insstart_less)
+ Insstart.col = 0;
+ else
+ Insstart.col -= insstart_less;
+ }
+}
+
+#ifdef INSERT_EXPAND
+/*
+ * Is the character 'c' a valid key to keep us in the current ctrl-x mode?
+ * -- webb
+ */
+ int
+is_ctrl_x_key(c)
+ int c;
+{
+ switch (ctrl_x_mode)
+ {
+ case 0: /* Not in any ctrl-x mode */
+ break;
+ case CTRL_X_NOT_DEFINED_YET:
+ if (c == Ctrl('X') || c == Ctrl('Y') || c == Ctrl('E') ||
+ c == Ctrl('L') || c == Ctrl('F') || c == Ctrl(']') ||
+ c == Ctrl('I') || c == Ctrl('D') || c == Ctrl('P') ||
+ c == Ctrl('N'))
+ return TRUE;
+ break;
+ case CTRL_X_SCROLL:
+ if (c == Ctrl('Y') || c == Ctrl('E'))
+ return TRUE;
+ break;
+ case CTRL_X_WHOLE_LINE:
+ if (c == Ctrl('L') || c == Ctrl('P') || c == Ctrl('N'))
+ return TRUE;
+ break;
+ case CTRL_X_FILES:
+ if (c == Ctrl('F') || c == Ctrl('P') || c == Ctrl('N'))
+ return TRUE;
+ break;
+ case CTRL_X_DICTIONARY:
+ if (c == Ctrl('K') || c == Ctrl('P') || c == Ctrl('N'))
+ return TRUE;
+ break;
+ case CTRL_X_TAGS:
+ if (c == Ctrl(']') || c == Ctrl('P') || c == Ctrl('N'))
+ return TRUE;
+ break;
+ case CTRL_X_PATH_PATTERNS:
+ if (c == Ctrl('P') || c == Ctrl('N'))
+ return TRUE;
+ break;
+ case CTRL_X_PATH_DEFINES:
+ if (c == Ctrl('D') || c == Ctrl('P') || c == Ctrl('N'))
+ return TRUE;
+ break;
+ default:
+ emsg(e_internal);
+ break;
+ }
+ return FALSE;
+}
+
+/*
+ * This is like add_completion(), but if ic and inf are set, then the
+ * case of the originally typed text is used, and the case of the completed
+ * text is infered, ie this tries to work out what case you probably wanted
+ * the rest of the word to be in -- webb
+ */
+ int
+add_completion_and_infercase(str, len, fname, dir)
+ char_u *str;
+ int len;
+ char_u *fname;
+ int dir;
+{
+ int has_lower = FALSE;
+ int was_letter = FALSE;
+ int orig_len;
+ int idx;
+
+ if (p_ic && curbuf->b_p_inf && len < IOSIZE)
+ {
+ /* Infer case of completed part -- webb */
+ orig_len = STRLEN(original_text);
+
+ /* Use IObuff, str would change text in buffer! */
+ STRNCPY(IObuff, str, len);
+ IObuff[len] = NUL;
+
+ /* Rule 1: Were any chars converted to lower? */
+ for (idx = 0; idx < orig_len; ++idx)
+ {
+ if (islower(original_text[idx]))
+ {
+ has_lower = TRUE;
+ if (isupper(IObuff[idx]))
+ {
+ /* Rule 1 is satisfied */
+ for (idx = orig_len; idx < len; ++idx)
+ IObuff[idx] = TO_LOWER(IObuff[idx]);
+ break;
+ }
+ }
+ }
+
+ /*
+ * Rule 2: No lower case, 2nd consecutive letter converted to
+ * upper case.
+ */
+ if (!has_lower)
+ {
+ for (idx = 0; idx < orig_len; ++idx)
+ {
+ if (was_letter && isupper(original_text[idx]) &&
+ islower(IObuff[idx]))
+ {
+ /* Rule 2 is satisfied */
+ for (idx = orig_len; idx < len; ++idx)
+ IObuff[idx] = TO_UPPER(IObuff[idx]);
+ break;
+ }
+ was_letter = isalpha(original_text[idx]);
+ }
+ }
+
+ /* Copy the original case of the part we typed */
+ STRNCPY(IObuff, original_text, orig_len);
+
+ return add_completion(IObuff, len, fname, dir);
+ }
+ return add_completion(str, len, fname, dir);
+}
+
+/*
+ * Add a match to the list of matches.
+ * If the given string is already in the list of completions, then return
+ * FAIL, otherwise add it to the list and return OK. If there is an error,
+ * maybe because alloc returns NULL, then RET_ERROR is returned -- webb.
+ */
+ static int
+add_completion(str, len, fname, dir)
+ char_u *str;
+ int len;
+ char_u *fname;
+ int dir;
+{
+ struct Completion *match;
+
+ mch_breakcheck();
+ if (got_int)
+ return RET_ERROR;
+ if (len < 0)
+ len = STRLEN(str);
+
+ /*
+ * If the same match is already present, don't add it.
+ */
+ if (first_match != NULL)
+ {
+ match = first_match;
+ do
+ {
+ if (STRNCMP(match->str, str, (size_t)len) == 0 &&
+ match->str[len] == NUL)
+ return FAIL;
+ match = match->next;
+ } while (match != NULL && match != first_match);
+ }
+
+ /*
+ * Allocate a new match structure.
+ * Copy the values to the new match structure.
+ */
+ match = (struct Completion *)alloc((unsigned)sizeof(struct Completion));
+ if (match == NULL)
+ return RET_ERROR;
+ if ((match->str = strnsave(str, len)) == NULL)
+ {
+ vim_free(match);
+ return RET_ERROR;
+ }
+ if (fname != NULL)
+ match->fname = strsave(fname); /* ignore errors */
+ else
+ match->fname = NULL;
+ match->original = FALSE;
+
+ /*
+ * Link the new match structure in the list of matches.
+ */
+ if (first_match == NULL)
+ {
+ first_match = curr_match = match;
+ curr_match->next = curr_match->prev = NULL;
+ }
+ else
+ {
+ if (dir == FORWARD)
+ {
+ match->next = NULL;
+ match->prev = curr_match;
+ curr_match->next = match;
+ curr_match = match;
+ }
+ else /* BACKWARD */
+ {
+ match->prev = NULL;
+ match->next = curr_match;
+ curr_match->prev = match;
+ first_match = curr_match = match;
+ }
+ }
+
+ return OK;
+}
+
+/*
+ * Make the completion list cyclic. Add the original text at the end.
+ * Return the number of matches (excluding the original).
+ */
+ static int
+make_cyclic()
+{
+ struct Completion *match, *orig;
+ int count = 0;
+
+ if (first_match != NULL)
+ {
+ /*
+ * Find the end of the list.
+ */
+ match = first_match;
+ count = 1;
+ while (match->next != NULL)
+ {
+ match = match->next;
+ ++count;
+ }
+
+ if (original_text != NULL)
+ {
+ /*
+ * Allocate a new structure for the original text.
+ * Copy the original text to the new structure.
+ * Link it in the list at the end.
+ */
+ orig = (struct Completion *)alloc((unsigned)sizeof(
+ struct Completion));
+ if (orig != NULL)
+ {
+ if ((orig->str = strsave(original_text)) == NULL)
+ vim_free(orig);
+ else
+ {
+ orig->fname = NULL;
+ orig->original = TRUE;
+ orig->prev = match;
+ match->next = orig;
+ match = orig;
+ curr_match = orig;
+ }
+ }
+ }
+ match->next = first_match;
+ first_match->prev = match;
+ }
+ return count;
+}
+
+/*
+ * Add any identifiers that match the given pattern to the list of
+ * completions.
+ */
+ static void
+complete_dictionaries(pat, dir)
+ char_u *pat;
+ int dir;
+{
+ struct Completion *save_curr_match = curr_match;
+ char_u *dict = p_dict;
+ char_u *ptr;
+ char_u *buf;
+ char_u *fname;
+ int at_start;
+ FILE *fp;
+ struct regexp *prog = NULL;
+
+ if ((buf = alloc(LSIZE)) == NULL)
+ return;
+ if (curr_match != NULL)
+ {
+ while (curr_match->next != NULL)
+ curr_match = curr_match->next;
+ }
+ if (*dict != NUL)
+ {
+ (void)set_highlight('r');
+ msg_highlight = TRUE;
+ MSG("Please wait, searching dictionaries");
+ set_reg_ic(pat); /* set reg_ic according to p_ic, p_scs and pat */
+ reg_magic = p_magic;
+ prog = vim_regcomp(pat);
+ }
+ while (*dict != NUL && prog != NULL && !got_int)
+ {
+ /* copy one dictionary file name into buf */
+ (void)copy_option_part(&dict, buf, LSIZE, ",");
+
+ fp = fopen((char *)buf, "r"); /* open dictionary file */
+
+ if (fp != NULL)
+ {
+ fname = strsave(buf); /* keep name of file */
+ /*
+ * Read dictionary file line by line.
+ * Check each line for a match.
+ */
+ while (!got_int && !vim_fgets(buf, LSIZE, fp))
+ {
+ ptr = buf;
+ at_start = TRUE;
+ while (vim_regexec(prog, ptr, at_start))
+ {
+ at_start = FALSE;
+ ptr = prog->startp[0];
+ while (iswordchar(*ptr))
+ ++ptr;
+ if (add_completion_and_infercase(prog->startp[0],
+ (int)(ptr - prog->startp[0]), fname, FORWARD)
+ == RET_ERROR)
+ break;
+ }
+ line_breakcheck();
+ }
+ fclose(fp);
+ vim_free(fname);
+ }
+ }
+ vim_free(prog);
+ if (save_curr_match != NULL)
+ curr_match = save_curr_match;
+ else if (dir == BACKWARD)
+ curr_match = first_match;
+ vim_free(buf);
+}
+
+/*
+ * Free the list of completions
+ */
+ static void
+free_completions()
+{
+ struct Completion *match;
+
+ if (first_match == NULL)
+ return;
+ curr_match = first_match;
+ do
+ {
+ match = curr_match;
+ curr_match = curr_match->next;
+ vim_free(match->str);
+ vim_free(match->fname);
+ vim_free(match);
+ } while (curr_match != NULL && curr_match != first_match);
+ first_match = curr_match = NULL;
+}
+
+/*
+ * Return the number of items in the Completion list
+ */
+ static int
+count_completions()
+{
+ struct Completion *match;
+ int num = 0;
+
+ if (first_match == NULL)
+ return 0;
+ match = first_match;
+ do
+ {
+ if (!match->original) /* original string doesn't count */
+ num++;
+ match = match->next;
+ } while (match != NULL && match != first_match);
+ return num;
+}
+#endif /* INSERT_EXPAND */
+
+/*
+ * Next character is interpreted literally.
+ * A one, two or three digit decimal number is interpreted as its byte value.
+ * If one or two digits are entered, the next character is given to vungetc().
+ */
+ int
+get_literal()
+{
+ int cc;
+ int nc;
+ int i;
+
+ if (got_int)
+ return Ctrl('C');
+
+#ifdef USE_GUI
+ /*
+ * In GUI there is no point inserting the internal code for a special key.
+ * It is more useful to insert the string "<KEY>" instead. This would
+ * probably be useful in a text window too, but it would not be
+ * vi-compatible (maybe there should be an option for it?) -- webb
+ */
+ if (gui.in_use)
+ ++allow_keys;
+#endif
+ ++no_mapping; /* don't map the next key hits */
+ cc = 0;
+ for (i = 0; i < 3; ++i)
+ {
+ do
+ nc = vgetc();
+ while (nc == K_IGNORE || nc == K_SCROLLBAR || nc == K_HORIZ_SCROLLBAR);
+ if (!(State & CMDLINE))
+ add_to_showcmd(nc, FALSE);
+ if (IS_SPECIAL(nc) || !isdigit(nc))
+ break;
+ cc = cc * 10 + nc - '0';
+ if (cc > 255)
+ cc = 255; /* limit range to 0-255 */
+ nc = 0;
+ }
+ if (i == 0) /* no number entered */
+ {
+ if (nc == K_ZERO) /* NUL is stored as NL */
+ {
+ cc = '\n';
+ nc = 0;
+ }
+ else
+ {
+ cc = nc;
+ nc = 0;
+ }
+ }
+
+ if (cc == 0) /* NUL is stored as NL */
+ cc = '\n';
+
+ --no_mapping;
+#ifdef USE_GUI
+ if (gui.in_use)
+ --allow_keys;
+#endif
+ if (nc)
+ vungetc(nc);
+ got_int = FALSE; /* CTRL-C typed after CTRL-V is not an interrupt */
+ return cc;
+}
+
+/*
+ * Insert character, taking care of special keys and mod_mask
+ */
+ static void
+insert_special(c, allow_modmask)
+ int c;
+ int allow_modmask;
+{
+ char_u *p;
+ int len;
+
+ /*
+ * Special function key, translate into "<Key>". Up to the last '>' is
+ * inserted with ins_str(), so as not to replace characters in replace
+ * mode.
+ * Only use mod_mask for special keys, to avoid things like <S-Space>,
+ * unless 'allow_modmask' is TRUE.
+ */
+ if (IS_SPECIAL(c) || (mod_mask && allow_modmask))
+ {
+ p = get_special_key_name(c, mod_mask);
+ len = STRLEN(p);
+ c = p[len - 1];
+ if (len > 2)
+ {
+ p[len - 1] = NUL;
+ ins_str(p);
+ AppendToRedobuff(p);
+ }
+ }
+ insertchar(c, FALSE, -1);
+}
+
+/*
+ * Special characters in this context are those that need processing other
+ * than the simple insertion that can be performed here. This includes ESC
+ * which terminates the insert, and CR/NL which need special processing to
+ * open up a new line. This routine tries to optimize insertions performed by
+ * the "redo", "undo" or "put" commands, so it needs to know when it should
+ * stop and defer processing to the "normal" mechanism.
+ */
+#define ISSPECIAL(c) ((c) < ' ' || (c) >= DEL)
+
+ void
+insertchar(c, force_formatting, second_indent)
+ unsigned c;
+ int force_formatting; /* format line regardless of p_fo */
+ int second_indent; /* indent for second line if >= 0 */
+{
+ int haveto_redraw = FALSE;
+ int textwidth;
+ colnr_t leader_len;
+ int first_line = TRUE;
+ int fo_ins_blank;
+ int save_char = NUL;
+
+ stop_arrow();
+
+ /*
+ * find out textwidth to be used:
+ * if 'textwidth' option is set, use it
+ * else if 'wrapmargin' option is set, use Columns - 'wrapmargin'
+ * if invalid value, use 0.
+ * Set default to window width (maximum 79) for "Q" command.
+ */
+ textwidth = curbuf->b_p_tw;
+ if (textwidth == 0 && curbuf->b_p_wm)
+ textwidth = Columns - curbuf->b_p_wm;
+ if (textwidth < 0)
+ textwidth = 0;
+ if (force_formatting && textwidth == 0)
+ {
+ textwidth = Columns - 1;
+ if (textwidth > 79)
+ textwidth = 79;
+ }
+
+ fo_ins_blank = has_format_option(FO_INS_BLANK);
+
+ /*
+ * Try to break the line in two or more pieces when:
+ * - Always do this if we have been called to do formatting only.
+ * - Otherwise:
+ * - Don't do this if inserting a blank
+ * - Don't do this if an existing character is being replaced.
+ * - Do this if the cursor is not on the line where insert started
+ * or - 'formatoptions' doesn't have 'l' or the line was not too long
+ * before the insert.
+ * - 'formatoptions' doesn't have 'b' or a blank was inserted at or
+ * before 'textwidth'
+ */
+ if (force_formatting || (!vim_iswhite(c) &&
+ !(State == REPLACE && *ml_get_cursor() != NUL) &&
+ (curwin->w_cursor.lnum != Insstart.lnum ||
+ ((!has_format_option(FO_INS_LONG) ||
+ Insstart_textlen <= (colnr_t)textwidth) &&
+ (!fo_ins_blank || Insstart_blank_vcol <= (colnr_t)textwidth)))))
+ {
+ /*
+ * When 'ai' is off we don't want a space under the cursor to be
+ * deleted. Replace it with an 'x' temporarily.
+ */
+ if (!curbuf->b_p_ai && vim_iswhite(gchar_cursor()))
+ {
+ save_char = gchar_cursor();
+ pchar_cursor('x');
+ }
+ while (textwidth && curwin->w_virtcol >= (colnr_t)textwidth)
+ {
+ int startcol; /* Cursor column at entry */
+ int wantcol; /* column at textwidth border */
+ int foundcol; /* column for start of spaces */
+ int end_foundcol = 0; /* column for start of word */
+ colnr_t len;
+
+ if (!force_formatting && has_format_option(FO_WRAP_COMS))
+ fo_do_comments = TRUE;
+
+ /* Don't break until after the comment leader */
+ leader_len = get_leader_len(ml_get_curline(), NULL);
+ if (!force_formatting && leader_len == 0 &&
+ !has_format_option(FO_WRAP))
+
+ {
+ textwidth = 0;
+ break;
+ }
+ if ((startcol = curwin->w_cursor.col) == 0)
+ break;
+ /* find column of textwidth border */
+ coladvance((colnr_t)textwidth);
+ wantcol = curwin->w_cursor.col;
+
+ curwin->w_cursor.col = startcol - 1;
+ foundcol = 0;
+ /*
+ * Find position to break at.
+ * Stop at start of line.
+ * Stop at first entered white when 'formatoptions' has 'v'
+ */
+ while (curwin->w_cursor.col > 0 &&
+ ((!fo_ins_blank && !has_format_option(FO_INS_VI)) ||
+ curwin->w_cursor.lnum != Insstart.lnum ||
+ curwin->w_cursor.col >= Insstart.col))
+ {
+ if (vim_iswhite(gchar_cursor()))
+ {
+ /* remember position of blank just before text */
+ end_foundcol = curwin->w_cursor.col;
+ while (curwin->w_cursor.col > 0 &&
+ vim_iswhite(gchar_cursor()))
+ --curwin->w_cursor.col;
+ if (curwin->w_cursor.col == 0 &&
+ vim_iswhite(gchar_cursor()))
+ break; /* only spaces in front of text */
+ /* Don't break until after the comment leader */
+ if (curwin->w_cursor.col < leader_len)
+ break;
+ foundcol = curwin->w_cursor.col + 1;
+ if (curwin->w_cursor.col < (colnr_t)wantcol)
+ break;
+ }
+ --curwin->w_cursor.col;
+ }
+
+ if (foundcol == 0) /* no spaces, cannot break line */
+ {
+ curwin->w_cursor.col = startcol;
+ break;
+ }
+
+ /*
+ * offset between cursor position and line break is used by
+ * replace stack functions
+ */
+ replace_offset = startcol - end_foundcol - 1;
+
+ /*
+ * adjust startcol for spaces that will be deleted and
+ * characters that will remain on top line
+ */
+ curwin->w_cursor.col = foundcol;
+ while (vim_iswhite(gchar_cursor()))
+ {
+ ++curwin->w_cursor.col;
+ --startcol;
+ }
+ startcol -= foundcol;
+ if (startcol < 0)
+ startcol = 0;
+
+ /* put cursor after pos. to break line */
+ curwin->w_cursor.col = foundcol;
+
+ Opencmd(FORWARD, FALSE, TRUE);
+
+ replace_offset = 0;
+ if (second_indent >= 0 && first_line)
+ set_indent(second_indent, TRUE);
+ first_line = FALSE;
+
+ /*
+ * check if cursor is not past the NUL off the line, cindent may
+ * have added or removed indent.
+ */
+ curwin->w_cursor.col += startcol;
+ len = STRLEN(ml_get_curline());
+ if (curwin->w_cursor.col > len)
+ curwin->w_cursor.col = len;
+
+ curs_columns(FALSE); /* update curwin->w_virtcol */
+ haveto_redraw = TRUE;
+#ifdef CINDENT
+ can_cindent = TRUE;
+#endif
+ }
+
+ if (save_char) /* put back space after cursor */
+ pchar_cursor(save_char);
+
+ if (c == NUL) /* formatting only */
+ return;
+ fo_do_comments = FALSE;
+ if (haveto_redraw)
+ {
+ /*
+ * If the cursor ended up just below the screen we scroll up here
+ * to avoid a redraw of the whole screen in the most common cases.
+ */
+ if (curwin->w_cursor.lnum == curwin->w_botline &&
+ !curwin->w_empty_rows)
+ win_del_lines(curwin, 0, 1, TRUE, TRUE);
+ updateScreen(CURSUPD);
+ }
+ }
+ if (c == NUL) /* only formatting was wanted */
+ return;
+
+ did_ai = FALSE;
+ did_si = FALSE;
+ can_si = FALSE;
+ can_si_back = FALSE;
+
+ /*
+ * If there's any pending input, grab up to INPUT_BUFLEN at once.
+ * This speeds up normal text input considerably.
+ */
+#define INPUT_BUFLEN 100
+ if (!ISSPECIAL(c) && vpeekc() != NUL && State != REPLACE
+#ifdef RIGHTLEFT
+ && !p_ri
+#endif
+ )
+ {
+ char_u p[INPUT_BUFLEN + 1];
+ int i;
+
+ p[0] = c;
+ i = 1;
+ while ((c = vpeekc()) != NUL && !ISSPECIAL(c) && i < INPUT_BUFLEN &&
+ (textwidth == 0 ||
+ (curwin->w_virtcol += charsize(p[i - 1])) < (colnr_t)textwidth) &&
+ !(!no_abbr && !iswordchar(c) && iswordchar(p[i - 1])))
+ {
+#ifdef RIGHTLEFT
+ c = vgetc();
+ if (p_hkmap && KeyTyped)
+ c = hkmap(c); /* Hebrew mode mapping */
+ p[i++] = c;
+#else
+ p[i++] = vgetc();
+#endif
+ }
+
+#ifdef DIGRAPHS
+ do_digraph(-1); /* clear digraphs */
+ do_digraph(p[i-1]); /* may be the start of a digraph */
+#endif
+ p[i] = '\0';
+ ins_str(p);
+ AppendToRedobuff(p);
+ }
+ else
+ {
+ ins_char(c);
+ AppendCharToRedobuff(c);
+ }
+}
+
+/*
+ * start_arrow() is called when an arrow key is used in insert mode.
+ * It resembles hitting the <ESC> key.
+ */
+ static void
+start_arrow(end_insert_pos)
+ FPOS *end_insert_pos;
+{
+ if (!arrow_used) /* something has been inserted */
+ {
+ AppendToRedobuff(ESC_STR);
+ arrow_used = TRUE; /* this means we stopped the current insert */
+ stop_insert(end_insert_pos);
+ }
+}
+
+/*
+ * stop_arrow() is called before a change is made in insert mode.
+ * If an arrow key has been used, start a new insertion.
+ */
+ static void
+stop_arrow()
+{
+ if (arrow_used)
+ {
+ (void)u_save_cursor(); /* errors are ignored! */
+ Insstart = curwin->w_cursor; /* new insertion starts here */
+ Insstart_textlen = linetabsize(ml_get_curline());
+ ResetRedobuff();
+ AppendToRedobuff((char_u *)"1i"); /* pretend we start an insertion */
+ arrow_used = FALSE;
+ }
+}
+
+/*
+ * do a few things to stop inserting
+ */
+ static void
+stop_insert(end_insert_pos)
+ FPOS *end_insert_pos; /* where insert ended */
+{
+ stop_redo_ins();
+
+ /*
+ * save the inserted text for later redo with ^@
+ */
+ vim_free(last_insert);
+ last_insert = get_inserted();
+ last_insert_skip = new_insert_skip;
+
+ /*
+ * If we just did an auto-indent, remove the white space from the end of
+ * the line, and put the cursor back.
+ */
+ if (did_ai && !arrow_used)
+ {
+ if (gchar_cursor() == NUL && curwin->w_cursor.col > 0)
+ --curwin->w_cursor.col;
+ while (vim_iswhite(gchar_cursor()))
+ delchar(TRUE);
+ if (gchar_cursor() != NUL)
+ ++curwin->w_cursor.col; /* put cursor back on the NUL */
+ if (curwin->w_p_list) /* the deletion is only seen in list
+ * mode */
+ updateline();
+ }
+ did_ai = FALSE;
+ did_si = FALSE;
+ can_si = FALSE;
+ can_si_back = FALSE;
+
+ /* set '[ and '] to the inserted text */
+ curbuf->b_op_start = Insstart;
+ curbuf->b_op_end = *end_insert_pos;
+}
+
+/*
+ * Set the last inserted text to a single character.
+ * Used for the replace command.
+ */
+ void
+set_last_insert(c)
+ int c;
+{
+ vim_free(last_insert);
+ last_insert = alloc(4);
+ if (last_insert != NULL)
+ {
+ last_insert[0] = Ctrl('V');
+ last_insert[1] = c;
+ last_insert[2] = ESC;
+ last_insert[3] = NUL;
+ /* Use the CTRL-V only when not entering a digit */
+ last_insert_skip = isdigit(c) ? 1 : 0;
+ }
+}
+
+/*
+ * move cursor to start of line
+ * if flag == TRUE move to first non-white
+ * if flag == MAYBE then move to first non-white if startofline is set,
+ * otherwise don't move at all.
+ */
+ void
+beginline(flag)
+ int flag;
+{
+ if (flag == MAYBE && !p_sol)
+ coladvance(curwin->w_curswant);
+ else
+ {
+ curwin->w_cursor.col = 0;
+ if (flag)
+ {
+ register char_u *ptr;
+
+ for (ptr = ml_get_curline(); vim_iswhite(*ptr); ++ptr)
+ ++curwin->w_cursor.col;
+ }
+ curwin->w_set_curswant = TRUE;
+ }
+}
+
+/*
+ * oneright oneleft cursor_down cursor_up
+ *
+ * Move one char {right,left,down,up}.
+ * Return OK when sucessful, FAIL when we hit a line of file boundary.
+ */
+
+ int
+oneright()
+{
+ char_u *ptr;
+
+ ptr = ml_get_cursor();
+ if (*ptr++ == NUL || *ptr == NUL)
+ return FAIL;
+ curwin->w_set_curswant = TRUE;
+ ++curwin->w_cursor.col;
+ return OK;
+}
+
+ int
+oneleft()
+{
+ if (curwin->w_cursor.col == 0)
+ return FAIL;
+ curwin->w_set_curswant = TRUE;
+ --curwin->w_cursor.col;
+ return OK;
+}
+
+ int
+cursor_up(n)
+ long n;
+{
+ if (n != 0 && curwin->w_cursor.lnum == 1)
+ return FAIL;
+ if (n >= curwin->w_cursor.lnum)
+ curwin->w_cursor.lnum = 1;
+ else
+ curwin->w_cursor.lnum -= n;
+
+ /* try to advance to the column we want to be at */
+ coladvance(curwin->w_curswant);
+
+ if (op_type == NOP)
+ cursupdate(); /* make sure curwin->w_topline is valid */
+
+ return OK;
+}
+
+ int
+cursor_down(n)
+ long n;
+{
+ if (n != 0 && curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
+ return FAIL;
+ curwin->w_cursor.lnum += n;
+ if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
+ curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
+
+ /* try to advance to the column we want to be at */
+ coladvance(curwin->w_curswant);
+
+ if (op_type == NOP)
+ cursupdate(); /* make sure curwin->w_topline is valid */
+
+ return OK;
+}
+
+/*
+ * screengo() --
+ *
+ * move 'dist' lines in direction 'dir', counting lines by *screen*
+ * lines rather than lines in the file
+ * 'dist' must be positive.
+ *
+ * return OK if able to move cursor, FAIL otherwise.
+ */
+
+ int
+screengo(dir, dist)
+ int dir;
+ long dist;
+{
+ int linelen = linetabsize(ml_get_curline());
+ int retval = OK;
+ int atend = FALSE;
+ int n;
+
+ op_motion_type = MCHAR;
+ op_inclusive = FALSE;
+
+ /*
+ * Instead of sticking at the last character of the line in the file we
+ * try to stick in the last column of the screen
+ */
+ if (curwin->w_curswant == MAXCOL)
+ {
+ atend = TRUE;
+ curwin->w_curswant = ((curwin->w_virtcol +
+ (curwin->w_p_nu ? 8 : 0)) / Columns + 1) * Columns - 1;
+ if (curwin->w_p_nu && curwin->w_curswant > 8)
+ curwin->w_curswant -= 8;
+ }
+ else
+ while (curwin->w_curswant >= (colnr_t)(linelen + Columns))
+ curwin->w_curswant -= Columns;
+
+ while (dist--)
+ {
+ if (dir == BACKWARD)
+ {
+ /* move back within line */
+ if ((long)curwin->w_curswant >= Columns)
+ curwin->w_curswant -= Columns;
+ else /* to previous line */
+ {
+ if (curwin->w_cursor.lnum == 1)
+ {
+ retval = FAIL;
+ break;
+ }
+ --curwin->w_cursor.lnum;
+ linelen = linetabsize(ml_get_curline());
+ n = ((linelen + (curwin->w_p_nu ? 8 : 0) - 1) / Columns)
+ * Columns;
+ if (curwin->w_p_nu &&
+ (long)curwin->w_curswant >= Columns - 8 && n)
+ n -= Columns;
+ curwin->w_curswant += n;
+ }
+ }
+ else /* dir == FORWARD */
+ {
+ n = ((linelen + (curwin->w_p_nu ? 8 : 0) - 1) / Columns) * Columns;
+ if (curwin->w_p_nu && n > 8)
+ n -= 8;
+ /* move forward within line */
+ if (curwin->w_curswant < (colnr_t)n)
+ curwin->w_curswant += Columns;
+ else /* to next line */
+ {
+ if (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
+ {
+ retval = FAIL;
+ break;
+ }
+ curwin->w_cursor.lnum++;
+ linelen = linetabsize(ml_get_curline());
+ curwin->w_curswant %= Columns;
+ }
+ }
+ }
+ coladvance(curwin->w_curswant);
+ if (atend)
+ curwin->w_curswant = MAXCOL; /* stick in the last column */
+ if (op_type == NOP)
+ cursupdate();
+ return retval;
+}
+
+/*
+ * move screen 'count' pages up or down and update screen
+ *
+ * return FAIL for failure, OK otherwise
+ */
+ int
+onepage(dir, count)
+ int dir;
+ long count;
+{
+ linenr_t lp;
+ long n;
+ int off;
+
+ if (curbuf->b_ml.ml_line_count == 1) /* nothing to do */
+ return FAIL;
+ for ( ; count > 0; --count)
+ {
+ if (dir == FORWARD ? (curwin->w_topline >=
+ curbuf->b_ml.ml_line_count - 1) : (curwin->w_topline == 1))
+ {
+ beep_flush();
+ return FAIL;
+ }
+ if (dir == FORWARD)
+ {
+ /* at end of file */
+ if (curwin->w_botline > curbuf->b_ml.ml_line_count)
+ curwin->w_topline = curbuf->b_ml.ml_line_count;
+ /* next line is big */
+ /* or just three lines on screen */
+ else
+ {
+ if (plines(curwin->w_botline) >= curwin->w_height - 2 ||
+ curwin->w_botline - curwin->w_topline <= 3)
+ off = 0;
+ else
+ off = 2;
+ curwin->w_topline = curwin->w_botline - off;
+ curwin->w_cursor.lnum = curwin->w_topline;
+ }
+ comp_Botline(curwin);
+ }
+ else /* dir == BACKWARDS */
+ {
+ lp = curwin->w_topline;
+ /*
+ * If the first two lines on the screen are not too big, we keep
+ * them on the screen.
+ */
+ if ((n = plines(lp)) > curwin->w_height / 2)
+ --lp;
+ else if (lp < curbuf->b_ml.ml_line_count &&
+ n + plines(lp + 1) < curwin->w_height / 2)
+ ++lp;
+ curwin->w_cursor.lnum = lp;
+ n = 0;
+ while (n <= curwin->w_height && lp >= 1)
+ {
+ n += plines(lp);
+ --lp;
+ }
+ if (n <= curwin->w_height) /* at begin of file */
+ {
+ curwin->w_topline = 1;
+ comp_Botline(curwin);
+ }
+ else if (lp >= curwin->w_topline - 2) /* very long lines */
+ {
+ --curwin->w_topline;
+ comp_Botline(curwin);
+ curwin->w_cursor.lnum = curwin->w_botline - 1;
+ }
+ else
+ {
+ curwin->w_topline = lp + 2;
+ comp_Botline(curwin);
+ }
+ }
+ }
+ cursor_correct();
+ beginline(MAYBE);
+ updateScreen(VALID);
+ return OK;
+}
+
+/* #define KEEP_SCREEN_LINE */
+
+ void
+halfpage(flag, Prenum)
+ int flag;
+ linenr_t Prenum;
+{
+ long scrolled = 0;
+ int i;
+ int n;
+
+ if (Prenum)
+ curwin->w_p_scroll = (Prenum > curwin->w_height) ?
+ curwin->w_height : Prenum;
+ n = (curwin->w_p_scroll <= curwin->w_height) ?
+ curwin->w_p_scroll : curwin->w_height;
+
+ if (flag) /* scroll down */
+ {
+ while (n > 0 && curwin->w_botline <= curbuf->b_ml.ml_line_count)
+ {
+ i = plines(curwin->w_topline);
+ n -= i;
+ if (n < 0 && scrolled)
+ break;
+ scrolled += i;
+ ++curwin->w_topline;
+ comp_Botline(curwin); /* compute curwin->w_botline */
+#ifndef KEEP_SCREEN_LINE
+ if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
+ ++curwin->w_cursor.lnum;
+#endif
+ }
+#ifndef KEEP_SCREEN_LINE
+ /*
+ * When hit bottom of the file: move cursor down.
+ */
+ if (n > 0)
+ {
+ curwin->w_cursor.lnum += n;
+ if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
+ curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
+ }
+#else
+ /* try to put the cursor in the same screen line */
+ while ((curwin->w_cursor.lnum < curwin->w_topline || scrolled > 0)
+ && curwin->w_cursor.lnum < curwin->w_botline - 1)
+ {
+ scrolled -= plines(curwin->w_cursor.lnum);
+ if (scrolled < 0 && curwin->w_cursor.lnum >= curwin->w_topline)
+ break;
+ ++curwin->w_cursor.lnum;
+ }
+#endif
+ }
+ else /* scroll up */
+ {
+ while (n > 0 && curwin->w_topline > 1)
+ {
+ i = plines(curwin->w_topline - 1);
+ n -= i;
+ if (n < 0 && scrolled)
+ break;
+ scrolled += i;
+ --curwin->w_topline;
+#ifndef KEEP_SCREEN_LINE
+ if (curwin->w_cursor.lnum > 1)
+ --curwin->w_cursor.lnum;
+#endif
+ }
+ comp_Botline(curwin); /* compute curwin->w_botline */
+#ifndef KEEP_SCREEN_LINE
+ /*
+ * When hit top of the file: move cursor up.
+ */
+ if (n > 0)
+ {
+ if (curwin->w_cursor.lnum > (linenr_t)n)
+ curwin->w_cursor.lnum -= n;
+ else
+ curwin->w_cursor.lnum = 1;
+ }
+#else
+ /* try to put the cursor in the same screen line */
+ scrolled += n; /* move cursor when topline is 1 */
+ while (curwin->w_cursor.lnum > curwin->w_topline &&
+ (scrolled > 0 || curwin->w_cursor.lnum >= curwin->w_botline))
+ {
+ scrolled -= plines(curwin->w_cursor.lnum - 1);
+ if (scrolled < 0 && curwin->w_cursor.lnum < curwin->w_botline)
+ break;
+ --curwin->w_cursor.lnum;
+ }
+#endif
+ }
+ cursor_correct();
+ beginline(MAYBE);
+ updateScreen(VALID);
+}
+
+/*
+ * Stuff the last inserted text in the read buffer.
+ * Last_insert actually is a copy of the redo buffer, so we
+ * first have to remove the command.
+ */
+ int
+stuff_inserted(c, count, no_esc)
+ int c;
+ long count;
+ int no_esc;
+{
+ char_u *esc_ptr = NULL;
+ char_u *ptr;
+
+ ptr = get_last_insert();
+ if (ptr == NULL)
+ {
+ EMSG(e_noinstext);
+ return FAIL;
+ }
+
+ if (c)
+ stuffcharReadbuff(c);
+ if (no_esc && (esc_ptr = (char_u *)vim_strrchr(ptr, 27)) != NULL)
+ *esc_ptr = NUL; /* remove the ESC */
+
+ do
+ stuffReadbuff(ptr);
+ while (--count > 0);
+
+ if (esc_ptr != NULL)
+ *esc_ptr = 27; /* put the ESC back */
+
+ return OK;
+}
+
+ char_u *
+get_last_insert()
+{
+ if (last_insert == NULL)
+ return NULL;
+ return last_insert + last_insert_skip;
+}
+
+/*
+ * Check the word in front of the cursor for an abbreviation.
+ * Called when the non-id character "c" has been entered.
+ * When an abbreviation is recognized it is removed from the text and
+ * the replacement string is inserted in typebuf[], followed by "c".
+ */
+ static int
+echeck_abbr(c)
+ int c;
+{
+ if (p_paste || no_abbr) /* no abbreviations or in paste mode */
+ return FALSE;
+
+ return check_abbr(c, ml_get_curline(), curwin->w_cursor.col,
+ curwin->w_cursor.lnum == Insstart.lnum ? Insstart.col : 0);
+}
+
+/*
+ * replace-stack functions
+ *
+ * When replacing characters the replaced character is remembered
+ * for each new character. This is used to re-insert the old text
+ * when backspacing.
+ *
+ * replace_offset is normally 0, in which case replace_push will add a new
+ * character at the end of the stack. If replace_offset is not 0, that many
+ * characters will be left on the stack above the newly inserted character.
+ */
+
+char_u *replace_stack = NULL;
+long replace_stack_nr = 0; /* next entry in replace stack */
+long replace_stack_len = 0; /* max. number of entries */
+
+ void
+replace_push(c)
+ int c; /* character that is replaced (NUL is none) */
+{
+ char_u *p;
+
+ if (replace_stack_nr < replace_offset) /* nothing to do */
+ return;
+ if (replace_stack_len <= replace_stack_nr)
+ {
+ replace_stack_len += 50;
+ p = lalloc(sizeof(char_u) * replace_stack_len, TRUE);
+ if (p == NULL) /* out of memory */
+ {
+ replace_stack_len -= 50;
+ return;
+ }
+ if (replace_stack != NULL)
+ {
+ vim_memmove(p, replace_stack,
+ (size_t)(replace_stack_nr * sizeof(char_u)));
+ vim_free(replace_stack);
+ }
+ replace_stack = p;
+ }
+ p = replace_stack + replace_stack_nr - replace_offset;
+ if (replace_offset)
+ vim_memmove(p + 1, p, (size_t)(replace_offset * sizeof(char_u)));
+ *p = c;
+ ++replace_stack_nr;
+}
+
+/*
+ * pop one item from the replace stack
+ * return -1 if stack empty
+ * return 0 if no character was replaced
+ * return replaced character otherwise
+ */
+ int
+replace_pop()
+{
+ if (replace_stack_nr == 0)
+ return -1;
+ return (int)replace_stack[--replace_stack_nr];
+}
+
+/*
+ * make the replace stack empty
+ * (called when exiting replace mode)
+ */
+ void
+replace_flush()
+{
+ vim_free(replace_stack);
+ replace_stack = NULL;
+ replace_stack_len = 0;
+ replace_stack_nr = 0;
+}
+
+#if defined(LISPINDENT) || defined(CINDENT)
+/*
+ * Re-indent the current line, based on the current contents of it and the
+ * surrounding lines. Fixing the cursor position seems really easy -- I'm very
+ * confused what all the part that handles Control-T is doing that I'm not.
+ * "get_the_indent" should be get_c_indent or get_lisp_indent.
+ */
+
+ void
+fixthisline(get_the_indent)
+ int (*get_the_indent) __ARGS((void));
+{
+ change_indent(INDENT_SET, get_the_indent(), FALSE);
+ if (linewhite(curwin->w_cursor.lnum))
+ did_ai = TRUE; /* delete the indent if the line stays empty */
+}
+#endif /* defined(LISPINDENT) || defined(CINDENT) */
+
+#ifdef CINDENT
+/*
+ * return TRUE if 'cinkeys' contains the key "keytyped",
+ * when == '*': Only if key is preceded with '*' (indent before insert)
+ * when == '!': Only if key is prededed with '!' (don't insert)
+ * when == ' ': Only if key is not preceded with '*'(indent afterwards)
+ *
+ * If line_is_empty is TRUE accept keys with '0' before them.
+ */
+ int
+in_cinkeys(keytyped, when, line_is_empty)
+ int keytyped;
+ int when;
+ int line_is_empty;
+{
+ char_u *look;
+ int try_match;
+ char_u *p;
+
+ for (look = curbuf->b_p_cink; *look; )
+ {
+ /*
+ * Find out if we want to try a match with this key, depending on
+ * 'when' and a '*' or '!' before the key.
+ */
+ switch (when)
+ {
+ case '*': try_match = (*look == '*'); break;
+ case '!': try_match = (*look == '!'); break;
+ default: try_match = (*look != '*'); break;
+ }
+ if (*look == '*' || *look == '!')
+ ++look;
+
+ /*
+ * If there is a '0', only accept a match if the line is empty.
+ */
+ if (*look == '0')
+ {
+ if (!line_is_empty)
+ try_match = FALSE;
+ ++look;
+ }
+
+ /*
+ * does it look like a control character?
+ */
+ if (*look == '^' && look[1] >= '@' && look[1] <= '_')
+ {
+ if (try_match && keytyped == Ctrl(look[1]))
+ return TRUE;
+ look += 2;
+ }
+ /*
+ * 'o' means "o" command, open forward.
+ * 'O' means "O" command, open backward.
+ */
+ else if (*look == 'o')
+ {
+ if (try_match && keytyped == KEY_OPEN_FORW)
+ return TRUE;
+ ++look;
+ }
+ else if (*look == 'O')
+ {
+ if (try_match && keytyped == KEY_OPEN_BACK)
+ return TRUE;
+ ++look;
+ }
+
+ /*
+ * 'e' means to check for "else" at start of line and just before the
+ * cursor.
+ */
+ else if (*look == 'e')
+ {
+ if (try_match && keytyped == 'e' && curwin->w_cursor.col >= 4)
+ {
+ p = ml_get_curline();
+ if (skipwhite(p) == p + curwin->w_cursor.col - 4 &&
+ STRNCMP(p + curwin->w_cursor.col - 4, "else", 4) == 0)
+ return TRUE;
+ }
+ ++look;
+ }
+
+ /*
+ * ':' only causes an indent if it is at the end of a label or case
+ * statement.
+ */
+ else if (*look == ':')
+ {
+ if (try_match && keytyped == ':')
+ {
+ p = ml_get_curline();
+ if (iscase(p) || islabel(30))
+ return TRUE;
+ }
+ ++look;
+ }
+
+
+ /*
+ * Is it a key in <>, maybe?
+ */
+ else if (*look == '<')
+ {
+ if (try_match)
+ {
+ /*
+ * make up some named keys <o>, <O>, <e>, <0>, <>>, <<>, <*>
+ * and <!> so that people can re-indent on o, O, e, 0, <, >, *
+ * and ! keys if they really really want to.
+ */
+ if (vim_strchr((char_u *)"<>!*oOe0", look[1]) != NULL &&
+ keytyped == look[1])
+ return TRUE;
+
+ if (keytyped == get_special_key_code(look + 1))
+ return TRUE;
+ }
+ while (*look && *look != '>')
+ look++;
+ while (*look == '>')
+ look++;
+ }
+
+ /*
+ * ok, it's a boring generic character.
+ */
+ else
+ {
+ if (try_match && *look == keytyped)
+ return TRUE;
+ ++look;
+ }
+
+ /*
+ * Skip over ", ".
+ */
+ look = skip_to_option_part(look);
+ }
+ return FALSE;
+}
+#endif /* CINDENT */
+
+#if defined(RIGHTLEFT) || defined(PROTO)
+/*
+ * Map Hebrew keyboard when in hkmap mode.
+ */
+ int
+hkmap(c)
+ int c;
+{
+ switch(c)
+ {
+ case '`': return ';';
+ case '/': return '.';
+ case '\'': return ',';
+ case 'q': return '/';
+ case 'w': return '\'';
+
+ /* Hebrew letters - set offset from 'a' */
+ case ',': c = '{'; break;
+ case '.': c = 'v'; break;
+ case ';': c = 't'; break;
+ default: {
+ static char str[] = "zqbcxlsjphmkwonu ydafe rig";
+
+ if (c < 'a' || c > 'z')
+ return c;
+ c = str[c - 'a'];
+ break;
+ }
+ }
+
+ return c - 'a' + p_aleph;
+}
+#endif