]> code.delx.au - gnu-emacs/blobdiff - lisp/progmodes/etags.el
Update copyright notice.
[gnu-emacs] / lisp / progmodes / etags.el
index 154217b6e8e1c54ce8fdc0cc70bda418dc19fa70..ed9959a1c02bc106c79b0dfc4c9c1e20ace36848 100644 (file)
@@ -1,8 +1,10 @@
 ;;; etags.el --- etags facility for Emacs
-;; Copyright (C) 1985, 86, 88, 89, 92, 93, 94, 95, 96, 98, 2000
+
+;; Copyright (C) 1985, 86, 88, 89, 92, 93, 94, 95, 96, 98, 2000, 2001
 ;;     Free Software Foundation, Inc.
 
 ;; Author: Roland McGrath <roland@gnu.org>
+;; Maintainer: FSF
 ;; Keywords: tools
 
 ;; This file is part of GNU Emacs.
 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 ;; Boston, MA 02111-1307, USA.
 
+;;; Commentary:
+
 ;;; Code:
 
 (require 'ring)
-(eval-when-compile (require 'cl)) ; for `gensym'
 
 ;;;###autoload
 (defvar tags-file-name nil
@@ -61,6 +64,26 @@ 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.
@@ -81,7 +104,7 @@ to the current list (as opposed to starting a new list)."
   "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
+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'.")
@@ -117,7 +140,7 @@ 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 'function)
+  :type '(choice (const nil) function))
 
 (defcustom find-tag-marker-ring-length 16
   "*Length of marker rings `find-tag-marker-ring' and `tags-location-ring'."
@@ -144,7 +167,7 @@ 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.
+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.
@@ -155,7 +178,9 @@ Example value:
     (\"Common Lisp\" common-lisp-hyperspec common-lisp-hyperspec-obarray)
     (\"SCWM\" scwm-documentation scwm-obarray))"
   :group 'etags
-  :type 'list
+  :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)
@@ -188,9 +213,9 @@ nil means it has not yet been computed; use `tags-table-files' to do so.")
 \f
 ;; Hooks for file formats.
 
-(defvar tags-table-format-hooks '(etags-recognize-tags-table
-                                 tags-recognize-empty-tags-table)
-  "List of functions to be called in a tags table buffer to identify the type of tags table.  
+(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.")
@@ -200,7 +225,7 @@ of the format-parsing tags function variables if successful.")
 (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.")
+  "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'.")
 (defvar goto-tag-location-function nil
@@ -240,11 +265,7 @@ One argument, the tag info returned by `snarf-tag-function'.")
   ;; are global.
 
   ;; Value is t if we have found a valid tags table buffer.
-  (let ((hooks tags-table-format-hooks))
-    (while (and hooks
-               (not (funcall (car hooks))))
-      (setq hooks (cdr hooks)))
-    hooks))
+  (run-hook-with-args-until-success 'tags-table-format-functions))
 
 ;;;###autoload
 (defun visit-tags-table (file &optional local)
@@ -635,9 +656,11 @@ Returns t if it visits a tags table, or nil if there are no more in the list."
                                          tags-table-set-list)))
                          ;; Clear out buffers holding old tables.
                          (dolist (table tags-table-list)
-                           (let ((buffer (find-buffer-visiting table)))
+                           ;; The list can contain items `t'.
+                           (if (stringp table)
+                               (let ((buffer (find-buffer-visiting table)))
                              (if buffer
-                                 (kill-buffer buffer))))
+                                 (kill-buffer buffer)))))
                          (setq tags-table-list (list local-tags-file-name))))
 
                      ;; Recompute tags-table-computed-list.
@@ -779,7 +802,7 @@ Assumes the tags table is the current buffer."
 
 ;; Get interactive args for find-tag{-noselect,-other-window,-regexp}.
 (defun find-tag-interactive (prompt &optional no-default)
-  (if current-prefix-arg
+  (if (and current-prefix-arg last-tag)
       (list nil (if (< (prefix-numeric-value current-prefix-arg) 0)
                    '-
                  t))
@@ -809,7 +832,7 @@ or just \\[negative-argument]), pop back to the previous tag gone to.
 
 If third arg REGEXP-P is non-nil, treat TAGNAME as a regexp.
 
-A marker representing the point when this command is onvoked is pushed
+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.
 
@@ -817,8 +840,9 @@ 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.
+  ;; 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.
@@ -836,7 +860,7 @@ See documentation of variable `tags-file-name'."
              (run-hooks 'local-find-tag-hook))))
       ;; Record whence we came.
       (ring-insert find-tag-marker-ring (point-marker))
-      (if next-p
+      (if (and next-p last-tag)
          ;; Find the same table we last used.
          (visit-tags-table-buffer 'same)
        ;; Pick a table to use.
@@ -849,7 +873,7 @@ See documentation of variable `tags-file-name'."
          (set-buffer
           ;; find-tag-in-order does the real work.
           (find-tag-in-order
-           (if next-p last-tag tagname)
+           (if (and next-p last-tag) last-tag tagname)
            (if regexp-p
                find-tag-regexp-search-function
              find-tag-search-function)
@@ -860,7 +884,7 @@ See documentation of variable `tags-file-name'."
                find-tag-regexp-next-line-after-failure-p
              find-tag-next-line-after-failure-p)
            (if regexp-p "matching" "containing")
-           (not next-p)))
+           (or (not next-p) (not last-tag))))
          (set-marker marker (point))
          (run-hooks 'local-find-tag-hook)
          (ring-insert tags-location-ring marker)
@@ -880,13 +904,16 @@ or just \\[negative-argument]), pop back to the previous tag gone to.
 
 If third arg REGEXP-P is non-nil, treat TAGNAME as a regexp.
 
-A marker representing the point when this command is onvoked is pushed
+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: "))
-  (switch-to-buffer (find-tag-noselect tagname next-p regexp-p)))
+  (let ((buf (find-tag-noselect tagname next-p regexp-p)))
+    (condition-case nil
+       (switch-to-buffer buf)
+      (error (pop-to-buffer buf)))))
 ;;;###autoload (define-key esc-map "." 'find-tag)
 
 ;;;###autoload
@@ -904,7 +931,7 @@ just \\[negative-argument]), pop back to the previous tag gone to.
 
 If third arg REGEXP-P is non-nil, treat TAGNAME as a regexp.
 
-A marker representing the point when this command is onvoked is pushed
+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.
 
@@ -945,7 +972,7 @@ just \\[negative-argument]), pop back to the previous tag gone to.
 
 If third arg REGEXP-P is non-nil, treat TAGNAME as a regexp.
 
-A marker representing the point when this command is onvoked is pushed
+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.
 
@@ -968,7 +995,7 @@ just \\[negative-argument]), pop back to the previous tag gone to.
 
 If third arg OTHER-WINDOW is non-nil, select the buffer in another window.
 
-A marker representing the point when this command is onvoked is pushed
+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.
 
@@ -1003,12 +1030,12 @@ where they were found."
 ;; any member of the function list ORDER (third arg).  If ORDER is nil,
 ;; use saved state to continue a previous search.
 
-;; Fourth arg MATCHING is a string, an English '-ing' word, to be used in
-;; an error message.
-
-;; Fifth arg NEXT-LINE-AFTER-FAILURE-P is non-nil if after a failed match,
+;; 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
@@ -1094,14 +1121,52 @@ where they were found."
       (setq tag-lines-already-matched (cons match-marker
                                            tag-lines-already-matched))
       ;; Expand the filename, using the tags table buffer's default-directory.
-      (setq file (expand-file-name (file-of-tag))
+      ;; 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)
 
       ;; Find the right line in the specified file.
-      (set-buffer (find-file-noselect 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)))
       (widen)
       (push-mark)
       (funcall goto-func tag-info)
@@ -1129,9 +1194,12 @@ where they were found."
               (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)
@@ -1143,7 +1211,7 @@ where they were found."
 ;; 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 1) ?\f))
+  (eq (char-after (point-min)) ?\f))
 
 (defun etags-file-of-tag ()
   (save-excursion
@@ -1187,7 +1255,7 @@ where they were found."
        ;; the beginning of the file.
        (setq tag-text t
              line nil
-             startpos 1)
+             startpos (point-min))
 
       ;; Find the end of the tag and record the whole tag text.
       (search-forward "\177")
@@ -1269,7 +1337,7 @@ where they were found."
     (beginning-of-line)))
 
 (defun etags-list-tags (file)
-  (goto-char 1)
+  (goto-char (point-min))
   (when (search-forward (concat "\f\n" file ",") nil t)
     (forward-line 1)
     (while (not (or (eobp) (looking-at "\f")))
@@ -1294,7 +1362,7 @@ where they were found."
 
 (defmacro tags-with-face (face &rest body)
   "Execute BODY, give output to `standard-output' face FACE."
-  (let ((pp (gensym "twf-")))
+  (let ((pp (make-symbol "start")))
     `(let ((,pp (with-current-buffer standard-output (point))))
        ,@body
        (put-text-property ,pp (with-current-buffer standard-output (point))
@@ -1334,7 +1402,7 @@ where they were found."
     (princ "Tags in file `")
     (tags-with-face 'highlight (princ buffer-file-name))
     (princ "':\n\n"))
-  (goto-char 1)
+  (goto-char (point-min))
   (while (re-search-forward string nil t)
     (beginning-of-line)
     (let ((tag (buffer-substring (point)
@@ -1394,7 +1462,7 @@ where they were found."
             (lambda () (zerop (buffer-size))))))
 \f
 ;; Match qualifier functions for tagnames.
-;; XXX these functions assume etags file format.
+;; 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)
@@ -1412,6 +1480,23 @@ where they were found."
 ;;       (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)
@@ -1421,6 +1506,17 @@ where they were found."
       ;; 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)
@@ -1436,10 +1532,13 @@ where they were found."
        (save-excursion (backward-char (length tag))
                       (looking-at "\\b"))))
 
-(defun tag-exact-file-name-match-p (tag)
-  (and (looking-at ",")
-       (save-excursion (backward-char (length tag))
-                      (looking-at "\f\n"))))
+;; 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)
@@ -1551,7 +1650,7 @@ Bind `case-fold-search' during the evaluation, depending on the value of
                              tags-case-fold-search
                            case-fold-search)))
     (eval form)))
-  
+
 
 ;;;###autoload
 (defun tags-loop-continue (&optional first-time)
@@ -1569,24 +1668,35 @@ nil, we exit; otherwise we scan the next file."
        ;; 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.
-           (if (or messaged
-                   (and (not first-time)
-                        (> baud-rate search-slow-speed)
-                        (setq messaged t)))
-               (message "Scanning file %s..." (or new buffer-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.
@@ -1596,7 +1706,8 @@ nil, we exit; otherwise we scan the next file."
                (set-buffer (find-file-noselect new))
                (setq new nil)          ;No longer in a temp buffer.
                (widen)
-               (goto-char pos)))
+               (goto-char pos))
+           (push-mark original-point t))
 
          (switch-to-buffer (current-buffer))
 
@@ -1622,30 +1733,27 @@ See documentation of variable `tags-file-name'."
           (null tags-loop-operate))
       ;; Continue last tags-search as if by M-,.
       (tags-loop-continue nil)
-    (setq tags-loop-scan
-         (list 're-search-forward (list 'quote regexp) nil t)
+    (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 start end file-list-form)
-  "Query-replace-regexp FROM with TO through all files listed in tags table.
+(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] or ESC), you can resume the query-replace
+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))
-  (setq tags-loop-scan (list 'prog1
-                            (list 'if (list 're-search-forward
-                                            (list 'quote 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 (list 'perform-replace
-                               (list 'quote from) (list 'quote to) nil nil
-                               t t (list 'quote delimited)))
+  (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)))
 \f
 (defun tags-complete-tags-table-file (string predicate what)
@@ -1654,10 +1762,8 @@ See documentation of variable `tags-file-name'."
     (let ((enable-recursive-minibuffers t))
       (visit-tags-table-buffer))
     (if (eq what t)
-       (all-completions string (mapcar 'list (tags-table-files))
-                        predicate)
-      (try-completion string (mapcar 'list (tags-table-files))
-                     predicate))))
+       (all-completions string (tags-table-files) predicate)
+      (try-completion string (tags-table-files) predicate))))
 
 ;;;###autoload
 (defun list-tags (file &optional next-match)
@@ -1703,7 +1809,7 @@ directory specification."
     (setq buffer-read-only t)
     (apropos-mode)))
 \f
-;;; XXX Kludge interface.
+;; XXX Kludge interface.
 
 ;; XXX If a file is in multiple tables, selection may get the wrong one.
 ;;;###autoload
@@ -1745,7 +1851,7 @@ see the doc of that variable if you want to add names to the list."
       (prin1 (car set-list) (current-buffer)) ;invisible
       (insert "\n")
       (setq set-list (delete (car set-list) set-list)))
-    (goto-char 1)
+    (goto-char (point-min))
     (insert-before-markers
      "Type `t' to select a tags table or set of tags tables:\n\n")
     (if desired-point
@@ -1791,7 +1897,7 @@ see the doc of that variable if you want to add names to the list."
   (interactive)
   (quit-window t (selected-window)))
 \f
-;;; Note, there is another definition of this function in bindings.el.
+;; Note, there is another definition of this function in bindings.el.
 ;;;###autoload
 (defun complete-tag ()
   "Perform tags completion on the text around point.