1 /* $OpenBSD: cmode.c,v 1.8 2012/05/18 02:13:44 lum Exp $ */
3 * This file is in the public domain.
5 * Author: Kjell Wooding <kjell@openbsd.org>
9 * Implement an non-irritating KNF-compliant mode for editing
18 /* Pull in from modes.c */
19 extern int changemode(int, int, char *);
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 */
26 static int getmatch(int, int);
27 static int getindent(const struct line *, int *);
28 static int in_whitespace(struct line *, int);
29 static int findcolpos(const struct buffer *, const struct line *, int);
30 static struct line *findnonblank(struct line *);
31 static int isnonblank(const struct line *, int);
33 void cmode_init(void);
34 int cc_comment(int, int);
38 static PF cmode_brace[] = {
42 static PF cmode_cCP[] = {
47 static PF cmode_cc[] = {
61 static PF cmode_spec[] = {
65 static struct KEYMAPE (1 + IMAPEXT) cmode_cmap = {
70 { 'P', 'P', cmode_cCP, NULL }
74 static struct KEYMAPE (3 + IMAPEXT) cmodemap = {
79 { CCHR('C'), CCHR('M'), cmode_cc, (KEYMAP *) &cmode_cmap },
80 { ':', ':', cmode_spec, NULL },
81 { '}', '}', cmode_brace, NULL }
85 /* Funtion, Mode hooks */
90 funmap_add(cmode, "c-mode");
91 funmap_add(cc_char, "c-handle-special-char");
92 funmap_add(cc_brace, "c-handle-special-brace");
93 funmap_add(cc_tab, "c-tab-or-indent");
94 funmap_add(cc_indent, "c-indent");
95 funmap_add(cc_lfindent, "c-indent-and-newline");
96 maps_add((KEYMAP *)&cmodemap, "c");
100 * Enable/toggle c-mode
105 return(changemode(f, n, "c"));
109 * Handle special C character - selfinsert then indent.
112 cc_char(int f, int n)
116 if (selfinsert(FFRAND, n) == FALSE)
118 return (cc_indent(FFRAND, n));
122 * Handle special C character - selfinsert then indent.
125 cc_brace(int f, int n)
129 if (showmatch(FFRAND, 1) == FALSE)
131 return (cc_indent(FFRAND, n));
136 * If we are in the whitespace at the beginning of the line,
137 * simply act as a regular tab. If we are not, indent
138 * current line according to whitespace rules.
143 int inwhitep = FALSE; /* In leading whitespace? */
145 inwhitep = in_whitespace(curwp->w_dotp, llength(curwp->w_dotp));
147 /* If empty line, or in whitespace */
148 if (llength(curwp->w_dotp) == 0 || inwhitep)
149 return (selfinsert(f, n));
151 return (cc_indent(FFRAND, 1));
155 * Attempt to indent current line according to KNF rules.
158 cc_indent(int f, int n)
160 int pi, mi; /* Previous indents */
161 int ci, dci; /* current indent, don't care */
168 undo_boundary_enable(FFRAND, 0);
170 deltrailwhite(FFRAND, 1);
173 * Search backwards for a non-blank, non-preprocessor,
177 lp = findnonblank(curwp->w_dotp);
179 pi = getindent(lp, &mi);
181 /* Strip leading space on current line */
182 delleadwhite(FFRAND, 1);
183 /* current indent is computed only to current position */
184 dci = getindent(curwp->w_dotp, &ci);
187 ret = indent(FFOTHARG, 0);
189 ret = indent(FFOTHARG, pi + ci);
191 undo_boundary_enable(FFRAND, 1);
197 * Indent-and-newline (technically, newline then indent)
200 cc_lfindent(int f, int n)
204 if (newline(FFRAND, 1) == FALSE)
206 return (cc_indent(FFRAND, n));
210 * Get the level of indention after line lp is processed
211 * Note getindent has two returns:
212 * curi = value if indenting current line.
213 * return value = value affecting subsequent lines.
216 getindent(const struct line *lp, int *curi)
218 int lo, co; /* leading space, current offset*/
219 int nicol = 0; /* position count */
220 int ccol = 0; /* current column */
221 int c = '\0'; /* current char */
222 int newind = 0; /* new index value */
223 int stringp = FALSE; /* in string? */
224 int escp = FALSE; /* Escape char? */
225 int lastc = '\0'; /* Last matched string delimeter */
226 int nparen = 0; /* paren count */
227 int obrace = 0; /* open brace count */
228 int cbrace = 0; /* close brace count */
229 int contp = FALSE; /* Continue? */
230 int firstnwsp = FALSE; /* First nonspace encountered? */
231 int colonp = FALSE; /* Did we see a colon? */
232 int questionp = FALSE; /* Did we see a question mark? */
233 int slashp = FALSE; /* Slash? */
234 int astp = FALSE; /* Asterisk? */
235 int cpos = -1; /* comment position */
236 int cppp = FALSE; /* Preprocessor command? */
240 /* Compute leading space */
241 for (lo = 0; lo < llength(lp); lo++) {
242 if (!isspace(c = lgetc(lp, lo)))
246 && !(curbp->b_flag & BFNOTAB)
254 /* If last line was blank, choose 0 */
255 if (lo == llength(lp))
259 ccol = nicol; /* current column */
260 /* Compute modifiers */
261 for (co = lo; co < llength(lp); co++) {
263 /* We have a non-whitespace char */
264 if (!firstnwsp && !isspace(c)) {
273 if (!escp && (c == '"' || c == '\'')) {
274 /* unescaped string char */
275 if (getmatch(c, lastc))
278 } else if (c == '"' || c == '\'') {
281 } else if (c == '(') {
283 } else if (c == ')') {
285 } else if (c == '{') {
289 } else if (c == '}') {
291 } else if (c == '?') {
293 } else if (c == ':') {
294 /* ignore (foo ? bar : baz) construct */
297 } else if (c == ';') {
300 } else if (c == '/') {
301 /* first nonwhitespace? -> indent */
303 /* If previous char asterisk -> close */
309 } else if (c == '*') {
310 /* If previous char slash -> open */
315 } else if (firstnwsp) {
319 /* Reset matches that apply to next character only */
328 * If not terminated with a semicolon, and brace or paren open.
332 *curi += cc_colon_indent;
333 newind -= cc_colon_indent;
336 *curi -= (cbrace) * cc_basic_indent;
337 newind += obrace * cc_basic_indent;
340 newind -= cc_cont_indent;
342 newind += cc_cont_indent;
346 /* Ignore preprocessor. Otherwise, add current column */
355 newind = findcolpos(curbp, lp, cpos);
361 * Given a delimeter and its purported mate, tell us if they
365 getmatch(int c, int mc)
374 match = (mc == '\'');
391 in_whitespace(struct line *lp, int len)
394 int inwhitep = FALSE;
396 for (lo = 0; lo < len; lo++) {
397 if (!isspace(lgetc(lp, lo)))
407 /* convert a line/offset pair to a column position (for indenting) */
409 findcolpos(const struct buffer *bp, const struct line *lp, int lo)
414 /* determine column */
417 for (i = 0; i < lo; ++i) {
421 && !(bp->b_flag & BFNOTAB)
426 } else if (ISCTRL(c) != FALSE)
428 else if (isprint(c)) {
431 col += snprintf(tmp, sizeof(tmp), "\\%o", c);
439 * Find a non-blank line, searching backwards from the supplied line pointer.
440 * For C, nonblank is non-preprocessor, non C++, and accounts
441 * for complete C-style comments.
444 findnonblank(struct line *lp)
447 int nonblankp = FALSE;
448 int commentp = FALSE;
453 while (lback(lp) != curbp->b_headp && (commentp || !nonblankp)) {
458 /* Potential nonblank? */
459 nonblankp = isnonblank(lp, llength(lp));
462 * Search from end, removing complete C-style
463 * comments. If one is found, ignore it and
464 * test for nonblankness from where it starts.
467 /* Scan backwards from end to find C-style comment */
468 for (lo = llength(lp) - 1; lo >= 0; lo--) {
469 if (!isspace(c = lgetc(lp, lo))) {
470 if (commentp) { /* find comment "open" */
473 else if (astp && c == '/') {
475 /* whitespace to here? */
476 nonblankp = isnonblank(lp, lo);
478 } else { /* find comment "close" */
481 else if (slashp && c == '*')
482 /* found a comment */
489 /* Rewound to start of file? */
490 if (lback(lp) == curbp->b_headp && !nonblankp)
491 return (curbp->b_headp);
497 * Given a line, scan forward to 'omax' and determine if we
498 * are all C whitespace.
499 * Note that preprocessor directives and C++-style comments
500 * count as whitespace. C-style comments do not, and must
501 * be handled elsewhere.
504 isnonblank(const struct line *lp, int omax)
506 int nonblankp = FALSE; /* Return value */
507 int slashp = FALSE; /* Encountered slash */
508 int lo; /* Loop index */
509 int c; /* char being read */
511 /* Scan from front for preprocessor, C++ comments */
512 for (lo = 0; lo < omax; lo++) {
513 if (!isspace(c = lgetc(lp, lo))) {
514 /* Possible nonblank line */
516 /* skip // and # starts */
517 if (c == '#' || (slashp && c == '/')) {
520 } else if (!slashp && c == '/') {