From 5b9b071a8b59e6d66cc71c3c06a12bea3a0cebb7 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Wed, 1 Dec 2010 02:10:27 +0000 Subject: Add basic emacs resp-mode (modified from scheme.el). git-svn-id: http://svn.drobilla.net/resp/resp@273 ad02d1e2-f140-0410-9f75-f8b11f17cedd --- support/emacs/resp.el | 391 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 391 insertions(+) create mode 100644 support/emacs/resp.el (limited to 'support/emacs') diff --git a/support/emacs/resp.el b/support/emacs/resp.el new file mode 100644 index 0000000..d3a6301 --- /dev/null +++ b/support/emacs/resp.el @@ -0,0 +1,391 @@ +;;; 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) + + +(provide 'resp) + +;;; resp.el ends here -- cgit v1.2.1