home *** CD-ROM | disk | FTP | other *** search
-
- /********** Documentation to be inserted in file "edoc" ***********
-
- for-mode Do automatic indentation of Fortran code.
- This command puts the current buffer in Fortran mode and
- is invoked automatically for a file with extension .for .
- <Enter> runs fortran-indent-line() to insert all necessary tabs
- or spaces in the line just typed. tab-size is set to 3. When
- the buffer is written, you are asked if it should be untabified.
- Matching ('s are shown when typed. fill-mode is set on, and
- fill-column is set to 72, so excess space-delimited entities
- move to the next line. The mode line says Fortran Fill.
-
- fortran-indent-line Indents previous line of Fortran code.
- Determines where whitespace should be adjusted to put statement
- numbers in the required columns and to show the block structure of the
- code. Does nothing to full-line or on-line comments. If the state-
- ment before the one just typed was incomplete, a continuation is made.
- if-then-else-elseif-endif blocks are indented to show code structure.
- do-continue blocks are also, if there is a continue for each do.
- A do-loop without a continue will cause improper indentation of the
- following statement, unless the loop is only two lines long.
-
- fortran-indent-region Indent a region of Fortran code.
- Indents all lines that terminate between point and mark using
- fortran-indent-line().
-
- *********** End of documentation **********/
-
- /* ----- Gary R. Smith (smith#gary@b.mfenet@nmfecc.arpa) */
-
- /****************** Beginning of file "fortran.e" ******************/
-
- #include "eel.h"
-
- /*
- Automatic indentation for Fortran code.
- Written by Gary R. Smith in Sept. 1986.
-
- Indentation occurs after each Fortran statement is typed, or when
- the command fortran-indent-region is given. Entering of Fortran code
- is speeded up, because the programmer can let Epsilon insert the spaces
- needed to produce clearly indented code.
-
- Indentation involves scanning a statement and the lines preceding it
- to determine where whitespace should be adjusted to put statement
- numbers in the required columns and to show the block structure of the
- code. The following steps occur:
- 1. If the line begins with a comment character, nothing is done.
- 2. If the previous statement was incomplete (ends in one of "=+-*,(" ),
- the present line is indented as a continuation and the remaining steps
- are omitted.
- 3. If the line begins with a number (string of digits), indentation
- and right justification occurs for a statement number.
- 4. For lines that are neither comments nor continuations, the presence
- of if-then-else-elseif-endif or do-continue blocks is detected by
- examination of the first strings (after statement numbers, if any) in both
- the previous and present statements. An indentation level found from the
- previous statement is used for the present line except in these cases:
- (a) Presence of "if-then", "else", "elseif-then", or "do" in the previous
- statement, increments (by tab_size) the indentation level for the
- present line.
- (b) Presence of "else", "elseif-then", "endif", or "continue" on the
- present line, decrements the indentation level for the present line.
- (Note that all "continue"s are treated as if they end do-continue
- blocks and may be indented too little if they do not.)
- 5. If the previous statement begins with a statement number, it ends a
- two-line do-loop if the statement before it contains "do" followed by
- that statement number. In this case, the indentation level for the
- present line is decremented. Only two-line do-loops are detected;
- automatic indentation of longer loops occurs only if they end with
- "continue".
-
- The previous statement is found by checking the previous line and its
- predecessors, skipping blank and empty lines and (both full-line and
- on-line) comments, for a line that is not a continuation.
-
- Fill mode is enabled automatically and the fill column set to 73.
- Therefore, a space entered past column 72 will cause space-delimited
- entities to move to the next line. If the last character remaining
- on the long line indicates an incomplete statement, the next line is
- made a continuation automatically.
-
- The command fortran-indent-region indents all lines that terminate
- between point and mark. The steps described above are performed on all
- of the lines. A future improvement would be to minimize time-consuming
- searches by remembering the indentation level from one line to the next.
- */
-
- buffer int fortran_mode = 0; /* Are we in fortran mode? */
-
- keytable fortran_tab; /* Key table for fortran mode */
-
- #define FORTRAN_SKIP "(([ \t]*\n)|([ \t]*[!#].*\n))+"
- #define FORTRAN_COMMENT "cC!#*"
- #define FORTRAN_OP "=+-*,("
- #define FORTRAN_CONT '.'
- #define MAXWORD 80
- #define NBEGWORDS 4
- #define NENDWORDS 5
-
- char *beg_word[NBEGWORDS];
- char *end_word[NENDWORDS];
-
- command fortran_indent_region() on fortran_tab[ALT(CTRL('\\'))]
- {
- int temp, *begin;
-
- if (point > mark) { /* If point follows mark, swap */
- temp = mark;
- mark = point;
- point = temp;
- }
- if (nl_reverse()) /* To begin of line, even if it's first */
- point++;
- begin = alloc_spot();
- *begin = point;
- while (point < mark) { /* Indent each line in region */
- if (!nl_forward()) break;
- fortran_indent_line();
- }
- mark = *begin; /* Region is just-indented set of lines */
- free_spot(begin);
- iter = 0;
- return;
- }
-
- fortran_indent_line()
- {
- int *orig;
- int this_beg, prev_end, prev_indent, prev_cont, prev_stmt;
- int stmt_no_len, this_col6;
- char key_word[MAXWORD];
- int i, cmp_result;
- char stmt_no[6], stmt_np[6];
-
- if (point == 0) return; /* Handle first line if indenting region */
-
- this_beg = prev_screen_line(1);
- if (index(FORTRAN_COMMENT, character(this_beg))) return;
- /* Comment found */
- orig = alloc_spot();
- *orig = point;
- re_search(-1, "[ \t]*\n");
- if (point == this_beg) goto exit;
- /* Blank or empty line found */
-
- point = this_beg;
- prev_end = find_prev_end();
- if (prev_end > 0 && stmt_incomp(prev_end)) {
- /* Test if stmt is incomplete */
- prev_indent = find_indent(prev_end);
- prev_cont = check_cont(prev_end); /* 1 if was cont. */
- if (check_cont(this_beg)) move_to_column(6);
- else make_cont(); /* Make this line a continuation
- by replacing cols. 1-6 */
- to_column(6 + prev_indent + !prev_cont * tab_size);
- /* Replace indentation with same as previous line
- + tab_size if previous stmt not continuation */
- goto exit;
- }
-
- point = this_beg;
- if (stmt_no_len = check_no(stmt_no)) {
- to_column(5 - stmt_no_len); /* Right justify stmt no */
- point = point + stmt_no_len; /* Point after stmt no */
- }
- to_column(6); /* Remove whitespace and
- put space in col. 6 ( or 1-6 if no stmt no) */
- this_col6 = point;
-
- if (prev_end > 0) { /* Test that previous stmt found */
- point = prev_end;
- prev_indent = find_prev_stmt(stmt_no); /* Find indentation */
- if (find_word(1, key_word)) {
- /* Word that began previous stmt */
- for(i=0; i < NBEGWORDS; ++i) { /* Is it on list? */
- cmp_result = strcmp(key_word, beg_word[i]);
- if (cmp_result >= 0) break;
- }
- if (cmp_result == 0 && i == 0) {
- /* Is if followed by then? */
- point = prev_end;
- find_word(-1, key_word);
- if (strcmp(key_word, "then"))
- cmp_result = 1; /* No */
- }
- if (!cmp_result) {
- prev_indent += tab_size;
- goto fin;
- }
- else {
- /* Not beginning of block, check for two-line do-loop */
- if (*stmt_no == '\0') goto this;
- to_begin_line();
- if ((prev_end = find_prev_end()) < 0)
- goto this;
- find_prev_stmt(stmt_np);
- if (find_word(1, key_word)) {
- if (strcmp(key_word, "do"))
- goto this;
- if (check_no(stmt_np) &&
- !strcmp(stmt_no, stmt_np)) {
- prev_indent -= tab_size;
- goto fin;
- }
- }
- else {
- key_word_error();
- goto exit;
- }
- }
- }
- else { /* Apparent error in user type-in */
- key_word_error();
- goto exit;
- }
- }
- else goto exit; /* Nothing to be done if no previous stmt */
-
- this: point = this_col6;
- if (find_word(1, key_word)) { /* Word that begins this stmt */
- for(i=0; i < NENDWORDS; ++i) { /* Is it on list? */
- cmp_result = strcmp(key_word, end_word[i]);
- if (cmp_result >= 0) break;
- }
- if (!cmp_result) prev_indent -= tab_size;
- }
- else { /* Apparent error in user type-in */
- key_word_error();
- goto exit;
- }
-
- fin: point = this_col6;
- if (prev_indent > 0) insert_to_column(6, 6 + prev_indent);
- exit: point = *orig;
- free_spot(orig);
- return;
- }
-
- find_prev_end() /* Move point to last character of previous stmt,
- passing comments and whitespace. Return
- point if there was a previous statement,
- -1 if not */
- {
- fortran_skip_lines();
- re_search(-1, FORTRAN_SKIP); /* Skip to end of stmt */
- if (point > 0) return point; /* Last character found */
- else return -1; /* No previous stmt */
- }
-
- fortran_skip_lines() /* Assumes point is at beginning of a line and
- skips back over blank, empty, or comment lines */
- {
- re_search(-1, "[ \t\n]*"); /* Skip to a line with darkspace */
- if (point == 0) return; /* Done if at beginning of file */
- while (to_begin_line(), index(FORTRAN_COMMENT, curchar())) {
- /* Comment line found */
- re_search(-1, "[ \t\n]*");/* Skip to a line with darkspace */
- if (point == 0) return;
- }
- nl_forward(); /* After \n of searchable line */
- return;
- }
-
- stmt_incomp(loc) /* Check if character before loc indicates an
- incomplete stmt. Return nonzero, if incomplete */
- int loc;
- {
- if (loc > 0)
- return (index(FORTRAN_OP, character(--loc)) != 0);
- else return 0;
- }
-
- find_indent(loc) /* Return number of columns of indentation in
- the stmt surrounding loc. Point is left
- after the end of the indentation */
- int loc;
- {
- point = loc;
- move_to_column(6); /* Position point after continuation col. */
- re_search(1, "[ \t]*"); /* Move point to first darkspace character */
- return current_column() - 6;
- }
-
- check_cont(loc) /* Return 1 if the line containing loc has whitespace
- in cols. 1-5, then a darkspace character in col. 6.
- Otherwise return 0. Leave point after col. 6. */
- int loc;
- {
- point = loc;
- to_begin_line();
- re_search(1, "[ \t]*"); /* Move point to first darkspace character */
- if (current_column() == 5) {
- ++point;
- return 1;
- }
- else {
- move_to_column(6);
- return 0;
- }
- }
-
- make_cont() /* Make line containing point a continuation by
- replacing initial whitespace with 5 columns of
- whitespace followed by the continuation char.
- First darkspace should be the first char
- of Fortran on this line */
- {
- to_begin_line();
- to_column(5);
- insert(FORTRAN_CONT);
- return; /* Point is left after continuation char. */
- }
-
- check_no(stmt_no) /* Check if, following point, there is (optional)
- whitespace, then a string of 5 or fewer digits
- (a Fortran statement number). If so, return
- pointer to it in stmt_no */
- char *stmt_no;
- {
- int orig = point;
- int begin;
- int length;
-
- re_search(1, "[ \t]*"); /* Skip whitespace, if any */
- begin = point;
- while(isdigit(curchar())) point++;
- length = point - begin; /* Length of string of digits */
- if (length > 0 && length <= 5) { /* String was found */
- point = begin;
- parse_string(1, "[0-9]*", stmt_no);
- }
- else { /* Not found */
- length = 0;
- *stmt_no = '\0';
- }
-
- point = orig;
- return length;
- }
-
- find_prev_stmt(stmt_no) /* Find start of previous statement, when point is in
- it, returning length of statement's indentation
- or -1 if there was no previous statement.
- Also, return pointer stmt_no to a statement
- number, if present */
- char *stmt_no;
- {
- while (point > 0 && check_cont(point)) {
- to_begin_line();
- fortran_skip_lines();
- /* Skip back over empty and blank lines & comments */
- --point;
- }
- if (point >= 0) {
- to_begin_line();
- check_no(stmt_no);
- return find_indent(point);
- }
- else return -1;
- }
-
- find_word(dir, word) /* Searching in direction dir,
- find string of alphabetic characters beginning
- at point, and copy the string to word,
- changing to lower case, and return
- the length of the string as function value */
- int dir;
- char *word;
- {
- int length;
-
- length = parse_string(dir, "[a-zA-Z]*", word);
- while ((*word++ = tolower(*word)) != '\0');
- return length;
- }
-
- #define MAXERRSTRING 10
- key_word_error() /* Report apparent error in user type-in */
- {
- char err_string[MAXERRSTRING+1];
-
- grab(point, point+MAXERRSTRING, err_string);
- say("%s%s", "Expected Fortran keyword but found: ",
- err_string);
- maybe_ding();
- return;
- }
-
- command for_mode()
- {
- char resp[80];
-
- mode_keys = fortran_tab; /* Use these keys */
- fortran_tab[')'] = (short) show_matching_delimiter;
- indenter = fortran_indent_line;
- auto_indent = 1;
- fill_mode = 1;
- margin_right = 73;
- fortran_mode = 1;
- major_mode = strsave("Fortran");
- make_mode();
-
- beg_word[0] = strsave("if");
- beg_word[1] = strsave("elseif");
- beg_word[2] = strsave("else");
- beg_word[3] = strsave("do");
-
- end_word[0] = strsave("endif");
- end_word[1] = strsave("end");
- end_word[2] = strsave("elseif");
- end_word[3] = strsave("else");
- end_word[4] = strsave("continue");
-
- if (search(1, "\t")) {
- get_string(resp, "Tab found in file, untabify buffer? [y]");
- if (toupper(*resp) != 'N') {
- resp[0] = 'a';
- while (!isdigit(*resp)) {
- resp[0] = '3';
- get_string(resp,
- "What size tab for untabify? [3]");
- }
- tab_size = resp[0] - '0';
- build_first = 1;
- maybe_refresh();
-
- point = 0;
- mark = size();
- untabify_region();
- }
- }
- point = 0;
- mark = 0;
- tab_size = 3;
- }
-
- /* Make this the default mode for .for files */
- suffix_for() { for_mode(); }
-
- /* save_file() from files.e was modified to behave appropriately if
- narrow or fortran mode is on, by Gary R. Smith, Sept. 1986 */
-
- buffer int narrow_mode;
-
- command save_file() on cx_tab[CTRL('S')]
- {
- char backupname[80];
- int err;
- int mod;
- char resp[80];
-
- iter = 0;
- if (!*filename)
- return write_file();
- strcpy(backupname, filename);
- strcpy(get_extension(backupname), ".bak");
- if (want_backups && strcmp(filename, backupname)) {
- delete_file(backupname); /* don't check errors */
- rename_file(filename, backupname);
- }
- if (fortran_mode) { /* Fortran file may need to be untabified */
- get_string(resp, "Untabify buffer? [y]");
- if (toupper(*resp) != 'N') {
- point = 0;
- mark = size();
- untabify_region();
- }
- }
- mod = modified;
- if (err = file_write(filename, strip_returns))
- file_error(err,filename,"write error");
- else
- say("%s written.", filename);
- if (narrow_mode)
- modified = mod;
- return err;
- }
-
- /****************** End of file "fortran.e" ******************/
-