gno/bin/vi/edit.c

413 lines
8.8 KiB
C

/*
* 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!grwalter
*/
#include "stevie.h"
/*
* This flag is used to make auto-indent work right on lines where only a
* <RETURN> or <ESC> is typed. It is set when an auto-indent is done, and
* reset when any other editting is done on the line. If an <ESC> or <RETURN>
* is received, and did_ai is TRUE, the line is truncated.
*/
bool_t did_ai = FALSE;
static int replace_num;
void
edit(void)
{
char c;
bool_t literal_next_flag = FALSE;
char *replace_line;
char *ptr;
int len;
void dosuspend(void);
Prenum = 0;
/* position the display and the cursor at the top of the file. */
*Topchar = *Filemem;
*Curschar = *Filemem;
Cursrow = Curscol = 0;
for (;;) {
if (!RedrawingDisabled) {
/* Figure out where the cursor is based on Curschar. */
cursupdate(UPDATE_CURSOR);
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 (isascii(c)) { /* must disallow special chars from "ascii.h" */
if ((Prenum > 0 && isdigit(c)) || (isdigit(c) && c != '0')) {
Prenum = Prenum * 10 + (c - '0');
continue;
}
}
/* execute the command */
normal(c);
if (State == INSERT && last_command == 'R') {
ptr = Curschar->linep->s + Curschar->index;
len = strlen(ptr) + 1;
replace_line = (char *) NULL;
replace_num = 0;
if (len > 1) {
replace_line = alloc((unsigned) len);
if (replace_line != (char *) NULL)
strcpy(replace_line, ptr);
}
}
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 ESC: /* an escape ends input mode */
doESCkey:
dosuspend();
/*
* If we just did an auto-indent, truncate the line, and put
* the cursor back.
*/
if (did_ai) {
Curschar->linep->s[0] = NUL;
Curschar->index = 0;
did_ai = FALSE;
}
set_want_col = TRUE;
/*
* 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) {
if (gchar(Curschar) == NUL)
dec(Curschar);
else if (Insbuffptr != NULL)
dec(Curschar);
}
State = NORMAL;
msg("");
if (!UndoInProgress) {
int n;
char *p;
if (last_command == 'o')
AppendToUndobuff(UNDO_SHIFTJ_STR);
if (Insbuffptr != NULL) {
if (last_command == 'O')
AppendToUndobuff("0");
AppendToRedobuff(Insbuff);
AppendToUndoUndobuff(Insbuff);
n = 0;
for (p = Insbuff; *p != NUL; 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);
if (last_command == 'R' && replace_line != (char *) NULL) {
if (replace_num > 0) {
if (replace_num < len) {
AppendToUndobuff("i");
replace_line[replace_num] = '\0';
} else {
AppendToUndobuff("a");
}
AppendToUndobuff(replace_line);
AppendToUndobuff(ESC_STR);
free(replace_line);
}
}
}
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);
/*
* It's a little strange to put backspaces into the redo
* buffer, but it makes auto-indent a lot easier to deal
* with.
*/
AppendToInsbuff(BS_STR);
if (!RedrawingDisabled) /* screen will be fixed later */
S_LINE_NOT_VALID;
break;
case CR:
case NL:
AppendToInsbuff(NL_STR);
if (!OpenForward(!RedrawingDisabled))
goto doESCkey; /* out of memory */
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", "undo" or "put" commands, so it needs to know when it should
* stop and defer processing to the "normal" mechanism.
*/
#define ISSPECIAL(c) ((c) == BS || (c) == NL || (c) == CR || (c) == ESC)
void
insertchar(char c)
{
/*
* If there's any pending input, grab up to MAX_COLUMNS at once.
*/
if (anyinput() && (last_command != 'R' || (gchar(Curschar) == NUL))) {
char p[MAX_COLUMNS + 1];
int i;
p[0] = c;
i = 1;
c = vpeekc();
while (!ISSPECIAL(c) && anyinput() && (i < MAX_COLUMNS)) {
p[i++] = vgetc();
c = vpeekc();
}
p[i] = '\0';
insstr(p);
replace_num += i;
AppendToInsbuff(p);
} else {
inschar(c);
replace_num++;
AppendToInsbuff(mkstr(c));
}
if (!RedrawingDisabled) /* screen will be fixed later */
S_LINE_NOT_VALID;
}
void
getout(int r)
{
windgoto(Rows - 1, 0);
outchar('\n');
windexit(r);
}
void
scrolldown(int nlines)
{
register LPtr *p;
S_MUST_UPDATE_BOTCHAR;
S_CHECK_TOPCHAR_AND_BOTCHAR;
/* Scroll up 'nlines' lines. */
while (nlines--) {
p = prevline(Topchar);
if (p == NULL)
break;
Topchar->linep = p->linep;
}
/*
* The calling routine must make sure that Curschar is in the correct
* place with relation to Botchar.
*/
}
void
scrollup(int nlines)
{
register LPtr *p;
S_MUST_UPDATE_BOTCHAR;
S_CHECK_TOPCHAR_AND_BOTCHAR;
/* Scroll down 'nlines' lines. */
while (nlines--) {
p = nextline(Topchar);
if (p == NULL)
break;
Topchar->linep = p->linep;
}
/*
* The calling routine must make sure that Curschar is in the correct
* place with relation to Topchar.
*/
}
/*
* 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(void)
{
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(void)
{
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(bool_t flag)
{
while (oneleft());
if (flag) {
while (isspace(gchar(Curschar)) && oneright());
}
set_want_col = TRUE;
}
bool_t
oneup(int n)
{
register int k;
S_CHECK_TOPCHAR_AND_BOTCHAR;
for (k = 0; k < n; k++) {
if (Curschar->linep->prev == Filetop->linep) {
if (k > 0)
break;
else
return FALSE;
}
Curschar->linep = Curschar->linep->prev;
}
/* try to advance to the column we want to be at */
Curschar->index = 0;
coladvance(Curschar, Curswant);
return TRUE;
}
bool_t
onedown(int n)
{
register int k;
S_CHECK_TOPCHAR_AND_BOTCHAR;
for (k = 0; k < n; k++) {
if (Curschar->linep->next == Fileend->linep) {
if (k > 0)
break;
else
return FALSE;
}
Curschar->linep = Curschar->linep->next;
}
/* try to advance to the column we want to be at */
Curschar->index = 0;
coladvance(Curschar, Curswant);
return TRUE;
}