gno/bin/vi/edit.c
gdr-ftp 784e3de7cd Initial checkin of aroff, binprint, center, less, ls, make, makemake,
passwd, ps, purge, shutdown, stty, upper, and vi.  These sources are
for the versions of the utils shipped with GNO v2.0.4.
1998-03-09 08:30:21 +00:00

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;
}