windows-nt/Source/XPSP1/NT/sdktools/vi/edit.c
2020-09-26 16:20:57 +08:00

383 lines
11 KiB
C

/*
* The main edit loop as well as some other simple cursor movement routines.
*/
#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;
void
edit()
{
extern bool_t need_redraw;
int c;
register char *p, *q;
Prenum = 0;
/* position the display and the cursor at the top of the file. */
*Topchar = *Filemem;
*Curschar = *Filemem;
Cursrow = Curscol = 0;
do_mlines(); /* check for mode lines before starting */
updatescreen();
for ( ;; ) {
/* Figure out where the cursor is based on Curschar. */
cursupdate();
if (need_redraw && !anyinput()) {
updatescreen();
need_redraw = FALSE;
}
if (!anyinput())
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 {
/*
* Insert or Replace mode.
*/
switch (c) {
case ESC: /* an escape ends input mode */
/*
* 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;
/* 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("");
/* construct the Redo buffer */
p = ralloc(Redobuff,
Ninsert+2 < REDOBUFFMIN
? REDOBUFFMIN : Ninsert+2);
if(p == NULL) {
msg("Insufficient memory -- command not saved for redo");
} else {
Redobuff=p;
q=Insbuff;
while ( q < Insptr )
*p++ = *q++;
*p++ = ESC;
*p = NUL;
}
updatescreen();
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);
if (State == INSERT)
delchar(TRUE);
/*
* It's a little strange to put backspaces into
* the redo buffer, but it makes auto-indent a
* lot easier to deal with.
*/
insertchar(BS);
cursupdate();
updateline();
break;
case CR:
case NL:
insertchar(NL);
opencmd(FORWARD, TRUE); /* open a new line */
break;
case TAB:
if (!P(P_HT)) {
/* fake TAB with spaces */
int i = P(P_TS) - (Curscol % P(P_TS));
did_ai = FALSE;
while (i--) {
inschar(' ');
insertchar(' ');
}
updateline();
break;
}
/* else fall through to normal case */
default:
did_ai = FALSE;
inschar(c);
insertchar(c);
updateline();
break;
}
}
}
}
void
insertchar(c)
int c;
{
char *p;
*Insptr++ = (char)c;
Ninsert++;
if(Ninsert == InsbuffSize) { // buffer is full -- enlarge it
if((p = ralloc(Insbuff,InsbuffSize+INSERTSLOP)) != NULL) {
Insptr += p - Insbuff;
Insbuff = p;
InsbuffSize += INSERTSLOP;
} else { // could not get bigger buffer
stuffin(mkstr(ESC)); // just end insert mode
}
}
}
void
getout()
{
windgoto(Rows-1,0);
//putchar('\r');
putchar('\n');
windexit(0);
}
void
scrolldown(nlines)
int nlines;
{
register LNPTR *p;
register 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 the cursor is on the bottom line, we need to
* make sure it gets moved up the appropriate number
* of lines so it stays on the screen.
*/
if (Curschar->linep == Botchar->linep->prev) {
int i = 0;
while (i < done) {
i += plines(Curschar);
*Curschar = *prevline(Curschar);
}
}
}
s_ins(0, done);
}
void
scrollup(nlines)
int nlines;
{
register LNPTR *p;
register int done = 0; /* total # of physical lines done */
register 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 */
/* fall through */
case -1:
return FALSE;
DEFAULT_UNREACHABLE;
}
/*NOTREACHED*/
}
bool_t
oneleft()
{
set_want_col = TRUE;
switch (dec(Curschar)) {
case 0:
return TRUE;
case 1:
inc(Curschar); /* crossed a line, so back up */
/* fall through */
case -1:
return FALSE;
DEFAULT_UNREACHABLE;
}
/*NOTREACHED*/
}
void
beginline(flag)
bool_t flag;
{
while ( oneleft() )
;
if (flag) {
while (isspace(gchar(Curschar)) && oneright())
;
}
set_want_col = TRUE;
}
bool_t
oneup(n)
int n;
{
LNPTR p, *np;
register 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;
/* This makes sure Topchar gets updated so the complete line */
/* is one the screen. */
cursupdate();
/* try to advance to the column we want to be at */
*Curschar = *coladvance(&p, Curswant);
return TRUE;
}
bool_t
onedown(n)
int n;
{
LNPTR p, *np;
register 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;
}
/* try to advance to the column we want to be at */
*Curschar = *coladvance(&p, Curswant);
return TRUE;
}