aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2010-05-17 15:27:26 -0400
committerStefan Monnier2010-05-17 15:27:26 -0400
commit5ad4bef5758fd694d209a8fb63f42bcfdb22785c (patch)
tree813a37580e4d3a4d1585110aae6d14815c6d2ad6
parent16455a8509026404dcba9a1adfe389883b3ef990 (diff)
downloademacs-5ad4bef5758fd694d209a8fb63f42bcfdb22785c.tar.gz
emacs-5ad4bef5758fd694d209a8fb63f42bcfdb22785c.zip
Provide a simple generic indentation engine and use it for Prolog.
* emacs-lisp/smie.el: New file. * progmodes/prolog.el (prolog-smie-op-levels) (prolog-smie-indent-rules): New var. (prolog-mode-variables): Use them to configure SMIE. (prolog-indent-line, prolog-indent-level): Remove.
-rw-r--r--etc/NEWS2
-rw-r--r--lisp/ChangeLog11
-rw-r--r--lisp/emacs-lisp/smie.el688
-rw-r--r--lisp/progmodes/prolog.el120
-rw-r--r--lisp/simple.el12
5 files changed, 769 insertions, 64 deletions
diff --git a/etc/NEWS b/etc/NEWS
index 715dc12c467..df68e42cd66 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -210,6 +210,8 @@ threads simultaneously.
210 210
211* New Modes and Packages in Emacs 24.1 211* New Modes and Packages in Emacs 24.1
212 212
213** smie.el is a package providing a simple generic indentation engine.
214
213** secrets.el is an implementation of the Secret Service API, an 215** secrets.el is an implementation of the Secret Service API, an
214interface to password managers like GNOME Keyring or KDE Wallet. The 216interface to password managers like GNOME Keyring or KDE Wallet. The
215Secret Service API requires D-Bus for communication. 217Secret Service API requires D-Bus for communication.
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index f6d4ce0c884..e85fa58d360 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,12 @@
12010-05-17 Stefan Monnier <monnier@iro.umontreal.ca>
2
3 Provide a simple generic indentation engine and use it for Prolog.
4 * emacs-lisp/smie.el: New file.
5 * progmodes/prolog.el (prolog-smie-op-levels)
6 (prolog-smie-indent-rules): New var.
7 (prolog-mode-variables): Use them to configure SMIE.
8 (prolog-indent-line, prolog-indent-level): Remove.
9
12010-05-17 Jay Belanger <jay.p.belanger@gmail.com> 102010-05-17 Jay Belanger <jay.p.belanger@gmail.com>
2 11
3 * calc/calc-vec.el (math-vector-avg): Put the vector elements in 12 * calc/calc-vec.el (math-vector-avg): Put the vector elements in
@@ -6,7 +15,7 @@
62010-05-16 Jay Belanger <jay.p.belanger@gmail.com> 152010-05-16 Jay Belanger <jay.p.belanger@gmail.com>
7 16
8 * calc/calc-vec.el (calc-histogram): 17 * calc/calc-vec.el (calc-histogram):
9 (calcFunc-histogram): Allow vectors as inputs. 18 (calcFunc-histogram): Allow vectors as inputs.
10 (math-vector-avg): New function. 19 (math-vector-avg): New function.
11 20
12 * calc/calc-ext.el (math-group-float): Have the number of digits 21 * calc/calc-ext.el (math-group-float): Have the number of digits
diff --git a/lisp/emacs-lisp/smie.el b/lisp/emacs-lisp/smie.el
new file mode 100644
index 00000000000..46c4222f3fe
--- /dev/null
+++ b/lisp/emacs-lisp/smie.el
@@ -0,0 +1,688 @@
1;;; smie.el --- Simple Minded Indentation Engine
2
3;; Copyright (C) 2010 Free Software Foundation, Inc.
4
5;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
6;; Keywords: languages, lisp, internal, parsing, indentation
7
8;; This file is part of GNU Emacs.
9
10;; GNU Emacs is free software; you can redistribute it and/or modify
11;; it under the terms of the GNU General Public License as published by
12;; the Free Software Foundation, either version 3 of the License, or
13;; (at your option) any later version.
14
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
21;; along with this program. If not, see <http://www.gnu.org/licenses/>.
22
23;;; Commentary:
24
25;; While working on the SML indentation code, the idea grew that maybe
26;; I could write something generic to do the same thing, and at the
27;; end of working on the SML code, I had a pretty good idea of what it
28;; could look like. That idea grew stronger after working on
29;; LaTeX indentation.
30;;
31;; So at some point I decided to try it out, by writing a new
32;; indentation code for Coq while trying to keep most of the code
33;; "table driven", where only the tables are Coq-specific. The result
34;; (which was used for Beluga-mode as well) turned out to be based on
35;; something pretty close to an operator precedence parser.
36
37;; So here is another rewrite, this time following the actual principles of
38;; operator precedence grammars. Why OPG? Even though they're among the
39;; weakest kinds of parsers, these parsers have some very desirable properties
40;; for Emacs:
41;; - most importantly for indentation, they work equally well in either
42;; direction, so you can use them to parse backward from the indentation
43;; point to learn the syntactic context;
44;; - they work locally, so there's no need to keep a cache of
45;; the parser's state;
46;; - because of that locality, indentation also works just fine when earlier
47;; parts of the buffer are syntactically incorrect since the indentation
48;; looks at "as little as possible" of the buffer make an indentation
49;; decision.
50;; - they typically have no error handling and can't even detect a parsing
51;; error, so we don't have to worry about what to do in case of a syntax
52;; error because the parser just automatically does something. Better yet,
53;; we can afford to use a sloppy grammar.
54
55;; The development (especially the parts building the 2D precedence
56;; tables and then computing the precedence levels from it) is largely
57;; inspired from page 187-194 of "Parsing techniques" by Dick Grune
58;; and Ceriel Jacobs (BookBody.pdf available at
59;; http://www.cs.vu.nl/~dick/PTAPG.html).
60;;
61;; OTOH we had to kill many chickens, read many coffee grounds, and practiced
62;; untold numbers of black magic spells.
63
64;;; Code:
65
66(eval-when-compile (require 'cl))
67
68;;; Building precedence level tables from BNF specs.
69
70(defun smie-set-prec2tab (table x y val &optional override)
71 (assert (and x y))
72 (let* ((key (cons x y))
73 (old (gethash key table)))
74 (if (and old (not (eq old val)))
75 (if (gethash key override)
76 ;; FIXME: The override is meant to resolve ambiguities,
77 ;; but it also hides real conflicts. It would be great to
78 ;; be able to distinguish the two cases so that overrides
79 ;; don't hide real conflicts.
80 (puthash key (gethash key override) table)
81 (display-warning 'smie (format "Conflict: %s %s/%s %s" x old val y)))
82 (puthash key val table))))
83
84(defun smie-precs-precedence-table (precs)
85 "Compute a 2D precedence table from a list of precedences.
86PRECS should be a list, sorted by precedence (e.g. \"+\" will
87come before \"*\"), of elements of the form \(left OP ...)
88or (right OP ...) or (nonassoc OP ...) or (assoc OP ...). All operators in
89one of those elements shares the same precedence level and associativity."
90 (let ((prec2-table (make-hash-table :test 'equal)))
91 (dolist (prec precs)
92 (dolist (op (cdr prec))
93 (let ((selfrule (cdr (assq (car prec)
94 '((left . >) (right . <) (assoc . =))))))
95 (when selfrule
96 (dolist (other-op (cdr prec))
97 (smie-set-prec2tab prec2-table op other-op selfrule))))
98 (let ((op1 '<) (op2 '>))
99 (dolist (other-prec precs)
100 (if (eq prec other-prec)
101 (setq op1 '> op2 '<)
102 (dolist (other-op (cdr other-prec))
103 (smie-set-prec2tab prec2-table op other-op op2)
104 (smie-set-prec2tab prec2-table other-op op op1)))))))
105 prec2-table))
106
107(defun smie-merge-prec2s (tables)
108 (if (null (cdr tables))
109 (car tables)
110 (let ((prec2 (make-hash-table :test 'equal)))
111 (dolist (table tables)
112 (maphash (lambda (k v)
113 (smie-set-prec2tab prec2 (car k) (cdr k) v))
114 table))
115 prec2)))
116
117(defun smie-bnf-precedence-table (bnf &rest precs)
118 (let ((nts (mapcar 'car bnf)) ;Non-terminals
119 (first-ops-table ())
120 (last-ops-table ())
121 (first-nts-table ())
122 (last-nts-table ())
123 (prec2 (make-hash-table :test 'equal))
124 (override (smie-merge-prec2s
125 (mapcar 'smie-precs-precedence-table precs)))
126 again)
127 (dolist (rules bnf)
128 (let ((nt (car rules))
129 (last-ops ())
130 (first-ops ())
131 (last-nts ())
132 (first-nts ()))
133 (dolist (rhs (cdr rules))
134 (assert (consp rhs))
135 (if (not (member (car rhs) nts))
136 (pushnew (car rhs) first-ops)
137 (pushnew (car rhs) first-nts)
138 (when (consp (cdr rhs))
139 ;; If the first is not an OP we add the second (which
140 ;; should be an OP if BNF is an "operator grammar").
141 ;; Strictly speaking, this should only be done if the
142 ;; first is a non-terminal which can expand to a phrase
143 ;; without any OP in it, but checking doesn't seem worth
144 ;; the trouble, and it lets the writer of the BNF
145 ;; be a bit more sloppy by skipping uninteresting base
146 ;; cases which are terminals but not OPs.
147 (assert (not (member (cadr rhs) nts)))
148 (pushnew (cadr rhs) first-ops)))
149 (let ((shr (reverse rhs)))
150 (if (not (member (car shr) nts))
151 (pushnew (car shr) last-ops)
152 (pushnew (car shr) last-nts)
153 (when (consp (cdr shr))
154 (assert (not (member (cadr shr) nts)))
155 (pushnew (cadr shr) last-ops)))))
156 (push (cons nt first-ops) first-ops-table)
157 (push (cons nt last-ops) last-ops-table)
158 (push (cons nt first-nts) first-nts-table)
159 (push (cons nt last-nts) last-nts-table)))
160 ;; Compute all first-ops by propagating the initial ones we have
161 ;; now, according to first-nts.
162 (setq again t)
163 (while (prog1 again (setq again nil))
164 (dolist (first-nts first-nts-table)
165 (let* ((nt (pop first-nts))
166 (first-ops (assoc nt first-ops-table)))
167 (dolist (first-nt first-nts)
168 (dolist (op (cdr (assoc first-nt first-ops-table)))
169 (unless (member op first-ops)
170 (setq again t)
171 (push op (cdr first-ops))))))))
172 ;; Same thing for last-ops.
173 (setq again t)
174 (while (prog1 again (setq again nil))
175 (dolist (last-nts last-nts-table)
176 (let* ((nt (pop last-nts))
177 (last-ops (assoc nt last-ops-table)))
178 (dolist (last-nt last-nts)
179 (dolist (op (cdr (assoc last-nt last-ops-table)))
180 (unless (member op last-ops)
181 (setq again t)
182 (push op (cdr last-ops))))))))
183 ;; Now generate the 2D precedence table.
184 (dolist (rules bnf)
185 (dolist (rhs (cdr rules))
186 (while (cdr rhs)
187 (cond
188 ((member (car rhs) nts)
189 (dolist (last (cdr (assoc (car rhs) last-ops-table)))
190 (smie-set-prec2tab prec2 last (cadr rhs) '> override)))
191 ((member (cadr rhs) nts)
192 (dolist (first (cdr (assoc (cadr rhs) first-ops-table)))
193 (smie-set-prec2tab prec2 (car rhs) first '< override))
194 (if (and (cddr rhs) (not (member (car (cddr rhs)) nts)))
195 (smie-set-prec2tab prec2 (car rhs) (car (cddr rhs))
196 '= override)))
197 (t (smie-set-prec2tab prec2 (car rhs) (cadr rhs) '= override)))
198 (setq rhs (cdr rhs)))))
199 prec2))
200
201(defun smie-prec2-levels (prec2)
202 "Take a 2D precedence table and turn it into an alist of precedence levels.
203PREC2 is a table as returned by `smie-precs-precedence-table' or
204`smie-bnf-precedence-table'."
205 ;; For each operator, we create two "variables" (corresponding to
206 ;; the left and right precedence level), which are represented by
207 ;; cons cells. Those are the vary cons cells that appear in the
208 ;; final `table'. The value of each "variable" is kept in the `car'.
209 (let ((table ())
210 (csts ())
211 (eqs ())
212 tmp x y)
213 ;; From `prec2' we construct a list of constraints between
214 ;; variables (aka "precedence levels"). These can be either
215 ;; equality constraints (in `eqs') or `<' constraints (in `csts').
216 (maphash (lambda (k v)
217 (if (setq tmp (assoc (car k) table))
218 (setq x (cddr tmp))
219 (setq x (cons nil nil))
220 (push (cons (car k) (cons nil x)) table))
221 (if (setq tmp (assoc (cdr k) table))
222 (setq y (cdr tmp))
223 (setq y (cons nil (cons nil nil)))
224 (push (cons (cdr k) y) table))
225 (ecase v
226 (= (push (cons x y) eqs))
227 (< (push (cons x y) csts))
228 (> (push (cons y x) csts))))
229 prec2)
230 ;; First process the equality constraints.
231 (let ((eqs eqs))
232 (while eqs
233 (let ((from (caar eqs))
234 (to (cdar eqs)))
235 (setq eqs (cdr eqs))
236 (if (eq to from)
237 (debug) ;Can it happen?
238 (dolist (other-eq eqs)
239 (if (eq from (cdr other-eq)) (setcdr other-eq to))
240 (when (eq from (car other-eq))
241 ;; This can happen because of `assoc' settings in precs
242 ;; or because of a rhs like ("op" foo "op").
243 (setcar other-eq to)))
244 (dolist (cst csts)
245 (if (eq from (cdr cst)) (setcdr cst to))
246 (if (eq from (car cst)) (setcar cst to)))))))
247 ;; Then eliminate trivial constraints iteratively.
248 (let ((i 0))
249 (while csts
250 (let ((rhvs (mapcar 'cdr csts))
251 (progress nil))
252 (dolist (cst csts)
253 (unless (memq (car cst) rhvs)
254 (setq progress t)
255 (setcar (car cst) i)
256 (setq csts (delq cst csts))))
257 (unless progress
258 (error "Can't resolve the precedence table to precedence levels")))
259 (incf i))
260 ;; Propagate equalities back to their source.
261 (dolist (eq (nreverse eqs))
262 (assert (null (caar eq)))
263 (setcar (car eq) (cadr eq)))
264 ;; Finally, fill in the remaining vars (which only appeared on the
265 ;; right side of the < constraints).
266 ;; Tho leaving them at nil is not a bad choice, since it makes
267 ;; it clear that these don't bind at all.
268 ;; (dolist (x table)
269 ;; (unless (nth 1 x) (setf (nth 1 x) i))
270 ;; (unless (nth 2 x) (setf (nth 2 x) i)))
271 )
272 table))
273
274;;; Parsing using a precedence level table.
275
276(defvar smie-op-levels 'unset
277 "List of token parsing info.
278Each element is of the form (TOKEN LEFT-LEVEL RIGHT-LEVEL).
279Parsing is done using an operator precedence parser.")
280
281(defun smie-backward-token ()
282 ;; FIXME: This may be an OK default but probably needs a hook.
283 (buffer-substring (point)
284 (progn (if (zerop (skip-syntax-backward "."))
285 (skip-syntax-backward "w_'"))
286 (point))))
287
288(defun smie-forward-token ()
289 ;; FIXME: This may be an OK default but probably needs a hook.
290 (buffer-substring (point)
291 (progn (if (zerop (skip-syntax-forward "."))
292 (skip-syntax-forward "w_'"))
293 (point))))
294
295(defun smie-backward-sexp (&optional halfsexp)
296 "Skip over one sexp.
297HALFSEXP if non-nil, means skip over a partial sexp if needed. I.e. if the
298first token we see is an operator, skip over its left-hand-side argument.
299Possible return values:
300 (LEFT-LEVEL POS TOKEN): we couldn't skip TOKEN because its right-level
301 is too high. LEFT-LEVEL is the left-level of TOKEN,
302 POS is its start position in the buffer.
303 (t POS TOKEN): Same thing but for an open-paren or the beginning of buffer.
304 (nil POS TOKEN): we skipped over a paren-like pair.
305 nil: we skipped over an identifier, matched parentheses, ..."
306 (if (bobp) (list t (point))
307 (catch 'return
308 (let ((levels ()))
309 (while
310 (let* ((pos (point))
311 (token (progn (forward-comment (- (point-max)))
312 (smie-backward-token)))
313 (toklevels (cdr (assoc token smie-op-levels))))
314
315 (cond
316 ((null toklevels)
317 (if (equal token "")
318 (condition-case err
319 (progn (goto-char pos) (backward-sexp 1) nil)
320 (scan-error (throw 'return (list t (caddr err)))))))
321 ((null (nth 1 toklevels))
322 ;; A token like a paren-close.
323 (assert (nth 0 toklevels)) ;Otherwise, why mention it?
324 (push (nth 0 toklevels) levels))
325 (t
326 (while (and levels (< (nth 1 toklevels) (car levels)))
327 (setq levels (cdr levels)))
328 (cond
329 ((null levels)
330 (if (and halfsexp (nth 0 toklevels))
331 (push (nth 0 toklevels) levels)
332 (throw 'return
333 (prog1 (list (or (car toklevels) t) (point) token)
334 (goto-char pos)))))
335 (t
336 (while (and levels (= (nth 1 toklevels) (car levels)))
337 (setq levels (cdr levels)))
338 (cond
339 ((null levels)
340 (cond
341 ((null (nth 0 toklevels))
342 (throw 'return (list nil (point) token)))
343 ((eq (nth 0 toklevels) (nth 1 toklevels))
344 (throw 'return
345 (prog1 (list (or (car toklevels) t) (point) token)
346 (goto-char pos))))
347 (t (debug)))) ;Not sure yet what to do here.
348 (t
349 (if (nth 0 toklevels)
350 (push (nth 0 toklevels) levels))))))))
351 levels)
352 (setq halfsexp nil))))))
353
354;; Mirror image, not used for indentation.
355(defun smie-forward-sexp (&optional halfsexp)
356 "Skip over one sexp.
357HALFSEXP if non-nil, means skip over a partial sexp if needed. I.e. if the
358first token we see is an operator, skip over its left-hand-side argument.
359Possible return values:
360 (RIGHT-LEVEL POS TOKEN): we couldn't skip TOKEN because its left-level
361 is too high. RIGHT-LEVEL is the right-level of TOKEN,
362 POS is its end position in the buffer.
363 (t POS TOKEN): Same thing but for an open-paren or the beginning of buffer.
364 (nil POS TOKEN): we skipped over a paren-like pair.
365 nil: we skipped over an identifier, matched parentheses, ..."
366 (if (eobp) (list t (point))
367 (catch 'return
368 (let ((levels ()))
369 (while
370 (let* ((pos (point))
371 (token (progn (forward-comment (point-max))
372 (smie-forward-token)))
373 (toklevels (cdr (assoc token smie-op-levels))))
374
375 (cond
376 ((null toklevels)
377 (if (equal token "")
378 (condition-case err
379 (progn (goto-char pos) (forward-sexp 1) nil)
380 (scan-error (throw 'return (list t (caddr err)))))))
381 ((null (nth 0 toklevels))
382 ;; A token like a paren-close.
383 (assert (nth 1 toklevels)) ;Otherwise, why mention it?
384 (push (nth 1 toklevels) levels))
385 (t
386 (while (and levels (< (nth 0 toklevels) (car levels)))
387 (setq levels (cdr levels)))
388 (cond
389 ((null levels)
390 (if (and halfsexp (nth 1 toklevels))
391 (push (nth 1 toklevels) levels)
392 (throw 'return
393 (prog1 (list (or (nth 1 toklevels) t) (point) token)
394 (goto-char pos)))))
395 (t
396 (while (and levels (= (nth 0 toklevels) (car levels)))
397 (setq levels (cdr levels)))
398 (cond
399 ((null levels)
400 (cond
401 ((null (nth 1 toklevels))
402 (throw 'return (list nil (point) token)))
403 ((eq (nth 1 toklevels) (nth 0 toklevels))
404 (throw 'return
405 (prog1 (list (or (nth 1 toklevels) t) (point) token)
406 (goto-char pos))))
407 (t (debug)))) ;Not sure yet what to do here.
408 (t
409 (if (nth 1 toklevels)
410 (push (nth 1 toklevels) levels))))))))
411 levels)
412 (setq halfsexp nil))))))
413
414(defun smie-backward-sexp-command (&optional n)
415 "Move backward through N logical elements."
416 (interactive "p")
417 (if (< n 0)
418 (smie-forward-sexp-command (- n))
419 (let ((forward-sexp-function nil))
420 (while (> n 0)
421 (decf n)
422 (let ((pos (point))
423 (res (smie-backward-sexp 'halfsexp)))
424 (if (and (car res) (= pos (point)) (not (bolp)))
425 (signal 'scan-error
426 (list "Containing expression ends prematurely"
427 (cadr res) (cadr res)))
428 nil))))))
429
430(defun smie-forward-sexp-command (&optional n)
431 "Move forward through N logical elements."
432 (interactive "p")
433 (if (< n 0)
434 (smie-backward-sexp-command (- n))
435 (let ((forward-sexp-function nil))
436 (while (> n 0)
437 (decf n)
438 (let ((pos (point))
439 (res (smie-forward-sexp 'halfsexp)))
440 (if (and (car res) (= pos (point)) (not (bolp)))
441 (signal 'scan-error
442 (list "Containing expression ends prematurely"
443 (cadr res) (cadr res)))
444 nil))))))
445
446;;; The indentation engine.
447
448(defcustom smie-indent-basic 4
449 "Basic amount of indentation."
450 :type 'integer)
451
452(defvar smie-indent-rules 'unset
453 "Rules of the following form.
454\(TOK OFFSET) how to indent right after TOK.
455\(TOK O1 O2) how to indent right after TOK:
456 O1 is the default;
457 O2 is used if TOK is \"hanging\".
458\((T1 . T2) . OFFSET) how to indent token T2 w.r.t T1.
459\((t . TOK) . OFFSET) how to indent TOK with respect to its parent.
460\(list-intro . TOKENS) declare TOKENS as being followed by what may look like
461 a funcall but is just a sequence of expressions.
462\(t . OFFSET) basic indentation step.
463\(args . OFFSET) indentation of arguments.
464A nil offset defaults to `smie-indent-basic'.")
465
466(defun smie-indent-hanging-p ()
467 ;; A Hanging keyword is one that's at the end of a line except it's not at
468 ;; the beginning of a line.
469 (and (save-excursion (smie-forward-token)
470 (skip-chars-forward " \t") (eolp))
471 (save-excursion (skip-chars-backward " \t") (not (bolp)))))
472
473(defun smie-bolp ()
474 (save-excursion (skip-chars-backward " \t") (bolp)))
475
476(defun smie-indent-offset (elem)
477 (or (cdr (assq elem smie-indent-rules))
478 (cdr (assq t smie-indent-rules))
479 smie-indent-basic))
480
481(defun smie-indent-calculate (&optional virtual)
482 "Compute the indentation to use for point.
483If VIRTUAL is non-nil, it means we're not trying to indent point but just
484need to compute the column at which point should be indented
485in order to figure out the indentation of some other (further down) point.
486VIRTUAL can take two different non-nil values:
487- :bolp: means that the current indentation of point can be trusted
488 to be good only if if it follows a line break.
489- :hanging: means that the current indentation of point can be
490 trusted to be good except if the following token is hanging."
491 ;; FIXME: This has accumulated a lot of rules, some of which aren't
492 ;; clearly orthogonal any more, so we should probably try and
493 ;; restructure it somewhat.
494 (or
495 ;; Trust pre-existing indentation on other lines.
496 (and virtual
497 (if (eq virtual :hanging) (not (smie-indent-hanging-p)) (smie-bolp))
498 (current-column))
499 ;; Align close paren with opening paren.
500 (save-excursion
501 ;; (forward-comment (point-max))
502 (when (looking-at "\\s)")
503 (while (not (zerop (skip-syntax-forward ")")))
504 (skip-chars-forward " \t"))
505 (condition-case nil
506 (progn
507 (backward-sexp 1)
508 (smie-indent-calculate :hanging))
509 (scan-error nil))))
510 ;; Align closing token with the corresponding opening one.
511 ;; (e.g. "of" with "case", or "in" with "let").
512 (save-excursion
513 (let* ((pos (point))
514 (token (smie-forward-token))
515 (toklevels (cdr (assoc token smie-op-levels))))
516 (when (car toklevels)
517 (let ((res (smie-backward-sexp 'halfsexp)) tmp)
518 ;; If we didn't move at all, that means we didn't really skip
519 ;; what we wanted.
520 (when (< (point) pos)
521 (cond
522 ((eq (car res) (car toklevels))
523 ;; We bumped into a same-level operator. align with it.
524 (goto-char (cadr res))
525 ;; Don't use (smie-indent-calculate :hanging) here, because we
526 ;; want to jump back over a sequence of same-level ops such as
527 ;; a -> b -> c
528 ;; -> d
529 ;; So as to align with the earliest appropriate place.
530 (smie-indent-calculate :bolp))
531 ((equal token (save-excursion
532 (forward-comment (- (point-max)))
533 (smie-backward-token)))
534 ;; in cases such as "fn x => fn y => fn z =>",
535 ;; jump back to the very first fn.
536 ;; FIXME: should we only do that for special tokens like "=>"?
537 (smie-indent-calculate :bolp))
538 ((setq tmp (assoc (cons (caddr res) token)
539 smie-indent-rules))
540 (goto-char (cadr res))
541 (+ (cdr tmp) (smie-indent-calculate :hanging)))
542 (t
543 (+ (or (cdr (assoc (cons t token) smie-indent-rules)) 0)
544 (current-column)))))))))
545 ;; Indentation of a comment.
546 (and (looking-at comment-start-skip)
547 (save-excursion
548 (forward-comment (point-max))
549 (skip-chars-forward " \t\r\n")
550 (smie-indent-calculate nil)))
551 ;; Indentation inside a comment.
552 (and (looking-at "\\*") (nth 4 (syntax-ppss))
553 (let ((ppss (syntax-ppss)))
554 (save-excursion
555 (forward-line -1)
556 (if (<= (point) (nth 8 ppss))
557 (progn (goto-char (1+ (nth 8 ppss))) (current-column))
558 (skip-chars-forward " \t")
559 (if (looking-at "\\*")
560 (current-column))))))
561 ;; Indentation right after a special keyword.
562 (save-excursion
563 (let* ((tok (progn (forward-comment (- (point-max)))
564 (smie-backward-token)))
565 (tokinfo (assoc tok smie-indent-rules))
566 (toklevel (assoc tok smie-op-levels)))
567 (when (or tokinfo (and toklevel (null (cadr toklevel))))
568 (if (or (smie-indent-hanging-p)
569 ;; If calculating the virtual indentation point, prefer
570 ;; looking up the virtual indentation of the alignment
571 ;; point as well. This is used for indentation after
572 ;; "fn x => fn y =>".
573 virtual)
574 (+ (smie-indent-calculate :bolp)
575 (or (caddr tokinfo) (cadr tokinfo) (smie-indent-offset t)))
576 (+ (current-column)
577 (or (cadr tokinfo) (smie-indent-offset t)))))))
578 ;; Main loop (FIXME: whatever that means!?).
579 (save-excursion
580 (let ((positions nil)
581 (begline nil)
582 arg)
583 (while (and (null (car (smie-backward-sexp)))
584 (push (point) positions)
585 (not (setq begline (smie-bolp)))))
586 (save-excursion
587 ;; Figure out if the atom we just skipped is an argument rather
588 ;; than a function.
589 (setq arg (or (null (car (smie-backward-sexp)))
590 (member (progn (forward-comment (- (point-max)))
591 (smie-backward-token))
592 (cdr (assoc 'list-intro smie-indent-rules))))))
593 (cond
594 ((and arg positions)
595 (goto-char (car positions))
596 (current-column))
597 ((and (null begline) (cdr positions))
598 ;; We skipped some args plus the function and bumped into something.
599 ;; Align with the first arg.
600 (goto-char (cadr positions))
601 (current-column))
602 ((and (null begline) positions)
603 ;; We're the first arg.
604 ;; FIXME: it might not be a funcall, in which case we might be the
605 ;; second element.
606 (goto-char (car positions))
607 (+ (smie-indent-offset 'args)
608 ;; We used to use (smie-indent-calculate :bolp), but that
609 ;; doesn't seem right since it might then indent args less than
610 ;; the function itself.
611 (current-column)))
612 ((and (null arg) (null positions))
613 ;; We're the function itself. Not sure what to do here yet.
614 (if virtual (current-column)
615 (save-excursion
616 (let* ((pos (point))
617 (tok (progn (forward-comment (- (point-max)))
618 (smie-backward-token)))
619 (toklevels (cdr (assoc tok smie-op-levels))))
620 (cond
621 ((numberp (car toklevels))
622 ;; We're right after an infix token. Let's skip over the
623 ;; lefthand side.
624 (goto-char pos)
625 (let (res)
626 (while (progn (setq res (smie-backward-sexp 'halfsexp))
627 (and (not (smie-bolp))
628 (equal (car res) (car toklevels)))))
629 ;; We should be right after a token of equal or
630 ;; higher precedence.
631 (cond
632 ((and (consp res) (memq (car res) '(t nil)))
633 ;; The token of higher-precedence is like an open-paren.
634 ;; Sample case for t: foo { bar, \n[TAB] baz }.
635 ;; Sample case for nil: match ... with \n[TAB] | toto ...
636 ;; (goto-char (cadr res))
637 (smie-indent-calculate :hanging))
638 ((and (consp res) (<= (car res) (car toklevels)))
639 ;; We stopped at a token of equal or higher precedence
640 ;; because we found a place with which to align.
641 (current-column))
642 )))
643 ;; For other cases.... hmm... we'll see when we get there.
644 )))))
645 ((null positions)
646 (smie-backward-token)
647 (+ (smie-indent-offset 'args) (smie-indent-calculate :bolp)))
648 ((car (smie-backward-sexp))
649 ;; No arg stands on its own line, but the function does:
650 (if (cdr positions)
651 (progn
652 (goto-char (cadr positions))
653 (current-column))
654 (goto-char (car positions))
655 (+ (current-column) (smie-indent-offset 'args))))
656 (t
657 ;; We've skipped to a previous arg on its own line: align.
658 (goto-char (car positions))
659 (current-column)))))))
660
661(defun smie-indent-line ()
662 "Indent current line using the SMIE indentation engine."
663 (interactive)
664 (let* ((savep (point))
665 (indent (condition-case nil
666 (save-excursion
667 (forward-line 0)
668 (skip-chars-forward " \t")
669 (if (>= (point) savep) (setq savep nil))
670 (or (smie-indent-calculate) 0))
671 (error 0))))
672 (if (not (numberp indent))
673 ;; If something funny is used (e.g. `noindent'), return it.
674 indent
675 (if (< indent 0) (setq indent 0)) ;Just in case.
676 (if savep
677 (save-excursion (indent-line-to indent))
678 (indent-line-to indent)))))
679
680;;;###autoload
681(defun smie-setup (op-levels indent-rules)
682 (set (make-local-variable 'smie-indent-rules) indent-rules)
683 (set (make-local-variable 'smie-op-levels) op-levels)
684 (set (make-local-variable 'indent-line-function) 'smie-indent-line))
685
686
687(provide 'smie)
688;;; smie.el ends here
diff --git a/lisp/progmodes/prolog.el b/lisp/progmodes/prolog.el
index 5bca3502af9..891f3610f96 100644
--- a/lisp/progmodes/prolog.el
+++ b/lisp/progmodes/prolog.el
@@ -98,6 +98,61 @@ When nil, send actual operating system end of file."
98(defvar prolog-mode-abbrev-table nil) 98(defvar prolog-mode-abbrev-table nil)
99(define-abbrev-table 'prolog-mode-abbrev-table ()) 99(define-abbrev-table 'prolog-mode-abbrev-table ())
100 100
101(defconst prolog-smie-op-levels
102 ;; Rather than construct the operator levels table from the BNF,
103 ;; we directly provide the operator precedences from GNU Prolog's
104 ;; manual. The only problem is that GNU Prolog's manual uses
105 ;; precedence levels in the opposite sense (higher numbers bind less
106 ;; tightly) than SMIE, so we use negative numbers.
107 '(("." -10000 -10000)
108 (":-" -1200 -1200)
109 ("-->" -1200 -1200)
110 (";" -1100 -1100)
111 ("->" -1050 -1050)
112 ("," -1000 -1000)
113 ("\\+" -900 -900)
114 ("=" -700 -700)
115 ("\\=" -700 -700)
116 ("=.." -700 -700)
117 ("==" -700 -700)
118 ("\\==" -700 -700)
119 ("@<" -700 -700)
120 ("@=<" -700 -700)
121 ("@>" -700 -700)
122 ("@>=" -700 -700)
123 ("is" -700 -700)
124 ("=:=" -700 -700)
125 ("=\\=" -700 -700)
126 ("<" -700 -700)
127 ("=<" -700 -700)
128 (">" -700 -700)
129 (">=" -700 -700)
130 (":" -600 -600)
131 ("+" -500 -500)
132 ("-" -500 -500)
133 ("/\\" -500 -500)
134 ("\\/" -500 -500)
135 ("*" -400 -400)
136 ("/" -400 -400)
137 ("//" -400 -400)
138 ("rem" -400 -400)
139 ("mod" -400 -400)
140 ("<<" -400 -400)
141 (">>" -400 -400)
142 ("**" -200 -200)
143 ("^" -200 -200)
144 ;; Prefix
145 ;; ("+" 200 200)
146 ;; ("-" 200 200)
147 ;; ("\\" 200 200)
148 )
149 "Precedence levels of infix operators.")
150
151(defconst prolog-smie-indent-rules
152 '((":-")
153 ("->"))
154 "Prolog indentation rules.")
155
101(defun prolog-mode-variables () 156(defun prolog-mode-variables ()
102 (make-local-variable 'paragraph-separate) 157 (make-local-variable 'paragraph-separate)
103 (setq paragraph-separate (concat "%%\\|$\\|" page-delimiter)) ;'%%..' 158 (setq paragraph-separate (concat "%%\\|$\\|" page-delimiter)) ;'%%..'
@@ -105,8 +160,10 @@ When nil, send actual operating system end of file."
105 (setq paragraph-ignore-fill-prefix t) 160 (setq paragraph-ignore-fill-prefix t)
106 (make-local-variable 'imenu-generic-expression) 161 (make-local-variable 'imenu-generic-expression)
107 (setq imenu-generic-expression '((nil "^\\sw+" 0))) 162 (setq imenu-generic-expression '((nil "^\\sw+" 0)))
108 (make-local-variable 'indent-line-function) 163 (smie-setup prolog-smie-op-levels prolog-smie-indent-rules)
109 (setq indent-line-function 'prolog-indent-line) 164 (set (make-local-variable 'forward-sexp-function)
165 'smie-forward-sexp-command)
166 (set (make-local-variable 'smie-indent-basic) prolog-indent-width)
110 (make-local-variable 'comment-start) 167 (make-local-variable 'comment-start)
111 (setq comment-start "%") 168 (setq comment-start "%")
112 (make-local-variable 'comment-start-skip) 169 (make-local-variable 'comment-start-skip)
@@ -149,65 +206,6 @@ if that value is non-nil."
149 nil nil nil 206 nil nil nil
150 beginning-of-line))) 207 beginning-of-line)))
151 208
152(defun prolog-indent-line ()
153 "Indent current line as Prolog code.
154With argument, indent any additional lines of the same clause
155rigidly along with this one (not yet)."
156 (interactive "p")
157 (let ((indent (prolog-indent-level))
158 (pos (- (point-max) (point))))
159 (beginning-of-line)
160 (indent-line-to indent)
161 (if (> (- (point-max) pos) (point))
162 (goto-char (- (point-max) pos)))))
163
164(defun prolog-indent-level ()
165 "Compute Prolog indentation level."
166 (save-excursion
167 (beginning-of-line)
168 (skip-chars-forward " \t")
169 (cond
170 ((looking-at "%%%") 0) ;Large comment starts
171 ((looking-at "%[^%]") comment-column) ;Small comment starts
172 ((bobp) 0) ;Beginning of buffer
173 (t
174 (let ((empty t) ind more less)
175 (if (looking-at ")")
176 (setq less t) ;Find close
177 (setq less nil))
178 ;; See previous indentation
179 (while empty
180 (forward-line -1)
181 (beginning-of-line)
182 (if (bobp)
183 (setq empty nil)
184 (skip-chars-forward " \t")
185 (if (not (or (looking-at "%[^%]") (looking-at "\n")))
186 (setq empty nil))))
187 (if (bobp)
188 (setq ind 0) ;Beginning of buffer
189 (setq ind (current-column))) ;Beginning of clause
190 ;; See its beginning
191 (if (looking-at "%%[^%]")
192 ind
193 ;; Real prolog code
194 (if (looking-at "(")
195 (setq more t) ;Find open
196 (setq more nil))
197 ;; See its tail
198 (end-of-prolog-clause)
199 (or (bobp) (forward-char -1))
200 (cond ((looking-at "[,(;>]")
201 (if (and more (looking-at "[^,]"))
202 (+ ind prolog-indent-width) ;More indentation
203 (max tab-width ind))) ;Same indentation
204 ((looking-at "-") tab-width) ;TAB
205 ((or less (looking-at "[^.]"))
206 (max (- ind prolog-indent-width) 0)) ;Less indentation
207 (t 0)) ;No indentation
208 )))
209 )))
210
211(defun end-of-prolog-clause () 209(defun end-of-prolog-clause ()
212 "Go to end of clause in this line." 210 "Go to end of clause in this line."
213 (beginning-of-line 1) 211 (beginning-of-line 1)
diff --git a/lisp/simple.el b/lisp/simple.el
index f0c38093d86..48e1148ae6b 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -2077,7 +2077,11 @@ to `shell-command-history'."
2077 2077
2078Like `shell-command' but if COMMAND doesn't end in ampersand, adds `&' 2078Like `shell-command' but if COMMAND doesn't end in ampersand, adds `&'
2079surrounded by whitespace and executes the command asynchronously. 2079surrounded by whitespace and executes the command asynchronously.
2080The output appears in the buffer `*Async Shell Command*'." 2080The output appears in the buffer `*Async Shell Command*'.
2081
2082In Elisp, you will often be better served by calling `start-process'
2083directly, since it offers more control and does not impose the use of a
2084shell (with its need to quote arguments)."
2081 (interactive 2085 (interactive
2082 (list 2086 (list
2083 (read-shell-command "Async shell command: " nil nil 2087 (read-shell-command "Async shell command: " nil nil
@@ -2138,7 +2142,11 @@ If the optional third argument ERROR-BUFFER is non-nil, it is a buffer
2138or buffer name to which to direct the command's standard error output. 2142or buffer name to which to direct the command's standard error output.
2139If it is nil, error output is mingled with regular output. 2143If it is nil, error output is mingled with regular output.
2140In an interactive call, the variable `shell-command-default-error-buffer' 2144In an interactive call, the variable `shell-command-default-error-buffer'
2141specifies the value of ERROR-BUFFER." 2145specifies the value of ERROR-BUFFER.
2146
2147In Elisp, you will often be better served by calling `call-process' or
2148`start-process' directly, since it offers more control and does not impose
2149the use of a shell (with its need to quote arguments)."
2142 2150
2143 (interactive 2151 (interactive
2144 (list 2152 (list