diff lisp/progmodes/etags.el @ 88155:d7ddb3e565de

sync with trunk
author Henrik Enberg <henrik.enberg@telia.com>
date Mon, 16 Jan 2006 00:03:54 +0000
parents ee509ddc7a03
children
line wrap: on
line diff
--- a/lisp/progmodes/etags.el	Sun Jan 15 23:02:10 2006 +0000
+++ b/lisp/progmodes/etags.el	Mon Jan 16 00:03:54 2006 +0000
@@ -1,6 +1,7 @@
 ;;; etags.el --- etags facility for Emacs
 
-;; Copyright (C) 1985, 86, 88, 89, 92, 93, 94, 95, 96, 98, 2000, 2001
+;; Copyright (C) 1985, 1986, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1998,
+;;               2000, 2001, 2002, 2003, 2004, 2005
 ;;	Free Software Foundation, Inc.
 
 ;; Author: Roland McGrath <roland@gnu.org>
@@ -21,14 +22,15 @@
 
 ;; 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.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
 
 ;;; Commentary:
 
 ;;; Code:
 
 (require 'ring)
+(require 'button)
 
 ;;;###autoload
 (defvar tags-file-name nil
@@ -39,7 +41,7 @@
 ;; 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"
+(defgroup etags nil "Tags tables."
   :group 'tools)
 
 ;;;###autoload
@@ -221,13 +223,17 @@
 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).")
+  "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'.")
+  "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'.")
@@ -268,6 +274,14 @@
   (run-hook-with-args-until-success 'tags-table-format-functions))
 
 ;;;###autoload
+(defun tags-table-mode ()
+  "Major mode for tags table file buffers."
+  (interactive)
+  (setq major-mode 'tags-table-mode)
+  (setq mode-name "Tags Table")
+  (initialize-new-tags-table))
+
+;;;###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.
@@ -278,7 +292,7 @@
 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) "
+  (interactive (list (read-file-name "Visit tags table (default TAGS): "
 				     default-directory
 				     (expand-file-name "TAGS"
 						       default-directory)
@@ -409,7 +423,7 @@
       ;; 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)))
+	(setq win (or verify-tags-table-function (tags-table-mode)))
 	(if (or (verify-visited-file-modtime (current-buffer))
 		;; Decide whether to revert the file.
 		;; revert-without-query can say to revert
@@ -428,7 +442,7 @@
 	    (and verify-tags-table-function
 		 (funcall verify-tags-table-function))
 	  (revert-buffer t t)
-	  (initialize-new-tags-table)))
+	  (tags-table-mode)))
     (and (file-exists-p file)
 	 (progn
 	   (set-buffer (find-file-noselect file))
@@ -440,7 +454,7 @@
 		     (setcar tail buffer-file-name))
 		 (if (eq file tags-file-name)
 		     (setq tags-file-name buffer-file-name))))
-	   (initialize-new-tags-table)))))
+	   (tags-table-mode)))))
 
 ;; 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
@@ -513,6 +527,7 @@
     ;; 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.
@@ -583,7 +598,7 @@
 		  (car list))
 		;; Finally, prompt the user for a file name.
 		(expand-file-name
-		 (read-file-name "Visit tags table: (default TAGS) "
+		 (read-file-name "Visit tags table (default TAGS): "
 				 default-directory
 				 "TAGS"
 				 t))))))
@@ -702,11 +717,13 @@
 	tags-table-list-started-at nil
 	tags-table-set-list nil))
 
-(defun file-of-tag ()
+(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.
-File name returned is relative to tags table file's directory."
-  (funcall file-of-tag-function))
+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 ()
@@ -764,26 +781,6 @@
 	(all-completions string (tags-completion-table) predicate)
       (try-completion string (tags-completion-table) predicate))))
 
-;; Return a default tag to search for, based on the text at point.
-(defun find-tag-default ()
-  (save-excursion
-    (while (looking-at "\\sw\\|\\s_")
-      (forward-char 1))
-    (if (or (re-search-backward "\\sw\\|\\s_"
-				(save-excursion (beginning-of-line) (point))
-				t)
-	    (re-search-forward "\\(\\sw\\|\\s_\\)+"
-			       (save-excursion (end-of-line) (point))
-			       t))
-	(progn (goto-char (match-end 0))
-	       (buffer-substring-no-properties
-                (point)
-                (progn (forward-sexp -1)
-                       (while (looking-at "\\s'")
-                         (forward-char 1))
-                       (point))))
-      nil)))
-
 ;; 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))
@@ -1142,45 +1139,53 @@
 
       ;; 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.
-      ;; 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)))
+      (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.
 
@@ -1221,15 +1226,22 @@
   ;; Use eq instead of = in case char-after returns nil.
   (eq (char-after (point-min)) ?\f))
 
-(defun etags-file-of-tag ()
+(defun etags-file-of-tag (&optional relative)
   (save-excursion
     (re-search-backward "\f\n\\([^\n]+\\),[0-9]*\n")
-    (expand-file-name (buffer-substring (match-beginning 1) (match-end 1))
-		      (file-truename default-directory))))
+    (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)))
+  (let ((table (make-vector 511 0))
+	(progress-reporter
+	 (make-progress-reporter
+	  (format "Making tags completion table for %s..." buffer-file-name)
+	  (point-min) (point-max))))
     (save-excursion
       (goto-char (point-min))
       ;; This monster regexp matches an etags tag line.
@@ -1245,16 +1257,17 @@
 \\([-a-zA-Z0-9_+*$?:]+\\)[^-a-zA-Z0-9_+*$?:\177]*\\)\177\
 \\(\\([^\n\001]+\\)\001\\)?\\([0-9]+\\)?,\\([0-9]+\\)?\n"
 	      nil t)
-	(intern	(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)))
+	(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)))
+		  (progress-reporter-update progress-reporter (point)))
 		table)))
     table))
 
-(defun etags-snarf-tag ()
-  (let (tag-text line startpos)
+(defun etags-snarf-tag (&optional use-explicit)
+  (let (tag-text line startpos explicit-start)
     (if (save-excursion
 	  (forward-line -1)
 	  (looking-at "\f\n"))
@@ -1270,19 +1283,25 @@
       (setq tag-text (buffer-substring (1- (point))
 				       (save-excursion (beginning-of-line)
 						       (point))))
-      ;; Skip explicit tag name if present.
-      (search-forward "\001" (save-excursion (forward-line 1) (point)) t)
+      ;; 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))))))
+	  (setq line (string-to-number (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)))))))
+	  (setq startpos (string-to-number (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))))
@@ -1346,27 +1365,35 @@
 
 (defun etags-list-tags (file)
   (goto-char (point-min))
-  (when (search-forward (concat "\f\n" file ",") nil t)
+  (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")))
-      (let ((tag (buffer-substring (point)
-				   (progn (skip-chars-forward "^\177")
-					  (point))))
-            (props `(action find-tag-other-window mouse-face highlight
-			    face ,tags-tag-face))
-            (pt (with-current-buffer standard-output (point))))
-        (when (looking-at "[^\n]+\001")
-	  ;; There is an explicit tag name; use that.
-          (setq tag (buffer-substring (1+ (point)) ; skip \177
-				      (progn (skip-chars-forward "^\001")
-                                             (point)))))
-        (princ tag)
-        (when (= (aref tag 0) ?\() (princ " ...)"))
-        (add-text-properties pt (with-current-buffer standard-output (point))
-                             (cons 'item (cons tag props)) standard-output))
+      (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))
+    t)))
 
 (defmacro tags-with-face (face &rest body)
   "Execute BODY, give output to `standard-output' face FACE."
@@ -1383,16 +1410,20 @@
       (princ "\n\n")
       (tags-with-face 'highlight (princ (car oba)))
       (princ":\n\n")
-      (let* ((props `(action ,(cadr oba) mouse-face highlight face
-			     ,tags-tag-face))
-             (beg (point))
+      (let* ((beg (point))
 	     (symbs (car (cddr oba)))
              (ins-symb (lambda (sy)
                          (let ((sn (symbol-name sy)))
                            (when (string-match regexp sn)
-                             (add-text-properties (point)
-						  (progn (princ sy) (point))
-						  (cons 'item (cons sn props)))
+                             (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)
@@ -1411,20 +1442,59 @@
     (tags-with-face 'highlight (princ buffer-file-name))
     (princ "':\n\n"))
   (goto-char (point-min))
-  (while (re-search-forward string nil t)
-    (beginning-of-line)
-    (let ((tag (buffer-substring (point)
-				 (progn (skip-chars-forward "^\177")
-					(point))))
-          (props `(action find-tag-other-window mouse-face highlight
-			  face ,tags-tag-face))
-          (pt (with-current-buffer standard-output (point))))
-      (princ tag)
-      (when (= (aref tag 0) ?\() (princ " ...)"))
-      (add-text-properties pt (with-current-buffer standard-output (point))
-                           `(item ,tag ,@props) standard-output))
-    (terpri)
-    (forward-line 1))
+  (let ((progress-reporter (make-progress-reporter
+			    (format "Making tags apropos buffer for `%s'..."
+				    string)
+			    (point-min) (point-max))))
+    (while (re-search-forward string nil t)
+      (progress-reporter-update progress-reporter (point))
+      (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 ()
@@ -1796,8 +1866,10 @@
 	(or gotany
 	    (error "File %s not in current tags tables" file)))))
   (with-current-buffer "*Tags List*"
-    (setq buffer-read-only t)
-    (apropos-mode)))
+    (require 'apropos)
+    (with-no-warnings
+      (apropos-mode))
+    (setq buffer-read-only t)))
 
 ;;;###autoload
 (defun tags-apropos (regexp)
@@ -1814,11 +1886,18 @@
 	  (funcall tags-apropos-function regexp))))
     (etags-tags-apropos-additional regexp))
   (with-current-buffer "*Tags List*"
-    (setq buffer-read-only t)
-    (apropos-mode)))
+    (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 '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 ()
@@ -1830,33 +1909,40 @@
   (setq buffer-read-only nil)
   (erase-buffer)
   (let ((set-list tags-table-set-list)
-	(desired-point nil))
+	(desired-point nil)
+	b)
     (when tags-table-list
-	  (setq desired-point (point-marker))
-	  (princ tags-table-list (current-buffer))
-	  (insert "\C-m")
-	  (prin1 (car tags-table-list) (current-buffer)) ;invisible
+      (setq desired-point (point-marker))
+      (setq b (point))
+      (princ (mapcar 'abbreviate-file-name tags-table-list) (current-buffer))
+      (make-text-button b (point) 'type 'tags-select-tags-table
+                        'etags-table (car tags-table-list))
       (insert "\n"))
     (while set-list
       (unless (eq (car set-list) tags-table-list)
-	(princ (car set-list) (current-buffer))
-	(insert "\C-m")
-	(prin1 (car (car set-list)) (current-buffer)) ;invisible
+	(setq b (point))
+	(princ (mapcar 'abbreviate-file-name (car set-list)) (current-buffer))
+	(make-text-button b (point) 'type 'tags-select-tags-table
+                          'etags-table (car (car set-list)))
 	(insert "\n"))
       (setq set-list (cdr set-list)))
     (when tags-file-name
-	  (or desired-point
-	      (setq desired-point (point-marker)))
-	  (insert tags-file-name "\C-m")
-	  (prin1 tags-file-name (current-buffer)) ;invisible
+      (or desired-point
+          (setq desired-point (point-marker)))
+      (setq b (point))
+      (insert (abbreviate-file-name tags-file-name))
+      (make-text-button b (point) 'type 'tags-select-tags-table
+                        'etags-table tags-file-name)
       (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
-      (insert (car set-list) "\C-m")
-      (prin1 (car set-list) (current-buffer)) ;invisible
+      (setq b (point))
+      (insert (abbreviate-file-name (car set-list)))
+      (make-text-button b (point) 'type 'tags-select-tags-table
+                          'etags-table (car set-list))
       (insert "\n")
       (setq set-list (delete (car set-list) set-list)))
     (goto-char (point-min))
@@ -1868,34 +1954,28 @@
   (set-buffer-modified-p nil)
   (select-tags-table-mode))
 
-(defvar select-tags-table-mode-map)
-(let ((map (make-sparse-keymap)))
-  (define-key map "t" 'select-tags-table-select)
-  (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)
-  (setq select-tags-table-mode-map map))
+(defvar select-tags-table-mode-map
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map 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 ()
+(define-derived-mode select-tags-table-mode fundamental-mode "Select Tags Table"
   "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))
+  (setq buffer-read-only t))
 
-(defun select-tags-table-select ()
+(defun select-tags-table-select (button)
   "Select the tags table named on this line."
-  (interactive)
-  (search-forward "\C-m")
-  (let ((name (read (current-buffer))))
+  (interactive (list (or (button-at (line-beginning-position))
+                         (error "No tags table on current line"))))
+  (let ((name (button-get button 'etags-table)))
     (visit-tags-table name)
     (select-tags-table-quit)
     (message "Tags table now %s" name)))
@@ -1943,7 +2023,8 @@
 	   (message "Making completion list...")
 	   (with-output-to-temp-buffer "*Completions*"
 	     (display-completion-list
-	      (all-completions pattern 'tags-complete-tag nil)))
+	      (all-completions pattern 'tags-complete-tag nil)
+	      pattern))
 	   (message "Making completion list...%s" "done")))))
 
 (dolist (x '("^No tags table in use; use .* to select one$"
@@ -1961,4 +2042,5 @@
 
 (provide 'etags)
 
+;; arch-tag: b897c2b5-08f3-4837-b2d3-0e7d6db1b63e
 ;;; etags.el ends here