/************************************************************************ * This program is Copyright (C) 1986 by Jonathan Payne. JOVE is * * provided to you without charge, and with no warranty. You may give * * away copies of JOVE, including sources, provided that this notice is * * included in all the files. * ************************************************************************/ /* Routines to perform all kinds of deletion. */ #include "jove.h" RCS("$Id: delete.c,v 14.31.0.12 1994/06/22 07:58:19 tom Exp tom $") #include "ctype.h" /* Assumes that either line1 or line2 is actual the current line, so it can put its result into linebuf. */ private void patchup __(( Line *_(line1), int _(char1), Line *_(line2), int _(char2) )); private void patchup(line1, char1, line2, char2) register Line *line1, *line2; register int char1, char2; { if (line1 == line2 && char1 == char2) return; /* Nothing to do, really... */ DotTo(line1, char1); modify(); /* line1 (!) */ linecopy(linebuf, char1, lcontents(line2) + char2); if (line1 != line2) { ChkWindows(line1, line2); /* The following is a redisplay optimization. */ if (char1 == 0 && char2 == 0) line1->l_dline = line2->l_dline; } DFixMarks(line1, char1, line2, char2); makedirty(line1); /* curline */ } /* Create a new Line list that contains a single Line. */ private Line *newlist __(( const char *contents )); private Line * newlist(contents) const char *contents; { register Line *nl = nbufline(); nl->l_prev = NULL; nl->l_next = NULL; SavLine(nl, contents); return nl; } /* Unlinks the region by unlinking the lines in the middle, and patching things up. The unlinked lines are still in order. [TRH formerly reg_delete] -- expects that region is in order. */ private Line * reg_unlink __(( Line *_(line1), int _(char1), Line *_(line2), int _(char2) )); private Line * reg_unlink(l1, char1, l2, char2) Line *l1, *l2; { register Line *line1 = l1, *line2 = l2, *retline; ltobuf(line1, genbuf); if (line1 == line2) genbuf[char2] = '\0'; /* Shorten this line */ patchup(line1, char1, line2, char2); retline = newlist(&genbuf[char1]); if (line1 != line2) { retline->l_next = line1->l_next; ltobuf(line2, genbuf); genbuf[char2] = '\0'; /* Shorten this line */ SavLine(line2, genbuf); if (line1->l_next = line2->l_next) line1->l_next->l_prev = line1; else curbuf->b_last = line1; line2->l_next = NULL; } return retline; } /* Deletes a region by patching things up, and REMOVING the lines in the middle (these are irretrievably lost). Region does not have to be in order. [TRH - replaces lremove] */ void reg_delete(l1, char1, l2, char2) Line *l1, *l2; { register Line *next, *line1, *line2; fixorder(&l1, &char1, &l2, &char2); line1 = l1; line2 = l2; patchup(line1, char1, line2, char2); if (line1 == line2) return; next = line1->l_next; if (line1->l_next = line2->l_next) line1->l_next->l_prev = line1; else curbuf->b_last = line1; lfreereg(next, line2); /* Put region at end of free line list. */ } /* This kills a region between point, and line1/char1 and puts it on the kill-ring. If the last command was one of the kill commands, the region is appended (prepended if backwards) to the last entry. */ Line *killbuf[NUMKILLS]; Line **killptr = killbuf; private Line **adv_killptr __(( void )); private Line ** adv_killptr() { register Line **kp = killptr; *kp++; if (kp == &killbuf[NUMKILLS]) kp -= NUMKILLS; killptr = kp; lfreelist(*kp); *kp = NULL; return kp; } void reg_kill(line2, char2, dot_moved) Line *line2; { int forwards; int readonly; Bufpos bp1; register Bufpos *p1 = &bp1; DOTsave(p1); if (p1->p_line == line2 && p1->p_char == char2) complain((char *) 0); /* Kill commands are handled specially in read-only buffers: just copy the text to the kill ring, don't remove it from the buffer. And move the cursor if necessary. [Note that kill commands should NOT be marked as EDIT commands or else we would never get here!] */ if ((readonly = MinorMode(View))) { if (!dot_moved) DotTo(line2, char2); } forwards = fixorder(&p1->p_line, &p1->p_char, &line2, &char2); /* This is a kludge! But it possible for commands that don't know which direction they are deleting in (e.g., delete previous word could have been called with a negative argument in which case, it wouldn't know that it really deleted forward. [TRH - and reg_unlink expects region to be in order] */ if (last_cmd != KILLCMD) { register Line **kp = adv_killptr(); if (!readonly) { *kp = reg_unlink(p1->p_line, p1->p_char, line2, char2); this_cmd = KILLCMD; return; } *kp = newlist(NullStr); /* Fall through... */ } /*else*/ { register Line *kline = *killptr; register int kchar = 0; if (!dot_moved) forwards ^= YES; if (forwards) { /* append at end of kill region */ kline = lastline(kline); kchar = length(kline); } DoYank(p1->p_line, p1->p_char, line2, char2, kline, kchar, (Buffer *) 0); if (!readonly) reg_delete(p1->p_line, p1->p_char, line2, char2); } this_cmd = KILLCMD; } DEF_CMD( "kill-region", DelReg, EDIT ) { register Mark *mp = CurMark(); reg_kill(mp->m_line, mp->m_char, NO); } /* should region be saved automatically on region-changing commands? */ DEF_INT( "auto-save-region", SaveRegion, V_BOOL ) ZERO; /* Save a region. A pretend kill. */ DEF_CMD( "copy-region", CopyRegion, NO ) { register Line *nl; Bufpos r[2]; CurRegion(r); if (r[0].p_line == r[1].p_line && r[0].p_char == r[1].p_char) complain((char *) 0); (*adv_killptr()) = nl = newlist(NullStr); DoYank(r[0].p_line, r[0].p_char, r[1].p_line, r[1].p_char, nl, 0, (Buffer *) 0); s_mess("[Region saved]"); } /* Delete character forward */ DEF_CMD( "delete-next-character", DelNChar, NO/*EDIT--potential kill command*/ ) { register Buffer *cb = curbuf; register Line *line1 = cb->b_dot; register int char1 = cb->b_char; if (exp_p == YES) { /* ...even if 1 character! */ ForChar(); /* skip */ reg_kill(line1, char1, YES); } else { if (BufMinorMode(cb, View)) /* inline check for speed. */ view_mode_check(); ForChar(); /* skip */ if (line1 == cb->b_dot && char1 == cb->b_char) complain((char *) 0); reg_delete(line1, char1, cb->b_dot, cb->b_char); } } /* Delete character backward */ DEF_CMD( "delete-previous-character", DelPChar, NO/*EDIT--potential kill command*/|NEGATE ) { register Buffer *cb = curbuf; register int num; if ((num = exp) >= 0 || !BufMinorMode(cb, OverWrite)) { DelNChar(); return; } /* * Feb 90 [TRH] tabs need special attention, so we cannot * just use SelfInsert. */ view_mode_check(); if (!bolp(cb)) { modify(); makedirty(cb->b_dot); do { if (linebuf[--cb->b_char] == '\t') { exp = TabIncr(calc_pos(linebuf, cb->b_char)) - 1; Insert(' '); } linebuf[cb->b_char] = ' '; } while (++num < 0 && !bolp(cb)); } } /* Delete whitespace from current line in the given `direction'. (in both directions if zero, i.e., (FORWARD + BACKWARD) */ void del_whitespace(direction) int direction; { register Buffer *cb = curbuf; register char *ep = &linebuf[cb->b_char], *sp = ep; if (direction >= FORWARD + BACKWARD) { while (isspace(*ep++)) ; --ep; } if (direction <= FORWARD + BACKWARD) { while (sp > linebuf) { if (!isspace(*--sp)) { ++sp; break; } } } if (sp != ep) { modify(); makedirty(cb->b_dot); DFixMarks(cb->b_dot, cb->b_char = (int)(sp - linebuf), cb->b_dot, (int)(ep - linebuf)); strcpy(sp, ep); } } DEF_CMD( "delete-white-space", DelWtSpace, EDIT ) { del_whitespace(exp_p ? exp : FORWARD + BACKWARD); } DEF_CMD( "delete-blank-lines", DelBlnkLines, EDIT ) { register Buffer *cb = curbuf; register Line *line1 = cb->b_dot, *line2 = line1; register char first_blank; if (!blnkp(&linebuf[cb->b_char])) return; if (first_blank = blnkp(linebuf)) { do { if ((line1 = line1->l_prev) == NULL) break; SetLine(line1); } while (blnkp(linebuf)); } Eol(); del_whitespace(FORWARD + BACKWARD); while ((line1 = line2->l_next) && blnkp(lcontents(line1))) line2 = line1; /* * Now dot points to the start of the blank region to be deleted, * and line2 points to the last blank line of this region. Keep a * single blank line if original dot was on a blank line. */ if (first_blank && cb->b_char) /* i.e. not when at start of buffer */ line_move(FORWARD); reg_delete(cb->b_dot, cb->b_char, line2, length(line2)); } /* If argument is specified, kill that many lines down. Otherwise, if we "appear" to be at the end of a line, i.e. everything to the right of the cursor is white space, we delete the line separator as if we were at the end of the line. */ DEF_CMD( "kill-to-end-of-line", KillEOL, NO/*EDIT--kill command*/ ) { register Buffer *cb = curbuf; register Line *line2 = cb->b_dot; /* reasonable defaults */ register int char2 = 0; if (exp_p) { if (exp) { line2 = next_line(line2, exp); if ((LineDist(cb->b_dot, line2) < exp) || (line2 == cb->b_dot)) char2 = length(line2); } /* else Kill to beginning of line */ } else { if (!blnkp(&linebuf[cb->b_char]) || ((line2 = line2->l_next) == NULL && (line2 = cb->b_dot, TRUE))) char2 = length(line2); } reg_kill(line2, char2, NO); } /* Kill the region spanned by the `move' function. If numeric argument is zero, move in the opposite direction first, so that the entire entity (e.g., a word) is killed. */ private void mv_reg_kill __(( void (*_(move))(void) )); private void mv_reg_kill(move) void (*move)__((void)); { register Buffer *cb = curbuf; register Line *line1; register int char1; if (exp == 0) { exp = (LastCmd->Type & NEGATE) ? -1 : 1; (*move)(); exp = -exp; } line1 = cb->b_dot; char1 = cb->b_char; (*move)(); reg_kill(line1, char1, YES); } /* ...with this in hand, the following are of a refreshing simplicity. */ DEF_CMD( "kill-next-word", DelNWord, NO/*EDIT--kill command*/ ); DEF_CMD( "kill-previous-word", DelNWord, NO/*EDIT--kill command*/|NEGATE ) { extern void ForWord __(( void )); mv_reg_kill(ForWord); } DEF_CMD( "kill-to-beginning-of-sentence", KillEos, NO/*EDIT--kill command*/|NEGATE ); DEF_CMD( "kill-to-end-of-sentence", KillEos, NO/*EDIT--kill command*/ ) { extern void Eos __(( void )); mv_reg_kill(Eos); } DEF_CMD( "kill-s-expression", KillExpr, NO/*EDIT--kill command*/ ) { extern void FSexpr __(( void )); mv_reg_kill(FSexpr); } /* Make next command, if it is a kill command, append to current kill region. */ DEF_CMD( "append-next-kill", KillAppend, NO ) { if (*killptr != NULL) this_cmd = KILLCMD; } /*====================================================================== * $Log: delete.c,v $ * Revision 14.31.0.12 1994/06/22 07:58:19 tom * (patchup): bugfix: call modify _after_ DotTo so line1 is marked as modified. * * Revision 14.31.0.11 1994/06/12 04:09:22 tom * (patchup): return if empty region, modify() before any other action; * (newlist): new support function; (reg_unlink,reg_kill,CopyRegion): use it; * (reg_unlink): remove empty region check; * (adv_killptr): new support function; (reg_kill,CopyRegion): use it; * (reg_kill): add empty region check, don't kill in view mode--just copy; * (DelNChar,DelPChar,KillEOL,DelNWord,KillEos,KillExpr): remove EDIT flag * from DEF_CMD; (DelNChar,DelPChar): reorg to allow killing in View mode; * (KillEOL): code squeeze. * * Revision 14.31 1993/02/18 06:15:31 tom * remove (void) casts; lotsa random optimizations. * * Revision 14.30 1993/02/05 14:53:18 tom * cleanup whitespace; some random optimizations; be more careful to call * modify() before actual modification (so buffer remains unchanged if * confirmation of modification fails); make "delete-white-space" aware of * numeric argument. * * Revision 14.26 1992/08/27 02:05:15 tom * add "append-next-kill" command; make patchup(), reg_unlink() private; * add RCS directives. * */