Annotation of src/usr.bin/mg/cmode.c, Revision 1.1
1.1 ! kjell 1: /* $OpenBSD$ */
! 2: /*
! 3: * This file is in the public domain.
! 4: *
! 5: * Author: Kjell Wooding <kjell@openbsd.org>
! 6: */
! 7:
! 8: /*
! 9: * Implement an non-irritating KNF-compliant mode for editing
! 10: * C code.
! 11: */
! 12: #include <ctype.h>
! 13:
! 14: #include "def.h"
! 15: #include "kbd.h"
! 16: #include "funmap.h"
! 17:
! 18: /* Pull in from modes.c */
! 19: extern int changemode(int, int, char *);
! 20:
! 21: static int cc_strip_trailp = TRUE; /* Delete Trailing space? */
! 22: static int cc_basic_indent = 8; /* Basic Indent multiple */
! 23: static int cc_cont_indent = 4; /* Continued line indent */
! 24: static int cc_colon_indent = -8; /* Label / case indent */
! 25:
! 26: static int getmatch(int, int);
! 27: static int getindent(const struct line *, int *);
! 28:
! 29: /* Keymaps */
! 30:
! 31: static PF cmode_brace[] = {
! 32: cc_char, /* } */
! 33: };
! 34:
! 35: static PF cmode_ci[] = {
! 36: cc_tab, /* ^I */
! 37: rescan, /* ^J */
! 38: rescan, /* ^K */
! 39: rescan, /* ^L */
! 40: cc_lfindent, /* ^M */
! 41: };
! 42:
! 43: static PF cmode_colon[] = {
! 44: cc_char, /* : */
! 45: rescan, /* ; */
! 46: };
! 47:
! 48: static struct KEYMAPE (3 + IMAPEXT) cmodemap = {
! 49: 3,
! 50: 3 + IMAPEXT,
! 51: rescan,
! 52: {
! 53: { CCHR('I'), CCHR('M'), cmode_ci, NULL },
! 54: { ':', ';', cmode_colon, NULL },
! 55: { '}', '}', cmode_brace, NULL }
! 56: }
! 57: };
! 58:
! 59: /* Funtion, Mode hooks */
! 60:
! 61: void
! 62: cmode_init(void)
! 63: {
! 64: funmap_add(cmode, "c-mode");
! 65: funmap_add(cc_char, "c-handle-special-char");
! 66: funmap_add(cc_tab, "c-tab-or-indent");
! 67: funmap_add(cc_indent, "c-indent");
! 68: funmap_add(cc_lfindent, "c-indent-and-newline");
! 69: maps_add((KEYMAP *)&cmodemap, "c-mode");
! 70: }
! 71:
! 72: /*
! 73: * Enable/toggle c-mode
! 74: */
! 75: int
! 76: cmode(int f, int n)
! 77: {
! 78: return(changemode(f, n, "c-mode"));
! 79: }
! 80:
! 81: /*
! 82: * Handle special C character - selfinsert then indent.
! 83: */
! 84: int
! 85: cc_char(int f, int n)
! 86: {
! 87: if (n < 0)
! 88: return (FALSE);
! 89: if (selfinsert(FFRAND, n) == FALSE)
! 90: return (FALSE);
! 91: return (cc_indent(FFRAND, n));
! 92: }
! 93:
! 94: /*
! 95: * If we are in the whitespace at the beginning of the line,
! 96: * simply act as a regular tab. If we are not, indent
! 97: * current line according to whitespace rules.
! 98: */
! 99: int
! 100: cc_tab(int f, int n)
! 101: {
! 102: int lo;
! 103: int inwhitep = FALSE; /* In leading whitespace? */
! 104:
! 105: for (lo = 0; lo < llength(curwp->w_dotp); lo++) {
! 106: if (!isspace(lgetc(curwp->w_dotp, lo)))
! 107: break;
! 108: if (lo == curwp->w_doto)
! 109: inwhitep = TRUE;
! 110: }
! 111:
! 112: /* Special case: we could be at the end of a whitespace line */
! 113: if (lo == llength(curwp->w_dotp) && curwp->w_doto == lo)
! 114: inwhitep = TRUE;
! 115:
! 116: /* If empty line, or in whitespace */
! 117: if (llength(curwp->w_dotp) == 0 || inwhitep)
! 118: return (selfinsert(f, n));
! 119:
! 120: return (cc_indent(FFRAND, 1));
! 121: }
! 122:
! 123: /*
! 124: * Attempt to indent current line according to KNF rules.
! 125: */
! 126: int
! 127: cc_indent(int f, int n)
! 128: {
! 129: int pi, mi; /* Previous indents */
! 130: int ci, dci; /* current indent, don't care */
! 131: int lo;
! 132: int c;
! 133: int nonblankp;
! 134: struct line *lp;
! 135:
! 136: if (n < 0)
! 137: return (FALSE);
! 138:
! 139: if (cc_strip_trailp)
! 140: deltrailwhite(FFRAND, 1);
! 141:
! 142: /* Search backwards for a nonblank, non preprocessor line */
! 143: lp = curwp->w_dotp;
! 144: nonblankp = FALSE;
! 145: while (lback(lp) != curbp->b_headp && !nonblankp) {
! 146: lp = lback(lp);
! 147: for (lo = 0; lo < llength(lp); lo++) {
! 148: if (!isspace(c = lgetc(lp, lo))) {
! 149: /* Leading # is a blank */
! 150: if (c != '#')
! 151: nonblankp = TRUE;
! 152: break;
! 153: }
! 154: }
! 155: }
! 156:
! 157: pi = getindent(lp, &mi);
! 158:
! 159: /* Strip leading space on current line */
! 160: delleadwhite(FFRAND, 1);
! 161: /* current indent is computed only to current position */
! 162: dci = getindent(curwp->w_dotp, &ci);
! 163:
! 164: if (pi + ci < 0)
! 165: return(indent(FFOTHARG, 0));
! 166: else
! 167: return(indent(FFOTHARG, pi + ci));
! 168: }
! 169:
! 170: /*
! 171: * Indent-and-newline (technically, newline then indent)
! 172: */
! 173: int
! 174: cc_lfindent(int f, int n)
! 175: {
! 176: if (n < 0)
! 177: return (FALSE);
! 178: if (newline(FFRAND, 1) == FALSE)
! 179: return (FALSE);
! 180: return (cc_indent(FFRAND, n));
! 181: }
! 182:
! 183: /*
! 184: * Get the level of indention after line lp is processed
! 185: * Note getindent has two returns: curi, nexti.
! 186: * curi = value if indenting current line.
! 187: * return value = value affecting subsequent lines.
! 188: * note, we only process up to offset op.
! 189: * set to llength(lp) for the whole line.
! 190: */
! 191: static int
! 192: getindent(const struct line *lp, int *curi)
! 193: {
! 194: int lo, co; /* leading space, current offset*/
! 195: int nicol = 0; /* position count */
! 196: int c = '\0'; /* current char */
! 197: int newind = 0; /* new index value */
! 198: int stringp = FALSE; /* in string? */
! 199: int escp = FALSE; /* Escape char? */
! 200: int lastc = '\0'; /* Last matched string delimeter */
! 201: int nparen = 0; /* paren count */
! 202: int obrace = 0; /* open brace count */
! 203: int cbrace = 0; /* close brace count */
! 204: int contp = FALSE; /* Continue? */
! 205: int firstseenp = FALSE; /* First nonspace encountered? */
! 206: int colonp = FALSE; /* Did we see a colon? */
! 207: int questionp = FALSE; /* Did we see a question mark? */
! 208:
! 209: *curi = 0;
! 210:
! 211: /* Compute leading space */
! 212: for (lo = 0; lo < llength(lp); lo++) {
! 213: if (!isspace(c = lgetc(lp, lo)))
! 214: break;
! 215: if (c == '\t'
! 216: #ifdef NOTAB
! 217: && !(curbp-b_flag & BFNOTAB)
! 218: #endif /* NOTAB */
! 219: ) {
! 220: nicol |= 0x07;
! 221: }
! 222: nicol++;
! 223: }
! 224:
! 225: /* If last line was blank, choose 0 */
! 226: if (lo == llength(lp))
! 227: nicol = 0;
! 228:
! 229: if (c == '#')
! 230: return (0);
! 231:
! 232: newind = 0;
! 233: /* Compute modifiers */
! 234: for (co = lo; co < llength(lp); co++) {
! 235: c = lgetc(lp, co);
! 236: /* We have a non-whitespace char */
! 237: if (!firstseenp && !isspace(c)) {
! 238: contp = TRUE;
! 239: firstseenp = TRUE;
! 240: }
! 241: if (c == '\\')
! 242: escp = !escp;
! 243: else if (stringp) {
! 244: if (!escp && (c == '"' || c == '\'')) {
! 245: /* unescaped string char */
! 246: if (getmatch(c, lastc))
! 247: stringp = FALSE;
! 248: }
! 249: } else if (c == '"' || c == '\'') {
! 250: stringp = TRUE;
! 251: lastc = c;
! 252: } else if (c == '(') {
! 253: nparen++;
! 254: } else if (c == ')') {
! 255: nparen--;
! 256: } else if (c == '{') {
! 257: obrace++;
! 258: firstseenp = FALSE;
! 259: contp = FALSE;
! 260: } else if (c == '}') {
! 261: cbrace++;
! 262: } else if (c == '?') {
! 263: questionp = TRUE;
! 264: } else if (c == ':') {
! 265: /* ignore (foo ? bar : baz) construct */
! 266: if (!questionp)
! 267: colonp = TRUE;
! 268: } else if (c == ';') {
! 269: if (nparen > 0)
! 270: contp = FALSE;
! 271: }
! 272:
! 273: /* Reset escape character match */
! 274: if (c != '\\')
! 275: escp = FALSE;
! 276: }
! 277: /*
! 278: * If not terminated with a semicolon, and brace or paren open.
! 279: * we continue
! 280: */
! 281: if (colonp) {
! 282: *curi += cc_colon_indent;
! 283: newind -= cc_colon_indent;
! 284: }
! 285:
! 286: *curi -= (cbrace) * cc_basic_indent;
! 287: newind += obrace * cc_basic_indent;
! 288:
! 289: if (nparen < 0)
! 290: newind -= cc_cont_indent;
! 291: else if (nparen > 0)
! 292: newind += cc_cont_indent;
! 293: newind += nicol;
! 294: *curi += nicol;
! 295:
! 296: return (newind);
! 297: }
! 298:
! 299: /*
! 300: * Given a delimeter and its purported mate, tell us if they
! 301: * match.
! 302: */
! 303: static int
! 304: getmatch(int c, int mc)
! 305: {
! 306: int match = FALSE;
! 307:
! 308: switch (c) {
! 309: case '"':
! 310: match = (mc == '"');
! 311: break;
! 312: case '\'':
! 313: match = (mc == '\'');
! 314: break;
! 315: case '(':
! 316: match = (mc == ')');
! 317: break;
! 318: case '[':
! 319: match = (mc == ']');
! 320: break;
! 321: case '{':
! 322: match = (mc == '}');
! 323: break;
! 324: }
! 325:
! 326: return (match);
! 327: }