This is edit.c in view mode; [Download] [Up]
/* vi:ts=4:sw=4 * * VIM - Vi IMproved by Bram Moolenaar * * Read the file "credits.txt" for a list of people who contributed. * Read the file "uganda.txt" for copying and usage conditions. */ /* * edit.c: functions for insert mode */ #include "vim.h" #include "globals.h" #include "proto.h" #include "param.h" #include "ops.h" /* for operator */ extern char_u *get_inserted(); static void start_arrow __ARGS((void)); static void stop_arrow __ARGS((void)); static void stop_insert __ARGS((void)); static int echeck_abbr __ARGS((int)); int arrow_used; /* Normally FALSE, set to TRUE after hitting * cursor key in insert mode. Used by vgetorpeek() * to decide when to call u_sync() */ int restart_edit = 0; /* call edit when next command finished */ static char_u *last_insert = NULL; /* the text of the previous insert */ static int last_insert_skip; /* number of chars in front of the previous insert */ static int new_insert_skip; /* number of chars in front of the current insert */ void edit(count) long count; { int c; int cc; char_u *ptr; char_u *saved_line = NULL; /* saved line for replace mode */ linenr_t saved_lnum = 0; /* lnum of saved line */ int saved_char = NUL; /* char replaced by NL */ linenr_t lnum; int temp = 0; int mode; int nextc = 0; int lastc = 0; colnr_t mincol; static linenr_t o_lnum = 0; static int o_eol = FALSE; #ifdef WEBB_KEYWORD_COMPL FPOS complete_pos; FPOS first_match; char_u *complete_pat = NULL; char_u *completion_str = NULL; char_u *last_completion_str = NULL; char_u *tmp_ptr; int previous_c = 0; int complete_col = 0; /* init for gcc */ int complete_direction; int complete_any_word = 0; /* true -> ^N/^P hit with no prefix * init for gcc */ int done; int found_error = FALSE; char_u backup_char = 0; /* init for gcc */ c = NUL; #endif /* WEBB_KEYWORD_COMPL */ if (restart_edit) { 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). */ if (o_eol && curwin->w_cursor.lnum == o_lnum && *((ptr = ml_get(curwin->w_cursor.lnum)) + curwin->w_cursor.col) != NUL && *(ptr + curwin->w_cursor.col + 1) == NUL) ++curwin->w_cursor.col; } else { arrow_used = FALSE; o_eol = FALSE; } #ifdef DIGRAPHS dodigraph(-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); free(ptr); old_indent = 0; for (;;) { if (arrow_used) /* don't repeat insert when arrow key used */ count = 0; if (!arrow_used) curwin->w_set_curswant = TRUE; /* set curwin->w_curswant for next K_DARROW or K_UARROW */ cursupdate(); /* Figure out where the cursor is based on curwin->w_cursor. */ showruler(0); setcursor(); #ifdef WEBB_KEYWORD_COMPL previous_c = c; #endif /* WEBB_KEYWORD_COMPL */ if (nextc) /* character remaining from CTRL-V */ { c = nextc; nextc = 0; } else { c = vgetc(); if (c == Ctrl('C')) got_int = FALSE; } #ifdef WEBB_KEYWORD_COMPL if (previous_c == Ctrl('N') || previous_c == Ctrl('P')) { /* 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 (c != Ctrl('N') && c != Ctrl('P')) { /* Get here when we have finished typing a sequence of ^N and * ^P. Free up memory that was used, and make sure we can redo * the insert. */ if (completion_str != NULL) AppendToRedobuff(completion_str); free(complete_pat); free(completion_str); free(last_completion_str); complete_pat = completion_str = last_completion_str = NULL; } } #endif /* WEBB_KEYWORD_COMPL */ if (c != Ctrl('D')) /* remember to detect ^^D and 0^D */ lastc = c; /* * In replace mode a backspace puts the original text back. * We save the current line to be able to do that. * If characters are appended to the line, they will be deleted. * If we start a new line (with CR) the saved line will be empty, thus * the characters will be deleted. * If we backspace over the new line, that line will be saved. */ if (State == REPLACE && saved_lnum != curwin->w_cursor.lnum) { free(saved_line); saved_line = strsave(ml_get(curwin->w_cursor.lnum)); saved_lnum = curwin->w_cursor.lnum; } #ifdef DIGRAPHS c = dodigraph(c); #endif /* DIGRAPHS */ if (c == Ctrl('V')) { screen_start(); screen_outchar('^', curwin->w_row, curwin->w_col); AppendToRedobuff((char_u *)"\026"); /* CTRL-V */ cursupdate(); setcursor(); c = get_literal(&nextc); insertchar(c); continue; } switch (c) /* handle character in insert mode */ { case Ctrl('O'): /* execute one command */ if (echeck_abbr(Ctrl('O') + 0x100)) 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; case ESC: /* an escape ends input mode */ if (echeck_abbr(ESC + 0x100)) break; /*FALLTHROUGH*/ case Ctrl('C'): doESCkey: if (!arrow_used) { AppendToRedobuff(ESC_STR); if (--count > 0) /* repeat what was typed */ { (void)start_redo_ins(); continue; } stop_insert(); } if (!restart_edit) 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) && !p_ri) dec_cursor(); if (extraspace) /* did reverse replace in column 0 */ { (void)delchar(FALSE); updateline(); extraspace = FALSE; } State = NORMAL; /* inchar() may have deleted the "INSERT" message */ if (Recording) showmode(); else if (p_smd) MSG(""); free(saved_line); old_indent = 0; return; /* * Insert the previously inserted text. * Last_insert actually is a copy of the redo buffer, so we * first have to remove the command. * For ^@ the trailing ESC will end the insert. */ case K_ZERO: case Ctrl('A'): stuff_inserted(NUL, 1L, (c == Ctrl('A'))); break; /* * insert the contents of a register */ case Ctrl('R'): if (insertbuf(vgetc()) == FAIL) beep(); break; case Ctrl('B'): /* toggle reverse insert mode */ p_ri = !p_ri; showmode(); break; /* * If the cursor is on an indent, ^T/^D insert/delete one * shiftwidth. Otherwise ^T/^D behave like a TAB/backspace. * This isn't completely compatible with * vi, but the difference isn't very noticeable and now you can * mix ^D/backspace and ^T/TAB without thinking about which one * must be used. */ case Ctrl('T'): /* make indent one shiftwidth greater */ case Ctrl('D'): /* make indent one shiftwidth smaller */ stop_arrow(); AppendCharToRedobuff(c); 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 current indent */ /* determine offset from first non-blank */ temp = curwin->w_cursor.col; beginline(TRUE); temp -= curwin->w_cursor.col; set_indent(0, TRUE); /* remove all indent */ } else { ins_indent: /* determine offset from first non-blank */ temp = curwin->w_cursor.col; beginline(TRUE); temp -= curwin->w_cursor.col; shift_line(c == Ctrl('D'), TRUE, 1); /* try to put cursor on same character */ temp += curwin->w_cursor.col; } if (temp <= 0) curwin->w_cursor.col = 0; else curwin->w_cursor.col = temp; did_ai = FALSE; did_si = FALSE; can_si = FALSE; goto redraw; case BS: case DEL: nextbs: mode = 0; 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() || (!p_ri && ((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(); goto redraw; } stop_arrow(); if (p_ri) inc_cursor(); if (curwin->w_cursor.col <= 0) /* delete newline! */ { lnum = Insstart.lnum; if (curwin->w_cursor.lnum == Insstart.lnum || p_ri) { if (!u_save((linenr_t)(curwin->w_cursor.lnum - 2), (linenr_t)(curwin->w_cursor.lnum + 1))) goto redraw; --Insstart.lnum; Insstart.col = 0; /* this is in xvim, why? if (curbuf->b_p_ai) for (ptr = ml_get(Insstart.lnum); iswhite(*ptr++); Insstart.col++) ; */ } /* 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)dojoin(FALSE, TRUE); if (temp == NUL && gchar_cursor() != NUL) ++curwin->w_cursor.col; if (saved_char) /* restore what NL replaced */ { State = NORMAL; /* no replace for this char */ inschar(saved_char); /* but no showmatch */ State = REPLACE; saved_char = NUL; if (!p_ri) dec_cursor(); } else if (p_ri) /* in reverse mode */ saved_lnum = 0; /* save this line again */ } else dec_cursor(); did_ai = FALSE; } else { if (p_ri && State != REPLACE) dec_cursor(); mincol = 0; if (mode == 3 && !p_ri && curbuf->b_p_ai) /* keep indent */ { temp = curwin->w_cursor.col; beginline(TRUE); if (curwin->w_cursor.col < temp) mincol = curwin->w_cursor.col; curwin->w_cursor.col = temp; } /* delete upto starting point, start of line or previous word */ do { if (!p_ri) dec_cursor(); /* start of word? */ if (mode == 1 && !isspace(gchar_cursor())) { mode = 2; temp = isidchar(gchar_cursor()); } /* end of word? */ else if (mode == 2 && (isspace(cc = gchar_cursor()) || isidchar(cc) != temp)) { if (!p_ri) inc_cursor(); else if (State == REPLACE) dec_cursor(); break; } if (State == REPLACE) { if (saved_line) { if (extraspace) { if ((int)STRLEN(ml_get(curwin->w_cursor.lnum)) - 1 > (int)STRLEN(saved_line)) (void)delchar(FALSE); else { dec_cursor(); (void)delchar(FALSE); extraspace = FALSE; pchar_cursor(*saved_line); } } else if (curwin->w_cursor.col < STRLEN(saved_line)) pchar_cursor(saved_line[curwin->w_cursor.col]); else if (!p_ri) (void)delchar(FALSE); } } else /* State != REPLACE */ { (void)delchar(FALSE); if (p_ri && gchar_cursor() == NUL) break; } if (mode == 0) /* just a single backspace */ break; if (p_ri && State == REPLACE && inc_cursor()) break; } while (p_ri || (curwin->w_cursor.col > mincol && (curwin->w_cursor.lnum != Insstart.lnum || curwin->w_cursor.col != Insstart.col))); if (extraspace) dec_cursor(); } did_si = FALSE; can_si = 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); if (vpeekc() == BS) { c = vgetc(); goto nextbs; /* speedup multiple backspaces */ } redraw: cursupdate(); updateline(); break; case Ctrl('W'): /* delete word before cursor */ mode = 1; goto dodel; case Ctrl('U'): /* delete inserted text in current line */ mode = 3; goto dodel; case K_LARROW: if (oneleft() == OK) { start_arrow(); } /* if 'whichwrap' set for cursor in insert mode may go * to previous line */ else if ((p_ww & 16) && curwin->w_cursor.lnum > 1) { start_arrow(); --(curwin->w_cursor.lnum); coladvance(MAXCOL); curwin->w_curswant = MAXCOL; /* so we stay at the end */ } else beep(); break; case K_SLARROW: if (curwin->w_cursor.lnum > 1 || curwin->w_cursor.col > 0) { bck_word(1L, 0); start_arrow(); } else beep(); break; case K_RARROW: if (gchar_cursor() != NUL) { curwin->w_set_curswant = TRUE; start_arrow(); ++curwin->w_cursor.col; } /* if 'whichwrap' set for cursor in insert mode may go * to next line */ else if ((p_ww & 16) && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { curwin->w_set_curswant = TRUE; start_arrow(); ++curwin->w_cursor.lnum; curwin->w_cursor.col = 0; } else beep(); break; case K_SRARROW: if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count || gchar_cursor() != NUL) { fwd_word(1L, 0, 0); start_arrow(); } else beep(); break; case K_UARROW: if (oneup(1L)) start_arrow(); else beep(); break; case K_SUARROW: if (onepage(BACKWARD, 1L)) start_arrow(); else beep(); break; case K_DARROW: if (onedown(1L)) start_arrow(); else beep(); break; case K_SDARROW: if (onepage(FORWARD, 1L)) start_arrow(); else beep(); break; case TAB: if (echeck_abbr(TAB + 0x100)) break; if ((!curbuf->b_p_et && !(p_sta && inindent())) || (p_ri && State == REPLACE)) goto normalchar; AppendToRedobuff((char_u *)"\t"); if (!curbuf->b_p_et) /* insert smart tab */ goto ins_indent; /* p_te set: expand a tab into spaces */ stop_arrow(); did_ai = FALSE; did_si = FALSE; can_si = FALSE; if (p_sta && inindent()) temp = (int)curbuf->b_p_sw; else temp = (int)curbuf->b_p_ts; temp -= curwin->w_cursor.col % temp; inschar(' '); /* delete one char in replace mode */ while (--temp) insstr((char_u *)" "); /* insstr does not delete chars */ goto redraw; case CR: case NL: if (echeck_abbr(c + 0x100)) break; stop_arrow(); if (State == REPLACE) { saved_char = gchar_cursor(); (void)delchar(FALSE); } /* * When 'autoindent' set delete white space after the cursor. * Vi does this, although it probably does it implicitly due * to the way it does auto-indent -- webb. */ if (curbuf->b_p_ai || curbuf->b_p_si) while ((c = gchar_cursor()) == ' ' || c == TAB) (void)delchar(FALSE); AppendToRedobuff(NL_STR); if (!Opencmd(FORWARD, TRUE, State == INSERT)) goto doESCkey; /* out of memory */ if (p_ri) { dec_cursor(); if (State == REPLACE && curwin->w_cursor.col > 0) dec_cursor(); } break; #ifdef DIGRAPHS case Ctrl('K'): screen_start(); screen_outchar('?', curwin->w_row, curwin->w_col); setcursor(); c = vgetc(); if (c != ESC) { if (charsize(c) == 1) { screen_start(); screen_outchar(c, curwin->w_row, curwin->w_col); } setcursor(); cc = vgetc(); if (cc != ESC) { AppendToRedobuff((char_u *)"\026"); /* CTRL-V */ c = getdigraph(c, cc, TRUE); goto normalchar; } } updateline(); goto doESCkey; #endif /* DIGRAPHS */ #ifdef WEBB_KEYWORD_COMPL case Ctrl('P'): /* Do previous pattern completion */ case Ctrl('N'): /* Do next pattern completion */ if (c == Ctrl('P')) complete_direction = BACKWARD; else complete_direction = FORWARD; if (previous_c != Ctrl('N') && previous_c != Ctrl('P')) { /* First time we hit ^N or ^P (in a row, I mean) */ complete_pos = curwin->w_cursor; ptr = ml_get(complete_pos.lnum); complete_col = complete_pos.col; temp = complete_col - 1; if (temp < 0 || !isidchar(ptr[temp])) { complete_pat = strsave((char_u *)"\\<[a-zA-Z_]"); complete_any_word = TRUE; } else { while (temp >= 0 && isidchar(ptr[temp])) temp--; temp++; complete_pat = alloc(curwin->w_cursor.col - temp + 3); if (complete_pat != NULL) sprintf((char *)complete_pat, "\\<%.*s", (int)(curwin->w_cursor.col - temp), ptr + temp); complete_any_word = FALSE; } last_completion_str = strsave((char_u *)" "); } else { /* This is not the first ^N or ^P we have hit in a row */ while (curwin->w_cursor.col != complete_col) { curwin->w_cursor.col--; delchar(FALSE); } if (completion_str != NULL) { free(last_completion_str); last_completion_str = strsave(completion_str); } } if (complete_pat == NULL || last_completion_str == NULL) { found_error = TRUE; break; } if (!complete_any_word) { ptr = ml_get(curwin->w_cursor.lnum); backup_char = ptr[complete_col - 1]; ptr[complete_col - 1] = ' '; } done = FALSE; found_error = FALSE; first_match.lnum = 0; keep_old_search_pattern = TRUE; while (!done) { if (complete_direction == BACKWARD) { ptr = ml_get(complete_pos.lnum); while (isidchar(ptr[complete_pos.col])) complete_pos.col--; complete_pos.col++; } if (!searchit(&complete_pos, complete_direction, complete_pat, 1L, TRUE, TRUE)) { found_error = TRUE; break; } if (complete_any_word) ptr = ml_get_pos(&complete_pos); else ptr = ml_get_pos(&complete_pos) + 1; tmp_ptr = ptr; temp = 1; while (*tmp_ptr != NUL && isidchar(*tmp_ptr++)) temp++; free (completion_str); tmp_ptr = completion_str = alloc(temp); if (completion_str == NULL) { found_error = TRUE; break; } while (*ptr != NUL && isidchar(*ptr)) *(tmp_ptr++) = *(ptr++); *tmp_ptr = NUL; if (completion_str[0] != NUL && STRCMP(completion_str, last_completion_str) != 0) done = TRUE; else if (first_match.lnum == 0) { first_match.lnum = complete_pos.lnum; first_match.col = complete_pos.col; } else if (complete_pos.lnum == first_match.lnum && complete_pos.col == first_match.col) { if (completion_str[0] == NUL) EMSG("Exact match only"); else EMSG("No other matches"); done = TRUE; } } if (!found_error) insstr(completion_str); if (!complete_any_word) { ptr = ml_get(curwin->w_cursor.lnum); ptr[complete_col - 1] = backup_char; } keep_old_search_pattern = FALSE; updateline(); break; #endif /* WEBB_KEYWORD_COMPL */ case Ctrl('Y'): /* copy from previous line */ lnum = curwin->w_cursor.lnum - 1; goto copychar; case Ctrl('E'): /* copy from next line */ lnum = curwin->w_cursor.lnum + 1; copychar: if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) { beep(); break; } /* try to advance to the cursor column */ temp = 0; ptr = ml_get(lnum); while (temp < curwin->w_virtcol && *ptr) temp += chartabsize(*ptr++, (long)temp); if (temp > curwin->w_virtcol) --ptr; if ((c = *ptr) == NUL) { beep(); break; } /*FALLTHROUGH*/ default: normalchar: /* * do some very smart indenting when entering '{' or '}' or '#' */ if (curwin->w_cursor.col > 0 && ((can_si && c == '}') || (did_si && c == '{'))) { FPOS *pos, old_pos; int i; /* for '}' set indent equal to matching '{' */ if (c == '}' && (pos = showmatch('{')) != NULL) { old_pos = curwin->w_cursor; curwin->w_cursor = *pos; i = get_indent(); curwin->w_cursor = old_pos; set_indent(i, TRUE); } else shift_line(TRUE, TRUE, 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 (isidchar(c) || !echeck_abbr(c)) insertchar(c); break; } } } /* * 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, *nextc is set to the next character. */ int get_literal(nextc) int *nextc; { int cc; int nc; int oldstate; int i; oldstate = State; State = NOMAPPING; /* next characters not mapped */ if (got_int) { *nextc = NUL; return Ctrl('C'); } cc = 0; for (i = 0; i < 3; ++i) { nc = vgetc(); if (!isdigit(nc)) break; cc = cc * 10 + nc - '0'; nc = 0; } if (i == 0) /* no number entered */ { cc = nc; nc = 0; if (cc == K_ZERO) /* NUL is stored as NL */ cc = '\n'; } else if (cc == 0) /* NUL is stored as NL */ cc = '\n'; State = oldstate; *nextc = nc; got_int = FALSE; /* CTRL-C typed after CTRL-V is not an interrupt */ return cc; } /* * 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) unsigned c; { int haveto_redraw = FALSE; int textwidth; 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, us 0. */ textwidth = curbuf->b_p_tw; if (textwidth == 0 && curbuf->b_p_wm) textwidth = Columns - curbuf->b_p_wm; if (textwidth < 0) textwidth = 0; /* * If the cursor is past 'textwidth' and we are inserting a non-space, * try to break the line in two or more pieces. If c == NUL then we have * been called to do formatting only. If textwidth == 0 it does nothing. * Don't do this if an existing character is being replaced. */ if (c == NUL || !(isspace(c) || (State == REPLACE && *ml_get_cursor() != NUL))) { while (textwidth && curwin->w_virtcol >= textwidth) { int startcol; /* Cursor column at entry */ int wantcol; /* column at textwidth border */ int foundcol; /* column for start of word */ if ((startcol = curwin->w_cursor.col) == 0) break; coladvance(textwidth); /* find column of textwidth border */ wantcol = curwin->w_cursor.col; curwin->w_cursor.col = startcol - 1; foundcol = 0; while (curwin->w_cursor.col > 0) /* find position to break at */ { if (isspace(gchar_cursor())) { while (curwin->w_cursor.col > 0 && isspace(gchar_cursor())) --curwin->w_cursor.col; if (curwin->w_cursor.col == 0) /* only spaces in front of text */ break; foundcol = curwin->w_cursor.col + 1; if (curwin->w_cursor.col < wantcol) break; } --curwin->w_cursor.col; } if (foundcol == 0) /* no spaces, cannot break line */ { curwin->w_cursor.col = startcol; break; } curwin->w_cursor.col = foundcol; /* put cursor after pos. to break line */ startcol -= foundcol; Opencmd(FORWARD, FALSE, FALSE); while (isspace(gchar_cursor()) && startcol) /* delete blanks */ { (void)delchar(FALSE); --startcol; /* adjust cursor pos. */ } curwin->w_cursor.col += startcol; curs_columns(FALSE); /* update curwin->w_virtcol */ haveto_redraw = TRUE; } if (c == NUL) /* formatting only */ return; 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); } } did_ai = FALSE; did_si = FALSE; can_si = FALSE; /* * If there's any pending input, grab up to MAX_COLUMNS at once. * This speeds up normal text input considerably. */ if (vpeekc() != NUL && State != REPLACE && !p_ri) { char_u p[MAX_COLUMNS + 1]; int i; p[0] = c; i = 1; while ((c = vpeekc()) != NUL && !ISSPECIAL(c) && i < MAX_COLUMNS && (textwidth == 0 || (curwin->w_virtcol += charsize(p[i - 1])) < textwidth) && !(!no_abbr && !isidchar(c) && isidchar(p[i - 1]))) p[i++] = vgetc(); #ifdef DIGRAPHS dodigraph(-1); /* clear digraphs */ dodigraph(p[i-1]); /* may be the start of a digraph */ #endif p[i] = '\0'; insstr(p); AppendToRedobuff(p); } else { inschar(c); AppendCharToRedobuff(c); } /* * TODO: If the cursor has shifted past the end of the screen, should * adjust the screen display. Avoids extra redraw. */ updateline(); } /* * start_arrow() is called when an arrow key is used in insert mode. * It resembles hitting the <ESC> key. */ static void start_arrow() { if (!arrow_used) /* something has been inserted */ { AppendToRedobuff(ESC_STR); arrow_used = TRUE; /* this means we stopped the current insert */ stop_insert(); } } /* * 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) { u_save_cursor(); /* errors are ignored! */ Insstart = curwin->w_cursor; /* new insertion starts here */ ResetRedobuff(); AppendToRedobuff((char_u *)"1i"); /* pretend we start an insertion */ arrow_used = FALSE; } } /* * do a few things to stop inserting */ static void stop_insert() { stop_redo_ins(); /* * save the inserted text for later redo with ^@ */ free(last_insert); last_insert = get_inserted(); last_insert_skip = new_insert_skip; /* * If we just did an auto-indent, truncate the line, and put * the cursor back. */ if (did_ai && !arrow_used) { ml_replace(curwin->w_cursor.lnum, (char_u *)"", TRUE); curwin->w_cursor.col = 0; if (curwin->w_p_list) /* the deletion is only seen in list mode */ updateline(); } did_ai = FALSE; did_si = FALSE; can_si = FALSE; } /* * move cursor to start of line * if flag == TRUE move to first non-white */ void beginline(flag) int flag; { curwin->w_cursor.col = 0; if (flag) { register char_u *ptr; for (ptr = ml_get(curwin->w_cursor.lnum); iswhite(*ptr); ++ptr) ++curwin->w_cursor.col; } curwin->w_set_curswant = TRUE; } /* * oneright oneleft onedown oneup * * 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 oneup(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; if (operator == NOP) cursupdate(); /* make sure curwin->w_topline is valid */ /* try to advance to the column we want to be at */ coladvance(curwin->w_curswant); return OK; } int onedown(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; if (operator == NOP) cursupdate(); /* make sure curwin->w_topline is valid */ /* try to advance to the column we want to be at */ coladvance(curwin->w_curswant); return OK; } /* * 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; 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(); return FAIL; } if (dir == FORWARD) { if (curwin->w_botline > curbuf->b_ml.ml_line_count) /* at end of file */ curwin->w_topline = curbuf->b_ml.ml_line_count; else if (plines(curwin->w_botline) >= curwin->w_height - 2 || /* next line is big */ curwin->w_botline - curwin->w_topline <= 3) /* just three lines on screen */ curwin->w_topline = curwin->w_botline; else curwin->w_topline = curwin->w_botline - 2; curwin->w_cursor.lnum = curwin->w_topline; if (count != 1) 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; else if (lp >= curwin->w_topline - 2) /* happens with very long lines */ { --curwin->w_topline; comp_Botline(curwin); curwin->w_cursor.lnum = curwin->w_botline - 1; } else curwin->w_topline = lp + 2; } } beginline(TRUE); updateScreen(VALID); return OK; } void stuff_inserted(c, count, no_esc) int c; long count; int no_esc; { char_u *esc_ptr = NULL; char_u *ptr; if (last_insert == NULL) { EMSG("No inserted text yet"); return; } if (c) stuffcharReadbuff(c); if (no_esc && (esc_ptr = (char_u *)STRRCHR(last_insert, 27)) != NULL) *esc_ptr = NUL; /* remove the ESC */ /* skip the command */ ptr = last_insert + last_insert_skip; do stuffReadbuff(ptr); while (--count > 0); if (no_esc && esc_ptr) *esc_ptr = 27; /* put the ESC back */ } 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 typestr, 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(curwin->w_cursor.lnum), curwin->w_cursor.col, curwin->w_cursor.lnum == Insstart.lnum ? Insstart.col : 0); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.