aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2010-09-03 15:06:51 +0200
committerStefan Monnier2010-09-03 15:06:51 +0200
commit3b843809cab8990cb1e239ef7d60262b6139ba34 (patch)
treebaea3fbeee58ce57dea651b2b9e59415c88a08bf
parent0da208548b7029e608567112c0f4613613aae688 (diff)
downloademacs-3b843809cab8990cb1e239ef7d60262b6139ba34.tar.gz
emacs-3b843809cab8990cb1e239ef7d60262b6139ba34.zip
* lisp/electric.el (electricity): New group.
(electric-indent-chars): New var. (electric-indent-post-self-insert-function): New fun. (electric-indent-mode): New minor mode. (electric-pair-skip-self): New custom. (electric-pair-post-self-insert-function): New function. (electric-pair-mode): New minor mode.
-rw-r--r--etc/NEWS2
-rw-r--r--lisp/ChangeLog8
-rw-r--r--lisp/electric.el140
3 files changed, 149 insertions, 1 deletions
diff --git a/etc/NEWS b/etc/NEWS
index be1c4aac946..72075945f62 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -427,6 +427,8 @@ system or session bus.
427 427
428* New Modes and Packages in Emacs 24.1 428* New Modes and Packages in Emacs 24.1
429 429
430** New global minor modes electric-pair-mode and electric-indent-mode.
431
430** pcase.el provides the ML-style pattern matching macro `pcase'. 432** pcase.el provides the ML-style pattern matching macro `pcase'.
431 433
432** smie.el is a package providing a simple generic indentation engine. 434** smie.el is a package providing a simple generic indentation engine.
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 1c83ea2ad61..65beeb6e531 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,5 +1,13 @@
12010-09-03 Stefan Monnier <monnier@iro.umontreal.ca> 12010-09-03 Stefan Monnier <monnier@iro.umontreal.ca>
2 2
3 * electric.el (electricity): New group.
4 (electric-indent-chars): New var.
5 (electric-indent-post-self-insert-function): New fun.
6 (electric-indent-mode): New minor mode.
7 (electric-pair-skip-self): New custom.
8 (electric-pair-post-self-insert-function): New function.
9 (electric-pair-mode): New minor mode.
10
3 * calc/calc-aent.el (calcAlg-blink-matching-check): New fun, to replace 11 * calc/calc-aent.el (calcAlg-blink-matching-check): New fun, to replace
4 calcAlg-blink-matching-open. 12 calcAlg-blink-matching-open.
5 (calc-alg-ent-map, calc-alg-ent-esc-map): Initialize in the declaration. 13 (calc-alg-ent-map, calc-alg-ent-esc-map): Initialize in the declaration.
diff --git a/lisp/electric.el b/lisp/electric.el
index fb3e462efba..48d865d8957 100644
--- a/lisp/electric.el
+++ b/lisp/electric.el
@@ -24,10 +24,23 @@
24 24
25;;; Commentary: 25;;; Commentary:
26 26
27; zaaaaaaap 27;; "Electric" has been used in Emacs to refer to different things.
28;; Among them:
29;;
30;; - electric modes and buffers: modes that typically pop-up in a modal kind of
31;; way a transient buffer that automatically disappears as soon as the user
32;; is done with it.
33;;
34;; - electric keys: self inserting keys which additionally perform some side
35;; operation which happens to be often convenient at that time. Examples of
36;; such side operations are: reindenting code, inserting a newline,
37;; ... auto-fill-mode and abbrev-mode can be considered as built-in forms of
38;; electric key behavior.
28 39
29;;; Code: 40;;; Code:
30 41
42(eval-when-compile (require 'cl))
43
31;; This loop is the guts for non-standard modes which retain control 44;; This loop is the guts for non-standard modes which retain control
32;; until some event occurs. It is a `do-forever', the only way out is 45;; until some event occurs. It is a `do-forever', the only way out is
33;; to throw. It assumes that you have set up the keymap, window, and 46;; to throw. It assumes that you have set up the keymap, window, and
@@ -157,6 +170,131 @@
157 (fit-window-to-buffer win max-height)) 170 (fit-window-to-buffer win max-height))
158 win))) 171 win)))
159 172
173;;; Electric keys.
174
175(defgroup electricity ()
176 "Electric behavior for self inserting keys."
177 :group 'editing)
178
179;; Electric indentation.
180
181(defvar electric-indent-chars '(?\n)
182 "Characters that should cause automatic reindentation.")
183
184(defun electric-indent-post-self-insert-function ()
185 ;; FIXME: This reindents the current line, but what we really want instead is
186 ;; to reindent the whole affected text. That's the current line for simple
187 ;; cases, but not all cases. We do take care of the newline case in an
188 ;; ad-hoc fashion, but there are still missing cases such as the case of
189 ;; electric-pair-mode wrapping a region with a pair of parens.
190 ;; There might be a way to get it working by analyzing buffer-undo-list, but
191 ;; it looks challenging.
192 (when (and (memq last-command-event electric-indent-chars)
193 ;; Don't reindent while inserting spaces at beginning of line.
194 (or (not (memq last-command-event '(?\s ?\t)))
195 (save-excursion (skip-chars-backward " \t") (not (bolp))))
196 ;; Not in a string or comment.
197 (not (nth 8 (syntax-ppss))))
198 ;; For newline, we want to reindent both lines and basically behave like
199 ;; reindent-then-newline-and-indent (whose code we hence copied).
200 (when (and (eq last-command-event ?\n)
201 ;; Sanity check.
202 (eq (char-before) last-command-event))
203 (let ((pos (copy-marker (1- (point)) t)))
204 (save-excursion
205 (goto-char pos)
206 (indent-according-to-mode)
207 ;; We are at EOL before the call to indent-according-to-mode, and
208 ;; after it we usually are as well, but not always. We tried to
209 ;; address it with `save-excursion' but that uses a normal marker
210 ;; whereas we need `move after insertion', so we do the
211 ;; save/restore by hand.
212 (goto-char pos)
213 ;; Remove the trailing whitespace after indentation because
214 ;; indentation may (re)introduce the whitespace.
215 (delete-horizontal-space t))))
216 (indent-according-to-mode)))
217
218;;;###autoload
219(define-minor-mode electric-indent-mode
220 "Automatically reindent lines of code when inserting particular chars.
221`electric-indent-chars' specifies the set of chars that should cause reindentation."
222 :global t
223 :group 'electricity
224 (if electric-indent-mode
225 (add-hook 'post-self-insert-hook
226 #'electric-indent-post-self-insert-function)
227 (remove-hook 'post-self-insert-hook
228 #'electric-indent-post-self-insert-function)))
229
230;; Electric pairing.
231
232(defcustom electric-pair-skip-self t
233 "If non-nil, skip char instead of inserting a second closing paren.
234When inserting a closing paren character right before the same character,
235just skip that character instead, so that hitting ( followed by ) results
236in \"()\" rather than \"())\".
237This can be convenient for people who find it easier to hit ) than C-f."
238 :type 'boolean)
239
240(defun electric-pair-post-self-insert-function ()
241 (let* ((syntax (and (eq (char-before) last-command-event) ; Sanity check.
242 (char-syntax last-command-event)))
243 ;; FIXME: when inserting the closer, we should maybe use
244 ;; self-insert-command, although it may prove tricky running
245 ;; post-self-insert-hook recursively, and we wouldn't want to trigger
246 ;; blink-matching-open.
247 (closer (if (eq syntax ?\()
248 (cdr (aref (syntax-table) last-command-event))
249 last-command-event)))
250 (cond
251 ;; Wrap a pair around the active region.
252 ((and (memq syntax '(?\( ?\" ?\$)) (use-region-p))
253 (if (> (mark) (point))
254 (goto-char (mark))
255 ;; We already inserted the open-paren but at the end of the region,
256 ;; so we have to remove it and start over.
257 (delete-char -1)
258 (save-excursion
259 (goto-char (mark))
260 (insert last-command-event)))
261 (insert closer))
262 ;; Backslash-escaped: no pairing, no skipping.
263 ((save-excursion
264 (goto-char (1- (point)))
265 (not (zerop (% (skip-syntax-backward "\\") 2))))
266 nil)
267 ;; Skip self.
268 ((and (memq syntax '(?\) ?\" ?\$))
269 electric-pair-skip-self
270 (eq (char-after) last-command-event))
271 ;; This is too late: rather than insert&delete we'd want to only skip (or
272 ;; insert in overwrite mode). The difference is in what goes in the
273 ;; undo-log and in the intermediate state which might be visible to other
274 ;; post-self-insert-hook. We'll just have to live with it for now.
275 (delete-char 1))
276 ;; Insert matching pair.
277 ((not (or (not (memq syntax `(?\( ?\" ?\$)))
278 overwrite-mode
279 ;; I find it more often preferable not to pair when the
280 ;; same char is next.
281 (eq last-command-event (char-after))
282 (eq last-command-event (char-before (1- (point))))
283 ;; I also find it often preferable not to pair next to a word.
284 (eq (char-syntax (following-char)) ?w)))
285 (save-excursion (insert closer))))))
286
287;;;###autoload
288(define-minor-mode electric-pair-mode
289 "Automatically pair-up parens when inserting an open paren."
290 :global t
291 :group 'electricity
292 (if electric-pair-mode
293 (add-hook 'post-self-insert-hook
294 #'electric-pair-post-self-insert-function)
295 (remove-hook 'post-self-insert-hook
296 #'electric-pair-post-self-insert-function)))
297
160(provide 'electric) 298(provide 'electric)
161 299
162;; arch-tag: dae045eb-dc2d-4fb7-9f27-9cc2ce277be8 300;; arch-tag: dae045eb-dc2d-4fb7-9f27-9cc2ce277be8