Someone on [[gnu.emacs.help]] asked if there was a way to browse through multiple matching tags for an identifier and choose a specific one (as opposed to tags-loop-continue).  This is helpful when you have overloaded function names and want to go to the right one.  I am using an aspect-oriented language (Specman) that extends methods all over the place, so this was something I've been thinking about doing for a while.  So I finally took a couple hours and did it.

It works for EmacsTags in [[Emacs]] and [[XEmacs]].

* Lisp:etags-select.el

== Discussion ==

I have integrated this patch after having used it for a month and it works fine imo. It adds TAB completion just like find-tag has. - MartinNordholts

Tag completion been updated to work on Emacs/XEmacs. - ScottFrazer

On Emacs 22.2.1, I get "Buffer is read-only error" when I press return. - bvk-chaitanya
[new]
Do you have some hook or minor-mode that overrides 'return' for all modes? - ScottFrazer

[new]
I ran into the "Buffer is read-only error" also, on Emacs 24.1.1, but only when launching Emacs in the terminal (emacs-nox), not in an X-window.  The difference must be in how the terminal passes input events to emacs.  It turns out that the RETURN key was not recognized in etags-select's mode-map.  It's the same "error" you would receive by pressing a non-mapped key such as "z" in the etags-select window.  I addressed it by changing the following:
<code>
(define-key map (kbd "RET") 'etags-select-goto-tag)
(define-key map (kbd "M-RET") 'etags-select-goto-tag-other-window)
</code>
-DanHarms

[new]
I also added a means to scroll the etags window by space and del, as we're used to in other non-editable windows.  
<code>
(define-key map (kbd "SPC") 'scroll-up-command)
(define-key map (kbd "DEL") 'scroll-down-command)
</code>
-DanHarms

To find tags in a included tags file, such as TAGS-LISP for Emacs Lisp, re-define `etags-select-get-tag-files' as

  (defun etags-select-get-tag-files ()
    "Get tag files."
    (if etags-select-use-xemacs-etags-p
        (buffer-tag-table-list)
      (mapcar 'tags-expand-table-name tags-table-list)
      (tags-table-check-computed-list)
      tags-table-computed-list))
[new]
I was running into an error "wrong-type-argument stringp t" only the first time per session that I followed a tag.  It turned out to originate from tags-table-computed-list having an element that was t, not a proper filename.  I use a TAGS file that links to external tags files; for every such link, you will receive the above error the first time you look up a tag after loading the main tags file.  The value t is used as an initial placeholder for unloaded tables (see the documentation for tags-table-extend-computed-list for more details).  So I changed the end of etags-select-get-tag-files as follows:  replace
<code>
(tags-table-check-computed-list)
tags-table-computed-list))
</code>
with
<code>
(etags-select-remove-placeholders)
tags-table-computed-list))
</code>
where the referenced defun is:
<code>
(defun etags-select-remove-placeholders() "Remove placeholder entries from tags-table-computed-list"
 (let ((list tags-table-computed-list))
  (while list
   (if (eq (car list) t)
       (progn
        (tags-table-extend-computed-list)
        (setq list tags-table-computed-list))
      (setq list (cdr list))))))
</code>
-DanHarms

[new]
etags-select.el provide a bottom window for select

||source||
||select||                                             

But sometimes, the select info is too short (e.g. struct abc under
different #define condition). So a *preview* window besides select
window will be helpful.
||||source||
||select||preview||  

[new]
I also preferred the etags window to shrink if it only had a few entries, leaving more of the original source code buffer visible.  Right before the end of etags-select-find, add the following:
<code>
(shrink-window-if-larger-than-buffer)
(etags-select-mode tagname)))))
</code>
-DanHarms

== Finding TAGS Files ==
[new]
See EmacsTags EtagsTable
[new]
Vim has a nice feature where you can indicate that you would like the editor to search up the directory tree from the current working directory for a file named “tags”. These two functions provide that for etags-select.el, searching up the filesystem from the buffer-file-name of the current buffer. (Modified version from EmacsTags)
[new]
<pre>
;;load the etags-select.el source code
(load-file "path/to/etags-select.el")
;;binding the key
(global-set-key "\M-?" 'etags-select-find-tag-at-point)
(global-set-key "\M-." 'etags-select-find-tag)

(defun jds-find-tags-file ()
  "recursively searches each parent directory for a file named 'TAGS' and returns the
path to that file or nil if a tags file is not found. Returns nil if the buffer is
not visiting a file"
  (progn
      (defun find-tags-file-r (path)
         "find the tags file from the parent directories"
         (let* ((parent (file-name-directory path))
                (possible-tags-file (concat parent "TAGS")))
           (cond
             ((file-exists-p possible-tags-file) (throw 'found-it possible-tags-file))
             ((string= "/TAGS" possible-tags-file) (error "no tags file found"))
             (t (find-tags-file-r (directory-file-name parent))))))

    (if (buffer-file-name)
        (catch 'found-it 
          (find-tags-file-r (buffer-file-name)))
        (error "buffer is not visiting a file"))))

(defun jds-set-tags-file-path ()
  "calls `jds-find-tags-file' to recursively search up the directory tree to find
a file named 'TAGS'. If found, set 'tags-table-list' with that path as an argument
otherwise raises an error."
  (interactive)
  (setq tags-table-list (cons (jds-find-tags-file) tags-table-list)))

;; delay search the TAGS file after open the source file
(add-hook 'emacs-startup-hook 
	'(lambda () (jds-set-tags-file-path)))
</pre>

== Mixing etags-select and Ido ==

[new:dchenbecker:2012-09-21 13:55 UTC]
I love etags-select, but I also really like ido's fuzzy matching, so I mixed the two. Based on a contribution on the [[InteractivelyDoThings]] page, I came up with this function:

<pre>
;; Use ido to list tags, but then select via etags-select (best of both worlds!)
(defun my-ido-find-tag ()
  "Find a tag using ido"
  (interactive)
  (tags-completion-table)
  (let (tag-names)
    (mapatoms (lambda (x)
                (push (prin1-to-string x t) tag-names))
              tags-completion-table)
    (etags-select-find (ido-completing-read "Tag: " tag-names))))
(global-set-key (kbd "M-.") 'my-ido-find-tag)
</pre>

== Related Commands From Other Packages ==

[new:DrewAdams:2008-02-18 21:33 UTC]
'''See Also:''' '''[[Icicles]]''' command `icicle-search-tag'.  You can browse matching tags (including duplicates) in any order, and not necessarily consecutively. Very easy to filter tags to those you want to visit, using regexps; you can filter the source files to search also. Tag completion is also available.

----
CategoryProgrammerUtils
CategoryNavigation
