* This file defines the operation sequences which interface the
* logical changes to the file buffer with the internal and external
* display representations.
* Undo is accomplished in two ways. We often for small changes in the
* current line know how (in terms of a change operator) how the change
* occurred. Thus on an intelligent terminal we can undo the operation
* by another such operation, using insert and delete character
* stuff. The pointers vU[AD][12] index the buffer vutmp when this
* is possible and provide the necessary information.
* The other case is that the change involved multiple lines or that
* we have moved away from the line or forgotten how the change was
* accomplished. In this case we do a redisplay and hope that the
* low level optimization routines (which don't look for winning
* via insert/delete character) will not lose too badly.
* Avoid UU which clobbers ability to do u.
if (vundkind
== VCAPU
|| vUNDdot
!= dot
) {
vUD1
= linebuf
; vUD2
= strend(linebuf
);
vUA1
= linebuf
; vUA2
= strend(linebuf
);
if (state
== ONEOPEN
|| state
== HARDOPEN
) {
vjumpto(dot
, vUNDcurs
, 0);
bool show
; /* if true update the screen */
if (undkind
== UNDPUT
&& undap1
== undap2
) {
* Undo() call below basically replaces undap1 to undap2-1
* with dol through unddol-1. Hack screen image to
* reflect this replacement.
vreplace(undap1
- addr
, undap2
- undap1
,
undkind
== UNDPUT
? 0 : unddol
- dol
);
if (show
&& (vundkind
!= VMCHNG
|| addr
!= dot
))
if (cnt
< 0 || cnt
> vcnt
|| state
!= VISUAL
) {
vjumpto(dot
, NOSTR
, '.');
doomed
= column(vUA2
- 1) - column(vUA1
- 1);
cp
= vUA1
; vUA1
= vUD1
; vUD1
= cp
;
cp
= vUA2
; vUA2
= vUD2
; vUD2
= cp
;
OO
= Outchar
; Outchar
= vinschar
; hold
|= HOLDQIK
;
temp
[vUA2
- linebuf
] = 0;
for (cp
= &temp
[vUA1
- linebuf
]; *cp
;)
Outchar
= OO
; hold
= oldhold
;
physdc(cindent(), cindent() + doomed
);
if (cursor
> linebuf
&& cursor
>= strend(linebuf
))
* Routine to handle a change inside a macro.
* Fromvis is true if we were called from a visual command (as
* opposed to an ex command). This has nothing to do with being
* in open/visual mode as :s/foo/bar is not fromvis.
fprintf(trace
, "vmacchng, vch_mac=%d, linebuf='%s', *dot=%o\n", vch_mac
, linebuf
, *dot
);
fprintf(trace
, "after vsave, linebuf='%s', *dot=%o\n", linebuf
, *dot
);
/* Save current state somewhere */
vudump("before vmacchng hairy case");
savedot
= dot
; savedol
= dol
; savecursor
= cursor
;
while ((line
*) endcore
- truedol
< nlines
)
copyw(truedol
+1, zero
+1, nlines
);
/* Restore state as it was at beginning of macro */
/* Do the saveall we should have done then */
/* Restore current state from where saved */
more
= savedol
- dol
; /* amount we shift everything by */
(*(more
>0 ? copywR
: copyw
))(savedol
+1, dol
+1, truedol
-dol
);
unddol
+= more
; truedol
+= more
; undap2
+= more
;
copyw(zero
+1, truedol
+1, nlines
);
dot
= savedot
; dol
= savedol
; cursor
= savecursor
;
/* Arrange that no further undo saving happens within macro */
otchng
= tchng
; /* Copied this line blindly - bug? */
inopen
= -1; /* no need to save since it had to be 1 or -1 before */
vudump("after vmacchng");
/* Nothing to do for various reasons. */
* Initialize undo information before an append.
* All the rest of the motion sequences have one or more
* cases to deal with. In the case wdot == 0, operation
* is totally within current line, from cursor to wcursor.
* If wdot is given, but wcursor is 0, then operation affects
* the inclusive line range. The hardest case is when both wdot
* and wcursor are given, then operation affects from line dot at
* cursor to line wdot at wcursor.
* Move is simple, except for moving onto new lines in hardcopy open mode.
if (wdot
< one
|| wdot
> dol
) {
* When we move onto a new line, save information for U undo.
* In hardcopy open, type characters to left of cursor
* on new line, or back cursor up if its to left of where we are.
* In any case if the current line is ``rubbled'' i.e. has trashy
* looking overstrikes on it or \'s from deletes, we reprint
* so it is more comprehensible (and also because we can't work
* if we let it get more out of sync since column() won't work right.
vreopen(WTOP
, lineDOT(), vcline
);
} else if (wcursor
> cursor
) {
for (cp
= cursor
; *cp
&& cp
< wcursor
;) {
register int c
= *cp
++ & TRIM
;
* Hard case of deleting a range where both wcursor and wdot
* are specified is treated as a special case of change and handled
* by vchange (although vchange may pass it back if it degenerates
* to a full line range delete.)
if (cp
> linebuf
&& (cp
[0] == 0 || c
== '#'))
physdc(column(cursor
- 1), i
);
vreopen(LINE(vcline
), lineDOT(), vcline
);
* In a single line we mark the end of the changed area with '$'.
* On multiple whole lines, we clear the lines first.
* Across lines with both wcursor and wdot given, we delete
* and sync then append (but one operation for undo).
register int i
, ind
, cnt
;
* Change/delete of lines or across line boundaries.
if (wcursor
&& cnt
== 1) {
* Across line boundaries, but not
* necessarily whole lines.
* Construct what will be left.
if (strlen(genbuf
) + strlen(wcursor
) > LBSIZE
- 2) {
if (c
== 'd' && *vpastwh(genbuf
) == 0) {
* Although this is a delete
* spanning line boundaries, what
* would be left is all white space,
} else if (c
== 'd' && wcursor
== 0) {
* We are just substituting text for whole lines,
* so determine the first autoindent.
if (value(LISP
) && value(AUTOINDENT
))
i
= vcline
>= 0 ? LINE(vcline
) : WTOP
;
* Delete the lines from the buffer,
* and remember how the partial stuff came about in
* case we are told to put.
* If DEL[0] were nonzero, put would put it back
* rather than the deleted lines.
* Now hack the screen image coordination.
vreplace(vcline
, cnt
, 0);
* If this is a across line delete/change,
* cursor stays where it is; just splice together the pieces
* of the new line. Otherwise generate a autoindent
* Indicate a change on hardcopies by
* erasing the current line.
if (c
!= 'd' && state
!= VISUAL
&& state
!= HARDOPEN
) {
hold
|= HOLDAT
, vclrlin(i
, dot
), hold
= oldhold
;
* Open the line (logically) on the screen, and
* update the screen tail. Unless we are really a delete
* go off and gather up inserted characters.
if (*cursor
== 0 && cursor
> linebuf
)
* The rest of this is just low level hacking on changes
* of small numbers of characters.
* Put out the \\'s indicating changed text in hardcopy,
* or mark the end of the change with $ if not hardcopy.
* Remember the deleted text for possible put,
* and then prepare and execute the input portion of the change.
vgoto(outline, value(NUMBER) << 3);
* Tricky thing here is slowopen. This causes display updating
* to be held off so that 300 baud dumb terminals don't lose badly.
* This also suppressed counts, which otherwise say how many blank
* space to open up. Counts are also suppressed on intelligent terminals.
* Actually counts are obsoleted, since if your terminal is slow
* you are better off with slowopen.
int c
; /* mjm: char --> int */
if (value(SLOWOPEN
) || value(REDRAW
) && AL
&& DL
)
oldmask
= sigblock(sigmask(SIGWINCH
));
c
= vcline
< 0 ? WTOP
- cnt
: LINE(vcline
) + DEPTH(vcline
);
i
= LINE(vcline
+ 1) - c
;
if (i
< cnt
&& c
<= WBOT
&& (!AL
|| !DL
))
vinslin(c
, cnt
- i
, vcline
);
(void)sigsetmask(oldmask
);
* > < and = shift operators.
* Note that =, which aligns lisp, is just a ragged sort of shift,
* since it never distributes text between lines.
char vshnam
[2] = { 'x', 0 };
vreplace(vcline
, cnt
, cnt
);
* Filter portions of the buffer through unix commands.
oglobp
= globp
; globp
= genbuf
+ 1;
ungetchar(d
); globp
= oglobp
;
vgoto(WECHO
, 0); flusho();
if (dot
== zero
&& dol
> zero
)
* BUG: we shouldn't be depending on what undap2 and undap1 are,
* since we may be inside a macro. What's really wanted is the
* number of lines we read from the filter. However, the mistake
* will be an overestimate so it only results in extra work,
* it shouldn't cause any real screwups.
vreplace(vcline
, cnt
, undap2
- undap1
);
* Xdw exchanges dot and wdot if appropriate and also checks
* that wdot is reasonable. Its name comes from
if (wdot
== NOLINE
|| wdot
< one
|| wdot
> dol
) {
if (dot
> wdot
|| (dot
== wdot
&& wcursor
!= 0 && cursor
> wcursor
)) {
addr
= dot
; dot
= wdot
; wdot
= addr
;
cp
= cursor
; cursor
= wcursor
; wcursor
= cp
;
* If a region is specified but wcursor is at the begining
* of the last line, then we move it to be the end of the
* previous line (actually off the end).
if (cursor
&& wcursor
== linebuf
&& wdot
> dot
) {
if (vpastwh(linebuf
) >= cursor
)
wcursor
= strend(linebuf
);
* Should prepare in caller for possible dot == wdot.
vremote(cnt
, YANKreg
, vreg
);
* Kill buffer code. If delete operator is c or d, then save
* the region in numbered buffers.
* BUG: This may be somewhat inefficient due
* to the way named buffer are implemented,
* necessitating some optimization.
vremote(cnt
, YANKreg
, '1');
* Routine for vremote to call to implement shifts.
* Replace a single character with the next input character.
* A funny kind of insert.
if (cnt
> strlen(cursor
)) {
i
= column(cursor
+ cnt
- 1);
vUD1
= cursor
; vUD2
= wcursor
;
* Yanking to string registers occurs for free (essentially)
if (notecnt
<= vcnt
- vcline
&& notecnt
< value(REPORT
))
* Set pkill variables so a put can
* know how to put back partial text.
* This is necessary because undo needs the complete
* line images to be saved, while a put wants to trim
* the first and last lines. The compromise
* is for put to be more clever.