/* * STEVIE - Simply Try this Editor for VI Enthusiasts * * Code Contributions By : Tim Thompson twitch!tjt * Tony Andrews onecom!wldrdg!tony * G. R. (Fred) Walter watmath!watcgl!grwalter */ #include "stevie.h" /* * This flag is used to make auto-indent work right on lines where only a * or is typed. It is set when an auto-indent is done, and * reset when any other editting is done on the line. If an or * is received, and did_ai is TRUE, the line is truncated. */ bool_t did_ai = FALSE; void edit() { char c; bool_t literal_next_flag = FALSE; Prenum = 0; /* position the display and the cursor at the top of the file. */ *Topchar = *Filemem; *Curschar = *Filemem; Cursrow = Curscol = 0; for (;;) { if (!RedrawingDisabled) { cursupdate(); /* Figure out where the cursor is based on * Curschar. */ if (MustRedrawLine) redrawline(); if (MustRedrawScreen) updateRealscreen(); windgoto(Cursrow, Curscol); } c = vgetc(); if (State == NORMAL) { /* We're in the normal (non-insert) mode. */ /* Pick up any leading digits and compute 'Prenum' */ if ((Prenum > 0 && isdigit(c)) || (isdigit(c) && c != '0')) { Prenum = Prenum * 10 + (c - '0'); continue; } /* execute the command */ normal(c); Prenum = 0; } else { if (c == CTRL('V') && !literal_next_flag) { literal_next_flag = TRUE; outchar('^'); continue; } if (literal_next_flag) { literal_next_flag = FALSE; outchar('\b'); if (c != NL) { did_ai = FALSE; insertchar(c); continue; } } switch (c) { /* We're in insert mode */ case CR: case NL: *Insbuffptr++ = NL; *Insbuffptr = NUL; if (!opencmd(FORWARD, TRUE)) goto doESCkey; /* out of memory */ if (!RedrawingDisabled) windgoto(Cursrow, Curscol); break; case ESC: /* an escape ends input mode */ doESCkey: set_want_col = TRUE; /* Don't end up on a '\n' if you can help it. */ if (gchar(Curschar) == NUL && Curschar->index != 0) dec(Curschar); /* * The cursor should end up on the last inserted character. * This is an attempt to match the real 'vi', but it may not * be quite right yet. */ if (Curschar->index != 0 && !endofline(Curschar)) dec(Curschar); State = NORMAL; msg(""); if (!UndoInProgress) { int n; char *p; if (last_command == 'o') AppendToUndobuff(UNDO_SHIFTJ_STR); if (Insbuff != Insbuffptr) { if (last_command == 'O') AppendToUndobuff("0"); AppendToRedobuff(Insbuff); AppendToUndoUndobuff(Insbuff); n = 0; for (p = Insbuff; p < Insbuffptr; p++) { if (*p == NL) { if (n) { AppendNumberToUndobuff(n); AppendToUndobuff("dl"); n = 0; } AppendToUndobuff(UNDO_SHIFTJ_STR); } else n++; } if (n) { AppendNumberToUndobuff(n); AppendToUndobuff("dl"); } } if (last_command == 'c') { AppendToUndobuff(mkstr(last_command_char)); AppendToUndobuff(Yankbuff); AppendToUndobuff(ESC_STR); } AppendToRedobuff(ESC_STR); AppendToUndoUndobuff(ESC_STR); if (last_command == 'O') AppendToUndobuff(UNDO_SHIFTJ_STR); } break; case CTRL('D'): /* * Control-D is treated as a backspace in insert mode to make * auto-indent easier. This isn't completely compatible with * vi, but it's a lot easier than doing it exactly right, and * the difference isn't very noticeable. */ case BS: /* can't backup past starting point */ if (Curschar->linep == Insstart->linep && Curschar->index <= Insstart->index) { beep(); break; } /* can't backup to a previous line */ if (Curschar->linep != Insstart->linep && Curschar->index <= 0) { beep(); break; } did_ai = FALSE; dec(Curschar); delchar(TRUE, FALSE); Insbuffptr--; *Insbuffptr = NUL; cursupdate(); updateline(); break; default: did_ai = FALSE; insertchar(c); break; } } } } /* * 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" command, so it needs to know when it should stop and defer * processing to the "normal" mechanism. */ #define ISSPECIAL(c) ((c) == NL || (c) == CR || (c) == ESC) void insertchar(c) char c; { if (anyinput()) { /* If there's any pending input, grab it all * at once. */ char *p; p = Insbuffptr; *Insbuffptr++ = c; for (c = vpeekc(); !ISSPECIAL(c) && anyinput(); c = vpeekc()) { c = vgetc(); *Insbuffptr++ = c; /* * The following kludge avoids overflowing the insert buffer. */ if (Insbuffptr + 10 >= &Insbuff[INSERT_SIZE]) { int n; *Insbuffptr = NUL; insstr(p); Insbuffptr = Insbuff; p = Insbuffptr; emsg("Insert buffer overflow - buffers flushed"); sleep(2); n = cntllines(Filemem, Curschar); AppendPositionToUndobuff(Curschar->index, n); AppendPositionToUndoUndobuff(Curschar->index, n); if (endofline(Curschar)) { AppendToRedobuff("a"); AppendToUndoUndobuff("a"); } else { AppendToRedobuff("i"); AppendToUndoUndobuff("i"); } } } *Insbuffptr = NUL; insstr(p); } else { inschar(c); *Insbuffptr++ = c; /* * The following kludge avoids overflowing the insert buffer. */ if (Insbuffptr + 10 >= &Insbuff[INSERT_SIZE]) { int n; Insbuffptr = Insbuff; emsg("Insert buffer overflow - buffers flushed"); sleep(2); n = cntllines(Filemem, Curschar); AppendPositionToUndobuff(Curschar->index, n); AppendPositionToUndoUndobuff(Curschar->index, n); if (endofline(Curschar)) { AppendToRedobuff("a"); AppendToUndoUndobuff("a"); } else { AppendToRedobuff("i"); AppendToUndoUndobuff("i"); } } *Insbuffptr = NUL; } updateline(); } void getout(r) int r; { windgoto(Rows - 1, 0); putchar('\r'); putchar('\n'); windexit(r); } void scrolldown(nlines) int nlines; { LPTR *p; int done = 0; /* total # of physical lines done */ /* Scroll up 'nlines' lines. */ while (nlines--) { if ((p = prevline(Topchar)) == NULL) break; done += plines(p); *Topchar = *p; if (Curschar->linep == Botchar->linep->prev) *Curschar = *prevline(Curschar); } s_ins(0, done); } void scrollup(nlines) int nlines; { LPTR *p; int done = 0; /* total # of physical lines done */ int pl; /* # of plines for the current line */ /* Scroll down 'nlines' lines. */ while (nlines--) { pl = plines(Topchar); if ((p = nextline(Topchar)) == NULL) break; done += pl; if (Curschar->linep == Topchar->linep) *Curschar = *p; *Topchar = *p; } s_del(0, done); } /* * oneright oneleft onedown oneup * * Move one char {right,left,down,up}. Return TRUE when sucessful, FALSE when * we hit a boundary (of a line, or the file). */ bool_t oneright() { set_want_col = TRUE; switch (inc(Curschar)) { case 0: return TRUE; case 1: dec(Curschar); /* crossed a line, so back up */ /* FALLTHROUGH */ case -1: return FALSE; } return FALSE; /* PARANOIA: should never reach here */ } bool_t oneleft() { set_want_col = TRUE; switch (dec(Curschar)) { case 0: return TRUE; case 1: inc(Curschar); /* crossed a line, so back up */ /* FALLTHROUGH */ case -1: return FALSE; } return FALSE; /* PARANOIA: should never reach here */ } void beginline(flag) bool_t flag; { while (oneleft()); if (flag) { while (isspace(gchar(Curschar)) && oneright()); } set_want_col = TRUE; } bool_t oneup(n) { LPTR p, *np; int k; p = *Curschar; for (k = 0; k < n; k++) { /* Look for the previous line */ if ((np = prevline(&p)) == NULL) { /* If we've at least backed up a little .. */ if (k > 0) break; /* to update the cursor, etc. */ else return FALSE; } p = *np; } *Curschar = p; cursupdate(); /* make sure Topchar is valid */ /* try to advance to the column we want to be at */ *Curschar = *coladvance(&p, Curswant); return TRUE; } bool_t onedown(n) { LPTR p, *np; int k; p = *Curschar; for (k = 0; k < n; k++) { /* Look for the next line */ if ((np = nextline(&p)) == NULL) { if (k > 0) break; else return FALSE; } p = *np; } cursupdate(); /* make sure Topchar is valid */ /* try to advance to the column we want to be at */ *Curschar = *coladvance(&p, Curswant); return TRUE; }