home *** CD-ROM | disk | FTP | other *** search
- /* Lisp mode for Epsilon. This editor mode is intended to be used with
- programs written in Common Lisp or Scheme. It attempts to be compatible
- with Symbolics Zmacs and GNU Emacs.
-
- (c) Copyright 1990 Carl W. Hoffman. All rights reserved.
-
- This file may be freely copied, distributed, or modified for non-commercial
- use provided that this copyright notice is not removed. For further
- information about other Common Lisp and Scheme utilities, contact the
- following address:
-
- Carl W. Hoffman, 363 Marlborough Street, Boston, MA 02115, U.S.A.
- Internet: CWH@AI.MIT.EDU CompuServe: 76416,3365 Fax: 617-262-4284
-
- This code has been tested with Epsilon version 4.13.
-
- This file expects that FILLPREF.E will also be loaded so as to enable the
- filling of Lisp comments. */
-
- #include <eel.h>
-
- #define OPEN_PAREN '('
- #define CLOSE_PAREN ')'
-
- buffer char fill_prefix[60];
- keytable lisp_tab;
-
- #define NOT_WHITESPACE "[^ \t\n]"
- #define NOT_WHITESPACE_OR_OPEN "[^ \t\n(]"
- #define NOT_WHITESPACE_OR_CLOSE "[^ \t\n)]"
- #define LISP_BREAK "[^\\][ \t\n\"|()]"
-
- forward_one_sexp()
- {
- int start = point;
- int level = 0;
-
- /* Jump over whitespace and close parentheses.
- Abort if we reach the end of the buffer.
- Leave point on the first non-whitespace-or-close character we see. */
-
- if (!re_search(1, NOT_WHITESPACE_OR_CLOSE)) {
- say("At end of buffer");
- point = start;
- return 1;
- }
- point = matchstart;
-
- /* Loop skipping forward over Lisp tokens. The variable LEVEL keeps
- track of the current nesting level. */
-
- while (1) {
- switch (curchar()) {
-
- case OPEN_PAREN:
- point++;
- level++;
- break;
-
- case CLOSE_PAREN:
- point++;
- if (level > 0) level--;
- break;
-
- case ';':
- nl_forward();
- goto next_token;
-
- case '\'':
- case '`':
- case ',':
- point++;
- goto next_token;
-
- case '#':
- point++;
- switch (curchar()) {
-
- case OPEN_PAREN: /* #( */
- point++;
- level++;
- break;
-
- case '+': /* #+ */
- case '-': /* #- */
- point++;
- goto next_token;
-
- case '\\': /* #\ */
- point++;
- if (re_search(1, LISP_BREAK))
- point--;
- break;
-
- default:
- break;
- }
- break;
-
- /* We're inside a string. Search for the next two-character sequence
- where the first character is not backslash and the second character
- is double quote. */
-
- case '"':
- re_search(1, "[^\\]\"");
- break;
-
- /* We're inside a quoted symbol. Search for the next two-character
- sequence where the first character is not backslash and the second
- character is vertical bar. */
-
- case '|':
- re_search(1, "[^\\]%|");
- break;
-
- /* We're inside an ordinary symbol. Search for the next two-character
- sequence where the first character is not a backslash and the second
- character is a Lisp token break character. */
-
- default:
- if (re_search(1, LISP_BREAK))
- point--;
- break;
- }
-
- if (level == 0) return 1;
-
- /* Skip over whitespace to find the start of the next token. Leave point
- on the break character, if we find one, or at the end of the buffer. */
-
- next_token:
- if (!re_search(1, NOT_WHITESPACE))
- break;
- point = matchstart;
- }
-
- say("Unmatched parentheses");
- point = start;
- return 0;
- }
-
- backward_one_sexp()
- {
- int start = point;
- int level = 0;
-
- /* Jump over whitespace and open parentheses.
- Abort if we reach the beginning of the buffer. */
-
- if (!re_search(-1, NOT_WHITESPACE_OR_OPEN)) {
- say("At beginning of buffer");
- point = start;
- return 1;
- }
-
- /* Loop skipping backward over Lisp tokens. The variable LEVEL keeps
- track of the current nesting level. */
-
- while (1) {
- switch (curchar()) {
-
- /* If we see a slashified open paren at the end of a token,
- it is either a character constant or a symbol ending in a
- slashified paren. */
-
- case CLOSE_PAREN:
- if (character(point-1) == '\\') {
- re_search(-1, LISP_BREAK);
- point++;
- }
- else
- level++;
- break;
-
- case OPEN_PAREN:
- if (character(point-1) == '\\') {
- re_search(-1, LISP_BREAK);
- point++;
- }
- else {
- if (level > 0) level--;
- if (index("'`,#", character(point-1)))
- point--;
- else if (index("+-", character(point-1)) &&
- (character(point-2) == '#'))
- point -= 2;
- }
- break;
-
- /* We're inside a string. Search for the next two-character sequence
- where the first character is not backslash and the second character
- is double quote. */
-
- case '"':
- re_search(-1, "[^\\]\"");
- point++;
- break;
-
- /* We're inside a quoted symbol. Search for the next two-character
- sequence where the first character is not backslash and the second
- character is vertical bar. */
-
- case '|':
- re_search(-1, "[^\\]%|");
- if (!index("'`,", curchar()))
- point++;
- break;
-
- /* We're inside an ordinary symbol. Search for the next two-character
- sequence where the first character is not a backslash and the second
- character is a Lisp token break character. */
-
- /* We must treat the case of a single Lisp break character at the
- beginnning of the buffer specially, since we won't find it as
- a two-character sequence. */
-
- default:
- if (re_search(-1, LISP_BREAK))
- point = matchstart;
- else if (index("()|\"#", curchar()))
- point++;
- break;
- }
-
- if (level == 0) return 1;
-
- if (!re_search(-1, NOT_WHITESPACE))
- break;
- }
-
- say("Unmatched parentheses");
- point = start;
- return 0;
- }
-
- command forward_sexp() on lisp_tab[ALT(CTRL('F'))]
- {
- if (iter < 0)
- while (iter++ < 0)
- backward_one_sexp();
- else
- while (iter-- > 0)
- forward_one_sexp();
- }
-
- command backward_sexp() on lisp_tab[ALT(CTRL('B'))]
- {
- if (iter < 0)
- while (iter++ < 0)
- forward_one_sexp();
- else
- while (iter-- > 0)
- backward_one_sexp();
- }
-
- command kill_sexp() on lisp_tab[ALT(CTRL('K'))]
- {
- int start = point;
- forward_sexp();
- do_save_kill(start, point);
- }
-
- command up_sexp() on lisp_tab[ALT(CTRL('U'))]
- {
- while (1) {
- if (!re_search(-1, "[^ \t\n]"))
- break;
- if (curchar() == OPEN_PAREN)
- break;
- point++;
- if (!backward_one_sexp())
- break;
- if (current_column() == 0)
- break;
- }
- }
-
- /* What should this command do when the cursor is on an open paren?
- At first I thought it should move forward one character and then
- try to go down a level. However, this means that C-M-D followed
- by C-M-U doesn't leave you where you began. So, now I have it
- defined to just go forward one character. */
-
- command down_sexp() on lisp_tab[ALT(CTRL('D'))]
- {
- int start = point;
- /* Must treat this as a special case since the re_search
- will only look for two character sequences. */
- if (curchar() == OPEN_PAREN) {
- point++;
- return;
- }
- if (!re_search(1, "[^\\][()]"))
- return;
- if (character(point-1) == CLOSE_PAREN) {
- point = start;
- }
- }
-
- command begin_defun() on lisp_tab[ALT(CTRL('A'))]
- {
- while (1) {
- if (!search(-1, "("))
- break;
- if (current_column() == 0)
- break;
- }
- }
-
- lisp_compute_indent()
- {
- int start, indent;
- start = point;
-
- /* Find first open or close paren above the current line. */
- to_begin_line();
- if (!re_search(-1, "[()]")) {
- /* No parens at all before point.
- Leave point at the end of the indentation on the current line. */
- point = start;
- to_indentation();
- indent = current_column();
- /* point = start; */
- return indent;
- }
-
- /* Skip backward over the preceding S-expression. Then search backward
- for either the beginning of the line or the operator of the current form.
- To correctly indent DO, DO*, UNWIND-PROTECT, and LOOP, it is necessary to
- always search for the operator of the current form. */
-
- if (curchar() == CLOSE_PAREN)
- while (1) {
- point++;
- backward_one_sexp();
- indent = current_column();
- if (!re_search(-1, "[()\n]")) {
- point = start;
- return indent;
- }
- if (curchar() == '\n') {
- point++;
- to_indentation();
- indent = current_column();
- point = start;
- return indent;
- }
- if (curchar() == OPEN_PAREN)
- break;
- }
-
- /* The point is just before an open paren. Find the indentation of the
- first S-expression following the operator. Also check the operator name
- for certain special forms. */
-
- {
- int operator_start, operator_end;
- int open_paren_column = current_column();
- point++;
- operator_start = point;
- re_search(1, "[ \t\n(]");
- point--;
- operator_end = point;
- if ((curchar() == ' ') || (curchar() == '\t')) {
- re_search(1, "[^ \t]");
- point--;
- }
- if (curchar() == '\n')
- indent = open_paren_column + 2;
- else {
- /* It might be better to do this with a regexp. */
- char operator[80];
- grab(operator_start, operator_end, operator);
- if (!( strnfcmp(operator, "def", 3)
- && strnfcmp(operator, "let", 3)
- && strnfcmp(operator, "with", 4)
- && strfcmp(operator, "case")
- && strfcmp(operator, "flet")
- && strfcmp(operator, "when")
- && strfcmp(operator, "ccase")
- && strfcmp(operator, "ecase")
- && strfcmp(operator, "labels")
- && strfcmp(operator, "lambda")
- && strfcmp(operator, "unless")
- && strfcmp(operator, "dolist")
- && strfcmp(operator, "dotimes")
- && strfcmp(operator, "macrolet")
- && strfcmp(operator, "ctypecase")
- && strfcmp(operator, "etypecase")
- ))
- indent = open_paren_column + 2;
- else
- indent = current_column();
- }
- }
-
- point = start;
- return indent;
- }
-
- lisp_indent() on lisp_tab['\t']
- {
- int start = point;
- int offset = 0;
- to_indentation();
- if (point < start) offset = start - point;
- to_column(lisp_compute_indent());
- point += offset;
- }
-
- command indent_sexp() on lisp_tab[ALT(CTRL('Q'))]
- {
- int start = point;
- int *end = alloc_spot();
- forward_one_sexp();
- *end = point;
- point = start;
- while (1) {
- if (!nl_forward())
- break;
- if (point >= *end)
- break;
- to_column(lisp_compute_indent());
- }
- point = start;
- }
-
- /* The command show_matching_delimiter tests to see if move_level
- returns 1, and only then does a show_line. Should we do the same? */
-
- command show_matching_paren() on lisp_tab[CLOSE_PAREN]
- {
- int start;
- normal_character();
- start = point;
- say("");
- backward_one_sexp();
- show_line();
- point = start;
- }
-
- command insert_parens() on lisp_tab[ALT(OPEN_PAREN)]
- {
- stuff("()");
- point--;
- }
-
- command move_over_close_paren() on lisp_tab[ALT(CLOSE_PAREN)]
- {
- re_search(1, ")");
- }
-
- lisp_indenter() { to_column(lisp_compute_indent()); }
-
- command lisp_mode ()
- {
- mode_keys = lisp_tab;
- indenter = lisp_indenter;
- auto_indent = 1;
- margin_right = 79;
- fill_mode = 0;
- strcpy(fill_prefix, ";; ");
- major_mode = "Lisp";
- make_mode();
- }
-
- suffix_lsp() { lisp_mode(); }
- suffix_scm() { lisp_mode(); }
-
- /* Tag all Lisp functions in this file */
-
- /*
- tag_suffix_lsp()
- {
- }
-
- tag_suffix_scm() { tag_suffix_lsp(); }
-
- */
-