X-Git-Url: https://pd.if.org/git/?p=pd_readline;a=blobdiff_plain;f=mg%2Fcmode.c;fp=mg%2Fcmode.c;h=674d89d6e77ff79b680c2422eb0606208e987981;hp=0000000000000000000000000000000000000000;hb=a9843085ec916c175bd245a8398f30e6cc03f984;hpb=26fe4e09c6c3c250334fdeed60ce3061febecde2 diff --git a/mg/cmode.c b/mg/cmode.c new file mode 100644 index 0000000..674d89d --- /dev/null +++ b/mg/cmode.c @@ -0,0 +1,528 @@ +/* $OpenBSD: cmode.c,v 1.8 2012/05/18 02:13:44 lum Exp $ */ +/* + * This file is in the public domain. + * + * Author: Kjell Wooding + */ + +/* + * Implement an non-irritating KNF-compliant mode for editing + * C code. + */ +#include + +#include "def.h" +#include "kbd.h" +#include "funmap.h" + +/* Pull in from modes.c */ +extern int changemode(int, int, char *); + +static int cc_strip_trailp = TRUE; /* Delete Trailing space? */ +static int cc_basic_indent = 8; /* Basic Indent multiple */ +static int cc_cont_indent = 4; /* Continued line indent */ +static int cc_colon_indent = -8; /* Label / case indent */ + +static int getmatch(int, int); +static int getindent(const struct line *, int *); +static int in_whitespace(struct line *, int); +static int findcolpos(const struct buffer *, const struct line *, int); +static struct line *findnonblank(struct line *); +static int isnonblank(const struct line *, int); + +void cmode_init(void); +int cc_comment(int, int); + +/* Keymaps */ + +static PF cmode_brace[] = { + cc_brace, /* } */ +}; + +static PF cmode_cCP[] = { + compile, /* C-c P */ +}; + + +static PF cmode_cc[] = { + NULL, /* ^C */ + rescan, /* ^D */ + rescan, /* ^E */ + rescan, /* ^F */ + rescan, /* ^G */ + rescan, /* ^H */ + cc_tab, /* ^I */ + rescan, /* ^J */ + rescan, /* ^K */ + rescan, /* ^L */ + cc_lfindent, /* ^M */ +}; + +static PF cmode_spec[] = { + cc_char, /* : */ +}; + +static struct KEYMAPE (1 + IMAPEXT) cmode_cmap = { + 1, + 1 + IMAPEXT, + rescan, + { + { 'P', 'P', cmode_cCP, NULL } + } +}; + +static struct KEYMAPE (3 + IMAPEXT) cmodemap = { + 3, + 3 + IMAPEXT, + rescan, + { + { CCHR('C'), CCHR('M'), cmode_cc, (KEYMAP *) &cmode_cmap }, + { ':', ':', cmode_spec, NULL }, + { '}', '}', cmode_brace, NULL } + } +}; + +/* Funtion, Mode hooks */ + +void +cmode_init(void) +{ + funmap_add(cmode, "c-mode"); + funmap_add(cc_char, "c-handle-special-char"); + funmap_add(cc_brace, "c-handle-special-brace"); + funmap_add(cc_tab, "c-tab-or-indent"); + funmap_add(cc_indent, "c-indent"); + funmap_add(cc_lfindent, "c-indent-and-newline"); + maps_add((KEYMAP *)&cmodemap, "c"); +} + +/* + * Enable/toggle c-mode + */ +int +cmode(int f, int n) +{ + return(changemode(f, n, "c")); +} + +/* + * Handle special C character - selfinsert then indent. + */ +int +cc_char(int f, int n) +{ + if (n < 0) + return (FALSE); + if (selfinsert(FFRAND, n) == FALSE) + return (FALSE); + return (cc_indent(FFRAND, n)); +} + +/* + * Handle special C character - selfinsert then indent. + */ +int +cc_brace(int f, int n) +{ + if (n < 0) + return (FALSE); + if (showmatch(FFRAND, 1) == FALSE) + return (FALSE); + return (cc_indent(FFRAND, n)); +} + + +/* + * If we are in the whitespace at the beginning of the line, + * simply act as a regular tab. If we are not, indent + * current line according to whitespace rules. + */ +int +cc_tab(int f, int n) +{ + int inwhitep = FALSE; /* In leading whitespace? */ + + inwhitep = in_whitespace(curwp->w_dotp, llength(curwp->w_dotp)); + + /* If empty line, or in whitespace */ + if (llength(curwp->w_dotp) == 0 || inwhitep) + return (selfinsert(f, n)); + + return (cc_indent(FFRAND, 1)); +} + +/* + * Attempt to indent current line according to KNF rules. + */ +int +cc_indent(int f, int n) +{ + int pi, mi; /* Previous indents */ + int ci, dci; /* current indent, don't care */ + struct line *lp; + int ret; + + if (n < 0) + return (FALSE); + + undo_boundary_enable(FFRAND, 0); + if (cc_strip_trailp) + deltrailwhite(FFRAND, 1); + + /* + * Search backwards for a non-blank, non-preprocessor, + * non-comment line + */ + + lp = findnonblank(curwp->w_dotp); + + pi = getindent(lp, &mi); + + /* Strip leading space on current line */ + delleadwhite(FFRAND, 1); + /* current indent is computed only to current position */ + dci = getindent(curwp->w_dotp, &ci); + + if (pi + ci < 0) + ret = indent(FFOTHARG, 0); + else + ret = indent(FFOTHARG, pi + ci); + + undo_boundary_enable(FFRAND, 1); + + return (ret); +} + +/* + * Indent-and-newline (technically, newline then indent) + */ +int +cc_lfindent(int f, int n) +{ + if (n < 0) + return (FALSE); + if (newline(FFRAND, 1) == FALSE) + return (FALSE); + return (cc_indent(FFRAND, n)); +} + +/* + * Get the level of indention after line lp is processed + * Note getindent has two returns: + * curi = value if indenting current line. + * return value = value affecting subsequent lines. + */ +static int +getindent(const struct line *lp, int *curi) +{ + int lo, co; /* leading space, current offset*/ + int nicol = 0; /* position count */ + int ccol = 0; /* current column */ + int c = '\0'; /* current char */ + int newind = 0; /* new index value */ + int stringp = FALSE; /* in string? */ + int escp = FALSE; /* Escape char? */ + int lastc = '\0'; /* Last matched string delimeter */ + int nparen = 0; /* paren count */ + int obrace = 0; /* open brace count */ + int cbrace = 0; /* close brace count */ + int contp = FALSE; /* Continue? */ + int firstnwsp = FALSE; /* First nonspace encountered? */ + int colonp = FALSE; /* Did we see a colon? */ + int questionp = FALSE; /* Did we see a question mark? */ + int slashp = FALSE; /* Slash? */ + int astp = FALSE; /* Asterisk? */ + int cpos = -1; /* comment position */ + int cppp = FALSE; /* Preprocessor command? */ + + *curi = 0; + + /* Compute leading space */ + for (lo = 0; lo < llength(lp); lo++) { + if (!isspace(c = lgetc(lp, lo))) + break; + if (c == '\t' +#ifdef NOTAB + && !(curbp->b_flag & BFNOTAB) +#endif /* NOTAB */ + ) { + nicol |= 0x07; + } + nicol++; + } + + /* If last line was blank, choose 0 */ + if (lo == llength(lp)) + nicol = 0; + + newind = 0; + ccol = nicol; /* current column */ + /* Compute modifiers */ + for (co = lo; co < llength(lp); co++) { + c = lgetc(lp, co); + /* We have a non-whitespace char */ + if (!firstnwsp && !isspace(c)) { + contp = TRUE; + if (c == '#') + cppp = TRUE; + firstnwsp = TRUE; + } + if (c == '\\') + escp = !escp; + else if (stringp) { + if (!escp && (c == '"' || c == '\'')) { + /* unescaped string char */ + if (getmatch(c, lastc)) + stringp = FALSE; + } + } else if (c == '"' || c == '\'') { + stringp = TRUE; + lastc = c; + } else if (c == '(') { + nparen++; + } else if (c == ')') { + nparen--; + } else if (c == '{') { + obrace++; + firstnwsp = FALSE; + contp = FALSE; + } else if (c == '}') { + cbrace++; + } else if (c == '?') { + questionp = TRUE; + } else if (c == ':') { + /* ignore (foo ? bar : baz) construct */ + if (!questionp) + colonp = TRUE; + } else if (c == ';') { + if (nparen > 0) + contp = FALSE; + } else if (c == '/') { + /* first nonwhitespace? -> indent */ + if (firstnwsp) { + /* If previous char asterisk -> close */ + if (astp) + cpos = -1; + else + slashp = TRUE; + } + } else if (c == '*') { + /* If previous char slash -> open */ + if (slashp) + cpos = co; + else + astp = TRUE; + } else if (firstnwsp) { + firstnwsp = FALSE; + } + + /* Reset matches that apply to next character only */ + if (c != '\\') + escp = FALSE; + if (c != '*') + astp = FALSE; + if (c != '/') + slashp = FALSE; + } + /* + * If not terminated with a semicolon, and brace or paren open. + * we continue + */ + if (colonp) { + *curi += cc_colon_indent; + newind -= cc_colon_indent; + } + + *curi -= (cbrace) * cc_basic_indent; + newind += obrace * cc_basic_indent; + + if (nparen < 0) + newind -= cc_cont_indent; + else if (nparen > 0) + newind += cc_cont_indent; + + *curi += nicol; + + /* Ignore preprocessor. Otherwise, add current column */ + if (cppp) { + newind = nicol; + *curi = 0; + } else { + newind += nicol; + } + + if (cpos != -1) + newind = findcolpos(curbp, lp, cpos); + + return (newind); +} + +/* + * Given a delimeter and its purported mate, tell us if they + * match. + */ +static int +getmatch(int c, int mc) +{ + int match = FALSE; + + switch (c) { + case '"': + match = (mc == '"'); + break; + case '\'': + match = (mc == '\''); + break; + case '(': + match = (mc == ')'); + break; + case '[': + match = (mc == ']'); + break; + case '{': + match = (mc == '}'); + break; + } + + return (match); +} + +static int +in_whitespace(struct line *lp, int len) +{ + int lo; + int inwhitep = FALSE; + + for (lo = 0; lo < len; lo++) { + if (!isspace(lgetc(lp, lo))) + break; + if (lo == len - 1) + inwhitep = TRUE; + } + + return (inwhitep); +} + + +/* convert a line/offset pair to a column position (for indenting) */ +static int +findcolpos(const struct buffer *bp, const struct line *lp, int lo) +{ + int col, i, c; + char tmp[5]; + + /* determine column */ + col = 0; + + for (i = 0; i < lo; ++i) { + c = lgetc(lp, i); + if (c == '\t' +#ifdef NOTAB + && !(bp->b_flag & BFNOTAB) +#endif /* NOTAB */ + ) { + col |= 0x07; + col++; + } else if (ISCTRL(c) != FALSE) + col += 2; + else if (isprint(c)) { + col++; + } else { + col += snprintf(tmp, sizeof(tmp), "\\%o", c); + } + + } + return (col); +} + +/* + * Find a non-blank line, searching backwards from the supplied line pointer. + * For C, nonblank is non-preprocessor, non C++, and accounts + * for complete C-style comments. + */ +static struct line * +findnonblank(struct line *lp) +{ + int lo; + int nonblankp = FALSE; + int commentp = FALSE; + int slashp; + int astp; + int c; + + while (lback(lp) != curbp->b_headp && (commentp || !nonblankp)) { + lp = lback(lp); + slashp = FALSE; + astp = FALSE; + + /* Potential nonblank? */ + nonblankp = isnonblank(lp, llength(lp)); + + /* + * Search from end, removing complete C-style + * comments. If one is found, ignore it and + * test for nonblankness from where it starts. + */ + slashp = FALSE; + /* Scan backwards from end to find C-style comment */ + for (lo = llength(lp) - 1; lo >= 0; lo--) { + if (!isspace(c = lgetc(lp, lo))) { + if (commentp) { /* find comment "open" */ + if (c == '*') + astp = TRUE; + else if (astp && c == '/') { + commentp = FALSE; + /* whitespace to here? */ + nonblankp = isnonblank(lp, lo); + } + } else { /* find comment "close" */ + if (c == '/') + slashp = TRUE; + else if (slashp && c == '*') + /* found a comment */ + commentp = TRUE; + } + } + } + } + + /* Rewound to start of file? */ + if (lback(lp) == curbp->b_headp && !nonblankp) + return (curbp->b_headp); + + return (lp); +} + +/* + * Given a line, scan forward to 'omax' and determine if we + * are all C whitespace. + * Note that preprocessor directives and C++-style comments + * count as whitespace. C-style comments do not, and must + * be handled elsewhere. + */ +static int +isnonblank(const struct line *lp, int omax) +{ + int nonblankp = FALSE; /* Return value */ + int slashp = FALSE; /* Encountered slash */ + int lo; /* Loop index */ + int c; /* char being read */ + + /* Scan from front for preprocessor, C++ comments */ + for (lo = 0; lo < omax; lo++) { + if (!isspace(c = lgetc(lp, lo))) { + /* Possible nonblank line */ + nonblankp = TRUE; + /* skip // and # starts */ + if (c == '#' || (slashp && c == '/')) { + nonblankp = FALSE; + break; + } else if (!slashp && c == '/') { + slashp = TRUE; + continue; + } + } + slashp = FALSE; + } + return (nonblankp); +}