/* * STEVIE - Simply Try this Editor for VI Enthusiasts * * Code Contributions By : Tim Thompson twitch!tjt * Tony Andrews onecom!wldrdg!tony * G. R. (Fred) Walter watmath!watcgl!grwalter */ #include "stevie.h" static bool_t openfwd(), openbwd(); extern int did_ai; /* * opencmd * * Add a blank line above or below the current line. */ bool_t opencmd(dir, can_ai) int dir; int can_ai; /* if true, consider auto-indent */ { if (dir == FORWARD) return (openfwd(can_ai)); else return (openbwd(can_ai)); } static bool_t openfwd(can_ai) int can_ai; { LINE *l; LPTR *next; char *s; /* string to be moved to new line, if any */ /* * If we're in insert mode, we need to move the remainder of the current * line onto the new line. Otherwise the new line is left blank. */ if (State == INSERT) s = &Curschar->linep->s[Curschar->index]; else s = ""; if ((next = nextline(Curschar)) == NULL) /* open on last line */ next = Fileend; /* * By asking for as much space as the prior line had we make sure that * we'll have enough space for any auto-indenting. */ if ((l = newline(strlen(Curschar->linep->s) + SLOP)) == NULL) { emsg("out of memory"); beep(); sleep(2); return (FALSE); } if (*s != NUL) strcpy(l->s, s); /* copy string to new line */ #ifdef AUTO_INDENT else if (can_ai && P(P_AI)) { /* * Auto-indent removed due to buggy implementation... */ did_ai = TRUE; } #endif if (State == INSERT) /* truncate current line at cursor */ *s = NUL; Curschar->linep->next = l; /* link neighbors to new line */ next->linep->prev = l; l->prev = Curschar->linep; /* link new line to neighbors */ l->next = next->linep; if (next == Fileend) /* new line at end */ l->num = Curschar->linep->num + LINEINC; else if ((l->prev->num) + 1 == l->next->num) /* no gap, renumber */ renum(); else { /* stick it in the middle */ long lnum; lnum = (l->prev->num + l->next->num) / 2; l->num = lnum; } *Curschar = *nextline(Curschar); /* cursor moves down */ Curschar->index = 0; s_ins(Cursrow + 1, 1); /* insert a physical line */ updateNextscreen(); /* because Botchar is now invalid */ cursupdate(); /* update Cursrow before insert */ return (TRUE); } static bool_t openbwd(can_ai) int can_ai; { LINE *l; LPTR *prev; prev = prevline(Curschar); if ((l = newline(strlen(Curschar->linep->s) + SLOP)) == NULL) { emsg("out of memory"); beep(); sleep(2); return (FALSE); } Curschar->linep->prev = l; /* link neighbors to new line */ if (prev != NULL) prev->linep->next = l; l->next = Curschar->linep; /* link new line to neighbors */ if (prev != NULL) l->prev = prev->linep; #ifdef AUTO_INDENT if (can_ai && P(P_AI)) { /* * Auto-indent removed due to buggy implementation... */ did_ai = TRUE; } #endif *Curschar = *prevline(Curschar); /* cursor moves up */ Curschar->index = 0; if (prev == NULL) /* new start of file */ Filemem->linep = l; renum(); /* keep it simple - we don't do this often */ cursupdate(); /* update Cursrow before insert */ if (Cursrow != 0) s_ins(Cursrow, 1); /* insert a physical line */ updateNextscreen(); return (TRUE); } int cntllines(pbegin, pend) LPTR *pbegin, *pend; { register LINE *lp; register int lnum = 1; for (lp = pbegin->linep; lp != pend->linep; lp = lp->next) lnum++; return (lnum); } /* * plines(p) - return the number of physical screen lines taken by line 'p' */ int plines(p) LPTR *p; { int col; char *s; if (p == NULL) { fprintf(stderr, "plines(p) : p == NULL ????"); return (0); } s = p->linep->s; if (*s == NUL) /* empty line */ return 1; /* * If list mode is on, then the '$' at the end of the line takes up one * extra column. */ col = P(P_LS) ? 1 : 0; for (; *s != NUL; s++) { if (*s == TAB && !P(P_LS)) col += P(P_TS) - (col % P(P_TS)); else col += chars[*s].ch_size; } return ((col + (Columns - 1)) / Columns); } void fileinfo() { long l1, l2; char buf[MAX_COLUMNS + 1]; if (bufempty()) { msg("Buffer Empty"); return; } l1 = cntllines(Filemem, Curschar); l2 = cntllines(Filemem, Fileend) - 1; sprintf(buf, "\"%s\"%s line %ld of %ld -- %ld %% --", (Filename != NULL) ? Filename : "No File", Changed ? " [Modified]" : "", l1, l2, (l1 * 100) / l2); msg(buf); } /* * gotoline(n) - return a pointer to line 'n' * * Returns a pointer to the last line of the file if n is zero, or beyond the * end of the file. */ LPTR * gotoline(n) int n; { static LPTR l; l.index = 0; if (n == 0) l = *prevline(Fileend); else { LPTR *p; for (l = *Filemem; --n > 0; l = *p) if ((p = nextline(&l)) == NULL) break; } return &l; } void inschar(c) char c; { char *p; char *pend; /* make room for the new char. */ if (!canincrease(1)) return; p = &Curschar->linep->s[strlen(Curschar->linep->s) + 1]; pend = &Curschar->linep->s[Curschar->index]; for (; p > pend; p--) *p = *(p - 1); *p = c; if (RedrawingDisabled) { inc(Curschar); return; } /* * If we're in insert mode and showmatch mode is set, then 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, then flash to it briefly. If it * isn't on the screen, don't do anything. */ if (P(P_SM) && State == INSERT && (c == ')' || c == '}' || c == ']')) { LPTR *lpos, csave; if ((lpos = showmatch()) == NULL) /* no match, so beep */ beep(); else if (LINEOF(lpos) >= LINEOF(Topchar)) { updateNextscreen(); /* show the new char first */ updateRealscreen(); csave = *Curschar; *Curschar = *lpos; /* move to matching char */ cursupdate(); windgoto(Cursrow, Curscol); delay(); /* brief pause */ *Curschar = csave; /* restore cursor position */ cursupdate(); } } inc(Curschar); } void insstr(s) char *s; { char *p, *endp; int k, n = strlen(s); /* Move everything in the file over to make */ /* room for the new string. */ if (!canincrease(n)) return; endp = &Curschar->linep->s[Curschar->index]; p = Curschar->linep->s + strlen(Curschar->linep->s) + 1 + n; for (; p > endp; p--) *p = *(p - n); p = &Curschar->linep->s[Curschar->index]; for (k = 0; k < n; k++) { *p++ = *s++; inc(Curschar); } } bool_t delchar(fixpos, undo) bool_t fixpos; /* if TRUE fix the cursor position when done */ bool_t undo; /* if TRUE put char deleted into Undo buffer */ { int i; /* Check for degenerate case; there's nothing in the file. */ if (bufempty()) return FALSE; if (lineempty(Curschar)) /* can't do anything */ return FALSE; if (undo) AppendToUndobuff(mkstr(gchar(Curschar))); /* Delete the char. at Curschar by shifting everything in the line down. */ for (i = Curschar->index + 1; i < Curschar->linep->size; i++) Curschar->linep->s[i - 1] = Curschar->linep->s[i]; /* * If we just took off the last character of a non-blank line, we don't * want to end up positioned at the newline. */ if (fixpos) { if (gchar(Curschar) == NUL && Curschar->index > 0 && State != INSERT) Curschar->index--; } return TRUE; } void delline(nlines) { LINE *p, *q; int doscreen = TRUE; /* if true, update the screen */ /* * There's no point in keeping the screen updated if we're deleting more * than a screen's worth of lines. */ if (nlines > (Rows - 1)) { doscreen = FALSE; s_del(Cursrow, Rows - 1); /* flaky way to clear rest of screen */ } while (nlines-- > 0) { if (bufempty()) /* nothing to delete */ break; if (buf1line()) { /* just clear the line */ Curschar->linep->s[0] = NUL; Curschar->index = 0; break; } p = Curschar->linep->prev; q = Curschar->linep->next; if (p == NULL) { /* first line of file so... */ Filemem->linep = q; /* adjust start of file */ Topchar->linep = q; /* and screen */ } else p->next = q; q->prev = p; clrmark(Curschar->linep); /* clear marks for the line */ /* * Delete the correct number of physical lines on the screen */ if (doscreen) s_del(Cursrow, plines(Curschar)); /* * If deleting the top line on the screen, adjust Topchar */ if (Topchar->linep == Curschar->linep) Topchar->linep = q; free(Curschar->linep->s); free((char *) (Curschar->linep)); Curschar->linep = q; Curschar->index = 0; /* is this right? */ /* If we delete the last line in the file, back up */ if (Curschar->linep == Fileend->linep) { Curschar->linep = Curschar->linep->prev; /* and don't try to delete any more lines */ break; } } }