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