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

302 lines
8.3 KiB
C

/* $Header: /nw/tony/src/stevie/src/RCS/undo.c,v 1.7 89/08/06 09:51:06 tony Exp $
*
* Undo facility
*
* The routines in this file comprise a general undo facility for use
* throughout the rest of the editor. The routine u_save() is called
* before each edit operation to save the current contents of the lines
* to be editted. Later, u_undo() can be called to return those lines
* to their original state. The routine u_clear() should be called
* whenever a new file is going to be editted to clear the undo buffer.
*/
#include "stevie.h"
/*
* The next two variables mark the boundaries of the changed section
* of the file. Lines BETWEEN the lower and upper bounds are changed
* and originally contained the lines pointed to by u_lines. To undo
* the last change, insert the lines in u_lines between the lower and
* upper bounds.
*/
static LINE *u_lbound = NULL; /* line just prior to first changed line */
static LINE *u_ubound = NULL; /* line just after the last changed line */
static LINE *u_lline = NULL; /* bounds of the saved lines */
static LINE *u_uline = NULL;
static int u_col;
static bool_t u_valid = FALSE; /* is the undo buffer valid */
/*
* Local forward declarations
*/
static LINE *copyline();
static void u_lsave();
static void u_lfree();
/*
* u_save(l, u) - save the current contents of part of the file
*
* The lines between 'l' and 'u' are about to be changed. This routine
* saves their current contents into the undo buffer. The range l to u
* is not inclusive because when we do an open, for example, there aren't
* any lines in between. If no lines are to be saved, then l->next == u.
*/
void
u_save(l, u)
LINE *l, *u;
{
LINE *nl; /* copy of the current line */
/*
* If l or u is null, there's an error. We don't return an
* indication to the caller. They should find the problem
* while trying to perform whatever edit is being requested
* (e.g. a join on the last line).
*/
if (l == NULL || u == NULL)
return;
u_clear(); /* clear the buffer, first */
u_lsave(l, u); /* save to the "line undo" buffer, if needed */
u_lbound = l;
u_ubound = u;
if (l->next != u) { /* there are lines in the middle */
l = l->next;
u = u->prev;
u_lline = nl = copyline(l); /* copy the first line */
while (l != u) {
nl->next = copyline(l->next);
nl->next->prev = nl;
l = l->next;
nl = nl->next;
}
u_uline = nl;
} else
u_lline = u_uline = NULL;
u_valid = TRUE;
u_col = Cursvcol;
}
/*
* u_saveline() - save the current line in the undo buffer
*/
void
u_saveline()
{
u_save(Curschar->linep->prev, Curschar->linep->next);
}
/*
* u_undo() - effect an 'undo' operation
*
* The last edit is undone by restoring the modified section of the file
* to its original state. The lines we're going to trash are copied to
* the undo buffer so that even an 'undo' can be undone. Rings the bell
* if the undo buffer is empty.
*/
void
u_undo()
{
LINE *tl, *tu;
if (!u_valid) {
beep();
return;
}
/*
* Get the first line of the thing we're undoing on the screen.
*/
Curschar->linep = u_lbound->next;
Curschar->index = 0; /* for now */
if (Curschar->linep == Fileend->linep)
Curschar->linep = Curschar->linep->prev;
cursupdate();
/*
* Save pointers to what's in the file now.
*/
if (u_lbound->next != u_ubound) { /* there are lines to get */
tl = u_lbound->next;
tu = u_ubound->prev;
tl->prev = NULL;
tu->next = NULL;
} else
tl = tu = NULL; /* no lines between bounds */
/*
* Link the undo buffer into the right place in the file.
*/
if (u_lline != NULL) { /* there are lines in the undo buf */
/*
* If the top line of the screen is being undone, we need to
* fix up Topchar to point to the new line that will be there.
*/
if (u_lbound->next == Topchar->linep)
Topchar->linep = u_lline;
u_lbound->next = u_lline;
u_lline->prev = u_lbound;
u_ubound->prev = u_uline;
u_uline->next = u_ubound;
} else { /* no lines... link the bounds */
if (u_lbound->next == Topchar->linep)
Topchar->linep = u_ubound;
if (u_lbound == Filetop->linep)
Topchar->linep = u_ubound;
u_lbound->next = u_ubound;
u_ubound->prev = u_lbound;
}
/*
* If we swapped the top line, patch up Filemem appropriately.
*/
if (u_lbound == Filetop->linep)
Filemem->linep = Filetop->linep->next;
/*
* Now save the old stuff in the undo buffer.
*/
u_lline = tl;
u_uline = tu;
renum(); /* have to renumber everything */
/*
* Put the cursor on the first line of the 'undo' region.
*/
Curschar->linep = u_lbound->next;
Curschar->index = 0;
if (Curschar->linep == Fileend->linep)
Curschar->linep = Curschar->linep->prev;
*Curschar = *coladvance(Curschar, u_col);
cursupdate();
updatescreen(); /* now show the change */
u_lfree(); /* clear the "line undo" buffer */
}
/*
* u_clear() - clear the undo buffer
*
* This routine is called to clear the undo buffer at times when the
* pointers are about to become invalid, such as when a new file is
* about to be editted.
*/
void
u_clear()
{
LINE *l, *nextl;
if (!u_valid) /* nothing to do */
return;
for (l = u_lline; l != NULL ;l = nextl) {
nextl = l->next;
free(l->s);
free((char *)l);
}
u_lbound = u_ubound = u_lline = u_uline = NULL;
u_valid = FALSE;
}
/*
* The following functions and data implement the "line undo" feature
* performed by the 'U' command.
*/
static LINE *u_line; /* pointer to the line we last saved */
static LINE *u_lcopy = NULL; /* local copy of the original line */
/*
* u_lfree() - free the line save buffer
*/
static void
u_lfree()
{
if (u_lcopy != NULL) {
free(u_lcopy->s);
free((char *)u_lcopy);
u_lcopy = NULL;
}
u_line = NULL;
}
/*
* u_lsave() - save the current line if necessary
*/
static void
u_lsave(l, u)
LINE *l, *u;
{
if (l->next != u->prev) { /* not changing exactly one line */
u_lfree();
return;
}
if (l->next == u_line) /* more edits on the same line */
return;
u_lfree();
u_line = l->next;
u_lcopy = copyline(l->next);
}
/*
* u_lundo() - undo the current line (the 'U' command)
*/
void
u_lundo()
{
if (u_lcopy != NULL) {
free(Curschar->linep->s);
Curschar->linep->s = u_lcopy->s;
Curschar->linep->size = u_lcopy->size;
free((char *)u_lcopy);
} else
beep();
Curschar->index = 0;
cursupdate();
updatescreen(); /* now show the change */
u_lcopy = NULL; /* can't undo this kind of undo */
u_line = NULL;
}
/*
* u_lcheck() - clear the "line undo" buffer if we've moved to a new line
*/
void
u_lcheck()
{
if (Curschar->linep != u_line)
u_lfree();
}
/*
* copyline(l) - copy the given line, and return a pointer to the copy
*/
static LINE *
copyline(l)
LINE *l;
{
LINE *nl; /* the new line */
nl = newline(strlen(l->s));
strcpy(nl->s, l->s);
return nl;
}