aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2010-11-11 16:06:15 -0500
committerStefan Monnier2010-11-11 16:06:15 -0500
commitcbf83ce9f9163ef95b62c778f4d3efa3cc465cfe (patch)
tree0afbfc0bbcb18c98499e5683b7823cae82585fd2
parent90639ceacd6842d168bad3a18090a00b8b140c87 (diff)
downloademacs-cbf83ce9f9163ef95b62c778f4d3efa3cc465cfe.tar.gz
emacs-cbf83ce9f9163ef95b62c778f4d3efa3cc465cfe.zip
* lisp/progmodes/modula2.el: Use SMIE and skeleton.
(m2-mode-syntax-table): (*..*) can be nested. Add //...\n. Fix paren syntax. (m2-mode-map): Remove LF and TAB bindings. (m2-indent): Add safety property. (m2-smie-grammar): New var. (m2-smie-refine-colon, m2-smie-refine-of, m2-smie-backward-token) (m2-smie-forward-token, m2-smie-refine-semi, m2-smie-rules): New funs. (m2-mode): Use define-derived-mode. (m2-newline, m2-tab): Remove. (m2-begin, m2-case, m2-definition, m2-else, m2-for, m2-header) (m2-if, m2-loop, m2-module, m2-or, m2-procedure, m2-with, m2-record) (m2-stdio, m2-type, m2-until, m2-var, m2-while, m2-export) (m2-import): Use define-skeleton. * test/indent/modula2.mod: New file.
-rw-r--r--etc/NEWS2
-rw-r--r--lisp/ChangeLog21
-rw-r--r--lisp/progmodes/modula2.el599
-rw-r--r--test/ChangeLog4
-rw-r--r--test/indent/modula2.mod53
5 files changed, 404 insertions, 275 deletions
diff --git a/etc/NEWS b/etc/NEWS
index 2acca998e3a..aab6cf98eb0 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -289,6 +289,8 @@ set `x-select-enable-clipboard' to nil.
289 289
290* Changes in Specialized Modes and Packages in Emacs 24.1 290* Changes in Specialized Modes and Packages in Emacs 24.1
291 291
292** Modula-2 mode provides auto-indentation.
293
292** latex-electric-env-pair-mode keeps \begin..\end matched on the fly. 294** latex-electric-env-pair-mode keeps \begin..\end matched on the fly.
293 295
294** FIXME: xdg-open for browse-url and reportbug, 2010/08. 296** FIXME: xdg-open for browse-url and reportbug, 2010/08.
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 5ba650c43b0..70452c73254 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,20 @@
12010-11-11 Stefan Monnier <monnier@iro.umontreal.ca>
2
3 * progmodes/modula2.el: Use SMIE and skeleton.
4 (m2-mode-syntax-table): (*..*) can be nested.
5 Add //...\n. Fix paren syntax.
6 (m2-mode-map): Remove LF and TAB bindings.
7 (m2-indent): Add safety property.
8 (m2-smie-grammar): New var.
9 (m2-smie-refine-colon, m2-smie-refine-of, m2-smie-backward-token)
10 (m2-smie-forward-token, m2-smie-refine-semi, m2-smie-rules): New funs.
11 (m2-mode): Use define-derived-mode.
12 (m2-newline, m2-tab): Remove.
13 (m2-begin, m2-case, m2-definition, m2-else, m2-for, m2-header)
14 (m2-if, m2-loop, m2-module, m2-or, m2-procedure, m2-with, m2-record)
15 (m2-stdio, m2-type, m2-until, m2-var, m2-while, m2-export)
16 (m2-import): Use define-skeleton.
17
12010-11-11 Glenn Morris <rgm@gnu.org> 182010-11-11 Glenn Morris <rgm@gnu.org>
2 19
3 * obsolete/lucid.el: Don't warn about any CL functions in this file. 20 * obsolete/lucid.el: Don't warn about any CL functions in this file.
@@ -37,8 +54,8 @@
37 54
382010-11-10 Chong Yidong <cyd@stupidchicken.com> 552010-11-10 Chong Yidong <cyd@stupidchicken.com>
39 56
40 * emacs-lisp/package.el (package-read-all-archive-contents): Reset 57 * emacs-lisp/package.el (package-read-all-archive-contents):
41 package-archive-contents to nil before re-reading. 58 Reset package-archive-contents to nil before re-reading.
42 59
432010-11-10 Brandon Craig Rhodes <brandon@rhodesmill.org> 602010-11-10 Brandon Craig Rhodes <brandon@rhodesmill.org>
44 61
diff --git a/lisp/progmodes/modula2.el b/lisp/progmodes/modula2.el
index 3d2af5e217e..c6ab5347065 100644
--- a/lisp/progmodes/modula2.el
+++ b/lisp/progmodes/modula2.el
@@ -22,6 +22,8 @@
22 22
23;;; Code: 23;;; Code:
24 24
25(require 'smie)
26
25(defgroup modula2 nil 27(defgroup modula2 nil
26 "Major mode for editing Modula-2 code." 28 "Major mode for editing Modula-2 code."
27 :link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces) 29 :link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces)
@@ -29,7 +31,22 @@
29 :group 'languages) 31 :group 'languages)
30 32
31;;; Added by Tom Perrine (TEP) 33;;; Added by Tom Perrine (TEP)
32(defvar m2-mode-syntax-table nil 34(defvar m2-mode-syntax-table
35 (let ((table (make-syntax-table)))
36 (modify-syntax-entry ?\\ "\\" table)
37 (modify-syntax-entry ?/ ". 12" table)
38 (modify-syntax-entry ?\n ">" table)
39 (modify-syntax-entry ?\( "()1" table)
40 (modify-syntax-entry ?\) ")(4" table)
41 (modify-syntax-entry ?* ". 23nb" table)
42 (modify-syntax-entry ?+ "." table)
43 (modify-syntax-entry ?- "." table)
44 (modify-syntax-entry ?= "." table)
45 (modify-syntax-entry ?% "." table)
46 (modify-syntax-entry ?< "." table)
47 (modify-syntax-entry ?> "." table)
48 (modify-syntax-entry ?\' "\"" table)
49 table)
33 "Syntax table in use in Modula-2 buffers.") 50 "Syntax table in use in Modula-2 buffers.")
34 51
35(defcustom m2-compile-command "m2c" 52(defcustom m2-compile-command "m2c"
@@ -52,26 +69,10 @@
52 :type 'integer 69 :type 'integer
53 :group 'modula2) 70 :group 'modula2)
54 71
55(if m2-mode-syntax-table
56 ()
57 (let ((table (make-syntax-table)))
58 (modify-syntax-entry ?\\ "\\" table)
59 (modify-syntax-entry ?\( ". 1" table)
60 (modify-syntax-entry ?\) ". 4" table)
61 (modify-syntax-entry ?* ". 23" table)
62 (modify-syntax-entry ?+ "." table)
63 (modify-syntax-entry ?- "." table)
64 (modify-syntax-entry ?= "." table)
65 (modify-syntax-entry ?% "." table)
66 (modify-syntax-entry ?< "." table)
67 (modify-syntax-entry ?> "." table)
68 (modify-syntax-entry ?\' "\"" table)
69 (setq m2-mode-syntax-table table)))
70
71;;; Added by TEP 72;;; Added by TEP
72(defvar m2-mode-map 73(defvar m2-mode-map
73 (let ((map (make-sparse-keymap))) 74 (let ((map (make-sparse-keymap)))
74 (define-key map "\^i" 'm2-tab) 75 ;; FIXME: Many of those bindings are contrary to coding conventions.
75 (define-key map "\C-cb" 'm2-begin) 76 (define-key map "\C-cb" 'm2-begin)
76 (define-key map "\C-cc" 'm2-case) 77 (define-key map "\C-cc" 'm2-case)
77 (define-key map "\C-cd" 'm2-definition) 78 (define-key map "\C-cd" 'm2-definition)
@@ -94,7 +95,6 @@
94 (define-key map "\C-cy" 'm2-import) 95 (define-key map "\C-cy" 'm2-import)
95 (define-key map "\C-c{" 'm2-begin-comment) 96 (define-key map "\C-c{" 'm2-begin-comment)
96 (define-key map "\C-c}" 'm2-end-comment) 97 (define-key map "\C-c}" 'm2-end-comment)
97 (define-key map "\C-j" 'm2-newline)
98 (define-key map "\C-c\C-z" 'suspend-emacs) 98 (define-key map "\C-c\C-z" 'suspend-emacs)
99 (define-key map "\C-c\C-v" 'm2-visit) 99 (define-key map "\C-c\C-v" 'm2-visit)
100 (define-key map "\C-c\C-t" 'm2-toggle) 100 (define-key map "\C-c\C-t" 'm2-toggle)
@@ -107,9 +107,185 @@
107 "*This variable gives the indentation in Modula-2-Mode." 107 "*This variable gives the indentation in Modula-2-Mode."
108 :type 'integer 108 :type 'integer
109 :group 'modula2) 109 :group 'modula2)
110(put 'm2-indent 'safe-local-variable
111 (lambda (v) (or (null v) (integerp v))))
112
113(defconst m2-smie-grammar
114 ;; An official definition can be found as "M2R10.pdf". This grammar does
115 ;; not really follow it, for lots of technical reasons, but it can still be
116 ;; useful to refer to it.
117 (smie-prec2->grammar
118 (smie-merge-prec2s
119 (smie-bnf->prec2
120 '((range) (id) (epsilon)
121 (fields (fields ";" fields) (ids ":" type))
122 (proctype (id ":" type))
123 (type ("RECORD" fields "END")
124 ("POINTER" "TO" type)
125 ;; The PROCEDURE type is indistinguishable from the beginning
126 ;; of a PROCEDURE definition, so we need a "PROCEDURE-type" to
127 ;; prevent SMIE from trying to find the matching END.
128 ("PROCEDURE-type" proctype)
129 ;; OF's right hand side should bind tighter than ; for array
130 ;; types, but should bind less tight than | which itself binds
131 ;; less tight than ;. So we use two distinct OFs.
132 ("SET" "OF-type" id)
133 ("ARRAY" range "OF-type" type))
134 (args ("(" fargs ")"))
135 ;; VAR has lower precedence than ";" in formal args, but not
136 ;; in declarations. So we use "VAR-arg" for the formal arg case.
137 (farg (ids ":" type) ("CONST-arg" farg) ("VAR-arg" farg))
138 (fargs (fargs ";" fargs) (farg))
139 ;; Handling of PROCEDURE in decls is problematic: we'd want
140 ;; TYPE/CONST/VAR/PROCEDURE's parent to be any previous
141 ;; CONST/TYPE/VAR/PROCEDURE, but we also want PROCEDURE to be an opener
142 ;; (so that its END has PROCEDURE as its parent). So instead, we treat
143 ;; the last ";" in those blocks as a separator (we call it ";-block").
144 ;; FIXME: This means that "TYPE \n VAR" is not indented properly
145 ;; because there's no ";-block" between the two.
146 (decls (decls ";-block" decls)
147 ("TYPE" typedecls) ("CONST" constdecls) ("VAR" vardecls)
148 ;; END is usually a closer, but not quite for PROCEDURE...END.
149 ;; We could use "END-proc" for the procedure case, but
150 ;; I preferred to just pretend PROCEDURE's END is the closer.
151 ("PROCEDURE" decls "BEGIN" insts "END") ;END-proc id
152 ("PROCEDURE" decls "BEGIN" insts "FINALLY" insts "END")
153 ("PROCEDURE" decls "FORWARD")
154 ;; ("IMPLEMENTATION" epsilon "MODULE" decls
155 ;; "BEGIN" insts "FINALLY" insts "END")
156 )
157 (typedecls (typedecls ";" typedecls) (id "=" type))
158 (ids (ids "," ids))
159 (vardecls (vardecls ";" vardecls) (ids ":" type))
160 (constdecls (constdecls ";" constdecls) (id "=" exp))
161 (exp (id "-anchor-" id) ("(" exp ")"))
162 (caselabel (caselabel ".." caselabel) (caselabel "," caselabel))
163 ;; : for types binds tighter than ;, but the : for case labels binds
164 ;; less tight, so have to use two different :.
165 (cases (cases "|" cases) (caselabel ":-case" insts))
166 (forspec (exp "TO" exp))
167 (insts (insts ";" insts)
168 (id ":=" exp)
169 ("CASE" exp "OF" cases "END")
170 ("CASE" exp "OF" cases "ELSE" insts "END")
171 ("LOOP" insts "END")
172 ("WITH" exp "DO" insts "END")
173 ("REPEAT" insts "UNTIL" exp)
174 ("WHILE" exp "DO" insts "END")
175 ("FOR" forspec "DO" insts "END")
176 ("IF" exp "THEN" insts "END")
177 ("IF" exp "THEN" insts "ELSE" insts "END")
178 ("IF" exp "THEN" insts
179 "ELSIF" exp "THEN" insts "ELSE" insts "END")
180 ("IF" exp "THEN" insts
181 "ELSIF" exp "THEN" insts
182 "ELSIF" exp "THEN" insts "ELSE" insts "END"))
183 ;; This category is not used anywhere, but it adds some constraints that
184 ;; try to reduce the harm when an OF-type is not properly recognized.
185 (error-OF ("ARRAY" range "OF" type) ("SET" "OF" id)))
186 '((assoc ";")) '((assoc ";-block")) '((assoc "|"))
187 ;; For case labels.
188 '((assoc ",") (assoc ".."))
189 ;; '((assoc "TYPE" "CONST" "VAR" "PROCEDURE"))
190 )
191 (smie-precs->prec2
192 '((nonassoc "-anchor-" "=")
193 (nonassoc "<" "<=" ">=" ">" "<>" "#" "IN")
194 (assoc "OR" "+" "-")
195 (assoc "AND" "MOD" "DIV" "REM" "*" "/" "&")
196 (nonassoc "NOT" "~")
197 (left "." "^")
198 ))
199 )))
200
201(defun m2-smie-refine-colon ()
202 (let ((res nil))
203 (while (not res)
204 (let ((tok (smie-default-backward-token)))
205 (cond
206 ((zerop (length tok))
207 (let ((forward-sexp-function nil))
208 (condition-case nil
209 (forward-sexp -1)
210 (scan-error (setq res ":")))))
211 ((member tok '("|" "OF" "..")) (setq res ":-case"))
212 ((member tok '(":" "END" ";" "BEGIN" "VAR" "RECORD" "PROCEDURE"))
213 (setq res ":")))))
214 res))
215
216(defun m2-smie-refine-of ()
217 (let ((tok (smie-default-backward-token)))
218 (when (zerop (length tok))
219 (let ((forward-sexp-function nil))
220 (condition-case nil
221 (backward-sexp 1)
222 (scan-error nil))
223 (setq tok (smie-default-backward-token))))
224 (if (member tok '("ARRAY" "SET"))
225 "OF-type" "OF")))
226
227(defun m2-smie-refine-semi ()
228 (forward-comment (point-max))
229 (if (looking-at (regexp-opt '("PROCEDURE" "TYPE" "VAR" "CONST" "BEGIN")))
230 ";-block" ";"))
231
232;; FIXME: "^." are two tokens, not one.
233(defun m2-smie-forward-token ()
234 (pcase (smie-default-forward-token)
235 (`"VAR" (if (zerop (car (syntax-ppss))) "VAR" "VAR-arg"))
236 (`"CONST" (if (zerop (car (syntax-ppss))) "CONST" "CONST-arg"))
237 (`";" (save-excursion (m2-smie-refine-semi)))
238 (`"OF" (save-excursion (forward-char -2) (m2-smie-refine-of)))
239 (`":" (save-excursion (forward-char -1) (m2-smie-refine-colon)))
240 ;; (`"END" (if (and (looking-at "[ \t\n]*\\(\\(?:\\sw\\|\\s_\\)+\\)")
241 ;; (not (assoc (match-string 1) m2-smie-grammar)))
242 ;; "END-proc" "END"))
243 (token token)))
244
245(defun m2-smie-backward-token ()
246 (pcase (smie-default-backward-token)
247 (`"VAR" (if (zerop (car (syntax-ppss))) "VAR" "VAR-arg"))
248 (`"CONST" (if (zerop (car (syntax-ppss))) "CONST" "CONST-arg"))
249 (`";" (save-excursion (forward-char 1) (m2-smie-refine-semi)))
250 (`"OF" (save-excursion (m2-smie-refine-of)))
251 (`":" (save-excursion (m2-smie-refine-colon)))
252 ;; (`"END" (if (and (looking-at "\\sw+[ \t\n]+\\(\\(?:\\sw\\|\\s_\\)+\\)")
253 ;; (not (assoc (match-string 1) m2-smie-grammar)))
254 ;; "END-proc" "END"))
255 (token token)))
256
257(defun m2-smie-rules (kind token)
258 ;; FIXME: Apparently, the usual indentation convention is something like:
259 ;;
260 ;; TYPE t1 = bar;
261 ;; VAR x : INTEGER;
262 ;; PROCEDURE f ();
263 ;; TYPE t2 = foo;
264 ;; PROCEDURE g ();
265 ;; BEGIN blabla END;
266 ;; VAR y : type;
267 ;; BEGIN blibli END
268 ;;
269 ;; This is inconsistent with the actual structure of the code in 2 ways:
270 ;; - The inner VAR/TYPE are indented just like the outer VAR/TYPE.
271 ;; - The inner PROCEDURE is not aligned with its VAR/TYPE siblings.
272 (pcase (cons kind token)
273 (`(:elem . basic) m2-indent)
274 (`(:after . ":=") (or m2-indent smie-indent-basic))
275 (`(:after . ,(or `"CONST" `"VAR" `"TYPE"))
276 (or m2-indent smie-indent-basic))
277 ;; (`(:before . ,(or `"VAR" `"TYPE" `"CONST"))
278 ;; (if (smie-rule-parent-p "PROCEDURE") 0))
279 (`(:after . ";-block")
280 (if (smie-rule-parent-p "PROCEDURE")
281 (smie-rule-parent (or m2-indent smie-indent-basic))))
282 (`(:before . "|") (smie-rule-separator kind))
283 ))
110 284
111;;;###autoload 285;;;###autoload
112(defun modula-2-mode () 286(defalias 'modula-2-mode 'm2-mode)
287;;;###autoload
288(define-derived-mode m2-mode prog-mode "Modula-2"
113 "This is a mode intended to support program development in Modula-2. 289 "This is a mode intended to support program development in Modula-2.
114All control constructs of Modula-2 can be reached by typing C-c 290All control constructs of Modula-2 can be reached by typing C-c
115followed by the first character of the construct. 291followed by the first character of the construct.
@@ -132,46 +308,23 @@ followed by the first character of the construct.
132 `m2-indent' controls the number of spaces for each indentation. 308 `m2-indent' controls the number of spaces for each indentation.
133 `m2-compile-command' holds the command to compile a Modula-2 program. 309 `m2-compile-command' holds the command to compile a Modula-2 program.
134 `m2-link-command' holds the command to link a Modula-2 program." 310 `m2-link-command' holds the command to link a Modula-2 program."
135 (interactive)
136 (kill-all-local-variables)
137 (use-local-map m2-mode-map)
138 (setq major-mode 'modula-2-mode)
139 (setq mode-name "Modula-2")
140 (make-local-variable 'comment-column)
141 (setq comment-column 41)
142 (make-local-variable 'm2-end-comment-column) 311 (make-local-variable 'm2-end-comment-column)
143 (set-syntax-table m2-mode-syntax-table) 312
144 (make-local-variable 'paragraph-start) 313 (set (make-local-variable 'paragraph-start) (concat "$\\|" page-delimiter))
145 (setq paragraph-start (concat "$\\|" page-delimiter)) 314 (set (make-local-variable 'paragraph-separate) paragraph-start)
146 (make-local-variable 'paragraph-separate) 315 (set (make-local-variable 'paragraph-ignore-fill-prefix) t)
147 (setq paragraph-separate paragraph-start) 316 (set (make-local-variable 'comment-start) "(* ")
148 (make-local-variable 'paragraph-ignore-fill-prefix) 317 (set (make-local-variable 'comment-end) " *)")
149 (setq paragraph-ignore-fill-prefix t) 318 (set (make-local-variable 'comment-start-skip) "\\(?:(\\*+\\|//+\\) *")
150; (make-local-variable 'indent-line-function) 319 (set (make-local-variable 'parse-sexp-ignore-comments) t)
151; (setq indent-line-function 'c-indent-line) 320 (set (make-local-variable 'font-lock-defaults)
152 (make-local-variable 'require-final-newline)
153 (setq require-final-newline mode-require-final-newline)
154 (make-local-variable 'comment-start)
155 (setq comment-start "(* ")
156 (make-local-variable 'comment-end)
157 (setq comment-end " *)")
158 (make-local-variable 'comment-column)
159 (setq comment-column 41)
160 (make-local-variable 'comment-start-skip)
161 (setq comment-start-skip "/\\*+ *")
162 (make-local-variable 'comment-indent-function)
163 (setq comment-indent-function 'c-comment-indent)
164 (make-local-variable 'parse-sexp-ignore-comments)
165 (setq parse-sexp-ignore-comments t)
166 (make-local-variable 'font-lock-defaults)
167 (setq font-lock-defaults
168 '((m3-font-lock-keywords 321 '((m3-font-lock-keywords
169 m3-font-lock-keywords-1 m3-font-lock-keywords-2) 322 m3-font-lock-keywords-1 m3-font-lock-keywords-2)
170 nil nil ((?_ . "w") (?. . "w") (?< . ". 1") (?> . ". 4")) nil 323 nil nil ((?_ . "w") (?. . "w") (?< . ". 1") (?> . ". 4")) nil
171 ;; Obsoleted by Emacs 19.35 parse-partial-sexp's COMMENTSTOP.
172 ;(font-lock-comment-start-regexp . "(\\*")
173 )) 324 ))
174 (run-mode-hooks 'm2-mode-hook)) 325 (smie-setup m2-smie-grammar #'m2-smie-rules
326 :forward-token #'m2-smie-forward-token
327 :backward-token #'m2-smie-backward-token))
175 328
176;; Regexps written with help from Ron Forrester <ron@orcad.com> 329;; Regexps written with help from Ron Forrester <ron@orcad.com>
177;; and Spencer Allain <sallain@teknowledge.com>. 330;; and Spencer Allain <sallain@teknowledge.com>.
@@ -257,231 +410,131 @@ followed by the first character of the construct.
257(defvar m2-font-lock-keywords m2-font-lock-keywords-1 410(defvar m2-font-lock-keywords m2-font-lock-keywords-1
258 "Default expressions to highlight in Modula-2 modes.") 411 "Default expressions to highlight in Modula-2 modes.")
259 412
260(defun m2-newline () 413(define-skeleton m2-begin
261 "Insert a newline and indent following line like previous line."
262 (interactive)
263 (let ((hpos (current-indentation)))
264 (newline)
265 (indent-to hpos)))
266
267(defun m2-tab ()
268 "Indent to next tab stop."
269 (interactive)
270 (indent-to (* (1+ (/ (current-indentation) m2-indent)) m2-indent)))
271
272(defun m2-begin ()
273 "Insert a BEGIN keyword and indent for the next line." 414 "Insert a BEGIN keyword and indent for the next line."
274 (interactive) 415 nil
275 (insert "BEGIN") 416 \n "BEGIN" > \n)
276 (m2-newline)
277 (m2-tab))
278 417
279(defun m2-case () 418(define-skeleton m2-case
280 "Build skeleton CASE statement, prompting for the <expression>." 419 "Build skeleton CASE statement, prompting for the <expression>."
281 (interactive) 420 "Case-Expression: "
282 (let ((name (read-string "Case-Expression: "))) 421 \n "CASE " str " OF" > \n _ \n "END (* " str " *);" > \n)
283 (insert "CASE " name " OF") 422
284 (m2-newline) 423(define-skeleton m2-definition
285 (m2-newline)
286 (insert "END (* case " name " *);"))
287 (end-of-line 0)
288 (m2-tab))
289
290(defun m2-definition ()
291 "Build skeleton DEFINITION MODULE, prompting for the <module name>." 424 "Build skeleton DEFINITION MODULE, prompting for the <module name>."
292 (interactive) 425 "Name: "
293 (insert "DEFINITION MODULE ") 426 \n "DEFINITION MODULE " str ";" > \n \n _ \n \n "END " str "." > \n)
294 (let ((name (read-string "Name: ")))
295 (insert name ";\n\n\n\nEND " name ".\n"))
296 (forward-line -3))
297 427
298(defun m2-else () 428(define-skeleton m2-else
299 "Insert ELSE keyword and indent for next line." 429 "Insert ELSE keyword and indent for next line."
300 (interactive) 430 nil
301 (m2-newline) 431 \n "ELSE" > \n)
302 (backward-delete-char-untabify m2-indent ())
303 (insert "ELSE")
304 (m2-newline)
305 (m2-tab))
306 432
307(defun m2-for () 433(define-skeleton m2-for
308 "Build skeleton FOR loop statement, prompting for the loop parameters." 434 "Build skeleton FOR loop statement, prompting for the loop parameters."
309 (interactive) 435 "Loop Initializer: "
310 (insert "FOR ") 436 ;; FIXME: this seems to be lacking a "<var> :=".
311 (let ((name (read-string "Loop Initializer: ")) limit by) 437 \n "FOR " str " TO "
312 (insert name " TO ") 438 (setq v1 (read-string "Limit: "))
313 (setq limit (read-string "Limit: ")) 439 (let ((by (read-string "Step: ")))
314 (insert limit)
315 (setq by (read-string "Step: "))
316 (if (not (string-equal by "")) 440 (if (not (string-equal by ""))
317 (insert " BY " by)) 441 (concat " BY " by)))
318 (insert " DO") 442 " DO" > \n _ \n "END (* for " str " to " v1 " *);" > \n)
319 (m2-newline)
320 (m2-newline)
321 (insert "END (* for " name " to " limit " *);"))
322 (end-of-line 0)
323 (m2-tab))
324
325(defun m2-header ()
326 "Insert a comment block containing the module title, author, etc."
327 (interactive)
328 (insert "(*\n Title: \t")
329 (insert (read-string "Title: "))
330 (insert "\n Created:\t")
331 (insert (current-time-string))
332 (insert "\n Author: \t")
333 (insert (user-full-name))
334 (insert (concat "\n\t\t<" (user-login-name) "@" (system-name) ">\n"))
335 (insert "*)\n\n"))
336
337(defun m2-if ()
338 "Insert skeleton IF statement, prompting for <boolean-expression>."
339 (interactive)
340 (insert "IF ")
341 (let ((thecondition (read-string "<boolean-expression>: ")))
342 (insert thecondition " THEN")
343 (m2-newline)
344 (m2-newline)
345 (insert "END (* if " thecondition " *);"))
346 (end-of-line 0)
347 (m2-tab))
348
349(defun m2-loop ()
350 "Build skeleton LOOP (with END)."
351 (interactive)
352 (insert "LOOP")
353 (m2-newline)
354 (m2-newline)
355 (insert "END (* loop *);")
356 (end-of-line 0)
357 (m2-tab))
358
359(defun m2-module ()
360 "Build skeleton IMPLEMENTATION MODULE, prompting for <module-name>."
361 (interactive)
362 (insert "IMPLEMENTATION MODULE ")
363 (let ((name (read-string "Name: ")))
364 (insert name ";\n\n\n\nEND " name ".\n")
365 (forward-line -3)
366 (m2-header)
367 (m2-type)
368 (newline)
369 (m2-var)
370 (newline)
371 (m2-begin)
372 (m2-begin-comment)
373 (insert " Module " name " Initialisation Code "))
374 (m2-end-comment)
375 (newline)
376 (m2-tab))
377
378(defun m2-or ()
379 (interactive)
380 (m2-newline)
381 (backward-delete-char-untabify m2-indent)
382 (insert "|")
383 (m2-newline)
384 (m2-tab))
385 443
386(defun m2-procedure () 444(define-skeleton m2-header
387 (interactive) 445 "Insert a comment block containing the module title, author, etc."
388 (insert "PROCEDURE ") 446 "Title: "
389 (let ((name (read-string "Name: " )) 447 "(*\n Title: \t" str
390 args) 448 "\n Created: \t" (current-time-string)
391 (insert name " (") 449 "\n Author: \t" (user-full-name) " <" user-mail-address ">\n"
392 (insert (read-string "Arguments: ") ")") 450 "*)" > \n)
393 (setq args (read-string "Result Type: "))
394 (if (not (string-equal args ""))
395 (insert " : " args))
396 (insert ";")
397 (m2-newline)
398 (insert "BEGIN")
399 (m2-newline)
400 (m2-newline)
401 (insert "END ")
402 (insert name)
403 (insert ";")
404 (end-of-line 0)
405 (m2-tab)))
406
407(defun m2-with ()
408 (interactive)
409 (insert "WITH ")
410 (let ((name (read-string "Record-Type: ")))
411 (insert name)
412 (insert " DO")
413 (m2-newline)
414 (m2-newline)
415 (insert "END (* with " name " *);"))
416 (end-of-line 0)
417 (m2-tab))
418
419(defun m2-record ()
420 (interactive)
421 (insert "RECORD")
422 (m2-newline)
423 (m2-newline)
424 (insert "END (* record *);")
425 (end-of-line 0)
426 (m2-tab))
427
428(defun m2-stdio ()
429 (interactive)
430 (insert "
431FROM TextIO IMPORT
432 WriteCHAR, ReadCHAR, WriteINTEGER, ReadINTEGER,
433 WriteCARDINAL, ReadCARDINAL, WriteBOOLEAN, ReadBOOLEAN,
434 WriteREAL, ReadREAL, WriteBITSET, ReadBITSET,
435 WriteBasedCARDINAL, ReadBasedCARDINAL, WriteChars, ReadChars,
436 WriteString, ReadString, WhiteSpace, EndOfLine;
437
438FROM SysStreams IMPORT sysIn, sysOut, sysErr;
439
440"))
441
442(defun m2-type ()
443 (interactive)
444 (insert "TYPE")
445 (m2-newline)
446 (m2-tab))
447 451
448(defun m2-until () 452(define-skeleton m2-if
449 (interactive) 453 "Insert skeleton IF statement, prompting for <boolean-expression>."
450 (insert "REPEAT") 454 "<boolean-expression>: "
451 (m2-newline) 455 \n "IF " str " THEN" > \n _ \n "END (* if " str " *);" > \n)
452 (m2-newline)
453 (insert "UNTIL ")
454 (insert (read-string "<boolean-expression>: ") ";")
455 (end-of-line 0)
456 (m2-tab))
457
458(defun m2-var ()
459 (interactive)
460 (m2-newline)
461 (insert "VAR")
462 (m2-newline)
463 (m2-tab))
464 456
465(defun m2-while () 457(define-skeleton m2-loop
466 (interactive) 458 "Build skeleton LOOP (with END)."
467 (insert "WHILE ") 459 nil
468 (let ((name (read-string "<boolean-expression>: "))) 460 \n "LOOP" > \n _ \n "END (* loop *);" > \n)
469 (insert name " DO" )
470 (m2-newline)
471 (m2-newline)
472 (insert "END (* while " name " *);"))
473 (end-of-line 0)
474 (m2-tab))
475
476(defun m2-export ()
477 (interactive)
478 (insert "EXPORT QUALIFIED "))
479 461
480(defun m2-import () 462(define-skeleton m2-module
481 (interactive) 463 "Build skeleton IMPLEMENTATION MODULE, prompting for <module-name>."
482 (insert "FROM ") 464 "Name: "
483 (insert (read-string "Module: ")) 465 \n "IMPLEMENTATION MODULE " str ";" > \n \n
484 (insert " IMPORT ")) 466 '(m2-header)
467 '(m2-type) \n
468 '(m2-var) \n _ \n \n
469 '(m2-begin)
470 '(m2-begin-comment)
471 " Module " str " Initialisation Code "
472 '(m2-end-comment)
473 \n \n "END " str "." > \n)
474
475(define-skeleton m2-or
476 "No doc."
477 nil
478 \n "|" > \n)
479
480(define-skeleton m2-procedure
481 "No doc."
482 "Name: "
483 \n "PROCEDURE " str " (" (read-string "Arguments: ") ")"
484 (let ((args (read-string "Result Type: ")))
485 (if (not (equal args "")) (concat " : " args)))
486 ";" > \n "BEGIN" > \n _ \n "END " str ";" > \n)
487
488(define-skeleton m2-with
489 "No doc."
490 "Record-Type: "
491 \n "WITH " str " DO" > \n _ \n "END (* with " str " *);" > \n)
492
493(define-skeleton m2-record
494 "No doc."
495 nil
496 \n "RECORD" > \n _ \n "END (* record *);" > \n)
497
498(define-skeleton m2-stdio
499 "No doc."
500 nil
501 \n "FROM TextIO IMPORT"
502 > \n "WriteCHAR, ReadCHAR, WriteINTEGER, ReadINTEGER,"
503 > \n "WriteCARDINAL, ReadCARDINAL, WriteBOOLEAN, ReadBOOLEAN,"
504 > \n "WriteREAL, ReadREAL, WriteBITSET, ReadBITSET,"
505 > \n "WriteBasedCARDINAL, ReadBasedCARDINAL, WriteChars, ReadChars,"
506 > \n "WriteString, ReadString, WhiteSpace, EndOfLine;"
507 > \n \n "FROM SysStreams IMPORT sysIn, sysOut, sysErr;" > \n \n)
508
509(define-skeleton m2-type
510 "No doc."
511 nil
512 \n "TYPE" > \n ";" > \n)
513
514(define-skeleton m2-until
515 "No doc."
516 "<boolean-expression>: "
517 \n "REPEAT" > \n _ \n "UNTIL " str ";" > \n)
518
519(define-skeleton m2-var
520 "No doc."
521 nil
522 \n "VAR" > \n ";" > \n)
523
524(define-skeleton m2-while
525 "No doc."
526 "<boolean-expression>: "
527 \n "WHILE " str " DO" > \n _ \n "END (* while " str " *);" > \n)
528
529(define-skeleton m2-export
530 "No doc."
531 nil
532 \n "EXPORT QUALIFIED " > _ \n)
533
534(define-skeleton m2-import
535 "No doc."
536 "Module: "
537 \n "FROM " str " IMPORT " > _ \n)
485 538
486(defun m2-begin-comment () 539(defun m2-begin-comment ()
487 (interactive) 540 (interactive)
@@ -501,15 +554,15 @@ FROM SysStreams IMPORT sysIn, sysOut, sysErr;
501 554
502(defun m2-link () 555(defun m2-link ()
503 (interactive) 556 (interactive)
504 (if m2-link-name 557 (compile (concat m2-link-command " "
505 (compile (concat m2-link-command " " m2-link-name)) 558 (or m2-link-name
506 (compile (concat m2-link-command " " 559 (setq m2-link-name (read-string "Name of executable: "
507 (setq m2-link-name (read-string "Name of executable: " 560 (buffer-name)))))))
508 (buffer-name)))))))
509 561
510(defun m2-execute-monitor-command (command) 562(defun m2-execute-monitor-command (command)
511 (let* ((shell shell-file-name) 563 (let* ((shell shell-file-name)
512 (csh (equal (file-name-nondirectory shell) "csh"))) 564 ;; (csh (equal (file-name-nondirectory shell) "csh"))
565 )
513 (call-process shell nil t t "-cf" (concat "exec " command)))) 566 (call-process shell nil t t "-cf" (concat "exec " command))))
514 567
515(defun m2-visit () 568(defun m2-visit ()
diff --git a/test/ChangeLog b/test/ChangeLog
index 5a5c202ad3c..3b1921c5987 100644
--- a/test/ChangeLog
+++ b/test/ChangeLog
@@ -1,3 +1,7 @@
12010-11-11 Stefan Monnier <monnier@iro.umontreal.ca>
2
3 * indent/modula2.mod: New file.
4
12010-10-27 Stefan Monnier <monnier@iro.umontreal.ca> 52010-10-27 Stefan Monnier <monnier@iro.umontreal.ca>
2 6
3 * indent/octave.m: Add a test to ensure indentation is local. 7 * indent/octave.m: Add a test to ensure indentation is local.
diff --git a/test/indent/modula2.mod b/test/indent/modula2.mod
new file mode 100644
index 00000000000..f8fbcb7f4e5
--- /dev/null
+++ b/test/indent/modula2.mod
@@ -0,0 +1,53 @@
1(* -*- mode: modula-2; m2-indent:3 -*- *)
2
3IMPLEMENTATION MODULE Indent ;
4
5(* This is (* a nested comment *) *)
6// This is a single-line comment.
7
8FROM SYSTEM IMPORT ADR, TSIZE, SIZE, WORD ;
9
10CONST
11 c1 = 2;
12
13TYPE
14 t = POINTER TO ARRAY [0..10] OF LONGINT;
15
16VAR x: t;
17 y:LONGINT;
18
19
20PROCEDURE f1 (f: File) : INTEGER ;
21 VAR
22 fd: FileDescriptor ;
23 PROCEDURE foo (a:CARDINAL) : INTEGER;
24 BEGIN
25 END foo;
26BEGIN
27 IF f#Error
28 THEN
29 fd := GetIndice(FileInfo, f) ;
30 IF fd#NIL THEN
31 RETURN( fd^.unixfd )
32 ELSE
33 CASE z OF
34 1: do1();
35 | 2: do2();
36 toto(x);
37 | 3: ;
38 | 4: do4();
39 ELSE do5();
40 END ; (* CASE selection *)
41
42 END
43 END ;
44 FormatError1('file %d has not been opened or is out of range\n', f) ;
45 RETURN( -1 )
46END f1 ;
47
48
49BEGIN
50 init
51FINALLY
52 done
53END Indent.