emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] Changes to emacs/lisp/progmodes/etags.el [gnus-5_10-branch


From: Miles Bader
Subject: [Emacs-diffs] Changes to emacs/lisp/progmodes/etags.el [gnus-5_10-branch]
Date: Sat, 04 Sep 2004 08:30:33 -0400

Index: emacs/lisp/progmodes/etags.el
diff -c /dev/null emacs/lisp/progmodes/etags.el:1.179.2.1
*** /dev/null   Sat Sep  4 12:02:15 2004
--- emacs/lisp/progmodes/etags.el       Sat Sep  4 12:01:11 2004
***************
*** 0 ****
--- 1,2044 ----
+ ;;; etags.el --- etags facility for Emacs
+ 
+ ;; Copyright (C) 1985, 86, 88, 89, 92, 93, 94, 95, 96, 98, 2000, 2001
+ ;;    Free Software Foundation, Inc.
+ 
+ ;; Author: Roland McGrath <address@hidden>
+ ;; Maintainer: FSF
+ ;; Keywords: tools
+ 
+ ;; This file is part of GNU Emacs.
+ 
+ ;; GNU Emacs is free software; you can redistribute it and/or modify
+ ;; it under the terms of the GNU General Public License as published by
+ ;; the Free Software Foundation; either version 2, or (at your option)
+ ;; any later version.
+ 
+ ;; GNU Emacs is distributed in the hope that it will be useful,
+ ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ ;; GNU General Public License for more details.
+ 
+ ;; You should have received a copy of the GNU General Public License
+ ;; along with GNU Emacs; see the file COPYING.  If not, write to the
+ ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ ;; Boston, MA 02111-1307, USA.
+ 
+ ;;; Commentary:
+ 
+ ;;; Code:
+ 
+ (require 'ring)
+ (require 'button)
+ 
+ ;;;###autoload
+ (defvar tags-file-name nil
+   "*File name of tags table.
+ To switch to a new tags table, setting this variable is sufficient.
+ If you set this variable, do not also set `tags-table-list'.
+ 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
+ (defcustom tags-case-fold-search 'default
+   "*Whether tags operations should be case-sensitive.
+ A value of t means case-insensitive, a value of nil means case-sensitive.
+ Any other value means use the setting of `case-fold-search'."
+   :group 'etags
+   :type '(choice (const :tag "Case-sensitive" nil)
+                (const :tag "Case-insensitive" t)
+                (other :tag "Use default" default))
+   :version "21.1")
+ 
+ ;;;###autoload
+ ;; Use `visit-tags-table-buffer' to cycle through tags tables in this list.
+ (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."
+   :group 'etags
+   :type '(repeat file))
+ 
+ ;;;###autoload
+ (defcustom tags-compression-info-list '("" ".Z" ".bz2" ".gz" ".tgz")
+   "*List of extensions tried by etags when jka-compr is used.
+ An empty string means search the non-compressed file.
+ These extensions will be tried only if jka-compr was activated
+ \(i.e. via customize of `auto-compression-mode' or by calling the function
+ `auto-compression-mode')."
+   :type  '(repeat string)
+   :group 'etags)
+ 
+ ;; !!! tags-compression-info-list should probably be replaced by access
+ ;; to directory list and matching jka-compr-compression-info-list. Currently,
+ ;; this implementation forces each modification of
+ ;; jka-compr-compression-info-list to be reflected in this var.
+ ;; An alternative could be to say that introducing a special
+ ;; element in this list (e.g. t) means : try at this point
+ ;; using directory listing and regexp matching using
+ ;; jka-compr-compression-info-list.
+ 
+ 
+ ;;;###autoload
+ (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)."
+   :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'.
+ This includes tables implicitly included by other tables.  The list is not
+ always complete: the included tables of a table are not known until that
+ table is read into core.  An element that is t is a placeholder
+ indicating that the preceding element is a table that has not been read
+ into core and might contain included tables to search.
+ See `tags-table-check-computed-list'.")
+ 
+ (defvar tags-table-computed-list-for nil
+   "Value of `tags-table-list' that `tags-table-computed-list' corresponds to.
+ If `tags-table-list' changes, `tags-table-computed-list' is thrown away and
+ recomputed; see `tags-table-check-computed-list'.")
+ 
+ (defvar tags-table-list-pointer nil
+   "Pointer into `tags-table-computed-list' for the current state of searching.
+ Use `visit-tags-table-buffer' to cycle through tags tables in this list.")
+ 
+ (defvar tags-table-list-started-at nil
+   "Pointer into `tags-table-computed-list', where the current search 
started.")
+ 
+ (defvar tags-table-set-list nil
+   "List of sets of tags table which have been used together in the past.
+ Each element is a list of strings which are file names.")
+ 
+ ;;;###autoload
+ (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."
+   :group 'etags
+   :type 'hook)
+ 
+ ;;;###autoload
+ (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."
+   :group 'etags
+   :type '(choice (const nil) 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")
+ 
+ (defcustom tags-tag-face 'default
+   "*Face for tags in the output of `tags-apropos'."
+   :group 'etags
+   :type 'face
+   :version "21.1")
+ 
+ (defcustom tags-apropos-verbose nil
+   "If non-nil, print the name of the tags file in the *Tags List* buffer."
+   :group 'etags
+   :type 'boolean
+   :version "21.1")
+ 
+ (defcustom tags-apropos-additional-actions nil
+   "Specify additional actions for `tags-apropos'.
+ 
+ If non-nil, value should be a list of triples (TITLE FUNCTION
+ TO-SEARCH).  For each triple, `tags-apropos' processes TO-SEARCH and
+ lists tags from it.  TO-SEARCH should be an alist, obarray, or symbol.
+ If it is a symbol, the symbol's value is used.
+ TITLE, a string, is a title used to label the additional list of tags.
+ FUNCTION is a function to call when a symbol is selected in the
+ *Tags List* buffer.  It will be called with one argument SYMBOL which
+ is the symbol being selected.
+ 
+ Example value:
+ 
+   '((\"Emacs Lisp\" Info-goto-emacs-command-node obarray)
+     (\"Common Lisp\" common-lisp-hyperspec common-lisp-hyperspec-obarray)
+     (\"SCWM\" scwm-documentation scwm-obarray))"
+   :group 'etags
+   :type '(repeat (list (string :tag "Title")
+                      function
+                      (sexp :tag "Tags to search")))
+   :version "21.1")
+ 
+ (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-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].")
+ 
+ ;; Tags table state.
+ ;; These variables are local in tags table buffers.
+ 
+ (defvar tags-table-files nil
+   "List of file names covered by current tags table.
+ nil means it has not yet been computed; use `tags-table-files' to do so.")
+ 
+ (defvar tags-completion-table nil
+   "Obarray of tag names defined in current tags table.")
+ 
+ (defvar tags-included-tables nil
+   "List of tags tables included by the current tags table.")
+ 
+ (defvar next-file-list nil
+   "List of files for \\[next-file] to process.")
+ 
+ ;; Hooks for file formats.
+ 
+ (defvar tags-table-format-functions '(etags-recognize-tags-table
+                                     tags-recognize-empty-tags-table)
+   "Hook 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.")
+ 
+ (defvar file-of-tag-function nil
+   "Function to do the work of `file-of-tag' (which see).
+ One optional argument, a boolean specifying to return complete path (nil) or
+ relative path (non-nil).")
+ (defvar tags-table-files-function nil
+   "Function to do the work of `tags-table-files' (which see).")
+ (defvar tags-completion-table-function nil
+   "Function to build the `tags-completion-table'.")
+ (defvar snarf-tag-function nil
+   "Function to get info about a matched tag for `goto-tag-location-function'.
+ One optional argument, specifying to use explicit tag (non-nil) or not (nil).
+ The default is nil.")
+ (defvar goto-tag-location-function nil
+   "Function of to go to the location in the buffer specified by a tag.
+ One argument, the tag info returned by `snarf-tag-function'.")
+ (defvar find-tag-regexp-search-function nil
+   "Search function passed to `find-tag-in-order' for finding a regexp tag.")
+ (defvar find-tag-regexp-tag-order nil
+   "Tag order passed to `find-tag-in-order' for finding a regexp tag.")
+ (defvar find-tag-regexp-next-line-after-failure-p nil
+   "Flag passed to `find-tag-in-order' for finding a regexp tag.")
+ (defvar find-tag-search-function nil
+   "Search function passed to `find-tag-in-order' for finding a tag.")
+ (defvar find-tag-tag-order nil
+   "Tag order passed to `find-tag-in-order' for finding a tag.")
+ (defvar find-tag-next-line-after-failure-p nil
+   "Flag passed to `find-tag-in-order' for finding a tag.")
+ (defvar list-tags-function nil
+   "Function to do the work of `list-tags' (which see).")
+ (defvar tags-apropos-function nil
+   "Function to do the work of `tags-apropos' (which see).")
+ (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 current buffer contains valid tags file.")
+ 
+ ;; Initialize the tags table in the current buffer.
+ ;; Returns non-nil iff it is a valid tags table.  On
+ ;; non-nil return, the tags table state variable are
+ ;; made buffer-local and initialized to nil.
+ (defun initialize-new-tags-table ()
+   (set (make-local-variable 'tags-table-files) nil)
+   (set (make-local-variable 'tags-completion-table) nil)
+   (set (make-local-variable 'tags-included-tables) nil)
+   ;; We used to initialize find-tag-marker-ring and tags-location-ring
+   ;; here, to new empty rings.  But that is wrong, because those
+   ;; are global.
+ 
+   ;; Value is t if we have found a valid tags table buffer.
+   (run-hook-with-args-until-success 'tags-table-format-functions))
+ 
+ ;;;###autoload
+ (defun visit-tags-table (file &optional local)
+   "Tell tags commands to use tags table file FILE.
+ FILE should be the name of a file created with the `etags' program.
+ A directory name is ok too; it means file TAGS in that directory.
+ 
+ Normally \\[visit-tags-table] sets the global value of `tags-file-name'.
+ With a prefix arg, set the buffer-local value instead.
+ When you find a tag with \\[find-tag], the buffer it finds the tag
+ in is given a local value of this variable which is the name of the tags
+ file the tag was in."
+   (interactive (list (read-file-name "Visit tags table: (default TAGS) "
+                                    default-directory
+                                    (expand-file-name "TAGS"
+                                                      default-directory)
+                                    t)
+                    current-prefix-arg))
+   (or (stringp file) (signal 'wrong-type-argument (list 'stringp file)))
+   ;; Bind tags-file-name so we can control below whether the local or
+   ;; global value gets set.  Calling visit-tags-table-buffer will
+   ;; initialize a buffer for the file and set tags-file-name to the
+   ;; Calling visit-tags-table-buffer with tags-file-name set to FILE will
+   ;; initialize a buffer for FILE and set tags-file-name to the
+   ;; fully-expanded name.
+   (let ((tags-file-name file))
+     (save-excursion
+       (or (visit-tags-table-buffer file)
+         (signal 'file-error (list "Visiting tags table"
+                                   "file does not exist"
+                                   file)))
+       ;; Set FILE to the expanded name.
+       (setq file tags-file-name)))
+   (if local
+       ;; Set the local value of tags-file-name.
+       (set (make-local-variable 'tags-file-name) file)
+     ;; Set the global value of tags-file-name.
+     (setq-default tags-file-name file)))
+ 
+ (defun tags-table-check-computed-list ()
+   "Compute `tags-table-computed-list' from `tags-table-list' if necessary."
+   (let ((expanded-list (mapcar 'tags-expand-table-name tags-table-list)))
+     (or (equal tags-table-computed-list-for expanded-list)
+       ;; The list (or default-directory) has changed since last computed.
+       (let* ((compute-for (mapcar 'copy-sequence expanded-list))
+              (tables (copy-sequence compute-for)) ;Mutated in the loop.
+              (computed nil)
+              table-buffer)
+ 
+         (while tables
+           (setq computed (cons (car tables) computed)
+                 table-buffer (get-file-buffer (car tables)))
+           (if (and table-buffer
+                    ;; There is a buffer visiting the file.  Now make sure
+                    ;; it is initialized as a tag table buffer.
+                    (save-excursion
+                      (tags-verify-table (buffer-file-name table-buffer))))
+               (save-excursion
+                 (set-buffer table-buffer)
+                 (if (tags-included-tables)
+                     ;; Insert the included tables into the list we
+                     ;; are processing.
+                     (setcdr tables (nconc (mapcar 'tags-expand-table-name
+                                                   (tags-included-tables))
+                                           (cdr tables)))))
+             ;; This table is not in core yet.  Insert a placeholder
+             ;; saying we must read it into core to check for included
+             ;; tables before searching the next table in the list.
+             (setq computed (cons t computed)))
+           (setq tables (cdr tables)))
+ 
+         ;; Record the tags-table-list value (and the context of the
+         ;; current directory) we computed from.
+         (setq tags-table-computed-list-for compute-for
+               tags-table-computed-list (nreverse computed))))))
+ 
+ ;; Extend `tags-table-computed-list' to remove the first `t' placeholder.
+ ;; An element of the list that is `t' is a placeholder indicating that the
+ ;; preceding element is a table that has not been read into core and might
+ ;; contain included tables to search.  On return, the first placeholder
+ ;; element will be gone and the element before it read into core and its
+ ;; included tables inserted into the list.
+ (defun tags-table-extend-computed-list ()
+   (let ((list tags-table-computed-list))
+     (while (not (eq (nth 1 list) t))
+       (setq list (cdr list)))
+     (save-excursion
+       (if (tags-verify-table (car list))
+         ;; We are now in the buffer visiting (car LIST).  Extract its
+         ;; list of included tables and insert it into the computed list.
+         (let ((tables (tags-included-tables))
+               (computed nil)
+               table-buffer)
+           (while tables
+             (setq computed (cons (car tables) computed)
+                   table-buffer (get-file-buffer (car tables)))
+             (if table-buffer
+                 (save-excursion
+                   (set-buffer table-buffer)
+                   (if (tags-included-tables)
+                       ;; Insert the included tables into the list we
+                       ;; are processing.
+                       (setcdr tables (append (tags-included-tables)
+                                              tables))))
+               ;; This table is not in core yet.  Insert a placeholder
+               ;; saying we must read it into core to check for included
+               ;; tables before searching the next table in the list.
+               (setq computed (cons t computed)))
+             (setq tables (cdr tables)))
+           (setq computed (nreverse computed))
+           ;; COMPUTED now contains the list of included tables (and
+           ;; tables included by them, etc.).  Now splice this into the
+           ;; current list.
+           (setcdr list (nconc computed (cdr (cdr list)))))
+       ;; It was not a valid table, so just remove the following placeholder.
+       (setcdr list (cdr (cdr list)))))))
+ 
+ ;; Expand tags table name FILE into a complete file name.
+ (defun tags-expand-table-name (file)
+   (setq file (expand-file-name file))
+   (if (file-directory-p file)
+       (expand-file-name "TAGS" file)
+     file))
+ 
+ ;; Like member, but comparison is done after tags-expand-table-name on both
+ ;; sides and elements of LIST that are t are skipped.
+ (defun tags-table-list-member (file list)
+   (setq file (tags-expand-table-name file))
+   (while (and list
+             (or (eq (car list) t)
+                 (not (string= file (tags-expand-table-name (car list))))))
+     (setq list (cdr list)))
+   list)
+ 
+ (defun tags-verify-table (file)
+   "Read FILE into a buffer and verify that it is a valid tags table.
+ Sets the current buffer to one visiting FILE (if it exists).
+ Returns non-nil iff it is a valid table."
+   (if (get-file-buffer file)
+       ;; The file is already in a buffer.  Check for the visited file
+       ;; having changed since we last used it.
+       (let (win)
+       (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))
+               ;; 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)
+        (progn
+          (set-buffer (find-file-noselect file))
+          (or (string= file buffer-file-name)
+              ;; find-file-noselect has changed the file name.
+              ;; Propagate the change to tags-file-name and tags-table-list.
+              (let ((tail (member file tags-table-list)))
+                (if tail
+                    (setcar tail buffer-file-name))
+                (if (eq file tags-file-name)
+                    (setq tags-file-name buffer-file-name))))
+          (initialize-new-tags-table)))))
+ 
+ ;; Subroutine of visit-tags-table-buffer.  Search the current tags tables
+ ;; for one that has tags for THIS-FILE (or that includes a table that
+ ;; does).  Return the name of the first table table listing THIS-FILE; if
+ ;; the table is one included by another table, it is the master table that
+ ;; we return.  If CORE-ONLY is non-nil, check only tags tables that are
+ ;; already in buffers--don't visit any new files.
+ (defun tags-table-including (this-file core-only)
+   (let ((tables tags-table-computed-list)
+       (found nil))
+     ;; Loop over the list, looking for a table containing tags for THIS-FILE.
+     (while (and (not found)
+               tables)
+ 
+       (if core-only
+         ;; Skip tables not in core.
+         (while (eq (nth 1 tables) t)
+           (setq tables (cdr (cdr tables))))
+       (if (eq (nth 1 tables) t)
+           ;; This table has not been read into core yet.  Read it in now.
+           (tags-table-extend-computed-list)))
+ 
+       (if tables
+         ;; Select the tags table buffer and get the file list up to date.
+         (let ((tags-file-name (car tables)))
+           (visit-tags-table-buffer 'same)
+           (if (member this-file (mapcar 'expand-file-name
+                                         (tags-table-files)))
+               ;; Found it.
+               (setq found tables))))
+       (setq tables (cdr tables)))
+     (if found
+       ;; Now determine if the table we found was one included by another
+       ;; table, not explicitly listed.  We do this by checking each
+       ;; element of the computed list to see if it appears in the user's
+       ;; explicit list; the last element we will check is FOUND itself.
+       ;; Then we return the last one which did in fact appear in
+       ;; tags-table-list.
+       (let ((could-be nil)
+             (elt tags-table-computed-list))
+         (while (not (eq elt (cdr found)))
+           (if (tags-table-list-member (car elt) tags-table-list)
+               ;; This table appears in the user's list, so it could be
+               ;; the one which includes the table we found.
+               (setq could-be (car elt)))
+           (setq elt (cdr elt))
+           (if (eq t (car elt))
+               (setq elt (cdr elt))))
+         ;; The last element we found in the computed list before FOUND
+         ;; that appears in the user's list will be the table that
+         ;; included the one we found.
+         could-be))))
+ 
+ ;; Subroutine of visit-tags-table-buffer.  Move tags-table-list-pointer
+ ;; along and set tags-file-name.  Returns nil when out of tables.
+ (defun tags-next-table ()
+   ;; If there is a placeholder element next, compute the list to replace it.
+   (while (eq (nth 1 tags-table-list-pointer) t)
+     (tags-table-extend-computed-list))
+ 
+   ;; Go to the next table in the list.
+   (setq tags-table-list-pointer (cdr tags-table-list-pointer))
+   (or tags-table-list-pointer
+       ;; Wrap around.
+       (setq tags-table-list-pointer tags-table-computed-list))
+ 
+   (if (eq tags-table-list-pointer tags-table-list-started-at)
+       ;; We have come full circle.  No more tables.
+       (setq tags-table-list-pointer nil)
+     ;; Set tags-file-name to the name from the list.  It is already expanded.
+     (setq tags-file-name (car tags-table-list-pointer))))
+ 
+ ;;;###autoload
+ (defun visit-tags-table-buffer (&optional cont)
+   "Select the buffer containing the current tags table.
+ If optional arg is a string, visit that file as a tags table.
+ If optional arg is t, visit the next table in `tags-table-list'.
+ If optional arg is the atom `same', don't look for a new table;
+  just select the buffer visiting `tags-file-name'.
+ If arg is nil or absent, choose a first buffer from information in
+  `tags-file-name', `tags-table-list', `tags-table-list-pointer'.
+ Returns t if it visits a tags table, or nil if there are no more in the list."
+ 
+   ;; Set tags-file-name to the tags table file we want to visit.
+   (cond ((eq cont 'same)
+        ;; Use the ambient value of tags-file-name.
+        (or tags-file-name
+            (error "%s"
+                   (substitute-command-keys
+                    (concat "No tags table in use; "
+                            "use \\[visit-tags-table] to select one")))))
+ 
+       ((eq t cont)
+        ;; Find the next table.
+        (if (tags-next-table)
+            ;; Skip over nonexistent files.
+            (while (and (not (or (get-file-buffer tags-file-name)
+                                 (file-exists-p tags-file-name)))
+                        (tags-next-table)))))
+ 
+       (t
+        ;; Pick a table out of our hat.
+        (tags-table-check-computed-list) ;Get it up to date, we might use it.
+        (setq tags-file-name
+              (or
+               ;; If passed a string, use that.
+               (if (stringp cont)
+                   (prog1 cont
+                     (setq cont nil)))
+               ;; First, try a local variable.
+               (cdr (assq 'tags-file-name (buffer-local-variables)))
+               ;; Second, try a user-specified function to guess.
+               (and default-tags-table-function
+                    (funcall default-tags-table-function))
+               ;; Third, look for a tags table that contains tags for the
+               ;; current buffer's file.  If one is found, the lists will
+               ;; be frobnicated, and CONT will be set non-nil so we don't
+               ;; do it below.
+               (and buffer-file-name
+                    (or
+                     ;; First check only tables already in buffers.
+                     (tags-table-including buffer-file-name t)
+                     ;; Since that didn't find any, now do the
+                     ;; expensive version: reading new files.
+                     (tags-table-including buffer-file-name nil)))
+               ;; Fourth, use the user variable tags-file-name, if it is
+               ;; not already in the current list.
+               (and tags-file-name
+                    (not (tags-table-list-member tags-file-name
+                                                 tags-table-computed-list))
+                    tags-file-name)
+               ;; Fifth, use the user variable giving the table list.
+               ;; Find the first element of the list that actually exists.
+               (let ((list tags-table-list)
+                     file)
+                 (while (and list
+                             (setq file (tags-expand-table-name (car list)))
+                             (not (get-file-buffer file))
+                             (not (file-exists-p file)))
+                   (setq list (cdr list)))
+                 (car list))
+               ;; Finally, prompt the user for a file name.
+               (expand-file-name
+                (read-file-name "Visit tags table: (default TAGS) "
+                                default-directory
+                                "TAGS"
+                                t))))))
+ 
+   ;; Expand the table name into a full file name.
+   (setq tags-file-name (tags-expand-table-name tags-file-name))
+ 
+   (unless (and (eq cont t) (null tags-table-list-pointer))
+     ;; Verify that tags-file-name names a valid tags table.
+     ;; Bind another variable with the value of tags-file-name
+     ;; before we switch buffers, in case tags-file-name is buffer-local.
+     (let ((curbuf (current-buffer))
+         (local-tags-file-name tags-file-name))
+       (if (tags-verify-table local-tags-file-name)
+ 
+         ;; We have a valid tags table.
+         (progn
+           ;; Bury the tags table buffer so it
+           ;; doesn't get in the user's way.
+           (bury-buffer (current-buffer))
+ 
+           ;; If this was a new table selection (CONT is nil), make
+           ;; sure tags-table-list includes the chosen table, and
+           ;; update the list pointer variables.
+           (or cont
+               ;; Look in the list for the table we chose.
+               (let ((found (tags-table-list-member
+                             local-tags-file-name
+                             tags-table-computed-list)))
+                 (if found
+                     ;; There it is.  Just switch to it.
+                     (setq tags-table-list-pointer found
+                           tags-table-list-started-at found)
+ 
+                   ;; The table is not in the current set.
+                   ;; Try to find it in another previously used set.
+                   (let ((sets tags-table-set-list))
+                     (while (and sets
+                                 (not (tags-table-list-member
+                                       local-tags-file-name
+                                       (car sets))))
+                       (setq sets (cdr sets)))
+                     (if sets
+                         ;; Found in some other set.  Switch to that set.
+                         (progn
+                           (or (memq tags-table-list tags-table-set-list)
+                               ;; Save the current list.
+                               (setq tags-table-set-list
+                                     (cons tags-table-list
+                                           tags-table-set-list)))
+                           (setq tags-table-list (car sets)))
+ 
+                       ;; Not found in any existing set.
+                       (if (and tags-table-list
+                                (or (eq t tags-add-tables)
+                                    (and tags-add-tables
+                                         (y-or-n-p
+                                          (concat "Keep current list of "
+                                                  "tags tables also? ")))))
+                           ;; Add it to the current list.
+                           (setq tags-table-list (cons local-tags-file-name
+                                                       tags-table-list))
+ 
+                         ;; Make a fresh list, and store the old one.
+                         (message "Starting a new list of tags tables")
+                         (or (null tags-table-list)
+                             (memq tags-table-list tags-table-set-list)
+                             (setq tags-table-set-list
+                                   (cons tags-table-list
+                                         tags-table-set-list)))
+                         ;; Clear out buffers holding old tables.
+                         (dolist (table tags-table-list)
+                           ;; The list can contain items `t'.
+                           (if (stringp table)
+                               (let ((buffer (find-buffer-visiting table)))
+                             (if buffer
+                                 (kill-buffer buffer)))))
+                         (setq tags-table-list (list local-tags-file-name))))
+ 
+                     ;; Recompute tags-table-computed-list.
+                     (tags-table-check-computed-list)
+                     ;; Set the tags table list state variables to start
+                     ;; over from tags-table-computed-list.
+                     (setq tags-table-list-started-at tags-table-computed-list
+                           tags-table-list-pointer
+                           tags-table-computed-list)))))
+ 
+           ;; Return of t says the tags table is valid.
+           t)
+ 
+       ;; The buffer was not valid.  Don't use it again.
+       (set-buffer curbuf)
+       (kill-local-variable 'tags-file-name)
+       (if (eq local-tags-file-name tags-file-name)
+           (setq tags-file-name nil))
+       (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]."
+   (interactive)
+   ;; Clear out the markers we are throwing away.
+   (let ((i 0))
+     (while (< i find-tag-marker-ring-length)
+       (if (aref (cddr tags-location-ring) i)
+         (set-marker (aref (cddr tags-location-ring) i) nil))
+       (if (aref (cddr find-tag-marker-ring) i)
+         (set-marker (aref (cddr find-tag-marker-ring) i) nil))
+       (setq i (1+ i))))
+   (setq tags-file-name nil
+       tags-location-ring (make-ring find-tag-marker-ring-length)
+       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
+       tags-table-list-pointer nil
+       tags-table-list-started-at nil
+       tags-table-set-list nil))
+ 
+ (defun file-of-tag (&optional relative)
+   "Return the file name of the file whose tags point is within.
+ Assumes the tags table is the current buffer.
+ If RELATIVE is non-nil, file name returned is relative to tags
+ table file's directory. If RELATIVE is nil, file name returned
+ is complete."
+   (funcall file-of-tag-function relative))
+ 
+ ;;;###autoload
+ (defun tags-table-files ()
+   "Return a list of files in the current tags table.
+ Assumes the tags table is the current buffer.  The file names are returned
+ as they appeared in the `etags' command that created the table, usually
+ without directory names."
+   (or tags-table-files
+       (setq tags-table-files
+           (funcall tags-table-files-function))))
+ 
+ (defun tags-included-tables ()
+   "Return a list of tags tables included by the current table.
+ Assumes the tags table is the current buffer."
+   (or tags-included-tables
+       (setq tags-included-tables (funcall tags-included-tables-function))))
+ 
+ ;; Build tags-completion-table on demand.  The single current tags table
+ ;; and its included tags tables (and their included tables, etc.) have
+ ;; their tags included in the completion table.
+ (defun tags-completion-table ()
+   (or tags-completion-table
+       (condition-case ()
+         (prog2
+          (message "Making tags completion table for %s..." buffer-file-name)
+          (let ((included (tags-included-tables))
+                (table (funcall tags-completion-table-function)))
+            (save-excursion
+              ;; Iterate over the list of included tables, and combine each
+              ;; included table's completion obarray to the parent obarray.
+              (while included
+                ;; Visit the buffer.
+                (let ((tags-file-name (car included)))
+                  (visit-tags-table-buffer 'same))
+                ;; Recurse in that buffer to compute its completion table.
+                (if (tags-completion-table)
+                    ;; Combine the tables.
+                    (mapatoms (lambda (sym) (intern (symbol-name sym) table))
+                              tags-completion-table))
+                (setq included (cdr included))))
+            (setq tags-completion-table table))
+          (message "Making tags completion table for %s...done"
+                   buffer-file-name))
+       (quit (message "Tags completion table construction aborted.")
+             (setq tags-completion-table nil)))))
+ 
+ ;; Completion function for tags.  Does normal try-completion,
+ ;; but builds tags-completion-table on demand.
+ (defun tags-complete-tag (string predicate what)
+   (save-excursion
+     ;; If we need to ask for the tag table, allow that.
+     (let ((enable-recursive-minibuffers t))
+       (visit-tags-table-buffer))
+     (if (eq what t)
+       (all-completions string (tags-completion-table) predicate)
+       (try-completion string (tags-completion-table) predicate))))
+ 
+ ;; Read a tag name from the minibuffer with defaulting and completion.
+ (defun find-tag-tag (string)
+   (let* ((completion-ignore-case (if (memq tags-case-fold-search '(t nil))
+                                    tags-case-fold-search
+                                  case-fold-search))
+        (default (funcall (or find-tag-default-function
+                              (get major-mode 'find-tag-default-function)
+                              'find-tag-default)))
+        (spec (completing-read (if default
+                                   (format "%s (default %s): "
+                                           (substring string 0 (string-match 
"[ :]+\\'" string))
+                                           default)
+                                 string)
+                               'tags-complete-tag
+                               nil nil nil nil default)))
+     (if (equal spec "")
+       (or default (error "There is no default tag"))
+       spec)))
+ 
+ (defvar last-tag nil
+   "Last tag found by \\[find-tag].")
+ 
+ ;; Get interactive args for find-tag{-noselect,-other-window,-regexp}.
+ (defun find-tag-interactive (prompt &optional no-default)
+   (if (and current-prefix-arg last-tag)
+       (list nil (if (< (prefix-numeric-value current-prefix-arg) 0)
+                   '-
+                 t))
+     (list (if no-default
+             (read-string prompt)
+           (find-tag-tag prompt)))))
+ 
+ (defvar find-tag-history nil)
+ 
+ ;; Dynamic bondage:
+ (eval-when-compile
+   (defvar etags-case-fold-search)
+   (defvar etags-syntax-table))
+ 
+ ;;;###autoload
+ (defun find-tag-noselect (tagname &optional next-p regexp-p)
+   "Find tag (in current tags table) whose name contains TAGNAME.
+ Returns the buffer containing the tag's definition and moves its point there,
+ but does not select the buffer.
+ The default for TAGNAME is the expression in the buffer near point.
+ 
+ If second arg NEXT-P is t (interactively, with prefix arg), search for
+ another tag that matches the last tagname or regexp used.  When there are
+ 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 invoked 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: "))
+ 
+   (setq find-tag-history (cons tagname find-tag-history))
+   ;; Save the current buffer's value of `find-tag-hook' before
+   ;; selecting the tags table buffer.  For the same reason, save value
+   ;; of `tags-file-name' in case it has a buffer-local value.
+   (let ((local-find-tag-hook find-tag-hook))
+     (if (eq '- next-p)
+       ;; Pop back to a previous location.
+       (if (ring-empty-p tags-location-ring)
+           (error "No previous tag locations")
+         (let ((marker (ring-remove tags-location-ring 0)))
+           (prog1
+               ;; Move to the saved location.
+               (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 (and next-p last-tag)
+         ;; Find the same table we last used.
+         (visit-tags-table-buffer 'same)
+       ;; Pick a table to use.
+       (visit-tags-table-buffer)
+       ;; Record TAGNAME for a future call with NEXT-P non-nil.
+       (setq last-tag tagname))
+       ;; Record the location so we can pop back to it later.
+       (let ((marker (make-marker)))
+       (save-excursion
+         (set-buffer
+          ;; find-tag-in-order does the real work.
+          (find-tag-in-order
+           (if (and next-p last-tag) last-tag tagname)
+           (if regexp-p
+               find-tag-regexp-search-function
+             find-tag-search-function)
+           (if regexp-p
+               find-tag-regexp-tag-order
+             find-tag-tag-order)
+           (if regexp-p
+               find-tag-regexp-next-line-after-failure-p
+             find-tag-next-line-after-failure-p)
+           (if regexp-p "matching" "containing")
+           (or (not next-p) (not last-tag))))
+         (set-marker marker (point))
+         (run-hooks 'local-find-tag-hook)
+         (ring-insert tags-location-ring marker)
+         (current-buffer))))))
+ 
+ ;;;###autoload
+ (defun find-tag (tagname &optional next-p regexp-p)
+   "Find tag (in current tags table) whose name contains TAGNAME.
+ Select the buffer containing the tag's definition, and move point there.
+ The default for TAGNAME is the expression in the buffer around or before 
point.
+ 
+ If second arg NEXT-P is t (interactively, with prefix arg), search for
+ another tag that matches the last tagname or regexp used.  When there are
+ 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 invoked 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: "))
+   (let* ((buf (find-tag-noselect tagname next-p regexp-p))
+        (pos (with-current-buffer buf (point))))
+     (condition-case nil
+       (switch-to-buffer buf)
+       (error (pop-to-buffer buf)))
+     (goto-char pos)))
+ ;;;###autoload (define-key esc-map "." 'find-tag)
+ 
+ ;;;###autoload
+ (defun find-tag-other-window (tagname &optional next-p regexp-p)
+   "Find tag (in current tags table) whose name contains TAGNAME.
+ Select the buffer containing the tag's definition in another window, and
+ move point there.  The default for TAGNAME is the expression in the buffer
+ around or before point.
+ 
+ If second arg NEXT-P is t (interactively, with prefix arg), search for
+ another tag that matches the last tagname or regexp used.  When there are
+ 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 invoked 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: "))
+ 
+   ;; This hair is to deal with the case where the tag is found in the
+   ;; selected window's buffer; without the hair, point is moved in both
+   ;; windows.  To prevent this, we save the selected window's point before
+   ;; doing find-tag-noselect, and restore it after.
+   (let* ((window-point (window-point (selected-window)))
+        (tagbuf (find-tag-noselect tagname next-p regexp-p))
+        (tagpoint (progn (set-buffer tagbuf) (point))))
+     (set-window-point (prog1
+                         (selected-window)
+                       (switch-to-buffer-other-window tagbuf)
+                       ;; We have to set this new window's point; it
+                       ;; might already have been displaying a
+                       ;; different portion of tagbuf, in which case
+                       ;; switch-to-buffer-other-window doesn't set
+                       ;; the window's point from the buffer.
+                       (set-window-point (selected-window) tagpoint))
+                     window-point)))
+ ;;;###autoload (define-key ctl-x-4-map "." 'find-tag-other-window)
+ 
+ ;;;###autoload
+ (defun find-tag-other-frame (tagname &optional next-p)
+   "Find tag (in current tags table) whose name contains TAGNAME.
+ Select the buffer containing the tag's definition in another frame, and
+ move point there.  The default for TAGNAME is the expression in the buffer
+ around or before point.
+ 
+ If second arg NEXT-P is t (interactively, with prefix arg), search for
+ another tag that matches the last tagname or regexp used.  When there are
+ 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 invoked 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))
+     (find-tag-other-window tagname next-p)))
+ ;;;###autoload (define-key ctl-x-5-map "." 'find-tag-other-frame)
+ 
+ ;;;###autoload
+ (defun find-tag-regexp (regexp &optional next-p other-window)
+   "Find tag (in current tags table) whose name matches REGEXP.
+ Select the buffer containing the tag's definition and move point there.
+ 
+ If second arg NEXT-P is t (interactively, with prefix arg), search for
+ another tag that matches the last tagname or regexp used.  When there are
+ 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 OTHER-WINDOW is non-nil, select the buffer in another window.
+ 
+ A marker representing the point when this command is invoked 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)))
+ 
+ ;; Internal tag finding function.
+ 
+ ;; PATTERN is a string to pass to second arg SEARCH-FORWARD-FUNC, and to
+ ;; any member of the function list ORDER (third arg).  If ORDER is nil,
+ ;; use saved state to continue a previous search.
+ 
+ ;; Fourth arg NEXT-LINE-AFTER-FAILURE-P is non-nil if after a failed match,
+ ;; point should be moved to the next line.
+ 
+ ;; Fifth arg MATCHING is a string, an English '-ing' word, to be used in
+ ;; an error message.
+ 
+ ;; Algorithm is as follows.  For each qualifier-func in ORDER, go to
+ ;; beginning of tags file, and perform inner loop: for each naive match for
+ ;; PATTERN found using SEARCH-FORWARD-FUNC, qualify the naive match using
+ ;; qualifier-func.  If it qualifies, go to the specified line in the
+ ;; specified source file and return.  Qualified matches are remembered to
+ ;; avoid repetition.  State is saved so that the loop can be continued.
+ 
+ (defvar tag-lines-already-matched nil)        ;matches remembered here 
between calls
+ 
+ (defun find-tag-in-order (pattern
+                         search-forward-func
+                         order
+                         next-line-after-failure-p
+                         matching
+                         first-search)
+   (let (file                          ;name of file containing tag
+       tag-info                        ;where to find the tag in FILE
+       (first-table t)
+       (tag-order order)
+       (match-marker (make-marker))
+       goto-func
+       (case-fold-search (if (memq tags-case-fold-search '(nil t))
+                             tags-case-fold-search
+                           case-fold-search))
+       )
+     (save-excursion
+ 
+       (if first-search
+         ;; This is the start of a search for a fresh tag.
+         ;; Clear the list of tags matched by the previous search.
+         ;; find-tag-noselect has already put us in the first tags table
+         ;; buffer before we got called.
+         (setq tag-lines-already-matched nil)
+       ;; Continuing to search for the tag specified last time.
+       ;; tag-lines-already-matched lists locations matched in previous
+       ;; calls so we don't visit the same tag twice if it matches twice
+       ;; during two passes with different qualification predicates.
+       ;; Switch to the current tags table buffer.
+       (visit-tags-table-buffer 'same))
+ 
+       ;; Get a qualified match.
+       (catch 'qualified-match-found
+ 
+       ;; Iterate over the list of tags tables.
+       (while (or first-table
+                  (visit-tags-table-buffer t))
+ 
+         (and first-search first-table
+              ;; Start at beginning of tags file.
+              (goto-char (point-min)))
+ 
+         (setq first-table nil)
+ 
+         ;; Iterate over the list of ordering predicates.
+         (while order
+           (while (funcall search-forward-func pattern nil t)
+             ;; Naive match found.  Qualify the match.
+             (and (funcall (car order) pattern)
+                  ;; Make sure it is not a previous qualified match.
+                  (not (member (set-marker match-marker (save-excursion
+                                                          (beginning-of-line)
+                                                          (point)))
+                               tag-lines-already-matched))
+                  (throw 'qualified-match-found nil))
+             (if next-line-after-failure-p
+                 (forward-line 1)))
+           ;; Try the next flavor of match.
+           (setq order (cdr order))
+           (goto-char (point-min)))
+         (setq order tag-order))
+       ;; We throw out on match, so only get here if there were no matches.
+       ;; Clear out the markers we use to avoid duplicate matches so they
+       ;; don't slow down editting and are immediately available for GC.
+       (while tag-lines-already-matched
+         (set-marker (car tag-lines-already-matched) nil nil)
+         (setq tag-lines-already-matched (cdr tag-lines-already-matched)))
+       (set-marker match-marker nil nil)
+       (error "No %stags %s %s" (if first-search "" "more ")
+              matching pattern))
+ 
+       ;; Found a tag; extract location info.
+       (beginning-of-line)
+       (setq tag-lines-already-matched (cons match-marker
+                                           tag-lines-already-matched))
+       ;; Expand the filename, using the tags table buffer's default-directory.
+       ;; We should be able to search for file-name backwards in file-of-tag:
+       ;; the beginning-of-line is ok except when positioned on a "file-name" 
tag.
+       (setq file (expand-file-name
+                 (if (memq (car order) '(tag-exact-file-name-match-p
+                                         tag-file-name-match-p
+                                         tag-partial-file-name-match-p))
+                       (save-excursion (next-line 1)
+                                       (file-of-tag))
+                     (file-of-tag)))
+           tag-info (funcall snarf-tag-function))
+ 
+       ;; Get the local value in the tags table buffer before switching 
buffers.
+       (setq goto-func goto-tag-location-function)
+       (tag-find-file-of-tag-noselect file)
+       (widen)
+       (push-mark)
+       (funcall goto-func tag-info)
+ 
+       ;; Return the buffer where the tag was found.
+       (current-buffer))))
+ 
+ (defun tag-find-file-of-tag-noselect (file)
+   ;; Find the right line in the specified file.
+   ;; If we are interested in compressed-files,
+   ;; we search files with extensions.
+   ;; otherwise only the real file.
+   (let* ((buffer-search-extensions (if (featurep 'jka-compr)
+                                      tags-compression-info-list
+                                    '("")))
+        the-buffer
+        (file-search-extensions buffer-search-extensions))
+     ;; search a buffer visiting the file with each possible extension
+     ;; Note: there is a small inefficiency in find-buffer-visiting :
+     ;;   truename is computed even if not needed. Not too sure about this
+     ;;   but I suspect truename computation accesses the disk.
+     ;;   It is maybe a good idea to optimise this find-buffer-visiting.
+     ;; An alternative would be to use only get-file-buffer
+     ;; but this looks less "sure" to find the buffer for the file.
+     (while (and (not the-buffer) buffer-search-extensions)
+       (setq the-buffer (find-buffer-visiting (concat file (car 
buffer-search-extensions))))
+       (setq buffer-search-extensions (cdr buffer-search-extensions)))
+     ;; if found a buffer but file modified, ensure we re-read !
+     (if (and the-buffer (not (verify-visited-file-modtime the-buffer)))
+       (find-file-noselect (buffer-file-name the-buffer)))
+     ;; if no buffer found, search for files with possible extensions on disk
+     (while (and (not the-buffer) file-search-extensions)
+       (if (not (file-exists-p (concat file (car file-search-extensions))))
+         (setq file-search-extensions (cdr file-search-extensions))
+       (setq the-buffer (find-file-noselect (concat file (car 
file-search-extensions))))))
+     (if (not the-buffer)
+       (if (featurep 'jka-compr)
+           (error "File %s (with or without extensions %s) not found" file 
tags-compression-info-list)
+         (error "File %s not found" file))
+       (set-buffer the-buffer))))
+ 
+ (defun tag-find-file-of-tag (file)
+   (let ((buf (tag-find-file-of-tag-noselect file)))
+     (condition-case nil
+       (switch-to-buffer buf)
+       (error (pop-to-buffer buf)))))
+ 
+ ;; `etags' TAGS file format support.
+ 
+ ;; If the current buffer is a valid etags TAGS file, give it local values of
+ ;; the tags table format variables, and return non-nil.
+ (defun etags-recognize-tags-table ()
+   (and (etags-verify-tags-table)
+        ;; It is annoying to flash messages on the screen briefly,
+        ;; and this message is not useful.  -- rms
+        ;; (message "%s is an `etags' TAGS file" buffer-file-name)
+        (mapc (lambda (elt) (set (make-local-variable (car elt)) (cdr elt)))
+            '((file-of-tag-function . etags-file-of-tag)
+              (tags-table-files-function . etags-tags-table-files)
+              (tags-completion-table-function . etags-tags-completion-table)
+              (snarf-tag-function . etags-snarf-tag)
+              (goto-tag-location-function . etags-goto-tag-location)
+              (find-tag-regexp-search-function . re-search-forward)
+              (find-tag-regexp-tag-order . (tag-re-match-p))
+              (find-tag-regexp-next-line-after-failure-p . t)
+              (find-tag-search-function . search-forward)
+              (find-tag-tag-order . (tag-exact-file-name-match-p
+                                       tag-file-name-match-p
+                                     tag-exact-match-p
+                                     tag-implicit-name-match-p
+                                     tag-symbol-match-p
+                                     tag-word-match-p
+                                     tag-partial-file-name-match-p
+                                     tag-any-match-p))
+              (find-tag-next-line-after-failure-p . nil)
+              (list-tags-function . etags-list-tags)
+              (tags-apropos-function . etags-tags-apropos)
+              (tags-included-tables-function . etags-tags-included-tables)
+              (verify-tags-table-function . etags-verify-tags-table)
+              ))))
+ 
+ ;; Return non-nil iff the current buffer is a valid etags TAGS file.
+ (defun etags-verify-tags-table ()
+   ;; Use eq instead of = in case char-after returns nil.
+   (eq (char-after (point-min)) ?\f))
+ 
+ (defun etags-file-of-tag (&optional relative)
+   (save-excursion
+     (re-search-backward "\f\n\\([^\n]+\\),[0-9]*\n")
+     (let ((str (buffer-substring (match-beginning 1) (match-end 1))))
+       (if relative
+         str
+       (expand-file-name str
+                         (file-truename default-directory))))))
+ 
+ 
+ (defun etags-tags-completion-table ()
+   (let ((table (make-vector 511 0))
+       (point-max (/ (float (point-max)) 100.0))
+       (msg-fmt (format 
+                 "Making tags completion table for %s...%%d%%%%"
+                 buffer-file-name)))
+     (save-excursion
+       (goto-char (point-min))
+       ;; This monster regexp matches an etags tag line.
+       ;;   \1 is the string to match;
+       ;;   \2 is not interesting;
+       ;;   \3 is the guessed tag name; XXX guess should be better eg DEFUN
+       ;;   \4 is not interesting;
+       ;;   \5 is the explicitly-specified tag name.
+       ;;   \6 is the line to start searching at;
+       ;;   \7 is the char to start searching at.
+       (while (re-search-forward
+             "^\\(\\([^\177]+[^-a-zA-Z0-9_+*$:\177]+\\)?\
+ \\([-a-zA-Z0-9_+*$?:]+\\)[^-a-zA-Z0-9_+*$?:\177]*\\)\177\
+ \\(\\([^\n\001]+\\)\001\\)?\\([0-9]+\\)?,\\([0-9]+\\)?\n"
+             nil t)
+       (intern (prog1 (if (match-beginning 5)
+                          ;; There is an explicit tag name.
+                          (buffer-substring (match-beginning 5) (match-end 5))
+                        ;; No explicit tag name.  Best guess.
+                        (buffer-substring (match-beginning 3) (match-end 3)))
+                 (message msg-fmt (/ (point) point-max)))
+               table)))
+     table))
+ 
+ (defun etags-snarf-tag (&optional use-explicit)
+   (let (tag-text line startpos explicit-start)
+     (if (save-excursion
+         (forward-line -1)
+         (looking-at "\f\n"))
+       ;; The match was for a source file name, not any tag within a file.
+       ;; Give text of t, meaning to go exactly to the location we specify,
+       ;; the beginning of the file.
+       (setq tag-text t
+             line nil
+             startpos (point-min))
+ 
+       ;; Find the end of the tag and record the whole tag text.
+       (search-forward "\177")
+       (setq tag-text (buffer-substring (1- (point))
+                                      (save-excursion (beginning-of-line)
+                                                      (point))))
+       ;; If use-explicit is non nil and explicit tag is present, use it as 
part of
+       ;; return value. Else just skip it.
+       (setq explicit-start (point))
+       (when (and (search-forward "\001" (save-excursion (forward-line 1) 
(point)) t)
+                use-explicit)
+       (setq tag-text (buffer-substring explicit-start (1- (point)))))
+ 
+ 
+       (if (looking-at "[0-9]")
+         (setq line (string-to-int (buffer-substring
+                                    (point)
+                                    (progn (skip-chars-forward "0-9")
+                                           (point))))))
+       (search-forward ",")
+       (if (looking-at "[0-9]")
+         (setq startpos (string-to-int (buffer-substring
+                                        (point)
+                                        (progn (skip-chars-forward "0-9")
+                                               (point)))))))
+     ;; Leave point on the next line of the tags file.
+     (forward-line 1)
+     (cons tag-text (cons line startpos))))
+ 
+ ;; TAG-INFO is a cons (TEXT LINE . POSITION) where TEXT is the initial part
+ ;; of a line containing the tag and POSITION is the character position of
+ ;; TEXT within the file (starting from 1); LINE is the line number.  If
+ ;; TEXT is t, it means the tag refers to exactly LINE or POSITION
+ ;; (whichever is present, LINE having preference, no searching.  Either
+ ;; LINE or POSITION may be nil; POSITION is used if present.  If the tag
+ ;; isn't exactly at the given position then look around that position using
+ ;; 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))
+             (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,
+       ;; since just going around the loop once probably
+       ;; costs about as much as searching 2000 chars.
+       (setq offset 1000
+           found nil
+           pat (concat (if (eq selective-display t)
+                           "\\(^\\|\^m\\)" "^")
+                       (regexp-quote (car tag-info))))
+       ;; The character position in the tags table is 0-origin.
+       ;; Convert it to a 1-origin Emacs character position.
+       (if startpos (setq startpos (1+ startpos)))
+       ;; If no char pos was given, try the given line number.
+       (or startpos
+         (if line
+             (setq startpos (progn (goto-line line)
+                                   (point)))))
+       (or startpos
+         (setq startpos (point-min)))
+       ;; First see if the tag is right at the specified location.
+       (goto-char startpos)
+       (setq found (looking-at pat))
+       (while (and (not found)
+                 (progn
+                   (goto-char (- startpos offset))
+                   (not (bobp))))
+       (setq found
+             (re-search-forward pat (+ startpos offset) t)
+             offset (* 3 offset)))     ; expand search window
+       (or found
+         (re-search-forward pat nil t)
+         (error "Rerun etags: `%s' not found in %s"
+                pat buffer-file-name)))
+     ;; Position point at the right place
+     ;; if the search string matched an extra Ctrl-m at the beginning.
+     (and (eq selective-display t)
+        (looking-at "\^m")
+        (forward-char 1))
+     (beginning-of-line)))
+ 
+ (defun etags-list-tags (file)
+   (goto-char (point-min))
+   (when (re-search-forward (concat "\f\n" "\\(" file "\\)" ",") nil t)
+     (let ((path (save-excursion (forward-line 1) (file-of-tag)))
+         ;; Get the local value in the tags table
+         ;; buffer before switching buffers.
+         (goto-func goto-tag-location-function)
+         tag tag-info pt)
+     (forward-line 1)
+     (while (not (or (eobp) (looking-at "\f")))
+       (setq tag-info (save-excursion (funcall snarf-tag-function t))
+           tag (car tag-info)
+           pt (with-current-buffer standard-output (point)))
+       (princ tag)
+       (when (= (aref tag 0) ?\() (princ " ...)"))
+       (with-current-buffer standard-output
+       (make-text-button pt (point)
+                         'tag-info tag-info
+                         'file-path path
+                         'goto-func goto-func
+                         'action (lambda (button)
+                                   (let ((tag-info (button-get button 
'tag-info))
+                                         (goto-func (button-get button 
'goto-func)))
+                                     (tag-find-file-of-tag (button-get button 
'file-path))
+                                     (widen)
+                                     (funcall goto-func tag-info)))
+                         'face 'tags-tag-face
+                         'type 'button))
+       (terpri)
+       (forward-line 1))
+     t)))
+ 
+ (defmacro tags-with-face (face &rest body)
+   "Execute BODY, give output to `standard-output' face FACE."
+   (let ((pp (make-symbol "start")))
+     `(let ((,pp (with-current-buffer standard-output (point))))
+        ,@body
+        (put-text-property ,pp (with-current-buffer standard-output (point))
+                         'face ,face standard-output))))
+ 
+ (defun etags-tags-apropos-additional (regexp)
+   "Display tags matching REGEXP from `tags-apropos-additional-actions'."
+   (with-current-buffer standard-output
+     (dolist (oba tags-apropos-additional-actions)
+       (princ "\n\n")
+       (tags-with-face 'highlight (princ (car oba)))
+       (princ":\n\n")
+       (let* ((beg (point))
+            (symbs (car (cddr oba)))
+              (ins-symb (lambda (sy)
+                          (let ((sn (symbol-name sy)))
+                            (when (string-match regexp sn)
+                              (make-text-button (point)
+                                         (progn (princ sy) (point))
+                                         'action-internal(cadr oba)
+                                         'action (lambda (button) (funcall
+                                                                   (button-get 
button 'action-internal)
+                                                                   (button-get 
button 'item)))
+                                         'item sn
+                                         'face tags-tag-face
+                                         'type 'button)
+                              (terpri))))))
+         (when (symbolp symbs)
+           (if (boundp symbs)
+             (setq symbs (symbol-value symbs))
+           (insert "symbol `" (symbol-name symbs) "' has no value\n")
+           (setq symbs nil)))
+         (if (vectorp symbs)
+           (mapatoms ins-symb symbs)
+         (dolist (sy symbs)
+           (funcall ins-symb (car sy))))
+         (sort-lines nil beg (point))))))
+ 
+ (defun etags-tags-apropos (string)
+   (when tags-apropos-verbose
+     (princ "Tags in file `")
+     (tags-with-face 'highlight (princ buffer-file-name))
+     (princ "':\n\n"))
+   (goto-char (point-min))
+   (let ((point-max (/ (float (point-max)) 100.0)))
+     (while (re-search-forward string nil t)
+       (message "Making tags apropos buffer for `%s'...%d%%"
+              string
+              (/ (point) point-max))
+       (beginning-of-line)
+ 
+       (let* ( ;; Get the local value in the tags table
+            ;; buffer before switching buffers.
+            (goto-func goto-tag-location-function)
+            (tag-info (save-excursion (funcall snarf-tag-function)))
+            (tag (if (eq t (car tag-info)) nil (car tag-info)))
+            (file-path (save-excursion (if tag (file-of-tag)
+                                         (save-excursion (next-line 1)
+                                                         (file-of-tag)))))
+            (file-label (if tag (file-of-tag t)
+                          (save-excursion (next-line 1)
+                                          (file-of-tag t))))
+            (pt (with-current-buffer standard-output (point))))
+       (if tag
+           (progn
+             (princ (format "[%s]: " file-label))
+             (princ tag)
+             (when (= (aref tag 0) ?\() (princ " ...)"))
+             (with-current-buffer standard-output
+               (make-text-button pt (point)
+                                 'tag-info tag-info
+                                 'file-path file-path
+                                 'goto-func goto-func
+                                 'action (lambda (button)
+                                           (let ((tag-info (button-get button 
'tag-info))
+                                                 (goto-func (button-get button 
'goto-func)))
+                                             (tag-find-file-of-tag (button-get 
button 'file-path))
+                                             (widen)
+                                             (funcall goto-func tag-info)))
+                                 'face 'tags-tag-face
+                                 'type 'button)))
+         (princ (format "- %s" file-label))
+         (with-current-buffer standard-output
+           (make-text-button pt (point)
+                             'file-path file-path
+                             'action (lambda (button)
+                                       (tag-find-file-of-tag (button-get 
button 'file-path))
+                                       ;; Get the local value in the tags table
+                                       ;; buffer before switching buffers.
+                                       (goto-char (point-min)))
+                             'face 'tags-tag-face
+                             'type 'button))
+         ))
+       (terpri)
+       (forward-line 1))
+     (message nil))
+   (when tags-apropos-verbose (princ "\n")))
+ 
+ (defun etags-tags-table-files ()
+   (let ((files nil)
+       beg)
+     (goto-char (point-min))
+     (while (search-forward "\f\n" nil t)
+       (setq beg (point))
+       (end-of-line)
+       (skip-chars-backward "^," beg)
+       (or (looking-at "include$")
+         (setq files (cons (buffer-substring beg (1- (point))) files))))
+     (nreverse files)))
+ 
+ (defun etags-tags-included-tables ()
+   (let ((files nil)
+       beg)
+     (goto-char (point-min))
+     (while (search-forward "\f\n" nil t)
+       (setq beg (point))
+       (end-of-line)
+       (skip-chars-backward "^," beg)
+       (if (looking-at "include$")
+         ;; Expand in the default-directory of the tags table buffer.
+         (setq files (cons (expand-file-name (buffer-substring beg (1- 
(point))))
+                           files))))
+     (nreverse files)))
+ 
+ ;; Empty tags file support.
+ 
+ ;; Recognize an empty file and give it local values of the tags table format
+ ;; variables which do nothing.
+ (defun tags-recognize-empty-tags-table ()
+   (and (zerop (buffer-size))
+        (mapc (lambda (sym) (set (make-local-variable sym) 'ignore))
+            '(tags-table-files-function
+              tags-completion-table-function
+              find-tag-regexp-search-function
+              find-tag-search-function
+              tags-apropos-function
+              tags-included-tables-function))
+        (set (make-local-variable 'verify-tags-table-function)
+             (lambda () (zerop (buffer-size))))))
+ 
+ ;; Match qualifier functions for tagnames.
+ ;; These functions assume the etags file format defined in etc/ETAGS.EBNF.
+ 
+ ;; This might be a neat idea, but it's too hairy at the moment.
+ ;;(defmacro tags-with-syntax (&rest body)
+ ;;   `(let ((current (current-buffer))
+ ;;       (otable (syntax-table))
+ ;;       (buffer (find-file-noselect (file-of-tag)))
+ ;;       table)
+ ;;       (unwind-protect
+ ;;       (progn
+ ;;         (set-buffer buffer)
+ ;;         (setq table (syntax-table))
+ ;;         (set-buffer current)
+ ;;         (set-syntax-table table)
+ ;;            ,@body)
+ ;;       (set-syntax-table otable))))
+ ;;(put 'tags-with-syntax 'edebug-form-spec '(&rest form))
+ 
+ ;; exact file name match, i.e. searched tag must match complete file
+ ;; name including directories parts if there are some.
+ (defun tag-exact-file-name-match-p (tag)
+   (and (looking-at ",[0-9\n]")
+        (save-excursion (backward-char (+ 2 (length tag)))
+                      (looking-at "\f\n"))))
+ ;; file name match as above, but searched tag must match the file
+ ;; name not including the directories if there are some.
+ (defun tag-file-name-match-p (tag)
+   (and (looking-at ",[0-9\n]")
+        (save-excursion (backward-char (1+ (length tag)))
+                      (looking-at "/"))))
+ ;; this / to detect we are after a directory separator is ok for unix,
+ ;; is there a variable that contains the regexp for directory separator
+ ;; on whatever operating system ?
+ ;; Looks like ms-win will lose here :).
+ 
+ ;; t if point is at a tag line that matches TAG exactly.
+ ;; point should be just after a string that matches TAG.
+ (defun tag-exact-match-p (tag)
+   ;; The match is really exact if there is an explicit tag name.
+   (or (and (eq (char-after (point)) ?\001)
+          (eq (char-after (- (point) (length tag) 1)) ?\177))
+       ;; We are not on the explicit tag name, but perhaps it follows.
+       (looking-at (concat "[^\177\n]*\177" (regexp-quote tag) "\001"))))
+ 
+ ;; t if point is at a tag line that has an implicit name.
+ ;; point should be just after a string that matches TAG.
+ (defun tag-implicit-name-match-p (tag)
+   ;; Look at the comment of the make_tag function in lib-src/etags.c for
+   ;; a textual description of the four rules.
+   (and (string-match "^[^ \t()=,;]+$" tag) ;rule #1
+        (looking-at "[ \t()=,;]?\177") ;rules #2 and #4
+        (save-excursion
+        (backward-char (1+ (length tag)))
+        (looking-at "[\n \t()=,;]")))) ;rule #3
+ 
+ ;; t if point is at a tag line that matches TAG as a symbol.
+ ;; point should be just after a string that matches TAG.
+ (defun tag-symbol-match-p (tag)
+   (and (looking-at "\\Sw.*\177") (looking-at "\\S_.*\177")
+        (save-excursion
+        (backward-char (1+ (length tag)))
+        (and (looking-at "\\Sw") (looking-at "\\S_")))))
+ 
+ ;; t if point is at a tag line that matches TAG as a word.
+ ;; 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 (length tag))
+                      (looking-at "\\b"))))
+ 
+ ;; partial file name match, i.e. searched tag must match a substring
+ ;; of the file name (potentially including a directory separator).
+ (defun tag-partial-file-name-match-p (tag)
+   (and (looking-at ".*,[0-9\n]")
+        (save-excursion (beginning-of-line)
+                        (backward-char 2)
+                      (looking-at "\f\n"))))
+ 
+ ;; t if point is in a tag line with a tag containing TAG as a substring.
+ (defun tag-any-match-p (tag)
+   (looking-at ".*\177"))
+ 
+ ;; t if point is at a tag line that matches RE as a regexp.
+ (defun tag-re-match-p (re)
+   (save-excursion
+     (beginning-of-line)
+     (let ((bol (point)))
+       (and (search-forward "\177" (save-excursion (end-of-line) (point)) t)
+          (re-search-backward re bol t)))))
+ 
+ (defcustom tags-loop-revert-buffers nil
+   "*Non-nil means tags-scanning loops should offer to reread changed files.
+ These loops normally read each file into Emacs, but when a file
+ is already visited, they use the existing buffer.
+ When this flag is non-nil, they offer to revert the existing buffer
+ in the case where the file has changed since you visited it."
+   :type 'boolean
+   :group 'etags)
+ 
+ ;;;###autoload
+ (defun next-file (&optional initialize novisit)
+   "Select next file among files in current tags table.
+ 
+ A first argument of t (prefix arg, if interactive) initializes to the
+ beginning of the list of files in the tags table.  If the argument is
+ neither nil nor t, it is evalled to initialize the list of files.
+ 
+ Non-nil second argument NOVISIT means use a temporary buffer
+  to save time and avoid uninteresting warnings.
+ 
+ Value is nil if the file was already visited;
+ if the file was newly read in, the value is the filename."
+   ;; Make the interactive arg t if there was any prefix arg.
+   (interactive (list (if current-prefix-arg t)))
+   (cond ((not initialize)
+        ;; Not the first run.
+        )
+       ((eq initialize t)
+        ;; Initialize the list from the tags table.
+        (save-excursion
+          ;; Visit the tags table buffer to get its list of files.
+          (visit-tags-table-buffer)
+          ;; Copy the list so we can setcdr below, and expand the file
+          ;; names while we are at it, in this buffer's default directory.
+          (setq next-file-list (mapcar 'expand-file-name (tags-table-files)))
+          ;; Iterate over all the tags table files, collecting
+          ;; a complete list of referenced file names.
+          (while (visit-tags-table-buffer t)
+            ;; Find the tail of the working list and chain on the new
+            ;; sublist for this tags table.
+            (let ((tail next-file-list))
+              (while (cdr tail)
+                (setq tail (cdr tail)))
+              ;; Use a copy so the next loop iteration will not modify the
+              ;; list later returned by (tags-table-files).
+              (if tail
+                  (setcdr tail (mapcar 'expand-file-name (tags-table-files)))
+                (setq next-file-list (mapcar 'expand-file-name
+                                             (tags-table-files))))))))
+       (t
+        ;; Initialize the list by evalling the argument.
+        (setq next-file-list (eval initialize))))
+   (unless next-file-list
+     (and novisit
+        (get-buffer " *next-file*")
+        (kill-buffer " *next-file*"))
+     (error "All files processed"))
+   (let* ((next (car next-file-list))
+        (buffer (get-file-buffer next))
+        (new (not buffer)))
+     ;; Advance the list before trying to find the file.
+     ;; If we get an error finding the file, don't get stuck on it.
+     (setq next-file-list (cdr next-file-list))
+     ;; Optionally offer to revert buffers
+     ;; if the files have changed on disk.
+     (and buffer tags-loop-revert-buffers
+        (not (verify-visited-file-modtime buffer))
+        (with-current-buffer buffer
+          (revert-buffer t)))
+     (if (not (and new novisit))
+       (set-buffer (find-file-noselect next novisit))
+       ;; Like find-file, but avoids random warning messages.
+       (set-buffer (get-buffer-create " *next-file*"))
+       (kill-all-local-variables)
+       (erase-buffer)
+       (setq new next)
+       (insert-file-contents new nil))
+     new))
+ 
+ (defvar tags-loop-operate nil
+   "Form for `tags-loop-continue' to eval to change one file.")
+ 
+ (defvar tags-loop-scan
+   '(error "%s"
+         (substitute-command-keys
+          "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.")
+ 
+ (defun tags-loop-eval (form)
+   "Evaluate FORM and return its result.
+ Bind `case-fold-search' during the evaluation, depending on the value of
+ `tags-case-fold-search'."
+   (let ((case-fold-search (if (memq tags-case-fold-search '(t nil))
+                             tags-case-fold-search
+                           case-fold-search)))
+     (eval form)))
+ 
+ 
+ ;;;###autoload
+ (defun tags-loop-continue (&optional first-time)
+   "Continue last \\[tags-search] or \\[tags-query-replace] command.
+ Used noninteractively with non-nil argument to begin such a command (the
+ argument is passed to `next-file', which see).
+ 
+ Two variables control the processing we do on each file: the value of
+ `tags-loop-scan' is a form to be executed on each file to see if it is
+ interesting (it returns non-nil if so) and `tags-loop-operate' is a form to
+ evaluate to operate on an interesting file.  If the latter evaluates to
+ nil, we exit; otherwise we scan the next file."
+   (interactive)
+   (let (new
+       ;; Non-nil means we have finished one file
+       ;; and should not scan it again.
+       file-finished
+       original-point
+       (messaged nil))
+     (while
+       (progn
+         ;; Scan files quickly for the first or next interesting one.
+         ;; This starts at point in the current buffer.
+         (while (or first-time file-finished
+                    (save-restriction
+                      (widen)
+                      (not (tags-loop-eval tags-loop-scan))))
+           ;; If nothing was found in the previous file, and
+           ;; that file isn't in a temp buffer, restore point to
+           ;; where it was.
+           (when original-point
+             (goto-char original-point))
+ 
+           (setq file-finished nil)
+           (setq new (next-file first-time t))
+ 
+           ;; If NEW is non-nil, we got a temp buffer,
+           ;; and NEW is the file name.
+           (when (or messaged
+                     (and (not first-time)
+                          (> baud-rate search-slow-speed)
+                          (setq messaged t)))
+             (message "Scanning file %s..." (or new buffer-file-name)))
+ 
+           (setq first-time nil)
+           (setq original-point (if new nil (point)))
+           (goto-char (point-min)))
+ 
+         ;; If we visited it in a temp buffer, visit it now for real.
+         (if new
+             (let ((pos (point)))
+               (erase-buffer)
+               (set-buffer (find-file-noselect new))
+               (setq new nil)          ;No longer in a temp buffer.
+               (widen)
+               (goto-char pos))
+           (push-mark original-point t))
+ 
+         (switch-to-buffer (current-buffer))
+ 
+         ;; Now operate on the file.
+         ;; If value is non-nil, continue to scan the next file.
+         (tags-loop-eval tags-loop-operate))
+       (setq file-finished t))
+     (and messaged
+        (null tags-loop-operate)
+        (message "Scanning file %s...found" buffer-file-name))))
+ ;;;###autoload (define-key esc-map "," 'tags-loop-continue)
+ 
+ ;;;###autoload
+ (defun tags-search (regexp &optional file-list-form)
+   "Search through all files listed in tags table for match for REGEXP.
+ Stops when a match is found.
+ To continue searching for next match, use command \\[tags-loop-continue].
+ 
+ See documentation of variable `tags-file-name'."
+   (interactive "sTags search (regexp): ")
+   (if (and (equal regexp "")
+          (eq (car tags-loop-scan) 're-search-forward)
+          (null tags-loop-operate))
+       ;; Continue last tags-search as if by M-,.
+       (tags-loop-continue nil)
+     (setq tags-loop-scan `(re-search-forward ',regexp nil t)
+         tags-loop-operate nil)
+     (tags-loop-continue (or file-list-form t))))
+ 
+ ;;;###autoload
+ (defun tags-query-replace (from to &optional delimited file-list-form start 
end)
+   "Do `query-replace-regexp' of FROM with TO on all files listed in tags 
table.
+ Third arg DELIMITED (prefix arg) means replace only word-delimited matches.
+ If you exit (\\[keyboard-quit], RET or q), you can resume the query replace
+ with the command \\[tags-loop-continue].
+ 
+ See documentation of variable `tags-file-name'."
+   (interactive (query-replace-read-args "Tags query replace (regexp)" t t))
+   (setq tags-loop-scan `(let ,(unless (equal from (downcase from))
+                               '((case-fold-search nil)))
+                         (if (re-search-forward ',from nil t)
+                             ;; When we find a match, move back
+                             ;; to the beginning of it so perform-replace
+                             ;; will see it.
+                             (goto-char (match-beginning 0))))
+       tags-loop-operate `(perform-replace ',from ',to t t ',delimited))
+   (tags-loop-continue (or file-list-form t)))
+ 
+ (defun tags-complete-tags-table-file (string predicate what)
+   (save-excursion
+     ;; If we need to ask for the tag table, allow that.
+     (let ((enable-recursive-minibuffers t))
+       (visit-tags-table-buffer))
+     (if (eq what t)
+       (all-completions string (tags-table-files) predicate)
+       (try-completion string (tags-table-files) predicate))))
+ 
+ ;;;###autoload
+ (defun list-tags (file &optional next-match)
+   "Display list of tags in file FILE.
+ This searches only the first table in the list, and no included tables.
+ FILE should be as it appeared in the `etags' command, usually without a
+ directory specification."
+   (interactive (list (completing-read "List tags in file: "
+                                     'tags-complete-tags-table-file
+                                     nil t nil)))
+   (with-output-to-temp-buffer "*Tags List*"
+     (princ "Tags in file `")
+     (tags-with-face 'highlight (princ file))
+     (princ "':\n\n")
+     (save-excursion
+       (let ((first-time t)
+           (gotany nil))
+       (while (visit-tags-table-buffer (not first-time))
+         (setq first-time nil)
+         (if (funcall list-tags-function file)
+             (setq gotany t)))
+       (or gotany
+           (error "File %s not in current tags tables" file)))))
+   (with-current-buffer "*Tags List*"
+     (require 'apropos)
+     (apropos-mode)
+     (setq buffer-read-only t)))
+ 
+ ;;;###autoload
+ (defun tags-apropos (regexp)
+   "Display list of all tags in tags table REGEXP matches."
+   (interactive "sTags apropos (regexp): ")
+   (with-output-to-temp-buffer "*Tags List*"
+     (princ "Click mouse-2 to follow tags.\n\nTags matching regexp `")
+     (tags-with-face 'highlight (princ regexp))
+     (princ "':\n\n")
+     (save-excursion
+       (let ((first-time t))
+       (while (visit-tags-table-buffer (not first-time))
+         (setq first-time nil)
+         (funcall tags-apropos-function regexp))))
+     (etags-tags-apropos-additional regexp))
+   (with-current-buffer "*Tags List*"
+     (require 'apropos)
+     (apropos-mode)
+     ;; apropos-mode is derived from fundamental-mode and it kills
+     ;; all local variables.
+     (setq buffer-read-only t)))
+ 
+ ;; XXX Kludge interface.
+ 
+ (define-button-type 'tags-select-tags-table
+   'action (lambda (button) (select-tags-table-select))
+   'help-echo "RET, t or mouse-2: select tags table")
+ 
+ ;; XXX If a file is in multiple tables, selection may get the wrong one.
+ ;;;###autoload
+ (defun select-tags-table ()
+   "Select a tags table file from a menu of those you have already used.
+ The list of tags tables to select from is stored in `tags-table-set-list';
+ see the doc of that variable if you want to add names to the list."
+   (interactive)
+   (pop-to-buffer "*Tags Table List*")
+   (setq buffer-read-only nil)
+   (erase-buffer)
+   (let ((set-list tags-table-set-list)
+       (desired-point nil)
+       b)
+     (when tags-table-list
+         (setq desired-point (point-marker))
+         (setq b (point))
+         (princ tags-table-list (current-buffer))
+         (make-text-button b (point) 'type 'tags-select-tags-table)
+         (insert "\C-m")
+         (prin1 (car tags-table-list) (current-buffer)) ;invisible
+       (insert "\n"))
+     (while set-list
+       (unless (eq (car set-list) tags-table-list)
+       (setq b (point))
+       (princ (car set-list) (current-buffer))
+       (make-text-button b (point) 'type 'tags-select-tags-table)
+       (insert "\C-m")
+       (prin1 (car (car set-list)) (current-buffer)) ;invisible
+       (insert "\n"))
+       (setq set-list (cdr set-list)))
+     (when tags-file-name
+         (or desired-point
+             (setq desired-point (point-marker)))
+         (setq b (point))
+         (insert tags-file-name)
+         (make-text-button b (point) 'type 'tags-select-tags-table)
+         (insert "\C-m")
+         (prin1 tags-file-name (current-buffer)) ;invisible
+       (insert "\n"))
+     (setq set-list (delete tags-file-name
+                          (apply 'nconc (cons (copy-sequence tags-table-list)
+                                              (mapcar 'copy-sequence
+                                                      tags-table-set-list)))))
+     (while set-list
+       (setq b (point))
+       (insert (car set-list))
+       (make-text-button b (point) 'type 'tags-select-tags-table)
+       (insert "\C-m")
+       (prin1 (car set-list) (current-buffer)) ;invisible
+       (insert "\n")
+       (setq set-list (delete (car set-list) set-list)))
+     (goto-char (point-min))
+     (insert-before-markers
+      "Type `t' to select a tags table or set of tags tables:\n\n")
+     (if desired-point
+       (goto-char desired-point))
+     (set-window-start (selected-window) 1 t))
+   (set-buffer-modified-p nil)
+   (select-tags-table-mode))
+ 
+ (defvar select-tags-table-mode-map
+   (let ((map (copy-keymap button-buffer-map)))
+     (define-key map "t" 'push-button)
+     (define-key map " " 'next-line)
+     (define-key map "\^?" 'previous-line)
+     (define-key map "n" 'next-line)
+     (define-key map "p" 'previous-line)
+     (define-key map "q" 'select-tags-table-quit)
+     map))
+ 
+ (defun select-tags-table-mode ()
+   "Major mode for choosing a current tags table among those already loaded.
+ 
+ \\{select-tags-table-mode-map}"
+   (interactive)
+   (kill-all-local-variables)
+   (setq buffer-read-only t
+       major-mode 'select-tags-table-mode
+       mode-name "Select Tags Table")
+   (use-local-map select-tags-table-mode-map)
+   (setq selective-display t
+       selective-display-ellipses nil))
+ 
+ (defun select-tags-table-select ()
+   "Select the tags table named on this line."
+   (interactive)
+   (search-forward "\C-m")
+   (let ((name (read (current-buffer))))
+     (visit-tags-table name)
+     (select-tags-table-quit)
+     (message "Tags table now %s" name)))
+ 
+ (defun select-tags-table-quit ()
+   "Kill the buffer and delete the selected window."
+   (interactive)
+   (quit-window t (selected-window)))
+ 
+ ;; Note, there is another definition of this function in bindings.el.
+ ;;;###autoload
+ (defun complete-tag ()
+   "Perform tags completion on the text around point.
+ Completes to the set of names listed in the current tags table.
+ The string to complete is chosen in the same way as the default
+ for \\[find-tag] (which see)."
+   (interactive)
+   (or tags-table-list
+       tags-file-name
+       (error "%s"
+            (substitute-command-keys
+             "No tags table loaded; try \\[visit-tags-table]")))
+   (let ((completion-ignore-case (if (memq tags-case-fold-search '(t nil))
+                                   tags-case-fold-search
+                                 case-fold-search))
+       (pattern (funcall (or find-tag-default-function
+                             (get major-mode 'find-tag-default-function)
+                             'find-tag-default)))
+       beg
+       completion)
+     (or pattern
+       (error "Nothing to complete"))
+     (search-backward pattern)
+     (setq beg (point))
+     (forward-char (length pattern))
+     (setq completion (tags-complete-tag pattern nil nil))
+     (cond ((eq completion t))
+         ((null completion)
+          (message "Can't find completion for \"%s\"" pattern)
+          (ding))
+         ((not (string= pattern completion))
+          (delete-region beg (point))
+          (insert completion))
+         (t
+          (message "Making completion list...")
+          (with-output-to-temp-buffer "*Completions*"
+            (display-completion-list
+             (all-completions pattern 'tags-complete-tag nil)))
+          (message "Making completion list...%s" "done")))))
+ 
+ (dolist (x '("^No tags table in use; use .* to select one$"
+            "^There is no default tag$"
+            "^No previous tag locations$"
+            "^File .* is not a valid tags table$"
+            "^No \\(more \\|\\)tags \\(matching\\|containing\\) "
+            "^Rerun etags: `.*' not found in "
+            "^All files processed$"
+            "^No .* or .* in progress$"
+            "^File .* not in current tags tables$"
+            "^No tags table loaded"
+            "^Nothing to complete$"))
+       (add-to-list 'debug-ignored-errors x))
+ 
+ (provide 'etags)
+ 
+ ;;; arch-tag: b897c2b5-08f3-4837-b2d3-0e7d6db1b63e
+ ;;; etags.el ends here




reply via email to

[Prev in Thread] Current Thread [Next in Thread]