Mercurial > emacs
comparison lisp/emacs-lisp/smie.el @ 110161:eb977e012180
Provide blink-matching support to SMIE.
* lisp/emacs-lisp/smie.el (smie-bnf-closer-alist): New function.
(smie-blink-matching-triggers, smie-blink-matching-inners): New vars.
(smie-blink-matching-check, smie-blink-matching-open): New functions.
author | Stefan Monnier <monnier@iro.umontreal.ca> |
---|---|
date | Fri, 03 Sep 2010 13:18:45 +0200 |
parents | 5cab4c4229ff |
children | 06323f4d421b |
comparison
equal
deleted
inserted
replaced
110160:c32f6ba217a2 | 110161:eb977e012180 |
---|---|
72 | 72 |
73 (defvar comment-continue) | 73 (defvar comment-continue) |
74 (declare-function comment-string-strip "newcomment" (str beforep afterp)) | 74 (declare-function comment-string-strip "newcomment" (str beforep afterp)) |
75 | 75 |
76 ;;; Building precedence level tables from BNF specs. | 76 ;;; Building precedence level tables from BNF specs. |
77 | |
78 ;; We have 4 different representations of a "grammar": | |
79 ;; - a BNF table, which is a list of BNF rules of the form | |
80 ;; (NONTERM RHS1 ... RHSn) where each RHS is a list of terminals (tokens) | |
81 ;; or nonterminals. Any element in these lists which does not appear as | |
82 ;; the `car' of a BNF rule is taken to be a terminal. | |
83 ;; - A list of precedences (key word "precs"), is a list, sorted | |
84 ;; from lowest to highest precedence, of precedence classes that | |
85 ;; have the form (ASSOCIATIVITY TERMINAL1 .. TERMINALn), where | |
86 ;; ASSOCIATIVITY can be `assoc', `left', `right' or `nonassoc'. | |
87 ;; - a 2 dimensional precedence table (key word "prec2"), is a 2D | |
88 ;; table recording the precedence relation (can be `<', `=', `>', or | |
89 ;; nil) between each pair of tokens. | |
90 ;; - a precedence-level table (key word "levels"), while is a alist | |
91 ;; giving for each token its left and right precedence level (a | |
92 ;; number or nil). This is used in `smie-op-levels'. | |
93 ;; The prec2 tables are only intermediate data structures: the source | |
94 ;; code normally provides a mix of BNF and precs tables, and then | |
95 ;; turns them into a levels table, which is what's used by the rest of | |
96 ;; the SMIE code. | |
77 | 97 |
78 (defun smie-set-prec2tab (table x y val &optional override) | 98 (defun smie-set-prec2tab (table x y val &optional override) |
79 (assert (and x y)) | 99 (assert (and x y)) |
80 (let* ((key (cons x y)) | 100 (let* ((key (cons x y)) |
81 (old (gethash key table))) | 101 (old (gethash key table))) |
204 '= override))) | 224 '= override))) |
205 (t (smie-set-prec2tab prec2 (car rhs) (cadr rhs) '= override))) | 225 (t (smie-set-prec2tab prec2 (car rhs) (cadr rhs) '= override))) |
206 (setq rhs (cdr rhs))))) | 226 (setq rhs (cdr rhs))))) |
207 prec2)) | 227 prec2)) |
208 | 228 |
229 ;; (defun smie-prec2-closer-alist (prec2 include-inners) | |
230 ;; "Build a closer-alist from a PREC2 table. | |
231 ;; The return value is in the same form as `smie-closer-alist'. | |
232 ;; INCLUDE-INNERS if non-nil means that inner keywords will be included | |
233 ;; in the table, e.g. the table will include things like (\"if\" . \"else\")." | |
234 ;; (let* ((non-openers '()) | |
235 ;; (non-closers '()) | |
236 ;; ;; For each keyword, this gives the matching openers, if any. | |
237 ;; (openers (make-hash-table :test 'equal)) | |
238 ;; (closers '()) | |
239 ;; (done nil)) | |
240 ;; ;; First, find the non-openers and non-closers. | |
241 ;; (maphash (lambda (k v) | |
242 ;; (unless (or (eq v '<) (member (cdr k) non-openers)) | |
243 ;; (push (cdr k) non-openers)) | |
244 ;; (unless (or (eq v '>) (member (car k) non-closers)) | |
245 ;; (push (car k) non-closers))) | |
246 ;; prec2) | |
247 ;; ;; Then find the openers and closers. | |
248 ;; (maphash (lambda (k _) | |
249 ;; (unless (member (car k) non-openers) | |
250 ;; (puthash (car k) (list (car k)) openers)) | |
251 ;; (unless (or (member (cdr k) non-closers) | |
252 ;; (member (cdr k) closers)) | |
253 ;; (push (cdr k) closers))) | |
254 ;; prec2) | |
255 ;; ;; Then collect the matching elements. | |
256 ;; (while (not done) | |
257 ;; (setq done t) | |
258 ;; (maphash (lambda (k v) | |
259 ;; (when (eq v '=) | |
260 ;; (let ((aopeners (gethash (car k) openers)) | |
261 ;; (dopeners (gethash (cdr k) openers)) | |
262 ;; (new nil)) | |
263 ;; (dolist (o aopeners) | |
264 ;; (unless (member o dopeners) | |
265 ;; (setq new t) | |
266 ;; (push o dopeners))) | |
267 ;; (when new | |
268 ;; (setq done nil) | |
269 ;; (puthash (cdr k) dopeners openers))))) | |
270 ;; prec2)) | |
271 ;; ;; Finally, dump the resulting table. | |
272 ;; (let ((alist '())) | |
273 ;; (maphash (lambda (k v) | |
274 ;; (when (or include-inners (member k closers)) | |
275 ;; (dolist (opener v) | |
276 ;; (unless (equal opener k) | |
277 ;; (push (cons opener k) alist))))) | |
278 ;; openers) | |
279 ;; alist))) | |
280 | |
281 (defun smie-bnf-closer-alist (bnf &optional no-inners) | |
282 ;; We can also build this closer-alist table from a prec2 table, | |
283 ;; but it takes more work, and the order is unpredictable, which | |
284 ;; is a problem for smie-close-block. | |
285 ;; More convenient would be to build it from a levels table since we | |
286 ;; always have this table (contrary to the BNF), but it has all the | |
287 ;; disadvantages of the prec2 case plus the disadvantage that the levels | |
288 ;; table has lost some info which would result in extra invalid pairs. | |
289 "Build a closer-alist from a BNF table. | |
290 The return value is in the same form as `smie-closer-alist'. | |
291 NO-INNERS if non-nil means that inner keywords will be excluded | |
292 from the table, e.g. the table will not include things like (\"if\" . \"else\")." | |
293 (let ((nts (mapcar #'car bnf)) ;non terminals. | |
294 (alist '())) | |
295 (dolist (nt bnf) | |
296 (dolist (rhs (cdr nt)) | |
297 (unless (or (< (length rhs) 2) (member (car rhs) nts)) | |
298 (if no-inners | |
299 (let ((last (car (last rhs)))) | |
300 (unless (member last nts) | |
301 (pushnew (cons (car rhs) last) alist :test #'equal))) | |
302 ;; Reverse so that the "real" closer gets there first, | |
303 ;; which is important for smie-close-block. | |
304 (dolist (term (reverse (cdr rhs))) | |
305 (unless (member term nts) | |
306 (pushnew (cons (car rhs) term) alist :test #'equal))))))) | |
307 (nreverse alist))) | |
308 | |
309 | |
209 (defun smie-prec2-levels (prec2) | 310 (defun smie-prec2-levels (prec2) |
210 ;; FIXME: Rather than only return an alist of precedence levels, we should | 311 ;; FIXME: Rather than only return an alist of precedence levels, we should |
211 ;; also extract other useful data from it: | 312 ;; also extract other useful data from it: |
212 ;; - matching sets of block openers&closers (which can otherwise become | 313 ;; - matching sets of block openers&closers (which can otherwise become |
213 ;; collapsed into a single equivalence class in smie-op-levels) for | 314 ;; collapsed into a single equivalence class in smie-op-levels) for |
221 "Take a 2D precedence table and turn it into an alist of precedence levels. | 322 "Take a 2D precedence table and turn it into an alist of precedence levels. |
222 PREC2 is a table as returned by `smie-precs-precedence-table' or | 323 PREC2 is a table as returned by `smie-precs-precedence-table' or |
223 `smie-bnf-precedence-table'." | 324 `smie-bnf-precedence-table'." |
224 ;; For each operator, we create two "variables" (corresponding to | 325 ;; For each operator, we create two "variables" (corresponding to |
225 ;; the left and right precedence level), which are represented by | 326 ;; the left and right precedence level), which are represented by |
226 ;; cons cells. Those are the vary cons cells that appear in the | 327 ;; cons cells. Those are the very cons cells that appear in the |
227 ;; final `table'. The value of each "variable" is kept in the `car'. | 328 ;; final `table'. The value of each "variable" is kept in the `car'. |
228 (let ((table ()) | 329 (let ((table ()) |
229 (csts ()) | 330 (csts ()) |
230 (eqs ()) | 331 (eqs ()) |
231 tmp x y) | 332 tmp x y) |
593 (goto-char start) | 694 (goto-char start) |
594 (signal 'scan-error | 695 (signal 'scan-error |
595 (list "Containing expression ends prematurely" | 696 (list "Containing expression ends prematurely" |
596 pos end)))) | 697 pos end)))) |
597 (t))))))) | 698 (t))))))) |
699 | |
700 (defvar smie-blink-matching-triggers '(?\s ?\n) | |
701 "Chars which might trigger `blink-matching-open'. | |
702 These can include the final chars of end-tokens, or chars that are | |
703 typically inserted right after an end token. | |
704 I.e. a good choice can be: | |
705 (delete-dups | |
706 (mapcar (lambda (kw) (aref (cdr kw) (1- (length (cdr kw))))) | |
707 smie-closer-alist))") | |
708 | |
709 (defcustom smie-blink-matching-inners t | |
710 "Whether SMIE should blink to matching opener for inner keywords. | |
711 If non-nil, it will blink not only for \"begin..end\" but also for \"if...else\"." | |
712 :type 'boolean) | |
713 | |
714 (defun smie-blink-matching-check (start end) | |
715 (save-excursion | |
716 (goto-char end) | |
717 (let ((ender (funcall smie-backward-token-function))) | |
718 (cond | |
719 ((not (and ender (rassoc ender smie-closer-alist))) | |
720 ;; This not is one of the begin..end we know how to check. | |
721 (blink-matching-check-mismatch start end)) | |
722 ((not start) t) | |
723 (t | |
724 (goto-char start) | |
725 (let ((starter (funcall smie-forward-token-function))) | |
726 (not (member (cons starter ender) smie-closer-alist)))))))) | |
727 | |
728 (defun smie-blink-matching-open () | |
729 "Blink the matching opener when applicable. | |
730 This uses SMIE's tables and is expected to be placed on `post-self-insert-hook'." | |
731 (when (and blink-matching-paren | |
732 smie-closer-alist ; Optimization. | |
733 (eq (char-before) last-command-event) ; Sanity check. | |
734 (memq last-command-event smie-blink-matching-triggers) | |
735 (save-excursion | |
736 ;; FIXME: Here we assume that closers all end | |
737 ;; with a word-syntax char. | |
738 (unless (eq ?\w (char-syntax last-command-event)) | |
739 (forward-char -1)) | |
740 (and (looking-at "\\>") | |
741 (not (nth 8 (syntax-ppss)))))) | |
742 (save-excursion | |
743 (let ((pos (point)) | |
744 (token (funcall smie-backward-token-function))) | |
745 (if (= 1 (length token)) | |
746 ;; The trigger char is itself a token but is not | |
747 ;; one of the closers (e.g. ?\; in Octave mode), | |
748 ;; so go back to the previous token | |
749 (setq token (save-excursion | |
750 (funcall smie-backward-token-function))) | |
751 (goto-char pos)) | |
752 ;; Here we assume that smie-backward-token-function | |
753 ;; returns a token that is a string and whose content | |
754 ;; match the buffer's representation of this token. | |
755 (when (and (> (length token) 1) (stringp token) | |
756 (memq (aref token (1- (length token))) | |
757 smie-blink-matching-triggers) | |
758 (not (eq (aref token (1- (length token))) | |
759 last-command-event))) | |
760 ;; Token ends with a trigger char, so don't blink for | |
761 ;; anything else than this trigger char, lest we'd blink | |
762 ;; both when inserting the trigger char and when inserting a | |
763 ;; subsequent SPC. | |
764 (setq token nil)) | |
765 (when (and (rassoc token smie-closer-alist) | |
766 (or smie-blink-matching-inners | |
767 (null (nth 2 (assoc token smie-op-levels))))) | |
768 ;; The major mode might set blink-matching-check-function | |
769 ;; buffer-locally so that interactive calls to | |
770 ;; blink-matching-open work right, but let's not presume | |
771 ;; that's the case. | |
772 (let ((blink-matching-check-function #'smie-blink-matching-check)) | |
773 (blink-matching-open))))))) | |
598 | 774 |
599 ;;; The indentation engine. | 775 ;;; The indentation engine. |
600 | 776 |
601 (defcustom smie-indent-basic 4 | 777 (defcustom smie-indent-basic 4 |
602 "Basic amount of indentation." | 778 "Basic amount of indentation." |