*** empty log message ***
[bpt/emacs.git] / lisp / skeleton.el
index 31d1195..2b31194 100644 (file)
@@ -2,7 +2,7 @@
 
 ;; Copyright (C) 1993, 1994, 1995, 1996 by Free Software Foundation, Inc.
 
-;; Author: Daniel.Pfeiffer@Informatik.START.dbp.de, fax (+49 69) 7588-2389
+;; Author: Daniel Pfeiffer <occitan@esperanto.org>
 ;; Maintainer: FSF
 ;; Keywords: extensions, abbrev, languages, tools
 
@@ -38,7 +38,7 @@
 ;; page 3:     mirror-mode, an example for setting up paired insertion
 
 
-(defvar skeleton-transformation nil
+(defvar skeleton-transformation 'identity
   "*If non-nil, function applied to literal strings before they are inserted.
 It should take strings and characters and return them transformed, or nil
 which means no transformation.
@@ -60,11 +60,16 @@ We will probably delete this variable in a future Emacs version
 unless we get a substantial number of complaints about the auto-wrap
 feature.")
 
+(defvar skeleton-end-newline t
+  "If non-nil, make sure that the skeleton inserted ends with a newline.
+This just influences the way the default `skeleton-end-hook' behaves.")
+
 (defvar skeleton-end-hook
   (lambda ()
-    (or (eolp) (newline-and-indent)))
+    (or (eolp) (not skeleton-end-newline) (newline-and-indent)))
   "Hook called at end of skeleton but before going to point of interest.
-By default this moves out anything following to next line.
+By default this moves out anything following to next line,
+  unless `skeleton-end-newline' is set to nil.
 The variables `v1' and `v2' are still set when calling this.")
 
 
@@ -131,8 +136,9 @@ If there is a highlighted region, the skeleton text is wrapped
 around the region text.
 
 A prefix argument ARG says to wrap the skeleton around the next ARG words.
+A prefix argument of -1 says to wrap around region, even if not highlighted.
 A prefix argument of zero says to wrap around zero words---that is, nothing.
-This is a way of overiding the use of a highlighted region.")
+This is a way of overriding the use of a highlighted region.")
        (interactive "*P\nP")
        (skeleton-proxy-new ',skeleton str arg))))
 
@@ -255,15 +261,18 @@ If ELEMENT is a string or a character it gets inserted (see also
 `skeleton-transformation').  Other possibilities are:
 
        \\n     go to next line and indent according to mode
-       _       interesting point, interregion here, point after termination
+       _       interesting point, interregion here
        >       indent line (or interregion if > _) according to major mode
        @       add position to `skeleton-positions'
-       &       do next ELEMENT if previous moved point
-       |       do next ELEMENT if previous didn't move point
+       &       do next ELEMENT iff previous moved point
+       |       do next ELEMENT iff previous didn't move point
        -num    delete num preceding characters (see `skeleton-untabify')
        resume: skipped, continue here if quit is signaled
        nil     skipped
 
+After termination, point will be positioned at the first occurrence
+of _ or @ or at the end of the inserted text.
+
 Further elements can be defined via `skeleton-further-elements'.  ELEMENT may
 itself be a SKELETON with an INTERACTOR.  The user is prompted repeatedly for
 different inputs.  The SKELETON is processed as often as the user enters a
@@ -292,17 +301,16 @@ When done with skeleton, but before going back to `_'-point call
     (and skeleton-regions
         (setq skeleton-regions
               (if (> skeleton-regions 0)
-                  (list (point-marker)
+                  (list (copy-marker (point) t)
                         (save-excursion (forward-word skeleton-regions)
                                         (point-marker)))
                 (setq skeleton-regions (- skeleton-regions))
                 ;; copy skeleton-regions - 1 elements from `mark-ring'
                 (let ((l1 (cons (mark-marker) mark-ring))
-                      (l2 (list (point-marker))))
+                      (l2 (list (copy-marker (point) t))))
                   (while (and l1 (> skeleton-regions 0))
-                    (setq l2 (cons (car l1) l2)
-                          skeleton-regions (1- skeleton-regions)
-                          l1 (cdr l1)))
+                    (push (copy-marker (pop l1) t) l2)
+                    (setq skeleton-regions (1- skeleton-regions)))
                   (sort l2 '<))))
         (goto-char (car skeleton-regions))
         (setq skeleton-regions (cdr skeleton-regions)))
@@ -323,7 +331,9 @@ When done with skeleton, but before going back to `_'-point call
 
 (defun skeleton-read (prompt &optional initial-input recursive)
   "Function for reading a string from the minibuffer within skeletons.
-PROMPT may contain a `%s' which will be replaced by `skeleton-subprompt'.
+
+PROMPT must be a string or a form that evaluates to a string.
+It may contain a `%s' which will be replaced by `skeleton-subprompt'.
 If non-`nil' second arg INITIAL-INPUT or variable `input' is a string or
 cons with index to insert before reading.  If third arg RECURSIVE is non-`nil'
 i.e. we are handling the iterator of a subskeleton, returns empty string if
@@ -367,11 +377,13 @@ automatically, and you are prompted to fill in the variable parts.")))
 (defun skeleton-internal-list (skeleton &optional str recursive)
   (let* ((start (save-excursion (beginning-of-line) (point)))
         (column (current-column))
-        (line (buffer-substring start
-                                (save-excursion (end-of-line) (point))))
+        (line (buffer-substring start (line-end-position)))
         opoint)
     (or str
        (setq str `(setq str (skeleton-read ',(car skeleton) nil ,recursive))))
+    (when (and (eq (cadr skeleton) '\n)
+              (save-excursion (skip-chars-backward " \t") (bolp)))
+      (setq skeleton (cons nil (cons '> (cddr skeleton)))))
     (while (setq skeleton-modified (eq opoint (point))
                 opoint (point)
                 skeleton (cdr skeleton))
@@ -381,8 +393,8 @@ automatically, and you are prompted to fill in the variable parts.")))
         (if (eq (cdr quit) 'recursive)
             (setq recursive 'quit
                   skeleton (memq 'resume: skeleton))
-          ;; remove the subskeleton as far as it has been shown
-          ;; the subskeleton shouldn't have deleted outside current line
+          ;; Remove the subskeleton as far as it has been shown
+          ;; the subskeleton shouldn't have deleted outside current line.
           (end-of-line)
           (delete-region start (point))
           (insert line)
@@ -396,70 +408,79 @@ automatically, and you are prompted to fill in the variable parts.")))
       (signal 'quit 'recursive)
     recursive))
 
-
 (defun skeleton-internal-1 (element &optional literal)
-  (cond ((char-or-string-p element)
-        (if (and (integerp element)    ; -num
-                 (< element 0))
-            (if skeleton-untabify
-                (backward-delete-char-untabify (- element))
-              (delete-backward-char (- element)))
-          (insert-before-markers (if (and skeleton-transformation
-                                          (not literal))
-                                     (funcall skeleton-transformation element)
-                                   element))))
-       ((eq element '\n)               ; actually (eq '\n 'n)
-        (if (and skeleton-regions
-                 (eq (nth 1 skeleton) '_))
-            (progn
-              (or (eolp)
-                  (newline))
-              (indent-region (point) (car skeleton-regions) nil))
-          (if skeleton-newline-indent-rigidly
-              (indent-to (prog1 (current-indentation)
-                           (newline)))
-            (newline)
-            (indent-according-to-mode))))
-       ((eq element '>)
-        (if (and skeleton-regions
-                 (eq (nth 1 skeleton) '_))
-            (indent-region (point) (car skeleton-regions) nil)
-          (indent-according-to-mode)))
-       ((eq element '_)
-        (if skeleton-regions
-            (progn
-              (goto-char (car skeleton-regions))
-              (setq skeleton-regions (cdr skeleton-regions))
-              (and (<= (current-column) (current-indentation))
-                   (eq (nth 1 skeleton) '\n)
-                   (end-of-line 0)))
-          (or skeleton-point
-              (setq skeleton-point (point)))))
+  (cond
+   ((char-or-string-p element)
+    (if (and (integerp element)                ; -num
+            (< element 0))
+       (if skeleton-untabify
+           (backward-delete-char-untabify (- element))
+         (delete-backward-char (- element)))
+      (insert (if (and skeleton-transformation
+                      (not literal))
+                 (funcall skeleton-transformation element)
+               element))))
+   ((or (eq element '\n)                       ; actually (eq '\n 'n)
+       ;; The sequence `> \n' is handled specially so as to indent the first
+       ;; line after inserting the newline (to get the proper indentation).
+       (and (eq element '>) (eq (nth 1 skeleton) '\n) (pop skeleton)))
+    (let ((pos (if (eq element '>) (point))))
+      (cond
+       ((and skeleton-regions (eq (nth 1 skeleton) '_))
+       (or (eolp) (newline))
+       (if pos (save-excursion (goto-char pos) (indent-according-to-mode)))
+       (indent-region (line-beginning-position)
+                      (car skeleton-regions) nil))
+       ;; \n as last element only inserts \n if not at eol.
+       ((and (null (cdr skeleton)) (eolp))
+       (if pos (indent-according-to-mode)))
+       (skeleton-newline-indent-rigidly
+       (let ((pt (point)))
+         (newline)
+         (indent-to (save-excursion
+                      (goto-char pt)
+                      (if pos (indent-according-to-mode))
+                      (current-indentation)))))
+       (t (if pos (reindent-then-newline-and-indent)
+           (newline)
+           (indent-according-to-mode))))))
+   ((eq element '>)
+    (if (and skeleton-regions (eq (nth 1 skeleton) '_))
+       (indent-region (line-beginning-position)
+                      (car skeleton-regions) nil)
+      (indent-according-to-mode)))
+   ((eq element '_)
+    (if skeleton-regions
+       (progn
+         (goto-char (pop skeleton-regions))
+         (and (<= (current-column) (current-indentation))
+              (eq (nth 1 skeleton) '\n)
+              (end-of-line 0)))
+      (or skeleton-point
+         (setq skeleton-point (point)))))
        ((eq element '&)
-        (if skeleton-modified
-            (setq skeleton (cdr skeleton))))
-       ((eq element '|)
-        (or skeleton-modified
-            (setq skeleton (cdr skeleton))))
-       ((eq element '@)
-        (setq skeleton-positions (cons (point) skeleton-positions)))
-       ((eq 'quote (car-safe element))
-        (eval (nth 1 element)))
-       ((or (stringp (car-safe element))
-            (consp (car-safe element)))
-        (if (symbolp (car-safe (car element)))
-            (while (skeleton-internal-list element nil t))
-          (setq literal (car element))
-          (while literal
-            (skeleton-internal-list element (car literal))
-            (setq literal (cdr literal)))))
-       ((null element))
-       ((skeleton-internal-1 (eval element) t))))
-
-
+    (when skeleton-modified (pop skeleton)))
+   ((eq element '|)
+    (unless skeleton-modified (pop skeleton)))
+   ((eq element '@)
+    (push (point) skeleton-positions)
+    (unless skeleton-point (setq skeleton-point (point))))
+   ((eq 'quote (car-safe element))
+    (eval (nth 1 element)))
+   ((or (stringp (car-safe element))
+       (consp (car-safe element)))
+    (if (symbolp (car-safe (car element)))
+       (while (skeleton-internal-list element nil t))
+      (setq literal (car element))
+      (while literal
+       (skeleton-internal-list element (car literal))
+       (setq literal (cdr literal)))))
+   ((null element))
+   (t (skeleton-internal-1 (eval element) t))))
+\f
 ;; Maybe belongs into simple.el or elsewhere
-;; ;###autoload
-;;; (define-skeleton local-variables-section
+;; ;;;###autoload
+;; (define-skeleton local-variables-section
 ;;  "Insert a local variables section.  Use current comment syntax if any."
 ;;  (completing-read "Mode: " obarray
 ;;                (lambda (symbol)
@@ -468,7 +489,7 @@ automatically, and you are prompted to fill in the variable parts.")))
 ;;                t)
 ;;  '(save-excursion
 ;;     (if (re-search-forward page-delimiter nil t)
-;;      (error "Not on last page.")))
+;;      (error "Not on last page")))
 ;;  comment-start "Local Variables:" comment-end \n
 ;;  comment-start "mode: " str
 ;;  & -5 | '(kill-line 0) & -1 | comment-end \n
@@ -497,7 +518,7 @@ will attempt to insert pairs of matching characters.")
   "*If this is nil, paired insertion is inhibited before or inside a word.")
 
 
-(defvar skeleton-pair-filter (lambda ())
+(defvar skeleton-pair-filter (lambda () nil)
   "Attempt paired insertion if this function returns nil, before inserting.
 This allows for context-sensitive checking whether pairing is appropriate.")
 
@@ -518,6 +539,8 @@ With no ARG, if `skeleton-pair' is non-nil, pairing can occur.  If the region
 is visible the pair is wrapped around it depending on `skeleton-autowrap'.
 Else, if `skeleton-pair-on-word' is non-nil or we are not before or inside a
 word, and if `skeleton-pair-filter' returns nil, pairing is performed.
+Pairing is also prohibited if we are right after a quoting character
+such as backslash.
 
 If a match is found in `skeleton-pair-alist', that is inserted, else
 the defaults are used.  These are (), [], {}, <> and `' for the
@@ -529,6 +552,7 @@ symmetrical ones, and the same character twice for the others."
        (skeleton-end-hook))
     (if (or arg
            (not skeleton-pair)
+           (memq (char-syntax (preceding-char)) '(?\\ ?/))
            (and (not mark)
                 (or overwrite-mode
                     (if (not skeleton-pair-on-word) (looking-at "\\w"))
@@ -592,4 +616,4 @@ symmetrical ones, and the same character twice for the others."
 
 (provide 'skeleton)
 
-;; skeleton.el ends here
+;;; skeleton.el ends here