]> git.eshelyaron.com Git - emacs.git/commitdiff
(tempo-insert-template): Quoted transient-mark-mode
authorRichard M. Stallman <rms@gnu.org>
Sun, 22 Jan 1995 06:18:46 +0000 (06:18 +0000)
committerRichard M. Stallman <rms@gnu.org>
Sun, 22 Jan 1995 06:18:46 +0000 (06:18 +0000)
Expansion around region now puts point at the first mark.

(tempo-region-start, tempo-region-stop): New variables
(tempo-insert-template, tempo-insert): Don't affect the
mark.  Check for Transient Mark mode

(tempo-find-match-string): Removed the stupid 1+ again

(tempo-use-tag-list): Set tempo-match-finder to completion-function.

(tempo-match-finder): Renamed variable from
tempo-default-match-finder.  Change the value too.
(tempo-collection, tempo-dirty-collection): New variables.
(tempo-user-elements): New variable.
(tempo-insert): New argument ON-REGION.
New elements 'l and 'r.  Use tempo-is-user-element.
(tempo-is-user-element): New function.
(tempo-invalidate-collection, tempo-build-collection): New functions.
(tempo-find-match-string): Reinserted bugfix for re-search-backward.
(tempo-complete-tag): Complete rewrite.

(tempo-insert): Added the 'o tag.

lisp/tempo.el

index 9f809097dc32f12cf6d9a15888a79d848646cd8f..2ff7b75516da4ad43c3b8cb61ecc0a8bdfeddf32 100644 (file)
@@ -1,9 +1,9 @@
-;;; tempo.el --- templates with hotspots
+;;; tempo.el --- Flexible template insertion
 ;; Copyright (C) 1994 Free Software Foundation, Inc.
 
 ;; Author: David K}gedal <davidk@lysator.liu.se >
 ;; Created: 16 Feb 1994
-;; Version: 1.1.1
+;; Version: 1.2
 ;; Keywords: extensions, languages, tools
 
 ;; This file is part of GNU Emacs.
 ;; be the template definition, and its function value will be an
 ;; interactive function that inserts the template at the point.
 
-;; Full documentation for tempo.el can be found on the World Wide Web
-;; at http://www.lysator.liu.se:7500/~davidk/tempo.html (not yet
-;; completed)
-
 ;; The latest tempo.el distribution can be fetched from
 ;; ftp.lysator.liu.se in the directory /pub/emacs
 
+;;; Known bugs:
+
+;; If the 'o is the first element in a template, strange things can
+;; happen when the template is inserted at the beginning of a
+;; line. This is due to strange behaviour in open-line. But it should
+;; be easily avoided.
+
+;; The 'o tag is also a problem when including the region. This will
+;; be looked into.
+
+;; Clicking mouse-2 in the completion buffer gives strange results.
+
+;; There is a bug in some emacs versions that prevents completion from
+;; working. If it doesn't work for you, send me a note indicating your
+;; emacs version and your problems.
+
 ;;; Code:
 
-(provide 'tempo)
+;; (provide 'tempo)
 
-;;; Variables
+;;; User options
 
 (defvar tempo-interactive nil
   "*Prompt user for strings in templates.
@@ -93,7 +105,9 @@ user for text to insert in the templates")
   "*Automatically insert current region when there is a `r' in the template
 If this variable is NIL, `r' elements will be treated just like `p'
 elements, unless the template function is given a prefix (or a non-nil
-argument). If this variable is non-NIL, the behaviour is reversed.")
+argument). If this variable is non-NIL, the behaviour is reversed.
+
+In Transient Mark mode, this option is unused.")
 
 (defvar tempo-show-completion-buffer t
   "*If non-NIL, show a buffer with possible completions, when only
@@ -103,6 +117,8 @@ a partial completion can be found")
   "*If NIL, a completion buffer generated by \\[tempo-complete-tag]
 disappears at the next keypress; otherwise, it remains forever.")
 
+;;; Internal variables
+
 (defvar tempo-insert-string-functions nil
   "List of functions to run when inserting a string.
 Each function is called with a single arg, STRING."  )
@@ -112,7 +128,6 @@ Each function is called with a single arg, STRING."  )
 
 (defvar tempo-local-tags '((tempo-tags . nil))
   "A list of locally installed tag completion lists.
-
 It is a association list where the car of every element is a symbol
 whose varable value is a template list. The cdr part, if non-nil, is a
 function or a regexp that defines the string to match. See the
@@ -120,19 +135,56 @@ documentation for the function `tempo-complete-tag' for more info.
 
 `tempo-tags' is always in the last position in this list.")
 
+(defvar tempo-collection nil
+  "A collection of all the tags defined for the current buffer.")
+
+(defvar tempo-dirty-collection t
+  "Indicates if the tag collection needs to be rebuilt.")
+
 (defvar tempo-marks nil
   "A list of marks to jump to with `\\[tempo-forward-mark]' and `\\[tempo-backward-mark]'.")
 
-(defvar tempo-default-match-finder "\\b\\([^\\b]*\\)\\="
-  "The default regexp used to find the string to match against the tags.")
+(defvar tempo-match-finder "\\b\\([^\\b]+\\)\\="
+  "The regexp or function used to find the string to match against tags.
+
+If `tempo-match-finder is a string, it should contain a regular
+expression with at least one \\( \\) pair. When searching for tags,
+`tempo-complete-tag' calls `re-search-backward' with this string, and
+the string between the first \\( and \\) is used for matching against
+each string in the tag list. If one is found, the whole text between
+the first \\( and the point is replaced with the inserted template.
+
+You will probably want to include \\ \= at the end of the regexp to
+make sure that the string is matched only against text adjacent to the
+point.
+
+If `tempo-match-finder' is a symbol, it should be a function that
+returns a pair of the form (STRING . POS), where STRING is the string
+used for matching and POS is the buffer position after which text
+should be replaced with a template.")
+
+(defvar tempo-user-elements nil
+  "Element handlers for user-defined elements.
+A list of symbols which are bound to functions that take one argument.
+This function should return somthing to be sent to `tempo-insert' if
+it recognizes the argument, and NIL otherwise")
 
 (defvar tempo-named-insertions nil
   "Temporary storage for named insertions")
 
+(defvar tempo-region-start (make-marker)
+  "Region start when inserting around the region")
+
+(defvar tempo-region-stop (make-marker)
+  "Region stop when inserting around the region")
+
 ;; Make some variables local to every buffer
 
 (make-variable-buffer-local 'tempo-marks)
 (make-variable-buffer-local 'tempo-local-tags)
+(make-variable-buffer-local 'tempo-match-finder)
+(make-variable-buffer-local 'tempo-collection)
+(make-variable-buffer-local 'tempo-dirty-collection)
 
 ;;; Functions
 
@@ -163,10 +215,11 @@ The elements in ELEMENTS can be of several types:
    prompted in the minbuffer with PROMPT for a string to be inserted.
    If the optional parameter NAME is non-nil, the text is saved for
    later insertion with the `s' tag.
-   If `tempo-interactive is nil, it works like 'p.
+   If `tempo-interactive' is nil, it works like 'p.
  - (r PROMPT) like the previous, but if `tempo-interactive' is nil
    and `tempo-insert' is called with ON-REGION non-nil, the current
-   region is placed here.
+   region is placed here. This usually happens when you call the
+   template function with a prefix argument.
  - (s NAME) Inserts text previously read with the (p ..) construct.
    Finds the insertion saved under NAME and inserts it. Acts like 'p
    if tempo-interactive is nil.
@@ -178,7 +231,8 @@ The elements in ELEMENTS can be of several types:
  - '> The line is indented using `indent-according-to-mode'. Note that
    you often should place this item after the text you want on the
    line.
- - 'n> inserts a newline and indents line.
+ - 'n> Inserts a newline and indents line.
+ - 'o Like '% but leaves the point before the newline.
  - nil. It is ignored.
  - Anything else. It is evaluated and the result is parsed again."
 
@@ -204,40 +258,62 @@ The elements in ELEMENTS can be of several types:
 (defun tempo-insert-template (template on-region)
   "Insert a template.
 TEMPLATE is the template to be inserted.  If ON-REGION is non-nil the
-`r' elements are replaced with the current region."
+`r' elements are replaced with the current region. In Transient Mark
+mode, ON-REGION is ignored and assumed true if the region is active."
+  (if (and (boundp 'transient-mark-mode)
+          transient-mark-mode
+          mark-active)
+      (setq on-region t))
   (and on-region
-       (< (mark) (point))
-       (exchange-point-and-mark))
+       (set-marker tempo-region-start (min (mark) (point)))
+       (set-marker tempo-region-stop (max (mark) (point))))
+  (if on-region
+      (goto-char tempo-region-start))
   (save-excursion
     (tempo-insert-mark (point-marker))
-    (mapcar 'tempo-insert 
+    (mapcar (function (lambda (elt)
+                       (tempo-insert elt on-region)))
            (symbol-value template))
     (tempo-insert-mark (point-marker)))
   (tempo-forward-mark)
-  (tempo-forget-insertions))
+  (tempo-forget-insertions)
+  (and (boundp 'transient-mark-mode)
+       transient-mark-mode
+       (deactivate-mark)))
 
 ;;;
 ;;; tempo-insert
 
-(defun tempo-insert (element
+(defun tempo-insert (element on-region)
   "Insert a template element.
-Insert one element from a template. See documentation for
-`tempo-define-template' for the kind of elements possible."
+Insert one element from a template. If ON-REGION is non-nil the `r'
+elements are replaced with the current region.
+
+See documentation for `tempo-define-template' for the kind of elements
+possible."
   (cond ((stringp element) (tempo-process-and-insert-string element))
        ((and (consp element) (eq (car element) 'p))
         (tempo-insert-prompt (cdr element)))
        ((and (consp element) (eq (car element) 'r))
         (if on-region
-            (exchange-point-and-mark)
+            (goto-char tempo-region-stop)
           (tempo-insert-prompt (cdr element))))
        ((and (consp element) (eq (car element) 's))
         (if tempo-interactive
             (tempo-insert-named (cdr element))
           (tempo-insert-mark (point-marker))))
+       ((and (consp element) (eq (car element) 'l))
+        (mapcar (function (lambda (elt) (tempo-insert elt on-region)))
+                (cdr element)))
        ((eq element 'p) (tempo-insert-mark (point-marker)))
        ((eq element 'r) (if on-region
-                            (exchange-point-and-mark)
+                            (goto-char tempo-region-stop)
                           (tempo-insert-mark (point-marker))))
+       ((eq element 'r>) (if on-region
+                             (progn
+                               (goto-char tempo-region-stop)
+                               (indent-region (mark) (point) nil))
+                           (tempo-insert-mark (point-marker))))
        ((eq element '>) (indent-according-to-mode))
        ((eq element '&) (if (not (or (= (current-column) 0)
                                      (save-excursion
@@ -251,8 +327,19 @@ Insert one element from a template. See documentation for
                             (insert "\n")))
        ((eq element 'n) (insert "\n"))
        ((eq element 'n>) (insert "\n") (indent-according-to-mode))
+       ;; Bug: If the 'o is the first element in a template, strange
+       ;; things can happen when the template is inserted at the
+       ;; beginning of a line.
+       ((eq element 'o) (if (not (or on-region
+                                     (eolp)
+                                     (save-excursion
+                                       (re-search-forward
+                                        "\\=\\s-*$" nil t))))
+                            (open-line 1)))
        ((null element))
-       (t (tempo-insert (eval element)))))
+       (t (tempo-insert (or (tempo-is-user-element element)
+                            (eval element))
+                        on-region))))
 
 ;;;
 ;;; tempo-insert-prompt
@@ -280,6 +367,20 @@ a name to save the inserted text under."
              (tempo-remember-insertion save-name inserted-text))))
     (tempo-insert-mark (point-marker))))
 
+;;;
+;;; tempo-is-user-element
+
+(defun tempo-is-user-element (element)
+  "Tries all the user-defined element handlers in
+`tempo-user-elements'"
+  ;; Sigh... I need (some list)
+  (catch 'found
+    (mapcar (function (lambda (handler)
+                       (let ((result (funcall handler element)))
+                         (if result (throw 'found result)))))
+           tempo-user-elements)
+    (throw 'found nil)))
+
 ;;;
 ;;; tempo-remember-insertion
 
@@ -384,7 +485,6 @@ and insert the results."
 
 (defun tempo-add-tag (tag template &optional tag-list)
   "Add a template tag.
-
 Add the TAG, that should complete to TEMPLATE to the list in TAG-LIST,
 or to `tempo-tags' if TAG-LIST is nil."
 
@@ -392,52 +492,70 @@ or to `tempo-tags' if TAG-LIST is nil."
   (if (null tag-list)
       (setq tag-list 'tempo-tags))
   (if (not (assoc tag (symbol-value tag-list)))
-      (set tag-list (cons (cons tag template) (symbol-value tag-list)))))
+      (set tag-list (cons (cons tag template) (symbol-value tag-list))))
+  (tempo-invalidate-collection))
 
 ;;;
 ;;; tempo-use-tag-list
 
 (defun tempo-use-tag-list (tag-list &optional completion-function)
   "Install TAG-LIST to be used for template completion in the current buffer.
-
 TAG-LIST is a symbol whose variable value is a tag list created with
-`tempo-add-tag' and COMPLETION-FUNCTION is an optional function or
-string that is used by `\\[tempo-complete-tag]' to find a string to
-match the tag against.
-
-If COMPLETION-FUNCTION is a string, it should contain a regular
-expression with at least one \\( \\) pair. When searching for tags,
-`tempo-complete-tag' calls `re-search-backward' with this string, and
-the string between the first \\( and \\) is used for matching against
-each string in the tag list. If one is found, the whole text between
-the first \\( and the point is replaced with the inserted template.
-
-You will probably want to include \\ \= at the end of the regexp to make
-sure that the string is matched only against text adjacent to the
-point.
-
-If COPMLETION-FUNCTION is a symbol, it should be a function that
-returns a cons cell of the form (STRING . POS), where STRING is the
-string used for matching and POS is the buffer position after which
-text should be replaced with a template."
+`tempo-add-tag'.
 
+COMPLETION-FUNCTION is an obsolete option for specifyingis an optional
+function or string that is used by `\\[tempo-complete-tag]' to find a
+string to match the tag against. It has the same definition as the
+variable `tempo-match-finder'. In this version, supplying a
+COMPLETION-FUNCTION just sets `tempo-match-finder' locally."
   (let ((old (assq tag-list tempo-local-tags)))
     (if old
        (setcdr old completion-function)
       (setq tempo-local-tags (cons (cons tag-list completion-function)
-                                  tempo-local-tags)))))
+                                  tempo-local-tags))))
+  (if completion-function
+      (setq tempo-match-finder completion-function))
+  (tempo-invalidate-collection))
+
+;;;
+;;; tempo-invalidate-collection
+
+(defun tempo-invalidate-collection ()
+  "Marks the tag collection as obsolete.
+Whenever it is needed again it will be rebuilt."
+  (setq tempo-dirty-collection t))
+
+;;;
+;;; tempo-build-collection
+
+(defun tempo-build-collection ()
+  "Build a collection of all the tags and return it.
+If `tempo-dirty-collection' is NIL, the old collection is reused."
+  (setq tempo-dirty nil)
+  (or (and (not tempo-dirty-collection)
+          tempo-collection)
+      (setq tempo-collection
+           (apply (function append)
+                  (mapcar (function (lambda (tag-list)
+                                       ; If the format for
+                                       ; tempo-local-tags changes,
+                                       ; change this
+                                      (eval (car tag-list))))
+                          tempo-local-tags)))))
 
 ;;;
 ;;; tempo-find-match-string
 
 (defun tempo-find-match-string (finder)
   "Find a string to be matched against a tag list.
-
 FINDER is a function or a string. Returns (STRING . POS)."
   (cond ((stringp finder)
         (save-excursion
-          (re-search-backward finder nil t))
-        (cons (buffer-substring (match-beginning 1) (1+ (match-end 1)))
+          (or (re-search-backward finder nil t)
+              0))
+        (cons (buffer-substring (match-beginning 1)
+                                (match-end 1)) ; This seems to be a
+                                               ; bug in emacs
               (match-beginning 1)))
        (t
         (funcall finder))))
@@ -447,76 +565,44 @@ FINDER is a function or a string. Returns (STRING . POS)."
 
 (defun tempo-complete-tag (&optional silent)
   "Look for a tag and expand it.
+All the tags in the tag lists in `tempo-local-tags' (this includes
+`tempo-tags') are searched for a match for the text before the point.
+The way the string to match for is determined can be altered with the
+variable `tempo-match-finder'
+
+If a single match is found, the corresponding template is expanded in
+place of the matching string.
+
+If a partial completion or no match at all is found, and SILENT is
+non-NIL, the function will give a signal.
+
+If a partial completion is found and `tempo-show-completion-buffer' is
+non-NIL, a buffer containing possible completions is displayed."
+
+  ;; This function may look like a hack, but this is how I want it to
+  ;; work.
+  (interactive "*")
+  (let* ((collection (tempo-build-collection))
+        (match-info (tempo-find-match-string tempo-match-finder))
+        (match-string (car match-info))
+        (match-start (cdr match-info))
+        (exact (assoc match-string collection))
+        (compl (or (car exact)
+                   (try-completion match-string collection))))
+    (if compl (delete-region match-start (point)))
+    (cond ((null compl) (or silent (ding)))
+         ((eq compl t) (tempo-insert-template
+                        (cdr (assoc match-string
+                                    collection))
+                        nil))
+         (t (if (setq exact (assoc compl collection))
+                (tempo-insert-template (cdr exact) nil)
+              (insert compl)
+              (or silent (ding))
+              (if tempo-show-completion-buffer
+                  (tempo-display-completions match-string
+                                             collection)))))))
 
-It goes through the tag lists in `tempo-local-tags' (this includes
-`tempo-tags') and for each list it uses the corresponding match-finder
-function, or `tempo-default-match-finder' if none is given, and tries
-to match the match string against the tags in the list using
-`try-completion'. If none is found it proceeds to the next list until
-one is found. If a partial completion is found, it is replaced by the
-template if it can be completed uniquely, or completed as far as
-possible.
-
-When doing partial completion, only tags in the currently examined
-list are considered, so if you provide similar tags in different lists
-in `tempo-local-tags', the result may not be desirable.
-
-If no match is found or a partial match is found, and SILENT is
-non-nil, the function will give a signal.
-
-If tempo-show-completion-buffer is non-NIL, a buffer containing
-possible completions is displayed when a partial completion is found."
-
-  ;; This function is really messy. Some cleaning up is necessary.
-  (interactive)
-  (if (catch 'completed
-       (mapcar
-        (function
-         (lambda (tag-list-a)
-           (let* ((tag-list (symbol-value(car tag-list-a)))
-                  (match-string-finder (or (cdr tag-list-a)
-                                           tempo-default-match-finder))
-                  (match-info (tempo-find-match-string match-string-finder))
-                  (match-string (car match-info))
-                  (match-start (cdr match-info))
-                  (compl (or (cdr (assoc match-string tag-list))
-                             (try-completion match-string
-                                             tag-list))))
-       
-             (if compl                 ;any match
-                 (delete-region match-start (point)))
-
-             (cond
-              ((null compl)            ; No match
-               nil)
-              ((symbolp compl)         ; ??
-               (tempo-insert-template compl nil)
-               (throw 'completed t))
-              ((eq compl t)            ; Exact, sole match
-               (tempo-insert-template (cdr (assoc match-string tag-list))
-                                      nil)
-               (throw 'completed t))
-              ((stringp compl)         ; (partial) completion found
-               (let ((compl2 (assoc compl tag-list)))
-                 (if compl2
-                     (tempo-insert-template (cdr compl2) nil)
-                   (insert compl)
-                   (if t ;(string= match-string compl)
-                       (if tempo-show-completion-buffer
-                           (tempo-display-completions match-string
-                                                      tag-list)
-                         (if (not silent)
-                             (ding))))))
-               (throw 'completed t))))))
-        tempo-local-tags)
-       ;; No completion found. Return nil
-       nil)
-      ;; Do nothing if a completion was found
-      t
-    ;; No completion was found
-    (if (not silent)
-       (ding))
-    nil))
 
 ;;;
 ;;; tempo-display-completions
@@ -533,4 +619,6 @@ possible completions is displayed when a partial completion is found."
         (all-completions string tag-list)))
       (sit-for 32767))))
 
+(provide 'tempo)
+
 ;;; tempo.el ends here