Line data Source code
1 : ;;; electric.el --- window maker and Command loop for `electric' modes
2 :
3 : ;; Copyright (C) 1985-1986, 1995, 2001-2017 Free Software Foundation,
4 : ;; Inc.
5 :
6 : ;; Author: K. Shane Hartman
7 : ;; Maintainer: emacs-devel@gnu.org
8 : ;; Keywords: extensions
9 :
10 : ;; This file is part of GNU Emacs.
11 :
12 : ;; GNU Emacs is free software: you can redistribute it and/or modify
13 : ;; it under the terms of the GNU General Public License as published by
14 : ;; the Free Software Foundation, either version 3 of the License, or
15 : ;; (at your option) any later version.
16 :
17 : ;; GNU Emacs is distributed in the hope that it will be useful,
18 : ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 : ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 : ;; GNU General Public License for more details.
21 :
22 : ;; You should have received a copy of the GNU General Public License
23 : ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
24 :
25 : ;;; Commentary:
26 :
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.
39 :
40 : ;;; Code:
41 :
42 : ;; This loop is the guts for non-standard modes which retain control
43 : ;; until some event occurs. It is a `do-forever', the only way out is
44 : ;; to throw. It assumes that you have set up the keymap, window, and
45 : ;; everything else: all it does is read commands and execute them -
46 : ;; providing error messages should one occur (if there is no loop
47 : ;; function - which see). The required argument is a tag which should
48 : ;; expect a value of nil if the user decides to punt. The second
49 : ;; argument is the prompt to be used: if nil, use "->", if 'noprompt,
50 : ;; don't use a prompt, if a string, use that string as prompt, and if
51 : ;; a function of no variable, it will be evaluated in every iteration
52 : ;; of the loop and its return value, which can be nil, 'noprompt or a
53 : ;; string, will be used as prompt. Given third argument non-nil, it
54 : ;; INHIBITS quitting unless the user types C-g at toplevel. This is
55 : ;; so user can do things like C-u C-g and not get thrown out. Fourth
56 : ;; argument, if non-nil, should be a function of two arguments which
57 : ;; is called after every command is executed. The fifth argument, if
58 : ;; provided, is the state variable for the function. If the
59 : ;; loop-function gets an error, the loop will abort WITHOUT throwing
60 : ;; (moral: use unwind-protect around call to this function for any
61 : ;; critical stuff). The second argument for the loop function is the
62 : ;; conditions for any error that occurred or nil if none.
63 :
64 : (defun Electric-command-loop (return-tag
65 : &optional prompt inhibit-quitting
66 : loop-function loop-state)
67 :
68 0 : (let (cmd
69 : (err nil)
70 0 : (inhibit-quit inhibit-quitting)
71 0 : (prompt-string prompt))
72 0 : (while t
73 0 : (if (functionp prompt)
74 0 : (setq prompt-string (funcall prompt)))
75 0 : (if (not (stringp prompt-string))
76 0 : (setq prompt-string (unless (eq prompt-string 'noprompt) "->")))
77 0 : (setq cmd (read-key-sequence prompt-string))
78 0 : (setq last-command-event (aref cmd (1- (length cmd)))
79 0 : this-command (key-binding cmd t)
80 0 : cmd this-command)
81 0 : (if (or (prog1 quit-flag (setq quit-flag nil))
82 0 : (eq last-input-event ?\C-g))
83 0 : (progn (setq unread-command-events nil
84 0 : prefix-arg nil)
85 : ;; If it wasn't canceling a prefix character, then quit.
86 0 : (if (or (= (length (this-command-keys)) 1)
87 0 : (not inhibit-quit)) ; safety
88 0 : (progn (ding)
89 0 : (message "Quit")
90 0 : (throw return-tag nil))
91 0 : (setq cmd nil))))
92 0 : (setq current-prefix-arg prefix-arg)
93 0 : (if cmd
94 0 : (condition-case conditions
95 0 : (progn (command-execute cmd)
96 0 : (setq last-command this-command)
97 0 : (if (or (prog1 quit-flag (setq quit-flag nil))
98 0 : (eq last-input-event ?\C-g))
99 0 : (progn (setq unread-command-events nil)
100 0 : (if (not inhibit-quit)
101 0 : (progn (ding)
102 0 : (message "Quit")
103 0 : (throw return-tag nil))
104 0 : (ding)))))
105 0 : (buffer-read-only (if loop-function
106 0 : (setq err conditions)
107 0 : (ding)
108 0 : (message "Buffer is read-only")
109 0 : (sit-for 2)))
110 0 : (beginning-of-buffer (if loop-function
111 0 : (setq err conditions)
112 0 : (ding)
113 0 : (message "Beginning of Buffer")
114 0 : (sit-for 2)))
115 0 : (end-of-buffer (if loop-function
116 0 : (setq err conditions)
117 0 : (ding)
118 0 : (message "End of Buffer")
119 0 : (sit-for 2)))
120 0 : (error (if loop-function
121 0 : (setq err conditions)
122 0 : (ding)
123 0 : (message "Error: %s"
124 0 : (if (eq (car conditions) 'error)
125 0 : (car (cdr conditions))
126 0 : (prin1-to-string conditions)))
127 0 : (sit-for 2))))
128 0 : (ding))
129 0 : (if loop-function (funcall loop-function loop-state err))))
130 0 : (ding)
131 0 : (throw return-tag nil))
132 :
133 : ;; This function is like pop-to-buffer, sort of.
134 : ;; The algorithm is
135 : ;; If there is a window displaying buffer
136 : ;; Select it
137 : ;; Else if there is only one window
138 : ;; Split it, selecting the window on the bottom with height being
139 : ;; the lesser of max-height (if non-nil) and the number of lines in
140 : ;; the buffer to be displayed subject to window-min-height constraint.
141 : ;; Else
142 : ;; Switch to buffer in the current window.
143 : ;;
144 : ;; Then if max-height is nil, and not all of the lines in the buffer
145 : ;; are displayed, grab the whole frame.
146 : ;;
147 : ;; Returns selected window on buffer positioned at point-min.
148 :
149 : (defun Electric-pop-up-window (buffer &optional max-height)
150 0 : (let* ((win (or (get-buffer-window buffer) (selected-window)))
151 0 : (buf (get-buffer buffer))
152 0 : (one-window (one-window-p t))
153 : (pop-up-windows t)
154 : (pop-up-frames nil))
155 0 : (if (not buf)
156 0 : (error "Buffer %s does not exist" buffer)
157 0 : (cond ((and (eq (window-buffer win) buf))
158 0 : (select-window win))
159 0 : (one-window
160 0 : (pop-to-buffer buffer)
161 0 : (setq win (selected-window)))
162 : (t
163 0 : (switch-to-buffer buf)))
164 : ;; Don't shrink the window, but expand it if necessary.
165 0 : (goto-char (point-min))
166 0 : (unless (= (point-max) (window-end win t))
167 : ;; This call is executed even if the window existed before, was
168 : ;; reused, ... contradicting a claim in the comment before this
169 : ;; function.
170 0 : (fit-window-to-buffer win max-height nil nil nil t))
171 0 : win)))
172 :
173 : ;;; Electric keys.
174 :
175 : (defgroup electricity ()
176 : "Electric behavior for self inserting keys."
177 : :group 'editing)
178 :
179 : (defun electric--after-char-pos ()
180 : "Return the position after the char we just inserted.
181 : Returns nil when we can't find this char."
182 0 : (let ((pos (point)))
183 0 : (when (or (eq (char-before) last-command-event) ;; Sanity check.
184 0 : (save-excursion
185 0 : (or (progn (skip-chars-backward " \t")
186 0 : (setq pos (point))
187 0 : (eq (char-before) last-command-event))
188 0 : (progn (skip-chars-backward " \n\t")
189 0 : (setq pos (point))
190 0 : (eq (char-before) last-command-event)))))
191 0 : pos)))
192 :
193 : (defun electric--sort-post-self-insertion-hook ()
194 : "Ensure order of electric functions in `post-self-insertion-hook'.
195 :
196 : Hooks in this variable interact in non-trivial ways, so a
197 : relative order must be maintained within it."
198 0 : (setq-default post-self-insert-hook
199 0 : (sort (default-value 'post-self-insert-hook)
200 0 : #'(lambda (fn1 fn2)
201 0 : (< (or (get fn1 'priority) 0)
202 0 : (or (get fn2 'priority) 0))))))
203 :
204 : ;;; Electric indentation.
205 :
206 : ;; Autoloading variables is generally undesirable, but major modes
207 : ;; should usually set this variable by adding elements to the default
208 : ;; value, which only works well if the variable is preloaded.
209 : ;;;###autoload
210 : (defvar electric-indent-chars '(?\n)
211 : "Characters that should cause automatic reindentation.")
212 :
213 : (defvar electric-indent-functions nil
214 : "Special hook run to decide whether to auto-indent.
215 : Each function is called with one argument (the inserted char), with
216 : point right after that char, and it should return t to cause indentation,
217 : `no-indent' to prevent indentation or nil to let other functions decide.")
218 :
219 : (defvar-local electric-indent-inhibit nil
220 : "If non-nil, reindentation is not appropriate for this buffer.
221 : This should be set by major modes such as `python-mode' since
222 : Python does not lend itself to fully automatic indentation.")
223 :
224 : (defvar electric-indent-functions-without-reindent
225 : '(indent-relative indent-to-left-margin indent-relative-maybe
226 : py-indent-line coffee-indent-line org-indent-line yaml-indent-line
227 : haskell-indentation-indent-line haskell-indent-cycle haskell-simple-indent
228 : yaml-indent-line)
229 : "List of indent functions that can't reindent.
230 : If `line-indent-function' is one of those, then `electric-indent-mode' will
231 : not try to reindent lines. It is normally better to make the major
232 : mode set `electric-indent-inhibit', but this can be used as a workaround.")
233 :
234 : (defun electric-indent-post-self-insert-function ()
235 : "Function that `electric-indent-mode' adds to `post-self-insert-hook'.
236 : This indents if the hook `electric-indent-functions' returns non-nil,
237 : or if a member of `electric-indent-chars' was typed; but not in a string
238 : or comment."
239 : ;; FIXME: This reindents the current line, but what we really want instead is
240 : ;; to reindent the whole affected text. That's the current line for simple
241 : ;; cases, but not all cases. We do take care of the newline case in an
242 : ;; ad-hoc fashion, but there are still missing cases such as the case of
243 : ;; electric-pair-mode wrapping a region with a pair of parens.
244 : ;; There might be a way to get it working by analyzing buffer-undo-list, but
245 : ;; it looks challenging.
246 0 : (let (pos)
247 0 : (when (and
248 0 : electric-indent-mode
249 : ;; Don't reindent while inserting spaces at beginning of line.
250 0 : (or (not (memq last-command-event '(?\s ?\t)))
251 0 : (save-excursion (skip-chars-backward " \t") (not (bolp))))
252 0 : (setq pos (electric--after-char-pos))
253 0 : (save-excursion
254 0 : (goto-char pos)
255 0 : (let ((act (or (run-hook-with-args-until-success
256 : 'electric-indent-functions
257 0 : last-command-event)
258 0 : (memq last-command-event electric-indent-chars))))
259 0 : (not
260 0 : (or (memq act '(nil no-indent))
261 : ;; In a string or comment.
262 0 : (unless (eq act 'do-indent) (nth 8 (syntax-ppss))))))))
263 : ;; For newline, we want to reindent both lines and basically behave like
264 : ;; reindent-then-newline-and-indent (whose code we hence copied).
265 0 : (let ((at-newline (<= pos (line-beginning-position))))
266 0 : (when at-newline
267 0 : (let ((before (copy-marker (1- pos) t)))
268 0 : (save-excursion
269 0 : (unless (or (memq indent-line-function
270 0 : electric-indent-functions-without-reindent)
271 0 : electric-indent-inhibit)
272 : ;; Don't reindent the previous line if the indentation function
273 : ;; is not a real one.
274 0 : (goto-char before)
275 0 : (indent-according-to-mode))
276 : ;; We are at EOL before the call to indent-according-to-mode, and
277 : ;; after it we usually are as well, but not always. We tried to
278 : ;; address it with `save-excursion' but that uses a normal marker
279 : ;; whereas we need `move after insertion', so we do the
280 : ;; save/restore by hand.
281 0 : (goto-char before)
282 0 : (when (eolp)
283 : ;; Remove the trailing whitespace after indentation because
284 : ;; indentation may (re)introduce the whitespace.
285 0 : (delete-horizontal-space t)))))
286 0 : (unless (and electric-indent-inhibit
287 0 : (not at-newline))
288 0 : (indent-according-to-mode))))))
289 :
290 : (put 'electric-indent-post-self-insert-function 'priority 60)
291 :
292 : (defun electric-indent-just-newline (arg)
293 : "Insert just a newline, without any auto-indentation."
294 : (interactive "*P")
295 0 : (let ((electric-indent-mode nil))
296 0 : (newline arg 'interactive)))
297 :
298 : ;;;###autoload
299 : (define-key global-map "\C-j" 'electric-newline-and-maybe-indent)
300 : ;;;###autoload
301 : (defun electric-newline-and-maybe-indent ()
302 : "Insert a newline.
303 : If `electric-indent-mode' is enabled, that's that, but if it
304 : is *disabled* then additionally indent according to major mode.
305 : Indentation is done using the value of `indent-line-function'.
306 : In programming language modes, this is the same as TAB.
307 : In some text modes, where TAB inserts a tab, this command indents to the
308 : column specified by the function `current-left-margin'."
309 : (interactive "*")
310 0 : (if electric-indent-mode
311 0 : (electric-indent-just-newline nil)
312 0 : (newline-and-indent)))
313 :
314 : ;;;###autoload
315 : (define-minor-mode electric-indent-mode
316 : "Toggle on-the-fly reindentation (Electric Indent mode).
317 : With a prefix argument ARG, enable Electric Indent mode if ARG is
318 : positive, and disable it otherwise. If called from Lisp, enable
319 : the mode if ARG is omitted or nil.
320 :
321 : When enabled, this reindents whenever the hook `electric-indent-functions'
322 : returns non-nil, or if you insert a character from `electric-indent-chars'.
323 :
324 : This is a global minor mode. To toggle the mode in a single buffer,
325 : use `electric-indent-local-mode'."
326 : :global t :group 'electricity
327 : :initialize 'custom-initialize-delay
328 : :init-value t
329 0 : (if (not electric-indent-mode)
330 0 : (unless (catch 'found
331 0 : (dolist (buf (buffer-list))
332 0 : (with-current-buffer buf
333 0 : (if electric-indent-mode (throw 'found t)))))
334 0 : (remove-hook 'post-self-insert-hook
335 0 : #'electric-indent-post-self-insert-function))
336 0 : (add-hook 'post-self-insert-hook
337 0 : #'electric-indent-post-self-insert-function)
338 0 : (electric--sort-post-self-insertion-hook)))
339 :
340 : ;;;###autoload
341 : (define-minor-mode electric-indent-local-mode
342 : "Toggle `electric-indent-mode' only in this buffer."
343 : :variable (buffer-local-value 'electric-indent-mode (current-buffer))
344 0 : (cond
345 0 : ((eq electric-indent-mode (default-value 'electric-indent-mode))
346 0 : (kill-local-variable 'electric-indent-mode))
347 0 : ((not (default-value 'electric-indent-mode))
348 : ;; Locally enabled, but globally disabled.
349 0 : (electric-indent-mode 1) ; Setup the hooks.
350 0 : (setq-default electric-indent-mode nil) ; But keep it globally disabled.
351 0 : )))
352 :
353 : ;;; Electric newlines after/before/around some chars.
354 :
355 : (defvar electric-layout-rules nil
356 : "List of rules saying where to automatically insert newlines.
357 :
358 : Each rule has the form (CHAR . WHERE) where CHAR is the char that
359 : was just inserted and WHERE specifies where to insert newlines
360 : and can be: nil, `before', `after', `around', `after-stay', or a
361 : function of no arguments that returns one of those symbols.
362 :
363 : The symbols specify where in relation to CHAR the newline
364 : character(s) should be inserted. `after-stay' means insert a
365 : newline after CHAR but stay in the same place.")
366 :
367 : (defun electric-layout-post-self-insert-function ()
368 0 : (let* ((rule (cdr (assq last-command-event electric-layout-rules)))
369 : pos)
370 0 : (when (and rule
371 0 : (setq pos (electric--after-char-pos))
372 : ;; Not in a string or comment.
373 0 : (not (nth 8 (save-excursion (syntax-ppss pos)))))
374 0 : (let ((end (point-marker))
375 0 : (sym (if (functionp rule) (funcall rule) rule)))
376 0 : (set-marker-insertion-type end (not (eq sym 'after-stay)))
377 0 : (goto-char pos)
378 0 : (pcase sym
379 : ;; FIXME: we used `newline' down here which called
380 : ;; self-insert-command and ran post-self-insert-hook recursively.
381 : ;; It happened to make electric-indent-mode work automatically with
382 : ;; electric-layout-mode (at the cost of re-indenting lines
383 : ;; multiple times), but I'm not sure it's what we want.
384 : ;;
385 : ;; FIXME: check eolp before inserting \n?
386 0 : (`before (goto-char (1- pos)) (skip-chars-backward " \t")
387 0 : (unless (bolp) (insert "\n")))
388 0 : (`after (insert "\n"))
389 0 : (`after-stay (save-excursion
390 0 : (let ((electric-layout-rules nil))
391 0 : (newline 1 t))))
392 0 : (`around (save-excursion
393 0 : (goto-char (1- pos)) (skip-chars-backward " \t")
394 0 : (unless (bolp) (insert "\n")))
395 0 : (insert "\n"))) ; FIXME: check eolp before inserting \n?
396 0 : (goto-char end)))))
397 :
398 : (put 'electric-layout-post-self-insert-function 'priority 40)
399 :
400 : ;;;###autoload
401 : (define-minor-mode electric-layout-mode
402 : "Automatically insert newlines around some chars.
403 : With a prefix argument ARG, enable Electric Layout mode if ARG is
404 : positive, and disable it otherwise. If called from Lisp, enable
405 : the mode if ARG is omitted or nil.
406 : The variable `electric-layout-rules' says when and how to insert newlines."
407 : :global t :group 'electricity
408 0 : (cond (electric-layout-mode
409 0 : (add-hook 'post-self-insert-hook
410 0 : #'electric-layout-post-self-insert-function)
411 0 : (electric--sort-post-self-insertion-hook))
412 : (t
413 0 : (remove-hook 'post-self-insert-hook
414 0 : #'electric-layout-post-self-insert-function))))
415 :
416 : ;;; Electric quoting.
417 :
418 : (defcustom electric-quote-comment t
419 : "Non-nil means to use electric quoting in program comments."
420 : :version "25.1"
421 : :type 'boolean :safe 'booleanp :group 'electricity)
422 :
423 : (defcustom electric-quote-string nil
424 : "Non-nil means to use electric quoting in program strings."
425 : :version "25.1"
426 : :type 'boolean :safe 'booleanp :group 'electricity)
427 :
428 : (defcustom electric-quote-chars '(?‘ ?’ ?“ ?”)
429 : "Curved quote characters for `electric-quote-mode'.
430 : This list's members correspond to left single quote, right single
431 : quote, left double quote, and right double quote, respectively."
432 : :version "26.1"
433 : :type '(list character character character character)
434 : :safe #'(lambda (x)
435 : (pcase x
436 : (`(,(pred characterp) ,(pred characterp)
437 : ,(pred characterp) ,(pred characterp))
438 : t)))
439 : :group 'electricity)
440 :
441 : (defcustom electric-quote-paragraph t
442 : "Non-nil means to use electric quoting in text paragraphs."
443 : :version "25.1"
444 : :type 'boolean :safe 'booleanp :group 'electricity)
445 :
446 : (defcustom electric-quote-context-sensitive nil
447 : "Non-nil means to replace \\=' with an electric quote depending on context.
448 : If `electric-quote-context-sensitive' is non-nil, Emacs replaces
449 : \\=' and \\='\\=' with an opening quote after a line break,
450 : whitespace, opening parenthesis, or quote and leaves \\=` alone."
451 : :version "26.1"
452 : :type 'boolean :safe #'booleanp :group 'electricity)
453 :
454 : (defvar electric-quote-inhibit-functions ()
455 : "List of functions that should inhibit electric quoting.
456 : When the variable `electric-quote-mode' is non-nil, Emacs will
457 : call these functions in order after the user has typed an \\=` or
458 : \\=' character. If one of them returns non-nil, electric quote
459 : substitution is inhibited. The functions are called after the
460 : \\=` or \\=' character has been inserted with point directly
461 : after the inserted character. The functions in this hook should
462 : not move point or change the current buffer.")
463 :
464 : (defun electric-quote-post-self-insert-function ()
465 : "Function that `electric-quote-mode' adds to `post-self-insert-hook'.
466 : This requotes when a quoting key is typed."
467 0 : (when (and electric-quote-mode
468 0 : (or (eq last-command-event ?\')
469 0 : (and (not electric-quote-context-sensitive)
470 0 : (eq last-command-event ?\`)))
471 0 : (not (run-hook-with-args-until-success
472 0 : 'electric-quote-inhibit-functions))
473 0 : (if (derived-mode-p 'text-mode)
474 0 : electric-quote-paragraph
475 0 : (and comment-start comment-use-syntax
476 0 : (or electric-quote-comment electric-quote-string)
477 0 : (let* ((syntax (syntax-ppss))
478 0 : (beg (nth 8 syntax)))
479 0 : (and beg
480 0 : (or (and electric-quote-comment (nth 4 syntax))
481 0 : (and electric-quote-string (nth 3 syntax)))
482 : ;; Do not requote a quote that starts or ends
483 : ;; a comment or string.
484 0 : (eq beg (nth 8 (save-excursion
485 0 : (syntax-ppss (1- (point)))))))))))
486 0 : (pcase electric-quote-chars
487 : (`(,q< ,q> ,q<< ,q>>)
488 0 : (save-excursion
489 0 : (let ((backtick ?\`))
490 0 : (if (or (eq last-command-event ?\`)
491 0 : (and electric-quote-context-sensitive
492 0 : (save-excursion
493 0 : (backward-char)
494 0 : (or (bobp) (bolp)
495 0 : (memq (char-before) (list q< q<<))
496 0 : (memq (char-syntax (char-before))
497 0 : '(?\s ?\())))
498 0 : (setq backtick ?\')))
499 0 : (cond ((search-backward (string q< backtick) (- (point) 2) t)
500 0 : (replace-match (string q<<))
501 0 : (when (and electric-pair-mode
502 0 : (eq (cdr-safe
503 0 : (assq q< electric-pair-text-pairs))
504 0 : (char-after)))
505 0 : (delete-char 1))
506 0 : (setq last-command-event q<<))
507 0 : ((search-backward (string backtick) (1- (point)) t)
508 0 : (replace-match (string q<))
509 0 : (setq last-command-event q<)))
510 0 : (cond ((search-backward (string q> ?') (- (point) 2) t)
511 0 : (replace-match (string q>>))
512 0 : (setq last-command-event q>>))
513 0 : ((search-backward "'" (1- (point)) t)
514 0 : (replace-match (string q>))
515 0 : (setq last-command-event q>))))))))))
516 :
517 : (put 'electric-quote-post-self-insert-function 'priority 10)
518 :
519 : ;;;###autoload
520 : (define-minor-mode electric-quote-mode
521 : "Toggle on-the-fly requoting (Electric Quote mode).
522 : With a prefix argument ARG, enable Electric Quote mode if
523 : ARG is positive, and disable it otherwise. If called from Lisp,
524 : enable the mode if ARG is omitted or nil.
525 :
526 : When enabled, as you type this replaces \\=` with ‘, \\=' with ’,
527 : \\=`\\=` with “, and \\='\\=' with ”. This occurs only in comments, strings,
528 : and text paragraphs, and these are selectively controlled with
529 : `electric-quote-comment', `electric-quote-string', and
530 : `electric-quote-paragraph'.
531 :
532 : Customize `electric-quote-chars' to use characters other than the
533 : ones listed here.
534 :
535 : This is a global minor mode. To toggle the mode in a single buffer,
536 : use `electric-quote-local-mode'."
537 : :global t :group 'electricity
538 : :initialize 'custom-initialize-delay
539 : :init-value nil
540 0 : (if (not electric-quote-mode)
541 0 : (unless (catch 'found
542 0 : (dolist (buf (buffer-list))
543 0 : (with-current-buffer buf
544 0 : (if electric-quote-mode (throw 'found t)))))
545 0 : (remove-hook 'post-self-insert-hook
546 0 : #'electric-quote-post-self-insert-function))
547 0 : (add-hook 'post-self-insert-hook
548 0 : #'electric-quote-post-self-insert-function)
549 0 : (electric--sort-post-self-insertion-hook)))
550 :
551 : ;;;###autoload
552 : (define-minor-mode electric-quote-local-mode
553 : "Toggle `electric-quote-mode' only in this buffer."
554 : :variable (buffer-local-value 'electric-quote-mode (current-buffer))
555 0 : (cond
556 0 : ((eq electric-quote-mode (default-value 'electric-quote-mode))
557 0 : (kill-local-variable 'electric-quote-mode))
558 0 : ((not (default-value 'electric-quote-mode))
559 : ;; Locally enabled, but globally disabled.
560 0 : (electric-quote-mode 1) ; Setup the hooks.
561 0 : (setq-default electric-quote-mode nil) ; But keep it globally disabled.
562 0 : )))
563 :
564 : (provide 'electric)
565 :
566 : ;;; electric.el ends here
|