(skeleton-marks): New variable.
[bpt/emacs.git] / lisp / skeleton.el
CommitLineData
f3611c70 1;;; skeleton.el --- Lisp language extension for writing statement skeletons
b578f267 2
017d787a 3;; Copyright (C) 1993, 1994, 1995, 1996 by Free Software Foundation, Inc.
ac59aed8 4
f3611c70 5;; Author: Daniel.Pfeiffer@Informatik.START.dbp.de, fax (+49 69) 7588-2389
ac59aed8 6;; Maintainer: FSF
f3611c70 7;; Keywords: extensions, abbrev, languages, tools
ac59aed8
RS
8
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software; you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
13;; the Free Software Foundation; either version 2, or (at your option)
14;; any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
b578f267
EN
22;; along with GNU Emacs; see the file COPYING. If not, write to the
23;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24;; Boston, MA 02111-1307, USA.
ac59aed8
RS
25
26;;; Commentary:
27
f3611c70 28;; A very concise language extension for writing structured statement
ac59aed8
RS
29;; skeleton insertion commands for programming language modes. This
30;; originated in shell-script mode and was applied to ada-mode's
31;; commands which shrunk to one third. And these commands are now
32;; user configurable.
33
34;;; Code:
35
f3611c70 36;; page 1: statement skeleton language definition & interpreter
ac59aed8
RS
37;; page 2: paired insertion
38;; page 3: mirror-mode, an example for setting up paired insertion
39
40
41(defvar skeleton-transformation nil
f3611c70 42 "*If non-nil, function applied to literal strings before they are inserted.
ac59aed8
RS
43It should take strings and characters and return them transformed, or nil
44which means no transformation.
45Typical examples might be `upcase' or `capitalize'.")
46
47; this should be a fourth argument to defvar
48(put 'skeleton-transformation 'variable-interactive
49 "aTransformation function: ")
50
51
017d787a
RS
52(defvar skeleton-autowrap t
53 "Controls wrapping behaviour of functions created with `define-skeleton'.
54When the region is visible (due to `transient-mark-mode' or marking a region
55with the mouse) and this is non-`nil' and the function was called without an
56explicit ARG, then the ARG defaults to -1, i.e. wrapping around the visible
57region.
58
59We will probably delete this variable in a future Emacs version
60unless we get a substantial number of complaints about the auto-wrap
61feature.")
ac59aed8 62
da6a884f
KH
63(defvar skeleton-end-hook
64 (lambda ()
65 (or (eolp) (newline-and-indent)))
66 "Hook called at end of skeleton but before going to point of interest.
67By default this moves out anything following to next line.
68The variables `v1' and `v2' are still set when calling this.")
69
70
f3611c70
KH
71;;;###autoload
72(defvar skeleton-filter 'identity
017d787a 73 "Function for transforming a skeleton proxy's aliases' variable value.")
f3611c70 74
f3611c70
KH
75(defvar skeleton-untabify t
76 "When non-`nil' untabifies when deleting backwards with element -ARG.")
77
4bfd70e9
KH
78(defvar skeleton-newline-indent-rigidly nil
79 "When non-`nil', indent rigidly under current line for element `\\n'.
80Else use mode's `indent-line-function'.")
f3611c70
KH
81
82(defvar skeleton-further-elements ()
83 "A buffer-local varlist (see `let') of mode specific skeleton elements.
84These variables are bound while interpreting a skeleton. Their value may
85in turn be any valid skeleton element if they are themselves to be used as
86skeleton elements.")
87(make-variable-buffer-local 'skeleton-further-elements)
88
89
ac59aed8
RS
90(defvar skeleton-subprompt
91 (substitute-command-keys
92 "RET, \\<minibuffer-local-map>\\[abort-recursive-edit] or \\[help-command]")
f3611c70
KH
93 "*Replacement for %s in prompts of recursive subskeletons.")
94
ac59aed8 95
017d787a
RS
96(defvar skeleton-abbrev-cleanup nil
97 "Variable used to delete the character that led to abbrev expansion.")
ac59aed8
RS
98
99
100(defvar skeleton-debug nil
101 "*If non-nil `define-skeleton' will override previous definition.")
102
a6dccb51
RS
103(defvar skeleton-marks nil
104 "Variable used to keep the list of positions marked with @ after
105skeleton insertion. The list is in the reverse order of the insertion
106order. This list is reseted before skeleton insertion.")
107
4bfd70e9
KH
108;; reduce the number of compiler warnings
109(defvar skeleton)
110(defvar skeleton-modified)
111(defvar skeleton-point)
112(defvar skeleton-regions)
ac59aed8 113
ac59aed8 114;;;###autoload
f3611c70 115(defmacro define-skeleton (command documentation &rest skeleton)
ac59aed8
RS
116 "Define a user-configurable COMMAND that enters a statement skeleton.
117DOCUMENTATION is that of the command, while the variable of the same name,
f3611c70
KH
118which contains the skeleton, has a documentation to that effect.
119INTERACTOR and ELEMENT ... are as defined under `skeleton-insert'."
ac59aed8 120 (if skeleton-debug
f3611c70 121 (set command skeleton))
773500ab 122 `(progn
28895aea
RS
123 (defun ,command (&optional str arg)
124 ,(concat documentation
125 (if (string-match "\n\\>" documentation)
126 "" "\n")
127 "\n"
128 "This is a skeleton command (see `skeleton-insert').
129Normally the skeleton text is inserted at point, with nothing \"inside\".
130If there is a highlighted region, the skeleton text is wrapped
131around the region text.
132
133A prefix argument ARG says to wrap the skeleton around the next ARG words.
134A prefix argument of zero says to wrap around zero words---that is, nothing.
135This is a way of overiding the use of a highlighted region.")
136 (interactive "*P\nP")
137 (skeleton-proxy-new ',skeleton str arg))))
f3611c70 138
28895aea
RS
139;;;###autoload
140(defun skeleton-proxy-new (skeleton &optional str arg)
141 "Insert skeleton defined by variable of same name (see `skeleton-insert').
142Prefix ARG allows wrapping around words or regions (see `skeleton-insert').
143If no ARG was given, but the region is visible, ARG defaults to -1 depending
144on `skeleton-autowrap'. An ARG of M-0 will prevent this just for once.
145This command can also be an abbrev expansion (3rd and 4th columns in
146\\[edit-abbrevs] buffer: \"\" command-name).
f3611c70 147
28895aea
RS
148When called as a function, optional first argument STR may also be a string
149which will be the value of `str' whereas the skeleton's interactor is then
150ignored."
151 (interactive "*P\nP")
152 (setq skeleton (funcall skeleton-filter skeleton))
153 (if (not skeleton)
154 (if (memq this-command '(self-insert-command
155 skeleton-pair-insert-maybe
156 expand-abbrev))
157 (setq buffer-undo-list (primitive-undo 1 buffer-undo-list)))
158 (skeleton-insert skeleton
159 (if (setq skeleton-abbrev-cleanup
160 (or (eq this-command 'self-insert-command)
161 (eq this-command
162 'skeleton-pair-insert-maybe)))
163 ()
164 ;; Pretend C-x a e passed its prefix arg to us
165 (if (or arg current-prefix-arg)
166 (prefix-numeric-value (or arg
167 current-prefix-arg))
168 (and skeleton-autowrap
169 (or (eq last-command 'mouse-drag-region)
170 (and transient-mark-mode mark-active))
171 -1)))
172 (if (stringp str)
173 str))
174 (and skeleton-abbrev-cleanup
175 (setq skeleton-abbrev-cleanup (point))
176 (add-hook 'post-command-hook 'skeleton-abbrev-cleanup nil t))))
ac59aed8 177
7a8f27db 178;; This command isn't meant to be called, only its aliases with meaningful
f3611c70
KH
179;; names are.
180;;;###autoload
4bfd70e9
KH
181(defun skeleton-proxy (&optional str arg)
182 "Insert skeleton defined by variable of same name (see `skeleton-insert').
f3611c70 183Prefix ARG allows wrapping around words or regions (see `skeleton-insert').
ff85d4f3 184If no ARG was given, but the region is visible, ARG defaults to -1 depending
017d787a 185on `skeleton-autowrap'. An ARG of M-0 will prevent this just for once.
f3611c70 186This command can also be an abbrev expansion (3rd and 4th columns in
4bfd70e9
KH
187\\[edit-abbrevs] buffer: \"\" command-name).
188
189When called as a function, optional first argument STR may also be a string
190which will be the value of `str' whereas the skeleton's interactor is then
191ignored."
192 (interactive "*P\nP")
f3611c70 193 (let ((function (nth 1 (backtrace-frame 1))))
017d787a 194 (if (eq function 'nth) ; uncompiled Lisp function
f3611c70
KH
195 (setq function (nth 1 (backtrace-frame 5)))
196 (if (eq function 'byte-code) ; tracing byte-compiled function
197 (setq function (nth 1 (backtrace-frame 2)))))
198 (if (not (setq function (funcall skeleton-filter (symbol-value function))))
4bfd70e9
KH
199 (if (memq this-command '(self-insert-command
200 skeleton-pair-insert-maybe
201 expand-abbrev))
202 (setq buffer-undo-list (primitive-undo 1 buffer-undo-list)))
f3611c70 203 (skeleton-insert function
f3611c70
KH
204 (if (setq skeleton-abbrev-cleanup
205 (or (eq this-command 'self-insert-command)
da6a884f
KH
206 (eq this-command
207 'skeleton-pair-insert-maybe)))
f3611c70 208 ()
4bfd70e9 209 ;; Pretend C-x a e passed its prefix arg to us
f3611c70
KH
210 (if (or arg current-prefix-arg)
211 (prefix-numeric-value (or arg
017d787a
RS
212 current-prefix-arg))
213 (and skeleton-autowrap
214 (or (eq last-command 'mouse-drag-region)
215 (and transient-mark-mode mark-active))
ff85d4f3 216 -1)))
4bfd70e9
KH
217 (if (stringp str)
218 str))
017d787a
RS
219 (and skeleton-abbrev-cleanup
220 (setq skeleton-abbrev-cleanup (point))
221 (add-hook 'post-command-hook 'skeleton-abbrev-cleanup nil t)))))
f3611c70
KH
222
223
224(defun skeleton-abbrev-cleanup (&rest list)
225 "Value for `post-command-hook' to remove char that expanded abbrev."
226 (if (integerp skeleton-abbrev-cleanup)
227 (progn
228 (delete-region skeleton-abbrev-cleanup (point))
017d787a
RS
229 (setq skeleton-abbrev-cleanup)
230 (remove-hook 'post-command-hook 'skeleton-abbrev-cleanup t))))
ac59aed8
RS
231
232
233;;;###autoload
da6a884f 234(defun skeleton-insert (skeleton &optional skeleton-regions str)
f3611c70 235 "Insert the complex statement skeleton SKELETON describes very concisely.
ac59aed8 236
f3611c70
KH
237With optional third REGIONS wrap first interesting point (`_') in skeleton
238around next REGIONS words, if REGIONS is positive. If REGIONS is negative,
239wrap REGIONS preceding interregions into first REGIONS interesting positions
240\(successive `_'s) in skeleton. An interregion is the stretch of text between
241two contiguous marked points. If you marked A B C [] (where [] is the cursor)
242in alphabetical order, the 3 interregions are simply the last 3 regions. But
243if you marked B A [] C, the interregions are B-A, A-[], []-C.
244
4bfd70e9
KH
245Optional fourth STR is the value for the variable `str' within the skeleton.
246When this is non-`nil' the interactor gets ignored, and this should be a valid
247skeleton element.
248
f3611c70
KH
249SKELETON is made up as (INTERACTOR ELEMENT ...). INTERACTOR may be nil if
250not needed, a prompt-string or an expression for complex read functions.
ac59aed8
RS
251
252If ELEMENT is a string or a character it gets inserted (see also
253`skeleton-transformation'). Other possibilities are:
254
4bfd70e9 255 \\n go to next line and indent according to mode
f3611c70
KH
256 _ interesting point, interregion here, point after termination
257 > indent line (or interregion if > _) according to major mode
a6dccb51 258 @ add position to `skeleton-marks'
f3611c70
KH
259 & do next ELEMENT if previous moved point
260 | do next ELEMENT if previous didn't move point
261 -num delete num preceding characters (see `skeleton-untabify')
ac59aed8
RS
262 resume: skipped, continue here if quit is signaled
263 nil skipped
264
f3611c70
KH
265Further elements can be defined via `skeleton-further-elements'. ELEMENT may
266itself be a SKELETON with an INTERACTOR. The user is prompted repeatedly for
267different inputs. The SKELETON is processed as often as the user enters a
268non-empty string. \\[keyboard-quit] terminates skeleton insertion, but
269continues after `resume:' and positions at `_' if any. If INTERACTOR in such
270a subskeleton is a prompt-string which contains a \".. %s ..\" it is
4bfd70e9
KH
271formatted with `skeleton-subprompt'. Such an INTERACTOR may also a list of
272strings with the subskeleton being repeated once for each string.
ac59aed8 273
017d787a
RS
274Quoted Lisp expressions are evaluated evaluated for their side-effect.
275Other Lisp expressions are evaluated and the value treated as above.
49168e73 276Note that expressions may not return `t' since this implies an
f3611c70
KH
277endless loop. Modes can define other symbols by locally setting them
278to any valid skeleton element. The following local variables are
279available:
ac59aed8 280
f3611c70 281 str first time: read a string according to INTERACTOR
ac59aed8 282 then: insert previously read string once more
f3611c70 283 help help-form during interaction with the user or `nil'
773500ab 284 input initial input (string or cons with index) while reading str
017d787a 285 v1, v2 local variables for memorizing anything you want
4bfd70e9
KH
286
287When done with skeleton, but before going back to `_'-point call
288`skeleton-end-hook' if that is non-`nil'."
289 (and skeleton-regions
290 (setq skeleton-regions
291 (if (> skeleton-regions 0)
f3611c70 292 (list (point-marker)
4bfd70e9
KH
293 (save-excursion (forward-word skeleton-regions)
294 (point-marker)))
295 (setq skeleton-regions (- skeleton-regions))
296 ;; copy skeleton-regions - 1 elements from `mark-ring'
f3611c70
KH
297 (let ((l1 (cons (mark-marker) mark-ring))
298 (l2 (list (point-marker))))
4bfd70e9 299 (while (and l1 (> skeleton-regions 0))
f3611c70 300 (setq l2 (cons (car l1) l2)
4bfd70e9 301 skeleton-regions (1- skeleton-regions)
f3611c70
KH
302 l1 (cdr l1)))
303 (sort l2 '<))))
4bfd70e9
KH
304 (goto-char (car skeleton-regions))
305 (setq skeleton-regions (cdr skeleton-regions)))
773500ab 306 (let ((beg (point))
4bfd70e9 307 skeleton-modified skeleton-point resume: help input v1 v2)
a6dccb51 308 (setq skeleton-marks nil)
f3611c70 309 (unwind-protect
773500ab 310 (eval `(let ,skeleton-further-elements
4bfd70e9
KH
311 (skeleton-internal-list skeleton str)))
312 (run-hooks 'skeleton-end-hook)
773500ab
KH
313 (sit-for 0)
314 (or (pos-visible-in-window-p beg)
315 (progn
316 (goto-char beg)
317 (recenter 0)))
4bfd70e9
KH
318 (if skeleton-point
319 (goto-char skeleton-point)))))
f3611c70 320
f3611c70 321(defun skeleton-read (str &optional initial-input recursive)
773500ab 322 "Function for reading a string from the minibuffer within skeletons.
f3611c70 323PROMPT may contain a `%s' which will be replaced by `skeleton-subprompt'.
773500ab
KH
324If non-`nil' second arg INITIAL-INPUT or variable `input' is a string or
325cons with index to insert before reading. If third arg RECURSIVE is non-`nil'
326i.e. we are handling the iterator of a subskeleton, returns empty string if
327user didn't modify input.
328While reading, the value of `minibuffer-help-form' is variable `help' if that
329is non-`nil' or a default string."
da6a884f
KH
330 (let ((minibuffer-help-form (or (if (boundp 'help) (symbol-value 'help))
331 (if recursive "\
ac59aed8
RS
332As long as you provide input you will insert another subskeleton.
333
334If you enter the empty string, the loop inserting subskeletons is
335left, and the current one is removed as far as it has been entered.
336
337If you quit, the current subskeleton is removed as far as it has been
338entered. No more of the skeleton will be inserted, except maybe for a
f3611c70 339syntactically necessary termination."
773500ab 340 "\
f3611c70 341You are inserting a skeleton. Standard text gets inserted into the buffer
da6a884f
KH
342automatically, and you are prompted to fill in the variable parts.")))
343 (eolp (eolp)))
344 ;; since Emacs doesn't show main window's cursor, do something noticeable
345 (or eolp
346 (open-line 1))
347 (unwind-protect
348 (setq str (if (stringp str)
349 (read-string (format str skeleton-subprompt)
350 (setq initial-input
351 (or initial-input
352 (symbol-value 'input))))
353 (eval str)))
354 (or eolp
355 (delete-char 1))))
773500ab
KH
356 (if (and recursive
357 (or (null str)
358 (string= str "")
359 (equal str initial-input)
360 (equal str (car-safe initial-input))))
ac59aed8
RS
361 (signal 'quit t)
362 str))
363
4bfd70e9 364(defun skeleton-internal-list (skeleton &optional str recursive)
f3611c70
KH
365 (let* ((start (save-excursion (beginning-of-line) (point)))
366 (column (current-column))
367 (line (buffer-substring start
368 (save-excursion (end-of-line) (point))))
369 opoint)
4bfd70e9
KH
370 (or str
371 (setq str `(setq str (skeleton-read ',(car skeleton) nil ,recursive))))
372 (while (setq skeleton-modified (eq opoint (point))
773500ab
KH
373 opoint (point)
374 skeleton (cdr skeleton))
375 (condition-case quit
376 (skeleton-internal-1 (car skeleton))
377 (quit
378 (if (eq (cdr quit) 'recursive)
4bfd70e9
KH
379 (setq recursive 'quit
380 skeleton (memq 'resume: skeleton))
773500ab
KH
381 ;; remove the subskeleton as far as it has been shown
382 ;; the subskeleton shouldn't have deleted outside current line
da6a884f 383 (end-of-line)
773500ab
KH
384 (delete-region start (point))
385 (insert line)
386 (move-to-column column)
387 (if (cdr quit)
388 (setq skeleton ()
389 recursive nil)
390 (signal 'quit 'recursive)))))))
391 ;; maybe continue loop or go on to next outer resume: section
392 (if (eq recursive 'quit)
393 (signal 'quit 'recursive)
394 recursive))
f3611c70
KH
395
396
397(defun skeleton-internal-1 (element &optional literal)
4bfd70e9
KH
398 (cond ((char-or-string-p element)
399 (if (and (integerp element) ; -num
400 (< element 0))
401 (if skeleton-untabify
402 (backward-delete-char-untabify (- element))
403 (delete-backward-char (- element)))
404 (insert-before-markers (if (and skeleton-transformation
405 (not literal))
406 (funcall skeleton-transformation element)
407 element))))
c93d212a 408 ((eq element '\n) ; actually (eq '\n 'n)
4bfd70e9
KH
409 (if (and skeleton-regions
410 (eq (nth 1 skeleton) '_))
411 (progn
412 (or (eolp)
413 (newline))
414 (indent-region (point) (car skeleton-regions) nil))
415 (if skeleton-newline-indent-rigidly
416 (indent-to (prog1 (current-indentation)
417 (newline)))
418 (newline)
419 (indent-according-to-mode))))
c93d212a 420 ((eq element '>)
4bfd70e9 421 (if (and skeleton-regions
f3611c70 422 (eq (nth 1 skeleton) '_))
4bfd70e9
KH
423 (indent-region (point) (car skeleton-regions) nil)
424 (indent-according-to-mode)))
c93d212a 425 ((eq element '_)
4bfd70e9 426 (if skeleton-regions
f3611c70 427 (progn
4bfd70e9 428 (goto-char (car skeleton-regions))
da6a884f
KH
429 (setq skeleton-regions (cdr skeleton-regions))
430 (and (<= (current-column) (current-indentation))
431 (eq (nth 1 skeleton) '\n)
432 (end-of-line 0)))
4bfd70e9
KH
433 (or skeleton-point
434 (setq skeleton-point (point)))))
c93d212a 435 ((eq element '&)
4bfd70e9 436 (if skeleton-modified
f3611c70 437 (setq skeleton (cdr skeleton))))
c93d212a 438 ((eq element '|)
4bfd70e9 439 (or skeleton-modified
f3611c70 440 (setq skeleton (cdr skeleton))))
a6dccb51
RS
441 ((eq element '@)
442 (setq skeleton-marks (cons (point) skeleton-marks)))
4bfd70e9 443 ((eq 'quote (car-safe element))
f3611c70 444 (eval (nth 1 element)))
4bfd70e9
KH
445 ((or (stringp (car-safe element))
446 (consp (car-safe element)))
447 (if (symbolp (car-safe (car element)))
448 (while (skeleton-internal-list element nil t))
449 (setq literal (car element))
450 (while literal
451 (skeleton-internal-list element (car literal))
452 (setq literal (cdr literal)))))
f3611c70
KH
453 ((null element))
454 ((skeleton-internal-1 (eval element) t))))
ac59aed8 455
4bfd70e9 456
f3611c70 457;; Maybe belongs into simple.el or elsewhere
ff85d4f3
RS
458;; ;###autoload
459;;; (define-skeleton local-variables-section
460;; "Insert a local variables section. Use current comment syntax if any."
461;; (completing-read "Mode: " obarray
462;; (lambda (symbol)
463;; (if (commandp symbol)
464;; (string-match "-mode$" (symbol-name symbol))))
465;; t)
466;; '(save-excursion
467;; (if (re-search-forward page-delimiter nil t)
468;; (error "Not on last page.")))
469;; comment-start "Local Variables:" comment-end \n
470;; comment-start "mode: " str
471;; & -5 | '(kill-line 0) & -1 | comment-end \n
472;; ( (completing-read (format "Variable, %s: " skeleton-subprompt)
473;; obarray
474;; (lambda (symbol)
475;; (or (eq symbol 'eval)
476;; (user-variable-p symbol)))
477;; t)
478;; comment-start str ": "
479;; (read-from-minibuffer "Expression: " nil read-expression-map nil
480;; 'read-expression-history) | _
481;; comment-end \n)
482;; resume:
28895aea 483;; comment-start "End:" comment-end \n)
ac59aed8 484\f
bc35d5b3 485;; Variables and command for automatically inserting pairs like () or "".
ac59aed8 486
bc35d5b3 487(defvar skeleton-pair nil
ac59aed8 488 "*If this is nil pairing is turned off, no matter what else is set.
bc35d5b3
RS
489Otherwise modes with `skeleton-pair-insert-maybe' on some keys
490will attempt to insert pairs of matching characters.")
ac59aed8
RS
491
492
bc35d5b3
RS
493(defvar skeleton-pair-on-word nil
494 "*If this is nil, paired insertion is inhibited before or inside a word.")
ac59aed8
RS
495
496
bc35d5b3
RS
497(defvar skeleton-pair-filter (lambda ())
498 "Attempt paired insertion if this function returns nil, before inserting.
ac59aed8
RS
499This allows for context-sensitive checking whether pairing is appropriate.")
500
501
bc35d5b3
RS
502(defvar skeleton-pair-alist ()
503 "An override alist of pairing partners matched against `last-command-char'.
504Each alist element, which looks like (ELEMENT ...), is passed to
505`skeleton-insert' with no interactor. Variable `str' does nothing.
ac59aed8 506
f3611c70 507Elements might be (?` ?` _ \"''\"), (?\\( ? _ \" )\") or (?{ \\n > _ \\n ?} >).")
ac59aed8
RS
508
509
ac59aed8 510;;;###autoload
bc35d5b3 511(defun skeleton-pair-insert-maybe (arg)
ac59aed8
RS
512 "Insert the character you type ARG times.
513
ff85d4f3
RS
514With no ARG, if `skeleton-pair' is non-nil, pairing can occur. If the region
515is visible the pair is wrapped around it depending on `skeleton-autowrap'.
516Else, if `skeleton-pair-on-word' is non-nil or we are not before or inside a
bc35d5b3 517word, and if `skeleton-pair-filter' returns nil, pairing is performed.
ac59aed8 518
bc35d5b3 519If a match is found in `skeleton-pair-alist', that is inserted, else
ac59aed8
RS
520the defaults are used. These are (), [], {}, <> and `' for the
521symmetrical ones, and the same character twice for the others."
522 (interactive "*P")
ff85d4f3
RS
523 (let ((mark (and skeleton-autowrap
524 (or (eq last-command 'mouse-drag-region)
525 (and transient-mark-mode mark-active))))
526 (skeleton-end-hook))
527 (if (or arg
528 (not skeleton-pair)
529 (and (not mark)
530 (or overwrite-mode
531 (if (not skeleton-pair-on-word) (looking-at "\\w"))
532 (funcall skeleton-pair-filter))))
533 (self-insert-command (prefix-numeric-value arg))
534 (setq last-command-char (logand last-command-char 255))
535 (or skeleton-abbrev-cleanup
536 (skeleton-insert
537 (cons nil (or (assq last-command-char skeleton-pair-alist)
538 (assq last-command-char '((?( _ ?))
539 (?[ _ ?])
540 (?{ _ ?})
541 (?< _ ?>)
542 (?` _ ?')))
543 `(,last-command-char _ ,last-command-char)))
544 (if mark -1))))))
ac59aed8
RS
545
546\f
ff85d4f3
RS
547;; A more serious example can be found in sh-script.el
548;;; (defun mirror-mode ()
017d787a
RS
549;; "This major mode is an amusing little example of paired insertion.
550;;All printable characters do a paired self insert, while the other commands
551;;work normally."
552;; (interactive)
553;; (kill-all-local-variables)
ff85d4f3
RS
554;; (make-local-variable 'skeleton-pair)
555;; (make-local-variable 'skeleton-pair-on-word)
556;; (make-local-variable 'skeleton-pair-filter)
557;; (make-local-variable 'skeleton-pair-alist)
017d787a
RS
558;; (setq major-mode 'mirror-mode
559;; mode-name "Mirror"
ff85d4f3 560;; skeleton-pair-on-word t
017d787a 561;; ;; in the middle column insert one or none if odd window-width
ff85d4f3
RS
562;; skeleton-pair-filter (lambda ()
563;; (if (>= (current-column)
564;; (/ (window-width) 2))
565;; ;; insert both on next line
566;; (next-line 1)
567;; ;; insert one or both?
568;; (= (* 2 (1+ (current-column)))
569;; (window-width))))
017d787a 570;; ;; mirror these the other way round as well
ff85d4f3
RS
571;; skeleton-pair-alist '((?) _ ?()
572;; (?] _ ?[)
573;; (?} _ ?{)
574;; (?> _ ?<)
575;; (?/ _ ?\\)
576;; (?\\ _ ?/)
577;; (?` ?` _ "''")
578;; (?' ?' _ "``"))
017d787a 579;; ;; in this mode we exceptionally ignore the user, else it's no fun
ff85d4f3
RS
580;; skeleton-pair t)
581;; (let ((map (make-vector 256 'skeleton-pair-insert-maybe))
582;; (i 0))
583;; (use-local-map `(keymap ,map))
584;; (while (< i ? )
585;; (aset map i nil)
586;; (aset map (+ i 128) nil)
017d787a
RS
587;; (setq i (1+ i))))
588;; (run-hooks 'mirror-mode-hook))
ac59aed8 589
2aea64fb
RS
590(provide 'skeleton)
591
ac59aed8 592;; skeleton.el ends here