;;; resp.el --- Resp editing mode ;; Copyright (C) 1986, 1987, 1988, 1997, 1998, 2001, 2002, 2003, 2004, 2005, ;; 2006, 2007, 2008, 2009 Free Software Foundation, Inc. ;; Based on resp.el ;; Author: Bill Rozas ;; Adapted-by: Dave Love ;; Adapted-by: David Robillard ;; Keywords: languages, lisp, resp, resp ;; This file is not part of GNU Emacs. ;; GNU Emacs 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 3 of the License, or ;; (at your option) any later version. ;; GNU Emacs 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. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see . ;;; Commentary: ;; The major mode for editing Resp-type Lisp code, very similar to ;; the Lisp mode documented in the Emacs manual. ;;; Code: (require 'lisp-mode) (defvar resp-mode-syntax-table (let ((st (make-syntax-table)) (i 0)) ;; Default is atom-constituent. (while (< i 256) (modify-syntax-entry i "_ " st) (setq i (1+ i))) ;; Word components. (setq i ?0) (while (<= i ?9) (modify-syntax-entry i "w " st) (setq i (1+ i))) (setq i ?A) (while (<= i ?Z) (modify-syntax-entry i "w " st) (setq i (1+ i))) (setq i ?a) (while (<= i ?z) (modify-syntax-entry i "w " st) (setq i (1+ i))) ;; Whitespace (modify-syntax-entry ?\t " " st) (modify-syntax-entry ?\n "> " st) (modify-syntax-entry ?\f " " st) (modify-syntax-entry ?\r " " st) (modify-syntax-entry ?\s " " st) ;; These characters are delimiters but otherwise undefined. ;; Brackets and braces balance for editing convenience. (modify-syntax-entry ?\[ "(] " st) (modify-syntax-entry ?\] ")[ " st) (modify-syntax-entry ?{ "(} " st) (modify-syntax-entry ?} "){ " st) ;; Other atom delimiters (modify-syntax-entry ?\( "() " st) (modify-syntax-entry ?\) ")( " st) ;; It's used for single-line comments as well as for #;(...) sexp-comments. (modify-syntax-entry ?\; "< 2 " st) (modify-syntax-entry ?\" "\" " st) (modify-syntax-entry ?' "' " st) (modify-syntax-entry ?` "' " st) ;; Special characters (modify-syntax-entry ?, "' " st) (modify-syntax-entry ?@ "' " st) (modify-syntax-entry ?# "' 14b" st) (modify-syntax-entry ?\\ "\\ " st) st)) (defvar resp-mode-abbrev-table nil) (define-abbrev-table 'resp-mode-abbrev-table ()) (defvar resp-imenu-generic-expression '((nil "^(def\\(\\|-type\\)\\s-+(?\\(\\sw+\\)" 2)) "Imenu generic expression for Resp mode. See `imenu-generic-expression'.") (defun resp-mode-variables () (set-syntax-table resp-mode-syntax-table) (setq local-abbrev-table resp-mode-abbrev-table) (make-local-variable 'paragraph-start) (setq paragraph-start (concat "$\\|" page-delimiter)) (make-local-variable 'paragraph-separate) (setq paragraph-separate paragraph-start) (make-local-variable 'paragraph-ignore-fill-prefix) (setq paragraph-ignore-fill-prefix t) (make-local-variable 'fill-paragraph-function) (setq fill-paragraph-function 'lisp-fill-paragraph) ;; Adaptive fill mode gets in the way of auto-fill, ;; and should make no difference for explicit fill ;; because lisp-fill-paragraph should do the job. (make-local-variable 'adaptive-fill-mode) (setq adaptive-fill-mode nil) (make-local-variable 'indent-line-function) (setq indent-line-function 'lisp-indent-line) (make-local-variable 'parse-sexp-ignore-comments) (setq parse-sexp-ignore-comments t) (make-local-variable 'outline-regexp) (setq outline-regexp ";;; \\|(....") (make-local-variable 'comment-start) (setq comment-start ";") (set (make-local-variable 'comment-add) 1) (make-local-variable 'comment-start-skip) ;; Look within the line for a ; following an even number of backslashes ;; after either a non-backslash or the line beginning. (setq comment-start-skip "\\(\\(^\\|[^\\\\\n]\\)\\(\\\\\\\\\\)*\\);+[ \t]*") (set (make-local-variable 'font-lock-comment-start-skip) ";+ *") (make-local-variable 'comment-column) (setq comment-column 40) (make-local-variable 'parse-sexp-ignore-comments) (setq parse-sexp-ignore-comments t) (make-local-variable 'lisp-indent-function) (setq lisp-indent-function 'resp-indent-function) (setq mode-line-process '("" resp-mode-line-process)) (set (make-local-variable 'imenu-case-fold-search) t) (setq imenu-generic-expression resp-imenu-generic-expression) (set (make-local-variable 'imenu-syntax-alist) '(("+-*/.<>=?!$%_&~^:" . "w"))) (set (make-local-variable 'font-lock-defaults) '((resp-font-lock-keywords resp-font-lock-keywords-1 resp-font-lock-keywords-2) nil t (("+-*/.<>=!?$%_&~^:" . "w") (?#. "w 14")) beginning-of-defun (font-lock-mark-block-function . mark-defun) (font-lock-syntactic-face-function . resp-font-lock-syntactic-face-function) (parse-sexp-lookup-properties . t) (font-lock-extra-managed-props syntax-table))) (set (make-local-variable 'lisp-doc-string-elt-property) 'resp-doc-string-elt)) (defvar resp-mode-line-process "") (defvar resp-mode-map (let ((smap (make-sparse-keymap)) (map (make-sparse-keymap "Resp"))) (set-keymap-parent smap lisp-mode-shared-map) (define-key smap [menu-bar resp] (cons "Resp" map)) (define-key map [run-resp] '("Run Inferior Resp" . run-resp)) (define-key map [uncomment-region] '("Uncomment Out Region" . (lambda (beg end) (interactive "r") (comment-region beg end '(4))))) (define-key map [comment-region] '("Comment Out Region" . comment-region)) (define-key map [indent-region] '("Indent Region" . indent-region)) (define-key map [indent-line] '("Indent Line" . lisp-indent-line)) (put 'comment-region 'menu-enable 'mark-active) (put 'uncomment-region 'menu-enable 'mark-active) (put 'indent-region 'menu-enable 'mark-active) smap) "Keymap for Resp mode. All commands in `lisp-mode-shared-map' are inherited by this map.") ;;;###autoload (defun resp-mode () "Major mode for editing Resp code. Editing commands are similar to those of `lisp-mode'. Commands: Delete converts tabs to spaces as it moves back. Blank lines separate paragraphs. Semicolons start comments. \\{resp-mode-map} Entry to this mode calls the value of `resp-mode-hook' if that value is non-nil." (interactive) (kill-all-local-variables) (use-local-map resp-mode-map) (setq major-mode 'resp-mode) (setq mode-name "Resp") (resp-mode-variables) (run-mode-hooks 'resp-mode-hook)) (defgroup resp nil "Editing Resp code." :link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces) :group 'lisp) (defcustom resp-mode-hook nil "Normal hook run when entering `resp-mode'. See `run-hooks'." :type 'hook :group 'resp) ;; This is shared by cmuresp and xresp. (defcustom resp-program-name "resp" "*Program invoked by the `run-resp' command." :type 'string :group 'resp) (defconst resp-font-lock-keywords-1 (eval-when-compile (list ;; ;; Declarations. Hannes Haug says ;; this works for SOS, STklos, SCOOPS, Meroon and Tiny CLOS. (list (concat "(\\(def\\*?\\(" ;; Function names. "\\(\\|-public\\|-method\\|-generic\\(-procedure\\)?\\)\\|" ;; Macro names, as variable names. A bit dubious, this. "\\(-syntax\\|-macro\\)\\|" ;; Class names. "\\(-class\\|-type\\)\\|" ;; Guile modules. "\\|-module" "\\)\\)\\>" ;; Any whitespace and declared object. "[ \t]*(?" "\\(\\sw+\\)?") '(1 font-lock-keyword-face) '(6 (cond ((match-beginning 3) font-lock-function-name-face) ((match-beginning 5) font-lock-variable-name-face) (t font-lock-type-face)) nil t)) )) "Subdued expressions to highlight in Resp modes.") (defconst resp-font-lock-keywords-2 (append resp-font-lock-keywords-1 (eval-when-compile (list ;; ;; Control structures. (cons (concat "(" (regexp-opt '("begin" "call-with-current-continuation" "call/cc" "call-with-input-file" "call-with-output-file" "case" "cond" "do" "else" "for-each" "if" "lambda" "fn" "match" "let" "let*" "let-syntax" "letrec" "letrec-syntax" ;; SRFI 11 usage comes up often enough. "let-values" "let*-values" ;; Hannes Haug wants: "and" "or" "delay" "force" ;; Stefan Monnier says don't bother: ;;"quasiquote" "quote" "unquote" "unquote-splicing" "map" "syntax" "syntax-rules") t) "\\>") 1) ;; ;; Named let '("(let\\s-+\\(\\sw+\\)" (1 font-lock-function-name-face)) ;; ;; David Fox for SOS/STklos class specifiers. '("\\<<\\sw+>\\>" . font-lock-type-face) ;; ;; Resp `:' and `#:' keywords as builtins. '("\\<#?:\\sw+\\>" . font-lock-builtin-face) ;; ))) "Gaudy expressions to highlight in Resp modes.") (defvar resp-font-lock-keywords resp-font-lock-keywords-1 "Default expressions to highlight in Resp modes.") (defconst resp-sexp-comment-syntax-table (let ((st (make-syntax-table resp-mode-syntax-table))) (modify-syntax-entry ?\; "." st) (modify-syntax-entry ?\n " " st) (modify-syntax-entry ?# "'" st) st)) (put 'lambda 'resp-doc-string-elt 2) ;; Docstring's pos in a `define' depends on whether it's a var or fun def. (put 'define 'resp-doc-string-elt (lambda () ;; The function is called with point right after "define". (forward-comment (point-max)) (if (eq (char-after) ?\() 2 0))) (defun resp-font-lock-syntactic-face-function (state) (when (and (null (nth 3 state)) (eq (char-after (nth 8 state)) ?#) (eq (char-after (1+ (nth 8 state))) ?\;)) ;; It's a sexp-comment. Tell parse-partial-sexp where it ends. (save-excursion (let ((pos (point)) (end (condition-case err (let ((parse-sexp-lookup-properties nil)) (goto-char (+ 2 (nth 8 state))) ;; FIXME: this doesn't handle the case where the sexp ;; itself contains a #; comment. (forward-sexp 1) (point)) (scan-error (nth 2 err))))) (when (< pos (- end 2)) (put-text-property pos (- end 2) 'syntax-table resp-sexp-comment-syntax-table)) (put-text-property (- end 1) end 'syntax-table '(12))))) ;; Choose the face to use. (lisp-font-lock-syntactic-face-function state)) (defvar calculate-lisp-indent-last-sexp) ;; Copied from lisp-indent-function, but with gets of ;; resp-indent-{function,hook}. (defun resp-indent-function (indent-point state) (let ((normal-indent (current-column))) (goto-char (1+ (elt state 1))) (parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t) (if (and (elt state 2) (not (looking-at "\\sw\\|\\s_"))) ;; car of form doesn't seem to be a symbol (progn (if (not (> (save-excursion (forward-line 1) (point)) calculate-lisp-indent-last-sexp)) (progn (goto-char calculate-lisp-indent-last-sexp) (beginning-of-line) (parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t))) ;; Indent under the list or under the first sexp on the same ;; line as calculate-lisp-indent-last-sexp. Note that first ;; thing on that line has to be complete sexp since we are ;; inside the innermost containing sexp. (backward-prefix-chars) (current-column)) (let ((function (buffer-substring (point) (progn (forward-sexp 1) (point)))) method) (setq method (or (get (intern-soft function) 'resp-indent-function) (get (intern-soft function) 'resp-indent-hook))) (cond ((or (eq method 'defun) (and (null method) (> (length function) 3) (string-match "\\`def" function))) (lisp-indent-defform state indent-point)) ((integerp method) (lisp-indent-specform method state indent-point normal-indent)) (method (funcall method state indent-point normal-indent))))))) ;;; Let is different in Resp (defun would-be-symbol (string) (not (string-equal (substring string 0 1) "("))) (defun next-sexp-as-string () ;; Assumes that it is protected by a save-excursion (forward-sexp 1) (let ((the-end (point))) (backward-sexp 1) (buffer-substring (point) the-end))) ;; This is correct but too slow. ;; The one below works almost always. ;;(defun resp-let-indent (state indent-point) ;; (if (would-be-symbol (next-sexp-as-string)) ;; (resp-indent-specform 2 state indent-point) ;; (resp-indent-specform 1 state indent-point))) (defun resp-let-indent (state indent-point normal-indent) (skip-chars-forward " \t") (if (looking-at "[-a-zA-Z0-9+*/?!@$%^&_:~]") (lisp-indent-specform 2 state indent-point normal-indent) (lisp-indent-specform 1 state indent-point normal-indent))) ;; (put 'begin 'resp-indent-function 0), say, causes begin to be indented ;; like defun if the first form is placed on the next line, otherwise ;; it is indented like any other form (i.e. forms line up under first). (put 'def 'resp-indent-function 1) (put 'match 'resp-indent-function 1) (put 'fn 'resp-indent-function 1) (put 'let 'resp-indent-function 'resp-let-indent) (provide 'resp) ;;; resp.el ends here