home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World of Computer Software
/
World_Of_Computer_Software-02-387-Vol-3of3.iso
/
x
/
xvisrc.zoo
/
edit.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-07-28
|
14KB
|
623 lines
/* Copyright (c) 1990,1991,1992 Chris and John Downey */
#ifndef lint
static char *sccsid = "@(#)edit.c 2.4 (Chris & John Downey) 8/26/92";
#endif
/***
* program name:
xvi
* function:
PD version of UNIX "vi" editor, with extensions.
* module name:
edit.c
* module function:
Insert and replace mode handling.
* history:
STEVIE - ST Editor for VI Enthusiasts, Version 3.10
Originally by Tim Thompson (twitch!tjt)
Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
Heavily modified by Chris & John Downey
***/
#include "xvi.h"
static void end_replace P((int));
/*
* Position of start of insert. This is used
* to prevent backing up past the starting point.
*/
static Posn Insertloc;
/*
* This flexbuf is used to hold the current insertion text.
*/
static Flexbuf Insbuff;
/*
* Replace-mode stuff.
*/
static enum {
replace_one, /* replace command was an 'r' */
got_one, /* normal ending state for replace_one */
overwrite /* replace command was an 'R' */
} repstate;
static char *saved_line; /*
* record of old line before replace
* started; note that, if
* (repstate != overwrite), this
* should never be referenced.
*/
static int nchars; /* no of chars in saved_line */
static int start_index; /* index into line where we entered replace */
static int start_column; /* virtual col corresponding to start_index */
/*
* Process the given character, in insert mode.
*/
bool_t
i_proc(c)
int c;
{
register Posn *curpos;
static bool_t literal_next = FALSE;
static bool_t wait_buffer = FALSE;
curpos = curwin->w_cursor;
if (wait_buffer || (!literal_next && c == CTRL('A'))) {
/*
* Add contents of named buffer, or the last
* insert buffer if CTRL('A') was typed.
*/
if (!wait_buffer) {
c = '<';
}
yp_stuff_input(curwin, c, TRUE);
wait_buffer = FALSE;
return(FALSE);
} else if (!literal_next) {
/*
* This switch is for special characters; we skip over
* it for normal characters, or for literal-next mode.
*/
switch (c) {
case ESC: /* an escape ends input mode */
{
char *cltext;
cltext = curpos->p_line->l_text;
curwin->w_set_want_col = TRUE;
/*
* If there is only auto-indentation
* on the current line, delete it.
*/
if (curpos->p_index == indentchars &&
cltext[indentchars] == '\0') {
replchars(curwin, curpos->p_line, 0, indentchars, "");
begin_line(curwin, FALSE);
}
indentchars = 0;
/*
* 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.
*/
while (one_left(curwin, FALSE) &&
gchar(curwin->w_cursor) == '\0') {
;
}
State = NORMAL;
end_command(curwin);
(void) yank_str('<', flexgetstr(&Insbuff), FALSE);
flexclear(&Insbuff);
if (!(echo & e_CHARUPDATE)) {
echo |= e_CHARUPDATE;
move_window_to_cursor(curwin);
cursupdate(curwin);
}
update_buffer(curbuf);
return(TRUE);
}
case CTRL('T'):
case CTRL('D'):
/*
* If we're at the beginning of the line, or just
* after autoindent characters, move one shiftwidth
* left (CTRL('D')) or right (CTRL('T')).
*/
if (curpos->p_index <= indentchars) {
Line *lp;
int ind;
lp = curpos->p_line;
ind = get_indent(curpos->p_line);
ind += (c == CTRL('D') ? (ind < Pn(P_shiftwidth) ?
-ind :
-Pn(P_shiftwidth)) :
Pn(P_shiftwidth));
indentchars = set_indent(lp, ind);
move_cursor(curwin, curpos->p_line, indentchars);
cursupdate(curwin);
updateline(curwin);
(void) flexaddch(&Insbuff, c);
} else {
beep(curwin);
}
return(TRUE);
case '\b':
case DEL:
/*
* Can't backup past starting point.
*/
if (curpos->p_line == Insertloc.p_line &&
curpos->p_index <= Insertloc.p_index) {
beep(curwin);
return(TRUE);
}
/*
* Can't backup to a previous line.
*/
if (curpos->p_line != Insertloc.p_line && curpos->p_index <= 0) {
beep(curwin);
return(TRUE);
}
(void) one_left(curwin, FALSE);
if (curpos->p_index < indentchars)
indentchars--;
replchars(curwin, curpos->p_line, curpos->p_index, 1, "");
(void) flexaddch(&Insbuff, '\b');
cursupdate(curwin);
if (curwin->w_col == 0) {
/*
* Make sure backspacing over a physical line
* break updates the screen correctly.
*/
update_buffer(curbuf);
} else {
updateline(curwin);
}
return(TRUE);
case '\r':
case '\n':
{
int i;
int previndex;
Line *prevline;
(void) flexaddch(&Insbuff, '\n');
i = indentchars;
prevline = curpos->p_line;
previndex = curpos->p_index;
if (openfwd(TRUE) == FALSE) {
stuff("%c", ESC);
show_error(curwin,
"No buffer space - returning to command mode");
return(TRUE);
}
/*
* If the previous line only had
* auto-indent on it, delete it.
*/
if (i == previndex) {
replchars(curwin, prevline, 0, i, "");
}
move_window_to_cursor(curwin);
cursupdate(curwin);
update_buffer(curbuf);
}
return(TRUE);
case CTRL('B'):
wait_buffer = TRUE;
return(FALSE);
break;
case CTRL('V'):
(void) flexaddch(&Insbuff, CTRL('V'));
literal_next = TRUE;
return(FALSE);
}
}
/*
* If we get here, we want to insert the character into the buffer.
*/
/*
* We may already have been in literal-next mode.
* Careful not to reset this until after we need it.
*/
literal_next = FALSE;
/*
* Deal with wrapmargin.
*
* OK, so it isn't really right yet.
*/
if ((c == ' ' || c == '\t') && Pn(P_wrapmargin) != 0 &&
curwin->w_cursor->p_index >= curwin->w_ncols - Pn(P_wrapmargin)) {
(void) i_proc('\n');
/*
* We shouldn't really be putting the newline
* in the redo buffer, so we change it to the
* character we actually got.
*/
flexrmchar(&Insbuff);
(void) flexaddch(&Insbuff, c);
return(TRUE);
}
/*
* Do the actual insertion of the new character.
*/
replchars(curwin, curpos->p_line, curpos->p_index, 0, mkstr(c));
/*
* Update the screen.
*/
s_inschar(curwin, c);
updateline(curwin);
/*
* Put the character into the insert buffer.
*/
(void) flexaddch(&Insbuff, c);
/*
* If showmatch mode is set, check for right parens
* and braces. If there isn't a match, then beep.
* If there is a match AND it's on the screen,
* flash to it briefly.
*
* These characters included to make this
* source file work okay with showmatch: [ { (
*/
if (Pb(P_showmatch) && (c == ')' || c == '}' || c == ']')) {
Posn *lpos, csave;
lpos = showmatch();
if (lpos == NULL) {
beep(curwin);
} else if (!earlier(lpos->p_line, curwin->w_topline) &&
earlier(lpos->p_line, curwin->w_botline)) {
/*
* Show the match if it's on screen.
*/
update_buffer(curbuf);
csave = *curpos;
move_cursor(curwin, lpos->p_line, lpos->p_index);
cursupdate(curwin);
wind_goto(curwin);
delay();
move_cursor(curwin, csave.p_line, csave.p_index);
cursupdate(curwin);
}
}
/*
* Finally, move the cursor right one space.
*/
(void) one_right(curwin, TRUE);
return(TRUE);
}
/*
* This function is the interface provided for functions in
* normal mode to go into insert mode. We only come out of
* insert mode when the user presses escape.
*
* The parameter is a flag to say whether we should start
* at the start of the line.
*
* Note that we do not have to call start_command() as the
* caller does that for us - this is so the caller can include
* any other stuff (e.g. an initial delete) into the command.
*/
void
startinsert(startln)
int startln; /* if set, insert point really at start of line */
{
Insertloc = *curwin->w_cursor;
if (startln)
Insertloc.p_index = 0;
flexclear(&Insbuff);
State = INSERT;
}
/*
* Process the given character, in replace mode.
*/
bool_t
r_proc(c)
int c;
{
Posn *curpos;
static bool_t literal_next = FALSE;
static bool_t wait_buffer = FALSE;
curpos = curwin->w_cursor;
if (wait_buffer || (!literal_next && c == CTRL('A'))) {
/*
* Add contents of named buffer, or the last
* insert buffer if CTRL('A') was typed.
*/
if (!wait_buffer) {
c = '<';
}
yp_stuff_input(curwin, c, TRUE);
wait_buffer = FALSE;
return(FALSE);
} else if (!literal_next) {
switch (c) {
case ESC: /* an escape ends input mode */
end_replace(c);
return(TRUE);
case '\b': /* back space */
case DEL:
if (repstate == overwrite &&
curwin->w_virtcol > start_column) {
(void) one_left(curwin, FALSE);
replchars(curwin, curpos->p_line,
curpos->p_index, 1,
(curpos->p_index < nchars) ?
mkstr(saved_line[curpos->p_index]) : "");
updateline(curwin);
(void) flexaddch(&Insbuff, '\b');
} else {
beep(curwin);
if (repstate == replace_one) {
end_replace(c);
}
}
return(TRUE);
case K_LARROW: /* left arrow */
if (repstate == overwrite && curwin->w_virtcol > start_column &&
one_left(curwin, FALSE)) {
(void) flexaddch(&Insbuff, c);
return(TRUE);
} else {
beep(curwin);
if (repstate == replace_one) {
end_replace(c);
}
return(FALSE);
}
case K_RARROW: /* right arrow */
if (repstate == overwrite && one_right(curwin, FALSE)) {
(void) flexaddch(&Insbuff, c);
return(TRUE);
} else {
beep(curwin);
if (repstate == replace_one) {
end_replace(c);
}
return(FALSE);
}
case '\r': /* new line */
case '\n':
if (curpos->p_line->l_next == curbuf->b_lastline &&
repstate == overwrite) {
/*
* Don't allow splitting of last line of
* buffer in overwrite mode. Why not?
*/
beep(curwin);
return(TRUE);
}
if (repstate == replace_one) {
echo &= ~e_CHARUPDATE;
if (openfwd(TRUE) == FALSE) {
show_error(curwin, "No buffer space!");
return(TRUE);
}
(void) flexaddch(&Insbuff, c);
/*
* Having split the line, we must
* delete the character which was
* supposed to be replaced with
* the newline.
*/
replchars(curwin, curpos->p_line, curpos->p_index, 1, "");
repstate = got_one;
end_replace('\n');
} else {
(void) flexaddch(&Insbuff, '\n');
(void) onedown(curwin, 1L);
/*
* This is wrong, but it's difficult
* to get it right.
*/
coladvance(curwin, start_column);
free(saved_line);
saved_line = strsave(curpos->p_line->l_text);
if (saved_line == NULL) {
State = NORMAL;
return(TRUE);
}
nchars = strlen(saved_line);
}
return(TRUE);
case CTRL('B'):
wait_buffer = TRUE;
return(FALSE);
break;
case CTRL('V'):
(void) flexaddch(&Insbuff, CTRL('V'));
literal_next = TRUE;
return(TRUE);
}
}
/*
* If we get here, we want to insert the character into the buffer.
*/
/*
* We may already have been in literal-next mode.
* Careful not to reset this until after we need it.
*/
literal_next = FALSE;
/*
* Put the character into the insert buffer.
*/
(void) flexaddch(&Insbuff, c);
if (repstate == overwrite || repstate == replace_one) {
replchars(curwin, curpos->p_line, curpos->p_index, 1, mkstr(c));
updateline(curwin);
(void) one_right(curwin, TRUE);
}
/*
* If command was an 'r', leave replace mode after one character.
*/
if (repstate == replace_one) {
repstate = got_one;
end_replace(c);
}
return(TRUE);
}
/*
* This function is the interface provided for functions in
* normal mode to go into replace mode. We only come out of
* replace mode when the user presses escape, or when they
* used the 'r' command and typed a single character.
*
* The parameter is the command character which took us into replace mode.
*/
void
startreplace(c)
int c;
{
if (!start_command(curwin)) {
return;
}
start_index = curwin->w_cursor->p_index;
start_column = curwin->w_virtcol;
if (c == 'r') {
repstate = replace_one;
saved_line = NULL;
} else {
repstate = overwrite;
saved_line = strsave(curwin->w_cursor->p_line->l_text);
if (saved_line == NULL) {
beep(curwin);
end_command(curwin);
return;
}
nchars = strlen(saved_line);
/*
* Initialize Insbuff. Note that we don't do this if the
* command was 'r', because they might type ESC to abort
* the command, in which case we shouldn't change Insbuff.
*/
flexclear(&Insbuff);
}
State = REPLACE;
}
static void
end_replace(c)
int c;
{
Posn *curpos;
char *cltext;
curpos = curwin->w_cursor;
State = NORMAL;
end_command(curwin);
/*
* If (repstate == replace_one), they must have typed 'r', then
* thought better of it & typed ESC; so we shouldn't complain or
* change the buffer, the cursor position, or Insbuff.
*/
if (repstate != replace_one) {
(void) yank_str('<', flexgetstr(&Insbuff), FALSE);
flexclear(&Insbuff);
/*
* Free the saved line if necessary.
*/
if (repstate == overwrite) {
free(saved_line);
}
/*
* The cursor should end up on the
* last replaced character.
*/
cltext = curpos->p_line->l_text;
while (one_left(curwin, FALSE) && gchar(curwin->w_cursor) == '\0') {
;
}
if (!(echo & e_CHARUPDATE)) {
echo |= e_CHARUPDATE;
move_window_to_cursor(curwin);
cursupdate(curwin);
}
update_buffer(curbuf);
}
}
char *
mkstr(c)
int c;
{
static char s[2];
s[0] = c;
s[1] = '\0';
return(s);
}