]> code.delx.au - gnu-emacs/blobdiff - lisp/progmodes/etags.el
(bdf-generate-font): New argument CHARSET. Give WIDTH
[gnu-emacs] / lisp / progmodes / etags.el
index 0afff9a937490853675760a064b6c416b37762a8..fdc73dd47af099884d26593cc65db6c334e71df8 100644 (file)
@@ -1,6 +1,5 @@
 ;;; etags.el --- etags facility for Emacs
-
-;; Copyright (C) 1985, 1986, 1988, 1989, 1992, 1993, 1994, 1995
+;; Copyright (C) 1985, 1986, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1998
 ;;     Free Software Foundation, Inc.
 
 ;; Author: Roland McGrath <roland@gnu.ai.mit.edu>
@@ -25,6 +24,8 @@
 
 ;;; Code:
 
+(require 'ring)
+
 ;;;###autoload
 (defvar tags-file-name nil
   "*File name of tags table.
@@ -34,21 +35,35 @@ Use the `etags' program to make a tags table file.")
 ;; Make M-x set-variable tags-file-name like M-x visit-tags-table.
 ;;;###autoload (put 'tags-file-name 'variable-interactive "fVisit tags table: ")
 
+(defgroup etags nil "Tags tables"
+  :group 'tools)
+
 ;;;###autoload
 ;; Use `visit-tags-table-buffer' to cycle through tags tables in this list.
-(defvar tags-table-list nil
+(defcustom tags-table-list nil
   "*List of file names of tags tables to search.
 An element that is a directory means the file \"TAGS\" in that directory.
 To switch to a new list of tags tables, setting this variable is sufficient.
 If you set this variable, do not also set `tags-file-name'.
-Use the `etags' program to make a tags table file.")
+Use the `etags' program to make a tags table file."
+  :group 'etags
+  :type '(repeat file))
 
 ;;;###autoload
-(defvar tags-add-tables 'ask-user
+(defcustom tags-add-tables 'ask-user
   "*Control whether to add a new tags table to the current list.
 t means do; nil means don't (always start a new list).
 Any other value means ask the user whether to add a new tags table
-to the current list (as opposed to starting a new list).")
+to the current list (as opposed to starting a new list)."
+  :group 'etags
+  :type '(choice (const :tag "Do" t)
+                (const :tag "Don't" nil)
+                (other :tag "Ask" ask-user)))
+
+(defcustom tags-revert-without-query nil
+  "*Non-nil means reread a TAGS table without querying, if it has changed."
+  :group 'etags
+  :type 'boolean)
 
 (defvar tags-table-computed-list nil
   "List of tags tables to search, computed from `tags-table-list'.
@@ -76,25 +91,38 @@ Use `visit-tags-table-buffer' to cycle through tags tables in this list.")
 Each element is a list of strings which are file names.")
 
 ;;;###autoload
-(defvar find-tag-hook nil
+(defcustom find-tag-hook nil
   "*Hook to be run by \\[find-tag] after finding a tag.  See `run-hooks'.
 The value in the buffer in which \\[find-tag] is done is used,
-not the value in the buffer \\[find-tag] goes to.")
+not the value in the buffer \\[find-tag] goes to."
+  :group 'etags
+  :type 'hook)
 
 ;;;###autoload
-(defvar find-tag-default-function nil
+(defcustom find-tag-default-function nil
   "*A function of no arguments used by \\[find-tag] to pick a default tag.
 If nil, and the symbol that is the value of `major-mode'
 has a `find-tag-default-function' property (see `put'), that is used.
-Otherwise, `find-tag-default' is used.")
+Otherwise, `find-tag-default' is used."
+  :group 'etags
+  :type 'function)
+
+(defcustom find-tag-marker-ring-length 16
+  "*Length of marker rings `find-tag-marker-ring' and `tags-location-ring'."
+  :group 'etags
+  :type 'integer
+  :version "20.3")
+
+(defvar find-tag-marker-ring (make-ring find-tag-marker-ring-length)
+  "Ring of markers which are locations from which \\[find-tag] was invoked.")
 
 (defvar default-tags-table-function nil
   "If non-nil, a function to choose a default tags file for a buffer.
 This function receives no arguments and should return the default
 tags table file to use for the current buffer.")
 
-(defvar tags-location-stack nil
-  "List of markers which are locations visited by \\[find-tag].
+(defvar tags-location-ring (make-ring find-tag-marker-ring-length)
+  "Ring of markers which are locations visited by \\[find-tag].
 Pop back to the last location with \\[negative-argument] \\[find-tag].")
 \f
 ;; Tags table state.
@@ -117,8 +145,8 @@ nil means it has not yet been computed; use `tags-table-files' to do so.")
 
 (defvar tags-table-format-hooks '(etags-recognize-tags-table
                                  recognize-empty-tags-table)
-  "List of functions to be called in a tags table buffer to identify
-the type of tags table.  The functions are called in order, with no arguments,
+  "List of functions to be called in a tags table buffer to identify the type of tags table.  
+The functions are called in order, with no arguments,
 until one returns non-nil.  The function should make buffer-local bindings
 of the format-parsing tags function variables if successful.")
 
@@ -152,8 +180,7 @@ One argument, the tag info returned by `snarf-tag-function'.")
 (defvar tags-included-tables-function nil
   "Function to do the work of `tags-included-tables' (which see).")
 (defvar verify-tags-table-function nil
-  "Function to return t iff the current buffer contains a valid
-\(already initialized\) tags file.")
+  "Function to return t iff current buffer contains valid tags file.")
 \f
 ;; Initialize the tags table in the current buffer.
 ;; Returns non-nil iff it is a valid tags table.  On
@@ -163,6 +190,8 @@ One argument, the tag info returned by `snarf-tag-function'.")
   (set (make-local-variable 'tags-table-files) nil)
   (set (make-local-variable 'tags-completion-table) nil)
   (set (make-local-variable 'tags-included-tables) nil)
+  (setq find-tag-marker-ring (make-ring find-tag-marker-ring-length))
+  (setq tags-location-ring (make-ring find-tag-marker-ring-length))
   ;; Value is t if we have found a valid tags table buffer.
   (let ((hooks tags-table-format-hooks))
     (while (and hooks
@@ -314,10 +343,22 @@ Returns non-nil iff it is a valid table."
        (set-buffer (get-file-buffer file))
        (setq win (or verify-tags-table-function (initialize-new-tags-table)))
        (if (or (verify-visited-file-modtime (current-buffer))
-               (not (yes-or-no-p
-                     (format "Tags file %s has changed, read new contents? "
-                             file))))
-           (and win (funcall verify-tags-table-function))
+               ;; Decide whether to revert the file.
+               ;; revert-without-query can say to revert
+               ;; or the user can say to revert.
+               (not (or (let ((tail revert-without-query)
+                              (found nil))
+                          (while tail
+                            (if (string-match (car tail) buffer-file-name)
+                                (setq found t))
+                            (setq tail (cdr tail)))
+                          found)
+                        tags-revert-without-query
+                        (yes-or-no-p
+                         (format "Tags file %s has changed, read new contents? "
+                                 file)))))
+           (and verify-tags-table-function
+                (funcall verify-tags-table-function))
          (revert-buffer t t)
          (initialize-new-tags-table)))
     (and (file-exists-p file)
@@ -420,8 +461,8 @@ Returns t if it visits a tags table, or nil if there are no more in the list."
         (or tags-file-name
             (error "%s"
                    (substitute-command-keys
-                    (concat "No tags table in use "
-                            "Use \\[visit-tags-table] to select one.")))))
+                    (concat "No tags table in use; "
+                            "use \\[visit-tags-table] to select one")))))
 
        ((eq t cont)
         ;; Find the next table.
@@ -570,11 +611,19 @@ Returns t if it visits a tags table, or nil if there are no more in the list."
        (error "File %s is not a valid tags table" local-tags-file-name)))))
 
 (defun tags-reset-tags-tables ()
-  "Reset tags state to cancel effect of any previous \\[visit-tags-table]
-or \\[find-tag]."
+  "Reset tags state to cancel effect of any previous \\[visit-tags-table] or \\[find-tag]."
   (interactive)
   (setq tags-file-name nil
-       tags-location-stack nil
+       tags-location-ring (progn
+                            (mapcar (lambda (m)
+                                      (set-marker m nil))
+                                    tags-location-ring)
+                            (make-ring find-tag-marker-ring-length))
+       find-tag-marker-ring (progn
+                              (mapcar (lambda (m)
+                                        (set-marker m nil))
+                                      find-tag-marker-ring)
+                              (make-ring find-tag-marker-ring-length))
        tags-table-list nil
        tags-table-computed-list nil
        tags-table-computed-list-for nil
@@ -673,7 +722,8 @@ Assumes the tags table is the current buffer."
         (spec (completing-read (if default
                                    (format "%s(default %s) " string default)
                                  string)
-                               'tags-complete-tag)))
+                               'tags-complete-tag
+                               nil nil nil nil default)))
     (if (equal spec "")
        (or default (error "There is no default tag"))
       spec)))
@@ -708,6 +758,10 @@ or just \\[negative-argument]), pop back to the previous tag gone to.
 
 If third arg REGEXP-P is non-nil, treat TAGNAME as a regexp.
 
+A marker representing the point when this command is onvoked is pushed
+onto a ring and may be popped back to with \\[pop-tag-mark].
+Contrast this with the ring of marks gone to by the command.
+
 See documentation of variable `tags-file-name'."
   (interactive (find-tag-interactive "Find tag: "))
 
@@ -717,19 +771,20 @@ See documentation of variable `tags-file-name'."
   (let ((local-find-tag-hook find-tag-hook))
     (if (eq '- next-p)
        ;; Pop back to a previous location.
-       (if (null tags-location-stack)
+       (if (ring-empty-p tags-location-ring)
            (error "No previous tag locations")
-         (let ((marker (car tags-location-stack)))
-           ;; Pop the stack.
-           (setq tags-location-stack (cdr tags-location-stack))
+         (let ((marker (ring-remove tags-location-ring 0)))
            (prog1
                ;; Move to the saved location.
-               (set-buffer (marker-buffer marker))
+               (set-buffer (or (marker-buffer marker)
+                                (error "The marked buffer has been deleted")))
              (goto-char (marker-position marker))
              ;; Kill that marker so it doesn't slow down editing.
              (set-marker marker nil nil)
              ;; Run the user's hook.  Do we really want to do this for pop?
              (run-hooks 'local-find-tag-hook))))
+      ;; Record whence we came.
+      (ring-insert find-tag-marker-ring (point-marker))
       (if next-p
          ;; Find the same table we last used.
          (visit-tags-table-buffer 'same)
@@ -757,8 +812,7 @@ See documentation of variable `tags-file-name'."
            (not next-p)))
          (set-marker marker (point))
          (run-hooks 'local-find-tag-hook)
-         (setq tags-location-stack
-               (cons marker tags-location-stack))
+         (ring-insert tags-location-ring marker)
          (current-buffer))))))
 
 ;;;###autoload
@@ -773,6 +827,12 @@ multiple matches for a tag, more exact matches are found first.  If NEXT-P
 is the atom `-' (interactively, with prefix arg that is a negative number
 or just \\[negative-argument]), pop back to the previous tag gone to.
 
+If third arg REGEXP-P is non-nil, treat TAGNAME as a regexp.
+
+A marker representing the point when this command is onvoked is pushed
+onto a ring and may be popped back to with \\[pop-tag-mark].
+Contrast this with the ring of marks gone to by the command.
+
 See documentation of variable `tags-file-name'."
   (interactive (find-tag-interactive "Find tag: "))
   (switch-to-buffer (find-tag-noselect tagname next-p regexp-p)))
@@ -791,6 +851,12 @@ multiple matches for a tag, more exact matches are found first.  If NEXT-P
 is negative (interactively, with prefix arg that is a negative number or
 just \\[negative-argument]), pop back to the previous tag gone to.
 
+If third arg REGEXP-P is non-nil, treat TAGNAME as a regexp.
+
+A marker representing the point when this command is onvoked is pushed
+onto a ring and may be popped back to with \\[pop-tag-mark].
+Contrast this with the ring of marks gone to by the command.
+
 See documentation of variable `tags-file-name'."
   (interactive (find-tag-interactive "Find tag other window: "))
 
@@ -826,6 +892,12 @@ multiple matches for a tag, more exact matches are found first.  If NEXT-P
 is negative (interactively, with prefix arg that is a negative number or
 just \\[negative-argument]), pop back to the previous tag gone to.
 
+If third arg REGEXP-P is non-nil, treat TAGNAME as a regexp.
+
+A marker representing the point when this command is onvoked is pushed
+onto a ring and may be popped back to with \\[pop-tag-mark].
+Contrast this with the ring of marks gone to by the command.
+
 See documentation of variable `tags-file-name'."
   (interactive (find-tag-interactive "Find tag other frame: "))
   (let ((pop-up-frames t))
@@ -845,12 +917,34 @@ just \\[negative-argument]), pop back to the previous tag gone to.
 
 If third arg OTHER-WINDOW is non-nil, select the buffer in another window.
 
+A marker representing the point when this command is onvoked is pushed
+onto a ring and may be popped back to with \\[pop-tag-mark].
+Contrast this with the ring of marks gone to by the command.
+
 See documentation of variable `tags-file-name'."
   (interactive (find-tag-interactive "Find tag regexp: " t))
   ;; We go through find-tag-other-window to do all the display hair there.
   (funcall (if other-window 'find-tag-other-window 'find-tag)
           regexp next-p t))
 ;;;###autoload (define-key esc-map [?\C-.] 'find-tag-regexp)
+
+;;;###autoload (define-key esc-map "*" 'pop-tag-mark)
+
+;;;###autoload
+(defun pop-tag-mark ()
+  "Pop back to where \\[find-tag] was last invoked.
+
+This is distinct from invoking \\[find-tag] with a negative argument
+since that pops a stack of markers at which tags were found, not from
+where they were found."
+  (interactive)
+  (if (ring-empty-p find-tag-marker-ring)
+      (error "No previous locations for find-tag invocation"))
+  (let ((marker (ring-remove find-tag-marker-ring 0)))
+    (switch-to-buffer (or (marker-buffer marker)
+                          (error "The marked buffer has been deleted")))
+    (goto-char (marker-position marker))
+    (set-marker marker nil nil)))
 \f
 ;; Internal tag finding function.
 
@@ -1000,10 +1094,9 @@ See documentation of variable `tags-file-name'."
 
 (defun etags-file-of-tag ()
   (save-excursion
-    (if (looking-at "./")
-        (re-search-forward "\\([^\n]+\\),[0-9]*\n")
-      (re-search-backward "\f\n\\([^\n]+\\),[0-9]*\n"))
-    (buffer-substring (match-beginning 1) (match-end 1))))
+    (re-search-backward "\f\n\\([^\n]+\\),[0-9]*\n")
+    (expand-file-name (buffer-substring (match-beginning 1) (match-end 1))
+                     (file-truename default-directory))))
 
 
 (defun etags-tags-completion-table ()
@@ -1075,11 +1168,12 @@ See documentation of variable `tags-file-name'."
 ;; a search window which expands until it hits the start of file.
 (defun etags-goto-tag-location (tag-info)
   (let ((startpos (cdr (cdr tag-info)))
+       (line (car (cdr tag-info)))
        offset found pat)
     (if (eq (car tag-info) t)
        ;; Direct file tag.
        (cond (line (goto-line line))
-             (position (goto-char position))
+             (startpos (goto-char startpos))
              (t (error "etags.el BUG: bogus direct file tag")))
       ;; This constant is 1/2 the initial search window.
       ;; There is no sense in making it too small,
@@ -1095,8 +1189,8 @@ See documentation of variable `tags-file-name'."
       (if startpos (setq startpos (1+ startpos)))
       ;; If no char pos was given, try the given line number.
       (or startpos
-         (if (car (cdr tag-info))
-             (setq startpos (progn (goto-line (car (cdr tag-info)))
+         (if line
+             (setq startpos (progn (goto-line line)
                                    (point)))))
       (or startpos
          (setq startpos (point-min)))
@@ -1234,12 +1328,12 @@ See documentation of variable `tags-file-name'."
 ;; point should be just after a string that matches TAG.
 (defun tag-word-match-p (tag)
   (and (looking-at "\\b.*\177")
-       (save-excursion (backward-char (1+ (length tag)))
+       (save-excursion (backward-char (length tag))
                       (looking-at "\\b"))))
 
 (defun tag-exact-file-name-match-p (tag)
   (and (looking-at ",")
-       (save-excursion (backward-char (1+ (length tag)))
+       (save-excursion (backward-char (length tag))
                       (looking-at "\f\n"))))
 
 ;; t if point is in a tag line with a tag containing TAG as a substring.
@@ -1302,7 +1396,7 @@ if the file was newly read in, the value is the filename."
     (and novisit
         (get-buffer " *next-file*")
         (kill-buffer " *next-file*"))
-    (error "All files processed."))
+    (error "All files processed"))
   (let* ((next (car next-file-list))
         (new (not (get-file-buffer next))))
     ;; Advance the list before trying to find the file.
@@ -1324,7 +1418,7 @@ if the file was newly read in, the value is the filename."
 (defvar tags-loop-scan
   '(error "%s"
          (substitute-command-keys
-          "No \\[tags-search] or \\[tags-query-replace] in progress."))
+          "No \\[tags-search] or \\[tags-query-replace] in progress"))
   "Form for `tags-loop-continue' to eval to scan one file.
 If it returns non-nil, this file needs processing by evalling
 \`tags-loop-operate'.  Otherwise, move on to the next file.")
@@ -1557,10 +1651,9 @@ see the doc of that variable if you want to add names to the list."
 (defun select-tags-table-quit ()
   "Kill the buffer and delete the selected window."
   (interactive)
-  (kill-buffer (current-buffer))
-  (or (one-window-p)
-      (delete-window)))
+  (quit-window t (selected-window)))
 \f
+;;; Note, there is another definition of this function in bindings.el.
 ;;;###autoload
 (defun complete-tag ()
   "Perform tags completion on the text around point.
@@ -1572,7 +1665,7 @@ for \\[find-tag] (which see)."
       tags-file-name
       (error "%s"
             (substitute-command-keys
-             "No tags table loaded.  Try \\[visit-tags-table].")))
+             "No tags table loaded; try \\[visit-tags-table]")))
   (let ((pattern (funcall (or find-tag-default-function
                              (get major-mode 'find-tag-default-function)
                              'find-tag-default)))
@@ -1597,8 +1690,6 @@ for \\[find-tag] (which see)."
             (display-completion-list
              (all-completions pattern 'tags-complete-tag nil)))
           (message "Making completion list...%s" "done")))))
-
-;;;###autoload (define-key esc-map "\t" 'complete-tag)
 \f
 (provide 'etags)