Mercurial > hg > xemacs-beta
comparison lisp/packages/paren.el @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | 4103f0995bd7 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:376386a54a3c |
---|---|
1 ;;; paren.el --- highlight (un)matching parens and whole expressions | |
2 | |
3 ;; Copyright (C) 1993 Free Software Foundation, Inc. | |
4 ;; Copyright (C) 1993, 1994, 1995 Tinker Systems | |
5 ;; | |
6 ;; Author: Jonathan Stigelman <Stig@hackvan.com> | |
7 ;; Note: (some code scammed from simple.el and blink-paren.el) | |
8 ;; Maintainer: Jonathan Stigelman <Stig@hackvan.com> | |
9 ;; Keywords: languages, faces | |
10 | |
11 ;;; This file is part of XEmacs. | |
12 ;;; | |
13 ;;; XEmacs is free software; you can redistribute it and/or modify | |
14 ;;; it under the terms of the GNU General Public License as published by | |
15 ;;; the Free Software Foundation; either version 2 of the License, or | |
16 ;;; (at your option) any later version. | |
17 ;;; | |
18 ;;; XEmacs is distributed in the hope that it will be useful, | |
19 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 ;;; GNU General Public License for more details. | |
22 ;;; | |
23 ;;; You should have received a copy of the GNU General Public License | |
24 ;;; along with XEmacs; if not, write to the Free Software | |
25 ;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
26 | |
27 ;;; Synched up with: Not synched with FSF. | |
28 ;;; Way different from FSF. | |
29 | |
30 ;;; Commentary: | |
31 | |
32 ;; Purpose of this package: | |
33 ;; | |
34 ;; This package highlights matching parens (or whole sexps) for easier | |
35 ;; editing of source code, particularly lisp source code. | |
36 ;; | |
37 ;; The `paren-highlight' hook function runs after each command and | |
38 ;; checks to see if the cursor is at a parenthesis. If so, then it | |
39 ;; highlights, in one of several ways, the matching parenthesis. | |
40 ;; | |
41 ;; Priority is given to matching parentheses right before the cursor because | |
42 ;; that's what makes sense when you're typing a lot of closed parentheses. | |
43 ;; | |
44 ;; This is especially intuitive if you frequently use forward-sexp (M-C-f) | |
45 ;; and backward-sexp (M-C-b) to maneuver around in source code. | |
46 ;; | |
47 ;; Different faces are used for matching and mismatching parens so that it | |
48 ;; is easier to see mistakes as you type them. Audible feedback is optional. | |
49 ;; | |
50 ;; If a (mis)matching paren is offscreen, then a message is sent to the modeline. | |
51 ;; | |
52 ;; If paren-mode is `sexp', entire S-expressions are highlighted instead of | |
53 ;; just matching parens. | |
54 | |
55 ;;; Code: | |
56 | |
57 (defvar paren-message-offscreen t | |
58 "*Display message if matching open paren is offscreen.") | |
59 | |
60 (defvar paren-ding-unmatched nil | |
61 "*Make noise if the cursor is at an unmatched paren. | |
62 | |
63 If T, then typing or passing over an unmatched paren will ring the bell | |
64 using the `paren' sound. If NIL, then the bell will not ring even if an | |
65 unmatched paren is typed. If neither T or NIL, then the bell will not ring | |
66 when the cursor moves over unmatched parens but will ring if one is typed.") | |
67 | |
68 ;;;###autoload | |
69 (defvar paren-mode nil | |
70 "*Sets the style of parenthesis highlighting. | |
71 Valid values are nil, `blink-paren', `paren', and `sexp'. | |
72 nil no parenthesis highlighting. | |
73 blink-paren causes the matching paren to blink. | |
74 paren causes the matching paren to be highlighted but not to blink. | |
75 sexp whole expression enclosed by the local paren at its mate. | |
76 nested (not yet implemented) use variable shading to see the | |
77 nesting of an expression. Also groks regular expressions | |
78 and shell quoting. | |
79 | |
80 This variable is global by default, but you can make it buffer-local and | |
81 highlight parentheses differrently in different major modes.") | |
82 | |
83 (make-face 'paren-match) | |
84 (or (face-differs-from-default-p 'paren-match) | |
85 (copy-face 'highlight 'paren-match)) | |
86 | |
87 (make-face 'paren-mismatch) | |
88 (cond ((face-differs-from-default-p 'paren-mismatch) nil) | |
89 (t (let ((color-tag (list 'x 'color)) | |
90 (mono-tag (list 'x 'mono)) | |
91 (gray-tag (list 'x 'grayscale))) | |
92 (set-face-background 'paren-mismatch "DeepPink" 'global color-tag) | |
93 (set-face-reverse-p 'paren-mismatch t 'global 'tty) | |
94 (set-face-background 'paren-mismatch [modeline background] 'global | |
95 mono-tag) | |
96 (set-face-foreground 'paren-mismatch [modeline foreground] 'global | |
97 mono-tag) | |
98 (set-face-background 'paren-mismatch [modeline background] 'global | |
99 gray-tag) | |
100 (set-face-foreground 'paren-mismatch [modeline foreground] 'global | |
101 gray-tag)))) | |
102 | |
103 (make-face 'paren-blink-off) | |
104 (or (face-differs-from-default-p 'paren-blink-off) | |
105 (set-face-foreground 'paren-blink-off (face-background 'default))) | |
106 | |
107 ;; this is either paren-match or paren-mismatch... | |
108 (defvar paren-blink-on-face nil) | |
109 | |
110 (defvar paren-blink-interval 0.2 | |
111 "*If the cursor is on a parenthesis, the matching parenthesis will blink. | |
112 This variable controls how long each phase of the blink lasts in seconds. | |
113 This should be a fractional part of a second (a float.)") | |
114 | |
115 (defvar paren-max-blinks (* 5 60 5) ; 5 minutes is plenty... | |
116 ;; idea from Eric Eide <eeide@jaguar.cs.utah.edu> | |
117 "*Maximum number of times that a matching parenthesis will blink. | |
118 Set this to NIL if you want indefinite blinking.") | |
119 | |
120 ;; timeout to blink the face | |
121 (defvar paren-timeout-id nil) | |
122 | |
123 ;; Code: | |
124 | |
125 (defvar paren-n-blinks) | |
126 (defvar paren-extent nil) | |
127 | |
128 ;; used to suppress messages from the same position so that other messages | |
129 ;; can be seen in the modeline. | |
130 (make-variable-buffer-local | |
131 (defvar paren-message-suppress nil)) | |
132 | |
133 (defsubst pos-visible-in-window-safe (pos) | |
134 "safe version of pos-visible-in-window-p" | |
135 (condition-case nil | |
136 ;; #### - is this needed in XEmacs??? | |
137 (pos-visible-in-window-p pos) | |
138 (args-out-of-range nil))) | |
139 | |
140 ;; called before a new command is executed in the pre-command-hook | |
141 ;; cleanup by removing the extent and the time-out | |
142 (defun paren-nuke-extent () | |
143 (condition-case c ; don't ever signal an error in pre-command-hook! | |
144 (let ((inhibit-quit t)) | |
145 (if paren-timeout-id | |
146 (disable-timeout (prog1 paren-timeout-id | |
147 (setq paren-timeout-id nil)))) | |
148 (if paren-extent | |
149 (delete-extent (prog1 paren-extent | |
150 (setq paren-extent nil))))) | |
151 (error | |
152 (message "paren-nuke-extent error! %s" c)))) | |
153 | |
154 ;; callback for the timeout | |
155 ;; swap the face of the extent on the matching paren | |
156 (defun paren-blink-timeout (arg) | |
157 ;; The extent could have been deleted for some reason and not point to a | |
158 ;; buffer anymore. So catch any error to remove the timeout. | |
159 (condition-case () | |
160 (if (and paren-max-blinks | |
161 (> (setq paren-n-blinks (1+ paren-n-blinks)) paren-max-blinks)) | |
162 (paren-nuke-extent) | |
163 (set-extent-face paren-extent | |
164 (if (eq (extent-face paren-extent) | |
165 paren-blink-on-face) | |
166 'paren-blink-off | |
167 paren-blink-on-face))) | |
168 (error (paren-nuke-extent)))) | |
169 | |
170 | |
171 (defun paren-describe-match (pos mismatch) | |
172 (or (window-minibuffer-p (selected-window)) | |
173 (save-excursion | |
174 (goto-char pos) | |
175 (message "%s %s" | |
176 (if mismatch "MISMATCH:" "Matches") | |
177 ;; if there's stuff on this line preceding the paren, then | |
178 ;; display text from beginning of line to paren. | |
179 ;; | |
180 ;; If, however, the paren is at the beginning of a line, then | |
181 ;; skip whitespace forward and display text from paren to end | |
182 ;; of the next line containing nonspace text. | |
183 ;; | |
184 ;; If paren-backwards-message gravity were implemented, then | |
185 ;; perhaps it would reverse this behavior and look to the | |
186 ;; previous line for meaningful context. | |
187 (if (save-excursion | |
188 (skip-chars-backward " \t") | |
189 (not (bolp))) | |
190 (concat (buffer-substring | |
191 (progn (beginning-of-line) (point)) | |
192 (1+ pos)) "...") | |
193 (buffer-substring | |
194 pos (progn | |
195 (forward-char 1) | |
196 (skip-chars-forward "\n \t") | |
197 (end-of-line) | |
198 (point)))))))) | |
199 | |
200 (defun paren-maybe-ding () | |
201 (and (or (eq paren-ding-unmatched t) | |
202 (and paren-ding-unmatched | |
203 (eq this-command 'self-insert-command))) | |
204 (progn | |
205 (message "Unmatched parenthesis.") | |
206 (ding nil 'paren)))) | |
207 | |
208 ;; Find the place to show, if there is one, | |
209 ;; and show it until input arrives. | |
210 (defun paren-highlight () | |
211 "This highlights matching parentheses. | |
212 | |
213 See the variables: | |
214 paren-message-offscreen use modeline when matchingparen is offscreen? | |
215 paren-ding-unmatched make noise when passing over mismatched parens? | |
216 paren-mode 'blink-paren, 'paren, or 'sexp | |
217 blink-matching-paren-distance maximum distance to search for parens. | |
218 | |
219 and the following faces: | |
220 paren-match, paren-mismatch, paren-blink-off" | |
221 | |
222 ;; I suppose I could check here to see if a keyboard macro is executing, | |
223 ;; but I did a quick empirical check and couldn't tell that there was any | |
224 ;; difference in performance | |
225 | |
226 (let ((oldpos (point)) | |
227 (pface nil) ; face for paren...nil kills the overlay | |
228 (dir (and paren-mode | |
229 (not (input-pending-p)) | |
230 (not executing-kbd-macro) | |
231 (cond ((eq (char-syntax (preceding-char)) ?\)) | |
232 -1) | |
233 ((eq (char-syntax (following-char)) ?\() | |
234 1)))) | |
235 pos mismatch) | |
236 | |
237 (save-excursion | |
238 (if (or (not dir) | |
239 (not (save-restriction | |
240 ;; Determine the range within which to look for a match. | |
241 (if blink-matching-paren-distance | |
242 (narrow-to-region | |
243 (max (point-min) | |
244 (- (point) blink-matching-paren-distance)) | |
245 (min (point-max) | |
246 (+ (point) blink-matching-paren-distance)))) | |
247 | |
248 ;; Scan across one sexp within that range. | |
249 (condition-case nil | |
250 (setq pos (scan-sexps (point) dir)) | |
251 ;; NOTE - if blink-matching-paren-distance is set, | |
252 ;; then we can have spurious unmatched parens. | |
253 (error (paren-maybe-ding) | |
254 nil))))) | |
255 | |
256 ;; do nothing if we didn't find a matching paren... | |
257 nil | |
258 | |
259 ;; See if the "matching" paren is the right kind of paren | |
260 ;; to match the one we started at. | |
261 (let ((beg (min pos oldpos)) (end (max pos oldpos))) | |
262 (setq mismatch | |
263 (and (/= (char-syntax (char-after beg)) ?\\) | |
264 (/= (char-syntax (char-after beg)) ?\$) | |
265 (/= (char-after (1- end)) | |
266 (logand (lsh (aref (syntax-table) | |
267 (char-after beg)) | |
268 -8) | |
269 255)))) | |
270 (if (eq paren-mode 'sexp) | |
271 (setq paren-extent (make-extent beg end)))) | |
272 (and mismatch | |
273 (paren-maybe-ding)) | |
274 (setq pface (if mismatch | |
275 'paren-mismatch | |
276 'paren-match)) | |
277 (and (memq paren-mode '(blink-paren paren)) | |
278 (setq paren-extent (make-extent (- pos dir) pos))) | |
279 | |
280 (if (and paren-message-offscreen | |
281 (eq dir -1) | |
282 (not (eq paren-message-suppress (point))) | |
283 (not (window-minibuffer-p (selected-window))) | |
284 (not (pos-visible-in-window-safe pos))) | |
285 (progn | |
286 (setq paren-message-suppress (point)) | |
287 (paren-describe-match pos mismatch)) | |
288 (setq paren-message-suppress nil)) | |
289 | |
290 ;; put the right face on the extent | |
291 (cond (pface | |
292 (set-extent-face paren-extent pface) | |
293 (set-extent-priority paren-extent 100) ; want this to be high | |
294 (and (eq paren-mode 'blink-paren) | |
295 (setq paren-blink-on-face pface | |
296 paren-n-blinks 0 | |
297 paren-timeout-id | |
298 (and paren-blink-interval | |
299 (add-timeout paren-blink-interval | |
300 'paren-blink-timeout | |
301 nil | |
302 paren-blink-interval)))))) | |
303 )))) | |
304 | |
305 ;; kill off the competition, er, uh, eliminate redundancy... | |
306 (setq post-command-hook (delq 'show-paren-command-hook post-command-hook)) | |
307 (setq pre-command-hook (delq 'blink-paren-pre-command pre-command-hook)) | |
308 (setq post-command-hook (delq 'blink-paren-post-command post-command-hook)) | |
309 | |
310 ;;;###autoload | |
311 (defun paren-set-mode (arg &optional quiet) | |
312 "Cycles through possible values for `paren-mode', force off with negative arg. | |
313 When called from lisp, a symbolic value for `paren-mode' can be pased directly. | |
314 See also `paren-mode' and `paren-highlight'." | |
315 (interactive "P") | |
316 (let* ((paren-modes '(blink-paren paren sexp)) | |
317 (paren-next-modes (cons nil (append paren-modes (list nil))))) | |
318 (setq paren-mode (if (and (numberp arg) (< arg 0)) | |
319 nil ; turn paren highlighting off | |
320 (cond ((and arg (symbolp arg)) arg) | |
321 ((and (numberp arg) (> arg 0)) | |
322 (nth (1- arg) paren-modes)) | |
323 ((numberp arg) nil) | |
324 (t (car (cdr (memq paren-mode | |
325 paren-next-modes))))) | |
326 ))) | |
327 (cond (paren-mode | |
328 (add-hook 'post-command-hook 'paren-highlight) | |
329 (add-hook 'pre-command-hook 'paren-nuke-extent) | |
330 (setq blink-matching-paren nil)) | |
331 ((not (local-variable-p 'paren-mode (current-buffer))) | |
332 (remove-hook 'post-command-hook 'paren-highlight) | |
333 (remove-hook 'pre-command-hook 'paren-nuke-extent) | |
334 (paren-nuke-extent) ; overkill | |
335 (setq blink-matching-paren t) | |
336 )) | |
337 (or quiet (message "Paren mode is %s" (or paren-mode "OFF")))) | |
338 | |
339 (eval-when-compile | |
340 ;; suppress compiler warning. | |
341 (defvar highlight-paren-expression)) | |
342 | |
343 (paren-set-mode (if (and (boundp 'highlight-paren-expression) | |
344 ;; bletcherous blink-paren no-naming-convention | |
345 highlight-paren-expression) | |
346 'sexp | |
347 (if (eq 'x (device-type (selected-device))) | |
348 'blink-paren | |
349 'paren)) | |
350 t) | |
351 | |
352 ;;;###autoload | |
353 (make-obsolete 'blink-paren 'paren-set-mode) | |
354 | |
355 ;;;###autoload | |
356 (defun blink-paren (&optional arg) | |
357 "Obsolete. Use `paren-set-mode' instead." | |
358 (interactive "P") | |
359 (paren-set-mode (if (and (numberp arg) (> arg 0)) | |
360 'blink-paren -1) t)) | |
361 | |
362 (provide 'blink-paren) | |
363 (provide 'paren) | |
364 | |
365 ;; Local Variables: | |
366 ;; byte-optimize: t | |
367 ;; End: | |
368 | |
369 ;;; paren.el ends here |