/************************************************************************ * 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. * ************************************************************************/ #include "jove.h" RCS("$Id: insert.c,v 14.32.0.12 1994/06/24 01:03:15 tom Exp tom $") #include "ctype.h" #include "re.h" #ifdef LISP private Bufpos *lisp_indent __(( void )); #endif /* Make a newline after AFTER in buffer BUF, UNLESS after is 0, in which case we insert the newline before after -- in which case BUF cannot be NULL */ Line * listput(buf, after) register Buffer *buf; register Line *after; { register Line *newline = nbufline(); if ((newline->l_prev = after) == NULL) { /* Before the first line */ newline->l_next = buf->b_first; buf->b_first = newline; if (buf->b_dot == NULL) buf->b_dot = newline; } else { newline->l_next = after->l_next; after->l_next = newline; } if (newline->l_next) newline->l_next->l_prev = newline; else if (buf) buf->b_last = newline; return newline; } /* Divide the current line and move the current line to the next one */ void LineInsert(num) { register Buffer *cb = curbuf; register Line *newdot = cb->b_dot; { register int n; if ((n = num) <= 0) return; modify(); do { newdot = listput(cb, newdot); SavLine(newdot, NullStr); } while (--n); } if (cb->b_char != 0) { register char *tail = &linebuf[cb->b_char]; register char c = *tail; *tail = '\0'; /* shorten this line */ lsave(); modify(); *tail = c; /* restore first char of tail */ strcpy(linebuf, tail); /* and shift tail */ } else { /* Redisplay optimization */ register disk_line null_da = newdot->l_dline; newdot->l_dline = cb->b_dot->l_dline; cb->b_dot->l_dline = null_da; } makedirty(cb->b_dot); makedirty(newdot); IFixMarks(cb->b_dot, cb->b_char, newdot, 0); cb->b_dot = newdot; cb->b_char = 0; } /* Makes the indent of the current line == goal. If the current indent is greater than GOAL it deletes. If more indent is needed, it uses tabs and spaces to get to where it's going. */ void n_indent(goal) { curchar = 0; AdjWtSpace(goal); } /* * insert the right amount of whitespace (spaces and tabs) * to get from current position to goal. */ void AdjWtSpace(goal) register int goal; { register int from, nblanks; del_whitespace(FORWARD + BACKWARD); from = calc_pos(linebuf, curchar); /* try to insert tabs first */ if ((nblanks = (goal - from)) && (from += TabIncr(from)) <= goal) { /* 'from' is now aligned on tabstop */ exp = ((goal - from) / tabstop + 1), Insert('\t'); nblanks = goal % tabstop; } /* do remainder with spaces */ exp = nblanks, Insert(' '); exp = 1; /* kludge */ } DEF_CMD( "self-insert", SelfInsert, EDIT ) { register Buffer *cb = curbuf; register int c = LastKeyStruck; #ifdef ABBREV if (BufMinorMode(cb, Abbrev) && !isword(c)) AbbrevExpand(); #endif if (BufMinorMode(cb, OverWrite) && c != '\t') { register int num = exp; exp = 1; while (--num >= 0) { if (!eolp(cb) && (linebuf[cb->b_char] != '\t' || TabIncr(calc_pos(linebuf, cb->b_char)) == 1)) { modify(); makedirty(cb->b_dot); linebuf[cb->b_char++] = c; } else Insert(c); } } else Insert(c); if (BufMinorMode(cb, Fill) && !isword(c) && (cb->b_char >= RMargin || calc_pos(linebuf, cb->b_char) >= RMargin)) DoJustify(cb->b_dot, 0, cb->b_dot, strlen(linebuf), YES, BufMinorMode(cb, Indent) ? get_indent(cb->b_dot) : LMargin); } void Insert(c) register int c; { register Buffer *cb = curbuf; register int num; if ((num = exp) <= 0) return; if (c == '\n') { LineInsert(num); return; } if (c == '\0') #ifdef NULLCHARS c = '\n'; #else return; #endif modify(); makedirty(cb->b_dot); ins_c(c, linebuf, cb->b_char, num, LBSIZE); IFixMarks(cb->b_dot, cb->b_char, cb->b_dot, cb->b_char + num); cb->b_char += num; } DEF_INT( "c-indentation-increment", CIndIncrmt, V_BASE10 ); _IF(def PRIVATE) DEF_INT( "indentation-increment", IndIncrmt, V_BASE10 ); _IF(def PRIVATE) /* Tab in to the right place for C (and Lisp) mode. */ DEF_CMD( "handle-tab", Tab, EDIT ) { register Buffer *cb = curbuf; #ifdef ABBREV if (BufMinorMode(cb, Abbrev)) AbbrevExpand(); #endif #ifdef LISP if (BufMajorMode(cb, LISPMODE) && (bolp(cb) || !eolp(cb))) { register int dotchar = cb->b_char; register Mark *m = NULL; ToIndent(); if (dotchar > cb->b_char) m = MakeMark(cb->b_dot, dotchar, FLOATER); lisp_indent(); if (m) { ToMark(m); DelMark(m); } else ToIndent(); return; } #endif { register int goal = 0, incrmt = IndIncrmt, num; if ((num = exp) < 0) return; if (C_like_Mode(cb)) incrmt = CIndIncrmt; if (incrmt <= 0) incrmt = tabstop; if (bolp(cb)) { if ((C_like_Mode(cb) && /* don't indent preprocessor directives. */ (linebuf[0] == '#' || c_indent(incrmt))) || /* use indent of previous line. (Not in fundamental mode) */ (!BufMajorMode(cb, FUNDAMENTAL) && ((goal = get_indent(cb->b_dot->l_prev)) > 0 || (goal = LMargin) > 0))) { /* Indent adjust counts as a single tab, unless an explicit argument is present, then it is not counted at all. */ if (!exp_p) --num; } } if (goal <= 0) { skip_blanks(); goal = calc_pos(linebuf, cb->b_char); } if (--num >= 0) { goal += (incrmt - goal % incrmt) + num * incrmt; } AdjWtSpace(goal); } } DEF_INT( "quote-char", QuoteChar, V_CHAR ) = CTL('Q'); DEF_CMD( "quoted-insert", QuotChar, EDIT ) { register int c; if ((c = exp) < 0) { /* use |exp| as character code */ c = -c & 0xff; exp = 1; } else { c = waitchar(); } LastKeyStruck = c, SelfInsert(); /* to handle overwrite mode correctly */ } /* Insert the paren. If in C mode and c is a '}' then insert the '}' in the "right" place for C indentation; that is indented the same amount as the matching '{' is indented. */ DEF_INT( "paren-flash-delay", PDelay, V_BASE10 ) = 5; /* 1/2 a second */ extern int SitFor __(( int delay )); void Blink(line, col) register Line *line; int col; { Bufpos save; register Bufpos *bp = &save; DOTsave(bp); if (Asking) { #ifndef TINY if (line == bp->p_line) { register char *mp = mesgbuf + Asking, *lp = linebuf + bp->p_char; register char c = *--lp; register int offset; /* make sure mesgbuf is up to date; scan to the left to count the number of chars recently inserted. */ do { if (*--mp != c) Asking++, mp++; } while (--lp >= linebuf && *lp == c); lp++; strcpy(mp, lp); /* now, find where the prompt ends. */ while (lp > linebuf) { if (*--lp != *--mp) { lp++, mp++; break; } } Asking += col - bp->p_char; /* reposition the minibuf if necessary. */ offset = HorWindow(calc_pos(linebuf, col), (int)(lp - linebuf), (int)(mp - mesgbuf)); if (offset != (int)(lp - linebuf)) { offset = how_far(linebuf, offset); Asking = col - offset + (int)(mp - mesgbuf); strcpy(mp, &linebuf[offset]); } updmesg(); SitFor(PDelay); } #endif return; /* sorry, can't oblige you */ } if (in_window(curwind, line) >= 0) { DotTo(line, col); } else { register char *lp = lcontents(line); register int lno = lineno(line); if (curwind->w_flags & W_NUMLINES) col += 8, s_mess("%6d %s", lno, lp); else s_mess("%s (l.%d)", lp, lno); Asking = col; } SitFor(PDelay); SetDot(bp); Asking = 0; updmesg(); /* to keep the message line */ } DEF_CMD( "paren-flash", DoParen, EDIT ) { register Buffer *cb = curbuf; register Bufpos *bp = (Bufpos *) -1; register int c = LastKeyStruck; if (C_like_Mode(cb)) { if (c == '}' && blnkp(linebuf)) bp = c_indent(0); } #ifdef LISP else if (BufMajorMode(cb, LISPMODE)) { if (c == ')' && blnkp(linebuf)) bp = lisp_indent(); } #endif SelfInsert(); if (BufMinorMode(cb, ShowMatch) && !charp() && !in_macro() && !backslashed(linebuf, &linebuf[cb->b_char - 1])) { if (bp == (Bufpos *) -1) { cb->b_char--; /* Back onto the ')' */ bp = m_paren(c, BACKWARD, MP_CAN_STOP|MP_CAN_MISMATCH, (Bufpos *)0); cb->b_char++; } if (bp) Blink(bp->p_line, bp->p_char); mp_error(); /* display error message */ } } /* * in C mode insert hash sign in the first column, and adjust whitespace to * the level of the previous preprocessor command, if there is nothing * but whitespace between Bol and point. * Automagically indent if previous was "if" or "else" command. * Automagically de-indent if current is "else" or "endif" command. */ DEF_INT( "#-indentation-increment", CprepIncr, V_BASE10 ) = 4; _IF(def PRIVATE) /* analogous to CindIncr */ DEF_CMD( "hash-insert", DoHash, EDIT ) { static char re_buf[8 + Bit(BPC-3)]; /* this should be big enough */ register Buffer *cb = curbuf; if (BufMajorMode(cb, CMODE) && exp_p != YES) { register int here = cb->b_char; register int num; if ((num = exp) < 0) exp = -num; ToIndent(); if (cb->b_char >= here) { register Bufpos *bp; /* * Remove existing `#' to redo indentation. * (it will be re-inserted shortly) */ if (linebuf[0] == '#') DelNChar(), ToIndent(); /* * kludge: UNdent existing else/endif directives. */ if (LookingAt("endif\\|else\\|elif", linebuf, cb->b_char)) exp_p = YES, num = -1; cb->b_char = here = 0; if (!re_buf[0]) REcompile("^#[ \t]*", YES, re_buf); if (bp = docompiled(BACKWARD, re_buf, (Bufpos *)0, (Line *)0)) { register char *lp = lcontents(bp->p_line); here = calc_pos(lp, REeom); /* * kludge: indent one more for if/else. But * line up with if/else if argument is negative. */ if (LookingAt("if\\|else\\|elif", lp, REeom)) exp_p += num * YES_NODIGIT; } if (exp_p > 0) here += num * CprepIncr - here % CprepIncr; Insert('#'); /* NOT SelfInsert because of OverWrite mode */ AdjWtSpace(here); return; } cb->b_char = here; } SelfInsert(); } DEF_INT( "delete-blank-tail", DBlnkTail, V_BOOL ) = YES; _IF(def PRIVATE) DEF_CMD( "newline", Newline, EDIT|ARG(NO) ); DEF_CMD( "newline-and-indent", Newline, EDIT|ARG(Indent) ) { register Buffer *cb = curbuf; register int indent = 0; /* first we calculate the indent of the current line (if we need it) */ if ((LastCmd->Type & ARG(Indent)) || BufMinorMode(cb, Indent)) { if ((indent = LMargin) == 0 && (indent = get_indent(cb->b_dot)) == 0) --indent; /* non-zero to indicate indent mode. */ } #ifdef ABBREV if (BufMinorMode(cb, Abbrev)) AbbrevExpand(); #endif #ifdef LISP if (BufMajorMode(cb, LISPMODE)) del_whitespace(FORWARD + BACKWARD); else #endif if (!(LastCmd->Type & ARG(~Indent))) { /* not "newline-and-backup" */ register int direction = FORWARD + BACKWARD; if (indent || blnkp(&linebuf[cb->b_char]) || /* otherwise, leave blanks AFTER point untouched. */ (direction -= FORWARD, True(DBlnkTail))) { del_whitespace(direction); } } /* If there is more than 2 blank lines in a row then don't make a newline, just move down one. (unless explicitly requested) */ if (exp_p == NO && eolp(cb) && TwoBlank()) SetLine(cb->b_dot->l_next); else LineInsert(exp); if (indent) { #ifdef LISP if (BufMajorMode(cb, LISPMODE)) { lisp_indent(); return; } #endif if (C_like_Mode(cb)) { register int incrmt; /* don't indent preprocessor directives */ if (linebuf[0] == '#') return; if ((incrmt = CIndIncrmt) <= 0) incrmt = tabstop; if (c_indent(incrmt)) return; } n_indent(indent); } } DEF_CMD( "newline-and-backup", OpenLine, EDIT|ARG(~Indent) ) { Bufpos save; register Bufpos *dot = &save; DOTsave(dot); ++exp_p, Newline(); /* Open the lines... */ SetDot(dot); } void ins_str(str, ok_nl) const char *str; { register Buffer *cb = curbuf; register char *s = (char *) str, *p, *tail = &linebuf[cb->b_char], *s_lim; register size_t s_len, /* length of substring to be inserted */ l_len, /* length of line in linebuf incl. \0 */ max_len; /* max length of substring */ if (*s == '\0') return; for (p = tail; *p++; ) ; /* skip to end of line */ for (l_len = p - linebuf ;; str = s, p = (tail = linebuf) + l_len) { max_len = LBSIZE - l_len; l_len -= cb->b_char; #ifdef NULLCHARS if (ok_nl < 0) { /* Kludge: interpret \n as \0 */ while (*s++) ; --s; } else #endif while (*s != '\n' && *++s) ; /* skip to next linefeed in string */ if (s_len = s - (char *) str) { modify(); makedirty(cb->b_dot); if (s_len > max_len) { /* adjust length of substring */ s = (char *)&str[s_len = max_len]; } s_lim = s; /* remember... */ for (s = p + s_len; p > tail; ) *--s = *--p; /* shift tail */ for (s = (char *) str; s < s_lim; ) *p++ = *s++; /* insert substring */ /* and here `s' has its original value */ IFixMarks(cb->b_dot, cb->b_char, cb->b_dot, cb->b_char + s_len); cb->b_char += s_len; } if (*s == '\0') break; /* end of insertion string */ #ifdef NULLCHARS if (!(ok_nl < 0)) #endif if (*s++ != '\n') { /* skip newline */ --s; if (!ok_nl) /* truncated string */ len_error(complain); } LineInsert(1); if (*s == '\0') break; } } /* Take the region FLINE/FCHAR to TLINE/TCHAR and insert it at ATLINE/ATCHAR in WHATBUF. (which should be either `curbuf', or NULL if yanking to the kill ring) [TRH] Some optimizations in an attempt to minimize disk I/O */ Bufpos * DoYank(fline, fchar, tline, tchar, atline, atchar, whatbuf) register Line *fline; Line *tline; register Line *atline; Buffer *whatbuf; { static Bufpos bp; char buf[LBSIZE]; register char *from; #define tail from /* alias !! */ Line *startline = atline; int startchar = atchar; disk_line start_dline; ltobuf(atline, genbuf); from = &lcontents(fline)[fchar]; lsave(); if (whatbuf) modify(); start_dline = atline->l_dline; if (fline == tline) { /* special case: affects single line. */ strcpy(buf, from); buf[tchar -= fchar] = '\0'; /* truncate line... */ linecopy(genbuf, atchar, buf); /* append... */ genbuf[atchar + tchar] = from[tchar]; /* for optimizing check below */ tail = &lcontents(atline)[atchar]; atchar += tchar; } else { /* [TRH] optimizations */ strcpy(buf, &genbuf[atchar]); if (atchar == 0 && fchar == 0) { /* replace entire line; use fline unmodified */ atline->l_dline = fline->l_dline; } else if (buf[0] == '\0' && from[0] == '\0') { /* append null string; leave atline unmodified */ } else { linecopy(genbuf, atchar, from); SavLine(atline, genbuf); } /* just copy the line list of the body */ do { makedirty(atline); fline = fline->l_next; atline = listput(whatbuf, atline); atline->l_dline = fline->l_dline; } while (fline != tline); ltobuf(atline, genbuf); tail = buf; atchar = tchar; fchar = 0; /* for optimizing check below */ } /* these optimizations are also OK for the special case above */ if (startchar == 0 && tchar == 0) { /* insert null string: use original first line */ atline->l_dline = start_dline; } else if (fchar == 0 && tail[0] == '\0' && genbuf[tchar] == '\0') { /* append null tail to tline==fline; just copy disk address */ atline->l_dline = fline->l_dline; } else { /* put modified last line */ linecopy(genbuf, atchar, tail); SavLine(atline, genbuf); } makedirty(atline); if (whatbuf) { IFixMarks(startline, startchar, atline, atchar); } bp.p_line = atline; bp.p_char = atchar; this_cmd = YANKCMD; getDOT(); /* Whatever used to be in linebuf */ return &bp; } /* (from misc.c) * [TRH Nov-88] allow argument on Yank: * abs(exp) Number of times to Yank kill buffer. The entire portion of * yanked text becomes the new Region * sign(exp) Controls placement of Point: at the end of the yanked region * if positive, at the beginning if negative * [TRH Jun-91] allow repeat count on YankPop (like Yank, except sign). */ private void do_yank __(( Line *start )); private void do_yank(start) /* common code for Yank and YankPop */ Line *start; { register int num; Bufpos save; register Bufpos *dot = &save; register Line *last; register int llen; if ((num = exp) < 0) num = -num; last = lastline(start); llen = length(last); DOTsave(dot); while (--num >= 0) dot = DoYank(start, 0, last, llen, dot->p_line, dot->p_char, curbuf); SetDot(dot); this_cmd = YANKCMD; /* even if exp == 0 */ } DEF_CMD( "yank", Yank, EDIT ) { register Line *start; if ((start = *killptr) == NULL) complain("[Nothing to yank!]"); set_mark(); do_yank(start); if (exp < 0) PtToMark(); } DEF_CMD( "yank-pop", YankPop, EDIT ) { if (last_cmd != YANKCMD) { /*- complain("Yank something first!"); -*/ Yank(); return; } /* First delete the region */ { register Buffer *cb = curbuf; register Mark *mp = CurMark(); reg_delete(mp->m_line, mp->m_char, cb->b_dot, cb->b_char); } /* Now must find a recently killed region. */ { register Line *start; register Line **kp = killptr; do { if (exp >= 0) { /* normal: rotate backwards */ if (kp == &killbuf[0]) kp += NUMKILLS; *--kp; } else { *kp++; if (kp == &killbuf[NUMKILLS]) kp -= NUMKILLS; } } while ((start = *kp) == NULL); killptr = kp; do_yank(start); } } /* This is an attempt to reduce the amount of memory taken up by each line. Without this each malloc of a line uses sizeof (Line) + sizeof(HEADER) where line is 3 words and HEADER is 1 word. This is going to allocate memory in chucks of CHUNKSIZE * sizeof (Line) and divide each chuck into Lines. A line is free in a chunk when its line->l_dline == 0, so freeline sets dline to 0. */ #define CHUNKSIZE 300 struct chunk { int c_nlines; /* Number of lines in this chunk (so they don't all have to be CHUNKSIZE long). */ struct chunk *c_nextfree; /* Next chunk of lines */ Line c_block[1]; /* rest of lines get appended to chunk */ }; static struct chunk *fchunk = NULL; static Line *ffline = NULL; /* First free line */ #define TOTCHUNKSIZE(n) (sizeof(struct chunk)-sizeof(Line) + (n)*sizeof(Line)) #define FROM_CACHE 0x8000 /* ORed in with c_nlines if block were stolen from the disk cache */ private void freeline __(( Line *_(line) )); private void freeline(line) register Line *line; { register Line *head; line->l_dline = 0; line->l_prev = NULL; if (head = ffline) head->l_prev = line; line->l_next = head; ffline = line; } void lfreelist(first) register Line *first; { lfreereg(first, (Line *)0); } /* Append region from line1 to line2 onto the free list of lines */ void lfreereg(line1, line2) register Line *line1; Line *line2; { register Line *next, *last; if ((last = line2) != NULL) last = last->l_next; while (line1 != last) { next = line1->l_next; freeline(line1); line1 = next; } } #include "temp.h" /* for definition of `Block' */ private void newchunk __(( void )); private void newchunk() { register Line *newline; register struct chunk *f; register int nlines = CHUNKSIZE; if (!(f = (struct chunk *) malloc(TOTCHUNKSIZE(CHUNKSIZE)))) { /* * try to steal a block from the cache. * if that doesn't work, try to obtain a smaller chunk. */ if (f = (struct chunk *) cache_block((Block *)0)) nlines = (sizeof(Block) - TOTCHUNKSIZE(0)) / sizeof(Line) | FROM_CACHE; else do { if ((nlines >>= 1) == 0) complain("[out of lines] "); } while (!(f = (struct chunk *) malloc(TOTCHUNKSIZE(nlines)))); } f->c_nlines = nlines; nlines &= ~FROM_CACHE; newline = f->c_block; do freeline(newline); while (++newline, --nlines); f->c_nextfree = fchunk; fchunk = f; } /* New BUFfer LINE */ Line * nbufline() { register Line *newline, *next; if (!(newline = ffline)) { /* No free list */ newchunk(); newline = ffline; } if (next = newline->l_next) next->l_prev = NULL; ffline = next; return newline; } /* This is used to garbage collect the chunks of lines when malloc fails and we are NOT looking for a new buffer line. This goes through each chunk, and if every line in a given chunk is not allocated, the entire chunk is `free'd by "free()". */ GCchunks() { register Line *lp; register int i; register struct chunk *cp, *prev = NULL, *next; register int did_some = 0; if (cp = fchunk) do { next = cp->c_nextfree; lp = cp->c_block, i = cp->c_nlines & ~FROM_CACHE; do { if (lp->l_dline) break; } while (++lp, --i); if (i) { prev = cp; continue; } if (prev) prev->c_nextfree = next; else fchunk = next; /* * Remove the free lines, in chunk cp, from the free list * because they are no longer free. */ lp = cp->c_block, i = cp->c_nlines & ~FROM_CACHE; do { if (lp->l_prev) lp->l_prev->l_next = lp->l_next; else ffline = lp->l_next; if (lp->l_next) lp->l_next->l_prev = lp->l_prev; } while (++lp, --i); if (cp->c_nlines & FROM_CACHE) cache_block((Block *) cp); else { free((void_*) cp); did_some++; } } while (cp = next); return did_some; } #ifdef LISP /* Grind S-Expr */ DEF_CMD( "grind-s-expr", GSexpr, EDIT ) _IF(def LISP) { register Buffer *cb = curbuf; Bufpos dot, end; if (linebuf[cb->b_char] != '(') complain((char *) 0); DOTsave(&dot); FSexpr(); DOTsave(&end); exp = 1; SetDot(&dot); while (cb->b_dot != end.p_line) { line_move(FORWARD); if (!blnkp(linebuf)) lisp_indent(); } SetDot(&dot); } /* lisp_indent() indents a new line in Lisp Mode, according to where the matching close-paren would go if we typed that (sort of). */ #include "table.h" private const char *init_specials[] = { "case", "def", "dolist", "fluid-let", "lambda", "let", "lexpr", "macro", "named-l", /* named-let and named-lambda */ "nlambda", "prog", "selectq" }; private Table specials = { init_specials, sizeof init_specials / sizeof init_specials[0], 0, casencmp, }; DEF_CMD( "add-lisp-special", AddSpecial, NO ) _IF(def LISP) { add_word(&specials, ask((char *) 0, ProcFmt)); } private Bufpos * lisp_indent() { register Buffer *cb = curbuf; register Bufpos *bp; Bufpos savedot; register int goal; #define c_char goal /* alias! */ if ((bp = m_paren(')', BACKWARD, MP_CAN_STOP, (Bufpos *)0)) == NULL) return NULL; /* We want to end up (atom atom atom ... ^ here. */ DOTsave(&savedot); SetDot(bp); /* we are at the '(' so this is safe. */ if (linebuf[c_char = cb->b_char + 1] != '(') { if (word_in_table(&specials, &linebuf[c_char])) c_char++; else if (exp = FORWARD, ForWord(), !LookingAt("[ \t]*;\\|[ \t]*$", linebuf, cb->b_char)) for (c_char = cb->b_char; isspace(linebuf[c_char]); ) c_char++; } goal = calc_pos(linebuf, c_char); SetDot(&savedot); n_indent(goal); return bp; #undef c_char } #endif /* LISP */ /* * [TRH] Adjust indent level of lines in region. * The increment is given by the argument count (which can be negative * in order to decrement the indent level). * If no argument is given, the increment equals the value of the * tabstop appropriate for the current mode. (i.e. c-indent-increment * in C mode, tabstop in other modes {{ lisp mode?? }} * If the argument is a single minus sign the tabstop value is used * as a decrement. */ DEF_CMD( "shift-region-left", RegAdjIndent, EDIT|NEGATE ); DEF_CMD( "shift-region-right", RegAdjIndent, EDIT ) { register Buffer *cb = curbuf; Bufpos r[2]; int swapped = CurRegion(r); int incrmt; if ((incrmt = exp) == 0) return; if (exp_p != YES) { register int step = IndIncrmt; if (C_like_Mode(cb)) step = CIndIncrmt; if (step <= 0) step = tabstop; incrmt *= step; } { register Line *lp = r[0].p_line, *end_lp = r[1].p_line; if (r[1].p_char > 0) end_lp = end_lp->l_next; if (r[0].p_char > 0) lp = lp->l_next; /* * loop termination guaranteed: r[0].p_line == r[1].p_line * implies r[0].p_char <= r[1].p_char (since we fixorder()ed), * also r[0].p_char > 0 implies r[1].p_char > 0, and still lp == end_lp * after handling the boundary conditions. */ for (; lp != end_lp; lp = lp->l_next) { SetLine(lp); if (linebuf[0] == '\0') /* empty line */ continue; if (linebuf[0] == '#' && BufMajorMode(cb, CMODE)) continue; /* don't indent preproc. directives */ ToIndent(); AdjWtSpace(calc_pos(linebuf, cb->b_char) + incrmt); } SetDot(&r[swapped]); } } /* Tab to next column, as defined by the previous (non-empty) line. */ #ifndef TINY DEF_INT( "column-tabstop", ColTabstop, V_BASE10 ) _IF(ndef TINY) = 8; _IF(def PRIVATE) DEF_STR( "column-format", ColFmt, 40, V_REGEXP ) _IF(ndef TINY)_IF(def PRIVATE) = "[^\t ]*[\t ]+"; DEF_CMD( "tab-to-next-column", TabCol, EDIT ) _IF(ndef TINY) { register const char *lp; register Buffer *cb = curbuf; int num; if ((num = exp) <= 0) return; /* find a non-empty line before current one. */ { register Line *line = cb->b_dot; do { if ((line = line->l_prev) == NULL) { lp = linebuf; break; } } while (blnkp(lp = lcontents(line))); } /* tab to next column, as defined by the "column-format" RE pattern. */ { register int goal = how_far(lp, calc_pos(linebuf, cb->b_char)); if (goal >= strlen(lp)) { lp = linebuf; goal = cb->b_char; } while (--num >= 0 && LookingAt(ColFmt, lp, goal)) { goal = REeom; } ++num; goal = calc_pos(lp, goal); # define tabstop ColTabstop /* for TabIncr */ while (--num >= 0) { goal += TabIncr(goal); } # undef tabstop AdjWtSpace(goal); } } #endif /* !TINY */ /*====================================================================== * $Log: insert.c,v $ * Revision 14.32.0.12 1994/06/24 01:03:15 tom * (Tab): don't use indent of previous line in Fundamental mode; * (Blink): move AUTOSCROLL handling to SitFor(). * * Revision 14.32.0.11 1994/06/13 01:30:24 tom * (listput,LineInsert,Insert,DoHash,NewLine,YankPop): code squeeze; * (SelfInsert): reformat; ("indentation-increment"): new Variable; * (Tab,NewLine,YankPop,RegAdjIndent): use it; * (Tab): adjust indent in all modes if bolp -- not just C mode, don't rely * on SelfInsert; * (NewLine): move Abbrev expansion to top; * (DoYank): IFixMarks only if whatbuf != NULL -- this was potential bug; * (RegAdjIndent): use CurRegion(). * * Revision 14.32 1993/06/09 01:02:19 tom * (ins_str): `ok_nl' < 0 interprets \n as null characters instead of newline. * * Revision 14.31 1993/02/18 06:16:22 tom * replace spurious DelWtSpace() with del_whitespace(); remove (void) casts; * lotsa random optimizations. * * Revision 14.30 1993/02/06 00:48:32 tom * cleanup whitespace; some random optimizations; be more careful to call * modify() before actual modification (so buffer remains unchanged if * confirmation of modification fails); handle "delete-blank-tail" correctly. * * Revision 14.28 1992/10/24 01:24:19 tom * convert to "port{ansi,defs}.h" conventions. * * Revision 14.27 1992/09/21 13:16:02 tom * add "quote-char" variable. * * Revision 14.26 1992/08/26 23:56:53 tom * make lisp_indent() private; PRIVATE-ized some Variable defs; * add RCS directives. * */