home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mega Top 1
/
os2_top1.zip
/
os2_top1
/
DEMO
/
RIM22
/
MACROS
/
C_SMART.RM
< prev
next >
Wrap
Text File
|
1993-12-08
|
41KB
|
1,608 lines
/*
* c_smart.rm
*
* 'C' smart indenting commands for RimStar Programmer's Editor V2.0
*
* Copyright (C) 1993 Free Software Foundation, Inc.
* Copyright (C) 1993, Brian L. Smith, K. Shane Hartman
*
* This file was ported from GNU Emacs.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details (the file COPYING)
*
* If you want a copy of the GNU General Public License, write to the
* Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* 16 Nov 93 - Fixed bug in c_backward_to_noncomment() causing
* infinite loop when preprocessor command is first
* non-white in file.
* 21 Nov 93 - Added _c_newline_break() to give alternate to _c_newline()
* this function will break the line instead of just opening a
* a line.
*/
#include "macro.h"
extern long IndentSize; /* built-in editor variable */
char *comment_start;
char *comment_end;
short comment_column = 40;
long matchstart = 0;
long matchend = 0;
BOOL auto_indent = 1;
BOOL Matchdelim = 0;
/* Enable much faster shortcut (most cases) for Shane's indentation style,
* if indentation looks funny for you, set this to 0. The full indenter
* is always correct for all settings of the variables below.
*/
int c_indenter_shortcut = 0;
/* Indentation of C statements with respect to containing block. */
int c_indent_level = 3;
/* Imagined indentation of a C open brace that actually follows a statement. */
int c_brace_imaginary_offset = 0;
/* Extra indentation for braces, compared with other text in same context. */
int c_brace_offset = 0;
/* If top level braces should be indented set to 1; if you want them pulled
* back to column 1, set to 0.
*/
int c_indent_toplevel_brace = 0;
/* Indentation level of declarations of C function arguments.
* Note this must be 0 for the quick indenter to work.
*/
int c_argdecl_indent = 4;
/* Offset of C label lines relative to usual indentation. */
int c_label_offset = -2;
/* Offset of C case statements relative to usual indentation. */
int c_case_offset = -3;
/* Extra indent for lines not starting new statements. */
int c_continued_statement_offset = 3;
/* If pre-processor statements should start at column 1 set this non-zero */
int c_preproc_col_1 = 0;
/* If c_preproc_col_1 == 0 then offset # statements from current indent level */
int c_preproc_offset = -1;
/* if you use a space after your open parens, set this to 2 else set it to 1
* Makes continued statements line up correctly.
*/
int c_paren_extra_space = 2;
/* Non-zero means TAB in C mode should always reindent the current line,
* regardless of where in the line the cursor is when the TAB command is used.
*/
int c_tab_always_indent = 0;
typedef struct pstate {
long beginning_of_defun;
long containing_cexp;
int incomment;
int instring;
int quoted;
int level;
} PSTATE;
int c_backward_to_start_of_do( long limit );
void c_backward_to_start_of_if( long limit );
void c_backward_to_noncomment( long limit );
long calculate_c_indent_in_comment( void );
long calculate_c_indent( PSTATE *state );
#define ARE_AT(pat) are_at(1, pat)
#define INDENT_TO(indent) Indent(indent)
#define SKIP_CHARS_BACKWARD(set) skip_chars (-1, (set))
#define SKIP_CHARS_FORWARD(set) skip_chars (1, (set))
#define FORWARD_SEXP() traverse_cexp(1)
#define BACKWARD_SEXP() traverse_cexp(-1)
#define SEARCH_FORWARD(str) find(1, (str))
#define SEARCH_BACKWARD(str) find(-1, (str))
#define AtSOL() (BufQueryColumn() == 1L)
#define BEGINNING_OF_DEFUN() \
( find_pat (-1, "^[{A-Za-z0-9$_]+[^A-Za-z0-9$_:]") ? \
(PosSOL(), 1) : (PosSOF(), 0) )
/* replace current indentation with indent to col */
void
Indent(long col) {
BufDeleteWhitespace();
BufIndentColumn(BufQueryColumn(), col);
} /* end Indent() */
int
find(int dir, char *str) {
long len;
if ( dir > 0 ) {
if ( (len = SrchFwd(str, 0, 1, 0, 0L)) > 0L )
PosNextChar(len);
else
PosEOF();
} else {
if ( (len = SrchBack(str, 0, 1, 0, 0L)) < 0L )
PosSOF();
}
return len != -1L ? 1 : 0;
} /* end find() */
int
find_pat(int dir, char *str) {
long len;
if ( dir > 0 ) {
if ( (len = SrchFwd(str, -1, 1, 0, 0L)) > 0L )
PosNextChar(len);
else
PosEOF();
} else {
if ( PosPrevChar() )
return 0;
if ( (len = SrchBack(str, -1, 1, 0, 0L)) < 0L )
PosSOF();
}
return len != -1 ? 1 : 0;
} /* end find_pat() */
void
skip_chars(int dir, char *set) {
if ( dir < 0 )
PosPrevChar();
while ( strchr(set, (BufQueryChar())) )
if ( PosNextChar((long)dir) )
break;
if ( dir < 0 )
PosNextChar();
} /* end skip_chars() */
long
query_indent_of_line() {
long indent;
MarkPushPos();
PosSOT();
indent = BufQueryColumn();
MarkPopPos();
return indent;
} /* end current_indent() */
int
Srch(int dir, char *pat) {
if ( dir > 0 )
return SrchFwd(pat, -1, 1, 0, 0);
else {
if ( PosPrevChar() )
return -1;
return SrchBack(pat, -1, 1, 0, 0);
}
} /* end Srch() */
int
are_at(int dir, char *pat, char *result) {
int rc = 0;
int len;
matchend = BufQueryOffset();
MarkPushPos();
if ( len = Srch(dir, pat) > 0 ) {
matchstart = BufQueryOffset();
if ( matchstart == matchend )
rc = 1;
else
rc = 0;
matchend = matchstart + len;
}
MarkPopPos();
return rc;
} /* end are_at() */
int
BufQueryPrevChar() {
long offset;
if ( (offset = BufQueryOffset()) == 0L )
return 0;
return BufQueryChar(offset-1);
} /* end BufQueryPrevChar() */
char matching_punct[] = "][]}{})()\"\"''";
char opening_punct[] = "[{(";
char closing_punct[] = "]})";
char
get_matching_punct(char c) {
char *s = strchr(matching_punct, c);
if ( !s )
return 0;
else
return s[1];
}
void
MovNextLine( void ) {
PosEOL();
PosNextChar();
} /* end of MovNextLine */
void
MovPrevLine( void ) {
PosSOL();
PosPrevChar();
} /* end of MovNextLine */
#define is_opening_punct(c) (strchr(opening_punct, (c)) != NULL)
#define is_closing_punct(c) (strchr(closing_punct, (c)) != NULL)
int
FindStartOfDef(void) {
if ( SrchBack("^[{A-Za-z0-9$_]+[^A-Za-z0-9$_:]", -1, 1, 0) > 0 )
return 1;
PosSOF();
return 0;
} /* end FindStartOfDef() */
int
query_matching_slashes (int dir) {
long slash_count = 0;
MarkPushPos();
if ( dir > 0 )
PosPrevChar();
while ( !PosPrevChar() && BufQueryChar() == '\\' )
slash_count++;
MarkPopPos();
return (int)(slash_count & 1); /* returns 1 if odd number of slashes 0 if even */
}
void
skip_c_comments(int dir) {
long offset = 1L;
long last;
long cp;
char c;
char cc;
last = BufQueryFilesize();
if (dir != 1) {
dir = -1;
offset = 0;
}
while ( find_pat(dir, "[^ \t\n\f]") &&
(cp = BufQueryOffset()) > 0 && cp < last ) {
c = BufQueryChar(cp - offset);
cc = BufQueryChar(cp - offset + dir);
if ( c == '/' ) {
if ( cc == '*' )
find(dir, dir > 0 ? "*/" : "/*");
else if ( cc == '/' ) { /* c++ comment */
if ( dir > 0 ) {
MovNextLine();
} else
PosPrevChar();
}
} else {
if ( dir > 0 )
PosPrevChar();
break;
}
}
} /* end skip_c_comment() */
/*
** move to next/prev c expression
*/
long
traverse_cexp(int dir) {
long level = 0L;
char c = 0;
char start = 0;
char end = 0;
long offset = 1L;
char buf[2];
char patbuf[40];
long orig;
orig = BufQueryOffset();
if ( dir != 1 ) {
dir = -1;
offset = 0;
}
if ( dir > 0 &&
orig < BufQueryFilesize() &&
strchr(":;?,", BufQueryChar())
)
PosNextChar();
skip_c_comments(dir);
if ( !strchr(matching_punct, c = (BufQueryChar())) ) {
if ( find_pat(dir, "[][)(}{\"';:,? \t\n\f]") ) {
if ( dir < 0 )
PosNextChar();
else
PosPrevChar();
return 1;
}
return 0;
} else if ((is_opening_punct(c) && dir > 0) ||
(is_closing_punct(c) && dir < 0) ||
c == '"' || c == '\'' )
{
if ( dir < 0 )
PosNextChar();
strcpy(patbuf, "([][)(}{\"'])|(/\\*)|(\\*/)|(//)");
while ( find_pat(dir, patbuf) ) {
buf[0] = c = BufQueryChar(BufQueryOffset() - offset);
buf[1] = 0;
if ( start == 0 ) {
start = c;
end = get_matching_punct(start);
if ( !end )
start = 0;
else {
strcpy (patbuf, "\\X|\\X|([\"'])|(/\\*)|(\\*/)|(//)");
patbuf[1] = start;
patbuf[4] = end;
}
}
if ( c == '"' || c == '\'' ) {
char strpat[8];
strcpy (strpat, "\\X|\n");
strpat[1] = c;
while ( find_pat(dir, strpat) && query_matching_slashes(dir) )
;
if ( BufQueryPrevChar() == '\n')
break;
} else if ( c == '*' ) {
find(dir, dir > 0 ? "*/" : "/*");
} else if ( c == '/' ) { /* c++ comment */
if ( dir > 0 ) {
PosEOL();
PosNextChar();
}
}
if ( c == start )
level++;
if ( c == end && !--level )
break;
}
return ( level == 0 && start != 0 );
} else {
if ( (dir > 0 && c == ')') ||
(dir < 0 && c == '(') ) {
PosNextChar((long)dir);
return 1;
}
else
PosToOffset(orig);
return 0;
}
} /* end traverse_cexp() */
void
parse_partial_cexp(long from, long to, PSTATE* state) {
char c = 0;
long stack[64];
char msg[ 64 ];
long point;
state->instring = 0;
state->incomment = 0;
state->containing_cexp = -1L;
state->quoted = 0;
state->level = -1;
point = from;
while ( find_pat (1, "([][(){}\"'])|(/\\*)|(//)") && ( (point = BufQueryOffset() ) < to) ) {
c = BufQueryPrevChar();
if ( is_opening_punct(c) ) {
state->level++;
if ( state->level > 63 ) {
sprintf( msg, "Expression nesting too deep: %d:", state->level);
PopupMsg( msg, "parse_partial_cexp" );
abort();
}
stack[state->level] = point - 1L;
} else if ( is_closing_punct(c) ) {
if ( state->level >= 0 &&
get_matching_punct(BufQueryChar(stack[state->level])) == c )
state->level--;
else if ( state->level >= 0 ) {
PosToOffset(stack[state->level]);
sprintf( msg, "Error: Should have found '%c' but found '%c' instead", BufQueryChar(stack[state->level]), c );
PopupMsg( msg, "parse_partial_cexp" );
abort();
}
} else if ( c == '"' || c == '\'' ) { /* quotes */
char strpat[8];
state->quoted = -1;
strcpy (strpat, "[X\n]");
strpat[1] = c;
while ( find_pat(1, strpat) && /* find matching quote or end of line */
query_matching_slashes(1) && /* single slashe behind it (the quote found is escaped) */
(point = BufQueryOffset()) < to ) /* not past where we are indenting */
;
if ( point < to )
state->quoted = 0; /* found the matching quote */
} else if (c == '*' || c == '/') {
state->incomment = 1;
state->quoted = -2;
if ( c == '*' ) {
find(1, "*/"); /* find close of comment */
if ( BufQueryOffset() < to )
state->incomment = state->quoted = 0;
} else { /* C++ comment - go to next line */
MovNextLine();
state->incomment = state->quoted = 0;
}
}
}
if ( state->level >= 0 )
state->containing_cexp = stack[state->level];
} /* end parse_partial_cexp() */
void
find_matching_delim(void) {
long level;
long offset;
int c;
int dir;
int start, end;
char patbuf[40];
c = BufQueryChar();
if ( is_opening_punct(c) ) {
dir = 1;
offset = 0L;
PosNextChar();
} else if ( is_closing_punct(c) ) {
dir = -1;
offset = 1L;
} else
return;
level = 0L;
start = 0;
do {
if ( dir > 0 )
c = BufQueryPrevChar();
else
c = BufQueryChar();
if ( start == 0 ) {
start = c;
end = get_matching_punct(start);
if ( !end )
start = 0;
else {
strcpy (patbuf, "\\X|\\X|([\"'])|(/\\*)|(\\*/)|(//)");
patbuf[1] = start;
patbuf[4] = end;
}
}
if ( c == '"' || c == '\'' ) {
char strpat[8];
strcpy (strpat, "\\X|\n");
strpat[1] = c;
while ( find_pat(dir, strpat) && query_matching_slashes(dir) )
;
if ( BufQueryPrevChar() == '\n' )
break;
} else if ( c == '*' ) {
find(dir, dir > 0 ? "*/" : "/*");
} else if ( c == '/' ) { /* c++ comment */
if ( dir > 0 ) {
PosEOL();
PosNextChar();
}
}
if ( c == start )
level++;
else if ( c == end && !--level ) {
if ( dir > 0 )
PosPrevChar();
break;
}
} while ( find_pat(dir, patbuf) );
} /* end find_matching_delim() */
/*
* Indenting functions
*/
#if 0
/* This will indent comments nicely but is not hooked up */
long
c_comment_indent() {
long ret = 0;
long save_excursion;
long opoint;
if (ARE_AT("^/\\*"))
return 0L; /* Existing comment at bol stays. */
save_excursion = opoint = BufQueryOffset();
PosSOL();
if ( ARE_AT("[ \t]*}[ \t]*(\n|(/\\*))") )
/*
* A comment following a solitary close-brace
* should have only one space.
*/
{
find(1, "}");
ret = BufQueryColumn() + 1;
}
else if ( ARE_AT("^#[ \t]*endif[ \t]*") ||
ARE_AT("^#[ \t]*else[ \t]*") )
ret = 7; /* 2 spaces after #endif */
else if ( PosToOffset(opoint),
skip_chars(-1, " \t"),
!comment_column && AtSOL() )
/*
* If comment_column is 0, and nothing but space
* before the comment, align it at 0 rather than 1.
*/
ret = 0;
else {
ret = BufQueryColumn() + 1; /* Else indent at comment column */
if ( comment_column > ret )
ret = comment_column; /* except leave at least one space. */
}
PosToOffset(save_excursion);
return ret;
} /* end c_comment_indent() */
#endif
/*
* Indent current line as C code. This does the work.
*/
long
c_indenter_1( PSTATE *state ) {
long indent;
long pos;
long startPos;
long size = BufQueryFilesize();
int ch;
startPos = BufQueryOffset();
if ( startPos == size ) { /* at EOF */
/*
* So that indenting in a brand new file works correctly
* when there are no characters after the indent point.
*/
BufInsertNewline(); /* BufInsertChar( '\n' ); */
PosPrevChar();
++size;
}
pos = size - startPos;
indent = calculate_c_indent(state);
PosSOL();
if ( indent == -1 ) /* do not change indent */
indent = query_indent_of_line();
else if ( indent == -2 ) /* in a comment */
indent = calculate_c_indent_in_comment ();
else if ( ARE_AT("[ \t]*#") ) { /* a preprocessor line */
if ( c_preproc_col_1 )
indent = 0L;
else
indent += c_preproc_offset;
} else {
startPos = BufQueryOffset();
skip_chars(1, " \t");
if ( ARE_AT("(case[ \t'(])|(default:)") ) { /* case or default */
if ( state->containing_cexp >= 0 ) {
PosToOffset( state->containing_cexp );
indent = query_indent_of_line() + c_case_offset + c_indent_level;
if ( indent < 0L )
indent = 0L;
PosToOffset( startPos );
}
} else if ( ARE_AT("[A-Za-z0-9$_]+:") ) { /* a label */
indent += c_label_offset;
if ( indent < 0L )
indent = 0L;
} else if ( ARE_AT("else[ \t\n]") ) { /* else followed by whitespace */
c_backward_to_start_of_if(state->beginning_of_defun);
indent = query_indent_of_line();
PosToOffset( startPos );
} else if ( ARE_AT("}[ \t]*else") ) { /* cbrace followed by else */
PosNextChar(); /* move past brace */
traverse_cexp(-1);
indent = query_indent_of_line();
PosToOffset( startPos );
} else if ( ARE_AT("while[ \t\n]") ) { /* `while' statement */
if ( c_backward_to_start_of_do(state->beginning_of_defun) ) {
/* This is a `while' that ends a do-while. */
indent = query_indent_of_line();
PosToOffset( startPos );
} else
goto next;
} else {
next:
ch = BufQueryChar();
if ( ch == '}' )
indent -= c_indent_level - c_brace_offset;
else if ( ch == '{' )
indent += c_brace_offset;
}
}
PosSOT();
if ( !c_indenter_shortcut ) {
ch = BufQueryChar();
if (ch == '{' &&
c_brace_offset &&
state->containing_cexp <= 0L ) {
if ( c_indent_toplevel_brace )
++indent; /* Indent top level braces */
else
indent -= c_brace_offset; /* Pull top level brace back a level */
} else if ( ch == '}' ) {
startPos = BufQueryOffset();
PosToOffset( state->containing_cexp );
if ( AtSOL() )
indent = 0;
PosToOffset( startPos );
}
}
if ( BufQueryColumn() != indent )
Indent(indent);
/*
* If initial point was within line's indentation,
* position after the indentation. Else stay at same point in text.
*/
size = BufQueryFilesize();
if ( size - pos > BufQueryOffset() )
PosToOffset( size - pos );
return indent;
} /* end c_indenter_1() */
int
c_indenter( void ) {
PSTATE state;
state.beginning_of_defun = -1;
return c_indenter_1( &state );
}
void
c_backward_to_exp_start( long lim ) {
if ( strchr( ")\"", BufQueryPrevChar()) )
traverse_cexp(-1);
PosSOL();
if ( BufQueryOffset() <= lim )
PosToOffset( lim + 1 );
skip_chars(1, " \t");
}
/*
* If point follows a `do' statement, move to beginning of it and return t.
* Otherwise return nil and don't move point.
*/
int
c_backward_to_start_of_do( long limit ) {
long save_excursion;
if ( limit < 0 ) {
#if 0
save_excursion = BufQueryOffset();
FindStartOfDef();
limit = BufQueryOffset();
PosToOffset(save_excursion);
#else
limit = 0;
#endif
}
{
long first = 1;
long startpos = BufQueryOffset();
long done = 0;
while ( !done ) {
c_backward_to_noncomment( limit );
if ( !traverse_cexp(-1) )
done = 2;
else if ( ARE_AT( "do[^A-Za-z0-9_$]" ) )
done = 1;
/*
* Otherwise, if we skipped a semicolon, we lose.
* (Exception: we can skip one semicolon before getting
* to the last token of the statement, unless that token
* is a close brace).
*/
else {
save_excursion = BufQueryOffset();
if ( traverse_cexp(1) ) {
if (!first && BufQueryPrevChar() == '}')
done = 2;
if (!done && BufQueryChar() == ';' && BufQueryPrevChar() == '}')
done = 2;
if (!done && BufQueryChar() == ';') {
if (first) {
if ( BufQueryPrevChar() == ')' && traverse_cexp(-1) ) {
if (traverse_cexp(-1) &&
ARE_AT("while") &&
c_backward_to_start_of_do(limit))
continue;
}
}
if (!first)
done = 2;
first = 0;
}
PosToOffset(save_excursion);
}
}
/* If we go too far back in the buffer, we lose. */
if ( BufQueryOffset() < limit && !done)
done = 2;
}
if (done != 1)
PosToOffset( startpos );
return done == 1;
}
} /* c_backward_to_start_of_do() */
/* Move to the start of the last "unbalanced" `if'. */
void
c_backward_to_start_of_if( long limit ) {
if ( limit < 0 ) {
#if 0
long save_excursion = BufQueryOffset();
FindStartOfDef();
limit = BufQueryOffset();
PosToOffset(save_excursion);
#else
limit = 0;
#endif
}
{
long if_level = 1;
while ( BufQueryOffset() > 0 && if_level > 0 ) {
if ( !traverse_cexp(-1) ) {
/* not bracketed if */
if ( if_level != 1 ) {
PopupMsg( "Syntax error", "c_backward_to_start_of_if" );
abort();
}
return;
}
if ( ARE_AT("else[^A-Za-z0-9$_]") )
if_level++;
else if ( ARE_AT("if[^A-Za-z0-9$_]") )
if_level--;
else if ( BufQueryOffset() < limit ) {
if_level = 0;
PosToOffset(limit);
}
}
}
} /* end c_backward_to_start_of_if() */
void
c_backward_to_noncomment( long lim ) {
long opoint = 0L;
long last_point = -1L;
long stop = 0L;
if ( lim < 0L )
lim = 0L;
while ( !stop ) {
skip_chars(-1, " \t\n\f");
opoint = BufQueryOffset();
if ( BufQueryOffset() >= 2L + lim ) {
PosPrevChar( 2L );
if (ARE_AT("\\*/")) {
if ( !find(-1, "/*") )
PosToOffset( lim > 0L ? lim : 0L );
continue;
}
PosNextChar( 2L );
}
if ( BufQueryOffset() <= lim || last_point == opoint )
stop = 1;
else {
PosSOL();
skip_chars(1, " \t");
if ( BufQueryChar() != '#' && !ARE_AT( "//" ) ) {
PSTATE ps;
long from = BufQueryOffset();
long to;
PosEOL();
to = BufQueryOffset();
PosToOffset( from );
parse_partial_cexp(from, to, &ps);
if ( ps.incomment && are_at (-1, "//.*") )
PosToOffset(matchstart);
else
stop = 1;
}
PosToOffset(opoint);
if ( !stop ) {
PosSOL();
last_point = opoint;
}
}
}
} /* end c_backward_to_noncomment() */
/*
* Return the indentation amount for line inside a block comment.
*/
long
calculate_c_indent_in_comment( void ) {
long indent = 0;
long save_excursion = BufQueryOffset();
PosSOL();
if ( BufQueryOffset() > 0 ) {
skip_chars(-1, " \t\n");
PosSOL();
skip_chars(1, " \t");
indent = BufQueryColumn();
if ( ARE_AT("/\\*") )
indent++;
}
PosToOffset( save_excursion );
return indent;
}
/*
* This lookback parser was not part of the original code. I added it
* to speed the beast up in most cases. It works for my indentation style;
* it may not work for yours since it makes more assumptions than the
* general indenter. It is disabled by default, set the variable
* c-indenter-shortcut to 1 if you want to try it out. It is much faster
* if you indent like the style in this file but it WILL misindent code
* that does not use braces for cues in complicated code. The general
* indenter can handle everything.
*/
int
calculate_simple_c_indent(long indent_point) {
long save_excursion;
long lim;
long ret = -1;
save_excursion = BufQueryOffset();
lim = save_excursion - 512;
if ( !c_indenter_shortcut )
return -1;
if ( lim < 0 )
lim = -1;
PosToOffset(indent_point);
PosSOT();
indent_point = BufQueryOffset();
if ( BufQueryChar() == '}' ) {
PosNextChar();
if ( traverse_cexp(-1) ) {
long save_excursion_1 = BufQueryOffset();
PosSOT ();
if ( BufQueryOffset() == save_excursion_1 )
ret = BufQueryColumn() - c_brace_offset + c_indent_level;
}
goto finish;
}
c_backward_to_noncomment(lim);
if ( BufQueryOffset() > 0 ) {
if ( BufQueryPrevChar() == '{') {
long save_excursion_1 = BufQueryOffset();
PosPrevChar();
c_backward_to_noncomment(lim);
if ( BufQueryPrevChar() == ')' )
c_backward_to_exp_start(lim);
else {
PosToOffset(save_excursion_1);
PosSOT ();
}
ret = BufQueryColumn() + c_indent_level;
if ( BufQueryChar() == '{' )
ret -= c_brace_offset;
}
else if ( BufQueryPrevChar() == '}' ) {
PosSOT ();
ret = BufQueryColumn() - c_brace_offset;
} else if ( BufQueryPrevChar() == ')' ) {
long save_excursion_1;
c_backward_to_exp_start(lim);
save_excursion_1 = BufQueryOffset();
if ( ARE_AT("((if)|(else)|(else[ \t]+if)|(switch)|(for))[ \t\n(]") ) {
PosToOffset(matchend);
if ( traverse_cexp(1) && BufQueryOffset() <= indent_point ) {
PosToOffset(save_excursion_1);
ret = BufQueryColumn();
PosToOffset(indent_point);
if ( BufQueryChar() != '{' )
ret += c_indent_level;
}
else
PosToOffset(save_excursion_1);
} else
PosToOffset(save_excursion_1);
} else if (BufQueryPrevChar() == ';') {
PosPrevChar();
c_backward_to_exp_start(lim);
if (!ARE_AT("for[ \t\n(]")) {
long save_excursion_1 = BufQueryOffset();
skip_c_comments(-1);
if ( BufQueryChar() == ';' ) {
PosToOffset(save_excursion_1);
ret = query_indent_of_line();
} else if ( strchr("{}", BufQueryChar()) ) {
PosToOffset(save_excursion_1);
ret = query_indent_of_line();
} else {
PosSOT();
if ( ARE_AT("((else)|(if)|(for)|(while)|(switch))[ \t\n(]") )
ret = BufQueryColumn();
else if ( ARE_AT("(case[ \t'(])|(default:)") )
ret = BufQueryColumn() + c_indent_level - c_case_offset;
}
PosToOffset(indent_point);
if ( ARE_AT("(case[ \t'(])|(default:)") )
ret -= c_indent_level;
}
} else if ( BufQueryPrevChar() == ':' ) {
PosSOT();
if ( ARE_AT("(case[ \t'(])|(default:)") ) {
ret = BufQueryColumn() + c_indent_level;
PosToOffset(indent_point);
if ( ARE_AT("(case[ \t'(])|(default:)") )
ret -= c_indent_level;
else if ( BufQueryChar() == '{' )
ret -= c_case_offset + c_indent_level;
}
}
}
finish:
if ( ret >= 0 ) {
PosToOffset(indent_point);
if ( ARE_AT("(case[ \t'(])|(default:)") )
ret += c_case_offset;
}
PosToOffset(save_excursion);
return ret;
} /* end calculate_simple_c_indent() */
/*
* Return appropriate indentation for current line as C code.
* In usual case returns an integer: the column to indent to.
* Returns -1 if line starts inside a string, -2 if in a comment.
*/
long
calculate_c_indent(PSTATE *state) {
long startPos;
long savePos;
long found;
long colon_line_end;
long indent_point = 0L;
long parse_start = 0L;
long containing_cexp = -1L;
long ret = 0L;
PSTATE ps;
int stop = 0;
char c;
startPos = BufQueryOffset();
if ( !state ) {
state = &ps;
state->beginning_of_defun = -1L;
}
state->containing_cexp = -1L;
PosSOL();
if ( BufQueryOffset() > 0L ) { /* Not at SOF */
indent_point = BufQueryOffset();
if ( c_indenter_shortcut ) {
ret = calculate_simple_c_indent(indent_point);
if ( ret >= 0 )
goto finish;
}
if ( state->beginning_of_defun == -1 ) {
FindStartOfDef();
state->beginning_of_defun = BufQueryOffset();
}
PosToOffset(state->beginning_of_defun);
ret = 0L;
do {
parse_start = BufQueryOffset();
parse_partial_cexp(parse_start, indent_point, state);
} while ( BufQueryOffset() < indent_point );
containing_cexp = state->containing_cexp;
if ( state->incomment && ARE_AT("[ \t]*//") ) { /* at start of C++ comment? */
state->quoted = 0L;
state->incomment = 0L;
}
if ( state->instring || state->incomment ) {
/* return -1 or -2 if should not change this line */
ret = state->quoted;
} else if ( containing_cexp < 0L ) {
/* Line is at top level. May be data or function definition,
* or may be function argument declaration.
* Indent like the previous top level line
* unless that ends in a closeparen without semicolon,
* in which case this line is the first argument decl.
*/
PosToOffset(indent_point);
skip_chars(1, " \t");
if ( BufQueryChar() == '{' )
ret = 0L; /* Unless it starts a function body */
else if ( c_argdecl_indent > 0 ) { /* Handle *ix style arglist */
c_backward_to_noncomment(parse_start > 0L ? parse_start : 0L);
/*
* look at previous line that's at column 0
* to determine whether we are in top-level decls
* or function's arg decls.
*/
find_pat(-1, "^[^ \f\t\n#]"); /* beginning of line no whitespace or preprocessor statements */
if ( ARE_AT("[A-Za-z0-9$_]+[^\"\n=]*\\(") ) {
PosToOffset( matchend-1L );
traverse_cexp(1);
skip_chars(1, " \t\f");
if ( BufQueryOffset() < indent_point &&
!strchr(",;", BufQueryChar()) )
ret = c_argdecl_indent;
}
}
} else if ( BufQueryChar(containing_cexp) != '{' ) {
/*
* line is expression, not statement:
* indent to just after the surrounding open.
*/
PosToOffset( containing_cexp + c_paren_extra_space );
ret = BufQueryColumn();
} else {
/*
* Statement level. Is it a continuation or a new statement
* Find previous non-comment character.
*/
PosToOffset(indent_point);
c_backward_to_noncomment(containing_cexp);
/*
* Back up over label lines, since they don't
* affect whether our line is a continuation.
*/
while ( (c = BufQueryPrevChar()) == ',' ||
( c == ':' && ( (c = BufQueryChar(BufQueryOffset()-2)) == '\'' || iscsym(c) ) )
) {
if ( BufQueryChar() == ',' ) {
PosPrevChar();
c_backward_to_exp_start(containing_cexp);
}
PosSOL();
c_backward_to_noncomment(containing_cexp);
}
/*
* Check for a preprocessor statement or its continuation lines.
* Move back to end of previous non-preprocessor line.
*/
found = BufQueryOffset();
stop = 0;
while ( !stop ) {
savePos = BufQueryOffset();
if ( savePos > 0L )
PosEOL();
if ( BufQueryPrevChar() != '\\' ) {
PosToOffset(savePos);
/*
* This line is not preceded by a backslash.
* So either it starts a preprocessor command
* or any following continuation lines
* should not be skipped.
*/
PosSOT();
if ( BufQueryChar() == '#' ) {
PosEOL();
found = BufQueryOffset();
} else
stop = 1;
} else
MovPrevLine();
}
PosToOffset(found);
/* Now we get the answer. */
savePos = BufQueryOffset();
PosToOffset(indent_point);
skip_chars(1, " \t");
/*
* Don't treat a line with a close-brace
* as a continuation. It is probably the
* end of an enum type declaration.
*/
if ( BufQueryChar() != '}' ) {
PosToOffset(savePos);
if ( BufQueryOffset() > 0 && !strchr(",;{}", BufQueryPrevChar()) ) {
/*
* This line is continuation of preceding line's statement
* indent c_continued_statement_offset more than the
* previous line of the statement.
*/
c_backward_to_exp_start(containing_cexp);
ret = BufQueryColumn();
PosToOffset(indent_point);
skip_chars(1, " \t");
if ( BufQueryChar() != '{' )
ret += c_continued_statement_offset;
} else
goto new_statement;
} else {
new_statement:
/*
* This line starts a new statement.
* Position following last unclosed open.
*/
PosToOffset(containing_cexp);
/*
* Is line first statement after an open-brace or after a case.
* If no, find that first statement and indent like it.
*/
PosNextChar();
colon_line_end = 0L;
skip_chars(1, " \t\n");
while ( ARE_AT( "#|(/\\*)|(case[ \t\n'(].*:)|([a-zA-Z0-9_$]*:)" ) ) {
/* Skip over comments and labels following openbrace. */
if ( (c = BufQueryChar()) == '#' )
MovNextLine();
else if ( c == '/') {
PosToOffset(BufQueryOffset()+2L);
find(1, "*/");
} else {
/* case or label: */
savePos = BufQueryOffset();
PosEOL();
colon_line_end = BufQueryOffset();
PosToOffset(savePos);
find(1, ":");
}
skip_chars(1, " \t\n");
}
/*
* The immediately following code counts
* if it is before the line we want to indent.
*/
savePos = BufQueryOffset();
if ( savePos < indent_point &&
colon_line_end > 0L &&
colon_line_end < indent_point ) {
if ( colon_line_end > savePos )
ret = query_indent_of_line() - c_label_offset;
else if ( colon_line_end > 0L &&
colon_line_end > containing_cexp) {
PosToOffset(colon_line_end);
PosSOT();
if ( ARE_AT("(case[ \t'(])|(default:)") ) {
ret = BufQueryColumn();
PosToOffset(indent_point);
skip_chars(1, " \t");
if ( ! ARE_AT("[{}]") )
ret += c_indent_level;
else
ret -= (c_case_offset + c_brace_offset);
PosToOffset(savePos);
} else {
PosToOffset(savePos);
ret = BufQueryColumn();
}
} else
goto no_previous;
} else {
no_previous:
/*
* If no previous statement,
* indent it relative to line brace is on (or the last case
* statement). For open brace in column zero, don't let
* statement start there too. If c_indent_level is zero,
* use c_brace_offset + c_continued_statement_offset instead.
* For open-braces not the first thing in a line,
* add in c_brace_imaginary_offset.
*/
PosToOffset(containing_cexp);
if ( AtSOL() && !c_indent_level )
ret = c_brace_offset + c_continued_statement_offset;
else if ( AtSOL() )
ret = c_indent_level;
else
ret = c_indent_level - c_brace_offset;
/*
* Move back over whitespace before the openbrace.
* If openbrace is not first nonwhite thing on the line,
* add the c_brace_imaginary_offset.
*/
skip_chars(-1, " \t");
if ( !AtSOL() )
ret += c_brace_imaginary_offset;
/*
* If the openbrace is preceded by a parenthesized exp,
* move to the beginning of that;
* possibly a different line.
*/
if ( BufQueryPrevChar() == ')' )
traverse_cexp(-1);
/* Get initial indentation of the line we are on. */
ret += query_indent_of_line();
}
}
}
}
finish:
PosToOffset(startPos);
return ret;
} /* end calculate_c_indent() */
/*
** This function does not work on all styles
** of function beginnings, therefore it is not
** being used.
*/
void
beginning_of_defun() {
long last;
char c, cp;
last = BufQueryOffset();
while ( FindStartOfDef() ) {
long save_excursion;
long next;
next = BufQueryOffset();
if ( BufQueryChar() == '{') {
while ( BufQueryOffset() > 0 ) {
MovPrevLine();
save_excursion = BufQueryOffset();
PosSOL();
next = BufQueryOffset();
skip_chars(1, " \t");
if ( BufQueryOffset() == save_excursion ) {
MovNextLine();
break;
}
PosToOffset(next);
if ( ARE_AT("#|(.*\\*/)|(//)") ) {
MovNextLine();
break;
}
}
break;
}
skip_c_comments(-1);
save_excursion = BufQueryOffset();
PosSOL();
if ( BufQueryChar() == '#' ) {
PosToOffset(next);
break;
}
PosEOL();
if ( (c = BufQueryPrevChar()) == '\\' ) {
PosToOffset(next);
break;
} else if ( c == '{' || BufQueryChar() == '{' ) {
PosSOL();
break;
}
last = next;
PosToOffset(save_excursion);
c = BufQueryChar();
cp = BufQueryPrevChar();
if ( c == '}' ||
( c == ';' &&
save_excursion > 0L &&
( cp == '}' || cp == ')' ||
(traverse_cexp(-1),
skip_c_comments (-1),
BufQueryChar() == '='
)
)
)
) {
PosToOffset(last);
break;
}
if ( BufQueryOffset() == 0L )
break;
PosEOL();
PosNextChar();
}
} /* end beginning_of_defun() */
/*
* Commands
*/
/* make this the default mode for .c, .cpp, .h, .e, and .y files */
void
_c_indent() { /* on Tab key */
long lastPos;
if ( MarkQuerySelType() ) {
slide_in();
return;
}
if ( !c_tab_always_indent ) {
lastPos = BufQueryOffset();
/* If not in leading whitespace, do a tab */
skip_chars(-1, " \t");
if ( !AtSOL() ) {
PosToOffset(lastPos);
BufInsertChar('\t');
return;
}
PosToOffset(lastPos);
}
c_indenter();
} /* end _c_indent() */
/* _c_newline_break - reindents current line, breaks the line, and
* indents to proper position
*/
void
_c_newline_break() { /* on Keypad+Enter */
if ( auto_indent ) {
BufNewlineIndent();
c_indenter();
} else
BufInsertNewline();
} /* end _c_newline_break() */
/* _c_newline - reindents current line, opens a new line below, and
* indents to proper position in the new line
*/
void
_c_newline() { /* on Enter */
if ( auto_indent ) {
PosSOT();
/* May need to update indenting on current line */
if ( ARE_AT("#|((case)|(else))|([A-Za-z0-9$_]+:)|(while[ \t]*\\(.*\\)[ \t]*;)") )
c_indenter();
PosEOL();
BufInsertNewline();
c_indenter();
} else
BufInsertNewline();
} /* end _c_newline() */
void
_c_open() { /* on '{' */
BufInsertChar( '{' );
if ( auto_indent )
c_indenter();
} /* end _c_open() */
void
_c_close() { /* on '}' */
BufInsertChar( '}' );
if ( auto_indent )
c_indenter();
#if 0
if ( Matchdelim )
find_delimiter();
#endif
} /* end _c_close() */
void
_c_forward_cexp() { /* on ALT+CTRL+F */
long start;
long last;
start = BufQueryOffset();
if ( !traverse_cexp(1) ) {
PosToOffset(start);
abort();
}
last = BufQueryFilesize();
while (strchr (",:;\\", BufQueryChar()) &&
BufQueryOffset() < last )
PosNextChar();
}
void
_c_backward_cexp() { /* on Alt+Ctrl+B */
long start;
start = BufQueryOffset();
while ( BufQueryOffset() > 0 &&
strchr(",:;\\ \t\n\f", BufQueryPrevChar()) )
PosPrevChar();
if ( !traverse_cexp(-1) ) {
PosToOffset(start);
abort();
}
} /* end _c_backward_cexp() */
void
_c_start_of_func() { /* on Alt+Ctrl+A */
FindStartOfDef();
WinScrollTop();
}
void
_c_end_of_func() { /* on Alt+Ctrl+E */
long orig;
orig = BufQueryOffset();
traverse_cexp(1);
PosNextChar();
if ( FindStartOfDef() ) {
while ( traverse_cexp(1) && BufQueryPrevChar() != '}' )
;
if ( BufQueryPrevChar() != '}' )
PosToOffset(orig);
else if ( BufQueryChar() == ';' )
PosNextChar();
}
} /* end _c_end_of_func() */
void
_c_up_level() { /* on ALT+CTRL+U */
PSTATE state;
long orig;
orig = BufQueryOffset();
FindStartOfDef();
while ( BufQueryOffset() <= orig ) {
parse_partial_cexp(BufQueryOffset(), orig + 1, &state);
}
if ( state.containing_cexp >= 0 )
PosToOffset(state.containing_cexp);
else {
PosToOffset(orig);
PopupMsg("No containing C expression", "up_level");
return;
/* should do abort() */
}
} /* end _c_up_level() */
/*
* Note: this function can take quite a bit of time
* to do it's job.
*/
void
_c_indent_function() { /* on Alt+Ctrl+Q */
PSTATE ps;
long ret;
long stop;
MarkPushPos();
FindStartOfDef(); /* beginning_of_defun(); BROKEN */
ps.beginning_of_defun = BufQueryOffset();
_c_end_of_func();
stop = BufQueryOffset();
PosToOffset(ps.beginning_of_defun);
while ( BufQueryOffset() < stop ) {
SysSetHourglass(1);
c_indenter_1(&ps);
MovNextLine();
}
MarkPopPos();
SysSetHourglass(0);
} /* end _c_indent_function() */
/* _c_smart_setup()
* This function is called when this macro file is loaded.
* You can modify this function to change how
* certain constructs are indented relative to indent_size,
* if these values do not give the indent style you want.
* If you want to change the values that are arguments to this
* function, change the call in _init.
*/
void
_c_smart_setup(int indent_size,
int brace_offset,
int imag_offset,
int shortcut )
{
c_indenter_shortcut = shortcut;
c_indent_level = indent_size;
c_brace_imaginary_offset = imag_offset;
c_brace_offset = brace_offset;
c_argdecl_indent = indent_size+1;
c_label_offset = -indent_size+1;
c_case_offset = -indent_size;
c_continued_statement_offset = indent_size;
c_preproc_col_1 = 0;
c_preproc_offset = -1;
c_tab_always_indent = 0;
} /* end _c_smart_setup() */
void
_init(void) {
#if 1
/*
* If you do not want braces indented to the level of the code
* they enclose, enable this code.
*/
_c_smart_setup(IndentSize, /* c_indent_level */
0, /* c_brace_offset */
0, /* c_brace_imaginary_offset */
0); /* use shortcut indenter (faster) */
#else
/*
* If you want braces indented to the level of the code
* they enclose, enable this code.
*/
_c_smart_setup(IndentSize, /* c_indent_level */
IndentSize, /* c_brace_offset */
IndentSize, /* c_brace_imaginary_offset */
0); /* use shortcut indenter (faster) */
#endif
} /* end _init() */
/*
** End module: c_smart.rm
*/