home *** CD-ROM | disk | FTP | other *** search
-
- /************************************************************************
- * *
- * C-Extended Mode ver 1.0 *
- * *
- * ADDITIONS AND ENHANCEMENTS--03/24/87 *
- * *
- * Author: John Kubiatowicz *
- * KUBITRONICS information systems *
- * (C) March 24, 1987 *
- * kubitron%athena.mit.edu *
- * *
- * Much of this material is (c) 1987, by John Kubiatowicz. *
- * The remainder is (c) 1985, Lagaru Software Ltd. *
- * *
- * This material may be used and distributed freely, baring *
- * use in products for sale or lease. The author makes no *
- * claims of perfection, and will not be responsible for *
- * damages incurred during the use of this product. *
- * *
- ************************************************************************/
- /************************************************************************
- * "Epsilon", "EEL" and "Lugaru" are trademarks of Lugaru Software, Ltd. *
- * *
- * Copyright (C) 1985 Lugaru Software Ltd. All rights reserved. *
- * *
- * Limited permission is hereby granted to reproduce and modify this *
- * copyrighted material provided that the resulting code is used only in *
- * conjunction with Lugaru products and that this notice is retained in *
- * any such reproduction or modification. *
- ************************************************************************/
-
- #include "eel.h"
-
- /* Original Lagaru Comments
-
- Automatic indentation for C and EEL code. Several indenting styles
- are supported, selected by the following variables.
-
- * * * *
-
- The position of the closing brace is controlled by the Closeback
- variable, 1 by default:
-
- Closeback = 0; Closeback = 1;
- if (foo) { if (foo) {
- bar(); bar();
- baz(); baz();
- } }
-
- By placing the opening brace on the following line, these styles
- may be also used:
-
- Closeback = 0; Closeback = 1;
- if (foo) if (foo)
- { {
- bar(); bar();
- baz(); baz();
- } }
-
-
- * * * *
-
- The Topindent variable controls whether top-level statements in a function
- are indented. It is 1 by default.
-
- Topindent = 0; Topindent = 1;
- foo() foo()
- { {
- if (bar) if (bar)
- baz(); baz();
- } }
-
- * * * *
-
- The Matchdelim variable controls whether typing ), ], or } displays the
- corresponding (, [, or { when the former is typed. It uses the
- show-matching-delimiter command.
- */
-
-
- /* Comments on C-Extended Mode
- John Kubiatowicz
-
- In this file, I have implemented 'switch' statement indentation.
- This feature is selected by setting the flag 'Switch_form', which
- appears below. The following switch format is supported:
-
- Closeback = 0; Closeback = 1;
-
- switch(foo) { switch(foo) {
- case 0: case 0:
- statement1; statement1;
- statement2; statement2;
- case 1: case 1;
- case 2: case 2;
- statement3; statement3;
- statement4; statement4;
- } }
-
- In addition, I have altered the c_compute_indent() so that if
- the point is inside of a comment sequence, indentation
- will match that of the previous, non-blank line. If the
- initial character on the line is a '}' and Match_delim is set,
- then tabbing is adjusted as in c_close, which includes showing
- the matching delimiter.
-
- I have altered the c_close routine so that closing braces are aligned
- "properly" with opening braces. Further, the routine
- 'show_matching_delimiter' has been replaced with 'c_look_for_matching'.
- During an attempt to match a delimiter from outside of quotes,
- delimiters within single quotes are ignored, as are delimiters within
- double quotes. During an attempt to match a delimiter from inside
- quotes, only delimiters within the same string are considered.
-
- Ten extended marks have been added, bound to the Alt-F[1-10]
- and the Ctrl-F[1-10] characters. CTRL-F[1-10] sets a mark at the
- current location. Ctrl-F[1-10] goes to the preset location.
- During such a move, the last position is saved and can be recalled
- with Alt-ESC. Bindings for these functions can be changed, although
- the ten set keys must have consecutive bindings, as must the ten
- recall keys. See the macros GET_MARK_BASE, SET_MARK_BASE,
- and C_RETRIEVE_MARK.
-
- For those of you who don't place frequent comments in your code,
- a slight speed improvement in delimiter matching can be accomplished
- by setting the flag 'Comment_check_enable' to zero. Comments will no
- longer be recognized.
-
- The tab size automatically defaults to 4 for all .c,.h,.e, and .y
- files.
-
- Finally, I have included the "reload file" function, which reloads
- the current file off the disk, discarding the current buffer. This
- is bound to 'C-X C-R'. Note that this command is modelled afted a
- similar command by Bob Knight.
-
- I have other ideas for this code, like a new 'c_compute_indent',
- which I would like to implement at some later time. Perhaps I
- will work on version 1.5.....
-
- Good Luck,
- KUBI
- kubitron%athena.mit.edu
-
- P.S. It is quite possible that this code still has some bugs in it.
- I would appreciate any comments or suggestions.
-
- P.P.S. I have removed all tabs from this file. The file was originally
- composed with a tab size of 4 (which is the default in c_mode). You
- should probably decide on a tab size, then perform a 'tabify-region'
- to save space.
-
- *****************************************************************/
-
- /* Base for get_extended_mark and set_extended_mark keys. */
-
- #define GET_MARK_BASE FALT(1)
- #define SET_MARK_BASE FCTRL(1)
- #define C_RETRIEVE_MARK ALT(ESC)
-
- /* Special global format flags */
-
- int Switch_form = 1; /* Switch statement flag, set to on */
- int Comment_check_enable = 1;
-
- /* C-EXT mode globals */
-
- int Match_position = -1; /* Indicates position of last matched delimiter.*/
- int quote_value = 0; /* Indicates value of last quote match */
- int quote_position = -1; /* Indicates position of last quote */
- int comment_value = 0; /* Indicates value of last comment match */
- int comment_position = -1; /* Indicates position of beginning of
- last comment match. */
- /* C-EXT mode buffer vars */
-
- buffer int *extended_mark[10] = {0,0,0,0,0,0,0,0,0,0};
- /* Ten extended marks (spots start at NULL) */
- buffer int *prev_ext_mark = 0; /* Previous extended mark */
-
- keytable c_tab; /* key table for c mode */
-
- /* by default, the indenting levels are one tab stop apart */
- #define INCR tab_size
-
- /* define an RE matching C comments or whitespace */
- #define C_SKIP "((/%*([^*]|[^/]%*)*%*/)|[ \t\n])*"
-
- c_indenter() /* replace surrounding indentation with new */
- {
- to_column(c_compute_indent());
- }
-
- command c_look_for_matching();
- command c_set_extended_mark();
- command c_get_extended_mark();
- command c_retrieve_mark();
-
- #define SMB(c) (SET_MARK_BASE + (c))
- #define GMB(c) (GET_MARK_BASE + (c))
-
- command c_mode()
- {
- mode_keys = c_tab; /* use these keys */
- if (Matchdelim)
- c_tab[')'] = c_tab[']'] = (short) c_look_for_matching;
- c_tab[SMB(0)] = c_tab[SMB(1)] = (short) c_set_extended_mark;
- c_tab[SMB(2)] = c_tab[SMB(3)] = (short) c_set_extended_mark;
- c_tab[SMB(4)] = c_tab[SMB(5)] = (short) c_set_extended_mark;
- c_tab[SMB(6)] = c_tab[SMB(7)] = (short) c_set_extended_mark;
- c_tab[SMB(8)] = c_tab[SMB(9)] = (short) c_set_extended_mark;
- c_tab[GMB(0)] = c_tab[GMB(1)] = (short) c_get_extended_mark;
- c_tab[GMB(2)] = c_tab[GMB(3)] = (short) c_get_extended_mark;
- c_tab[GMB(4)] = c_tab[GMB(5)] = (short) c_get_extended_mark;
- c_tab[GMB(6)] = c_tab[GMB(7)] = (short) c_get_extended_mark;
- c_tab[GMB(8)] = c_tab[GMB(9)] = (short) c_get_extended_mark;
- c_tab[C_RETRIEVE_MARK] = (short) c_retrieve_mark;
- major_mode = strsave("C-Extended");
- make_mode();
- indenter = c_indenter;
- auto_indent = 1;
- tab_size = 4;
- }
-
- /* make this the default mode for .c, .h, .e, and .y files */
- suffix_c() { c_mode(); }
- suffix_h() { c_mode(); }
- suffix_e() { c_mode(); }
- suffix_y() { c_mode(); }
-
- /* Look at the last two lines and return the correct indentation
- for the current line, assuming C source (or similar). At start,
- we must be at the end of a line's indentation.
- */
-
- c_compute_indent()
- {
- int orig = point, prev_keyword;
- int prev_end = ';', prev2_end = ';'; /* last chr on 2 prev lines */
- int ind = 0; /* indentation to use */
- int first_char; /* cur line's first char */
-
- /* If within a comment, match indentation to previous line */
-
- if (in_comment_check()) {
- re_search(-1, "[ \t\n]*");
- to_indentation();
- ind = current_column(); /* Current column should match previous */
- point = orig;
- return ind;
- }
- first_char = curchar();
- if (first_char == '}' && Matchdelim) {
- point++;
- c_close_compute();
- point--;
- return current_column();
- }
- to_begin_line();
- re_search(-1, C_SKIP); /* skip whitespace and comments */
- if (point > 0) {
- prev_end = character(--point); /* get last relevant char */
- to_indentation();
- ind = current_column(); /* and indentation of its line */
- prev_keyword = point; /* save its position too */
-
- re_search(-1, C_SKIP); /* skip whitespace and comments */
- if (point > 0) /* and get last interesting char on */
- prev2_end = character(point-1); /* line before that */
-
- point = prev_keyword; /* prepare for checking prev line */
- }
-
- if (!ind) { /* special case if no previous indentation */
- if (top_indent(prev_end, prev2_end))
- ind += INCR;
- } else if (prev_end == ';') {
- if (!index(";{}", prev2_end) && (!Switch_form || prev2_end != ':'))
- ind -= INCR;
- } else if (prev_end == '}') {
- if (!Closeback)
- ind -= INCR;
- } else if (prev_end != '{' || index(";{}", prev2_end)
- || c_statement_start() || just_open())
- ind += INCR;
- /* otherwise, last is probably continuation of a stmt or func decl */
- if (Closeback && first_char == '}')
- ind -= INCR;
- point = orig;
- return ind;
- }
-
- /* Look at the last two lines and guess at the correct indentation,
- assuming C source (or similar). If we're not in this line's
- indentation, though, or our new indentation matches the old,
- just insert a tab.
- */
-
- c_indent() on c_tab['\t']
- {
- int orig = point;
- int orig_column = current_column();
-
- to_indentation();
- if (orig_column > current_column()) { /* if not in indentation */
- point = orig;
- insert('\t'); /* insert a tab */
- } else if (prev_cmd == C_INDENT) /* repeated, make bigger */
- to_column(orig_column + INCR);
- else
- to_column(c_compute_indent());
- this_cmd = C_INDENT;
- }
-
- /*
- Tell if more indent is required.
- Assumes last line had no indent and point is at its start.
- The hard case is distinguishing between these when Topindent is zero:
-
- foo(); foo();
- } }
- func(bar) if (bar)
-
- When point is just after these examples, we must examine the word
- at "func" to distinguish the left example (the end of a function and
- beginning of the next, requiring no indentation) from the right (the
- end of a block and start of a conditional, requiring indentation).
- */
-
- top_indent(prev_end, prev2_end)
- {
- int ret;
-
- if (Topindent)
- ret = prev_end == '{';
- else if (ret = !index(";}", prev_end) && index(";{}", prev2_end))
- if (prev_end != '{' && !c_statement_start())
- ret = 0;
- return ret;
- }
-
- /*
- Tell if the last line begins a statement (rather than a function
- declaration or the continuation of a previous statement).
- Assumes point is at the start of the last line.
- */
- c_statement_start()
- {
- return parse_string(1, "[{} \t]*(if|else|while|do|for)[^a-zA-Z0-9]",
- (char *) 0);
- }
-
- just_open()
- {
- return Closeback && parse_string(1, "{[ \t]*\n", (char *) 0);
- }
-
- /* fix indentation if necessary when { is typed */
- c_open() on c_tab['{']
- {
- int orig = point;
- int orig_column; /* point's column */
- int ind;
-
- if (Closeback) {
- orig_column = current_column();
- to_indentation();
- ind = current_column(); /* get line's indentation */
- point = orig;
- if (orig_column <= ind) /* if in indentation */
- to_column(ind - INCR);
- }
- normal_character();
- }
-
- /* fix indentation if necessary when } is typed */
- c_close() on c_tab['}']
- {
- if (point && check_single()) {
- normal_character();
- return;
- } else {
- normal_character();
- c_close_compute();
- }
- }
-
- c_close_compute()
- {
- int orig;
- int orig_column,p_orig_column; /* point's column */
- int quoflg,comflg,abspos;
- int temp,ind;
- char first,second;
- char *left = "[{(", *right = "]})";
-
- orig = point;
- orig_column = current_column() - 1;
- to_indentation();
- ind = current_column();
- if (ind < orig_column) {
- point = orig;
- return;
- }
- if (Matchdelim) {
- point = orig;
- match_character();
- abspos = -1;
- if (comflg = in_comment_check())
- abspos = comment_position;
- if (Match_position >= 0) {
- point = p_orig_column = Match_position;
- while (re_search(-1,"[]})\n]") && curchar() != '\n'
- && point > abspos)
- if (comflg == in_comment()) {
- point++;
- second = character(point - 1);
- first = left[index(right,second)-right];
- if (!search_close(first,second)) {
- say("Unmatched delimiter -- %c",second);
- break;
- } else
- point = Match_position;
- }
- if (curchar() != '\n')
- point = p_orig_column;
- else
- point++;
- to_indentation();
- ind = current_column();
- point = orig - 1;
- to_column(ind);
- point++;
- return;
- }
- } else if (Closeback) {
- if (orig_column <= ind) /* if in indentation */
- to_column(ind - INCR);
- point = orig;
- }
- }
-
- /* This routine implements indentation of switch statements.
- When the ':' character is entered, it checks first to make sure
- that the colon is not imbedded in some comment. Then, it makes
- sure that it is not imbedded in a string. Finally, if the previous
- line does not end in '{', the whole line is back indented on tab space.
-
- Author: John Kubiatowicz
- kubitron@athena.mit.edu
- */
-
- switch_back() on c_tab[':']
- {
- int ind;
- int orig;
-
- normal_character();
- orig = point;
- point--;
- if (!Switch_form || in_comment_check() ||
- in_quote_check() || char_exists('?') || check_single()) {
- point++;
- return; /* Do nothing special */
- }
- to_indentation();
- ind = current_column();
- re_search(-1,C_SKIP);
- if (point > 0 && index(":};",character(--point)))
- ind -= INCR;
- point = orig;
- to_indentation();
- to_column(ind);
- to_end_line();
- search(-1,":");
- point++;
- }
-
-
- /* This checks to see if the point is within a comment. */
-
- #define COM_SEARCH "(/%*)|(%*/)"
-
- int in_comment_check()
- {
- comment_position = -1;
- return in_comment();
- }
-
- int in_comment()
- {
- int orig = point;
-
- if (!Comment_check_enable)
- return 0;
- if (comment_position != -1 && point > comment_position)
- return comment_value;
- quote_position = -1;
- while (re_search(-1,COM_SEARCH) && curchar() == '/' && in_quotes());
- comment_position = point; /* Point at which answer was derived */
- comment_value = parse_string(1,"/*",(char *)0);
- point = orig;
- return comment_value;
- }
-
- /* This routine checks to see if the current character is imbedded
- within a set of quotes. It returns 'TRUE' if the character follows
- an odd number of double quotes on the current line. */
-
- int in_quote_check()
- {
- quote_position = -1; /* Force full scan */
- return in_quotes();
- }
-
- /* This routine checks the variable 'quote_position.' If it is not
- equal to -1, and point is greater than it, it returns the value
- 'quote_value.' Otherwise, it scans backward from the current
- point to see if there are an odd number of points between the
- current point and the beginning of the line.
- */
-
- int in_quotes()
- {
- int orig = point;
-
- if (quote_position != -1 && point > quote_position)
- return quote_value; /* Previous result still valid */
- quote_position = -1;
- quote_value = 0;
- while ((--point) >= 0 && curchar() != '\n')
- if (curchar() == '"' &&
- !(point > 0 && ((character(point - 1) == '\'' &&
- character(point + 1) == '\'') ||
- escseq(point)))) {
- quote_value = !quote_value; /* Toggle the quote flag */
- if (quote_position == -1)
- quote_position = point;
- }
- if (quote_position == -1)
- quote_position = point;
- point = orig;
- return (quote_value);
- }
-
- /* Check to see if the character at 'check_point' is part of
- an escape sequence.
- */
-
- escseq(check_point)
- char check_point;
- {
- int returnval = 0;
-
- while (--check_point > 0 && character(check_point) == '\\')
- returnval = !returnval;
- return returnval;
- }
-
- /* Check to see if the character at 'point' could potentially
- be enclosed in single quotes or part of an escape sequence ('\').
- Note that an ending single quote is not required. This routine
- is used as characters are being typed in, so presumably the final
- quote doesn't exist yet.
- */
-
- int check_single()
- {
- int backcount = 0;
- int check_point = point;
-
- if (!check_point)
- return 0;
- if (character(check_point - 1) == '\'' && !in_quotes()) {
- if (check_point == 1)
- return 1; /* Point of interest in quotes */
- if (character(check_point - 2) == '\\' &&
- (check_point == 2 || character(check_point - 3) != '\\'))
- return 0; /* Single quote is part of escape sequence */
- return (check_point == 2 ||
- (character(check_point - 3) != '\'' &&
- (check_point == 3 || character(check_point - 3) != '\\'
- || character(check_point - 4) != '\'')));
- } else if (character(check_point - 1) == '\\')
- return escseq(check_point);
- else
- return 0;
- }
-
- /* Check to see if the specified character exists earlier in the line. */
-
- int char_exists(desired)
- char desired;
- {
- int orig = point;
-
- while ((--point) >= 0 && curchar() != '\n')
- if (curchar() == desired) {
- point = orig;
- return 1; /* char exists */
- }
- point = orig;
- return 0; /* Char does not exist */
- }
-
- /* Search back for matching closing delimiter */
-
- int search_close(lchar,rchar)
- char lchar,rchar;
- {
- char searchstr[20];
- jmp_buf *old_level = top_level, this_level;
- int orig = point;
- int extradel = 0,quoflg,comflg,abspos;
-
- Match_position = -1; /* No position to match */
- top_level = &this_level;
- if (setjmp(top_level)) {
- top_level = old_level;
- point = orig;
- return 0;
- }
- if (rchar == ']')
- strcpy(searchstr,"[][]");
- else
- sprintf(searchstr,"[%c%c]",lchar,rchar);
- abspos = -1;
- if (comflg = in_comment_check())
- abspos = comment_position;
- if (quoflg = in_quote_check() && quote_position > abspos)
- abspos = quote_position;
- while (re_search(-1,searchstr) && point > abspos)
- if (comflg == in_comment() && quoflg == in_quotes()
- && !check_single())
- if (curchar() == rchar)
- extradel++;
- else {
- extradel--;
- if (!extradel)
- break;
- }
- top_level = old_level;
- if (curchar() == lchar && !extradel) {
- Match_position = point;
- point = orig;
- return 1;
- } else {
- point = orig;
- return 0;
- }
- }
-
- #define LEFTD "[({"
- #define RIGHTD "])}"
-
- /* This routine attempts to search for a matching delimiter,
- then briefly shows it on the screen. */
-
- command c_look_for_matching()
- {
- normal_character();
- point--;
- if (!check_single()) {
- point++;
- match_character();
- } else
- point++;
- }
-
- match_character()
- {
- char first,second;
- char *left = LEFTD, *right = RIGHTD,*foo;
-
- say("");
- second = character(point - 1);
- if (foo = index(right,second)) {
- first = left[foo - right];
- if (search_close(first,second))
- show_match_line();
- else
- say("Unmatched delimiter");
- }
- }
-
- /* Briefly display the window at Match_position, then return to point */
-
- show_match_line()
- {
- int orig = point;
- int time, oldstart = window_start;
-
- point = Match_position;
- maybe_refresh();
- time = (window_start == oldstart) ? 5 : 10;
- pause(time);
- window_start = oldstart;
- point = orig;
- build_first = 1;
- }
-
- /* The following commands implement the extended mark functions.
- Their names are basically self explanatory.
- */
-
- command c_set_extended_mark()
- {
- int fnum = key - SET_MARK_BASE;
-
- if (fnum < 0 || fnum > 9)
- error("Bad key-- c_set_extended_mark");
- if (extended_mark[fnum] == NULL)
- extended_mark[fnum] = alloc_spot();
- *(extended_mark[fnum]) = point; /* Get sticky mark */
- say("Extended mark #%d set.",fnum + 1);
- }
-
- command c_get_extended_mark()
- {
- int fnum = key - GET_MARK_BASE;
-
- if (fnum < 0 || fnum > 9)
- error("Bad key-- c_get_extended_mark");
- if (extended_mark[fnum] == NULL)
- error("Extended mark #%d not set.",fnum+1);
- else {
- if (prev_ext_mark == NULL)
- *(prev_ext_mark = alloc_spot()) = 0;
- *prev_ext_mark = point;
- point = *(extended_mark[fnum]);
- build_first = 1;
- say("Previous position saved.");
- }
- }
-
- command c_retrieve_mark()
- {
- int temp = point;
-
- if (prev_ext_mark == NULL)
- error("No previous position.");
- else {
- point = *prev_ext_mark;
- *prev_ext_mark = temp;
- build_first = 1;
- say("Previous position saved.");
- }
- }
-
- /* Restore the current buffer from its associated disk file.
- Note that two types of buffers cannot be reverted, process
- buffers, and directory buffers. Note that the extended marks
- are returned to their uninitialized states and the pointer
- is placed at the beginning of the buffer.
- */
-
- command reload_file() on cx_tab[CTRL('R')]
- {
- char answer[80];
- int count;
-
- if(!strcmp(bufname,"process"))
- error("Process buffers cannot be reloaded!");
- else if (!strcmp(bufname,"dired"))
- error("Directory buffers cannot be reloaded!");
- else {
- get_string(answer, "Restore file from disk? [n]");
- if (toupper(*answer) == 'Y') {
- read_file(filename);
- for (count = 0; count < 10; count++)
- extended_mark[count] = NULL;
- prev_ext_mark = NULL;
- }
- }
- }
-