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