1 ;;; shen-mode.el --- A major mode for editing shen source code
3 ;; Copyright (C) 2011 Free Software Foundation, Inc.
5 ;; Author: Eric Schulte <schulte.eric@gmail.com>
7 ;; Keywords: languages, shen
8 ;; Description: A major mode for editing shen source code
10 ;; This file is part of GNU Emacs.
12 ;; GNU Emacs is free software: you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation, either version 3 of the License, or
15 ;; (at your option) any later version.
17 ;; GNU Emacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
27 ;; A minor mode for editing shen source code.
32 (require 'shen-functions)
35 (defcustom shen-mode-hook '(turn-on-eldoc-mode)
36 "Normal hook run when entering `shen-mode'."
41 (let ((map (make-sparse-keymap)))
42 (set-keymap-parent map lisp-mode-shared-map)
43 (substitute-key-definition 'indent-new-comment-line
44 'c-indent-new-comment-line
46 (substitute-key-definition 'fill-paragraph 'c-fill-paragraph
49 "Currently just inherits from `lisp-mode-shared-map'.")
53 (defconst shen-font-lock-keywords
58 '("define" "defmacro" "defprolog" "/." "synonyms"))
62 (1 font-lock-keyword-face)
63 (2 font-lock-function-name-face nil t))
64 ("(\\(lambda\\)\\>[ \t]*(?\\(\\sw+\\)?"
65 (1 font-lock-keyword-face)
66 (2 font-lock-variable-name-face nil t))
68 ("(\\(datatype\\)\\>[ \t]*(?\\(\\sw+\\)?"
69 (1 font-lock-keyword-face)
70 (2 font-lock-type-face nil t))
72 ("\\<\\([A-Z]\\w*\\)\\>" . font-lock-variable-name-face)
78 '("let" "=" "eval-without-reader-macros" "freeze" "type") ; generic
79 '("if" "and" "or" "cond")) t) ; boolean
82 ("(\\(error\\)\\>" 1 font-lock-warning-face)
88 (lambda (it) (format "%s" it))
90 '(intern function) ; symbols
91 '(pos tlstr cn str string?) ; strings
92 '(set value) ; assignment
93 '(cons hd tl cons?) ; lists
94 '(absvector address-> <-address absvector?) ; vector
95 '(pr read-byte open close) ; stream
97 '(+ - * / > < >= <= number?) ; arithmetic
98 '(fst snd tupple?) ; tuple
100 '(put get) ; property lists
101 '(simple-error trap-error error-to-string) ; error
104 (lambda (it) (format "%s?" it))
105 '(boolean character complex congruent cons element empty float
106 integer number provable rational solved string symbol
109 (mapcar #'car shen-functions)
110 shen-more-functions))
113 1 font-lock-builtin-face)
114 ;; external global variables
118 (lambda (cnst) (format "*%s*" cnst))
119 '("language" "implementation" "port" "porters"
120 "stinput" "home-directory" "version"
121 "maximum-print-sequence-size" "printer" "macros")) t)
123 1 font-lock-builtin-face)))
124 "Default expressions to highlight in Shen mode.")
126 (defvar shen-mode-syntax-table
127 (let ((table (make-syntax-table)))
128 (dolist (pair '((?@ . "w")
137 ;; comment delimiters
140 (modify-syntax-entry (car pair) (cdr pair) table))
142 "Syntax table to use in shen-mode.")
146 ;; Copied from qi-mode, which in turn is from scheme-mode and from lisp-mode
147 (defun shen-indent-function (indent-point state)
148 (let ((normal-indent (current-column)))
149 (goto-char (1+ (elt state 1)))
150 (parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t)
151 (if (and (elt state 2)
152 (not (looking-at "\\sw\\|\\s_")))
153 ;; car of form doesn't seem to be a symbol
155 (if (not (> (save-excursion (forward-line 1) (point))
156 calculate-lisp-indent-last-sexp))
157 (progn (goto-char calculate-lisp-indent-last-sexp)
159 (parse-partial-sexp (point)
160 calculate-lisp-indent-last-sexp 0 t)))
161 ;; Indent under the list or under the first sexp on the same
162 ;; line as calculate-lisp-indent-last-sexp. Note that first
163 ;; thing on that line has to be complete sexp since we are
164 ;; inside the innermost containing sexp.
165 (backward-prefix-chars)
167 (let ((function (buffer-substring (point)
168 (progn (forward-sexp 1) (point))))
170 (setq method (or (get (intern-soft function) 'shen-indent-function)
171 (get (intern-soft function) 'shen-indent-hook)))
172 (cond ((or (eq method 'defun)
174 (> (length function) 3)
175 (string-match "\\`def" function)))
176 (lisp-indent-defform state indent-point))
178 (lisp-indent-specform method state
179 indent-point normal-indent))
181 (funcall method state indent-point normal-indent)))))))
183 (defun shen-let-indent (state indent-point normal-indent)
184 (let ((edge (- (current-column) 2)))
185 (goto-char indent-point) (skip-chars-forward " \t")
186 (if (looking-at "[-a-zA-Z0-9+*/?!@$%^&_:~]")
187 ;; deeper indent because we're still defining local variables
188 (lisp-indent-specform 5 state indent-point normal-indent)
189 ;; shallow indent because we're in the body
192 (defun shen-package-indent (state indent-point normal-indent)
193 (- (current-column) 8))
195 (put 'let 'shen-indent-function 'shen-let-indent)
196 (put 'lambda 'shen-indent-function 1)
197 (put 'package 'shen-indent-function 'shen-package-indent)
198 (put 'datatype 'shen-indent-function 1)
201 ;;; Function documentation
202 (defun shen-current-function ()
207 (thing-at-point 'word))))
209 (defun shen-mode-eldoc ()
210 (let ((func (assoc (intern (or (shen-current-function) "")) shen-functions)))
213 (propertize (symbol-name (car func))
214 'face 'font-lock-function-name-face)
215 (if (cadr func) (concat "[" (cadr func) "]") "")
216 (if (caddr func) (concat " " (caddr func)) "")))))
218 (defvar shen-imenu-generic-expression
219 '(("Functions" "^\\s-*(\\(define\\)" 1)))
222 ;;; Major mode definition
223 ;; apparently some versions of Emacs don't have `prog-mode' defined
224 (unless (fboundp 'prog-mode)
225 (defalias 'prog-mode 'fundamental-mode))
227 (define-derived-mode shen-mode prog-mode "shen"
228 "Major mode for editing Shen code."
229 :syntax-table shen-mode-syntax-table
230 ;; set a variety of local variables
231 ((lambda (local-vars)
232 (dolist (pair local-vars)
233 (set (make-local-variable (car pair)) (cdr pair))))
234 `((adaptive-fill-mode . nil)
235 (fill-paragraph-function . lisp-fill-paragraph)
236 (indent-line-function . lisp-indent-line)
237 (lisp-indent-function . shen-indent-function)
238 (parse-sexp-ignore-comments . t)
239 (comment-start . "\\* ")
240 (comment-end . " *\\")
242 (comment-column . 32)
243 (parse-sexp-ignore-comments . t)
244 (comment-use-global-state . nil)
245 (eldoc-documentation-function . shen-mode-eldoc)
246 (imenu-case-fold-search . t)
247 (imenu-generic-expression . ,shen-imenu-generic-expression)
249 (font-lock-defaults . (shen-font-lock-keywords)))))
251 (add-to-list 'auto-mode-alist '("\\.shen\\'" . shen-mode))
254 ;;; shen-mode.el ends here