📄 normal.c
字号:
/* $Header: /nw2/tony/src/stevie/src/RCS/normal.c,v 1.25 89/08/06 09:50:25 tony Exp $
*
* Contains the main routine for processing characters in command mode.
* Communicates closely with the code in ops.c to handle the operators.
*/
#include "stevie.h"
#include "ops.h"
/*
* Generally speaking, every command in normal() should either clear any
* pending operator (with CLEAROP), or set the motion type variable.
*/
#define CLEAROP (operator=NOP) /* clear any pending operator */
int operator = NOP; /* current pending operator */
int mtype; /* type of the current cursor motion */
bool_t mincl; /* true if char motion is inclusive */
LPTR startop; /* cursor pos. at start of operator */
/*
* Operators can have counts either before the operator, or between the
* operator and the following cursor motion as in:
*
* d3w or 3dw
*
* If a count is given before the operator, it is saved in opnum. If
* normal() is called with a pending operator, the count in opnum (if
* present) overrides any count that came later.
*/
static int opnum = 0;
#define DEFAULT1(x) (((x) == 0) ? 1 : (x))
/*
* normal(c)
*
* Execute a command in command mode.
*
* This is basically a big switch with the cases arranged in rough categories
* in the following order:
*
* 1. File positioning commands
* 2. Control commands (e.g. ^G, Z, screen redraw, etc)
* 3. Character motions
* 4. Search commands (of various kinds)
* 5. Edit commands (e.g. J, x, X)
* 6. Insert commands (e.g. i, o, O, A)
* 7. Operators
* 8. Abbreviations (e.g. D, C)
* 9. Marks
*/
void
normal(c)
register int c;
{
register int n;
register char *s; /* temporary variable for misc. strings */
bool_t flag = FALSE;
int type = 0; /* used in some operations to modify type */
int dir = FORWARD; /* search direction */
int nchar = NUL;
bool_t finish_op;
/*
* If there is an operator pending, then the command we take
* this time will terminate it. Finish_op tells us to finish
* the operation before returning this time (unless the operation
* was cancelled.
*/
finish_op = (operator != NOP);
/*
* If we're in the middle of an operator AND we had a count before
* the operator, then that count overrides the current value of
* Prenum. What this means effectively, is that commands like
* "3dw" get turned into "d3w" which makes things fall into place
* pretty neatly.
*/
if (finish_op) {
if (opnum != 0)
Prenum = opnum;
} else
opnum = 0;
u_lcheck(); /* clear the "line undo" buffer if we've moved */
switch (c & 0xff) {
/*
* Screen positioning commands
*/
case CTRL('D'):
CLEAROP;
if (Prenum)
P(P_SS) = (Prenum > Rows-1) ? Rows-1 : Prenum;
scrollup(P(P_SS));
onedown(P(P_SS));
updatescreen();
break;
case CTRL('U'):
CLEAROP;
if (Prenum)
P(P_SS) = (Prenum > Rows-1) ? Rows-1 : Prenum;
scrolldown(P(P_SS));
oneup(P(P_SS));
updatescreen();
break;
/*
* This is kind of a hack. If we're moving by one page, the calls
* to stuffin() do exactly the right thing in terms of leaving
* some context, and so on. If a count was given, we don't have
* to worry about these issues.
*/
case CTRL('F'):
CLEAROP;
n = DEFAULT1(Prenum);
if (n > 1) {
if ( ! onedown(Rows * n) )
beep();
cursupdate();
} else {
screenclear();
stuffin("Lz\nM");
}
break;
case CTRL('B'):
CLEAROP;
n = DEFAULT1(Prenum);
if (n > 1) {
if ( ! oneup(Rows * n) )
beep();
cursupdate();
} else {
screenclear();
stuffin("Hz-M");
}
break;
case CTRL('E'):
CLEAROP;
scrollup(DEFAULT1(Prenum));
updatescreen();
break;
case CTRL('Y'):
CLEAROP;
scrolldown(DEFAULT1(Prenum));
updatescreen();
break;
case 'z':
CLEAROP;
switch (vgetc()) {
case NL: /* put Curschar at top of screen */
case CR:
*Topchar = *Curschar;
Topchar->index = 0;
updatescreen();
break;
case '.': /* put Curschar in middle of screen */
n = Rows/2;
goto dozcmd;
case '-': /* put Curschar at bottom of screen */
n = Rows-1;
/* fall through */
dozcmd:
{
register LPTR *lp = Curschar;
register int l = 0;
while ((l < n) && (lp != NULL)) {
l += plines(lp);
*Topchar = *lp;
lp = prevline(lp);
}
}
Topchar->index = 0;
updatescreen();
break;
default:
beep();
}
break;
/*
* Control commands
*/
case ':':
CLEAROP;
if ((s = getcmdln(c)) != NULL)
docmdln(s);
break;
case K_HELP:
CLEAROP;
if (help()) {
screenclear();
updatescreen();
}
break;
case CTRL('L'):
CLEAROP;
screenclear();
updatescreen();
break;
case CTRL('O'): /* ignored */
/*
* A command that's ignored can be useful. We use it at
* times when we want to postpone redraws. By stuffing
* in a control-o, redraws get suspended until the editor
* gets back around to processing input.
*/
break;
case CTRL('G'):
CLEAROP;
fileinfo();
break;
case K_CGRAVE: /* shorthand command */
CLEAROP;
stuffin(":e #\n");
break;
case 'Z': /* write, if changed, and exit */
if (vgetc() != 'Z') {
beep();
break;
}
doxit();
break;
/*
* Macro evaluates true if char 'c' is a valid identifier character
*/
# define IDCHAR(c) (isalpha(c) || isdigit(c) || (c) == '_')
case CTRL(']'): /* :ta to current identifier */
CLEAROP;
{
char ch;
LPTR save;
save = *Curschar;
/*
* First back up to start of identifier. This
* doesn't match the real vi but I like it a
* little better and it shouldn't bother anyone.
*/
ch = gchar(Curschar);
while (IDCHAR(ch)) {
if (!oneleft())
break;
ch = gchar(Curschar);
}
if (!IDCHAR(ch))
oneright();
stuffin(":ta ");
/*
* Now grab the chars in the identifier
*/
ch = gchar(Curschar);
while (IDCHAR(ch)) {
stuffin(mkstr(ch));
if (!oneright())
break;
ch = gchar(Curschar);
}
stuffin("\n");
*Curschar = save; /* restore, in case of error */
}
break;
/*
* Character motion commands
*/
case 'G':
mtype = MLINE;
*Curschar = *gotoline(Prenum);
beginline(TRUE);
break;
case 'H':
mtype = MLINE;
*Curschar = *Topchar;
for (n = Prenum; n && onedown(1) ;n--)
;
beginline(TRUE);
break;
case 'M':
mtype = MLINE;
*Curschar = *Topchar;
for (n = 0; n < Rows/2 && onedown(1) ;n++)
;
beginline(TRUE);
break;
case 'L':
mtype = MLINE;
*Curschar = *prevline(Botchar);
for (n = Prenum; n && oneup(1) ;n--)
;
beginline(TRUE);
break;
case 'l':
case K_RARROW:
case ' ':
mtype = MCHAR;
mincl = FALSE;
n = DEFAULT1(Prenum);
while (n--) {
if ( ! oneright() )
beep();
}
set_want_col = TRUE;
break;
case 'h':
case K_LARROW:
case CTRL('H'):
mtype = MCHAR;
mincl = FALSE;
n = DEFAULT1(Prenum);
while (n--) {
if ( ! oneleft() )
beep();
}
set_want_col = TRUE;
break;
case '-':
flag = TRUE;
/* fall through */
case 'k':
case K_UARROW:
case CTRL('P'):
mtype = MLINE;
if ( ! oneup(DEFAULT1(Prenum)) )
beep();
if (flag)
beginline(TRUE);
break;
case '+':
case CR:
case NL:
flag = TRUE;
/* fall through */
case 'j':
case K_DARROW:
case CTRL('N'):
mtype = MLINE;
if ( ! onedown(DEFAULT1(Prenum)) )
beep();
if (flag)
beginline(TRUE);
break;
/*
* This is a strange motion command that helps make operators
* more logical. It is actually implemented, but not documented
* in the real 'vi'. This motion command actually refers to "the
* current line". Commands like "dd" and "yy" are really an alternate
* form of "d_" and "y_". It does accept a count, so "d3_" works to
* delete 3 lines.
*/
case '_':
lineop:
mtype = MLINE;
onedown(DEFAULT1(Prenum)-1);
break;
case '|':
mtype = MCHAR;
mincl = TRUE;
beginline(FALSE);
if (Prenum > 0)
*Curschar = *coladvance(Curschar, Prenum-1);
Curswant = Prenum - 1;
break;
/*
* Word Motions
*/
case 'B':
type = 1;
/* fall through */
case 'b':
mtype = MCHAR;
mincl = FALSE;
set_want_col = TRUE;
for (n = DEFAULT1(Prenum); n > 0 ;n--) {
LPTR *pos;
if ((pos = bck_word(Curschar, type)) == NULL) {
beep();
CLEAROP;
break;
} else
*Curschar = *pos;
}
break;
case 'W':
type = 1;
/* fall through */
case 'w':
/*
* This is a little strange. To match what the real vi
* does, we effectively map 'cw' to 'ce', and 'cW' to 'cE'.
* This seems impolite at first, but it's really more
* what we mean when we say 'cw'.
*/
if (operator == CHANGE)
goto doecmd;
mtype = MCHAR;
mincl = FALSE;
set_want_col = TRUE;
for (n = DEFAULT1(Prenum); n > 0 ;n--) {
LPTR *pos;
if ((pos = fwd_word(Curschar, type)) == NULL) {
beep();
CLEAROP;
break;
} else
*Curschar = *pos;
}
break;
case 'E':
type = 1;
/* fall through */
case 'e':
doecmd:
mtype = MCHAR;
mincl = TRUE;
set_want_col = TRUE;
for (n = DEFAULT1(Prenum); n > 0 ;n--) {
LPTR *pos;
/*
* The first motion gets special treatment if we're
* do a 'CHANGE'.
*/
if (n == DEFAULT1(Prenum))
pos = end_word(Curschar,type,operator==CHANGE);
else
pos = end_word(Curschar, type, FALSE);
if (pos == NULL) {
beep();
CLEAROP;
break;
} else
*Curschar = *pos;
}
break;
case '$':
mtype = MCHAR;
mincl = TRUE;
while ( oneright() )
;
Curswant = 999; /* so we stay at the end */
break;
case '^':
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -