summaryrefslogtreecommitdiff
path: root/lisp/tempo.el
diff options
context:
space:
mode:
authorRichard M. Stallman <rms@gnu.org>1995-01-22 06:18:46 +0000
committerRichard M. Stallman <rms@gnu.org>1995-01-22 06:18:46 +0000
commita04c216226cb3e059b4bee029b491132184d3554 (patch)
treee383598a8e5f9d11490b85e7d9c2942d27bb23a3 /lisp/tempo.el
parent0b08dd2539f297d7953bb17c18158a4c038a448b (diff)
downloademacs-a04c216226cb3e059b4bee029b491132184d3554.tar.gz
(tempo-insert-template): Quoted transient-mark-mode
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.
Diffstat (limited to 'lisp/tempo.el')
-rw-r--r--lisp/tempo.el330
1 files changed, 209 insertions, 121 deletions
diff --git a/lisp/tempo.el b/lisp/tempo.el
index 9f809097dc3..2ff7b75516d 100644
--- a/lisp/tempo.el
+++ b/lisp/tempo.el
@@ -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.
@@ -71,18 +71,30 @@
;; 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
@@ -281,6 +368,20 @@ a name to save the inserted text under."
(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
(defun tempo-remember-insertion (save-name string)
@@ -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