1 ;; promela-mode.el --- major mode for editing PROMELA program files
2 ;; $Revision: 1.11 $ $Date: 2001/07/09 18:36:45 $ $Author: engstrom $
4 ;; Author: Eric Engstrom <eric.engstrom@honeywell.com>
5 ;; Maintainer: Eric Engstrom
6 ;; Keywords: spin, promela, tools
8 ;; Copyright (C) 1998-2003 Eric Engstrom / Honeywell Laboratories
10 ;; ... Possibly insert GPL here someday ...
14 ;; This file contains code for a GNU Emacs major mode for editing
15 ;; PROMELA (SPIN) program files.
17 ;; Type "C-h m" in Emacs (while in a buffer in promela-mode) for
18 ;; information on how to configure indentation and fontification,
19 ;; or look at the configuration variables below.
21 ;; To use, place promela-mode.el in a directory in your load-path.
22 ;; Then, put the following lines into your .emacs and promela-mode
23 ;; will be automatically loaded when editing a PROMELA program.
25 ;; (autoload 'promela-mode "promela-mode" "PROMELA mode" nil t)
26 ;; (setq auto-mode-alist
28 ;; (list (cons "\\.promela$" 'promela-mode)
29 ;; (cons "\\.spin$" 'promela-mode)
30 ;; (cons "\\.pml$" 'promela-mode)
31 ;; ;; (cons "\\.other-extensions$" 'promela-mode)
35 ;; If you wish for promela-mode to be used for files with other
36 ;; extensions you add your own patterned after the code above.
38 ;; Note that promela-mode adhears to the font-lock "standards" and
39 ;; defines several "levels" of fontification or colorization. The
40 ;; default is fairly gaudy, so I can imagine that some folks would
41 ;; like a bit less. FMI: see `font-lock-maximum-decoration'
43 ;; This mode is known to work under the following versions of emacs:
44 ;; - XEmacs: 19.16, 20.x, 21.x
45 ;; - FSF/GNU Emacs: 19.34
46 ;; - NTEmacs (FSF): 20.[67]
47 ;; That is not to say there are no bugs specific to one of those versions :-)
49 ;; Please send any comments, bugs, patches or other requests to
50 ;; Eric Engstrom at engstrom@htc.honeywell.com
53 ;; - compile/syntax-check/verify? (suggested by R.Goldman)
54 ;; - indentation - splitting lines at logical operators (M. Rangarajan)
55 ;; [ This might "devolve" to indentation after "->" or ";"
56 ;; being as is, but anything else indent even more? ]
57 ;; :: SomeReallyLongArrayRef[this].typedefField != SomeReallyLongConstant -> /* some-comment */
58 ;; [ Suggestion would be to break the first line after the !=, therefore: ]
59 ;; :: SomeReallyLongArrayRef[this].typedefField
60 ;; != SomeReallyLongConstant -> /* some-comment */
61 ;; [ at this point I'm not so sure about this change... EE: 2001/05/19 ]
63 ;;; -------------------------------------------------------------------------
66 ;; NOTE: same as CVS revision:
67 (defconst promela-mode-version "$Revision: 1.11 $"
68 "Promela-mode version number.")
70 ;; -------------------------------------------------------------------------
71 ;; The following constant values can be modified by the user in a .emacs file
73 (defconst promela-block-indent 2
74 "*Controls indentation of lines within a block (`{') construct")
76 (defconst promela-selection-indent 2
77 "*Controls indentation of options within a selection (`if')
78 or iteration (`do') construct")
80 (defconst promela-selection-option-indent 3
81 "*Controls indentation of lines after options within selection or
82 iteration construct (`::')")
84 (defconst promela-comment-col 32
85 "*Defines the desired comment column for comments to the right of text.")
87 (defconst promela-tab-always-indent t
88 "*Non-nil means TAB in Promela mode should always reindent the current line,
89 regardless of where in the line point is when the TAB command is used.")
91 (defconst promela-auto-match-delimiter t
92 "*Non-nil means typing an open-delimiter (i.e. parentheses, brace, quote, etc)
93 should also insert the matching closing delmiter character.")
95 ;; That should be about it for most users...
96 ;; unless you wanna hack elisp, the rest of this is probably uninteresting
99 ;; -------------------------------------------------------------------------
100 ;; help determine what emacs we have here...
102 (defconst promela-xemacsp (string-match "XEmacs" (emacs-version))
103 "Non-nil if we are running in the XEmacs environment.")
105 ;;;(defconst promela-xemacs20p (and promela-xemacsp (>= emacs-major-version 20))
106 ;; "Non-nil if we are running in an XEmacs environment version 20 or greater.")
108 ;; -------------------------------------------------------------------------
109 ;; promela-mode font faces/definitions
111 ;; make use of font-lock stuff, so say that explicitly
114 ;; BLECH! YUCK! I just wish these guys could agree to something....
115 ;; Faces available in: ntemacs emacs xemacs xemacs xemacs
116 ;; font-lock- xxx -face 20.6 19.34 19.16 20.x 21.x
119 ;; -comment- X X X X X
120 ;; -doc-string- X X X
121 ;; -function-name- X X X X X
122 ;; -keyword- X X X X X
123 ;; -preprocessor- X X X
124 ;; -reference- X X X X
125 ;; -signal-name- X X!20.0
126 ;; -string- X X X X X
128 ;; -variable-name- X X X X X
131 ;;; Compatibility on faces between versions of emacs-en
132 (unless promela-xemacsp
134 (defvar font-lock-preprocessor-face 'font-lock-preprocessor-face
135 "Face name to use for preprocessor statements.")
136 ;; For consistency try to define the preprocessor face == builtin face
138 (copy-face 'font-lock-builtin-face 'font-lock-preprocessor-face)
140 (defface font-lock-preprocessor-face
141 '((t (:foreground "blue" :italic nil :underline t)))
142 "Face Lock mode face used to highlight preprocessor statements."
143 :group 'font-lock-highlighting-faces)))
145 (defvar font-lock-reference-face 'font-lock-reference-face
146 "Face name to use for constants and reference and label names.")
147 ;; For consistency try to define the reference face == constant face
149 (copy-face 'font-lock-constant-face 'font-lock-reference-face)
151 (defface font-lock-reference-face
152 '((((class grayscale) (background light))
153 (:foreground "LightGray" :bold t :underline t))
154 (((class grayscale) (background dark))
155 (:foreground "Gray50" :bold t :underline t))
156 (((class color) (background light)) (:foreground "CadetBlue"))
157 (((class color) (background dark)) (:foreground "Aquamarine"))
158 (t (:bold t :underline t)))
159 "Font Lock mode face used to highlight constancs, references and labels."
160 :group 'font-lock-highlighting-faces)))
164 ;; send-poll "symbol" face is custom to promela-mode
165 ;; but check for existence to allow someone to override it
166 (defvar promela-fl-send-poll-face 'promela-fl-send-poll-face
167 "Face name to use for Promela Send or Poll symbols: `!' or `?'")
168 (copy-face (if promela-xemacsp 'modeline 'region)
169 'promela-fl-send-poll-face)
171 ;; some emacs-en don't define or have regexp-opt available.
172 (unless (functionp 'regexp-opt)
173 (defmacro regexp-opt (strings)
174 "Cheap imitation of `regexp-opt' since it's not availble in this emacs"
175 `(mapconcat 'identity ,strings "\\|")))
178 ;; -------------------------------------------------------------------------
179 ;; promela-mode font lock specifications/regular-expressions
180 ;; - for help, look at definition of variable 'font-lock-keywords
181 ;; - some fontification ideas from -- [engstrom:20010309.1435CST]
182 ;; Pat Tullman (tullmann@cs.utah.edu) and
183 ;; Ny Aina Razermera Mamy (ainarazr@cs.uoregon.edu)
184 ;; both had promela-mode's that I discovered after starting this one...
185 ;; (but neither did any sort of indentation ;-)
187 (defconst promela-font-lock-keywords-1 nil
188 "Subdued level highlighting for Promela mode.")
190 (defconst promela-font-lock-keywords-2 nil
191 "Medium level highlighting for Promela mode.")
193 (defconst promela-font-lock-keywords-3 nil
194 "Gaudy level highlighting for Promela mode.")
196 ;; set each of those three variables now..
197 (let ((promela-keywords
200 '("active" "assert" "atomic" "break" "d_step"
201 "do" "dproctype" "else" "empty" "enabled"
202 "eval" "fi" "full" "goto" "hidden" "if" "init"
203 "inline" "len" "local" "mtype" "nempty" "never"
204 "nfull" "od" "of" "pcvalue" "printf" "priority"
205 "proctype" "provided" "run" "show" "skip"
206 "timeout" "trace" "typedef" "unless" "xr" "xs"))))
209 (regexp-opt '("bit" "bool" "byte" "short"
210 "int" "unsigned" "chan")))))
212 ;; really simple fontification (strings and comments come for "free")
213 (setq promela-font-lock-keywords-1
216 (cons (concat "\\<\\(" promela-keywords "\\)\\>")
217 'font-lock-keyword-face)
219 (cons (concat "\\<\\(" promela-types "\\)\\>")
220 'font-lock-type-face)
221 ;; Special constants:
222 '("\\<_\\(np\\|pid\\|last\\)\\>" . font-lock-reference-face)))
224 ;; more complex fontification
225 ;; add function (proctype) names, lables and goto statements
226 ;; also add send/receive/poll fontification
227 (setq promela-font-lock-keywords-2
228 (append promela-font-lock-keywords-1
230 ;; ANY Pre-Processor directive (lazy method: any line beginning with "#[a-z]+")
231 '("^\\(#[ \t]*[a-z]+\\)" 1 'font-lock-preprocessor-face t)
233 ;; "Functions" (proctype-s and inline-s)
234 (list (concat "\\<\\("
236 (regexp-opt '("run" "dproctype" "proctype" "inline")))
237 "\\)\\>[ \t]*\\(\\sw+\\)?")
238 ;;'(1 'font-lock-keyword-face nil)
239 '(2 'font-lock-function-name-face nil t))
241 ;; Labels and GOTO labels
242 '("^\\(\\sw+\\):" 1 'font-lock-reference-face)
243 '("\\<\\(goto\\)\\>[ \t]+\\(\\sw+\\)"
244 ;;(1 'font-lock-keyword-face nil)
245 (2 'font-lock-reference-face nil t))
247 ;; Send, Receive and Poll
248 '("\\(\\sw+\\)\\(\\[[^\\?!]+\\]\\)?\\(\\??\\?\\|!?!\\)\\(\\sw+\\)"
249 (1 'font-lock-variable-name-face nil t)
250 (3 'promela-fl-send-poll-face nil t)
251 (4 'font-lock-reference-face nil t)
255 ;; most complex fontification
256 ;; add pre-processor directives, typed variables and hidden/typedef decls.
257 (setq promela-font-lock-keywords-3
258 (append promela-font-lock-keywords-2
260 ;; ANY Pre-Processor directive (lazy method: any line beginning with "#[a-z]+")
261 ;;'("^\\(#[ \t]*[a-z]+\\)" 1 'font-lock-preprocessor-face t)
262 ;; "defined" in an #if or #elif and associated macro names
263 '("^#[ \t]*\\(el\\)?if\\>"
264 ("\\<\\(defined\\)\\>[ \t]*(?\\(\\sw+\\)" nil nil
265 (1 'font-lock-preprocessor-face nil t)
266 (2 'font-lock-reference-face nil t)))
267 '("^#[ \t]*ifn?def\\>"
268 ("[ \t]*\\(\\sw+\\)" nil nil
269 (1 'font-lock-reference-face nil t)))
270 ;; Filenames in #include <...> directives
271 '("^#[ \t]*include[ \t]+<\\([^>\"\n]+\\)>" 1 'font-lock-string-face nil t)
272 ;; Defined functions and constants/types (non-functions)
273 '("^#[ \t]*define[ \t]+"
274 ("\\(\\sw+\\)(" nil nil (1 'font-lock-function-name-face nil t))
275 ("\\(\\sw+\\)[ \t]+\\(\\sw+\\)" nil nil (1 'font-lock-variable-name-face)
276 (2 'font-lock-reference-face nil t))
277 ("\\(\\sw+\\)[^(]?" nil nil (1 'font-lock-reference-face nil t)))
279 ;; Types AND variables
280 ;; - room for improvement: (i.e. don't currently):
281 ;; highlight user-defined types and asociated variable declarations
282 (list (concat "\\<\\(" promela-types "\\)\\>")
283 ;;'(1 'font-lock-type-face)
284 ;; now match the variables after the type definition, if any
285 '(promela-match-variable-or-declaration
287 (1 'font-lock-variable-name-face) ;; nil t)
288 (2 font-lock-reference-face nil t)))
290 ;; Typedef/hidden types and declarations
291 '("\\<\\(typedef\\|hidden\\)\\>[ \t]*\\(\\sw+\\)?"
292 ;;(1 'font-lock-keyword-face nil)
293 (2 'font-lock-type-face nil t)
294 ;; now match the variables after the type definition, if any
295 (promela-match-variable-or-declaration
297 (1 'font-lock-variable-name-face nil t)
298 (2 'font-lock-reference-face nil t)))
302 (defvar promela-font-lock-keywords promela-font-lock-keywords-1
303 "Default expressions to highlight in Promela mode.")
305 ;; Font-lock matcher functions:
306 (defun promela-match-variable-or-declaration (limit)
307 "Match, and move over, any declaration/definition item after point.
308 Matches after point, but ignores leading whitespace characters.
309 Does not move further than LIMIT.
311 The expected syntax of a declaration/definition item is `word' (preceded
312 by optional whitespace) optionally followed by a `= value' (preceded and
313 followed by more optional whitespace)
315 Thus the regexp matches after point: word [ = value ]
317 Where the match subexpressions are: 1 2
319 The item is delimited by (match-beginning 1) and (match-end 1).
320 If (match-beginning 2) is non-nil, the item is followed by a `value'."
321 (when (looking-at "[ \t]*\\(\\sw+\\)[ \t]*=?[ \t]*\\(\\sw+\\)?[ \t]*,?")
322 (goto-char (min limit (match-end 0)))))
325 ;; -------------------------------------------------------------------------
326 ;; "install" promela-mode font lock specifications
328 ;; FMI: look up 'font-lock-defaults
329 (defconst promela-font-lock-defaults
331 (promela-font-lock-keywords
332 promela-font-lock-keywords-1
333 promela-font-lock-keywords-2
334 promela-font-lock-keywords-3) ;; font-lock stuff (keywords)
335 nil ;; keywords-only flag
336 nil ;; case-fold keyword searching
337 ;;((?_ . "w") (?$ . ".")) ;; mods to syntax table
338 nil ;; mods to syntax table (see below)
340 (font-lock-mark-block-function . mark-defun))
343 ;; "install" the font-lock-defaults based upon version of emacs we have
344 (cond (promela-xemacsp
345 (put 'promela-mode 'font-lock-defaults promela-font-lock-defaults))
346 ((not (assq 'promela-mode font-lock-defaults-alist))
347 (setq font-lock-defaults-alist
349 (cons 'promela-mode promela-font-lock-defaults)
350 font-lock-defaults-alist))))
353 ;; -------------------------------------------------------------------------
354 ;; other promela-mode specific definitions
356 (defconst promela-defun-prompt-regexp
357 "^[ \t]*\\(d?proctype\\|init\\|inline\\|never\\|trace\\|typedef\\|mtype\\s-+=\\)[^{]*"
358 "Regexp describing the beginning of a Promela top-level definition.")
360 (defvar promela-mode-syntax-table nil
361 "Syntax table in use in PROMELA-mode buffers.")
362 (if promela-mode-syntax-table
364 (setq promela-mode-syntax-table (make-syntax-table))
365 (modify-syntax-entry ?\\ "\\" promela-mode-syntax-table)
366 (modify-syntax-entry ?/ ". 14" promela-mode-syntax-table)
367 (modify-syntax-entry ?* ". 23" promela-mode-syntax-table)
368 (modify-syntax-entry ?+ "." promela-mode-syntax-table)
369 (modify-syntax-entry ?- "." promela-mode-syntax-table)
370 (modify-syntax-entry ?= "." promela-mode-syntax-table)
371 (modify-syntax-entry ?% "." promela-mode-syntax-table)
372 (modify-syntax-entry ?< "." promela-mode-syntax-table)
373 (modify-syntax-entry ?> "." promela-mode-syntax-table)
374 (modify-syntax-entry ?& "." promela-mode-syntax-table)
375 (modify-syntax-entry ?| "." promela-mode-syntax-table)
376 (modify-syntax-entry ?. "_" promela-mode-syntax-table)
377 (modify-syntax-entry ?_ "w" promela-mode-syntax-table)
378 (modify-syntax-entry ?\' "\"" promela-mode-syntax-table)
381 (defvar promela-mode-abbrev-table nil
382 "*Abbrev table in use in promela-mode buffers.")
383 (if promela-mode-abbrev-table
385 (define-abbrev-table 'promela-mode-abbrev-table
387 ;; Commented out for now - need to think about what abbrevs make sense
388 ;; ("assert" "ASSERT" promela-check-expansion 0)
389 ;; ("d_step" "D_STEP" promela-check-expansion 0)
390 ;; ("break" "BREAK" promela-check-expansion 0)
391 ;; ("do" "DO" promela-check-expansion 0)
392 ;; ("proctype" "PROCTYPE" promela-check-expansion 0)
395 (defvar promela-mode-map nil
396 "Keymap for promela-mode.")
399 (setq promela-mode-map (make-sparse-keymap))
400 (define-key promela-mode-map "\t" 'promela-indent-command)
401 (define-key promela-mode-map "\C-m" 'promela-newline-and-indent)
402 ;(define-key promela-mode-map 'backspace 'backward-delete-char-untabify)
403 (define-key promela-mode-map "\C-c\C-p" 'promela-beginning-of-block)
404 ;(define-key promela-mode-map "\C-c\C-n" 'promela-end-of-block)
405 (define-key promela-mode-map "\M-\C-a" 'promela-beginning-of-defun)
406 ;(define-key promela-mode-map "\M-\C-e" 'promela-end-of-defun)
407 (define-key promela-mode-map "\C-c(" 'promela-toggle-auto-match-delimiter)
408 (define-key promela-mode-map "{" 'promela-open-delimiter)
409 (define-key promela-mode-map "}" 'promela-close-delimiter)
410 (define-key promela-mode-map "(" 'promela-open-delimiter)
411 (define-key promela-mode-map ")" 'promela-close-delimiter)
412 (define-key promela-mode-map "[" 'promela-open-delimiter)
413 (define-key promela-mode-map "]" 'promela-close-delimiter)
414 (define-key promela-mode-map ";" 'promela-insert-and-indent)
415 (define-key promela-mode-map ":" 'promela-insert-and-indent)
417 ;; this is preliminary at best - use at your own risk:
418 (define-key promela-mode-map "\C-c\C-s" 'promela-syntax-check)
420 ;;(define-key promela-mode-map "\C-c\C-d" 'promela-mode-toggle-debug)
421 ;;(define-key promela-mode-map "\C-c\C-r" 'promela-mode-revert-buffer)
424 (defvar promela-matching-delimiter-alist
432 "List of pairs of matching open/close delimiters - for auto-insert")
435 ;; -------------------------------------------------------------------------
436 ;; Promela-mode itself
438 (defun promela-mode ()
439 "Major mode for editing PROMELA code.
442 Variables controlling indentation style:
444 Relative offset of lines within a block (`{') construct.
446 promela-selection-indent
447 Relative offset of option lines within a selection (`if')
448 or iteration (`do') construct.
450 promela-selection-option-indent
451 Relative offset of lines after/within options (`::') within
452 selection or iteration constructs.
455 Defines the desired comment column for comments to the right of text.
457 promela-tab-always-indent
458 Non-nil means TAB in PROMELA mode should always reindent the current
459 line, regardless of where in the line the point is when the TAB
462 promela-auto-match-delimiter
463 Non-nil means typing an open-delimiter (i.e. parentheses, brace,
464 quote, etc) should also insert the matching closing delmiter
467 Turning on PROMELA mode calls the value of the variable promela-mode-hook with
468 no args, if that value is non-nil.
471 (setq promela-mode-hook '(lambda ()
472 (setq promela-block-indent 2)
473 (setq promela-selection-indent 0)
474 (setq promela-selection-option-indent 2)
475 (local-set-key \"\\C-m\" 'promela-indent-newline-indent)
478 will indent block two steps, will make selection options aligned with DO/IF
479 and sub-option lines indent to a column after the `::'. Also, lines will
480 be reindented when you hit RETURN.
482 Note that promela-mode adhears to the font-lock \"standards\" and
483 defines several \"levels\" of fontification or colorization. The
484 default is fairly gaudy, so if you would prefer a bit less, please see
485 the documentation for the variable: `font-lock-maximum-decoration'.
488 (kill-all-local-variables)
489 (setq mode-name "Promela")
490 (setq major-mode 'promela-mode)
491 (use-local-map promela-mode-map)
492 (set-syntax-table promela-mode-syntax-table)
493 (setq local-abbrev-table promela-mode-abbrev-table)
495 ;; Make local variables
496 (make-local-variable 'case-fold-search)
497 (make-local-variable 'paragraph-start)
498 (make-local-variable 'paragraph-separate)
499 (make-local-variable 'paragraph-ignore-fill-prefix)
500 (make-local-variable 'indent-line-function)
501 (make-local-variable 'indent-region-function)
502 (make-local-variable 'parse-sexp-ignore-comments)
503 (make-local-variable 'comment-start)
504 (make-local-variable 'comment-end)
505 (make-local-variable 'comment-column)
506 (make-local-variable 'comment-start-skip)
507 (make-local-variable 'comment-indent-hook)
508 (make-local-variable 'defun-prompt-regexp)
509 (make-local-variable 'compile-command)
510 ;; Now set their values
511 (setq case-fold-search t
512 paragraph-start (concat "^$\\|" page-delimiter)
513 paragraph-separate paragraph-start
514 paragraph-ignore-fill-prefix t
515 indent-line-function 'promela-indent-command
516 ;;indent-region-function 'promela-indent-region
517 parse-sexp-ignore-comments t
521 comment-start-skip "/\\*+ *"
522 ;;comment-start-skip "/\\*+ *\\|// *"
523 ;;comment-indent-hook 'promela-comment-indent
524 defun-prompt-regexp promela-defun-prompt-regexp
527 ;; Turn on font-lock mode
528 ;; (and promela-font-lock-mode (font-lock-mode))
531 ;; Finally, run the hooks and be done.
532 (run-hooks 'promela-mode-hook))
535 ;; -------------------------------------------------------------------------
536 ;; Interactive functions
539 (defun promela-mode-version ()
540 "Print the current version of promela-mode in the minibuffer"
542 (message (concat "Promela-Mode: " promela-mode-version)))
544 (defun promela-beginning-of-block ()
545 "Move backward to start of containing block.
546 Containing block may be `{', `do' or `if' construct, or comment."
548 (goto-char (promela-find-start-of-containing-block-or-comment)))
550 (defun promela-beginning-of-defun (&optional arg)
551 "Move backward to the beginning of a defun.
552 With argument, do it that many times.
553 Negative arg -N means move forward to Nth following beginning of defun.
554 Returns t unless search stops due to beginning or end of buffer.
556 See also 'beginning-of-defun.
558 This is a Promela-mode specific version since default (in xemacs 19.16 and
559 NT-Emacs 20) don't seem to skip comments - they will stop inside them.
561 Also, this makes sure that the beginning of the defun is actually the
562 line which starts the proctype/init/etc., not just the open-brace."
564 (beginning-of-defun arg)
565 (if (not (looking-at promela-defun-prompt-regexp))
566 (re-search-backward promela-defun-prompt-regexp nil t))
567 (if (promela-inside-comment-p)
568 (goto-char (promela-find-start-of-containing-comment))))
570 (defun promela-indent-command ()
571 "Indent the current line as PROMELA code."
573 (if (and (not promela-tab-always-indent)
575 (skip-chars-backward " \t")
578 (promela-indent-line)))
580 (defun promela-newline-and-indent ()
581 "Promela-mode specific newline-and-indent which expands abbrevs before
582 running a regular newline-and-indent."
586 (newline-and-indent))
588 (defun promela-indent-newline-indent ()
589 "Promela-mode specific newline-and-indent which expands abbrevs and
590 indents the current line before running a regular newline-and-indent."
592 (save-excursion (promela-indent-command))
595 (newline-and-indent))
597 (defun promela-insert-and-indent ()
598 "Insert the last character typed and re-indent the current line"
600 (insert last-command-char)
601 (save-excursion (promela-indent-command)))
603 (defun promela-open-delimiter ()
604 "Inserts the open and matching close delimiters, indenting as appropriate."
606 (insert last-command-char)
607 (if (and promela-auto-match-delimiter (not (promela-inside-comment-p)))
609 (insert (cdr (assq last-command-char promela-matching-delimiter-alist)))
610 (promela-indent-command))))
612 (defun promela-close-delimiter ()
613 "Inserts and indents a close delimiter."
615 (insert last-command-char)
616 (if (not (promela-inside-comment-p))
617 (save-excursion (promela-indent-command))))
619 (defun promela-toggle-auto-match-delimiter ()
620 "Toggle auto-insertion of parens and other delimiters.
621 See variable `promela-auto-insert-matching-delimiter'"
623 (setq promela-auto-match-delimiter
624 (not promela-auto-match-delimiter))
625 (message (concat "Promela auto-insert matching delimiters "
626 (if promela-auto-match-delimiter
627 "enabled" "disabled"))))
630 ;; -------------------------------------------------------------------------
631 ;; Compilation/Verification functions
633 ;; all of this is in serious "beta" mode - don't trust it ;-)
635 promela-compile-command "spin "
636 promela-syntax-check-args "-a -v "
639 ;;(setq compilation-error-regexp-alist
640 ;; (append compilation-error-regexp-alist
641 ;; '(("spin: +line +\\([0-9]+\\) +\"\\([^\"]+\\)\"" 2 1))))
643 (defun promela-syntax-check ()
645 (compile (concat promela-compile-command
646 promela-syntax-check-args
650 ;; -------------------------------------------------------------------------
651 ;; Indentation support functions
653 (defun promela-indent-around-label ()
654 "Indent the current line as PROMELA code,
655 but make sure to consider the label at the beginning of the line."
657 (delete-horizontal-space) ; delete any leading whitespace
658 (if (not (looking-at "\\sw+:\\([ \t]*\\)"))
659 (error "promela-indent-around-label: no label on this line")
660 (goto-char (match-beginning 1))
661 (let* ((space (length (match-string 1)))
662 (indent (promela-calc-indent))
663 (wanted (max 0 (- indent (current-column)))))
664 (if (>= space wanted)
665 (delete-region (point) (+ (point) (- space wanted)))
666 (goto-char (+ (point) space))
667 (indent-to-column indent)))))
669 ;; Note that indentation is based ENTIRELY upon the indentation of the
670 ;; previous line(s), esp. the previous non-blank line and the line
671 ;; starting the current containgng block...
672 (defun promela-indent-line ()
673 "Indent the current line as PROMELA code.
674 Return the amount the by which the indentation changed."
676 (if (looking-at "[ \t]*\\sw+:")
677 (promela-indent-around-label)
678 (let ((indent (promela-calc-indent))
681 (pos (- (point-max) (point))))
683 (skip-chars-forward " \t")
684 (setq shift-amt (- indent (current-column)))
685 (if (zerop shift-amt)
686 (if (> (- (point-max) pos) (point))
687 (goto-char (- (point-max) pos)))
688 (delete-region beg (point))
690 (if (> (- (point-max) pos) (point))
691 (goto-char (- (point-max) pos))))
694 (defun promela-calc-indent ()
695 "Return the appropriate indentation for this line as an int."
698 (let* ((orig-point (point))
699 (state (promela-parse-partial-sexp))
700 (paren-depth (nth 0 state))
701 (paren-point (or (nth 1 state) 1))
702 (paren-char (char-after paren-point)))
703 ;;(what-cursor-position)
705 ;; Indent not-at-all - inside a string
707 (current-indentation))
708 ;; Indent inside a comment
710 (promela-calc-indent-within-comment))
711 ;; looking at a pre-processor directive - indent=0
712 ((looking-at "[ \t]*#\\(define\\|if\\(n?def\\)?\\|else\\|endif\\)")
714 ;; If we're not inside a "true" block (i.e. "{}"), then indent=0
715 ;; I think this is fair, since no (indentable) code in promela
716 ;; exists outside of a proctype or similar "{ .. }" structure.
719 ;; Indent relative to non curly-brace "paren"
720 ;; [ NOTE: I'm saving this, but don't use it any more.
721 ;; Now, we let parens be indented like curly braces
722 ;;((and (>= paren-depth 1) (not (char-equal ?\{ paren-char)))
723 ;; (goto-char paren-point)
724 ;; (1+ (current-column)))
726 ;; Last option: indent relative to contaning block(s)
728 (goto-char orig-point)
729 (promela-calc-indent-within-block paren-point))))))
731 (defun promela-calc-indent-within-block (&optional limit)
732 "Return the appropriate indentation for this line, assume within block.
733 with optional arg, limit search back to `limit'"
735 (let* ((stop (or limit 1))
736 (block-point (promela-find-start-of-containing-block stop))
737 (block-type (promela-block-type-after block-point))
738 (indent-point (point))
739 (indent-type (promela-block-type-after indent-point)))
740 (if (not block-type) 0
741 ;;(message "paren: %d (%d); block: %s (%d); indent: %s (%d); stop: %d"
742 ;; paren-depth paren-point block-type block-point
743 ;; indent-type indent-point stop)
744 (goto-char block-point)
746 ;; Indent (options) inside "if" or "do"
747 ((memq block-type '(selection iteration))
748 (if (re-search-forward "\\(do\\|if\\)[ \t]*::" indent-point t)
749 (- (current-column) 2)
750 (+ (current-column) promela-selection-indent)))
751 ;; indent (generic code) inside "::" option
752 ((eq 'option block-type)
753 (if (and (not indent-type)
754 (re-search-forward "::.*->[ \t]*\\sw"
755 (save-excursion (end-of-line) (point))
757 (1- (current-column))
758 (+ (current-column) promela-selection-option-indent))
760 ;; indent code inside "{"
761 ((eq 'block block-type)
763 ;; if we are indenting the end of a block,
764 ;; use indentation of start-of-block
765 ((equal 'block-end indent-type)
766 (current-indentation))
767 ;; if the start of the code inside the block is not at eol
768 ;; then indent to the same column as the block start +some
769 ;; [ but ignore comments after "{" ]
770 ((and (not (promela-effective-eolp (1+ (point))))
771 (not (looking-at "{[ \t]*/\\*")))
772 (forward-char) ; skip block-start
773 (skip-chars-forward "{ \t") ; skip whitespace, if any
775 ;; anything else we indent +promela-block-indent from
776 ;; the indentation of the start of block (where we are now)
778 (+ (current-indentation)
779 promela-block-indent))))
780 ;; dunno what kind of block this is - sound an error
782 (error "promela-calc-indent-within-block: unknown block type: %s" block-type)
783 (current-indentation)))))))
785 (defun promela-calc-indent-within-comment ()
786 "Return the indentation amount for line, assuming that the
787 current line is to be regarded as part of a block comment."
790 (skip-chars-forward " \t")
791 (let ((indenting-end-of-comment (looking-at "\\*/"))
792 (indenting-blank-line (eolp)))
793 ;; if line is NOT blank and next char is NOT a "*'
794 (if (not (or indenting-blank-line (= (following-char) ?\*)))
795 ;; leave indent alone
797 ;; otherwise look back for _PREVIOUS_ possible nested comment start
798 (let ((comment-start (save-excursion
799 (re-search-backward comment-start-skip))))
800 ;; and see if there is an appropriate middle-comment "*"
801 (if (re-search-backward "^[ \t]+\\*" comment-start t)
802 (current-indentation)
803 ;; guess not, so indent relative to comment start
804 (goto-char comment-start)
805 (if indenting-end-of-comment
807 (1+ (current-column)))))))))
810 ;; -------------------------------------------------------------------------
811 ;; Misc other support functions
813 (defun promela-parse-partial-sexp (&optional start limit)
814 "Return the partial parse state of current defun or from optional start
817 (let ((end (or limit (point))))
820 (promela-beginning-of-defun))
821 (parse-partial-sexp (point) end))))
823 ;;(defun promela-at-end-of-block-p ()
824 ;; "Return t if cursor is at the end of a promela block"
826 ;; (let ((eol (progn (end-of-line) (point))))
827 ;; (beginning-of-line)
828 ;; (skip-chars-forward " \t")
829 ;; ;;(re-search-forward "\\(}\\|\\b\\(od\\|fi\\)\\b\\)" eol t))))
830 ;; (looking-at "[ \t]*\\(od\\|fi\\)\\b"))))
832 (defun promela-inside-comment-p ()
833 "Check if the point is inside a comment block."
835 (let ((origpoint (point))
838 (while (> origpoint (point))
839 (setq state (parse-partial-sexp (point) origpoint 0)))
842 (defun promela-inside-comment-or-string-p ()
843 "Check if the point is inside a comment or a string."
845 (let ((origpoint (point))
848 (while (> origpoint (point))
849 (setq state (parse-partial-sexp (point) origpoint 0)))
850 (or (nth 3 state) (nth 4 state)))))
853 (defun promela-effective-eolp (&optional point)
854 "Check if we are at the effective end-of-line, ignoring whitespace"
856 (if point (goto-char point))
857 (skip-chars-forward " \t")
860 (defun promela-check-expansion ()
861 "If abbrev was made within a comment or a string, de-abbrev!"
862 (if promela-inside-comment-or-string-p
865 (defun promela-block-type-after (&optional point)
866 "Return the type of block after current point or parameter as a symbol.
867 Return one of 'iteration `do', 'selection `if', 'option `::',
868 'block `{' or `}' or nil if none of the above match."
870 (goto-char (or point (point)))
871 (skip-chars-forward " \t")
873 ((looking-at "do\\b") 'iteration)
874 ;;((looking-at "od\\b") 'iteration-end)
875 ((looking-at "if\\b") 'selection)
876 ;;((looking-at "fi\\b") 'selection-end)
877 ((looking-at "::") 'option)
878 ((looking-at "[{(]") 'block)
879 ((looking-at "[})]") 'block-end)
882 (defun promela-find-start-of-containing-comment (&optional limit)
883 "Return the start point of the containing comment block.
884 Stop at `limit' or beginning of buffer."
885 (let ((stop (or limit 1)))
887 (while (and (>= (point) stop)
888 (nth 4 (promela-parse-partial-sexp)))
889 (re-search-backward comment-start-skip stop t))
892 (defun promela-find-start-of-containing-block (&optional limit)
893 "Return the start point of the containing `do', `if', `::' or
894 `{' block or containing comment.
895 Stop at `limit' or beginning of buffer."
897 (skip-chars-forward " \t")
898 (let* ((type (promela-block-type-after))
900 (save-excursion (promela-beginning-of-defun) (point))))
901 (state (promela-parse-partial-sexp stop))
902 (level (if (looking-at "\\(od\\|fi\\)\\b")
904 (if (zerop (nth 0 state)) 0 1))))
905 ;;(message "find-start-of-containing-block: type: %s; level %d; stop %d"
907 (while (and (> (point) stop) (not (zerop level)))
909 "\\({\\|}\\|::\\|\\b\\(do\\|od\\|if\\|fi\\)\\b\\)"
911 ;;(message "looking from %d back-to %d" (point) stop)
912 (setq state (promela-parse-partial-sexp stop))
914 (cond ((or (nth 3 state) (nth 4 state)) 0)
915 ((and (= 1 level) (looking-at "::")
916 (not (equal type 'option))) -1)
917 ((looking-at "\\({\\|\\(do\\|if\\)\\b\\)") -1)
918 ((looking-at "\\(}\\|\\(od\\|fi\\)\\b\\)") +1)
922 (defun promela-find-start-of-containing-block-or-comment (&optional limit)
923 "Return the start point of the containing comment or
924 the start of the containing `do', `if', `::' or `{' block.
925 Stop at limit or beginning of buffer."
926 (if (promela-inside-comment-p)
927 (promela-find-start-of-containing-comment limit)
928 (promela-find-start-of-containing-block limit)))
930 ;; -------------------------------------------------------------------------
933 ;; (defun promela-mode-toggle-debug ()
935 ;; (make-local-variable 'debug-on-error)
936 ;; (setq debug-on-error (not debug-on-error)))
938 ;;(defun promela-mode-revert-buffer ()
940 ;; (revert-buffer t t))
942 ;; -------------------------------------------------------------------------
945 (provide 'promela-mode)
948 ;;----------------------------------------------------------------------
951 ;; $Log: promela-mode.el,v $
952 ;; Revision 1.11 2001/07/09 18:36:45 engstrom
953 ;; - added comments on use of font-lock-maximum-decoration
954 ;; - moved basic preprocess directive fontification to "level 2"
956 ;; Revision 1.10 2001/05/22 16:29:59 engstrom
957 ;; - fixed error introduced in fontification levels stuff (xemacs only)
959 ;; Revision 1.9 2001/05/22 16:21:29 engstrom
960 ;; - commented out the compilation / syntax check stuff for now
962 ;; Revision 1.8 2001/05/22 16:18:49 engstrom
963 ;; - Munched history in preparation for first non-Honeywell release
964 ;; - Added "levels" of fontification to be controlled by the std. variable:
965 ;; 'font-lock-maximum-decoration'
967 ;; Revision 1.7 2001/04/20 01:41:46 engstrom
968 ;; Revision 1.6 2001/04/06 23:57:18 engstrom
969 ;; Revision 1.5 2001/04/04 20:04:15 engstrom
970 ;; Revision 1.4 2001/03/15 02:22:18 engstrom
971 ;; Revision 1.3 2001/03/09 19:39:51 engstrom
972 ;; Revision 1.2 2001/03/01 18:07:47 engstrom
973 ;; Revision 1.1 2001/02/01 xx:xx:xx engstrom
974 ;; migrated to CVS versioning...
976 ;; 99-10-04 V0.4 EDE Fixed bug in end-of-block indentation
977 ;; Simplified indentation code significantly
978 ;; 99-09-2x V0.3 EDE Hacked on indentation more while at FM'99
979 ;; 99-09-16 V0.2 EDE Hacked, hacked, hacked on indentation
980 ;; 99-04-01 V0.1 EDE Introduced (less-than) half-baked indentation
981 ;; 98-11-05 V0.0 EDE Created - much code stolen from rexx-mode.el
982 ;; Mostly just a fontification mode -
983 ;; (indentation is HARD ;-)
985 ;; EOF promela-mode.el