diff options
author | Richard M. Stallman <rms@gnu.org> | 1995-01-22 06:18:46 +0000 |
---|---|---|
committer | Richard M. Stallman <rms@gnu.org> | 1995-01-22 06:18:46 +0000 |
commit | 785c4478dd45aad5ba2ce78afd616f902833c107 (patch) | |
tree | 9b866874c3cc707a251d9f09f19462dd44310d73 /lisp/tempo.el | |
parent | 7fee19128471fd594baac54c7ccee54b48d9ff32 (diff) | |
download | emacs-785c4478dd45aad5ba2ce78afd616f902833c107.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.el | 330 |
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 |